| 1 | // Copyright (c) 2019 Google LLC |
| 2 | // |
| 3 | // Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 | // you may not use this file except in compliance with the License. |
| 5 | // You may obtain a copy of the License at |
| 6 | // |
| 7 | // http://www.apache.org/licenses/LICENSE-2.0 |
| 8 | // |
| 9 | // Unless required by applicable law or agreed to in writing, software |
| 10 | // distributed under the License is distributed on an "AS IS" BASIS, |
| 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 12 | // See the License for the specific language governing permissions and |
| 13 | // limitations under the License. |
| 14 | |
| 15 | #include "source/opt/eliminate_dead_members_pass.h" |
| 16 | |
| 17 | #include "ir_builder.h" |
| 18 | #include "source/opt/ir_context.h" |
| 19 | |
| 20 | namespace { |
| 21 | const uint32_t kRemovedMember = 0xFFFFFFFF; |
| 22 | } |
| 23 | |
| 24 | namespace spvtools { |
| 25 | namespace opt { |
| 26 | |
| 27 | Pass::Status EliminateDeadMembersPass::Process() { |
| 28 | if (!context()->get_feature_mgr()->HasCapability(SpvCapabilityShader)) |
| 29 | return Status::SuccessWithoutChange; |
| 30 | |
| 31 | FindLiveMembers(); |
| 32 | if (RemoveDeadMembers()) { |
| 33 | return Status::SuccessWithChange; |
| 34 | } |
| 35 | return Status::SuccessWithoutChange; |
| 36 | } |
| 37 | |
| 38 | void EliminateDeadMembersPass::FindLiveMembers() { |
| 39 | // Until we have implemented the rewritting of OpSpecConsantOp instructions, |
| 40 | // we have to mark them as fully used just to be safe. |
| 41 | for (auto& inst : get_module()->types_values()) { |
| 42 | if (inst.opcode() == SpvOpSpecConstantOp) { |
| 43 | MarkTypeAsFullyUsed(inst.type_id()); |
| 44 | } else if (inst.opcode() == SpvOpVariable) { |
| 45 | switch (inst.GetSingleWordInOperand(0)) { |
| 46 | case SpvStorageClassInput: |
| 47 | case SpvStorageClassOutput: |
| 48 | MarkPointeeTypeAsFullUsed(inst.type_id()); |
| 49 | break; |
| 50 | default: |
| 51 | break; |
| 52 | } |
| 53 | } |
| 54 | } |
| 55 | |
| 56 | for (const Function& func : *get_module()) { |
| 57 | FindLiveMembers(func); |
| 58 | } |
| 59 | } |
| 60 | |
| 61 | void EliminateDeadMembersPass::FindLiveMembers(const Function& function) { |
| 62 | function.ForEachInst( |
| 63 | [this](const Instruction* inst) { FindLiveMembers(inst); }); |
| 64 | } |
| 65 | |
| 66 | void EliminateDeadMembersPass::FindLiveMembers(const Instruction* inst) { |
| 67 | switch (inst->opcode()) { |
| 68 | case SpvOpStore: |
| 69 | MarkMembersAsLiveForStore(inst); |
| 70 | break; |
| 71 | case SpvOpCopyMemory: |
| 72 | case SpvOpCopyMemorySized: |
| 73 | MarkMembersAsLiveForCopyMemory(inst); |
| 74 | break; |
| 75 | case SpvOpCompositeExtract: |
| 76 | MarkMembersAsLiveForExtract(inst); |
| 77 | break; |
| 78 | case SpvOpAccessChain: |
| 79 | case SpvOpInBoundsAccessChain: |
| 80 | case SpvOpPtrAccessChain: |
| 81 | case SpvOpInBoundsPtrAccessChain: |
| 82 | MarkMembersAsLiveForAccessChain(inst); |
| 83 | break; |
| 84 | case SpvOpReturnValue: |
| 85 | // This should be an issue only if we are returning from the entry point. |
| 86 | // However, for now I will keep it more conservative because functions are |
| 87 | // often inlined leaving only the entry points. |
| 88 | MarkOperandTypeAsFullyUsed(inst, 0); |
| 89 | break; |
| 90 | case SpvOpArrayLength: |
| 91 | MarkMembersAsLiveForArrayLength(inst); |
| 92 | break; |
| 93 | case SpvOpLoad: |
| 94 | case SpvOpCompositeInsert: |
| 95 | case SpvOpCompositeConstruct: |
| 96 | break; |
| 97 | default: |
| 98 | // This path is here for safety. All instructions that can reference |
| 99 | // structs in a function body should be handled above. However, this will |
| 100 | // keep the pass valid, but not optimal, as new instructions get added |
| 101 | // or if something was missed. |
| 102 | MarkStructOperandsAsFullyUsed(inst); |
| 103 | break; |
| 104 | } |
| 105 | } |
| 106 | |
| 107 | void EliminateDeadMembersPass::MarkMembersAsLiveForStore( |
| 108 | const Instruction* inst) { |
| 109 | // We should only have to mark the members as live if the store is to |
| 110 | // memory that is read outside of the shader. Other passes can remove all |
| 111 | // store to memory that is not visible outside of the shader, so we do not |
| 112 | // complicate the code for now. |
| 113 | assert(inst->opcode() == SpvOpStore); |
| 114 | uint32_t object_id = inst->GetSingleWordInOperand(1); |
| 115 | Instruction* object_inst = context()->get_def_use_mgr()->GetDef(object_id); |
| 116 | uint32_t object_type_id = object_inst->type_id(); |
| 117 | MarkTypeAsFullyUsed(object_type_id); |
| 118 | } |
| 119 | |
| 120 | void EliminateDeadMembersPass::MarkTypeAsFullyUsed(uint32_t type_id) { |
| 121 | Instruction* type_inst = get_def_use_mgr()->GetDef(type_id); |
| 122 | assert(type_inst != nullptr); |
| 123 | if (type_inst->opcode() != SpvOpTypeStruct) { |
| 124 | return; |
| 125 | } |
| 126 | |
| 127 | // Mark every member of the current struct as used. |
| 128 | for (uint32_t i = 0; i < type_inst->NumInOperands(); ++i) { |
| 129 | used_members_[type_id].insert(i); |
| 130 | } |
| 131 | |
| 132 | // Mark any sub struct as fully used. |
| 133 | for (uint32_t i = 0; i < type_inst->NumInOperands(); ++i) { |
| 134 | MarkTypeAsFullyUsed(type_inst->GetSingleWordInOperand(i)); |
| 135 | } |
| 136 | } |
| 137 | |
| 138 | void EliminateDeadMembersPass::MarkPointeeTypeAsFullUsed(uint32_t ptr_type_id) { |
| 139 | Instruction* ptr_type_inst = get_def_use_mgr()->GetDef(ptr_type_id); |
| 140 | assert(ptr_type_inst->opcode() == SpvOpTypePointer); |
| 141 | MarkTypeAsFullyUsed(ptr_type_inst->GetSingleWordInOperand(1)); |
| 142 | } |
| 143 | |
| 144 | void EliminateDeadMembersPass::MarkMembersAsLiveForCopyMemory( |
| 145 | const Instruction* inst) { |
| 146 | uint32_t target_id = inst->GetSingleWordInOperand(0); |
| 147 | Instruction* target_inst = get_def_use_mgr()->GetDef(target_id); |
| 148 | uint32_t pointer_type_id = target_inst->type_id(); |
| 149 | Instruction* pointer_type_inst = get_def_use_mgr()->GetDef(pointer_type_id); |
| 150 | uint32_t type_id = pointer_type_inst->GetSingleWordInOperand(1); |
| 151 | MarkTypeAsFullyUsed(type_id); |
| 152 | } |
| 153 | |
| 154 | void EliminateDeadMembersPass::( |
| 155 | const Instruction* inst) { |
| 156 | assert(inst->opcode() == SpvOpCompositeExtract); |
| 157 | |
| 158 | uint32_t composite_id = inst->GetSingleWordInOperand(0); |
| 159 | Instruction* composite_inst = get_def_use_mgr()->GetDef(composite_id); |
| 160 | uint32_t type_id = composite_inst->type_id(); |
| 161 | |
| 162 | for (uint32_t i = 1; i < inst->NumInOperands(); ++i) { |
| 163 | Instruction* type_inst = get_def_use_mgr()->GetDef(type_id); |
| 164 | uint32_t member_idx = inst->GetSingleWordInOperand(i); |
| 165 | switch (type_inst->opcode()) { |
| 166 | case SpvOpTypeStruct: |
| 167 | used_members_[type_id].insert(member_idx); |
| 168 | type_id = type_inst->GetSingleWordInOperand(member_idx); |
| 169 | break; |
| 170 | case SpvOpTypeArray: |
| 171 | case SpvOpTypeRuntimeArray: |
| 172 | case SpvOpTypeVector: |
| 173 | case SpvOpTypeMatrix: |
| 174 | type_id = type_inst->GetSingleWordInOperand(0); |
| 175 | break; |
| 176 | default: |
| 177 | assert(false); |
| 178 | } |
| 179 | } |
| 180 | } |
| 181 | |
| 182 | void EliminateDeadMembersPass::MarkMembersAsLiveForAccessChain( |
| 183 | const Instruction* inst) { |
| 184 | assert(inst->opcode() == SpvOpAccessChain || |
| 185 | inst->opcode() == SpvOpInBoundsAccessChain || |
| 186 | inst->opcode() == SpvOpPtrAccessChain || |
| 187 | inst->opcode() == SpvOpInBoundsPtrAccessChain); |
| 188 | |
| 189 | uint32_t pointer_id = inst->GetSingleWordInOperand(0); |
| 190 | Instruction* pointer_inst = get_def_use_mgr()->GetDef(pointer_id); |
| 191 | uint32_t pointer_type_id = pointer_inst->type_id(); |
| 192 | Instruction* pointer_type_inst = get_def_use_mgr()->GetDef(pointer_type_id); |
| 193 | uint32_t type_id = pointer_type_inst->GetSingleWordInOperand(1); |
| 194 | |
| 195 | analysis::ConstantManager* const_mgr = context()->get_constant_mgr(); |
| 196 | |
| 197 | // For a pointer access chain, we need to skip the |element| index. It is not |
| 198 | // a reference to the member of a struct, and it does not change the type. |
| 199 | uint32_t i = (inst->opcode() == SpvOpAccessChain || |
| 200 | inst->opcode() == SpvOpInBoundsAccessChain |
| 201 | ? 1 |
| 202 | : 2); |
| 203 | for (; i < inst->NumInOperands(); ++i) { |
| 204 | Instruction* type_inst = get_def_use_mgr()->GetDef(type_id); |
| 205 | switch (type_inst->opcode()) { |
| 206 | case SpvOpTypeStruct: { |
| 207 | const analysis::IntConstant* member_idx = |
| 208 | const_mgr->FindDeclaredConstant(inst->GetSingleWordInOperand(i)) |
| 209 | ->AsIntConstant(); |
| 210 | assert(member_idx); |
| 211 | if (member_idx->type()->AsInteger()->width() == 32) { |
| 212 | used_members_[type_id].insert(member_idx->GetU32()); |
| 213 | type_id = type_inst->GetSingleWordInOperand(member_idx->GetU32()); |
| 214 | } else { |
| 215 | used_members_[type_id].insert( |
| 216 | static_cast<uint32_t>(member_idx->GetU64())); |
| 217 | type_id = type_inst->GetSingleWordInOperand( |
| 218 | static_cast<uint32_t>(member_idx->GetU64())); |
| 219 | } |
| 220 | } break; |
| 221 | case SpvOpTypeArray: |
| 222 | case SpvOpTypeRuntimeArray: |
| 223 | case SpvOpTypeVector: |
| 224 | case SpvOpTypeMatrix: |
| 225 | type_id = type_inst->GetSingleWordInOperand(0); |
| 226 | break; |
| 227 | default: |
| 228 | assert(false); |
| 229 | } |
| 230 | } |
| 231 | } |
| 232 | |
| 233 | void EliminateDeadMembersPass::MarkOperandTypeAsFullyUsed( |
| 234 | const Instruction* inst, uint32_t in_idx) { |
| 235 | uint32_t op_id = inst->GetSingleWordInOperand(in_idx); |
| 236 | Instruction* op_inst = get_def_use_mgr()->GetDef(op_id); |
| 237 | MarkTypeAsFullyUsed(op_inst->type_id()); |
| 238 | } |
| 239 | |
| 240 | void EliminateDeadMembersPass::MarkMembersAsLiveForArrayLength( |
| 241 | const Instruction* inst) { |
| 242 | assert(inst->opcode() == SpvOpArrayLength); |
| 243 | uint32_t object_id = inst->GetSingleWordInOperand(0); |
| 244 | Instruction* object_inst = get_def_use_mgr()->GetDef(object_id); |
| 245 | uint32_t pointer_type_id = object_inst->type_id(); |
| 246 | Instruction* pointer_type_inst = get_def_use_mgr()->GetDef(pointer_type_id); |
| 247 | uint32_t type_id = pointer_type_inst->GetSingleWordInOperand(1); |
| 248 | used_members_[type_id].insert(inst->GetSingleWordInOperand(1)); |
| 249 | } |
| 250 | |
| 251 | bool EliminateDeadMembersPass::RemoveDeadMembers() { |
| 252 | bool modified = false; |
| 253 | |
| 254 | // First update all of the OpTypeStruct instructions. |
| 255 | get_module()->ForEachInst([&modified, this](Instruction* inst) { |
| 256 | switch (inst->opcode()) { |
| 257 | case SpvOpTypeStruct: |
| 258 | modified |= UpdateOpTypeStruct(inst); |
| 259 | break; |
| 260 | default: |
| 261 | break; |
| 262 | } |
| 263 | }); |
| 264 | |
| 265 | // Now update all of the instructions that reference the OpTypeStructs. |
| 266 | get_module()->ForEachInst([&modified, this](Instruction* inst) { |
| 267 | switch (inst->opcode()) { |
| 268 | case SpvOpMemberName: |
| 269 | modified |= UpdateOpMemberNameOrDecorate(inst); |
| 270 | break; |
| 271 | case SpvOpMemberDecorate: |
| 272 | modified |= UpdateOpMemberNameOrDecorate(inst); |
| 273 | break; |
| 274 | case SpvOpGroupMemberDecorate: |
| 275 | modified |= UpdateOpGroupMemberDecorate(inst); |
| 276 | break; |
| 277 | case SpvOpSpecConstantComposite: |
| 278 | case SpvOpConstantComposite: |
| 279 | case SpvOpCompositeConstruct: |
| 280 | modified |= UpdateConstantComposite(inst); |
| 281 | break; |
| 282 | case SpvOpAccessChain: |
| 283 | case SpvOpInBoundsAccessChain: |
| 284 | case SpvOpPtrAccessChain: |
| 285 | case SpvOpInBoundsPtrAccessChain: |
| 286 | modified |= UpdateAccessChain(inst); |
| 287 | break; |
| 288 | case SpvOpCompositeExtract: |
| 289 | modified |= UpdateCompsiteExtract(inst); |
| 290 | break; |
| 291 | case SpvOpCompositeInsert: |
| 292 | modified |= UpdateCompositeInsert(inst); |
| 293 | break; |
| 294 | case SpvOpArrayLength: |
| 295 | modified |= UpdateOpArrayLength(inst); |
| 296 | break; |
| 297 | case SpvOpSpecConstantOp: |
| 298 | assert(false && "Not yet implemented." ); |
| 299 | // with OpCompositeExtract, OpCompositeInsert |
| 300 | // For kernels: OpAccessChain, OpInBoundsAccessChain, OpPtrAccessChain, |
| 301 | // OpInBoundsPtrAccessChain |
| 302 | break; |
| 303 | default: |
| 304 | break; |
| 305 | } |
| 306 | }); |
| 307 | return modified; |
| 308 | } |
| 309 | |
| 310 | bool EliminateDeadMembersPass::UpdateOpTypeStruct(Instruction* inst) { |
| 311 | assert(inst->opcode() == SpvOpTypeStruct); |
| 312 | |
| 313 | const auto& live_members = used_members_[inst->result_id()]; |
| 314 | if (live_members.size() == inst->NumInOperands()) { |
| 315 | return false; |
| 316 | } |
| 317 | |
| 318 | Instruction::OperandList new_operands; |
| 319 | for (uint32_t idx : live_members) { |
| 320 | new_operands.emplace_back(inst->GetInOperand(idx)); |
| 321 | } |
| 322 | |
| 323 | inst->SetInOperands(std::move(new_operands)); |
| 324 | context()->UpdateDefUse(inst); |
| 325 | return true; |
| 326 | } |
| 327 | |
| 328 | bool EliminateDeadMembersPass::UpdateOpMemberNameOrDecorate(Instruction* inst) { |
| 329 | assert(inst->opcode() == SpvOpMemberName || |
| 330 | inst->opcode() == SpvOpMemberDecorate); |
| 331 | |
| 332 | uint32_t type_id = inst->GetSingleWordInOperand(0); |
| 333 | auto live_members = used_members_.find(type_id); |
| 334 | if (live_members == used_members_.end()) { |
| 335 | return false; |
| 336 | } |
| 337 | |
| 338 | uint32_t orig_member_idx = inst->GetSingleWordInOperand(1); |
| 339 | uint32_t new_member_idx = GetNewMemberIndex(type_id, orig_member_idx); |
| 340 | |
| 341 | if (new_member_idx == kRemovedMember) { |
| 342 | context()->KillInst(inst); |
| 343 | return true; |
| 344 | } |
| 345 | |
| 346 | if (new_member_idx == orig_member_idx) { |
| 347 | return false; |
| 348 | } |
| 349 | |
| 350 | inst->SetInOperand(1, {new_member_idx}); |
| 351 | return true; |
| 352 | } |
| 353 | |
| 354 | bool EliminateDeadMembersPass::UpdateOpGroupMemberDecorate(Instruction* inst) { |
| 355 | assert(inst->opcode() == SpvOpGroupMemberDecorate); |
| 356 | |
| 357 | bool modified = false; |
| 358 | |
| 359 | Instruction::OperandList new_operands; |
| 360 | new_operands.emplace_back(inst->GetInOperand(0)); |
| 361 | for (uint32_t i = 1; i < inst->NumInOperands(); i += 2) { |
| 362 | uint32_t type_id = inst->GetSingleWordInOperand(i); |
| 363 | uint32_t member_idx = inst->GetSingleWordInOperand(i + 1); |
| 364 | uint32_t new_member_idx = GetNewMemberIndex(type_id, member_idx); |
| 365 | |
| 366 | if (new_member_idx == kRemovedMember) { |
| 367 | modified = true; |
| 368 | continue; |
| 369 | } |
| 370 | |
| 371 | new_operands.emplace_back(inst->GetOperand(i)); |
| 372 | if (new_member_idx != member_idx) { |
| 373 | new_operands.emplace_back( |
| 374 | Operand({SPV_OPERAND_TYPE_LITERAL_INTEGER, {new_member_idx}})); |
| 375 | modified = true; |
| 376 | } else { |
| 377 | new_operands.emplace_back(inst->GetOperand(i + 1)); |
| 378 | } |
| 379 | } |
| 380 | |
| 381 | if (!modified) { |
| 382 | return false; |
| 383 | } |
| 384 | |
| 385 | if (new_operands.size() == 1) { |
| 386 | context()->KillInst(inst); |
| 387 | return true; |
| 388 | } |
| 389 | |
| 390 | inst->SetInOperands(std::move(new_operands)); |
| 391 | context()->UpdateDefUse(inst); |
| 392 | return true; |
| 393 | } |
| 394 | |
| 395 | bool EliminateDeadMembersPass::UpdateConstantComposite(Instruction* inst) { |
| 396 | assert(inst->opcode() == SpvOpConstantComposite || |
| 397 | inst->opcode() == SpvOpCompositeConstruct); |
| 398 | uint32_t type_id = inst->type_id(); |
| 399 | |
| 400 | bool modified = false; |
| 401 | Instruction::OperandList new_operands; |
| 402 | for (uint32_t i = 0; i < inst->NumInOperands(); ++i) { |
| 403 | uint32_t new_idx = GetNewMemberIndex(type_id, i); |
| 404 | if (new_idx == kRemovedMember) { |
| 405 | modified = true; |
| 406 | } else { |
| 407 | new_operands.emplace_back(inst->GetInOperand(i)); |
| 408 | } |
| 409 | } |
| 410 | inst->SetInOperands(std::move(new_operands)); |
| 411 | context()->UpdateDefUse(inst); |
| 412 | return modified; |
| 413 | } |
| 414 | |
| 415 | bool EliminateDeadMembersPass::UpdateAccessChain(Instruction* inst) { |
| 416 | assert(inst->opcode() == SpvOpAccessChain || |
| 417 | inst->opcode() == SpvOpInBoundsAccessChain || |
| 418 | inst->opcode() == SpvOpPtrAccessChain || |
| 419 | inst->opcode() == SpvOpInBoundsPtrAccessChain); |
| 420 | |
| 421 | uint32_t pointer_id = inst->GetSingleWordInOperand(0); |
| 422 | Instruction* pointer_inst = get_def_use_mgr()->GetDef(pointer_id); |
| 423 | uint32_t pointer_type_id = pointer_inst->type_id(); |
| 424 | Instruction* pointer_type_inst = get_def_use_mgr()->GetDef(pointer_type_id); |
| 425 | uint32_t type_id = pointer_type_inst->GetSingleWordInOperand(1); |
| 426 | |
| 427 | analysis::ConstantManager* const_mgr = context()->get_constant_mgr(); |
| 428 | Instruction::OperandList new_operands; |
| 429 | bool modified = false; |
| 430 | new_operands.emplace_back(inst->GetInOperand(0)); |
| 431 | |
| 432 | // For pointer access chains we want to copy the element operand. |
| 433 | if (inst->opcode() == SpvOpPtrAccessChain || |
| 434 | inst->opcode() == SpvOpInBoundsPtrAccessChain) { |
| 435 | new_operands.emplace_back(inst->GetInOperand(1)); |
| 436 | } |
| 437 | |
| 438 | for (uint32_t i = static_cast<uint32_t>(new_operands.size()); |
| 439 | i < inst->NumInOperands(); ++i) { |
| 440 | Instruction* type_inst = get_def_use_mgr()->GetDef(type_id); |
| 441 | switch (type_inst->opcode()) { |
| 442 | case SpvOpTypeStruct: { |
| 443 | const analysis::IntConstant* member_idx = |
| 444 | const_mgr->FindDeclaredConstant(inst->GetSingleWordInOperand(i)) |
| 445 | ->AsIntConstant(); |
| 446 | assert(member_idx); |
| 447 | uint32_t orig_member_idx; |
| 448 | if (member_idx->type()->AsInteger()->width() == 32) { |
| 449 | orig_member_idx = member_idx->GetU32(); |
| 450 | } else { |
| 451 | orig_member_idx = static_cast<uint32_t>(member_idx->GetU64()); |
| 452 | } |
| 453 | uint32_t new_member_idx = GetNewMemberIndex(type_id, orig_member_idx); |
| 454 | assert(new_member_idx != kRemovedMember); |
| 455 | if (orig_member_idx != new_member_idx) { |
| 456 | InstructionBuilder ir_builder( |
| 457 | context(), inst, |
| 458 | IRContext::kAnalysisDefUse | |
| 459 | IRContext::kAnalysisInstrToBlockMapping); |
| 460 | uint32_t const_id = |
| 461 | ir_builder.GetUintConstant(new_member_idx)->result_id(); |
| 462 | new_operands.emplace_back(Operand({SPV_OPERAND_TYPE_ID, {const_id}})); |
| 463 | modified = true; |
| 464 | } else { |
| 465 | new_operands.emplace_back(inst->GetInOperand(i)); |
| 466 | } |
| 467 | // The type will have already been rewritten, so use the new member |
| 468 | // index. |
| 469 | type_id = type_inst->GetSingleWordInOperand(new_member_idx); |
| 470 | } break; |
| 471 | case SpvOpTypeArray: |
| 472 | case SpvOpTypeRuntimeArray: |
| 473 | case SpvOpTypeVector: |
| 474 | case SpvOpTypeMatrix: |
| 475 | new_operands.emplace_back(inst->GetInOperand(i)); |
| 476 | type_id = type_inst->GetSingleWordInOperand(0); |
| 477 | break; |
| 478 | default: |
| 479 | assert(false); |
| 480 | break; |
| 481 | } |
| 482 | } |
| 483 | |
| 484 | if (!modified) { |
| 485 | return false; |
| 486 | } |
| 487 | inst->SetInOperands(std::move(new_operands)); |
| 488 | context()->UpdateDefUse(inst); |
| 489 | return true; |
| 490 | } |
| 491 | |
| 492 | uint32_t EliminateDeadMembersPass::GetNewMemberIndex(uint32_t type_id, |
| 493 | uint32_t member_idx) { |
| 494 | auto live_members = used_members_.find(type_id); |
| 495 | if (live_members == used_members_.end()) { |
| 496 | return member_idx; |
| 497 | } |
| 498 | |
| 499 | auto current_member = live_members->second.find(member_idx); |
| 500 | if (current_member == live_members->second.end()) { |
| 501 | return kRemovedMember; |
| 502 | } |
| 503 | |
| 504 | return static_cast<uint32_t>( |
| 505 | std::distance(live_members->second.begin(), current_member)); |
| 506 | } |
| 507 | |
| 508 | bool EliminateDeadMembersPass::(Instruction* inst) { |
| 509 | uint32_t object_id = inst->GetSingleWordInOperand(0); |
| 510 | Instruction* object_inst = get_def_use_mgr()->GetDef(object_id); |
| 511 | uint32_t type_id = object_inst->type_id(); |
| 512 | |
| 513 | Instruction::OperandList new_operands; |
| 514 | bool modified = false; |
| 515 | new_operands.emplace_back(inst->GetInOperand(0)); |
| 516 | for (uint32_t i = 1; i < inst->NumInOperands(); ++i) { |
| 517 | uint32_t member_idx = inst->GetSingleWordInOperand(i); |
| 518 | uint32_t new_member_idx = GetNewMemberIndex(type_id, member_idx); |
| 519 | assert(new_member_idx != kRemovedMember); |
| 520 | if (member_idx != new_member_idx) { |
| 521 | modified = true; |
| 522 | } |
| 523 | new_operands.emplace_back( |
| 524 | Operand({SPV_OPERAND_TYPE_LITERAL_INTEGER, {new_member_idx}})); |
| 525 | |
| 526 | Instruction* type_inst = get_def_use_mgr()->GetDef(type_id); |
| 527 | switch (type_inst->opcode()) { |
| 528 | case SpvOpTypeStruct: |
| 529 | assert(i != 1 || (inst->opcode() != SpvOpPtrAccessChain && |
| 530 | inst->opcode() != SpvOpInBoundsPtrAccessChain)); |
| 531 | // The type will have already been rewriten, so use the new member |
| 532 | // index. |
| 533 | type_id = type_inst->GetSingleWordInOperand(new_member_idx); |
| 534 | break; |
| 535 | case SpvOpTypeArray: |
| 536 | case SpvOpTypeRuntimeArray: |
| 537 | case SpvOpTypeVector: |
| 538 | case SpvOpTypeMatrix: |
| 539 | type_id = type_inst->GetSingleWordInOperand(0); |
| 540 | break; |
| 541 | default: |
| 542 | assert(false); |
| 543 | } |
| 544 | } |
| 545 | |
| 546 | if (!modified) { |
| 547 | return false; |
| 548 | } |
| 549 | inst->SetInOperands(std::move(new_operands)); |
| 550 | context()->UpdateDefUse(inst); |
| 551 | return true; |
| 552 | } |
| 553 | |
| 554 | bool EliminateDeadMembersPass::UpdateCompositeInsert(Instruction* inst) { |
| 555 | uint32_t composite_id = inst->GetSingleWordInOperand(1); |
| 556 | Instruction* composite_inst = get_def_use_mgr()->GetDef(composite_id); |
| 557 | uint32_t type_id = composite_inst->type_id(); |
| 558 | |
| 559 | Instruction::OperandList new_operands; |
| 560 | bool modified = false; |
| 561 | new_operands.emplace_back(inst->GetInOperand(0)); |
| 562 | new_operands.emplace_back(inst->GetInOperand(1)); |
| 563 | for (uint32_t i = 2; i < inst->NumInOperands(); ++i) { |
| 564 | uint32_t member_idx = inst->GetSingleWordInOperand(i); |
| 565 | uint32_t new_member_idx = GetNewMemberIndex(type_id, member_idx); |
| 566 | if (new_member_idx == kRemovedMember) { |
| 567 | context()->KillInst(inst); |
| 568 | return true; |
| 569 | } |
| 570 | |
| 571 | if (member_idx != new_member_idx) { |
| 572 | modified = true; |
| 573 | } |
| 574 | new_operands.emplace_back( |
| 575 | Operand({SPV_OPERAND_TYPE_LITERAL_INTEGER, {new_member_idx}})); |
| 576 | |
| 577 | Instruction* type_inst = get_def_use_mgr()->GetDef(type_id); |
| 578 | switch (type_inst->opcode()) { |
| 579 | case SpvOpTypeStruct: |
| 580 | // The type will have already been rewritten, so use the new member |
| 581 | // index. |
| 582 | type_id = type_inst->GetSingleWordInOperand(new_member_idx); |
| 583 | break; |
| 584 | case SpvOpTypeArray: |
| 585 | case SpvOpTypeRuntimeArray: |
| 586 | case SpvOpTypeVector: |
| 587 | case SpvOpTypeMatrix: |
| 588 | type_id = type_inst->GetSingleWordInOperand(0); |
| 589 | break; |
| 590 | default: |
| 591 | assert(false); |
| 592 | } |
| 593 | } |
| 594 | |
| 595 | if (!modified) { |
| 596 | return false; |
| 597 | } |
| 598 | inst->SetInOperands(std::move(new_operands)); |
| 599 | context()->UpdateDefUse(inst); |
| 600 | return true; |
| 601 | } |
| 602 | |
| 603 | bool EliminateDeadMembersPass::UpdateOpArrayLength(Instruction* inst) { |
| 604 | uint32_t struct_id = inst->GetSingleWordInOperand(0); |
| 605 | Instruction* struct_inst = get_def_use_mgr()->GetDef(struct_id); |
| 606 | uint32_t pointer_type_id = struct_inst->type_id(); |
| 607 | Instruction* pointer_type_inst = get_def_use_mgr()->GetDef(pointer_type_id); |
| 608 | uint32_t type_id = pointer_type_inst->GetSingleWordInOperand(1); |
| 609 | |
| 610 | uint32_t member_idx = inst->GetSingleWordInOperand(1); |
| 611 | uint32_t new_member_idx = GetNewMemberIndex(type_id, member_idx); |
| 612 | assert(new_member_idx != kRemovedMember); |
| 613 | |
| 614 | if (member_idx == new_member_idx) { |
| 615 | return false; |
| 616 | } |
| 617 | |
| 618 | inst->SetInOperand(1, {new_member_idx}); |
| 619 | context()->UpdateDefUse(inst); |
| 620 | return true; |
| 621 | } |
| 622 | |
| 623 | void EliminateDeadMembersPass::MarkStructOperandsAsFullyUsed( |
| 624 | const Instruction* inst) { |
| 625 | if (inst->type_id() != 0) { |
| 626 | MarkTypeAsFullyUsed(inst->type_id()); |
| 627 | } |
| 628 | |
| 629 | inst->ForEachInId([this](const uint32_t* id) { |
| 630 | Instruction* instruction = get_def_use_mgr()->GetDef(*id); |
| 631 | if (instruction->type_id() != 0) { |
| 632 | MarkTypeAsFullyUsed(instruction->type_id()); |
| 633 | } |
| 634 | }); |
| 635 | } |
| 636 | } // namespace opt |
| 637 | } // namespace spvtools |
| 638 | |