| 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" | 
|---|
| 20 | #include "source/spirv_constant.h" | 
|---|
| 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( | 
|---|
| 61 | UptrVectorIterator<BasicBlock> ref_block_itr, BasicBlock* new_blk_ptr) { | 
|---|
| 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 | } | 
|---|
| 80 | new_blk_ptr->AddInstruction(std::move(mv_inst)); | 
|---|
| 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(); | 
|---|
| 110 | uint32_t buf_uint_ptr_id = GetOutputBufferPtrId(); | 
|---|
| 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 |  | 
|---|
| 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 |  | 
|---|
| 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 | 
|---|
| 159 | uint32_t load_id = GenVarLoad(builtin_id, builder); | 
|---|
| 160 | GenDebugOutputFieldCode(base_offset_id, builtin_off, load_id, builder); | 
|---|
| 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 | 
|---|
| 170 | GenBuiltinOutputCode( | 
|---|
| 171 | context()->GetBuiltinInputVarId(SpvBuiltInVertexIndex), | 
|---|
| 172 | kInstVertOutVertexIndex, base_offset_id, builder); | 
|---|
| 173 | GenBuiltinOutputCode( | 
|---|
| 174 | context()->GetBuiltinInputVarId(SpvBuiltInInstanceIndex), | 
|---|
| 175 | kInstVertOutInstanceIndex, base_offset_id, builder); | 
|---|
| 176 | } break; | 
|---|
| 177 | case SpvExecutionModelGLCompute: { | 
|---|
| 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 | GenDebugOutputFieldCode(base_offset_id, kInstCompOutGlobalInvocationIdX, | 
|---|
| 189 | x_inst->result_id(), builder); | 
|---|
| 190 | GenDebugOutputFieldCode(base_offset_id, kInstCompOutGlobalInvocationIdY, | 
|---|
| 191 | y_inst->result_id(), builder); | 
|---|
| 192 | GenDebugOutputFieldCode(base_offset_id, kInstCompOutGlobalInvocationIdZ, | 
|---|
| 193 | z_inst->result_id(), builder); | 
|---|
| 194 | } break; | 
|---|
| 195 | case SpvExecutionModelGeometry: { | 
|---|
| 196 | // Load and store PrimitiveId and InvocationId. | 
|---|
| 197 | GenBuiltinOutputCode( | 
|---|
| 198 | context()->GetBuiltinInputVarId(SpvBuiltInPrimitiveId), | 
|---|
| 199 | kInstGeomOutPrimitiveId, base_offset_id, builder); | 
|---|
| 200 | GenBuiltinOutputCode( | 
|---|
| 201 | context()->GetBuiltinInputVarId(SpvBuiltInInvocationId), | 
|---|
| 202 | kInstGeomOutInvocationId, base_offset_id, builder); | 
|---|
| 203 | } break; | 
|---|
| 204 | case SpvExecutionModelTessellationControl: { | 
|---|
| 205 | // Load and store InvocationId and PrimitiveId | 
|---|
| 206 | GenBuiltinOutputCode( | 
|---|
| 207 | context()->GetBuiltinInputVarId(SpvBuiltInInvocationId), | 
|---|
| 208 | kInstTessCtlOutInvocationId, base_offset_id, builder); | 
|---|
| 209 | GenBuiltinOutputCode( | 
|---|
| 210 | context()->GetBuiltinInputVarId(SpvBuiltInPrimitiveId), | 
|---|
| 211 | kInstTessCtlOutPrimitiveId, base_offset_id, builder); | 
|---|
| 212 | } break; | 
|---|
| 213 | case SpvExecutionModelTessellationEvaluation: { | 
|---|
| 214 | // Load and store PrimitiveId and TessCoord.uv | 
|---|
| 215 | GenBuiltinOutputCode( | 
|---|
| 216 | context()->GetBuiltinInputVarId(SpvBuiltInPrimitiveId), | 
|---|
| 217 | kInstTessEvalOutPrimitiveId, base_offset_id, builder); | 
|---|
| 218 | uint32_t load_id = GenVarLoad( | 
|---|
| 219 | context()->GetBuiltinInputVarId(SpvBuiltInTessCoord), builder); | 
|---|
| 220 | Instruction* uvec3_cast_inst = | 
|---|
| 221 | builder->AddUnaryOp(GetVec3UintId(), SpvOpBitcast, load_id); | 
|---|
| 222 | uint32_t uvec3_cast_id = uvec3_cast_inst->result_id(); | 
|---|
| 223 | Instruction* u_inst = builder->AddIdLiteralOp( | 
|---|
| 224 | GetUintId(), SpvOpCompositeExtract, uvec3_cast_id, 0); | 
|---|
| 225 | Instruction* v_inst = builder->AddIdLiteralOp( | 
|---|
| 226 | GetUintId(), SpvOpCompositeExtract, uvec3_cast_id, 1); | 
|---|
| 227 | GenDebugOutputFieldCode(base_offset_id, kInstTessEvalOutTessCoordU, | 
|---|
| 228 | u_inst->result_id(), builder); | 
|---|
| 229 | GenDebugOutputFieldCode(base_offset_id, kInstTessEvalOutTessCoordV, | 
|---|
| 230 | v_inst->result_id(), builder); | 
|---|
| 231 | } break; | 
|---|
| 232 | case SpvExecutionModelFragment: { | 
|---|
| 233 | // Load FragCoord and convert to Uint | 
|---|
| 234 | Instruction* frag_coord_inst = builder->AddUnaryOp( | 
|---|
| 235 | GetVec4FloatId(), SpvOpLoad, | 
|---|
| 236 | context()->GetBuiltinInputVarId(SpvBuiltInFragCoord)); | 
|---|
| 237 | Instruction* uint_frag_coord_inst = builder->AddUnaryOp( | 
|---|
| 238 | GetVec4UintId(), SpvOpBitcast, frag_coord_inst->result_id()); | 
|---|
| 239 | for (uint32_t u = 0; u < 2u; ++u) | 
|---|
| 240 | GenFragCoordEltDebugOutputCode( | 
|---|
| 241 | base_offset_id, uint_frag_coord_inst->result_id(), u, builder); | 
|---|
| 242 | } break; | 
|---|
| 243 | case SpvExecutionModelRayGenerationNV: | 
|---|
| 244 | case SpvExecutionModelIntersectionNV: | 
|---|
| 245 | case SpvExecutionModelAnyHitNV: | 
|---|
| 246 | case SpvExecutionModelClosestHitNV: | 
|---|
| 247 | case SpvExecutionModelMissNV: | 
|---|
| 248 | case SpvExecutionModelCallableNV: { | 
|---|
| 249 | // Load and store LaunchIdNV. | 
|---|
| 250 | uint32_t launch_id = GenVarLoad( | 
|---|
| 251 | context()->GetBuiltinInputVarId(SpvBuiltInLaunchIdNV), builder); | 
|---|
| 252 | Instruction* x_launch_inst = builder->AddIdLiteralOp( | 
|---|
| 253 | GetUintId(), SpvOpCompositeExtract, launch_id, 0); | 
|---|
| 254 | Instruction* y_launch_inst = builder->AddIdLiteralOp( | 
|---|
| 255 | GetUintId(), SpvOpCompositeExtract, launch_id, 1); | 
|---|
| 256 | Instruction* z_launch_inst = builder->AddIdLiteralOp( | 
|---|
| 257 | GetUintId(), SpvOpCompositeExtract, launch_id, 2); | 
|---|
| 258 | GenDebugOutputFieldCode(base_offset_id, kInstRayTracingOutLaunchIdX, | 
|---|
| 259 | x_launch_inst->result_id(), builder); | 
|---|
| 260 | GenDebugOutputFieldCode(base_offset_id, kInstRayTracingOutLaunchIdY, | 
|---|
| 261 | y_launch_inst->result_id(), builder); | 
|---|
| 262 | GenDebugOutputFieldCode(base_offset_id, kInstRayTracingOutLaunchIdZ, | 
|---|
| 263 | z_launch_inst->result_id(), builder); | 
|---|
| 264 | } break; | 
|---|
| 265 | default: { assert(false && "unsupported stage"); } break; | 
|---|
| 266 | } | 
|---|
| 267 | } | 
|---|
| 268 |  | 
|---|
| 269 | void InstrumentPass::GenDebugStreamWrite( | 
|---|
| 270 | uint32_t instruction_idx, uint32_t stage_idx, | 
|---|
| 271 | const std::vector<uint32_t>& validation_ids, InstructionBuilder* builder) { | 
|---|
| 272 | // Call debug output function. Pass func_idx, instruction_idx and | 
|---|
| 273 | // validation ids as args. | 
|---|
| 274 | uint32_t val_id_cnt = static_cast<uint32_t>(validation_ids.size()); | 
|---|
| 275 | uint32_t output_func_id = GetStreamWriteFunctionId(stage_idx, val_id_cnt); | 
|---|
| 276 | std::vector<uint32_t> args = {output_func_id, | 
|---|
| 277 | builder->GetUintConstantId(instruction_idx)}; | 
|---|
| 278 | (void)args.insert(args.end(), validation_ids.begin(), validation_ids.end()); | 
|---|
| 279 | (void)builder->AddNaryOp(GetVoidId(), SpvOpFunctionCall, args); | 
|---|
| 280 | } | 
|---|
| 281 |  | 
|---|
| 282 | uint32_t InstrumentPass::GenDebugDirectRead( | 
|---|
| 283 | const std::vector<uint32_t>& offset_ids, InstructionBuilder* builder) { | 
|---|
| 284 | // Call debug input function. Pass func_idx and offset ids as args. | 
|---|
| 285 | uint32_t off_id_cnt = static_cast<uint32_t>(offset_ids.size()); | 
|---|
| 286 | uint32_t input_func_id = GetDirectReadFunctionId(off_id_cnt); | 
|---|
| 287 | std::vector<uint32_t> args = {input_func_id}; | 
|---|
| 288 | (void)args.insert(args.end(), offset_ids.begin(), offset_ids.end()); | 
|---|
| 289 | return builder->AddNaryOp(GetUintId(), SpvOpFunctionCall, args)->result_id(); | 
|---|
| 290 | } | 
|---|
| 291 |  | 
|---|
| 292 | bool InstrumentPass::IsSameBlockOp(const Instruction* inst) const { | 
|---|
| 293 | return inst->opcode() == SpvOpSampledImage || inst->opcode() == SpvOpImage; | 
|---|
| 294 | } | 
|---|
| 295 |  | 
|---|
| 296 | void InstrumentPass::CloneSameBlockOps( | 
|---|
| 297 | std::unique_ptr<Instruction>* inst, | 
|---|
| 298 | std::unordered_map<uint32_t, uint32_t>* same_blk_post, | 
|---|
| 299 | std::unordered_map<uint32_t, Instruction*>* same_blk_pre, | 
|---|
| 300 | BasicBlock* block_ptr) { | 
|---|
| 301 | bool changed = false; | 
|---|
| 302 | (*inst)->ForEachInId([&same_blk_post, &same_blk_pre, &block_ptr, &changed, | 
|---|
| 303 | this](uint32_t* iid) { | 
|---|
| 304 | const auto map_itr = (*same_blk_post).find(*iid); | 
|---|
| 305 | if (map_itr == (*same_blk_post).end()) { | 
|---|
| 306 | const auto map_itr2 = (*same_blk_pre).find(*iid); | 
|---|
| 307 | if (map_itr2 != (*same_blk_pre).end()) { | 
|---|
| 308 | // Clone pre-call same-block ops, map result id. | 
|---|
| 309 | const Instruction* in_inst = map_itr2->second; | 
|---|
| 310 | std::unique_ptr<Instruction> sb_inst(in_inst->Clone(context())); | 
|---|
| 311 | const uint32_t rid = sb_inst->result_id(); | 
|---|
| 312 | const uint32_t nid = this->TakeNextId(); | 
|---|
| 313 | get_decoration_mgr()->CloneDecorations(rid, nid); | 
|---|
| 314 | sb_inst->SetResultId(nid); | 
|---|
| 315 | get_def_use_mgr()->AnalyzeInstDefUse(&*sb_inst); | 
|---|
| 316 | (*same_blk_post)[rid] = nid; | 
|---|
| 317 | *iid = nid; | 
|---|
| 318 | changed = true; | 
|---|
| 319 | CloneSameBlockOps(&sb_inst, same_blk_post, same_blk_pre, block_ptr); | 
|---|
| 320 | block_ptr->AddInstruction(std::move(sb_inst)); | 
|---|
| 321 | } | 
|---|
| 322 | } else { | 
|---|
| 323 | // Reset same-block op operand if necessary | 
|---|
| 324 | if (*iid != map_itr->second) { | 
|---|
| 325 | *iid = map_itr->second; | 
|---|
| 326 | changed = true; | 
|---|
| 327 | } | 
|---|
| 328 | } | 
|---|
| 329 | }); | 
|---|
| 330 | if (changed) get_def_use_mgr()->AnalyzeInstUse(&**inst); | 
|---|
| 331 | } | 
|---|
| 332 |  | 
|---|
| 333 | void InstrumentPass::UpdateSucceedingPhis( | 
|---|
| 334 | std::vector<std::unique_ptr<BasicBlock>>& new_blocks) { | 
|---|
| 335 | const auto first_blk = new_blocks.begin(); | 
|---|
| 336 | const auto last_blk = new_blocks.end() - 1; | 
|---|
| 337 | const uint32_t first_id = (*first_blk)->id(); | 
|---|
| 338 | const uint32_t last_id = (*last_blk)->id(); | 
|---|
| 339 | const BasicBlock& const_last_block = *last_blk->get(); | 
|---|
| 340 | const_last_block.ForEachSuccessorLabel( | 
|---|
| 341 | [&first_id, &last_id, this](const uint32_t succ) { | 
|---|
| 342 | BasicBlock* sbp = this->id2block_[succ]; | 
|---|
| 343 | sbp->ForEachPhiInst([&first_id, &last_id, this](Instruction* phi) { | 
|---|
| 344 | bool changed = false; | 
|---|
| 345 | phi->ForEachInId([&first_id, &last_id, &changed](uint32_t* id) { | 
|---|
| 346 | if (*id == first_id) { | 
|---|
| 347 | *id = last_id; | 
|---|
| 348 | changed = true; | 
|---|
| 349 | } | 
|---|
| 350 | }); | 
|---|
| 351 | if (changed) get_def_use_mgr()->AnalyzeInstUse(phi); | 
|---|
| 352 | }); | 
|---|
| 353 | }); | 
|---|
| 354 | } | 
|---|
| 355 |  | 
|---|
| 356 | uint32_t InstrumentPass::GetOutputBufferPtrId() { | 
|---|
| 357 | if (output_buffer_ptr_id_ == 0) { | 
|---|
| 358 | output_buffer_ptr_id_ = context()->get_type_mgr()->FindPointerToType( | 
|---|
| 359 | GetUintId(), SpvStorageClassStorageBuffer); | 
|---|
| 360 | } | 
|---|
| 361 | return output_buffer_ptr_id_; | 
|---|
| 362 | } | 
|---|
| 363 |  | 
|---|
| 364 | uint32_t InstrumentPass::GetInputBufferTypeId() { | 
|---|
| 365 | return (validation_id_ == kInstValidationIdBuffAddr) ? GetUint64Id() | 
|---|
| 366 | : GetUintId(); | 
|---|
| 367 | } | 
|---|
| 368 |  | 
|---|
| 369 | uint32_t InstrumentPass::GetInputBufferPtrId() { | 
|---|
| 370 | if (input_buffer_ptr_id_ == 0) { | 
|---|
| 371 | input_buffer_ptr_id_ = context()->get_type_mgr()->FindPointerToType( | 
|---|
| 372 | GetInputBufferTypeId(), SpvStorageClassStorageBuffer); | 
|---|
| 373 | } | 
|---|
| 374 | return input_buffer_ptr_id_; | 
|---|
| 375 | } | 
|---|
| 376 |  | 
|---|
| 377 | uint32_t InstrumentPass::GetOutputBufferBinding() { | 
|---|
| 378 | switch (validation_id_) { | 
|---|
| 379 | case kInstValidationIdBindless: | 
|---|
| 380 | return kDebugOutputBindingStream; | 
|---|
| 381 | case kInstValidationIdBuffAddr: | 
|---|
| 382 | return kDebugOutputBindingStream; | 
|---|
| 383 | case kInstValidationIdDebugPrintf: | 
|---|
| 384 | return kDebugOutputPrintfStream; | 
|---|
| 385 | default: | 
|---|
| 386 | assert(false && "unexpected validation id"); | 
|---|
| 387 | } | 
|---|
| 388 | return 0; | 
|---|
| 389 | } | 
|---|
| 390 |  | 
|---|
| 391 | uint32_t InstrumentPass::GetInputBufferBinding() { | 
|---|
| 392 | switch (validation_id_) { | 
|---|
| 393 | case kInstValidationIdBindless: | 
|---|
| 394 | return kDebugInputBindingBindless; | 
|---|
| 395 | case kInstValidationIdBuffAddr: | 
|---|
| 396 | return kDebugInputBindingBuffAddr; | 
|---|
| 397 | default: | 
|---|
| 398 | assert(false && "unexpected validation id"); | 
|---|
| 399 | } | 
|---|
| 400 | return 0; | 
|---|
| 401 | } | 
|---|
| 402 |  | 
|---|
| 403 | analysis::Type* InstrumentPass::GetUintXRuntimeArrayType( | 
|---|
| 404 | uint32_t width, analysis::Type** rarr_ty) { | 
|---|
| 405 | if (*rarr_ty == nullptr) { | 
|---|
| 406 | analysis::DecorationManager* deco_mgr = get_decoration_mgr(); | 
|---|
| 407 | analysis::TypeManager* type_mgr = context()->get_type_mgr(); | 
|---|
| 408 | analysis::Integer uint_ty(width, false); | 
|---|
| 409 | analysis::Type* reg_uint_ty = type_mgr->GetRegisteredType(&uint_ty); | 
|---|
| 410 | analysis::RuntimeArray uint_rarr_ty_tmp(reg_uint_ty); | 
|---|
| 411 | *rarr_ty = type_mgr->GetRegisteredType(&uint_rarr_ty_tmp); | 
|---|
| 412 | uint32_t uint_arr_ty_id = type_mgr->GetTypeInstruction(*rarr_ty); | 
|---|
| 413 | // By the Vulkan spec, a pre-existing RuntimeArray of uint must be part of | 
|---|
| 414 | // a block, and will therefore be decorated with an ArrayStride. Therefore | 
|---|
| 415 | // the undecorated type returned here will not be pre-existing and can | 
|---|
| 416 | // safely be decorated. Since this type is now decorated, it is out of | 
|---|
| 417 | // sync with the TypeManager and therefore the TypeManager must be | 
|---|
| 418 | // invalidated after this pass. | 
|---|
| 419 | assert(context()->get_def_use_mgr()->NumUses(uint_arr_ty_id) == 0 && | 
|---|
| 420 | "used RuntimeArray type returned"); | 
|---|
| 421 | deco_mgr->AddDecorationVal(uint_arr_ty_id, SpvDecorationArrayStride, | 
|---|
| 422 | width / 8u); | 
|---|
| 423 | } | 
|---|
| 424 | return *rarr_ty; | 
|---|
| 425 | } | 
|---|
| 426 |  | 
|---|
| 427 | analysis::Type* InstrumentPass::GetUintRuntimeArrayType(uint32_t width) { | 
|---|
| 428 | analysis::Type** rarr_ty = | 
|---|
| 429 | (width == 64) ? &uint64_rarr_ty_ : &uint32_rarr_ty_; | 
|---|
| 430 | return GetUintXRuntimeArrayType(width, rarr_ty); | 
|---|
| 431 | } | 
|---|
| 432 |  | 
|---|
| 433 | void InstrumentPass::AddStorageBufferExt() { | 
|---|
| 434 | if (storage_buffer_ext_defined_) return; | 
|---|
| 435 | if (!get_feature_mgr()->HasExtension(kSPV_KHR_storage_buffer_storage_class)) { | 
|---|
| 436 | context()->AddExtension( "SPV_KHR_storage_buffer_storage_class"); | 
|---|
| 437 | } | 
|---|
| 438 | storage_buffer_ext_defined_ = true; | 
|---|
| 439 | } | 
|---|
| 440 |  | 
|---|
| 441 | // Return id for output buffer | 
|---|
| 442 | uint32_t InstrumentPass::GetOutputBufferId() { | 
|---|
| 443 | if (output_buffer_id_ == 0) { | 
|---|
| 444 | // If not created yet, create one | 
|---|
| 445 | analysis::DecorationManager* deco_mgr = get_decoration_mgr(); | 
|---|
| 446 | analysis::TypeManager* type_mgr = context()->get_type_mgr(); | 
|---|
| 447 | analysis::Type* reg_uint_rarr_ty = GetUintRuntimeArrayType(32); | 
|---|
| 448 | analysis::Integer uint_ty(32, false); | 
|---|
| 449 | analysis::Type* reg_uint_ty = type_mgr->GetRegisteredType(&uint_ty); | 
|---|
| 450 | analysis::Struct buf_ty({reg_uint_ty, reg_uint_rarr_ty}); | 
|---|
| 451 | analysis::Type* reg_buf_ty = type_mgr->GetRegisteredType(&buf_ty); | 
|---|
| 452 | uint32_t obufTyId = type_mgr->GetTypeInstruction(reg_buf_ty); | 
|---|
| 453 | // By the Vulkan spec, a pre-existing struct containing a RuntimeArray | 
|---|
| 454 | // must be a block, and will therefore be decorated with Block. Therefore | 
|---|
| 455 | // the undecorated type returned here will not be pre-existing and can | 
|---|
| 456 | // safely be decorated. Since this type is now decorated, it is out of | 
|---|
| 457 | // sync with the TypeManager and therefore the TypeManager must be | 
|---|
| 458 | // invalidated after this pass. | 
|---|
| 459 | assert(context()->get_def_use_mgr()->NumUses(obufTyId) == 0 && | 
|---|
| 460 | "used struct type returned"); | 
|---|
| 461 | deco_mgr->AddDecoration(obufTyId, SpvDecorationBlock); | 
|---|
| 462 | deco_mgr->AddMemberDecoration(obufTyId, kDebugOutputSizeOffset, | 
|---|
| 463 | SpvDecorationOffset, 0); | 
|---|
| 464 | deco_mgr->AddMemberDecoration(obufTyId, kDebugOutputDataOffset, | 
|---|
| 465 | SpvDecorationOffset, 4); | 
|---|
| 466 | uint32_t obufTyPtrId_ = | 
|---|
| 467 | type_mgr->FindPointerToType(obufTyId, SpvStorageClassStorageBuffer); | 
|---|
| 468 | output_buffer_id_ = TakeNextId(); | 
|---|
| 469 | std::unique_ptr<Instruction> newVarOp(new Instruction( | 
|---|
| 470 | context(), SpvOpVariable, obufTyPtrId_, output_buffer_id_, | 
|---|
| 471 | {{spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER, | 
|---|
| 472 | {SpvStorageClassStorageBuffer}}})); | 
|---|
| 473 | context()->AddGlobalValue(std::move(newVarOp)); | 
|---|
| 474 | deco_mgr->AddDecorationVal(output_buffer_id_, SpvDecorationDescriptorSet, | 
|---|
| 475 | desc_set_); | 
|---|
| 476 | deco_mgr->AddDecorationVal(output_buffer_id_, SpvDecorationBinding, | 
|---|
| 477 | GetOutputBufferBinding()); | 
|---|
| 478 | AddStorageBufferExt(); | 
|---|
| 479 | if (get_module()->version() >= SPV_SPIRV_VERSION_WORD(1, 4)) { | 
|---|
| 480 | // Add the new buffer to all entry points. | 
|---|
| 481 | for (auto& entry : get_module()->entry_points()) { | 
|---|
| 482 | entry.AddOperand({SPV_OPERAND_TYPE_ID, {output_buffer_id_}}); | 
|---|
| 483 | context()->AnalyzeUses(&entry); | 
|---|
| 484 | } | 
|---|
| 485 | } | 
|---|
| 486 | } | 
|---|
| 487 | return output_buffer_id_; | 
|---|
| 488 | } | 
|---|
| 489 |  | 
|---|
| 490 | uint32_t InstrumentPass::GetInputBufferId() { | 
|---|
| 491 | if (input_buffer_id_ == 0) { | 
|---|
| 492 | // If not created yet, create one | 
|---|
| 493 | analysis::DecorationManager* deco_mgr = get_decoration_mgr(); | 
|---|
| 494 | analysis::TypeManager* type_mgr = context()->get_type_mgr(); | 
|---|
| 495 | uint32_t width = (validation_id_ == kInstValidationIdBuffAddr) ? 64u : 32u; | 
|---|
| 496 | analysis::Type* reg_uint_rarr_ty = GetUintRuntimeArrayType(width); | 
|---|
| 497 | analysis::Struct buf_ty({reg_uint_rarr_ty}); | 
|---|
| 498 | analysis::Type* reg_buf_ty = type_mgr->GetRegisteredType(&buf_ty); | 
|---|
| 499 | uint32_t ibufTyId = type_mgr->GetTypeInstruction(reg_buf_ty); | 
|---|
| 500 | // By the Vulkan spec, a pre-existing struct containing a RuntimeArray | 
|---|
| 501 | // must be a block, and will therefore be decorated with Block. Therefore | 
|---|
| 502 | // the undecorated type returned here will not be pre-existing and can | 
|---|
| 503 | // safely be decorated. Since this type is now decorated, it is out of | 
|---|
| 504 | // sync with the TypeManager and therefore the TypeManager must be | 
|---|
| 505 | // invalidated after this pass. | 
|---|
| 506 | assert(context()->get_def_use_mgr()->NumUses(ibufTyId) == 0 && | 
|---|
| 507 | "used struct type returned"); | 
|---|
| 508 | deco_mgr->AddDecoration(ibufTyId, SpvDecorationBlock); | 
|---|
| 509 | deco_mgr->AddMemberDecoration(ibufTyId, 0, SpvDecorationOffset, 0); | 
|---|
| 510 | uint32_t ibufTyPtrId_ = | 
|---|
| 511 | type_mgr->FindPointerToType(ibufTyId, SpvStorageClassStorageBuffer); | 
|---|
| 512 | input_buffer_id_ = TakeNextId(); | 
|---|
| 513 | std::unique_ptr<Instruction> newVarOp(new Instruction( | 
|---|
| 514 | context(), SpvOpVariable, ibufTyPtrId_, input_buffer_id_, | 
|---|
| 515 | {{spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER, | 
|---|
| 516 | {SpvStorageClassStorageBuffer}}})); | 
|---|
| 517 | context()->AddGlobalValue(std::move(newVarOp)); | 
|---|
| 518 | deco_mgr->AddDecorationVal(input_buffer_id_, SpvDecorationDescriptorSet, | 
|---|
| 519 | desc_set_); | 
|---|
| 520 | deco_mgr->AddDecorationVal(input_buffer_id_, SpvDecorationBinding, | 
|---|
| 521 | GetInputBufferBinding()); | 
|---|
| 522 | AddStorageBufferExt(); | 
|---|
| 523 | if (get_module()->version() >= SPV_SPIRV_VERSION_WORD(1, 4)) { | 
|---|
| 524 | // Add the new buffer to all entry points. | 
|---|
| 525 | for (auto& entry : get_module()->entry_points()) { | 
|---|
| 526 | entry.AddOperand({SPV_OPERAND_TYPE_ID, {input_buffer_id_}}); | 
|---|
| 527 | context()->AnalyzeUses(&entry); | 
|---|
| 528 | } | 
|---|
| 529 | } | 
|---|
| 530 | } | 
|---|
| 531 | return input_buffer_id_; | 
|---|
| 532 | } | 
|---|
| 533 |  | 
|---|
| 534 | uint32_t InstrumentPass::GetFloatId() { | 
|---|
| 535 | if (float_id_ == 0) { | 
|---|
| 536 | analysis::TypeManager* type_mgr = context()->get_type_mgr(); | 
|---|
| 537 | analysis::Float float_ty(32); | 
|---|
| 538 | analysis::Type* reg_float_ty = type_mgr->GetRegisteredType(&float_ty); | 
|---|
| 539 | float_id_ = type_mgr->GetTypeInstruction(reg_float_ty); | 
|---|
| 540 | } | 
|---|
| 541 | return float_id_; | 
|---|
| 542 | } | 
|---|
| 543 |  | 
|---|
| 544 | uint32_t InstrumentPass::GetVec4FloatId() { | 
|---|
| 545 | if (v4float_id_ == 0) { | 
|---|
| 546 | analysis::TypeManager* type_mgr = context()->get_type_mgr(); | 
|---|
| 547 | analysis::Float float_ty(32); | 
|---|
| 548 | analysis::Type* reg_float_ty = type_mgr->GetRegisteredType(&float_ty); | 
|---|
| 549 | analysis::Vector v4float_ty(reg_float_ty, 4); | 
|---|
| 550 | analysis::Type* reg_v4float_ty = type_mgr->GetRegisteredType(&v4float_ty); | 
|---|
| 551 | v4float_id_ = type_mgr->GetTypeInstruction(reg_v4float_ty); | 
|---|
| 552 | } | 
|---|
| 553 | return v4float_id_; | 
|---|
| 554 | } | 
|---|
| 555 |  | 
|---|
| 556 | uint32_t InstrumentPass::GetUintId() { | 
|---|
| 557 | if (uint_id_ == 0) { | 
|---|
| 558 | analysis::TypeManager* type_mgr = context()->get_type_mgr(); | 
|---|
| 559 | analysis::Integer uint_ty(32, false); | 
|---|
| 560 | analysis::Type* reg_uint_ty = type_mgr->GetRegisteredType(&uint_ty); | 
|---|
| 561 | uint_id_ = type_mgr->GetTypeInstruction(reg_uint_ty); | 
|---|
| 562 | } | 
|---|
| 563 | return uint_id_; | 
|---|
| 564 | } | 
|---|
| 565 |  | 
|---|
| 566 | uint32_t InstrumentPass::GetUint64Id() { | 
|---|
| 567 | if (uint64_id_ == 0) { | 
|---|
| 568 | analysis::TypeManager* type_mgr = context()->get_type_mgr(); | 
|---|
| 569 | analysis::Integer uint64_ty(64, false); | 
|---|
| 570 | analysis::Type* reg_uint64_ty = type_mgr->GetRegisteredType(&uint64_ty); | 
|---|
| 571 | uint64_id_ = type_mgr->GetTypeInstruction(reg_uint64_ty); | 
|---|
| 572 | } | 
|---|
| 573 | return uint64_id_; | 
|---|
| 574 | } | 
|---|
| 575 |  | 
|---|
| 576 | uint32_t InstrumentPass::GetUint8Id() { | 
|---|
| 577 | if (uint8_id_ == 0) { | 
|---|
| 578 | analysis::TypeManager* type_mgr = context()->get_type_mgr(); | 
|---|
| 579 | analysis::Integer uint8_ty(8, false); | 
|---|
| 580 | analysis::Type* reg_uint8_ty = type_mgr->GetRegisteredType(&uint8_ty); | 
|---|
| 581 | uint8_id_ = type_mgr->GetTypeInstruction(reg_uint8_ty); | 
|---|
| 582 | } | 
|---|
| 583 | return uint8_id_; | 
|---|
| 584 | } | 
|---|
| 585 |  | 
|---|
| 586 | uint32_t InstrumentPass::GetVecUintId(uint32_t len) { | 
|---|
| 587 | analysis::TypeManager* type_mgr = context()->get_type_mgr(); | 
|---|
| 588 | analysis::Integer uint_ty(32, false); | 
|---|
| 589 | analysis::Type* reg_uint_ty = type_mgr->GetRegisteredType(&uint_ty); | 
|---|
| 590 | analysis::Vector v_uint_ty(reg_uint_ty, len); | 
|---|
| 591 | analysis::Type* reg_v_uint_ty = type_mgr->GetRegisteredType(&v_uint_ty); | 
|---|
| 592 | uint32_t v_uint_id = type_mgr->GetTypeInstruction(reg_v_uint_ty); | 
|---|
| 593 | return v_uint_id; | 
|---|
| 594 | } | 
|---|
| 595 |  | 
|---|
| 596 | uint32_t InstrumentPass::GetVec4UintId() { | 
|---|
| 597 | if (v4uint_id_ == 0) v4uint_id_ = GetVecUintId(4u); | 
|---|
| 598 | return v4uint_id_; | 
|---|
| 599 | } | 
|---|
| 600 |  | 
|---|
| 601 | uint32_t InstrumentPass::GetVec3UintId() { | 
|---|
| 602 | if (v3uint_id_ == 0) v3uint_id_ = GetVecUintId(3u); | 
|---|
| 603 | return v3uint_id_; | 
|---|
| 604 | } | 
|---|
| 605 |  | 
|---|
| 606 | uint32_t InstrumentPass::GetBoolId() { | 
|---|
| 607 | if (bool_id_ == 0) { | 
|---|
| 608 | analysis::TypeManager* type_mgr = context()->get_type_mgr(); | 
|---|
| 609 | analysis::Bool bool_ty; | 
|---|
| 610 | analysis::Type* reg_bool_ty = type_mgr->GetRegisteredType(&bool_ty); | 
|---|
| 611 | bool_id_ = type_mgr->GetTypeInstruction(reg_bool_ty); | 
|---|
| 612 | } | 
|---|
| 613 | return bool_id_; | 
|---|
| 614 | } | 
|---|
| 615 |  | 
|---|
| 616 | uint32_t InstrumentPass::GetVoidId() { | 
|---|
| 617 | if (void_id_ == 0) { | 
|---|
| 618 | analysis::TypeManager* type_mgr = context()->get_type_mgr(); | 
|---|
| 619 | analysis::Void void_ty; | 
|---|
| 620 | analysis::Type* reg_void_ty = type_mgr->GetRegisteredType(&void_ty); | 
|---|
| 621 | void_id_ = type_mgr->GetTypeInstruction(reg_void_ty); | 
|---|
| 622 | } | 
|---|
| 623 | return void_id_; | 
|---|
| 624 | } | 
|---|
| 625 |  | 
|---|
| 626 | uint32_t InstrumentPass::GetStreamWriteFunctionId(uint32_t stage_idx, | 
|---|
| 627 | uint32_t val_spec_param_cnt) { | 
|---|
| 628 | // Total param count is common params plus validation-specific | 
|---|
| 629 | // params | 
|---|
| 630 | uint32_t param_cnt = kInstCommonParamCnt + val_spec_param_cnt; | 
|---|
| 631 | if (param2output_func_id_[param_cnt] == 0) { | 
|---|
| 632 | // Create function | 
|---|
| 633 | param2output_func_id_[param_cnt] = TakeNextId(); | 
|---|
| 634 | analysis::TypeManager* type_mgr = context()->get_type_mgr(); | 
|---|
| 635 | std::vector<const analysis::Type*> param_types; | 
|---|
| 636 | for (uint32_t c = 0; c < param_cnt; ++c) | 
|---|
| 637 | param_types.push_back(type_mgr->GetType(GetUintId())); | 
|---|
| 638 | analysis::Function func_ty(type_mgr->GetType(GetVoidId()), param_types); | 
|---|
| 639 | analysis::Type* reg_func_ty = type_mgr->GetRegisteredType(&func_ty); | 
|---|
| 640 | std::unique_ptr<Instruction> func_inst( | 
|---|
| 641 | new Instruction(get_module()->context(), SpvOpFunction, GetVoidId(), | 
|---|
| 642 | param2output_func_id_[param_cnt], | 
|---|
| 643 | {{spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER, | 
|---|
| 644 | {SpvFunctionControlMaskNone}}, | 
|---|
| 645 | {spv_operand_type_t::SPV_OPERAND_TYPE_ID, | 
|---|
| 646 | {type_mgr->GetTypeInstruction(reg_func_ty)}}})); | 
|---|
| 647 | get_def_use_mgr()->AnalyzeInstDefUse(&*func_inst); | 
|---|
| 648 | std::unique_ptr<Function> output_func = | 
|---|
| 649 | MakeUnique<Function>(std::move(func_inst)); | 
|---|
| 650 | // Add parameters | 
|---|
| 651 | std::vector<uint32_t> param_vec; | 
|---|
| 652 | for (uint32_t c = 0; c < param_cnt; ++c) { | 
|---|
| 653 | uint32_t pid = TakeNextId(); | 
|---|
| 654 | param_vec.push_back(pid); | 
|---|
| 655 | std::unique_ptr<Instruction> param_inst( | 
|---|
| 656 | new Instruction(get_module()->context(), SpvOpFunctionParameter, | 
|---|
| 657 | GetUintId(), pid, {})); | 
|---|
| 658 | get_def_use_mgr()->AnalyzeInstDefUse(&*param_inst); | 
|---|
| 659 | output_func->AddParameter(std::move(param_inst)); | 
|---|
| 660 | } | 
|---|
| 661 | // Create first block | 
|---|
| 662 | uint32_t test_blk_id = TakeNextId(); | 
|---|
| 663 | std::unique_ptr<Instruction> test_label(NewLabel(test_blk_id)); | 
|---|
| 664 | std::unique_ptr<BasicBlock> new_blk_ptr = | 
|---|
| 665 | MakeUnique<BasicBlock>(std::move(test_label)); | 
|---|
| 666 | InstructionBuilder builder( | 
|---|
| 667 | context(), &*new_blk_ptr, | 
|---|
| 668 | IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping); | 
|---|
| 669 | // Gen test if debug output buffer size will not be exceeded. | 
|---|
| 670 | uint32_t val_spec_offset = kInstStageOutCnt; | 
|---|
| 671 | uint32_t obuf_record_sz = val_spec_offset + val_spec_param_cnt; | 
|---|
| 672 | uint32_t buf_id = GetOutputBufferId(); | 
|---|
| 673 | uint32_t buf_uint_ptr_id = GetOutputBufferPtrId(); | 
|---|
| 674 | Instruction* obuf_curr_sz_ac_inst = | 
|---|
| 675 | builder.AddBinaryOp(buf_uint_ptr_id, SpvOpAccessChain, buf_id, | 
|---|
| 676 | builder.GetUintConstantId(kDebugOutputSizeOffset)); | 
|---|
| 677 | // Fetch the current debug buffer written size atomically, adding the | 
|---|
| 678 | // size of the record to be written. | 
|---|
| 679 | uint32_t obuf_record_sz_id = builder.GetUintConstantId(obuf_record_sz); | 
|---|
| 680 | uint32_t mask_none_id = builder.GetUintConstantId(SpvMemoryAccessMaskNone); | 
|---|
| 681 | uint32_t scope_invok_id = builder.GetUintConstantId(SpvScopeInvocation); | 
|---|
| 682 | Instruction* obuf_curr_sz_inst = builder.AddQuadOp( | 
|---|
| 683 | GetUintId(), SpvOpAtomicIAdd, obuf_curr_sz_ac_inst->result_id(), | 
|---|
| 684 | scope_invok_id, mask_none_id, obuf_record_sz_id); | 
|---|
| 685 | uint32_t obuf_curr_sz_id = obuf_curr_sz_inst->result_id(); | 
|---|
| 686 | // Compute new written size | 
|---|
| 687 | Instruction* obuf_new_sz_inst = | 
|---|
| 688 | builder.AddBinaryOp(GetUintId(), SpvOpIAdd, obuf_curr_sz_id, | 
|---|
| 689 | builder.GetUintConstantId(obuf_record_sz)); | 
|---|
| 690 | // Fetch the data bound | 
|---|
| 691 | Instruction* obuf_bnd_inst = | 
|---|
| 692 | builder.AddIdLiteralOp(GetUintId(), SpvOpArrayLength, | 
|---|
| 693 | GetOutputBufferId(), kDebugOutputDataOffset); | 
|---|
| 694 | // Test that new written size is less than or equal to debug output | 
|---|
| 695 | // data bound | 
|---|
| 696 | Instruction* obuf_safe_inst = builder.AddBinaryOp( | 
|---|
| 697 | GetBoolId(), SpvOpULessThanEqual, obuf_new_sz_inst->result_id(), | 
|---|
| 698 | obuf_bnd_inst->result_id()); | 
|---|
| 699 | uint32_t merge_blk_id = TakeNextId(); | 
|---|
| 700 | uint32_t write_blk_id = TakeNextId(); | 
|---|
| 701 | std::unique_ptr<Instruction> merge_label(NewLabel(merge_blk_id)); | 
|---|
| 702 | std::unique_ptr<Instruction> write_label(NewLabel(write_blk_id)); | 
|---|
| 703 | (void)builder.AddConditionalBranch(obuf_safe_inst->result_id(), | 
|---|
| 704 | write_blk_id, merge_blk_id, merge_blk_id, | 
|---|
| 705 | SpvSelectionControlMaskNone); | 
|---|
| 706 | // Close safety test block and gen write block | 
|---|
| 707 | new_blk_ptr->SetParent(&*output_func); | 
|---|
| 708 | output_func->AddBasicBlock(std::move(new_blk_ptr)); | 
|---|
| 709 | new_blk_ptr = MakeUnique<BasicBlock>(std::move(write_label)); | 
|---|
| 710 | builder.SetInsertPoint(&*new_blk_ptr); | 
|---|
| 711 | // Generate common and stage-specific debug record members | 
|---|
| 712 | GenCommonStreamWriteCode(obuf_record_sz, param_vec[kInstCommonParamInstIdx], | 
|---|
| 713 | stage_idx, obuf_curr_sz_id, &builder); | 
|---|
| 714 | GenStageStreamWriteCode(stage_idx, obuf_curr_sz_id, &builder); | 
|---|
| 715 | // Gen writes of validation specific data | 
|---|
| 716 | for (uint32_t i = 0; i < val_spec_param_cnt; ++i) { | 
|---|
| 717 | GenDebugOutputFieldCode(obuf_curr_sz_id, val_spec_offset + i, | 
|---|
| 718 | param_vec[kInstCommonParamCnt + i], &builder); | 
|---|
| 719 | } | 
|---|
| 720 | // Close write block and gen merge block | 
|---|
| 721 | (void)builder.AddBranch(merge_blk_id); | 
|---|
| 722 | new_blk_ptr->SetParent(&*output_func); | 
|---|
| 723 | output_func->AddBasicBlock(std::move(new_blk_ptr)); | 
|---|
| 724 | new_blk_ptr = MakeUnique<BasicBlock>(std::move(merge_label)); | 
|---|
| 725 | builder.SetInsertPoint(&*new_blk_ptr); | 
|---|
| 726 | // Close merge block and function and add function to module | 
|---|
| 727 | (void)builder.AddNullaryOp(0, SpvOpReturn); | 
|---|
| 728 | new_blk_ptr->SetParent(&*output_func); | 
|---|
| 729 | output_func->AddBasicBlock(std::move(new_blk_ptr)); | 
|---|
| 730 | std::unique_ptr<Instruction> func_end_inst( | 
|---|
| 731 | new Instruction(get_module()->context(), SpvOpFunctionEnd, 0, 0, {})); | 
|---|
| 732 | get_def_use_mgr()->AnalyzeInstDefUse(&*func_end_inst); | 
|---|
| 733 | output_func->SetFunctionEnd(std::move(func_end_inst)); | 
|---|
| 734 | context()->AddFunction(std::move(output_func)); | 
|---|
| 735 | } | 
|---|
| 736 | return param2output_func_id_[param_cnt]; | 
|---|
| 737 | } | 
|---|
| 738 |  | 
|---|
| 739 | uint32_t InstrumentPass::GetDirectReadFunctionId(uint32_t param_cnt) { | 
|---|
| 740 | uint32_t func_id = param2input_func_id_[param_cnt]; | 
|---|
| 741 | if (func_id != 0) return func_id; | 
|---|
| 742 | // Create input function for param_cnt. | 
|---|
| 743 | func_id = TakeNextId(); | 
|---|
| 744 | analysis::TypeManager* type_mgr = context()->get_type_mgr(); | 
|---|
| 745 | std::vector<const analysis::Type*> param_types; | 
|---|
| 746 | for (uint32_t c = 0; c < param_cnt; ++c) | 
|---|
| 747 | param_types.push_back(type_mgr->GetType(GetUintId())); | 
|---|
| 748 | uint32_t ibuf_type_id = GetInputBufferTypeId(); | 
|---|
| 749 | analysis::Function func_ty(type_mgr->GetType(ibuf_type_id), param_types); | 
|---|
| 750 | analysis::Type* reg_func_ty = type_mgr->GetRegisteredType(&func_ty); | 
|---|
| 751 | std::unique_ptr<Instruction> func_inst(new Instruction( | 
|---|
| 752 | get_module()->context(), SpvOpFunction, ibuf_type_id, func_id, | 
|---|
| 753 | {{spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER, | 
|---|
| 754 | {SpvFunctionControlMaskNone}}, | 
|---|
| 755 | {spv_operand_type_t::SPV_OPERAND_TYPE_ID, | 
|---|
| 756 | {type_mgr->GetTypeInstruction(reg_func_ty)}}})); | 
|---|
| 757 | get_def_use_mgr()->AnalyzeInstDefUse(&*func_inst); | 
|---|
| 758 | std::unique_ptr<Function> input_func = | 
|---|
| 759 | MakeUnique<Function>(std::move(func_inst)); | 
|---|
| 760 | // Add parameters | 
|---|
| 761 | std::vector<uint32_t> param_vec; | 
|---|
| 762 | for (uint32_t c = 0; c < param_cnt; ++c) { | 
|---|
| 763 | uint32_t pid = TakeNextId(); | 
|---|
| 764 | param_vec.push_back(pid); | 
|---|
| 765 | std::unique_ptr<Instruction> param_inst(new Instruction( | 
|---|
| 766 | get_module()->context(), SpvOpFunctionParameter, GetUintId(), pid, {})); | 
|---|
| 767 | get_def_use_mgr()->AnalyzeInstDefUse(&*param_inst); | 
|---|
| 768 | input_func->AddParameter(std::move(param_inst)); | 
|---|
| 769 | } | 
|---|
| 770 | // Create block | 
|---|
| 771 | uint32_t blk_id = TakeNextId(); | 
|---|
| 772 | std::unique_ptr<Instruction> blk_label(NewLabel(blk_id)); | 
|---|
| 773 | std::unique_ptr<BasicBlock> new_blk_ptr = | 
|---|
| 774 | MakeUnique<BasicBlock>(std::move(blk_label)); | 
|---|
| 775 | InstructionBuilder builder( | 
|---|
| 776 | context(), &*new_blk_ptr, | 
|---|
| 777 | IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping); | 
|---|
| 778 | // For each offset parameter, generate new offset with parameter, adding last | 
|---|
| 779 | // loaded value if it exists, and load value from input buffer at new offset. | 
|---|
| 780 | // Return last loaded value. | 
|---|
| 781 | uint32_t buf_id = GetInputBufferId(); | 
|---|
| 782 | uint32_t buf_ptr_id = GetInputBufferPtrId(); | 
|---|
| 783 | uint32_t last_value_id = 0; | 
|---|
| 784 | for (uint32_t p = 0; p < param_cnt; ++p) { | 
|---|
| 785 | uint32_t offset_id; | 
|---|
| 786 | if (p == 0) { | 
|---|
| 787 | offset_id = param_vec[0]; | 
|---|
| 788 | } else { | 
|---|
| 789 | if (ibuf_type_id != GetUintId()) { | 
|---|
| 790 | Instruction* ucvt_inst = | 
|---|
| 791 | builder.AddUnaryOp(GetUintId(), SpvOpUConvert, last_value_id); | 
|---|
| 792 | last_value_id = ucvt_inst->result_id(); | 
|---|
| 793 | } | 
|---|
| 794 | Instruction* offset_inst = builder.AddBinaryOp( | 
|---|
| 795 | GetUintId(), SpvOpIAdd, last_value_id, param_vec[p]); | 
|---|
| 796 | offset_id = offset_inst->result_id(); | 
|---|
| 797 | } | 
|---|
| 798 | Instruction* ac_inst = builder.AddTernaryOp( | 
|---|
| 799 | buf_ptr_id, SpvOpAccessChain, buf_id, | 
|---|
| 800 | builder.GetUintConstantId(kDebugInputDataOffset), offset_id); | 
|---|
| 801 | Instruction* load_inst = | 
|---|
| 802 | builder.AddUnaryOp(ibuf_type_id, SpvOpLoad, ac_inst->result_id()); | 
|---|
| 803 | last_value_id = load_inst->result_id(); | 
|---|
| 804 | } | 
|---|
| 805 | (void)builder.AddInstruction(MakeUnique<Instruction>( | 
|---|
| 806 | context(), SpvOpReturnValue, 0, 0, | 
|---|
| 807 | std::initializer_list<Operand>{{SPV_OPERAND_TYPE_ID, {last_value_id}}})); | 
|---|
| 808 | // Close block and function and add function to module | 
|---|
| 809 | new_blk_ptr->SetParent(&*input_func); | 
|---|
| 810 | input_func->AddBasicBlock(std::move(new_blk_ptr)); | 
|---|
| 811 | std::unique_ptr<Instruction> func_end_inst( | 
|---|
| 812 | new Instruction(get_module()->context(), SpvOpFunctionEnd, 0, 0, {})); | 
|---|
| 813 | get_def_use_mgr()->AnalyzeInstDefUse(&*func_end_inst); | 
|---|
| 814 | input_func->SetFunctionEnd(std::move(func_end_inst)); | 
|---|
| 815 | context()->AddFunction(std::move(input_func)); | 
|---|
| 816 | param2input_func_id_[param_cnt] = func_id; | 
|---|
| 817 | return func_id; | 
|---|
| 818 | } | 
|---|
| 819 |  | 
|---|
| 820 | bool InstrumentPass::InstrumentFunction(Function* func, uint32_t stage_idx, | 
|---|
| 821 | InstProcessFunction& pfn) { | 
|---|
| 822 | bool modified = false; | 
|---|
| 823 | // Compute function index | 
|---|
| 824 | uint32_t function_idx = 0; | 
|---|
| 825 | for (auto fii = get_module()->begin(); fii != get_module()->end(); ++fii) { | 
|---|
| 826 | if (&*fii == func) break; | 
|---|
| 827 | ++function_idx; | 
|---|
| 828 | } | 
|---|
| 829 | std::vector<std::unique_ptr<BasicBlock>> new_blks; | 
|---|
| 830 | // Using block iterators here because of block erasures and insertions. | 
|---|
| 831 | for (auto bi = func->begin(); bi != func->end(); ++bi) { | 
|---|
| 832 | for (auto ii = bi->begin(); ii != bi->end();) { | 
|---|
| 833 | // Generate instrumentation if warranted | 
|---|
| 834 | pfn(ii, bi, stage_idx, &new_blks); | 
|---|
| 835 | if (new_blks.size() == 0) { | 
|---|
| 836 | ++ii; | 
|---|
| 837 | continue; | 
|---|
| 838 | } | 
|---|
| 839 | // Add new blocks to label id map | 
|---|
| 840 | for (auto& blk : new_blks) id2block_[blk->id()] = &*blk; | 
|---|
| 841 | // If there are new blocks we know there will always be two or | 
|---|
| 842 | // more, so update succeeding phis with label of new last block. | 
|---|
| 843 | size_t newBlocksSize = new_blks.size(); | 
|---|
| 844 | assert(newBlocksSize > 1); | 
|---|
| 845 | UpdateSucceedingPhis(new_blks); | 
|---|
| 846 | // Replace original block with new block(s) | 
|---|
| 847 | bi = bi.Erase(); | 
|---|
| 848 | for (auto& bb : new_blks) { | 
|---|
| 849 | bb->SetParent(func); | 
|---|
| 850 | } | 
|---|
| 851 | bi = bi.InsertBefore(&new_blks); | 
|---|
| 852 | // Reset block iterator to last new block | 
|---|
| 853 | for (size_t i = 0; i < newBlocksSize - 1; i++) ++bi; | 
|---|
| 854 | modified = true; | 
|---|
| 855 | // Restart instrumenting at beginning of last new block, | 
|---|
| 856 | // but skip over any new phi or copy instruction. | 
|---|
| 857 | ii = bi->begin(); | 
|---|
| 858 | if (ii->opcode() == SpvOpPhi || ii->opcode() == SpvOpCopyObject) ++ii; | 
|---|
| 859 | new_blks.clear(); | 
|---|
| 860 | } | 
|---|
| 861 | } | 
|---|
| 862 | return modified; | 
|---|
| 863 | } | 
|---|
| 864 |  | 
|---|
| 865 | bool InstrumentPass::InstProcessCallTreeFromRoots(InstProcessFunction& pfn, | 
|---|
| 866 | std::queue<uint32_t>* roots, | 
|---|
| 867 | uint32_t stage_idx) { | 
|---|
| 868 | bool modified = false; | 
|---|
| 869 | std::unordered_set<uint32_t> done; | 
|---|
| 870 | // Don't process input and output functions | 
|---|
| 871 | for (auto& ifn : param2input_func_id_) done.insert(ifn.second); | 
|---|
| 872 | for (auto& ofn : param2output_func_id_) done.insert(ofn.second); | 
|---|
| 873 | // Process all functions from roots | 
|---|
| 874 | while (!roots->empty()) { | 
|---|
| 875 | const uint32_t fi = roots->front(); | 
|---|
| 876 | roots->pop(); | 
|---|
| 877 | if (done.insert(fi).second) { | 
|---|
| 878 | Function* fn = id2function_.at(fi); | 
|---|
| 879 | // Add calls first so we don't add new output function | 
|---|
| 880 | context()->AddCalls(fn, roots); | 
|---|
| 881 | modified = InstrumentFunction(fn, stage_idx, pfn) || modified; | 
|---|
| 882 | } | 
|---|
| 883 | } | 
|---|
| 884 | return modified; | 
|---|
| 885 | } | 
|---|
| 886 |  | 
|---|
| 887 | bool InstrumentPass::InstProcessEntryPointCallTree(InstProcessFunction& pfn) { | 
|---|
| 888 | // Check that format version 2 requested | 
|---|
| 889 | if (version_ != 2u) { | 
|---|
| 890 | if (consumer()) { | 
|---|
| 891 | std::string message = "Unsupported instrumentation format requested"; | 
|---|
| 892 | consumer()(SPV_MSG_ERROR, 0, {0, 0, 0}, message.c_str()); | 
|---|
| 893 | } | 
|---|
| 894 | return false; | 
|---|
| 895 | } | 
|---|
| 896 | // Make sure all entry points have the same execution model. Do not | 
|---|
| 897 | // instrument if they do not. | 
|---|
| 898 | // TODO(greg-lunarg): Handle mixed stages. Technically, a shader module | 
|---|
| 899 | // can contain entry points with different execution models, although | 
|---|
| 900 | // such modules will likely be rare as GLSL and HLSL are geared toward | 
|---|
| 901 | // one model per module. In such cases we will need | 
|---|
| 902 | // to clone any functions which are in the call trees of entrypoints | 
|---|
| 903 | // with differing execution models. | 
|---|
| 904 | uint32_t ecnt = 0; | 
|---|
| 905 | uint32_t stage = SpvExecutionModelMax; | 
|---|
| 906 | for (auto& e : get_module()->entry_points()) { | 
|---|
| 907 | if (ecnt == 0) | 
|---|
| 908 | stage = e.GetSingleWordInOperand(kEntryPointExecutionModelInIdx); | 
|---|
| 909 | else if (e.GetSingleWordInOperand(kEntryPointExecutionModelInIdx) != | 
|---|
| 910 | stage) { | 
|---|
| 911 | if (consumer()) { | 
|---|
| 912 | std::string message = "Mixed stage shader module not supported"; | 
|---|
| 913 | consumer()(SPV_MSG_ERROR, 0, {0, 0, 0}, message.c_str()); | 
|---|
| 914 | } | 
|---|
| 915 | return false; | 
|---|
| 916 | } | 
|---|
| 917 | ++ecnt; | 
|---|
| 918 | } | 
|---|
| 919 | // Check for supported stages | 
|---|
| 920 | if (stage != SpvExecutionModelVertex && stage != SpvExecutionModelFragment && | 
|---|
| 921 | stage != SpvExecutionModelGeometry && | 
|---|
| 922 | stage != SpvExecutionModelGLCompute && | 
|---|
| 923 | stage != SpvExecutionModelTessellationControl && | 
|---|
| 924 | stage != SpvExecutionModelTessellationEvaluation && | 
|---|
| 925 | stage != SpvExecutionModelRayGenerationNV && | 
|---|
| 926 | stage != SpvExecutionModelIntersectionNV && | 
|---|
| 927 | stage != SpvExecutionModelAnyHitNV && | 
|---|
| 928 | stage != SpvExecutionModelClosestHitNV && | 
|---|
| 929 | stage != SpvExecutionModelMissNV && | 
|---|
| 930 | stage != SpvExecutionModelCallableNV) { | 
|---|
| 931 | if (consumer()) { | 
|---|
| 932 | std::string message = "Stage not supported by instrumentation"; | 
|---|
| 933 | consumer()(SPV_MSG_ERROR, 0, {0, 0, 0}, message.c_str()); | 
|---|
| 934 | } | 
|---|
| 935 | return false; | 
|---|
| 936 | } | 
|---|
| 937 | // Add together the roots of all entry points | 
|---|
| 938 | std::queue<uint32_t> roots; | 
|---|
| 939 | for (auto& e : get_module()->entry_points()) { | 
|---|
| 940 | roots.push(e.GetSingleWordInOperand(kEntryPointFunctionIdInIdx)); | 
|---|
| 941 | } | 
|---|
| 942 | bool modified = InstProcessCallTreeFromRoots(pfn, &roots, stage); | 
|---|
| 943 | return modified; | 
|---|
| 944 | } | 
|---|
| 945 |  | 
|---|
| 946 | void InstrumentPass::InitializeInstrument() { | 
|---|
| 947 | output_buffer_id_ = 0; | 
|---|
| 948 | output_buffer_ptr_id_ = 0; | 
|---|
| 949 | input_buffer_ptr_id_ = 0; | 
|---|
| 950 | input_buffer_id_ = 0; | 
|---|
| 951 | float_id_ = 0; | 
|---|
| 952 | v4float_id_ = 0; | 
|---|
| 953 | uint_id_ = 0; | 
|---|
| 954 | uint64_id_ = 0; | 
|---|
| 955 | uint8_id_ = 0; | 
|---|
| 956 | v4uint_id_ = 0; | 
|---|
| 957 | v3uint_id_ = 0; | 
|---|
| 958 | bool_id_ = 0; | 
|---|
| 959 | void_id_ = 0; | 
|---|
| 960 | storage_buffer_ext_defined_ = false; | 
|---|
| 961 | uint32_rarr_ty_ = nullptr; | 
|---|
| 962 | uint64_rarr_ty_ = nullptr; | 
|---|
| 963 |  | 
|---|
| 964 | // clear collections | 
|---|
| 965 | id2function_.clear(); | 
|---|
| 966 | id2block_.clear(); | 
|---|
| 967 |  | 
|---|
| 968 | // clear maps | 
|---|
| 969 | param2input_func_id_.clear(); | 
|---|
| 970 | param2output_func_id_.clear(); | 
|---|
| 971 |  | 
|---|
| 972 | // Initialize function and block maps. | 
|---|
| 973 | for (auto& fn : *get_module()) { | 
|---|
| 974 | id2function_[fn.result_id()] = &fn; | 
|---|
| 975 | for (auto& blk : fn) { | 
|---|
| 976 | id2block_[blk.id()] = &blk; | 
|---|
| 977 | } | 
|---|
| 978 | } | 
|---|
| 979 |  | 
|---|
| 980 | // Remember original instruction offsets | 
|---|
| 981 | uint32_t module_offset = 0; | 
|---|
| 982 | Module* module = get_module(); | 
|---|
| 983 | for (auto& i : context()->capabilities()) { | 
|---|
| 984 | (void)i; | 
|---|
| 985 | ++module_offset; | 
|---|
| 986 | } | 
|---|
| 987 | for (auto& i : module->extensions()) { | 
|---|
| 988 | (void)i; | 
|---|
| 989 | ++module_offset; | 
|---|
| 990 | } | 
|---|
| 991 | for (auto& i : module->ext_inst_imports()) { | 
|---|
| 992 | (void)i; | 
|---|
| 993 | ++module_offset; | 
|---|
| 994 | } | 
|---|
| 995 | ++module_offset;  // memory_model | 
|---|
| 996 | for (auto& i : module->entry_points()) { | 
|---|
| 997 | (void)i; | 
|---|
| 998 | ++module_offset; | 
|---|
| 999 | } | 
|---|
| 1000 | for (auto& i : module->execution_modes()) { | 
|---|
| 1001 | (void)i; | 
|---|
| 1002 | ++module_offset; | 
|---|
| 1003 | } | 
|---|
| 1004 | for (auto& i : module->debugs1()) { | 
|---|
| 1005 | (void)i; | 
|---|
| 1006 | ++module_offset; | 
|---|
| 1007 | } | 
|---|
| 1008 | for (auto& i : module->debugs2()) { | 
|---|
| 1009 | (void)i; | 
|---|
| 1010 | ++module_offset; | 
|---|
| 1011 | } | 
|---|
| 1012 | for (auto& i : module->debugs3()) { | 
|---|
| 1013 | (void)i; | 
|---|
| 1014 | ++module_offset; | 
|---|
| 1015 | } | 
|---|
| 1016 | for (auto& i : module->ext_inst_debuginfo()) { | 
|---|
| 1017 | (void)i; | 
|---|
| 1018 | ++module_offset; | 
|---|
| 1019 | } | 
|---|
| 1020 | for (auto& i : module->annotations()) { | 
|---|
| 1021 | (void)i; | 
|---|
| 1022 | ++module_offset; | 
|---|
| 1023 | } | 
|---|
| 1024 | for (auto& i : module->types_values()) { | 
|---|
| 1025 | module_offset += 1; | 
|---|
| 1026 | module_offset += static_cast<uint32_t>(i.dbg_line_insts().size()); | 
|---|
| 1027 | } | 
|---|
| 1028 |  | 
|---|
| 1029 | auto curr_fn = get_module()->begin(); | 
|---|
| 1030 | for (; curr_fn != get_module()->end(); ++curr_fn) { | 
|---|
| 1031 | // Count function instruction | 
|---|
| 1032 | module_offset += 1; | 
|---|
| 1033 | curr_fn->ForEachParam( | 
|---|
| 1034 | [&module_offset](const Instruction*) { module_offset += 1; }, true); | 
|---|
| 1035 | for (auto& blk : *curr_fn) { | 
|---|
| 1036 | // Count label | 
|---|
| 1037 | module_offset += 1; | 
|---|
| 1038 | for (auto& inst : blk) { | 
|---|
| 1039 | module_offset += static_cast<uint32_t>(inst.dbg_line_insts().size()); | 
|---|
| 1040 | uid2offset_[inst.unique_id()] = module_offset; | 
|---|
| 1041 | module_offset += 1; | 
|---|
| 1042 | } | 
|---|
| 1043 | } | 
|---|
| 1044 | // Count function end instruction | 
|---|
| 1045 | module_offset += 1; | 
|---|
| 1046 | } | 
|---|
| 1047 | } | 
|---|
| 1048 |  | 
|---|
| 1049 | }  // namespace opt | 
|---|
| 1050 | }  // namespace spvtools | 
|---|
| 1051 |  | 
|---|