|
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 #include "jit/mips/MoveEmitter-mips.h" |
|
8 |
|
9 using namespace js; |
|
10 using namespace js::jit; |
|
11 |
|
12 MoveEmitterMIPS::MoveEmitterMIPS(MacroAssemblerMIPSCompat &masm) |
|
13 : inCycle_(false), |
|
14 masm(masm), |
|
15 pushedAtCycle_(-1), |
|
16 pushedAtSpill_(-1), |
|
17 spilledReg_(InvalidReg), |
|
18 spilledFloatReg_(InvalidFloatReg) |
|
19 { |
|
20 pushedAtStart_ = masm.framePushed(); |
|
21 } |
|
22 |
|
23 void |
|
24 MoveEmitterMIPS::emit(const MoveResolver &moves) |
|
25 { |
|
26 if (moves.hasCycles()) { |
|
27 // Reserve stack for cycle resolution |
|
28 masm.reserveStack(sizeof(double)); |
|
29 pushedAtCycle_ = masm.framePushed(); |
|
30 } |
|
31 |
|
32 for (size_t i = 0; i < moves.numMoves(); i++) |
|
33 emit(moves.getMove(i)); |
|
34 } |
|
35 |
|
36 MoveEmitterMIPS::~MoveEmitterMIPS() |
|
37 { |
|
38 assertDone(); |
|
39 } |
|
40 |
|
41 Address |
|
42 MoveEmitterMIPS::cycleSlot() const |
|
43 { |
|
44 int offset = masm.framePushed() - pushedAtCycle_; |
|
45 MOZ_ASSERT(Imm16::isInSignedRange(offset)); |
|
46 return Address(StackPointer, offset); |
|
47 } |
|
48 |
|
49 int32_t |
|
50 MoveEmitterMIPS::getAdjustedOffset(const MoveOperand &operand) |
|
51 { |
|
52 MOZ_ASSERT(operand.isMemoryOrEffectiveAddress()); |
|
53 if (operand.base() != StackPointer) |
|
54 return operand.disp(); |
|
55 |
|
56 // Adjust offset if stack pointer has been moved. |
|
57 return operand.disp() + masm.framePushed() - pushedAtStart_; |
|
58 } |
|
59 |
|
60 Address |
|
61 MoveEmitterMIPS::getAdjustedAddress(const MoveOperand &operand) |
|
62 { |
|
63 return Address(operand.base(), getAdjustedOffset(operand)); |
|
64 } |
|
65 |
|
66 |
|
67 Register |
|
68 MoveEmitterMIPS::tempReg() |
|
69 { |
|
70 spilledReg_ = SecondScratchReg; |
|
71 return SecondScratchReg; |
|
72 } |
|
73 |
|
74 void |
|
75 MoveEmitterMIPS::breakCycle(const MoveOperand &from, const MoveOperand &to, MoveOp::Type type) |
|
76 { |
|
77 // There is some pattern: |
|
78 // (A -> B) |
|
79 // (B -> A) |
|
80 // |
|
81 // This case handles (A -> B), which we reach first. We save B, then allow |
|
82 // the original move to continue. |
|
83 switch (type) { |
|
84 case MoveOp::FLOAT32: |
|
85 if (to.isMemory()) { |
|
86 FloatRegister temp = ScratchFloatReg; |
|
87 masm.loadFloat32(getAdjustedAddress(to), temp); |
|
88 masm.storeFloat32(temp, cycleSlot()); |
|
89 } else { |
|
90 masm.storeFloat32(to.floatReg(), cycleSlot()); |
|
91 } |
|
92 break; |
|
93 case MoveOp::DOUBLE: |
|
94 if (to.isMemory()) { |
|
95 FloatRegister temp = ScratchFloatReg; |
|
96 masm.loadDouble(getAdjustedAddress(to), temp); |
|
97 masm.storeDouble(temp, cycleSlot()); |
|
98 } else { |
|
99 masm.storeDouble(to.floatReg(), cycleSlot()); |
|
100 } |
|
101 break; |
|
102 case MoveOp::INT32: |
|
103 MOZ_ASSERT(sizeof(uintptr_t) == sizeof(int32_t)); |
|
104 case MoveOp::GENERAL: |
|
105 if (to.isMemory()) { |
|
106 Register temp = tempReg(); |
|
107 masm.loadPtr(getAdjustedAddress(to), temp); |
|
108 masm.storePtr(temp, cycleSlot()); |
|
109 } else { |
|
110 // Second scratch register should not be moved by MoveEmitter. |
|
111 MOZ_ASSERT(to.reg() != spilledReg_); |
|
112 masm.storePtr(to.reg(), cycleSlot()); |
|
113 } |
|
114 break; |
|
115 default: |
|
116 MOZ_ASSUME_UNREACHABLE("Unexpected move type"); |
|
117 } |
|
118 } |
|
119 |
|
120 void |
|
121 MoveEmitterMIPS::completeCycle(const MoveOperand &from, const MoveOperand &to, MoveOp::Type type) |
|
122 { |
|
123 // There is some pattern: |
|
124 // (A -> B) |
|
125 // (B -> A) |
|
126 // |
|
127 // This case handles (B -> A), which we reach last. We emit a move from the |
|
128 // saved value of B, to A. |
|
129 switch (type) { |
|
130 case MoveOp::FLOAT32: |
|
131 if (to.isMemory()) { |
|
132 FloatRegister temp = ScratchFloatReg; |
|
133 masm.loadFloat32(cycleSlot(), temp); |
|
134 masm.storeFloat32(temp, getAdjustedAddress(to)); |
|
135 } else { |
|
136 masm.loadFloat32(cycleSlot(), to.floatReg()); |
|
137 } |
|
138 break; |
|
139 case MoveOp::DOUBLE: |
|
140 if (to.isMemory()) { |
|
141 FloatRegister temp = ScratchFloatReg; |
|
142 masm.loadDouble(cycleSlot(), temp); |
|
143 masm.storeDouble(temp, getAdjustedAddress(to)); |
|
144 } else { |
|
145 masm.loadDouble(cycleSlot(), to.floatReg()); |
|
146 } |
|
147 break; |
|
148 case MoveOp::INT32: |
|
149 MOZ_ASSERT(sizeof(uintptr_t) == sizeof(int32_t)); |
|
150 case MoveOp::GENERAL: |
|
151 if (to.isMemory()) { |
|
152 Register temp = tempReg(); |
|
153 masm.loadPtr(cycleSlot(), temp); |
|
154 masm.storePtr(temp, getAdjustedAddress(to)); |
|
155 } else { |
|
156 // Second scratch register should not be moved by MoveEmitter. |
|
157 MOZ_ASSERT(to.reg() != spilledReg_); |
|
158 masm.loadPtr(cycleSlot(), to.reg()); |
|
159 } |
|
160 break; |
|
161 default: |
|
162 MOZ_ASSUME_UNREACHABLE("Unexpected move type"); |
|
163 } |
|
164 } |
|
165 |
|
166 void |
|
167 MoveEmitterMIPS::emitMove(const MoveOperand &from, const MoveOperand &to) |
|
168 { |
|
169 if (from.isGeneralReg()) { |
|
170 // Second scratch register should not be moved by MoveEmitter. |
|
171 MOZ_ASSERT(from.reg() != spilledReg_); |
|
172 |
|
173 if (to.isGeneralReg()) |
|
174 masm.movePtr(from.reg(), to.reg()); |
|
175 else if (to.isMemory()) |
|
176 masm.storePtr(from.reg(), getAdjustedAddress(to)); |
|
177 else |
|
178 MOZ_ASSUME_UNREACHABLE("Invalid emitMove arguments."); |
|
179 } else if (from.isMemory()) { |
|
180 if (to.isGeneralReg()) { |
|
181 masm.loadPtr(getAdjustedAddress(from), to.reg()); |
|
182 } else if (to.isMemory()) { |
|
183 masm.loadPtr(getAdjustedAddress(from), tempReg()); |
|
184 masm.storePtr(tempReg(), getAdjustedAddress(to)); |
|
185 } else { |
|
186 MOZ_ASSUME_UNREACHABLE("Invalid emitMove arguments."); |
|
187 } |
|
188 } else if (from.isEffectiveAddress()) { |
|
189 if (to.isGeneralReg()) { |
|
190 masm.computeEffectiveAddress(getAdjustedAddress(from), to.reg()); |
|
191 } else if (to.isMemory()) { |
|
192 masm.computeEffectiveAddress(getAdjustedAddress(from), tempReg()); |
|
193 masm.storePtr(tempReg(), getAdjustedAddress(to)); |
|
194 } else { |
|
195 MOZ_ASSUME_UNREACHABLE("Invalid emitMove arguments."); |
|
196 } |
|
197 } else { |
|
198 MOZ_ASSUME_UNREACHABLE("Invalid emitMove arguments."); |
|
199 } |
|
200 } |
|
201 |
|
202 void |
|
203 MoveEmitterMIPS::emitFloat32Move(const MoveOperand &from, const MoveOperand &to) |
|
204 { |
|
205 // Ensure that we can use ScratchFloatReg in memory move. |
|
206 MOZ_ASSERT_IF(from.isFloatReg(), from.floatReg() != ScratchFloatReg); |
|
207 MOZ_ASSERT_IF(to.isFloatReg(), to.floatReg() != ScratchFloatReg); |
|
208 |
|
209 if (from.isFloatReg()) { |
|
210 if (to.isFloatReg()) { |
|
211 masm.moveFloat32(from.floatReg(), to.floatReg()); |
|
212 } else if (to.isGeneralReg()) { |
|
213 // This should only be used when passing float parameter in a1,a2,a3 |
|
214 MOZ_ASSERT(to.reg() == a1 || to.reg() == a2 || to.reg() == a3); |
|
215 masm.moveFromFloat32(from.floatReg(), to.reg()); |
|
216 } else { |
|
217 MOZ_ASSERT(to.isMemory()); |
|
218 masm.storeFloat32(from.floatReg(), getAdjustedAddress(to)); |
|
219 } |
|
220 } else if (to.isFloatReg()) { |
|
221 MOZ_ASSERT(from.isMemory()); |
|
222 masm.loadFloat32(getAdjustedAddress(from), to.floatReg()); |
|
223 } else if (to.isGeneralReg()) { |
|
224 MOZ_ASSERT(from.isMemory()); |
|
225 // This should only be used when passing float parameter in a1,a2,a3 |
|
226 MOZ_ASSERT(to.reg() == a1 || to.reg() == a2 || to.reg() == a3); |
|
227 masm.loadPtr(getAdjustedAddress(from), to.reg()); |
|
228 } else { |
|
229 MOZ_ASSERT(from.isMemory()); |
|
230 MOZ_ASSERT(to.isMemory()); |
|
231 masm.loadFloat32(getAdjustedAddress(from), ScratchFloatReg); |
|
232 masm.storeFloat32(ScratchFloatReg, getAdjustedAddress(to)); |
|
233 } |
|
234 } |
|
235 |
|
236 void |
|
237 MoveEmitterMIPS::emitDoubleMove(const MoveOperand &from, const MoveOperand &to) |
|
238 { |
|
239 // Ensure that we can use ScratchFloatReg in memory move. |
|
240 MOZ_ASSERT_IF(from.isFloatReg(), from.floatReg() != ScratchFloatReg); |
|
241 MOZ_ASSERT_IF(to.isFloatReg(), to.floatReg() != ScratchFloatReg); |
|
242 |
|
243 if (from.isFloatReg()) { |
|
244 if (to.isFloatReg()) { |
|
245 masm.moveDouble(from.floatReg(), to.floatReg()); |
|
246 } else if (to.isGeneralReg()) { |
|
247 // Used for passing double parameter in a2,a3 register pair. |
|
248 // Two moves are added for one double parameter by |
|
249 // MacroAssemblerMIPSCompat::passABIArg |
|
250 if(to.reg() == a2) |
|
251 masm.moveFromDoubleLo(from.floatReg(), a2); |
|
252 else if(to.reg() == a3) |
|
253 masm.moveFromDoubleHi(from.floatReg(), a3); |
|
254 else |
|
255 MOZ_ASSUME_UNREACHABLE("Invalid emitDoubleMove arguments."); |
|
256 } else { |
|
257 MOZ_ASSERT(to.isMemory()); |
|
258 masm.storeDouble(from.floatReg(), getAdjustedAddress(to)); |
|
259 } |
|
260 } else if (to.isFloatReg()) { |
|
261 MOZ_ASSERT(from.isMemory()); |
|
262 masm.loadDouble(getAdjustedAddress(from), to.floatReg()); |
|
263 } else if (to.isGeneralReg()) { |
|
264 MOZ_ASSERT(from.isMemory()); |
|
265 // Used for passing double parameter in a2,a3 register pair. |
|
266 // Two moves are added for one double parameter by |
|
267 // MacroAssemblerMIPSCompat::passABIArg |
|
268 if(to.reg() == a2) |
|
269 masm.loadPtr(getAdjustedAddress(from), a2); |
|
270 else if(to.reg() == a3) |
|
271 masm.loadPtr(Address(from.base(), getAdjustedOffset(from) + sizeof(uint32_t)), a3); |
|
272 else |
|
273 MOZ_ASSUME_UNREACHABLE("Invalid emitDoubleMove arguments."); |
|
274 } else { |
|
275 MOZ_ASSERT(from.isMemory()); |
|
276 MOZ_ASSERT(to.isMemory()); |
|
277 masm.loadDouble(getAdjustedAddress(from), ScratchFloatReg); |
|
278 masm.storeDouble(ScratchFloatReg, getAdjustedAddress(to)); |
|
279 } |
|
280 } |
|
281 |
|
282 void |
|
283 MoveEmitterMIPS::emit(const MoveOp &move) |
|
284 { |
|
285 const MoveOperand &from = move.from(); |
|
286 const MoveOperand &to = move.to(); |
|
287 |
|
288 if (move.isCycleEnd()) { |
|
289 MOZ_ASSERT(inCycle_); |
|
290 completeCycle(from, to, move.type()); |
|
291 inCycle_ = false; |
|
292 return; |
|
293 } |
|
294 |
|
295 if (move.isCycleBegin()) { |
|
296 MOZ_ASSERT(!inCycle_); |
|
297 breakCycle(from, to, move.endCycleType()); |
|
298 inCycle_ = true; |
|
299 } |
|
300 |
|
301 switch (move.type()) { |
|
302 case MoveOp::FLOAT32: |
|
303 emitFloat32Move(from, to); |
|
304 break; |
|
305 case MoveOp::DOUBLE: |
|
306 emitDoubleMove(from, to); |
|
307 break; |
|
308 case MoveOp::INT32: |
|
309 MOZ_ASSERT(sizeof(uintptr_t) == sizeof(int32_t)); |
|
310 case MoveOp::GENERAL: |
|
311 emitMove(from, to); |
|
312 break; |
|
313 default: |
|
314 MOZ_ASSUME_UNREACHABLE("Unexpected move type"); |
|
315 } |
|
316 } |
|
317 |
|
318 void |
|
319 MoveEmitterMIPS::assertDone() |
|
320 { |
|
321 MOZ_ASSERT(!inCycle_); |
|
322 } |
|
323 |
|
324 void |
|
325 MoveEmitterMIPS::finish() |
|
326 { |
|
327 assertDone(); |
|
328 |
|
329 masm.freeStack(masm.framePushed() - pushedAtStart_); |
|
330 } |