Line data Source code
1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 : * vim: set ts=8 sts=4 et sw=4 tw=99:
3 : * This Source Code Form is subject to the terms of the Mozilla Public
4 : * License, v. 2.0. If a copy of the MPL was not distributed with this
5 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 :
7 : #ifndef jit_MoveResolver_h
8 : #define jit_MoveResolver_h
9 :
10 : #include "jit/InlineList.h"
11 : #include "jit/JitAllocPolicy.h"
12 : #include "jit/Registers.h"
13 : #include "jit/RegisterSets.h"
14 :
15 : namespace js {
16 : namespace jit {
17 :
18 : class MacroAssembler;
19 :
20 : // This is similar to Operand, but carries more information. We're also not
21 : // guaranteed that Operand looks like this on all ISAs.
22 : class MoveOperand
23 : {
24 : public:
25 : enum Kind {
26 : // A register in the "integer", aka "general purpose", class.
27 : REG,
28 : #ifdef JS_CODEGEN_REGISTER_PAIR
29 : // Two consecutive "integer" register (aka "general purpose"). The even
30 : // register contains the lower part, the odd register has the high bits
31 : // of the content.
32 : REG_PAIR,
33 : #endif
34 : // A register in the "float" register class.
35 : FLOAT_REG,
36 : // A memory region.
37 : MEMORY,
38 : // The address of a memory region.
39 : EFFECTIVE_ADDRESS
40 : };
41 :
42 : private:
43 : Kind kind_;
44 : uint32_t code_;
45 : int32_t disp_;
46 :
47 : public:
48 12146 : MoveOperand()
49 12146 : { }
50 17926 : explicit MoveOperand(Register reg) : kind_(REG), code_(reg.code())
51 17926 : { }
52 1 : explicit MoveOperand(FloatRegister reg) : kind_(FLOAT_REG), code_(reg.code())
53 1 : { }
54 2858 : MoveOperand(Register reg, int32_t disp, Kind kind = MEMORY)
55 2858 : : kind_(kind),
56 2858 : code_(reg.code()),
57 5716 : disp_(disp)
58 : {
59 2858 : MOZ_ASSERT(isMemoryOrEffectiveAddress());
60 :
61 : // With a zero offset, this is a plain reg-to-reg move.
62 2858 : if (disp == 0 && kind_ == EFFECTIVE_ADDRESS)
63 672 : kind_ = REG;
64 2858 : }
65 : MoveOperand(MacroAssembler& masm, const ABIArg& arg);
66 108618 : MoveOperand(const MoveOperand& other)
67 108618 : : kind_(other.kind_),
68 108618 : code_(other.code_),
69 217236 : disp_(other.disp_)
70 108618 : { }
71 34 : bool isFloatReg() const {
72 34 : return kind_ == FLOAT_REG;
73 : }
74 90555 : bool isGeneralReg() const {
75 90555 : return kind_ == REG;
76 : }
77 64170 : bool isGeneralRegPair() const {
78 : #ifdef JS_CODEGEN_REGISTER_PAIR
79 : return kind_ == REG_PAIR;
80 : #else
81 64170 : return false;
82 : #endif
83 : }
84 123837 : bool isMemory() const {
85 123837 : return kind_ == MEMORY;
86 : }
87 83204 : bool isEffectiveAddress() const {
88 83204 : return kind_ == EFFECTIVE_ADDRESS;
89 : }
90 102823 : bool isMemoryOrEffectiveAddress() const {
91 102823 : return isMemory() || isEffectiveAddress();
92 : }
93 43186 : Register reg() const {
94 43186 : MOZ_ASSERT(isGeneralReg());
95 43186 : return Register::FromCode(code_);
96 : }
97 0 : Register evenReg() const {
98 0 : MOZ_ASSERT(isGeneralRegPair());
99 0 : return Register::FromCode(code_);
100 : }
101 0 : Register oddReg() const {
102 0 : MOZ_ASSERT(isGeneralRegPair());
103 0 : return Register::FromCode(code_ + 1);
104 : }
105 8 : FloatRegister floatReg() const {
106 8 : MOZ_ASSERT(isFloatReg());
107 8 : return FloatRegister::FromCode(code_);
108 : }
109 10980 : Register base() const {
110 10980 : MOZ_ASSERT(isMemoryOrEffectiveAddress());
111 10980 : return Register::FromCode(code_);
112 : }
113 2760 : int32_t disp() const {
114 2760 : MOZ_ASSERT(isMemoryOrEffectiveAddress());
115 2760 : return disp_;
116 : }
117 :
118 32085 : bool aliases(MoveOperand other) const {
119 :
120 : // These are not handled presently, but MEMORY and EFFECTIVE_ADDRESS
121 : // only appear in controlled circumstances in the trampoline code
122 : // which ensures these cases never come up.
123 :
124 32085 : MOZ_ASSERT_IF(isMemoryOrEffectiveAddress() && other.isGeneralReg(),
125 : base() != other.reg());
126 32085 : MOZ_ASSERT_IF(other.isMemoryOrEffectiveAddress() && isGeneralReg(),
127 : other.base() != reg());
128 :
129 : // Check if one of the operand is a registe rpair, in which case, we
130 : // have to check any other register, or register pair.
131 32085 : if (isGeneralRegPair() || other.isGeneralRegPair()) {
132 0 : if (isGeneralRegPair() && other.isGeneralRegPair()) {
133 : // Assume that register pairs are aligned on even registers.
134 0 : MOZ_ASSERT(!evenReg().aliases(other.oddReg()));
135 0 : MOZ_ASSERT(!oddReg().aliases(other.evenReg()));
136 : // Pair of registers are composed of consecutive registers, thus
137 : // if the first registers are aliased, then the second registers
138 : // are aliased too.
139 0 : MOZ_ASSERT(evenReg().aliases(other.evenReg()) == oddReg().aliases(other.oddReg()));
140 0 : return evenReg().aliases(other.evenReg());
141 0 : } else if (other.isGeneralReg()) {
142 0 : MOZ_ASSERT(isGeneralRegPair());
143 0 : return evenReg().aliases(other.reg()) ||
144 0 : oddReg().aliases(other.reg());
145 0 : } else if (isGeneralReg()) {
146 0 : MOZ_ASSERT(other.isGeneralRegPair());
147 0 : return other.evenReg().aliases(reg()) ||
148 0 : other.oddReg().aliases(reg());
149 : }
150 0 : return false;
151 : }
152 :
153 32085 : if (kind_ != other.kind_)
154 7690 : return false;
155 24395 : if (kind_ == FLOAT_REG)
156 0 : return floatReg().aliases(other.floatReg());
157 24395 : if (code_ != other.code_)
158 23764 : return false;
159 631 : if (isMemoryOrEffectiveAddress())
160 516 : return disp_ == other.disp_;
161 115 : return true;
162 : }
163 :
164 40170 : bool operator ==(const MoveOperand& other) const {
165 40170 : if (kind_ != other.kind_)
166 4759 : return false;
167 35411 : if (code_ != other.code_)
168 33860 : return false;
169 1551 : if (isMemoryOrEffectiveAddress())
170 446 : return disp_ == other.disp_;
171 1105 : return true;
172 : }
173 10 : bool operator !=(const MoveOperand& other) const {
174 10 : return !operator==(other);
175 : }
176 : };
177 :
178 : // This represents a move operation.
179 19134 : class MoveOp
180 : {
181 : protected:
182 : MoveOperand from_;
183 : MoveOperand to_;
184 : bool cycleBegin_;
185 : bool cycleEnd_;
186 : int cycleBeginSlot_;
187 : int cycleEndSlot_;
188 : public:
189 : enum Type {
190 : GENERAL,
191 : INT32,
192 : FLOAT32,
193 : DOUBLE,
194 : SIMD128INT,
195 : SIMD128FLOAT
196 : };
197 :
198 : protected:
199 : Type type_;
200 :
201 : // If cycleBegin_ is true, endCycleType_ is the type of the move at the end
202 : // of the cycle. For example, given these moves:
203 : // INT32 move a -> b
204 : // GENERAL move b -> a
205 : // the move resolver starts by copying b into a temporary location, so that
206 : // the last move can read it. This copy needs to use use type GENERAL.
207 : Type endCycleType_;
208 :
209 : public:
210 4915 : MoveOp()
211 4915 : { }
212 19133 : MoveOp(const MoveOperand& from, const MoveOperand& to, Type type)
213 19133 : : from_(from),
214 : to_(to),
215 : cycleBegin_(false),
216 : cycleEnd_(false),
217 : cycleBeginSlot_(-1),
218 : cycleEndSlot_(-1),
219 19133 : type_(type)
220 19133 : { }
221 :
222 20213 : bool isCycleBegin() const {
223 20213 : return cycleBegin_;
224 : }
225 20218 : bool isCycleEnd() const {
226 20218 : return cycleEnd_;
227 : }
228 : uint32_t cycleBeginSlot() const {
229 : MOZ_ASSERT(cycleBeginSlot_ != -1);
230 : return cycleBeginSlot_;
231 : }
232 : uint32_t cycleEndSlot() const {
233 : MOZ_ASSERT(cycleEndSlot_ != -1);
234 : return cycleEndSlot_;
235 : }
236 71724 : const MoveOperand& from() const {
237 71724 : return from_;
238 : }
239 49901 : const MoveOperand& to() const {
240 49901 : return to_;
241 : }
242 19129 : Type type() const {
243 19129 : return type_;
244 : }
245 0 : Type endCycleType() const {
246 0 : MOZ_ASSERT(isCycleBegin());
247 0 : return endCycleType_;
248 : }
249 2706 : bool aliases(const MoveOperand& op) const {
250 2706 : return from().aliases(op) || to().aliases(op);
251 : }
252 1353 : bool aliases(const MoveOp& other) const {
253 1353 : return aliases(other.from()) || aliases(other.to());
254 : }
255 : #ifdef JS_CODEGEN_ARM
256 : void overwrite(MoveOperand& from, MoveOperand& to, Type type) {
257 : from_ = from;
258 : to_ = to;
259 : type_ = type;
260 : }
261 : #endif
262 : };
263 :
264 4503 : class MoveResolver
265 : {
266 : private:
267 : struct PendingMove
268 : : public MoveOp,
269 : public TempObject,
270 : public InlineListNode<PendingMove>
271 : {
272 4915 : PendingMove()
273 4915 : { }
274 19133 : PendingMove(const MoveOperand& from, const MoveOperand& to, Type type)
275 19133 : : MoveOp(from, to, type)
276 19133 : { }
277 :
278 5 : void setCycleBegin(Type endCycleType, int cycleSlot) {
279 5 : MOZ_ASSERT(!cycleBegin_);
280 5 : cycleBegin_ = true;
281 5 : cycleBeginSlot_ = cycleSlot;
282 5 : endCycleType_ = endCycleType;
283 5 : }
284 5 : void setCycleEnd(int cycleSlot) {
285 5 : MOZ_ASSERT(!cycleEnd_);
286 5 : cycleEnd_ = true;
287 5 : cycleEndSlot_ = cycleSlot;
288 5 : }
289 : };
290 :
291 : typedef InlineList<MoveResolver::PendingMove>::iterator PendingMoveIterator;
292 :
293 : js::Vector<MoveOp, 16, SystemAllocPolicy> orderedMoves_;
294 : int numCycles_;
295 : int curCycles_;
296 : TempObjectPool<PendingMove> movePool_;
297 :
298 : InlineList<PendingMove> pending_;
299 :
300 : PendingMove* findBlockingMove(const PendingMove* last);
301 : PendingMove* findCycledMove(PendingMoveIterator* stack, PendingMoveIterator end, const PendingMove* first);
302 : MOZ_MUST_USE bool addOrderedMove(const MoveOp& move);
303 : void reorderMove(size_t from, size_t to);
304 :
305 : // Internal reset function. Does not clear lists.
306 : void resetState();
307 :
308 : #ifdef JS_CODEGEN_ARM
309 : bool isDoubleAliasedAsSingle(const MoveOperand& move);
310 : #endif
311 :
312 : public:
313 : MoveResolver();
314 :
315 : // Resolves a move group into two lists of ordered moves. These moves must
316 : // be executed in the order provided. Some moves may indicate that they
317 : // participate in a cycle. For every cycle there are two such moves, and it
318 : // is guaranteed that cycles do not nest inside each other in the list.
319 : //
320 : // After calling addMove() for each parallel move, resolve() performs the
321 : // cycle resolution algorithm. Calling addMove() again resets the resolver.
322 : MOZ_MUST_USE bool addMove(const MoveOperand& from, const MoveOperand& to, MoveOp::Type type);
323 : MOZ_MUST_USE bool resolve();
324 : void sortMemoryToMemoryMoves();
325 :
326 32581 : size_t numMoves() const {
327 32581 : return orderedMoves_.length();
328 : }
329 19164 : const MoveOp& getMove(size_t i) const {
330 19164 : return orderedMoves_[i];
331 : }
332 : uint32_t numCycles() const {
333 : return numCycles_;
334 : }
335 4503 : void setAllocator(TempAllocator& alloc) {
336 4503 : movePool_.setAllocator(alloc);
337 4503 : }
338 : };
339 :
340 : } // namespace jit
341 : } // namespace js
342 :
343 : #endif /* jit_MoveResolver_h */
|