blob: ad7a8a393e9052ec2f009b557d8fadd262b444f7 [file] [log] [blame]
Matthew Gharrity5d6e27d2016-07-18 13:38:44 -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_allocation_resolver.h"
18
19#include "code_generator.h"
20#include "ssa_liveness_analysis.h"
21
22namespace art {
23
24RegisterAllocationResolver::RegisterAllocationResolver(ArenaAllocator* allocator,
25 CodeGenerator* codegen,
26 const SsaLivenessAnalysis& liveness)
27 : allocator_(allocator),
28 codegen_(codegen),
29 liveness_(liveness) {}
30
Vladimir Marko70e97462016-08-09 11:04:26 +010031void RegisterAllocationResolver::Resolve(ArrayRef<HInstruction* const> safepoints,
Matthew Gharrity5d6e27d2016-07-18 13:38:44 -070032 size_t reserved_out_slots,
33 size_t int_spill_slots,
34 size_t long_spill_slots,
35 size_t float_spill_slots,
36 size_t double_spill_slots,
37 size_t catch_phi_spill_slots,
Matthew Gharrity0b110f42016-07-20 10:13:45 -070038 const ArenaVector<LiveInterval*>& temp_intervals) {
Matthew Gharrity5d6e27d2016-07-18 13:38:44 -070039 size_t spill_slots = int_spill_slots
40 + long_spill_slots
41 + float_spill_slots
42 + double_spill_slots
43 + catch_phi_spill_slots;
44
Vladimir Marko70e97462016-08-09 11:04:26 +010045 // Update safepoints and calculate the size of the spills.
46 UpdateSafepointLiveRegisters();
47 size_t maximum_safepoint_spill_size = CalculateMaximumSafepointSpillSize(safepoints);
48
Matthew Gharrity5d6e27d2016-07-18 13:38:44 -070049 // Computes frame size and spill mask.
50 codegen_->InitializeCodeGeneration(spill_slots,
Vladimir Marko70e97462016-08-09 11:04:26 +010051 maximum_safepoint_spill_size,
Matthew Gharrity5d6e27d2016-07-18 13:38:44 -070052 reserved_out_slots, // Includes slot(s) for the art method.
53 codegen_->GetGraph()->GetLinearOrder());
54
55 // Resolve outputs, including stack locations.
56 // TODO: Use pointers of Location inside LiveInterval to avoid doing another iteration.
57 for (size_t i = 0, e = liveness_.GetNumberOfSsaValues(); i < e; ++i) {
58 HInstruction* instruction = liveness_.GetInstructionFromSsaIndex(i);
59 LiveInterval* current = instruction->GetLiveInterval();
60 LocationSummary* locations = instruction->GetLocations();
61 Location location = locations->Out();
62 if (instruction->IsParameterValue()) {
63 // Now that we know the frame size, adjust the parameter's location.
64 if (location.IsStackSlot()) {
65 location = Location::StackSlot(location.GetStackIndex() + codegen_->GetFrameSize());
66 current->SetSpillSlot(location.GetStackIndex());
67 locations->UpdateOut(location);
68 } else if (location.IsDoubleStackSlot()) {
69 location = Location::DoubleStackSlot(location.GetStackIndex() + codegen_->GetFrameSize());
70 current->SetSpillSlot(location.GetStackIndex());
71 locations->UpdateOut(location);
72 } else if (current->HasSpillSlot()) {
73 current->SetSpillSlot(current->GetSpillSlot() + codegen_->GetFrameSize());
74 }
75 } else if (instruction->IsCurrentMethod()) {
76 // The current method is always at offset 0.
77 DCHECK(!current->HasSpillSlot() || (current->GetSpillSlot() == 0));
78 } else if (instruction->IsPhi() && instruction->AsPhi()->IsCatchPhi()) {
79 DCHECK(current->HasSpillSlot());
80 size_t slot = current->GetSpillSlot()
81 + spill_slots
82 + reserved_out_slots
83 - catch_phi_spill_slots;
84 current->SetSpillSlot(slot * kVRegSize);
85 } else if (current->HasSpillSlot()) {
86 // Adjust the stack slot, now that we know the number of them for each type.
87 // The way this implementation lays out the stack is the following:
88 // [parameter slots ]
89 // [catch phi spill slots ]
90 // [double spill slots ]
91 // [long spill slots ]
92 // [float spill slots ]
93 // [int/ref values ]
94 // [maximum out values ] (number of arguments for calls)
95 // [art method ].
96 size_t slot = current->GetSpillSlot();
97 switch (current->GetType()) {
98 case Primitive::kPrimDouble:
99 slot += long_spill_slots;
100 FALLTHROUGH_INTENDED;
101 case Primitive::kPrimLong:
102 slot += float_spill_slots;
103 FALLTHROUGH_INTENDED;
104 case Primitive::kPrimFloat:
105 slot += int_spill_slots;
106 FALLTHROUGH_INTENDED;
107 case Primitive::kPrimNot:
108 case Primitive::kPrimInt:
109 case Primitive::kPrimChar:
110 case Primitive::kPrimByte:
111 case Primitive::kPrimBoolean:
112 case Primitive::kPrimShort:
113 slot += reserved_out_slots;
114 break;
115 case Primitive::kPrimVoid:
116 LOG(FATAL) << "Unexpected type for interval " << current->GetType();
117 }
118 current->SetSpillSlot(slot * kVRegSize);
119 }
120
121 Location source = current->ToLocation();
122
123 if (location.IsUnallocated()) {
124 if (location.GetPolicy() == Location::kSameAsFirstInput) {
125 if (locations->InAt(0).IsUnallocated()) {
126 locations->SetInAt(0, source);
127 } else {
128 DCHECK(locations->InAt(0).Equals(source));
129 }
130 }
131 locations->UpdateOut(source);
132 } else {
133 DCHECK(source.Equals(location));
134 }
135 }
136
137 // Connect siblings and resolve inputs.
138 for (size_t i = 0, e = liveness_.GetNumberOfSsaValues(); i < e; ++i) {
139 HInstruction* instruction = liveness_.GetInstructionFromSsaIndex(i);
Vladimir Marko70e97462016-08-09 11:04:26 +0100140 ConnectSiblings(instruction->GetLiveInterval());
Matthew Gharrity5d6e27d2016-07-18 13:38:44 -0700141 }
142
143 // Resolve non-linear control flow across branches. Order does not matter.
144 for (HLinearOrderIterator it(*codegen_->GetGraph()); !it.Done(); it.Advance()) {
145 HBasicBlock* block = it.Current();
146 if (block->IsCatchBlock() ||
147 (block->IsLoopHeader() && block->GetLoopInformation()->IsIrreducible())) {
148 // Instructions live at the top of catch blocks or irreducible loop header
149 // were forced to spill.
150 if (kIsDebugBuild) {
151 BitVector* live = liveness_.GetLiveInSet(*block);
152 for (uint32_t idx : live->Indexes()) {
153 LiveInterval* interval = liveness_.GetInstructionFromSsaIndex(idx)->GetLiveInterval();
154 LiveInterval* sibling = interval->GetSiblingAt(block->GetLifetimeStart());
155 // `GetSiblingAt` returns the sibling that contains a position, but there could be
156 // a lifetime hole in it. `CoversSlow` returns whether the interval is live at that
157 // position.
158 if ((sibling != nullptr) && sibling->CoversSlow(block->GetLifetimeStart())) {
159 DCHECK(!sibling->HasRegister());
160 }
161 }
162 }
163 } else {
164 BitVector* live = liveness_.GetLiveInSet(*block);
165 for (uint32_t idx : live->Indexes()) {
166 LiveInterval* interval = liveness_.GetInstructionFromSsaIndex(idx)->GetLiveInterval();
167 for (HBasicBlock* predecessor : block->GetPredecessors()) {
168 ConnectSplitSiblings(interval, predecessor, block);
169 }
170 }
171 }
172 }
173
174 // Resolve phi inputs. Order does not matter.
175 for (HLinearOrderIterator it(*codegen_->GetGraph()); !it.Done(); it.Advance()) {
176 HBasicBlock* current = it.Current();
177 if (current->IsCatchBlock()) {
178 // Catch phi values are set at runtime by the exception delivery mechanism.
179 } else {
180 for (HInstructionIterator inst_it(current->GetPhis()); !inst_it.Done(); inst_it.Advance()) {
181 HInstruction* phi = inst_it.Current();
182 for (size_t i = 0, e = current->GetPredecessors().size(); i < e; ++i) {
183 HBasicBlock* predecessor = current->GetPredecessors()[i];
184 DCHECK_EQ(predecessor->GetNormalSuccessors().size(), 1u);
185 HInstruction* input = phi->InputAt(i);
186 Location source = input->GetLiveInterval()->GetLocationAt(
187 predecessor->GetLifetimeEnd() - 1);
188 Location destination = phi->GetLiveInterval()->ToLocation();
189 InsertParallelMoveAtExitOf(predecessor, phi, source, destination);
190 }
191 }
192 }
193 }
194
195 // Resolve temp locations.
196 for (LiveInterval* temp : temp_intervals) {
197 if (temp->IsHighInterval()) {
198 // High intervals can be skipped, they are already handled by the low interval.
199 continue;
200 }
201 HInstruction* at = liveness_.GetTempUser(temp);
202 size_t temp_index = liveness_.GetTempIndex(temp);
203 LocationSummary* locations = at->GetLocations();
204 switch (temp->GetType()) {
205 case Primitive::kPrimInt:
206 locations->SetTempAt(temp_index, Location::RegisterLocation(temp->GetRegister()));
207 break;
208
209 case Primitive::kPrimDouble:
210 if (codegen_->NeedsTwoRegisters(Primitive::kPrimDouble)) {
211 Location location = Location::FpuRegisterPairLocation(
212 temp->GetRegister(), temp->GetHighInterval()->GetRegister());
213 locations->SetTempAt(temp_index, location);
214 } else {
215 locations->SetTempAt(temp_index, Location::FpuRegisterLocation(temp->GetRegister()));
216 }
217 break;
218
219 default:
220 LOG(FATAL) << "Unexpected type for temporary location "
221 << temp->GetType();
222 }
223 }
224}
225
Vladimir Marko70e97462016-08-09 11:04:26 +0100226void RegisterAllocationResolver::UpdateSafepointLiveRegisters() {
227 for (size_t i = 0, e = liveness_.GetNumberOfSsaValues(); i < e; ++i) {
228 HInstruction* instruction = liveness_.GetInstructionFromSsaIndex(i);
229 for (LiveInterval* current = instruction->GetLiveInterval();
230 current != nullptr;
231 current = current->GetNextSibling()) {
232 if (!current->HasRegister()) {
233 continue;
234 }
235 Location source = current->ToLocation();
236 for (SafepointPosition* safepoint_position = current->GetFirstSafepoint();
237 safepoint_position != nullptr;
238 safepoint_position = safepoint_position->GetNext()) {
239 DCHECK(current->CoversSlow(safepoint_position->GetPosition()));
240 LocationSummary* locations = safepoint_position->GetLocations();
241 switch (source.GetKind()) {
242 case Location::kRegister:
243 case Location::kFpuRegister: {
244 locations->AddLiveRegister(source);
245 break;
246 }
247 case Location::kRegisterPair:
248 case Location::kFpuRegisterPair: {
249 locations->AddLiveRegister(source.ToLow());
250 locations->AddLiveRegister(source.ToHigh());
251 break;
252 }
253 case Location::kStackSlot: // Fall-through
254 case Location::kDoubleStackSlot: // Fall-through
255 case Location::kConstant: {
256 // Nothing to do.
257 break;
258 }
259 default: {
260 LOG(FATAL) << "Unexpected location for object";
261 }
262 }
263 }
264 }
265 }
266}
267
268size_t RegisterAllocationResolver::CalculateMaximumSafepointSpillSize(
269 ArrayRef<HInstruction* const> safepoints) {
270 size_t core_register_spill_size = codegen_->GetWordSize();
271 size_t fp_register_spill_size = codegen_->GetFloatingPointSpillSlotSize();
272 size_t maximum_safepoint_spill_size = 0u;
273 for (HInstruction* instruction : safepoints) {
274 LocationSummary* locations = instruction->GetLocations();
275 if (locations->OnlyCallsOnSlowPath()) {
276 size_t core_spills =
277 codegen_->GetNumberOfSlowPathSpills(locations, /* core_registers */ true);
278 size_t fp_spills =
279 codegen_->GetNumberOfSlowPathSpills(locations, /* core_registers */ false);
280 size_t spill_size =
281 core_register_spill_size * core_spills + fp_register_spill_size * fp_spills;
282 maximum_safepoint_spill_size = std::max(maximum_safepoint_spill_size, spill_size);
283 } else if (locations->CallsOnMainAndSlowPath()) {
284 // Nothing to spill on the slow path if the main path already clobbers caller-saves.
285 DCHECK_EQ(0u, codegen_->GetNumberOfSlowPathSpills(locations, /* core_registers */ true));
286 DCHECK_EQ(0u, codegen_->GetNumberOfSlowPathSpills(locations, /* core_registers */ false));
287 }
288 }
289 return maximum_safepoint_spill_size;
290}
291
292void RegisterAllocationResolver::ConnectSiblings(LiveInterval* interval) {
Matthew Gharrity5d6e27d2016-07-18 13:38:44 -0700293 LiveInterval* current = interval;
294 if (current->HasSpillSlot()
295 && current->HasRegister()
296 // Currently, we spill unconditionnally the current method in the code generators.
297 && !interval->GetDefinedBy()->IsCurrentMethod()) {
298 // We spill eagerly, so move must be at definition.
299 InsertMoveAfter(interval->GetDefinedBy(),
300 interval->ToLocation(),
301 interval->NeedsTwoSpillSlots()
302 ? Location::DoubleStackSlot(interval->GetParent()->GetSpillSlot())
303 : Location::StackSlot(interval->GetParent()->GetSpillSlot()));
304 }
305 UsePosition* use = current->GetFirstUse();
306 UsePosition* env_use = current->GetFirstEnvironmentUse();
307
308 // Walk over all siblings, updating locations of use positions, and
309 // connecting them when they are adjacent.
310 do {
311 Location source = current->ToLocation();
312
313 // Walk over all uses covered by this interval, and update the location
314 // information.
315
316 LiveRange* range = current->GetFirstRange();
317 while (range != nullptr) {
318 while (use != nullptr && use->GetPosition() < range->GetStart()) {
319 DCHECK(use->IsSynthesized());
320 use = use->GetNext();
321 }
322 while (use != nullptr && use->GetPosition() <= range->GetEnd()) {
323 DCHECK(!use->GetIsEnvironment());
324 DCHECK(current->CoversSlow(use->GetPosition()) || (use->GetPosition() == range->GetEnd()));
325 if (!use->IsSynthesized()) {
326 LocationSummary* locations = use->GetUser()->GetLocations();
327 Location expected_location = locations->InAt(use->GetInputIndex());
328 // The expected (actual) location may be invalid in case the input is unused. Currently
329 // this only happens for intrinsics.
330 if (expected_location.IsValid()) {
331 if (expected_location.IsUnallocated()) {
332 locations->SetInAt(use->GetInputIndex(), source);
333 } else if (!expected_location.IsConstant()) {
334 AddInputMoveFor(interval->GetDefinedBy(), use->GetUser(), source, expected_location);
335 }
336 } else {
337 DCHECK(use->GetUser()->IsInvoke());
338 DCHECK(use->GetUser()->AsInvoke()->GetIntrinsic() != Intrinsics::kNone);
339 }
340 }
341 use = use->GetNext();
342 }
343
344 // Walk over the environment uses, and update their locations.
345 while (env_use != nullptr && env_use->GetPosition() < range->GetStart()) {
346 env_use = env_use->GetNext();
347 }
348
349 while (env_use != nullptr && env_use->GetPosition() <= range->GetEnd()) {
350 DCHECK(current->CoversSlow(env_use->GetPosition())
351 || (env_use->GetPosition() == range->GetEnd()));
352 HEnvironment* environment = env_use->GetEnvironment();
353 environment->SetLocationAt(env_use->GetInputIndex(), source);
354 env_use = env_use->GetNext();
355 }
356
357 range = range->GetNext();
358 }
359
360 // If the next interval starts just after this one, and has a register,
361 // insert a move.
362 LiveInterval* next_sibling = current->GetNextSibling();
363 if (next_sibling != nullptr
364 && next_sibling->HasRegister()
365 && current->GetEnd() == next_sibling->GetStart()) {
366 Location destination = next_sibling->ToLocation();
367 InsertParallelMoveAt(current->GetEnd(), interval->GetDefinedBy(), source, destination);
368 }
369
370 for (SafepointPosition* safepoint_position = current->GetFirstSafepoint();
371 safepoint_position != nullptr;
372 safepoint_position = safepoint_position->GetNext()) {
373 DCHECK(current->CoversSlow(safepoint_position->GetPosition()));
374
Vladimir Marko70e97462016-08-09 11:04:26 +0100375 if (current->GetType() == Primitive::kPrimNot) {
Matthew Gharrity5d6e27d2016-07-18 13:38:44 -0700376 DCHECK(interval->GetDefinedBy()->IsActualObject())
377 << interval->GetDefinedBy()->DebugName()
378 << "@" << safepoint_position->GetInstruction()->DebugName();
Vladimir Marko70e97462016-08-09 11:04:26 +0100379 LocationSummary* locations = safepoint_position->GetLocations();
380 if (current->GetParent()->HasSpillSlot()) {
381 locations->SetStackBit(current->GetParent()->GetSpillSlot() / kVRegSize);
Matthew Gharrity5d6e27d2016-07-18 13:38:44 -0700382 }
Vladimir Marko70e97462016-08-09 11:04:26 +0100383 if (source.GetKind() == Location::kRegister) {
384 locations->SetRegisterBit(source.reg());
Matthew Gharrity5d6e27d2016-07-18 13:38:44 -0700385 }
386 }
387 }
388 current = next_sibling;
389 } while (current != nullptr);
390
391 if (kIsDebugBuild) {
392 // Following uses can only be synthesized uses.
393 while (use != nullptr) {
394 DCHECK(use->IsSynthesized());
395 use = use->GetNext();
396 }
397 }
398}
399
400static bool IsMaterializableEntryBlockInstructionOfGraphWithIrreducibleLoop(
401 HInstruction* instruction) {
402 return instruction->GetBlock()->GetGraph()->HasIrreducibleLoops() &&
403 (instruction->IsConstant() || instruction->IsCurrentMethod());
404}
405
406void RegisterAllocationResolver::ConnectSplitSiblings(LiveInterval* interval,
407 HBasicBlock* from,
408 HBasicBlock* to) const {
409 if (interval->GetNextSibling() == nullptr) {
410 // Nothing to connect. The whole range was allocated to the same location.
411 return;
412 }
413
414 // Find the intervals that cover `from` and `to`.
415 size_t destination_position = to->GetLifetimeStart();
416 size_t source_position = from->GetLifetimeEnd() - 1;
417 LiveInterval* destination = interval->GetSiblingAt(destination_position);
418 LiveInterval* source = interval->GetSiblingAt(source_position);
419
420 if (destination == source) {
421 // Interval was not split.
422 return;
423 }
424
425 LiveInterval* parent = interval->GetParent();
426 HInstruction* defined_by = parent->GetDefinedBy();
427 if (codegen_->GetGraph()->HasIrreducibleLoops() &&
428 (destination == nullptr || !destination->CoversSlow(destination_position))) {
429 // Our live_in fixed point calculation has found that the instruction is live
430 // in the `to` block because it will eventually enter an irreducible loop. Our
431 // live interval computation however does not compute a fixed point, and
432 // therefore will not have a location for that instruction for `to`.
433 // Because the instruction is a constant or the ArtMethod, we don't need to
434 // do anything: it will be materialized in the irreducible loop.
435 DCHECK(IsMaterializableEntryBlockInstructionOfGraphWithIrreducibleLoop(defined_by))
436 << defined_by->DebugName() << ":" << defined_by->GetId()
437 << " " << from->GetBlockId() << " -> " << to->GetBlockId();
438 return;
439 }
440
441 if (!destination->HasRegister()) {
442 // Values are eagerly spilled. Spill slot already contains appropriate value.
443 return;
444 }
445
446 Location location_source;
447 // `GetSiblingAt` returns the interval whose start and end cover `position`,
448 // but does not check whether the interval is inactive at that position.
449 // The only situation where the interval is inactive at that position is in the
450 // presence of irreducible loops for constants and ArtMethod.
451 if (codegen_->GetGraph()->HasIrreducibleLoops() &&
452 (source == nullptr || !source->CoversSlow(source_position))) {
453 DCHECK(IsMaterializableEntryBlockInstructionOfGraphWithIrreducibleLoop(defined_by));
454 if (defined_by->IsConstant()) {
455 location_source = defined_by->GetLocations()->Out();
456 } else {
457 DCHECK(defined_by->IsCurrentMethod());
458 location_source = parent->NeedsTwoSpillSlots()
459 ? Location::DoubleStackSlot(parent->GetSpillSlot())
460 : Location::StackSlot(parent->GetSpillSlot());
461 }
462 } else {
463 DCHECK(source != nullptr);
464 DCHECK(source->CoversSlow(source_position));
465 DCHECK(destination->CoversSlow(destination_position));
466 location_source = source->ToLocation();
467 }
468
469 // If `from` has only one successor, we can put the moves at the exit of it. Otherwise
470 // we need to put the moves at the entry of `to`.
471 if (from->GetNormalSuccessors().size() == 1) {
472 InsertParallelMoveAtExitOf(from,
473 defined_by,
474 location_source,
475 destination->ToLocation());
476 } else {
477 DCHECK_EQ(to->GetPredecessors().size(), 1u);
478 InsertParallelMoveAtEntryOf(to,
479 defined_by,
480 location_source,
481 destination->ToLocation());
482 }
483}
484
485static bool IsValidDestination(Location destination) {
486 return destination.IsRegister()
487 || destination.IsRegisterPair()
488 || destination.IsFpuRegister()
489 || destination.IsFpuRegisterPair()
490 || destination.IsStackSlot()
491 || destination.IsDoubleStackSlot();
492}
493
494void RegisterAllocationResolver::AddMove(HParallelMove* move,
495 Location source,
496 Location destination,
497 HInstruction* instruction,
498 Primitive::Type type) const {
499 if (type == Primitive::kPrimLong
500 && codegen_->ShouldSplitLongMoves()
501 // The parallel move resolver knows how to deal with long constants.
502 && !source.IsConstant()) {
503 move->AddMove(source.ToLow(), destination.ToLow(), Primitive::kPrimInt, instruction);
504 move->AddMove(source.ToHigh(), destination.ToHigh(), Primitive::kPrimInt, nullptr);
505 } else {
506 move->AddMove(source, destination, type, instruction);
507 }
508}
509
510void RegisterAllocationResolver::AddInputMoveFor(HInstruction* input,
511 HInstruction* user,
512 Location source,
513 Location destination) const {
514 if (source.Equals(destination)) return;
515
516 DCHECK(!user->IsPhi());
517
518 HInstruction* previous = user->GetPrevious();
519 HParallelMove* move = nullptr;
520 if (previous == nullptr
521 || !previous->IsParallelMove()
522 || previous->GetLifetimePosition() < user->GetLifetimePosition()) {
523 move = new (allocator_) HParallelMove(allocator_);
524 move->SetLifetimePosition(user->GetLifetimePosition());
525 user->GetBlock()->InsertInstructionBefore(move, user);
526 } else {
527 move = previous->AsParallelMove();
528 }
529 DCHECK_EQ(move->GetLifetimePosition(), user->GetLifetimePosition());
530 AddMove(move, source, destination, nullptr, input->GetType());
531}
532
533static bool IsInstructionStart(size_t position) {
534 return (position & 1) == 0;
535}
536
537static bool IsInstructionEnd(size_t position) {
538 return (position & 1) == 1;
539}
540
541void RegisterAllocationResolver::InsertParallelMoveAt(size_t position,
542 HInstruction* instruction,
543 Location source,
544 Location destination) const {
545 DCHECK(IsValidDestination(destination)) << destination;
546 if (source.Equals(destination)) return;
547
548 HInstruction* at = liveness_.GetInstructionFromPosition(position / 2);
549 HParallelMove* move;
550 if (at == nullptr) {
551 if (IsInstructionStart(position)) {
552 // Block boundary, don't do anything the connection of split siblings will handle it.
553 return;
554 } else {
555 // Move must happen before the first instruction of the block.
556 at = liveness_.GetInstructionFromPosition((position + 1) / 2);
557 // Note that parallel moves may have already been inserted, so we explicitly
558 // ask for the first instruction of the block: `GetInstructionFromPosition` does
559 // not contain the `HParallelMove` instructions.
560 at = at->GetBlock()->GetFirstInstruction();
561
562 if (at->GetLifetimePosition() < position) {
563 // We may insert moves for split siblings and phi spills at the beginning of the block.
564 // Since this is a different lifetime position, we need to go to the next instruction.
565 DCHECK(at->IsParallelMove());
566 at = at->GetNext();
567 }
568
569 if (at->GetLifetimePosition() != position) {
570 DCHECK_GT(at->GetLifetimePosition(), position);
571 move = new (allocator_) HParallelMove(allocator_);
572 move->SetLifetimePosition(position);
573 at->GetBlock()->InsertInstructionBefore(move, at);
574 } else {
575 DCHECK(at->IsParallelMove());
576 move = at->AsParallelMove();
577 }
578 }
579 } else if (IsInstructionEnd(position)) {
580 // Move must happen after the instruction.
581 DCHECK(!at->IsControlFlow());
582 move = at->GetNext()->AsParallelMove();
583 // This is a parallel move for connecting siblings in a same block. We need to
584 // differentiate it with moves for connecting blocks, and input moves.
585 if (move == nullptr || move->GetLifetimePosition() > position) {
586 move = new (allocator_) HParallelMove(allocator_);
587 move->SetLifetimePosition(position);
588 at->GetBlock()->InsertInstructionBefore(move, at->GetNext());
589 }
590 } else {
591 // Move must happen before the instruction.
592 HInstruction* previous = at->GetPrevious();
593 if (previous == nullptr
594 || !previous->IsParallelMove()
595 || previous->GetLifetimePosition() != position) {
596 // If the previous is a parallel move, then its position must be lower
597 // than the given `position`: it was added just after the non-parallel
598 // move instruction that precedes `instruction`.
599 DCHECK(previous == nullptr
600 || !previous->IsParallelMove()
601 || previous->GetLifetimePosition() < position);
602 move = new (allocator_) HParallelMove(allocator_);
603 move->SetLifetimePosition(position);
604 at->GetBlock()->InsertInstructionBefore(move, at);
605 } else {
606 move = previous->AsParallelMove();
607 }
608 }
609 DCHECK_EQ(move->GetLifetimePosition(), position);
610 AddMove(move, source, destination, instruction, instruction->GetType());
611}
612
613void RegisterAllocationResolver::InsertParallelMoveAtExitOf(HBasicBlock* block,
614 HInstruction* instruction,
615 Location source,
616 Location destination) const {
617 DCHECK(IsValidDestination(destination)) << destination;
618 if (source.Equals(destination)) return;
619
620 DCHECK_EQ(block->GetNormalSuccessors().size(), 1u);
621 HInstruction* last = block->GetLastInstruction();
622 // We insert moves at exit for phi predecessors and connecting blocks.
623 // A block ending with an if or a packed switch cannot branch to a block
624 // with phis because we do not allow critical edges. It can also not connect
625 // a split interval between two blocks: the move has to happen in the successor.
626 DCHECK(!last->IsIf() && !last->IsPackedSwitch());
627 HInstruction* previous = last->GetPrevious();
628 HParallelMove* move;
629 // This is a parallel move for connecting blocks. We need to differentiate
630 // it with moves for connecting siblings in a same block, and output moves.
631 size_t position = last->GetLifetimePosition();
632 if (previous == nullptr || !previous->IsParallelMove()
633 || previous->AsParallelMove()->GetLifetimePosition() != position) {
634 move = new (allocator_) HParallelMove(allocator_);
635 move->SetLifetimePosition(position);
636 block->InsertInstructionBefore(move, last);
637 } else {
638 move = previous->AsParallelMove();
639 }
640 AddMove(move, source, destination, instruction, instruction->GetType());
641}
642
643void RegisterAllocationResolver::InsertParallelMoveAtEntryOf(HBasicBlock* block,
644 HInstruction* instruction,
645 Location source,
646 Location destination) const {
647 DCHECK(IsValidDestination(destination)) << destination;
648 if (source.Equals(destination)) return;
649
650 HInstruction* first = block->GetFirstInstruction();
651 HParallelMove* move = first->AsParallelMove();
652 size_t position = block->GetLifetimeStart();
653 // This is a parallel move for connecting blocks. We need to differentiate
654 // it with moves for connecting siblings in a same block, and input moves.
655 if (move == nullptr || move->GetLifetimePosition() != position) {
656 move = new (allocator_) HParallelMove(allocator_);
657 move->SetLifetimePosition(position);
658 block->InsertInstructionBefore(move, first);
659 }
660 AddMove(move, source, destination, instruction, instruction->GetType());
661}
662
663void RegisterAllocationResolver::InsertMoveAfter(HInstruction* instruction,
664 Location source,
665 Location destination) const {
666 DCHECK(IsValidDestination(destination)) << destination;
667 if (source.Equals(destination)) return;
668
669 if (instruction->IsPhi()) {
670 InsertParallelMoveAtEntryOf(instruction->GetBlock(), instruction, source, destination);
671 return;
672 }
673
674 size_t position = instruction->GetLifetimePosition() + 1;
675 HParallelMove* move = instruction->GetNext()->AsParallelMove();
676 // This is a parallel move for moving the output of an instruction. We need
677 // to differentiate with input moves, moves for connecting siblings in a
678 // and moves for connecting blocks.
679 if (move == nullptr || move->GetLifetimePosition() != position) {
680 move = new (allocator_) HParallelMove(allocator_);
681 move->SetLifetimePosition(position);
682 instruction->GetBlock()->InsertInstructionBefore(move, instruction->GetNext());
683 }
684 AddMove(move, source, destination, instruction, instruction->GetType());
685}
686
687} // namespace art