1 /**
2 * Copyright (c) 2021-2022 Huawei Device Co., Ltd.
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
16 #include "split_resolver.h"
17 #include "compiler/optimizer/ir/basicblock.h"
18 #include "compiler/optimizer/analysis/dominators_tree.h"
19 #include "compiler/optimizer/analysis/loop_analyzer.h"
20 #include "compiler/optimizer/optimizations/regalloc/reg_alloc_base.h"
21
22 namespace panda::compiler {
23
Run()24 void SplitResolver::Run()
25 {
26 for (auto interval : liveness_->GetLifeIntervals()) {
27 if (interval->GetSibling() == nullptr) {
28 continue;
29 }
30 ASSERT(!interval->IsPhysical());
31
32 // Connect siblings within the same block.
33 ConnectSiblings(interval);
34 }
35 // Resolve locations between basic blocks.
36 for (auto block : liveness_->GetLinearizedBlocks()) {
37 ASSERT(block != nullptr);
38 if (!block->IsEndBlock()) {
39 ProcessBlock(block);
40 }
41 }
42 }
43
ConnectSiblings(LifeIntervals * interval)44 void SplitResolver::ConnectSiblings(LifeIntervals *interval)
45 {
46 for (auto prev = interval, curr = interval->GetSibling(); curr != nullptr; prev = curr, curr = curr->GetSibling()) {
47 if (prev->GetEnd() != curr->GetBegin() || prev->GetLocation() == curr->GetLocation() ||
48 curr->GetLocation().IsConstant()) {
49 continue;
50 }
51 COMPILER_LOG(DEBUG, SPLIT_RESOLVER)
52 << "Connect siblings for inst v" << interval->GetInst()->GetId() << " at point: " << curr->GetBegin();
53
54 ASSERT(curr->IsSplitSibling());
55 auto inst = liveness_->GetInstByLifeNumber(curr->GetBegin() + 1U);
56 // inst == nullptr means that life number corresponds to some PHI instruction (== corresponds to start of
57 // some block), so the SpillFill should be placed at the end of predecessor block.
58 if (inst == nullptr) {
59 continue;
60 }
61
62 auto spill_fill = CreateSpillFillForSiblings(inst);
63 ConnectIntervals(spill_fill, prev, curr);
64 }
65 }
66
ProcessBlock(BasicBlock * block)67 void SplitResolver::ProcessBlock(BasicBlock *block)
68 {
69 auto succ_begin = liveness_->GetBlockLiveRange(block).GetBegin();
70 for (auto interval : liveness_->GetLifeIntervals()) {
71 // PHI and its inputs can be considered as one interval, which was split,
72 // so that the logic is equivalent to the logic of connecting split intervals
73 if (interval->GetBegin() == succ_begin) {
74 auto phi = interval->GetInst();
75 if (!phi->IsPhi() || phi->GetDstReg() == ACC_REG_ID) {
76 continue;
77 }
78 ASSERT(phi->IsPhi());
79 for (size_t i = 0; i < phi->GetInputsCount(); i++) {
80 auto input_inst = phi->GetDataFlowInput(i);
81 auto input_bb = phi->CastToPhi()->GetPhiInputBb(i);
82 auto input_liveness = liveness_->GetInstLifeIntervals(input_inst);
83 ConnectSpiltFromPredBlock(input_bb, input_liveness, block, interval);
84 }
85 continue;
86 }
87
88 // Skip not-splitted instruction
89 if (interval->GetSibling() == nullptr) {
90 continue;
91 }
92 ASSERT(!interval->IsPhysical());
93 auto succ_split = interval->FindSiblingAt(succ_begin);
94 if (succ_split == nullptr || succ_split->GetLocation().IsConstant() || !succ_split->SplitCover(succ_begin)) {
95 continue;
96 }
97 for (auto pred : block->GetPredsBlocks()) {
98 ConnectSpiltFromPredBlock(pred, interval, block, succ_split);
99 }
100 }
101 }
102
ConnectSpiltFromPredBlock(BasicBlock * src_bb,LifeIntervals * src_interval,BasicBlock * target_bb,LifeIntervals * target_split)103 void SplitResolver::ConnectSpiltFromPredBlock(BasicBlock *src_bb, LifeIntervals *src_interval, BasicBlock *target_bb,
104 LifeIntervals *target_split)
105 {
106 BasicBlock *resolver {nullptr};
107 // It's a resolver block inserted during register allocation
108 if (src_bb->GetId() >= liveness_->GetBlocksCount()) {
109 ASSERT(src_bb->GetSuccsBlocks().size() == 1 && src_bb->GetPredsBlocks().size() == 1);
110 resolver = src_bb;
111 src_bb = src_bb->GetPredecessor(0);
112 }
113 auto src_liveness = liveness_->GetBlockLiveRange(src_bb);
114 // Find sibling at the 'end - LIFE_NUMBER_GAP' position to connect siblings that were split at the end of the
115 // 'src_bb'
116 auto src_split = src_interval->FindSiblingAt(src_liveness.GetEnd() - 1U);
117 // Instruction was not defined at predecessor or has the same location there
118 if (src_split == nullptr || src_split->GetLocation() == target_split->GetLocation()) {
119 return;
120 }
121
122 COMPILER_LOG(DEBUG, SPLIT_RESOLVER) << "Resolve split move for inst v" << src_interval->GetInst()->GetId()
123 << " between blocks: BB" << src_bb->GetId() << " -> BB" << target_bb->GetId();
124
125 if (resolver == nullptr) {
126 if (src_bb->GetSuccsBlocks().size() == 1U) {
127 resolver = src_bb;
128 } else {
129 // Get rid of critical edge by inserting a new block and append SpillFill into it.
130 resolver = src_bb->InsertNewBlockToSuccEdge(target_bb);
131 // Fix Dominators info
132 auto &dom_tree = graph_->GetAnalysis<DominatorsTree>();
133 dom_tree.UpdateAfterResolverInsertion(src_bb, target_bb, resolver);
134 graph_->InvalidateAnalysis<LoopAnalyzer>();
135 }
136 }
137 auto spill_fill = CreateSpillFillForSplitMove(resolver);
138 ConnectIntervals(spill_fill, src_split, target_split);
139 }
140
CreateSpillFillForSplitMove(BasicBlock * source_block)141 SpillFillInst *SplitResolver::CreateSpillFillForSplitMove(BasicBlock *source_block)
142 {
143 auto iter = source_block->InstsReverse().begin();
144 while (iter != iter.end() && (*iter)->IsControlFlow() && !(*iter)->IsPhi()) {
145 ++iter;
146 }
147
148 if (iter == iter.end()) {
149 auto spill_fill = graph_->CreateInstSpillFill();
150 spill_fill->SetSpillFillType(SpillFillType::SPLIT_MOVE);
151 source_block->PrependInst(spill_fill);
152 return spill_fill;
153 }
154
155 auto inst = *iter;
156 // Don't reuse CONNECT_SPLIT_SIBLINGS SpillFills to avoid insertion of two opposite spill fill
157 // moves in case when an interval was split after last basic block's instruction and then
158 // it should moved back to original location for successor block:
159 //
160 // BB0:
161 // 2. Add v0, v1 -> r0
162 // BB1:
163 // 4. Sub v0, v2 -> ...
164 // <Split 2's interval and spill to stack slot s0>
165 // <jump to BB1>
166 //
167 // Without CONNECT_SPLIT_SIBLINGS single SpillFillInst would be inserted at the end of BB1
168 // with following moves: r0 -> s0 (to connect siblings), s0 -> r0 (as 2 has different location
169 // at the beginning of BB1). RegAllocResolver may not handle such moves so it should be
170 // avoided.
171 if (inst->IsSpillFill() && !Is<SpillFillType::CONNECT_SPLIT_SIBLINGS>(inst)) {
172 return inst->CastToSpillFill();
173 }
174
175 ASSERT(!inst->IsPhi());
176 auto spill_fill = graph_->CreateInstSpillFill();
177 spill_fill->SetSpillFillType(SpillFillType::SPLIT_MOVE);
178 source_block->InsertAfter(spill_fill, inst);
179 return spill_fill;
180 }
181
CreateSpillFillForSiblings(Inst * connect_at)182 SpillFillInst *SplitResolver::CreateSpillFillForSiblings(Inst *connect_at)
183 {
184 // Try to reuse existing CONNECT_SPLIT_SIBLINGS spill-fill-inst
185 auto prev = connect_at->GetPrev();
186 while (prev != nullptr && prev->IsSpillFill()) {
187 if (Is<SpillFillType::CONNECT_SPLIT_SIBLINGS>(prev)) {
188 return prev->CastToSpillFill();
189 }
190 ASSERT(Is<SpillFillType::INPUT_FILL>(prev));
191 connect_at = prev;
192 prev = prev->GetPrev();
193 }
194 auto spill_fill = graph_->CreateInstSpillFill();
195 spill_fill->SetSpillFillType(SpillFillType::CONNECT_SPLIT_SIBLINGS);
196 connect_at->InsertBefore(spill_fill);
197 return spill_fill;
198 }
199
200 } // namespace panda::compiler
201