blob: 9df8f5640d43e98dae07c0213603d927077a96cf [file] [log] [blame]
Nicolas Geoffray4e3d23a2014-05-22 18:32:45 +01001/*
2 * Copyright (C) 2014 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 */
Nicolas Geoffraya2d15b52015-03-31 18:13:51 +010016#include <iostream>
Nicolas Geoffray4e3d23a2014-05-22 18:32:45 +010017
18#include "parallel_move_resolver.h"
19#include "nodes.h"
20#include "locations.h"
21
22namespace art {
23
24void ParallelMoveResolver::EmitNativeCode(HParallelMove* parallel_move) {
25 DCHECK(moves_.IsEmpty());
26 // Build up a worklist of moves.
27 BuildInitialMoveList(parallel_move);
28
29 for (size_t i = 0; i < moves_.Size(); ++i) {
30 const MoveOperands& move = *moves_.Get(i);
31 // Skip constants to perform them last. They don't block other moves
32 // and skipping such moves with register destinations keeps those
33 // registers free for the whole algorithm.
34 if (!move.IsEliminated() && !move.GetSource().IsConstant()) {
35 PerformMove(i);
36 }
37 }
38
39 // Perform the moves with constant sources.
40 for (size_t i = 0; i < moves_.Size(); ++i) {
Nicolas Geoffray48c310c2015-01-14 10:45:05 +000041 MoveOperands* move = moves_.Get(i);
42 if (!move->IsEliminated()) {
43 DCHECK(move->GetSource().IsConstant());
Nicolas Geoffray4e3d23a2014-05-22 18:32:45 +010044 EmitMove(i);
Nicolas Geoffray48c310c2015-01-14 10:45:05 +000045 // Eliminate the move, in case following moves need a scratch register.
46 move->Eliminate();
Nicolas Geoffray4e3d23a2014-05-22 18:32:45 +010047 }
48 }
49
50 moves_.Reset();
51}
52
53
54void ParallelMoveResolver::BuildInitialMoveList(HParallelMove* parallel_move) {
55 // Perform a linear sweep of the moves to add them to the initial list of
56 // moves to perform, ignoring any move that is redundant (the source is
57 // the same as the destination, the destination is ignored and
58 // unallocated, or the move was already eliminated).
59 for (size_t i = 0; i < parallel_move->NumMoves(); ++i) {
60 MoveOperands* move = parallel_move->MoveOperandsAt(i);
61 if (!move->IsRedundant()) {
62 moves_.Add(move);
63 }
64 }
65}
66
Nicolas Geoffraya2d15b52015-03-31 18:13:51 +010067Location LowOf(Location location) {
68 if (location.IsRegisterPair()) {
69 return Location::RegisterLocation(location.low());
70 } else if (location.IsFpuRegisterPair()) {
71 return Location::FpuRegisterLocation(location.low());
72 } else if (location.IsDoubleStackSlot()) {
73 return Location::StackSlot(location.GetStackIndex());
74 } else {
75 return Location::NoLocation();
76 }
77}
78
79Location HighOf(Location location) {
80 if (location.IsRegisterPair()) {
81 return Location::RegisterLocation(location.high());
82 } else if (location.IsFpuRegisterPair()) {
83 return Location::FpuRegisterLocation(location.high());
84 } else if (location.IsDoubleStackSlot()) {
85 return Location::StackSlot(location.GetHighStackIndex(4));
86 } else {
87 return Location::NoLocation();
88 }
89}
90
Nicolas Geoffrayf7a0c4e2015-02-10 17:08:47 +000091// Update the source of `move`, knowing that `updated_location` has been swapped
92// with `new_source`. Note that `updated_location` can be a pair, therefore if
93// `move` is non-pair, we need to extract which register to use.
94static void UpdateSourceOf(MoveOperands* move, Location updated_location, Location new_source) {
95 Location source = move->GetSource();
Nicolas Geoffraya2d15b52015-03-31 18:13:51 +010096 if (LowOf(updated_location).Equals(source)) {
97 move->SetSource(LowOf(new_source));
98 } else if (HighOf(updated_location).Equals(source)) {
99 move->SetSource(HighOf(new_source));
100 } else {
101 DCHECK(updated_location.Equals(source)) << updated_location << " " << source;
Nicolas Geoffrayf7a0c4e2015-02-10 17:08:47 +0000102 move->SetSource(new_source);
Nicolas Geoffrayf7a0c4e2015-02-10 17:08:47 +0000103 }
104}
Nicolas Geoffray4e3d23a2014-05-22 18:32:45 +0100105
Nicolas Geoffrayf7a0c4e2015-02-10 17:08:47 +0000106MoveOperands* ParallelMoveResolver::PerformMove(size_t index) {
Nicolas Geoffray4e3d23a2014-05-22 18:32:45 +0100107 // Each call to this function performs a move and deletes it from the move
108 // graph. We first recursively perform any move blocking this one. We
109 // mark a move as "pending" on entry to PerformMove in order to detect
110 // cycles in the move graph. We use operand swaps to resolve cycles,
111 // which means that a call to PerformMove could change any source operand
112 // in the move graph.
113
Nicolas Geoffrayf7a0c4e2015-02-10 17:08:47 +0000114 MoveOperands* move = moves_.Get(index);
115 DCHECK(!move->IsPending());
116 if (move->IsRedundant()) {
117 // Because we swap register pairs first, following, un-pending
118 // moves may become redundant.
119 move->Eliminate();
120 return nullptr;
121 }
Nicolas Geoffray4e3d23a2014-05-22 18:32:45 +0100122
123 // Clear this move's destination to indicate a pending move. The actual
124 // destination is saved in a stack-allocated local. Recursion may allow
125 // multiple moves to be pending.
Nicolas Geoffrayf7a0c4e2015-02-10 17:08:47 +0000126 DCHECK(!move->GetSource().IsInvalid());
127 Location destination = move->MarkPending();
Nicolas Geoffray4e3d23a2014-05-22 18:32:45 +0100128
129 // Perform a depth-first traversal of the move graph to resolve
130 // dependencies. Any unperformed, unpending move with a source the same
131 // as this one's destination blocks this one so recursively perform all
132 // such moves.
Nicolas Geoffrayf7a0c4e2015-02-10 17:08:47 +0000133 MoveOperands* required_swap = nullptr;
Nicolas Geoffray4e3d23a2014-05-22 18:32:45 +0100134 for (size_t i = 0; i < moves_.Size(); ++i) {
135 const MoveOperands& other_move = *moves_.Get(i);
136 if (other_move.Blocks(destination) && !other_move.IsPending()) {
137 // Though PerformMove can change any source operand in the move graph,
Nicolas Geoffrayf7a0c4e2015-02-10 17:08:47 +0000138 // calling `PerformMove` cannot create a blocking move via a swap
139 // (this loop does not miss any).
140 // For example, assume there is a non-blocking move with source A
Nicolas Geoffray4e3d23a2014-05-22 18:32:45 +0100141 // and this move is blocked on source B and there is a swap of A and
142 // B. Then A and B must be involved in the same cycle (or they would
143 // not be swapped). Since this move's destination is B and there is
144 // only a single incoming edge to an operand, this move must also be
145 // involved in the same cycle. In that case, the blocking move will
146 // be created but will be "pending" when we return from PerformMove.
Nicolas Geoffrayf7a0c4e2015-02-10 17:08:47 +0000147 required_swap = PerformMove(i);
148
149 if (required_swap == move) {
150 // If this move is required to swap, we do so without looking
151 // at the next moves. Swapping is not blocked by anything, it just
152 // updates other moves's source.
153 break;
154 } else if (required_swap == moves_.Get(i)) {
155 // If `other_move` was swapped, we iterate again to find a new
156 // potential cycle.
157 required_swap = nullptr;
158 i = 0;
159 } else if (required_swap != nullptr) {
160 // A move is required to swap. We walk back the cycle to find the
161 // move by just returning from this `PerforrmMove`.
162 moves_.Get(index)->ClearPending(destination);
163 return required_swap;
164 }
Nicolas Geoffray4e3d23a2014-05-22 18:32:45 +0100165 }
166 }
Nicolas Geoffray4e3d23a2014-05-22 18:32:45 +0100167
168 // We are about to resolve this move and don't need it marked as
169 // pending, so restore its destination.
170 move->ClearPending(destination);
171
172 // This move's source may have changed due to swaps to resolve cycles and
173 // so it may now be the last move in the cycle. If so remove it.
174 if (move->GetSource().Equals(destination)) {
175 move->Eliminate();
Nicolas Geoffrayf7a0c4e2015-02-10 17:08:47 +0000176 DCHECK(required_swap == nullptr);
177 return nullptr;
Nicolas Geoffray4e3d23a2014-05-22 18:32:45 +0100178 }
179
180 // The move may be blocked on a (at most one) pending move, in which case
181 // we have a cycle. Search for such a blocking move and perform a swap to
182 // resolve it.
183 bool do_swap = false;
Nicolas Geoffrayf7a0c4e2015-02-10 17:08:47 +0000184 if (required_swap != nullptr) {
185 DCHECK_EQ(required_swap, move);
186 do_swap = true;
187 } else {
188 for (size_t i = 0; i < moves_.Size(); ++i) {
189 const MoveOperands& other_move = *moves_.Get(i);
190 if (other_move.Blocks(destination)) {
191 DCHECK(other_move.IsPending());
192 if (!destination.IsPair() && other_move.GetSource().IsPair()) {
193 // We swap pairs before swapping non-pairs. Go back from the
194 // cycle by returning the pair that must be swapped.
195 return moves_.Get(i);
196 }
197 do_swap = true;
198 break;
199 }
Nicolas Geoffray4e3d23a2014-05-22 18:32:45 +0100200 }
201 }
202
203 if (do_swap) {
204 EmitSwap(index);
205 // Any unperformed (including pending) move with a source of either
206 // this move's source or destination needs to have their source
207 // changed to reflect the state of affairs after the swap.
208 Location source = move->GetSource();
Andreas Gampe277ccbd2014-11-03 21:36:10 -0800209 Location swap_destination = move->GetDestination();
Nicolas Geoffray4e3d23a2014-05-22 18:32:45 +0100210 move->Eliminate();
211 for (size_t i = 0; i < moves_.Size(); ++i) {
212 const MoveOperands& other_move = *moves_.Get(i);
213 if (other_move.Blocks(source)) {
Nicolas Geoffrayf7a0c4e2015-02-10 17:08:47 +0000214 UpdateSourceOf(moves_.Get(i), source, swap_destination);
Andreas Gampe277ccbd2014-11-03 21:36:10 -0800215 } else if (other_move.Blocks(swap_destination)) {
Nicolas Geoffrayf7a0c4e2015-02-10 17:08:47 +0000216 UpdateSourceOf(moves_.Get(i), swap_destination, source);
Nicolas Geoffray4e3d23a2014-05-22 18:32:45 +0100217 }
218 }
Nicolas Geoffrayf7a0c4e2015-02-10 17:08:47 +0000219 // If the swap was required because of a pair in the middle of a cycle,
220 // we return the swapped move, so that the caller knows it needs to re-iterate
221 // its dependency loop.
222 return required_swap;
Nicolas Geoffray4e3d23a2014-05-22 18:32:45 +0100223 } else {
224 // This move is not blocked.
225 EmitMove(index);
226 move->Eliminate();
Nicolas Geoffrayf7a0c4e2015-02-10 17:08:47 +0000227 DCHECK(required_swap == nullptr);
228 return nullptr;
Nicolas Geoffray4e3d23a2014-05-22 18:32:45 +0100229 }
230}
231
Nicolas Geoffray86dbb9a2014-06-04 11:12:39 +0100232bool ParallelMoveResolver::IsScratchLocation(Location loc) {
233 for (size_t i = 0; i < moves_.Size(); ++i) {
234 if (moves_.Get(i)->Blocks(loc)) {
235 return false;
236 }
237 }
238
239 for (size_t i = 0; i < moves_.Size(); ++i) {
240 if (moves_.Get(i)->GetDestination().Equals(loc)) {
241 return true;
242 }
243 }
244
245 return false;
246}
247
Nicolas Geoffraye27f31a2014-06-12 17:53:14 +0100248int ParallelMoveResolver::AllocateScratchRegister(int blocked,
249 int register_count,
250 int if_scratch,
251 bool* spilled) {
252 DCHECK_NE(blocked, if_scratch);
Nicolas Geoffray86dbb9a2014-06-04 11:12:39 +0100253 int scratch = -1;
254 for (int reg = 0; reg < register_count; ++reg) {
Nicolas Geoffray56b9ee62014-10-09 11:47:51 +0100255 if ((blocked != reg) && IsScratchLocation(Location::RegisterLocation(reg))) {
Nicolas Geoffray86dbb9a2014-06-04 11:12:39 +0100256 scratch = reg;
257 break;
258 }
259 }
260
261 if (scratch == -1) {
262 *spilled = true;
Nicolas Geoffraye27f31a2014-06-12 17:53:14 +0100263 scratch = if_scratch;
Nicolas Geoffray86dbb9a2014-06-04 11:12:39 +0100264 } else {
265 *spilled = false;
266 }
267
268 return scratch;
269}
270
271
272ParallelMoveResolver::ScratchRegisterScope::ScratchRegisterScope(
Nicolas Geoffraye27f31a2014-06-12 17:53:14 +0100273 ParallelMoveResolver* resolver, int blocked, int if_scratch, int number_of_registers)
Nicolas Geoffray86dbb9a2014-06-04 11:12:39 +0100274 : resolver_(resolver),
275 reg_(kNoRegister),
276 spilled_(false) {
Nicolas Geoffraye27f31a2014-06-12 17:53:14 +0100277 reg_ = resolver_->AllocateScratchRegister(blocked, number_of_registers, if_scratch, &spilled_);
Nicolas Geoffray86dbb9a2014-06-04 11:12:39 +0100278
279 if (spilled_) {
280 resolver->SpillScratch(reg_);
281 }
282}
283
284
285ParallelMoveResolver::ScratchRegisterScope::~ScratchRegisterScope() {
286 if (spilled_) {
287 resolver_->RestoreScratch(reg_);
288 }
289}
290
Nicolas Geoffray4e3d23a2014-05-22 18:32:45 +0100291} // namespace art