1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/js/src/jit/arm/MoveEmitter-arm.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,320 @@ 1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- 1.5 + * vim: set ts=8 sts=4 et sw=4 tw=99: 1.6 + * This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +#include "jit/arm/MoveEmitter-arm.h" 1.11 + 1.12 +using namespace js; 1.13 +using namespace js::jit; 1.14 + 1.15 +MoveEmitterARM::MoveEmitterARM(MacroAssemblerARMCompat &masm) 1.16 + : inCycle_(false), 1.17 + masm(masm), 1.18 + pushedAtCycle_(-1), 1.19 + pushedAtSpill_(-1), 1.20 + spilledReg_(InvalidReg), 1.21 + spilledFloatReg_(InvalidFloatReg) 1.22 +{ 1.23 + pushedAtStart_ = masm.framePushed(); 1.24 +} 1.25 + 1.26 +void 1.27 +MoveEmitterARM::emit(const MoveResolver &moves) 1.28 +{ 1.29 + if (moves.hasCycles()) { 1.30 + // Reserve stack for cycle resolution 1.31 + masm.reserveStack(sizeof(double)); 1.32 + pushedAtCycle_ = masm.framePushed(); 1.33 + } 1.34 + 1.35 + for (size_t i = 0; i < moves.numMoves(); i++) 1.36 + emit(moves.getMove(i)); 1.37 +} 1.38 + 1.39 +MoveEmitterARM::~MoveEmitterARM() 1.40 +{ 1.41 + assertDone(); 1.42 +} 1.43 + 1.44 +Operand 1.45 +MoveEmitterARM::cycleSlot() const 1.46 +{ 1.47 + int offset = masm.framePushed() - pushedAtCycle_; 1.48 + JS_ASSERT(offset < 4096 && offset > -4096); 1.49 + return Operand(StackPointer, offset); 1.50 +} 1.51 + 1.52 +// THIS IS ALWAYS AN LDRAddr. It should not be wrapped in an operand, methinks 1.53 +Operand 1.54 +MoveEmitterARM::spillSlot() const 1.55 +{ 1.56 + int offset = masm.framePushed() - pushedAtSpill_; 1.57 + JS_ASSERT(offset < 4096 && offset > -4096); 1.58 + return Operand(StackPointer, offset); 1.59 +} 1.60 + 1.61 +Operand 1.62 +MoveEmitterARM::toOperand(const MoveOperand &operand, bool isFloat) const 1.63 +{ 1.64 + if (operand.isMemoryOrEffectiveAddress()) { 1.65 + if (operand.base() != StackPointer) { 1.66 + JS_ASSERT(operand.disp() < 1024 && operand.disp() > -1024); 1.67 + return Operand(operand.base(), operand.disp()); 1.68 + } 1.69 + 1.70 + JS_ASSERT(operand.disp() >= 0); 1.71 + 1.72 + // Otherwise, the stack offset may need to be adjusted. 1.73 + return Operand(StackPointer, operand.disp() + (masm.framePushed() - pushedAtStart_)); 1.74 + } 1.75 + 1.76 + if (operand.isGeneralReg()) 1.77 + return Operand(operand.reg()); 1.78 + 1.79 + JS_ASSERT(operand.isFloatReg()); 1.80 + return Operand(operand.floatReg()); 1.81 +} 1.82 + 1.83 +Register 1.84 +MoveEmitterARM::tempReg() 1.85 +{ 1.86 + if (spilledReg_ != InvalidReg) 1.87 + return spilledReg_; 1.88 + 1.89 + // For now, just pick r12/ip as the eviction point. This is totally 1.90 + // random, and if it ends up being bad, we can use actual heuristics later. 1.91 + // r12 is actually a bad choice. it is the scratch register, which is frequently 1.92 + // used for address computations, such as those found when we attempt to access 1.93 + // values more than 4096 off of the stack pointer. 1.94 + // instead, use lr, the LinkRegister. 1.95 + spilledReg_ = r14; 1.96 + if (pushedAtSpill_ == -1) { 1.97 + masm.Push(spilledReg_); 1.98 + pushedAtSpill_ = masm.framePushed(); 1.99 + } else { 1.100 + masm.ma_str(spilledReg_, spillSlot()); 1.101 + } 1.102 + return spilledReg_; 1.103 +} 1.104 + 1.105 +void 1.106 +MoveEmitterARM::breakCycle(const MoveOperand &from, const MoveOperand &to, MoveOp::Type type) 1.107 +{ 1.108 + // There is some pattern: 1.109 + // (A -> B) 1.110 + // (B -> A) 1.111 + // 1.112 + // This case handles (A -> B), which we reach first. We save B, then allow 1.113 + // the original move to continue. 1.114 + switch (type) { 1.115 + case MoveOp::FLOAT32: 1.116 + case MoveOp::DOUBLE: 1.117 + if (to.isMemory()) { 1.118 + FloatRegister temp = ScratchFloatReg; 1.119 + masm.ma_vldr(toOperand(to, true), temp); 1.120 + masm.ma_vstr(temp, cycleSlot()); 1.121 + } else { 1.122 + masm.ma_vstr(to.floatReg(), cycleSlot()); 1.123 + } 1.124 + break; 1.125 + case MoveOp::INT32: 1.126 + case MoveOp::GENERAL: 1.127 + // an non-vfp value 1.128 + if (to.isMemory()) { 1.129 + Register temp = tempReg(); 1.130 + masm.ma_ldr(toOperand(to, false), temp); 1.131 + masm.ma_str(temp, cycleSlot()); 1.132 + } else { 1.133 + if (to.reg() == spilledReg_) { 1.134 + // If the destination was spilled, restore it first. 1.135 + masm.ma_ldr(spillSlot(), spilledReg_); 1.136 + spilledReg_ = InvalidReg; 1.137 + } 1.138 + masm.ma_str(to.reg(), cycleSlot()); 1.139 + } 1.140 + break; 1.141 + default: 1.142 + MOZ_ASSUME_UNREACHABLE("Unexpected move type"); 1.143 + } 1.144 +} 1.145 + 1.146 +void 1.147 +MoveEmitterARM::completeCycle(const MoveOperand &from, const MoveOperand &to, MoveOp::Type type) 1.148 +{ 1.149 + // There is some pattern: 1.150 + // (A -> B) 1.151 + // (B -> A) 1.152 + // 1.153 + // This case handles (B -> A), which we reach last. We emit a move from the 1.154 + // saved value of B, to A. 1.155 + switch (type) { 1.156 + case MoveOp::FLOAT32: 1.157 + case MoveOp::DOUBLE: 1.158 + if (to.isMemory()) { 1.159 + FloatRegister temp = ScratchFloatReg; 1.160 + masm.ma_vldr(cycleSlot(), temp); 1.161 + masm.ma_vstr(temp, toOperand(to, true)); 1.162 + } else { 1.163 + masm.ma_vldr(cycleSlot(), to.floatReg()); 1.164 + } 1.165 + break; 1.166 + case MoveOp::INT32: 1.167 + case MoveOp::GENERAL: 1.168 + if (to.isMemory()) { 1.169 + Register temp = tempReg(); 1.170 + masm.ma_ldr(cycleSlot(), temp); 1.171 + masm.ma_str(temp, toOperand(to, false)); 1.172 + } else { 1.173 + if (to.reg() == spilledReg_) { 1.174 + // Make sure we don't re-clobber the spilled register later. 1.175 + spilledReg_ = InvalidReg; 1.176 + } 1.177 + masm.ma_ldr(cycleSlot(), to.reg()); 1.178 + } 1.179 + break; 1.180 + default: 1.181 + MOZ_ASSUME_UNREACHABLE("Unexpected move type"); 1.182 + } 1.183 +} 1.184 + 1.185 +void 1.186 +MoveEmitterARM::emitMove(const MoveOperand &from, const MoveOperand &to) 1.187 +{ 1.188 + if (to.isGeneralReg() && to.reg() == spilledReg_) { 1.189 + // If the destination is the spilled register, make sure we 1.190 + // don't re-clobber its value. 1.191 + spilledReg_ = InvalidReg; 1.192 + } 1.193 + 1.194 + if (from.isGeneralReg()) { 1.195 + if (from.reg() == spilledReg_) { 1.196 + // If the source is a register that has been spilled, make sure 1.197 + // to load the source back into that register. 1.198 + masm.ma_ldr(spillSlot(), spilledReg_); 1.199 + spilledReg_ = InvalidReg; 1.200 + } 1.201 + switch (toOperand(to, false).getTag()) { 1.202 + case Operand::OP2: 1.203 + // secretly must be a register 1.204 + masm.ma_mov(from.reg(), to.reg()); 1.205 + break; 1.206 + case Operand::MEM: 1.207 + masm.ma_str(from.reg(), toOperand(to, false)); 1.208 + break; 1.209 + default: 1.210 + MOZ_ASSUME_UNREACHABLE("strange move!"); 1.211 + } 1.212 + } else if (to.isGeneralReg()) { 1.213 + JS_ASSERT(from.isMemoryOrEffectiveAddress()); 1.214 + if (from.isMemory()) 1.215 + masm.ma_ldr(toOperand(from, false), to.reg()); 1.216 + else 1.217 + masm.ma_add(from.base(), Imm32(from.disp()), to.reg()); 1.218 + } else { 1.219 + // Memory to memory gpr move. 1.220 + Register reg = tempReg(); 1.221 + 1.222 + JS_ASSERT(from.isMemoryOrEffectiveAddress()); 1.223 + if (from.isMemory()) 1.224 + masm.ma_ldr(toOperand(from, false), reg); 1.225 + else 1.226 + masm.ma_add(from.base(), Imm32(from.disp()), reg); 1.227 + JS_ASSERT(to.base() != reg); 1.228 + masm.ma_str(reg, toOperand(to, false)); 1.229 + } 1.230 +} 1.231 + 1.232 +void 1.233 +MoveEmitterARM::emitFloat32Move(const MoveOperand &from, const MoveOperand &to) 1.234 +{ 1.235 + if (from.isFloatReg()) { 1.236 + if (to.isFloatReg()) 1.237 + masm.ma_vmov_f32(from.floatReg(), to.floatReg()); 1.238 + else 1.239 + masm.ma_vstr(VFPRegister(from.floatReg()).singleOverlay(), 1.240 + toOperand(to, true)); 1.241 + } else if (to.isFloatReg()) { 1.242 + masm.ma_vldr(toOperand(from, true), 1.243 + VFPRegister(to.floatReg()).singleOverlay()); 1.244 + } else { 1.245 + // Memory to memory move. 1.246 + JS_ASSERT(from.isMemory()); 1.247 + FloatRegister reg = ScratchFloatReg; 1.248 + masm.ma_vldr(toOperand(from, true), 1.249 + VFPRegister(reg).singleOverlay()); 1.250 + masm.ma_vstr(VFPRegister(reg).singleOverlay(), 1.251 + toOperand(to, true)); 1.252 + } 1.253 +} 1.254 + 1.255 +void 1.256 +MoveEmitterARM::emitDoubleMove(const MoveOperand &from, const MoveOperand &to) 1.257 +{ 1.258 + if (from.isFloatReg()) { 1.259 + if (to.isFloatReg()) 1.260 + masm.ma_vmov(from.floatReg(), to.floatReg()); 1.261 + else 1.262 + masm.ma_vstr(from.floatReg(), toOperand(to, true)); 1.263 + } else if (to.isFloatReg()) { 1.264 + masm.ma_vldr(toOperand(from, true), to.floatReg()); 1.265 + } else { 1.266 + // Memory to memory move. 1.267 + JS_ASSERT(from.isMemory()); 1.268 + FloatRegister reg = ScratchFloatReg; 1.269 + masm.ma_vldr(toOperand(from, true), reg); 1.270 + masm.ma_vstr(reg, toOperand(to, true)); 1.271 + } 1.272 +} 1.273 + 1.274 +void 1.275 +MoveEmitterARM::emit(const MoveOp &move) 1.276 +{ 1.277 + const MoveOperand &from = move.from(); 1.278 + const MoveOperand &to = move.to(); 1.279 + 1.280 + if (move.isCycleEnd()) { 1.281 + JS_ASSERT(inCycle_); 1.282 + completeCycle(from, to, move.type()); 1.283 + inCycle_ = false; 1.284 + return; 1.285 + } 1.286 + 1.287 + if (move.isCycleBegin()) { 1.288 + JS_ASSERT(!inCycle_); 1.289 + breakCycle(from, to, move.endCycleType()); 1.290 + inCycle_ = true; 1.291 + } 1.292 + 1.293 + switch (move.type()) { 1.294 + case MoveOp::FLOAT32: 1.295 + emitFloat32Move(from, to); 1.296 + break; 1.297 + case MoveOp::DOUBLE: 1.298 + emitDoubleMove(from, to); 1.299 + break; 1.300 + case MoveOp::INT32: 1.301 + case MoveOp::GENERAL: 1.302 + emitMove(from, to); 1.303 + break; 1.304 + default: 1.305 + MOZ_ASSUME_UNREACHABLE("Unexpected move type"); 1.306 + } 1.307 +} 1.308 + 1.309 +void 1.310 +MoveEmitterARM::assertDone() 1.311 +{ 1.312 + JS_ASSERT(!inCycle_); 1.313 +} 1.314 + 1.315 +void 1.316 +MoveEmitterARM::finish() 1.317 +{ 1.318 + assertDone(); 1.319 + 1.320 + if (pushedAtSpill_ != -1 && spilledReg_ != InvalidReg) 1.321 + masm.ma_ldr(spillSlot(), spilledReg_); 1.322 + masm.freeStack(masm.framePushed() - pushedAtStart_); 1.323 +}