1 /*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "register_allocator.h"
18
19 #include <iostream>
20 #include <sstream>
21
22 #include "base/bit_vector-inl.h"
23 #include "code_generator.h"
24 #include "register_allocator_graph_color.h"
25 #include "register_allocator_linear_scan.h"
26 #include "ssa_liveness_analysis.h"
27
28
29 namespace art {
30
RegisterAllocator(ArenaAllocator * allocator,CodeGenerator * codegen,const SsaLivenessAnalysis & liveness)31 RegisterAllocator::RegisterAllocator(ArenaAllocator* allocator,
32 CodeGenerator* codegen,
33 const SsaLivenessAnalysis& liveness)
34 : allocator_(allocator),
35 codegen_(codegen),
36 liveness_(liveness) {}
37
Create(ArenaAllocator * allocator,CodeGenerator * codegen,const SsaLivenessAnalysis & analysis,Strategy strategy)38 RegisterAllocator* RegisterAllocator::Create(ArenaAllocator* allocator,
39 CodeGenerator* codegen,
40 const SsaLivenessAnalysis& analysis,
41 Strategy strategy) {
42 switch (strategy) {
43 case kRegisterAllocatorLinearScan:
44 return new (allocator) RegisterAllocatorLinearScan(allocator, codegen, analysis);
45 case kRegisterAllocatorGraphColor:
46 return new (allocator) RegisterAllocatorGraphColor(allocator, codegen, analysis);
47 default:
48 LOG(FATAL) << "Invalid register allocation strategy: " << strategy;
49 UNREACHABLE();
50 }
51 }
52
CanAllocateRegistersFor(const HGraph & graph ATTRIBUTE_UNUSED,InstructionSet instruction_set)53 bool RegisterAllocator::CanAllocateRegistersFor(const HGraph& graph ATTRIBUTE_UNUSED,
54 InstructionSet instruction_set) {
55 return instruction_set == kArm
56 || instruction_set == kArm64
57 || instruction_set == kMips
58 || instruction_set == kMips64
59 || instruction_set == kThumb2
60 || instruction_set == kX86
61 || instruction_set == kX86_64;
62 }
63
64 class AllRangesIterator : public ValueObject {
65 public:
AllRangesIterator(LiveInterval * interval)66 explicit AllRangesIterator(LiveInterval* interval)
67 : current_interval_(interval),
68 current_range_(interval->GetFirstRange()) {}
69
Done() const70 bool Done() const { return current_interval_ == nullptr; }
CurrentRange() const71 LiveRange* CurrentRange() const { return current_range_; }
CurrentInterval() const72 LiveInterval* CurrentInterval() const { return current_interval_; }
73
Advance()74 void Advance() {
75 current_range_ = current_range_->GetNext();
76 if (current_range_ == nullptr) {
77 current_interval_ = current_interval_->GetNextSibling();
78 if (current_interval_ != nullptr) {
79 current_range_ = current_interval_->GetFirstRange();
80 }
81 }
82 }
83
84 private:
85 LiveInterval* current_interval_;
86 LiveRange* current_range_;
87
88 DISALLOW_COPY_AND_ASSIGN(AllRangesIterator);
89 };
90
ValidateIntervals(const ArenaVector<LiveInterval * > & intervals,size_t number_of_spill_slots,size_t number_of_out_slots,const CodeGenerator & codegen,ArenaAllocator * allocator,bool processing_core_registers,bool log_fatal_on_failure)91 bool RegisterAllocator::ValidateIntervals(const ArenaVector<LiveInterval*>& intervals,
92 size_t number_of_spill_slots,
93 size_t number_of_out_slots,
94 const CodeGenerator& codegen,
95 ArenaAllocator* allocator,
96 bool processing_core_registers,
97 bool log_fatal_on_failure) {
98 size_t number_of_registers = processing_core_registers
99 ? codegen.GetNumberOfCoreRegisters()
100 : codegen.GetNumberOfFloatingPointRegisters();
101 ArenaVector<ArenaBitVector*> liveness_of_values(
102 allocator->Adapter(kArenaAllocRegisterAllocatorValidate));
103 liveness_of_values.reserve(number_of_registers + number_of_spill_slots);
104
105 size_t max_end = 0u;
106 for (LiveInterval* start_interval : intervals) {
107 for (AllRangesIterator it(start_interval); !it.Done(); it.Advance()) {
108 max_end = std::max(max_end, it.CurrentRange()->GetEnd());
109 }
110 }
111
112 // Allocate a bit vector per register. A live interval that has a register
113 // allocated will populate the associated bit vector based on its live ranges.
114 for (size_t i = 0; i < number_of_registers + number_of_spill_slots; ++i) {
115 liveness_of_values.push_back(
116 ArenaBitVector::Create(allocator, max_end, false, kArenaAllocRegisterAllocatorValidate));
117 }
118
119 for (LiveInterval* start_interval : intervals) {
120 for (AllRangesIterator it(start_interval); !it.Done(); it.Advance()) {
121 LiveInterval* current = it.CurrentInterval();
122 HInstruction* defined_by = current->GetParent()->GetDefinedBy();
123 if (current->GetParent()->HasSpillSlot()
124 // Parameters and current method have their own stack slot.
125 && !(defined_by != nullptr && (defined_by->IsParameterValue()
126 || defined_by->IsCurrentMethod()))) {
127 BitVector* liveness_of_spill_slot = liveness_of_values[number_of_registers
128 + current->GetParent()->GetSpillSlot() / kVRegSize
129 - number_of_out_slots];
130 for (size_t j = it.CurrentRange()->GetStart(); j < it.CurrentRange()->GetEnd(); ++j) {
131 if (liveness_of_spill_slot->IsBitSet(j)) {
132 if (log_fatal_on_failure) {
133 std::ostringstream message;
134 message << "Spill slot conflict at " << j;
135 LOG(FATAL) << message.str();
136 } else {
137 return false;
138 }
139 } else {
140 liveness_of_spill_slot->SetBit(j);
141 }
142 }
143 }
144
145 if (current->HasRegister()) {
146 if (kIsDebugBuild && log_fatal_on_failure && !current->IsFixed()) {
147 // Only check when an error is fatal. Only tests code ask for non-fatal failures
148 // and test code may not properly fill the right information to the code generator.
149 CHECK(codegen.HasAllocatedRegister(processing_core_registers, current->GetRegister()));
150 }
151 BitVector* liveness_of_register = liveness_of_values[current->GetRegister()];
152 for (size_t j = it.CurrentRange()->GetStart(); j < it.CurrentRange()->GetEnd(); ++j) {
153 if (liveness_of_register->IsBitSet(j)) {
154 if (current->IsUsingInputRegister() && current->CanUseInputRegister()) {
155 continue;
156 }
157 if (log_fatal_on_failure) {
158 std::ostringstream message;
159 message << "Register conflict at " << j << " ";
160 if (defined_by != nullptr) {
161 message << "(" << defined_by->DebugName() << ")";
162 }
163 message << "for ";
164 if (processing_core_registers) {
165 codegen.DumpCoreRegister(message, current->GetRegister());
166 } else {
167 codegen.DumpFloatingPointRegister(message, current->GetRegister());
168 }
169 for (LiveInterval* interval : intervals) {
170 if (interval->HasRegister()
171 && interval->GetRegister() == current->GetRegister()
172 && interval->CoversSlow(j)) {
173 message << std::endl;
174 if (interval->GetDefinedBy() != nullptr) {
175 message << interval->GetDefinedBy()->GetKind() << " ";
176 } else {
177 message << "physical ";
178 }
179 interval->Dump(message);
180 }
181 }
182 LOG(FATAL) << message.str();
183 } else {
184 return false;
185 }
186 } else {
187 liveness_of_register->SetBit(j);
188 }
189 }
190 }
191 }
192 }
193 return true;
194 }
195
Split(LiveInterval * interval,size_t position)196 LiveInterval* RegisterAllocator::Split(LiveInterval* interval, size_t position) {
197 DCHECK_GE(position, interval->GetStart());
198 DCHECK(!interval->IsDeadAt(position));
199 if (position == interval->GetStart()) {
200 // Spill slot will be allocated when handling `interval` again.
201 interval->ClearRegister();
202 if (interval->HasHighInterval()) {
203 interval->GetHighInterval()->ClearRegister();
204 } else if (interval->HasLowInterval()) {
205 interval->GetLowInterval()->ClearRegister();
206 }
207 return interval;
208 } else {
209 LiveInterval* new_interval = interval->SplitAt(position);
210 if (interval->HasHighInterval()) {
211 LiveInterval* high = interval->GetHighInterval()->SplitAt(position);
212 new_interval->SetHighInterval(high);
213 high->SetLowInterval(new_interval);
214 } else if (interval->HasLowInterval()) {
215 LiveInterval* low = interval->GetLowInterval()->SplitAt(position);
216 new_interval->SetLowInterval(low);
217 low->SetHighInterval(new_interval);
218 }
219 return new_interval;
220 }
221 }
222
SplitBetween(LiveInterval * interval,size_t from,size_t to)223 LiveInterval* RegisterAllocator::SplitBetween(LiveInterval* interval, size_t from, size_t to) {
224 HBasicBlock* block_from = liveness_.GetBlockFromPosition(from / 2);
225 HBasicBlock* block_to = liveness_.GetBlockFromPosition(to / 2);
226 DCHECK(block_from != nullptr);
227 DCHECK(block_to != nullptr);
228
229 // Both locations are in the same block. We split at the given location.
230 if (block_from == block_to) {
231 return Split(interval, to);
232 }
233
234 /*
235 * Non-linear control flow will force moves at every branch instruction to the new location.
236 * To avoid having all branches doing the moves, we find the next non-linear position and
237 * split the interval at this position. Take the following example (block number is the linear
238 * order position):
239 *
240 * B1
241 * / \
242 * B2 B3
243 * \ /
244 * B4
245 *
246 * B2 needs to split an interval, whose next use is in B4. If we were to split at the
247 * beginning of B4, B3 would need to do a move between B3 and B4 to ensure the interval
248 * is now in the correct location. It makes performance worst if the interval is spilled
249 * and both B2 and B3 need to reload it before entering B4.
250 *
251 * By splitting at B3, we give a chance to the register allocator to allocate the
252 * interval to the same register as in B1, and therefore avoid doing any
253 * moves in B3.
254 */
255 if (block_from->GetDominator() != nullptr) {
256 for (HBasicBlock* dominated : block_from->GetDominator()->GetDominatedBlocks()) {
257 size_t position = dominated->GetLifetimeStart();
258 if ((position > from) && (block_to->GetLifetimeStart() > position)) {
259 // Even if we found a better block, we continue iterating in case
260 // a dominated block is closer.
261 // Note that dominated blocks are not sorted in liveness order.
262 block_to = dominated;
263 DCHECK_NE(block_to, block_from);
264 }
265 }
266 }
267
268 // If `to` is in a loop, find the outermost loop header which does not contain `from`.
269 for (HLoopInformationOutwardIterator it(*block_to); !it.Done(); it.Advance()) {
270 HBasicBlock* header = it.Current()->GetHeader();
271 if (block_from->GetLifetimeStart() >= header->GetLifetimeStart()) {
272 break;
273 }
274 block_to = header;
275 }
276
277 // Split at the start of the found block, to piggy back on existing moves
278 // due to resolution if non-linear control flow (see `ConnectSplitSiblings`).
279 return Split(interval, block_to->GetLifetimeStart());
280 }
281
282 } // namespace art
283