|
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/IonAllocPolicy.h" |
|
12 #include "jit/Registers.h" |
|
13 |
|
14 namespace js { |
|
15 namespace jit { |
|
16 |
|
17 // This is similar to Operand, but carries more information. We're also not |
|
18 // guaranteed that Operand looks like this on all ISAs. |
|
19 class MoveOperand |
|
20 { |
|
21 public: |
|
22 enum Kind { |
|
23 // A register in the "integer", aka "general purpose", class. |
|
24 REG, |
|
25 // A register in the "float" register class. |
|
26 FLOAT_REG, |
|
27 // A memory region. |
|
28 MEMORY, |
|
29 // The address of a memory region. |
|
30 EFFECTIVE_ADDRESS |
|
31 }; |
|
32 |
|
33 private: |
|
34 Kind kind_; |
|
35 uint32_t code_; |
|
36 int32_t disp_; |
|
37 |
|
38 public: |
|
39 MoveOperand() |
|
40 { } |
|
41 explicit MoveOperand(const Register ®) : kind_(REG), code_(reg.code()) |
|
42 { } |
|
43 explicit MoveOperand(const FloatRegister ®) : kind_(FLOAT_REG), code_(reg.code()) |
|
44 { } |
|
45 MoveOperand(const Register ®, int32_t disp, Kind kind = MEMORY) |
|
46 : kind_(kind), |
|
47 code_(reg.code()), |
|
48 disp_(disp) |
|
49 { |
|
50 JS_ASSERT(isMemoryOrEffectiveAddress()); |
|
51 |
|
52 // With a zero offset, this is a plain reg-to-reg move. |
|
53 if (disp == 0 && kind_ == EFFECTIVE_ADDRESS) |
|
54 kind_ = REG; |
|
55 } |
|
56 MoveOperand(const MoveOperand &other) |
|
57 : kind_(other.kind_), |
|
58 code_(other.code_), |
|
59 disp_(other.disp_) |
|
60 { } |
|
61 bool isFloatReg() const { |
|
62 return kind_ == FLOAT_REG; |
|
63 } |
|
64 bool isGeneralReg() const { |
|
65 return kind_ == REG; |
|
66 } |
|
67 bool isMemory() const { |
|
68 return kind_ == MEMORY; |
|
69 } |
|
70 bool isEffectiveAddress() const { |
|
71 return kind_ == EFFECTIVE_ADDRESS; |
|
72 } |
|
73 bool isMemoryOrEffectiveAddress() const { |
|
74 return isMemory() || isEffectiveAddress(); |
|
75 } |
|
76 Register reg() const { |
|
77 JS_ASSERT(isGeneralReg()); |
|
78 return Register::FromCode(code_); |
|
79 } |
|
80 FloatRegister floatReg() const { |
|
81 JS_ASSERT(isFloatReg()); |
|
82 return FloatRegister::FromCode(code_); |
|
83 } |
|
84 Register base() const { |
|
85 JS_ASSERT(isMemoryOrEffectiveAddress()); |
|
86 return Register::FromCode(code_); |
|
87 } |
|
88 int32_t disp() const { |
|
89 JS_ASSERT(isMemoryOrEffectiveAddress()); |
|
90 return disp_; |
|
91 } |
|
92 |
|
93 bool operator ==(const MoveOperand &other) const { |
|
94 if (kind_ != other.kind_) |
|
95 return false; |
|
96 if (code_ != other.code_) |
|
97 return false; |
|
98 if (isMemoryOrEffectiveAddress()) |
|
99 return disp_ == other.disp_; |
|
100 return true; |
|
101 } |
|
102 bool operator !=(const MoveOperand &other) const { |
|
103 return !operator==(other); |
|
104 } |
|
105 }; |
|
106 |
|
107 // This represents a move operation. |
|
108 class MoveOp |
|
109 { |
|
110 protected: |
|
111 MoveOperand from_; |
|
112 MoveOperand to_; |
|
113 bool cycleBegin_; |
|
114 bool cycleEnd_; |
|
115 |
|
116 public: |
|
117 enum Type { |
|
118 GENERAL, |
|
119 INT32, |
|
120 FLOAT32, |
|
121 DOUBLE |
|
122 }; |
|
123 |
|
124 protected: |
|
125 Type type_; |
|
126 |
|
127 // If cycleBegin_ is true, endCycleType_ is the type of the move at the end |
|
128 // of the cycle. For example, given these moves: |
|
129 // INT32 move a -> b |
|
130 // GENERAL move b -> a |
|
131 // the move resolver starts by copying b into a temporary location, so that |
|
132 // the last move can read it. This copy needs to use use type GENERAL. |
|
133 Type endCycleType_; |
|
134 |
|
135 public: |
|
136 MoveOp() |
|
137 { } |
|
138 MoveOp(const MoveOperand &from, const MoveOperand &to, Type type) |
|
139 : from_(from), |
|
140 to_(to), |
|
141 cycleBegin_(false), |
|
142 cycleEnd_(false), |
|
143 type_(type) |
|
144 { } |
|
145 |
|
146 bool isCycleBegin() const { |
|
147 return cycleBegin_; |
|
148 } |
|
149 bool isCycleEnd() const { |
|
150 return cycleEnd_; |
|
151 } |
|
152 const MoveOperand &from() const { |
|
153 return from_; |
|
154 } |
|
155 const MoveOperand &to() const { |
|
156 return to_; |
|
157 } |
|
158 Type type() const { |
|
159 return type_; |
|
160 } |
|
161 Type endCycleType() const { |
|
162 JS_ASSERT(isCycleBegin()); |
|
163 return endCycleType_; |
|
164 } |
|
165 }; |
|
166 |
|
167 class MoveResolver |
|
168 { |
|
169 private: |
|
170 struct PendingMove |
|
171 : public MoveOp, |
|
172 public TempObject, |
|
173 public InlineListNode<PendingMove> |
|
174 { |
|
175 PendingMove() |
|
176 { } |
|
177 PendingMove(const MoveOperand &from, const MoveOperand &to, Type type) |
|
178 : MoveOp(from, to, type) |
|
179 { } |
|
180 |
|
181 void setCycleBegin(Type endCycleType) { |
|
182 JS_ASSERT(!isCycleBegin() && !isCycleEnd()); |
|
183 cycleBegin_ = true; |
|
184 endCycleType_ = endCycleType; |
|
185 } |
|
186 void setCycleEnd() { |
|
187 JS_ASSERT(!isCycleBegin() && !isCycleEnd()); |
|
188 cycleEnd_ = true; |
|
189 } |
|
190 }; |
|
191 |
|
192 typedef InlineList<MoveResolver::PendingMove>::iterator PendingMoveIterator; |
|
193 |
|
194 private: |
|
195 // Moves that are definitely unblocked (constants to registers). These are |
|
196 // emitted last. |
|
197 js::Vector<MoveOp, 16, SystemAllocPolicy> orderedMoves_; |
|
198 bool hasCycles_; |
|
199 |
|
200 TempObjectPool<PendingMove> movePool_; |
|
201 |
|
202 InlineList<PendingMove> pending_; |
|
203 |
|
204 PendingMove *findBlockingMove(const PendingMove *last); |
|
205 |
|
206 // Internal reset function. Does not clear lists. |
|
207 void resetState(); |
|
208 |
|
209 public: |
|
210 MoveResolver(); |
|
211 |
|
212 // Resolves a move group into two lists of ordered moves. These moves must |
|
213 // be executed in the order provided. Some moves may indicate that they |
|
214 // participate in a cycle. For every cycle there are two such moves, and it |
|
215 // is guaranteed that cycles do not nest inside each other in the list. |
|
216 // |
|
217 // After calling addMove() for each parallel move, resolve() performs the |
|
218 // cycle resolution algorithm. Calling addMove() again resets the resolver. |
|
219 bool addMove(const MoveOperand &from, const MoveOperand &to, MoveOp::Type type); |
|
220 bool resolve(); |
|
221 |
|
222 size_t numMoves() const { |
|
223 return orderedMoves_.length(); |
|
224 } |
|
225 const MoveOp &getMove(size_t i) const { |
|
226 return orderedMoves_[i]; |
|
227 } |
|
228 bool hasCycles() const { |
|
229 return hasCycles_; |
|
230 } |
|
231 void clearTempObjectPool() { |
|
232 movePool_.clear(); |
|
233 } |
|
234 void setAllocator(TempAllocator &alloc) { |
|
235 movePool_.setAllocator(alloc); |
|
236 } |
|
237 }; |
|
238 |
|
239 } // namespace jit |
|
240 } // namespace js |
|
241 |
|
242 #endif /* jit_MoveResolver_h */ |