• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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(), &reg_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(), &reg_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