blob: e89417df7d9ef9cbb69365f1065f9e3c886fc971 [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 */
16
17#ifndef ART_COMPILER_OPTIMIZING_PARALLEL_MOVE_RESOLVER_H_
18#define ART_COMPILER_OPTIMIZING_PARALLEL_MOVE_RESOLVER_H_
19
Ian Rogers0279ebb2014-10-08 17:27:48 -070020#include "base/value_object.h"
Nicolas Geoffray4e3d23a2014-05-22 18:32:45 +010021#include "utils/growable_array.h"
Zheng Xuad4450e2015-04-17 18:48:56 +080022#include "locations.h"
Nicolas Geoffray4e3d23a2014-05-22 18:32:45 +010023
24namespace art {
25
26class HParallelMove;
27class MoveOperands;
28
Zheng Xuad4450e2015-04-17 18:48:56 +080029// Helper classes to resolve a set of parallel moves. Architecture dependent code generator must
30// have their own subclass that implements corresponding virtual functions.
Nicolas Geoffray4e3d23a2014-05-22 18:32:45 +010031class ParallelMoveResolver : public ValueObject {
32 public:
33 explicit ParallelMoveResolver(ArenaAllocator* allocator) : moves_(allocator, 32) {}
Nicolas Geoffrayaa037b52014-05-23 10:40:42 +010034 virtual ~ParallelMoveResolver() {}
Nicolas Geoffray4e3d23a2014-05-22 18:32:45 +010035
36 // Resolve a set of parallel moves, emitting assembler instructions.
Zheng Xuad4450e2015-04-17 18:48:56 +080037 virtual void EmitNativeCode(HParallelMove* parallel_move) = 0;
38
39 protected:
40 // Build the initial list of moves.
41 void BuildInitialMoveList(HParallelMove* parallel_move);
42
43 GrowableArray<MoveOperands*> moves_;
44
45 private:
46 DISALLOW_COPY_AND_ASSIGN(ParallelMoveResolver);
47};
48
49// This helper class uses swap to resolve dependencies and may emit swap.
50class ParallelMoveResolverWithSwap : public ParallelMoveResolver {
51 public:
52 explicit ParallelMoveResolverWithSwap(ArenaAllocator* allocator)
53 : ParallelMoveResolver(allocator) {}
54 virtual ~ParallelMoveResolverWithSwap() {}
55
56 // Resolve a set of parallel moves, emitting assembler instructions.
57 void EmitNativeCode(HParallelMove* parallel_move) OVERRIDE;
Nicolas Geoffray4e3d23a2014-05-22 18:32:45 +010058
59 protected:
Nicolas Geoffray86dbb9a2014-06-04 11:12:39 +010060 class ScratchRegisterScope : public ValueObject {
61 public:
Zheng Xuad4450e2015-04-17 18:48:56 +080062 ScratchRegisterScope(ParallelMoveResolverWithSwap* resolver,
Nicolas Geoffraye27f31a2014-06-12 17:53:14 +010063 int blocked,
64 int if_scratch,
65 int number_of_registers);
Nicolas Geoffray86dbb9a2014-06-04 11:12:39 +010066 ~ScratchRegisterScope();
67
68 int GetRegister() const { return reg_; }
69 bool IsSpilled() const { return spilled_; }
70
71 private:
Zheng Xuad4450e2015-04-17 18:48:56 +080072 ParallelMoveResolverWithSwap* resolver_;
Nicolas Geoffray86dbb9a2014-06-04 11:12:39 +010073 int reg_;
74 bool spilled_;
75 };
76
Zheng Xuad4450e2015-04-17 18:48:56 +080077 // Return true if the location can be scratched.
Nicolas Geoffray86dbb9a2014-06-04 11:12:39 +010078 bool IsScratchLocation(Location loc);
Nicolas Geoffray48c310c2015-01-14 10:45:05 +000079
80 // Allocate a scratch register for performing a move. The method will try to use
81 // a register that is the destination of a move, but that move has not been emitted yet.
Nicolas Geoffraye27f31a2014-06-12 17:53:14 +010082 int AllocateScratchRegister(int blocked, int if_scratch, int register_count, bool* spilled);
Nicolas Geoffray86dbb9a2014-06-04 11:12:39 +010083
Nicolas Geoffray4e3d23a2014-05-22 18:32:45 +010084 // Emit a move.
85 virtual void EmitMove(size_t index) = 0;
86
87 // Execute a move by emitting a swap of two operands.
88 virtual void EmitSwap(size_t index) = 0;
89
Nicolas Geoffray86dbb9a2014-06-04 11:12:39 +010090 virtual void SpillScratch(int reg) = 0;
91 virtual void RestoreScratch(int reg) = 0;
92
Nicolas Geoffray86dbb9a2014-06-04 11:12:39 +010093 static constexpr int kNoRegister = -1;
94
Nicolas Geoffray4e3d23a2014-05-22 18:32:45 +010095 private:
Nicolas Geoffray4e3d23a2014-05-22 18:32:45 +010096 // Perform the move at the moves_ index in question (possibly requiring
97 // other moves to satisfy dependencies).
Nicolas Geoffrayf7a0c4e2015-02-10 17:08:47 +000098 //
99 // Return whether another move in the dependency cycle needs to swap. This
Nicolas Geoffray90218252015-04-15 11:56:51 +0100100 // is to handle 64bits swaps:
101 // 1) In the case of register pairs, where we want the pair to swap first to avoid
102 // building pairs that are unexpected by the code generator. For example, if
103 // we were to swap R1 with R2, we would need to update all locations using
104 // R2 to R1. So a (R2,R3) pair register could become (R1,R3). We could make
105 // the code generator understand such pairs, but it's easier and cleaner to
106 // just not create such pairs and exchange pairs in priority.
107 // 2) Even when the architecture does not have pairs, we must handle 64bits swaps
108 // first. Consider the case: (R0->R1) (R1->S) (S->R0), where 'S' is a single
109 // stack slot. If we end up swapping S and R0, S will only contain the low bits
110 // of R0. If R0->R1 is for a 64bits instruction, R1 will therefore not contain
111 // the right value.
Nicolas Geoffrayf7a0c4e2015-02-10 17:08:47 +0000112 MoveOperands* PerformMove(size_t index);
Nicolas Geoffray4e3d23a2014-05-22 18:32:45 +0100113
Zheng Xuad4450e2015-04-17 18:48:56 +0800114 DISALLOW_COPY_AND_ASSIGN(ParallelMoveResolverWithSwap);
115};
116
117// This helper class uses additional scratch registers to resolve dependencies. It supports all kind
118// of dependency cycles and does not care about the register layout.
119class ParallelMoveResolverNoSwap : public ParallelMoveResolver {
120 public:
121 explicit ParallelMoveResolverNoSwap(ArenaAllocator* allocator)
122 : ParallelMoveResolver(allocator), scratches_(allocator, 32),
123 pending_moves_(allocator, 8), allocator_(allocator) {}
124 virtual ~ParallelMoveResolverNoSwap() {}
125
126 // Resolve a set of parallel moves, emitting assembler instructions.
127 void EmitNativeCode(HParallelMove* parallel_move) OVERRIDE;
128
129 protected:
130 // Called at the beginning of EmitNativeCode(). A subclass may put some architecture dependent
131 // initialization here.
132 virtual void PrepareForEmitNativeCode() = 0;
133
134 // Called at the end of EmitNativeCode(). A subclass may put some architecture dependent cleanup
135 // here. All scratch locations will be removed after this call.
136 virtual void FinishEmitNativeCode() = 0;
137
138 // Allocate a scratch location to perform a move from input kind of location. A subclass should
139 // implement this to get the best fit location. If there is no suitable physical register, it can
140 // also return a stack slot.
141 virtual Location AllocateScratchLocationFor(Location::Kind kind) = 0;
142
143 // Called after a move which takes a scratch location as source. A subclass can defer the cleanup
144 // to FinishEmitNativeCode().
145 virtual void FreeScratchLocation(Location loc) = 0;
146
147 // Emit a move.
148 virtual void EmitMove(size_t index) = 0;
149
150 // Return a scratch location from the moves which exactly matches the kind.
151 // Return Location::NoLocation() if no matching scratch location can be found.
152 Location GetScratchLocation(Location::Kind kind);
153
154 // Add a location to the scratch list which can be returned from GetScratchLocation() to resolve
155 // dependency cycles.
156 void AddScratchLocation(Location loc);
157
158 // Remove a location from the scratch list.
159 void RemoveScratchLocation(Location loc);
160
161 // List of scratch locations.
162 GrowableArray<Location> scratches_;
163
164 private:
165 // Perform the move at the given index in `moves_` (possibly requiring other moves to satisfy
166 // dependencies).
167 void PerformMove(size_t index);
168
169 void UpdateMoveSource(Location from, Location to);
170
171 void AddPendingMove(Location source, Location destination, Primitive::Type type);
172
173 void DeletePendingMove(MoveOperands* move);
174
175 // Find a move that may be unblocked after (loc -> XXX) is performed.
176 MoveOperands* GetUnblockedPendingMove(Location loc);
177
178 // Return true if the location is blocked by outstanding moves.
179 bool IsBlockedByMoves(Location loc);
180
181 // Return the number of pending moves.
182 size_t GetNumberOfPendingMoves();
183
184 // Additional pending moves which might be added to resolve dependency cycle.
185 GrowableArray<MoveOperands*> pending_moves_;
186
187 // Used to allocate pending MoveOperands.
188 ArenaAllocator* const allocator_;
189
190 DISALLOW_COPY_AND_ASSIGN(ParallelMoveResolverNoSwap);
Nicolas Geoffray4e3d23a2014-05-22 18:32:45 +0100191};
192
193} // namespace art
194
195#endif // ART_COMPILER_OPTIMIZING_PARALLEL_MOVE_RESOLVER_H_