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 "optimizer/ir/graph.h"
17 #include "reg_alloc_base.h"
18 #include "spill_fills_resolver.h"
19
20 namespace panda::compiler {
SpillFillsResolver(Graph * graph)21 SpillFillsResolver::SpillFillsResolver(Graph *graph)
22 : SpillFillsResolver(graph, INVALID_REG, MAX_NUM_REGS, MAX_NUM_VREGS)
23 {
24 }
25
SpillFillsResolver(Graph * graph,Register resolver,size_t regs_count,size_t vregs_count)26 SpillFillsResolver::SpillFillsResolver(Graph *graph, Register resolver, size_t regs_count, size_t vregs_count)
27 : graph_(graph),
28 moves_table_(graph->GetLocalAllocator()->Adapter()),
29 loads_count_(graph->GetLocalAllocator()->Adapter()),
30 pre_moves_(graph->GetLocalAllocator()->Adapter()),
31 post_moves_(graph->GetLocalAllocator()->Adapter()),
32 resolver_(resolver),
33 VREGS_TABLE_OFFSET(regs_count),
34 SLOTS_TABLE_OFFSET(VREGS_TABLE_OFFSET + vregs_count),
35 PARAMETER_SLOTS_OFFSET(SLOTS_TABLE_OFFSET + graph->GetStackSlotsCount()),
36 LOCATIONS_COUNT(PARAMETER_SLOTS_OFFSET + graph->GetParametersSlotsCount()),
37 reg_write_(graph->GetLocalAllocator()->Adapter()),
38 stack_write_(graph->GetLocalAllocator()->Adapter())
39 {
40 ASSERT_PRINT(std::numeric_limits<LocationIndex>::max() > LOCATIONS_COUNT,
41 "Summary amount of registers and slots overflow. Change LocationIndex type");
42
43 reg_write_.resize(SLOTS_TABLE_OFFSET);
44 stack_write_.resize(LOCATIONS_COUNT - SLOTS_TABLE_OFFSET);
45 moves_table_.resize(LOCATIONS_COUNT);
46 loads_count_.resize(LOCATIONS_COUNT);
47 }
48
Run()49 void SpillFillsResolver::Run()
50 {
51 VisitGraph();
52 }
53
Resolve(SpillFillInst * spill_fill_inst)54 void SpillFillsResolver::Resolve(SpillFillInst *spill_fill_inst)
55 {
56 CollectSpillFillsData(spill_fill_inst);
57 Reorder(spill_fill_inst);
58 }
59
ResolveIfRequired(SpillFillInst * spill_fill_inst)60 void SpillFillsResolver::ResolveIfRequired(SpillFillInst *spill_fill_inst)
61 {
62 if (NeedToResolve(spill_fill_inst->GetSpillFills())) {
63 Resolve(spill_fill_inst);
64 }
65 }
66
GetGraph() const67 Graph *SpillFillsResolver::GetGraph() const
68 {
69 return graph_;
70 }
71
GetBlocksToVisit() const72 const ArenaVector<BasicBlock *> &SpillFillsResolver::GetBlocksToVisit() const
73 {
74 return GetGraph()->GetBlocksRPO();
75 }
76
VisitSpillFill(GraphVisitor * visitor,Inst * inst)77 void SpillFillsResolver::VisitSpillFill(GraphVisitor *visitor, Inst *inst)
78 {
79 auto resolver = static_cast<SpillFillsResolver *>(visitor);
80 auto spill_fill_inst = inst->CastToSpillFill();
81 if (resolver->NeedToResolve(spill_fill_inst->GetSpillFills())) {
82 resolver->Resolve(spill_fill_inst);
83 } else {
84 #ifndef NDEBUG
85 // Verify spill_fill_inst
86 resolver->CollectSpillFillsData(spill_fill_inst);
87 #endif
88 }
89 }
90
MarkRegWrite(Location location,ArenaVector<bool> * reg_write,bool paired,size_t offset)91 static void MarkRegWrite(Location location, ArenaVector<bool> *reg_write, bool paired, size_t offset)
92 {
93 auto reg = location.IsFpRegister() ? location.GetValue() + offset : location.GetValue();
94 ASSERT(reg < reg_write->size());
95 (*reg_write)[reg] = true;
96 if (paired) {
97 (*reg_write)[reg + 1] = true;
98 }
99 }
100
IsRegWrite(Location location,ArenaVector<bool> * reg_write,bool paired,size_t offset)101 static bool IsRegWrite(Location location, ArenaVector<bool> *reg_write, bool paired, size_t offset)
102 {
103 auto reg = location.IsFpRegister() ? location.GetValue() + offset : location.GetValue();
104 ASSERT(reg < reg_write->size());
105 return (*reg_write)[reg] || (paired && (*reg_write)[reg + 1]);
106 }
107
MarkStackWrite(Location location,ArenaVector<bool> * stack_write,size_t offset)108 static void MarkStackWrite(Location location, ArenaVector<bool> *stack_write, size_t offset)
109 {
110 auto slot = location.IsStackParameter() ? location.GetValue() + offset : location.GetValue();
111 ASSERT(slot < stack_write->size());
112 (*stack_write)[slot] = true;
113 }
114
IsStackWrite(Location location,ArenaVector<bool> * stack_write,size_t offset)115 static bool IsStackWrite(Location location, ArenaVector<bool> *stack_write, size_t offset)
116 {
117 auto slot = location.IsStackParameter() ? location.GetValue() + offset : location.GetValue();
118 ASSERT(slot < stack_write->size());
119 return (*stack_write)[slot];
120 }
121
122 /*
123 * Find if there are conflicts between reading/writing registers
124 */
NeedToResolve(const ArenaVector<SpillFillData> & spill_fills)125 bool SpillFillsResolver::NeedToResolve(const ArenaVector<SpillFillData> &spill_fills)
126 {
127 if (spill_fills.size() < 2U) {
128 return false;
129 }
130
131 std::fill(reg_write_.begin(), reg_write_.end(), false);
132 std::fill(stack_write_.begin(), stack_write_.end(), false);
133 auto param_slot_offset = PARAMETER_SLOTS_OFFSET - SLOTS_TABLE_OFFSET;
134
135 for (const auto &sf : spill_fills) {
136 if (sf.DstType() == sf.SrcType() && sf.DstValue() == sf.SrcValue()) {
137 continue;
138 }
139
140 bool paired = IsPairedReg(GetGraph()->GetArch(), sf.GetType());
141 // set registers, that are rewrited
142 if (sf.GetDst().IsAnyRegister()) {
143 MarkRegWrite(sf.GetDst(), ®_write_, paired, VREGS_TABLE_OFFSET);
144 }
145
146 // if register was rewrited previously - that is a conflict
147 if (sf.GetSrc().IsAnyRegister()) {
148 if (IsRegWrite(sf.GetSrc(), ®_write_, paired, VREGS_TABLE_OFFSET)) {
149 return true;
150 }
151 }
152
153 // set stack slots, that are rewrited
154 if (sf.DstType() == LocationType::STACK || sf.DstType() == LocationType::STACK_PARAMETER) {
155 MarkStackWrite(sf.GetDst(), &stack_write_, param_slot_offset);
156 }
157
158 // if stack slot was rewrited previously - that is a conflict
159 if (sf.SrcType() == LocationType::STACK || sf.SrcType() == LocationType::STACK_PARAMETER) {
160 if (IsStackWrite(sf.GetSrc(), &stack_write_, param_slot_offset)) {
161 return true;
162 }
163 }
164 }
165 return false;
166 }
167
168 /*
169 * Parse spill-fills and populate `moves_table_` and `loads_count_`
170 */
CollectSpillFillsData(SpillFillInst * spill_fill_inst)171 void SpillFillsResolver::CollectSpillFillsData(SpillFillInst *spill_fill_inst)
172 {
173 std::fill(moves_table_.begin(), moves_table_.end(), MoveInfo {INVALID_LOCATION_INDEX, DataType::NO_TYPE});
174 std::fill(loads_count_.begin(), loads_count_.end(), 0);
175 pre_moves_.clear();
176 post_moves_.clear();
177
178 for (const auto &sf : spill_fill_inst->GetSpillFills()) {
179 if (sf.DstType() == sf.SrcType() && sf.DstValue() == sf.SrcValue()) {
180 continue;
181 }
182
183 if (sf.SrcType() == LocationType::IMMEDIATE) {
184 post_moves_.push_back(sf);
185 continue;
186 }
187
188 if (sf.DstType() == LocationType::STACK_ARGUMENT) {
189 pre_moves_.push_back(sf);
190 continue;
191 }
192
193 auto src_index = Map(sf.GetSrc());
194 auto dest_index = Map(sf.GetDst());
195 ASSERT(dest_index < LOCATIONS_COUNT);
196 ASSERT(src_index < LOCATIONS_COUNT);
197 ASSERT(moves_table_[dest_index].src == INVALID_LOCATION_INDEX);
198 moves_table_[dest_index].src = src_index;
199 moves_table_[dest_index].reg_type = sf.GetType();
200 loads_count_[src_index]++;
201 }
202 }
203
204 /**
205 * Iterate over dst-regs and add a chain of moves, containing this register, in two ways:
206 * - dst-reg is NOT used as src-reg in the other spill-fills
207 * - dst-reg is in the cyclically dependent chain of moves: (R1->R2, R2->R1)
208 */
Reorder(SpillFillInst * spill_fill_inst)209 void SpillFillsResolver::Reorder(SpillFillInst *spill_fill_inst)
210 {
211 spill_fill_inst->ClearSpillFills();
212 ArenaVector<LocationIndex> remap(LOCATIONS_COUNT, INVALID_LOCATION_INDEX,
213 GetGraph()->GetLocalAllocator()->Adapter());
214
215 for (auto &sf : pre_moves_) {
216 spill_fill_inst->AddSpillFill(sf);
217 }
218
219 // First we process chains which have tails
220 for (LocationIndex dst_reg = 0; dst_reg < static_cast<LocationIndex>(LOCATIONS_COUNT); ++dst_reg) {
221 if (loads_count_[dst_reg] == 0 && moves_table_[dst_reg].src != INVALID_LOCATION_INDEX) {
222 AddMovesChain<false>(dst_reg, &remap, spill_fill_inst);
223 }
224 }
225
226 // And than only loops should left
227 for (LocationIndex dst_reg = 0; dst_reg < static_cast<LocationIndex>(LOCATIONS_COUNT); ++dst_reg) {
228 if (moves_table_[dst_reg].src != INVALID_LOCATION_INDEX) {
229 ASSERT(loads_count_[dst_reg] > 0);
230 auto temp_reg = CheckAndResolveCyclicDependency(dst_reg);
231 AddMovesChain<true>(temp_reg, &remap, spill_fill_inst);
232 }
233 }
234
235 for (auto &sf : post_moves_) {
236 spill_fill_inst->AddSpillFill(sf);
237 }
238 }
239
240 /**
241 * Check if the chain of moves is cyclically dependent (R3->R1, R2->R3, R1->R2) and resolve it with a `temp-reg`:
242 * (R1->temp, R3->R1, R2->R3, temp->R2)
243 */
244
CheckAndResolveCyclicDependency(LocationIndex dst_first)245 SpillFillsResolver::LocationIndex SpillFillsResolver::CheckAndResolveCyclicDependency(LocationIndex dst_first)
246 {
247 auto dst_reg = dst_first;
248 auto src_reg = moves_table_[dst_reg].src;
249
250 [[maybe_unused]] size_t moves_counter = 0;
251 while (src_reg != dst_first) {
252 dst_reg = src_reg;
253 src_reg = moves_table_[dst_reg].src;
254 ASSERT(src_reg != INVALID_LOCATION_INDEX);
255 ASSERT_PRINT(moves_counter++ < moves_table_.size(), "Unresolved cyclic dependency");
256 }
257
258 auto resolver = GetResolver(moves_table_[dst_first].reg_type);
259 moves_table_[resolver].src = dst_first;
260 moves_table_[dst_reg].src = resolver;
261 loads_count_[resolver]++;
262 moves_table_[resolver].reg_type = moves_table_[dst_reg].reg_type;
263 return resolver;
264 }
265
266 /**
267 * Add a chain of `spill_fills`, which starts with a `dst` register:
268 * [src_0 -> dst],
269 * [src_1 -> src_0],
270 * [src_2 -> src_1], etc
271 * A chain finishes with an `INVALID_LOCATION_INDEX`
272 *
273 * After chain building remap the remaining moves with a new sources
274 */
275 template <bool is_cyclic>
AddMovesChain(LocationIndex dst,ArenaVector<LocationIndex> * remap,SpillFillInst * spill_fill_inst)276 void SpillFillsResolver::AddMovesChain(LocationIndex dst, ArenaVector<LocationIndex> *remap,
277 SpillFillInst *spill_fill_inst)
278 {
279 [[maybe_unused]] auto first_dst = dst;
280 ASSERT(first_dst != INVALID_LOCATION_INDEX);
281 ASSERT(remap->at(first_dst) == INVALID_LOCATION_INDEX);
282
283 auto src = moves_table_[dst].src;
284 [[maybe_unused]] auto first_src = src;
285 ASSERT(first_src != INVALID_LOCATION_INDEX);
286
287 // Make a chain of spill-fills
288 while (src != INVALID_LOCATION_INDEX) {
289 auto re = remap->at(src);
290 auto type = moves_table_[dst].reg_type;
291 if (re == INVALID_LOCATION_INDEX) {
292 spill_fill_inst->AddSpillFill(ToLocation(src), ToLocation(dst), type);
293 remap->at(src) = dst;
294 } else {
295 spill_fill_inst->AddSpillFill(ToLocation(re), ToLocation(dst), type);
296 }
297 ASSERT(loads_count_[src] > 0);
298 loads_count_[src]--;
299 moves_table_[dst].src = INVALID_LOCATION_INDEX;
300 dst = src;
301 src = moves_table_[dst].src;
302 }
303
304 // Fixup temp register remapping
305 // NOLINTNEXTLINE(readability-braces-around-statements,bugprone-suspicious-semicolon)
306 if constexpr (is_cyclic) {
307 ASSERT(dst == first_dst);
308 auto re = remap->at(first_dst);
309 ASSERT(re != INVALID_LOCATION_INDEX);
310 remap->at(first_src) = re;
311 remap->at(first_dst) = INVALID_LOCATION_INDEX;
312 }
313 }
314
GetResolver(DataType::Type type)315 SpillFillsResolver::LocationIndex SpillFillsResolver::GetResolver(DataType::Type type)
316 {
317 // There is a preassigned resolver
318 if (resolver_ != INVALID_REG) {
319 ASSERT(!DataType::IsFloatType(type));
320 GetGraph()->SetRegUsage(resolver_, type);
321 return resolver_;
322 }
323 // There are no temp registers in the Arch::AARCH32, use stack slot to resolve
324 if (GetGraph()->GetArch() == Arch::AARCH32) {
325 return Map(Location::MakeStackSlot(0));
326 }
327 if (DataType::IsFloatType(type)) {
328 return VREGS_TABLE_OFFSET;
329 }
330 return 0;
331 }
332 } // namespace panda::compiler
333