• 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 "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(), &reg_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(), &reg_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