| 71 | } |
| 72 | |
| 73 | BasicBlock* CodeSinkingPass::FindNewBasicBlockFor(Instruction* inst) { |
| 74 | assert(inst->result_id() != 0 && "Instruction should have a result."); |
| 75 | BasicBlock* original_bb = context()->get_instr_block(inst); |
| 76 | BasicBlock* bb = original_bb; |
| 77 | |
| 78 | std::unordered_set<uint32_t> bbs_with_uses; |
| 79 | get_def_use_mgr()->ForEachUse( |
| 80 | inst, [&bbs_with_uses, this](Instruction* use, uint32_t idx) { |
| 81 | if (use->opcode() != spv::Op::OpPhi) { |
| 82 | BasicBlock* use_bb = context()->get_instr_block(use); |
| 83 | if (use_bb) { |
| 84 | bbs_with_uses.insert(use_bb->id()); |
| 85 | } |
| 86 | } else { |
| 87 | bbs_with_uses.insert(use->GetSingleWordOperand(idx + 1)); |
| 88 | } |
| 89 | }); |
| 90 | |
| 91 | while (true) { |
| 92 | // If |inst| is used in |bb|, then |inst| cannot be moved any further. |
| 93 | if (bbs_with_uses.count(bb->id())) { |
| 94 | break; |
| 95 | } |
| 96 | |
| 97 | // If |bb| has one successor (succ_bb), and |bb| is the only predecessor |
| 98 | // of succ_bb, then |inst| can be moved to succ_bb. If succ_bb, has move |
| 99 | // then one predecessor, then moving |inst| into succ_bb could cause it to |
| 100 | // be executed more often, so the search has to stop. |
| 101 | if (bb->terminator()->opcode() == spv::Op::OpBranch) { |
| 102 | uint32_t succ_bb_id = bb->terminator()->GetSingleWordInOperand(0); |
| 103 | if (cfg()->preds(succ_bb_id).size() == 1) { |
| 104 | bb = context()->get_instr_block(succ_bb_id); |
| 105 | continue; |
| 106 | } else { |
| 107 | break; |
| 108 | } |
| 109 | } |
| 110 | |
| 111 | // The remaining checks need to know the merge node. If there is no merge |
| 112 | // instruction or an OpLoopMerge, then it is a break or continue. We could |
| 113 | // figure it out, but not worth doing it now. |
| 114 | Instruction* merge_inst = bb->GetMergeInst(); |
| 115 | if (merge_inst == nullptr || |
| 116 | merge_inst->opcode() != spv::Op::OpSelectionMerge) { |
| 117 | break; |
| 118 | } |
| 119 | |
| 120 | // Check all of the successors of |bb| it see which lead to a use of |inst| |
| 121 | // before reaching the merge node. |
| 122 | bool used_in_multiple_blocks = false; |
| 123 | uint32_t bb_used_in = 0; |
| 124 | bb->ForEachSuccessorLabel([this, bb, &bb_used_in, &used_in_multiple_blocks, |
| 125 | &bbs_with_uses](uint32_t* succ_bb_id) { |
| 126 | if (IntersectsPath(*succ_bb_id, bb->MergeBlockIdIfAny(), bbs_with_uses)) { |
| 127 | if (bb_used_in == 0) { |
| 128 | bb_used_in = *succ_bb_id; |
| 129 | } else { |
| 130 | used_in_multiple_blocks = true; |
nothing calls this directly
no test coverage detected