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-shared/MoveEmitter-mips-shared.h"
8 
9 #include "jit/MacroAssembler-inl.h"
10 
11 using namespace js;
12 using namespace js::jit;
13 
14 void
15 MoveEmitterMIPSShared::emit(const MoveResolver& moves)
16 {
17     if (moves.numCycles()) {
18         // Reserve stack for cycle resolution
19         masm.reserveStack(moves.numCycles() * sizeof(double));
20         pushedAtCycle_ = masm.framePushed();
21     }
22 
23     for (size_t i = 0; i < moves.numMoves(); i++)
24         emit(moves.getMove(i));
25 }
26 
27 Address
28 MoveEmitterMIPSShared::cycleSlot(uint32_t slot, uint32_t subslot) const
29 {
30     int32_t offset = masm.framePushed() - pushedAtCycle_;
31     MOZ_ASSERT(Imm16::IsInSignedRange(offset));
32     return Address(StackPointer, offset + slot * sizeof(double) + subslot);
33 }
34 
35 int32_t
36 MoveEmitterMIPSShared::getAdjustedOffset(const MoveOperand& operand)
37 {
38     MOZ_ASSERT(operand.isMemoryOrEffectiveAddress());
39     if (operand.base() != StackPointer)
40         return operand.disp();
41 
42     // Adjust offset if stack pointer has been moved.
43     return operand.disp() + masm.framePushed() - pushedAtStart_;
44 }
45 
46 Address
47 MoveEmitterMIPSShared::getAdjustedAddress(const MoveOperand& operand)
48 {
49     return Address(operand.base(), getAdjustedOffset(operand));
50 }
51 
52 
53 Register
54 MoveEmitterMIPSShared::tempReg()
55 {
56     spilledReg_ = SecondScratchReg;
57     return SecondScratchReg;
58 }
59 
60 void
61 MoveEmitterMIPSShared::emitMove(const MoveOperand& from, const MoveOperand& to)
62 {
63     if (from.isGeneralReg()) {
64         // Second scratch register should not be moved by MoveEmitter.
65         MOZ_ASSERT(from.reg() != spilledReg_);
66 
67         if (to.isGeneralReg())
68             masm.movePtr(from.reg(), to.reg());
69         else if (to.isMemory())
70             masm.storePtr(from.reg(), getAdjustedAddress(to));
71         else
72             MOZ_CRASH("Invalid emitMove arguments.");
73     } else if (from.isMemory()) {
74         if (to.isGeneralReg()) {
75             masm.loadPtr(getAdjustedAddress(from), to.reg());
76         } else if (to.isMemory()) {
77             masm.loadPtr(getAdjustedAddress(from), tempReg());
78             masm.storePtr(tempReg(), getAdjustedAddress(to));
79         } else {
80             MOZ_CRASH("Invalid emitMove arguments.");
81         }
82     } else if (from.isEffectiveAddress()) {
83         if (to.isGeneralReg()) {
84             masm.computeEffectiveAddress(getAdjustedAddress(from), to.reg());
85         } else if (to.isMemory()) {
86             masm.computeEffectiveAddress(getAdjustedAddress(from), tempReg());
87             masm.storePtr(tempReg(), getAdjustedAddress(to));
88         } else {
89             MOZ_CRASH("Invalid emitMove arguments.");
90         }
91     } else {
92         MOZ_CRASH("Invalid emitMove arguments.");
93     }
94 }
95 
96 void
97 MoveEmitterMIPSShared::emitInt32Move(const MoveOperand &from, const MoveOperand &to)
98 {
99     if (from.isGeneralReg()) {
100         // Second scratch register should not be moved by MoveEmitter.
101         MOZ_ASSERT(from.reg() != spilledReg_);
102 
103         if (to.isGeneralReg())
104             masm.move32(from.reg(), to.reg());
105         else if (to.isMemory())
106             masm.store32(from.reg(), getAdjustedAddress(to));
107         else
108             MOZ_CRASH("Invalid emitInt32Move arguments.");
109     } else if (from.isMemory()) {
110         if (to.isGeneralReg()) {
111             masm.load32(getAdjustedAddress(from), to.reg());
112         } else if (to.isMemory()) {
113             masm.load32(getAdjustedAddress(from), tempReg());
114             masm.store32(tempReg(), getAdjustedAddress(to));
115         } else {
116             MOZ_CRASH("Invalid emitInt32Move arguments.");
117         }
118     } else if (from.isEffectiveAddress()) {
119         if (to.isGeneralReg()) {
120             masm.computeEffectiveAddress(getAdjustedAddress(from), to.reg());
121         } else if (to.isMemory()) {
122             masm.computeEffectiveAddress(getAdjustedAddress(from), tempReg());
123             masm.store32(tempReg(), getAdjustedAddress(to));
124         } else {
125             MOZ_CRASH("Invalid emitInt32Move arguments.");
126         }
127     } else {
128         MOZ_CRASH("Invalid emitInt32Move arguments.");
129     }
130 }
131 
132 void
133 MoveEmitterMIPSShared::emitFloat32Move(const MoveOperand& from, const MoveOperand& to)
134 {
135     // Ensure that we can use ScratchFloat32Reg in memory move.
136     MOZ_ASSERT_IF(from.isFloatReg(), from.floatReg() != ScratchFloat32Reg);
137     MOZ_ASSERT_IF(to.isFloatReg(), to.floatReg() != ScratchFloat32Reg);
138 
139     if (from.isFloatReg()) {
140         if (to.isFloatReg()) {
141             masm.moveFloat32(from.floatReg(), to.floatReg());
142         } else if (to.isGeneralReg()) {
143             // This should only be used when passing float parameter in a1,a2,a3
144             MOZ_ASSERT(to.reg() == a1 || to.reg() == a2 || to.reg() == a3);
145             masm.moveFromFloat32(from.floatReg(), to.reg());
146         } else {
147             MOZ_ASSERT(to.isMemory());
148             masm.storeFloat32(from.floatReg(), getAdjustedAddress(to));
149         }
150     } else if (to.isFloatReg()) {
151         MOZ_ASSERT(from.isMemory());
152         masm.loadFloat32(getAdjustedAddress(from), to.floatReg());
153     } else if (to.isGeneralReg()) {
154         MOZ_ASSERT(from.isMemory());
155         // This should only be used when passing float parameter in a1,a2,a3
156         MOZ_ASSERT(to.reg() == a1 || to.reg() == a2 || to.reg() == a3);
157         masm.loadPtr(getAdjustedAddress(from), to.reg());
158     } else {
159         MOZ_ASSERT(from.isMemory());
160         MOZ_ASSERT(to.isMemory());
161         masm.loadFloat32(getAdjustedAddress(from), ScratchFloat32Reg);
162         masm.storeFloat32(ScratchFloat32Reg, getAdjustedAddress(to));
163     }
164 }
165 
166 void
167 MoveEmitterMIPSShared::emit(const MoveOp& move)
168 {
169     const MoveOperand& from = move.from();
170     const MoveOperand& to = move.to();
171 
172     if (move.isCycleEnd() && move.isCycleBegin()) {
173         // A fun consequence of aliased registers is you can have multiple
174         // cycles at once, and one can end exactly where another begins.
175         breakCycle(from, to, move.endCycleType(), move.cycleBeginSlot());
176         completeCycle(from, to, move.type(), move.cycleEndSlot());
177         return;
178     }
179 
180     if (move.isCycleEnd()) {
181         MOZ_ASSERT(inCycle_);
182         completeCycle(from, to, move.type(), move.cycleEndSlot());
183         MOZ_ASSERT(inCycle_ > 0);
184         inCycle_--;
185         return;
186     }
187 
188     if (move.isCycleBegin()) {
189         breakCycle(from, to, move.endCycleType(), move.cycleBeginSlot());
190         inCycle_++;
191     }
192 
193     switch (move.type()) {
194       case MoveOp::FLOAT32:
195         emitFloat32Move(from, to);
196         break;
197       case MoveOp::DOUBLE:
198         emitDoubleMove(from, to);
199         break;
200       case MoveOp::INT32:
201         emitInt32Move(from, to);
202         break;
203       case MoveOp::GENERAL:
204         emitMove(from, to);
205         break;
206       default:
207         MOZ_CRASH("Unexpected move type");
208     }
209 }
210 
211 void
212 MoveEmitterMIPSShared::assertDone()
213 {
214     MOZ_ASSERT(inCycle_ == 0);
215 }
216 
217 void
218 MoveEmitterMIPSShared::finish()
219 {
220     assertDone();
221 
222     masm.freeStack(masm.framePushed() - pushedAtStart_);
223 }
224