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