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