MoveEmitter-arm.cpp (14185B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- 2 * vim: set ts=8 sts=2 et sw=2 tw=80: 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/arm/MoveEmitter-arm.h" 8 9 #include "jit/MacroAssembler-inl.h" 10 11 using namespace js; 12 using namespace js::jit; 13 14 MoveEmitterARM::MoveEmitterARM(MacroAssembler& masm) 15 : inCycle_(0), 16 masm(masm), 17 pushedAtCycle_(-1), 18 pushedAtSpill_(-1), 19 spilledReg_(InvalidReg), 20 spilledFloatReg_(InvalidFloatReg) { 21 pushedAtStart_ = masm.framePushed(); 22 } 23 24 void MoveEmitterARM::emit(const MoveResolver& moves) { 25 if (moves.numCycles()) { 26 // Reserve stack for cycle resolution 27 static_assert(SpillSlotSize == 8); 28 masm.reserveStack(moves.numCycles() * SpillSlotSize); 29 pushedAtCycle_ = masm.framePushed(); 30 } 31 32 for (size_t i = 0; i < moves.numMoves(); i++) { 33 emit(moves.getMove(i)); 34 } 35 } 36 37 MoveEmitterARM::~MoveEmitterARM() { assertDone(); } 38 39 Address MoveEmitterARM::cycleSlot(uint32_t slot, uint32_t subslot) const { 40 int32_t offset = masm.framePushed() - pushedAtCycle_; 41 MOZ_ASSERT(offset < 4096 && offset > -4096); 42 return Address(StackPointer, offset + slot * sizeof(double) + subslot); 43 } 44 45 Address MoveEmitterARM::spillSlot() const { 46 int32_t offset = masm.framePushed() - pushedAtSpill_; 47 MOZ_ASSERT(offset < 4096 && offset > -4096); 48 return Address(StackPointer, offset); 49 } 50 51 Address MoveEmitterARM::toAddress(const MoveOperand& operand) const { 52 MOZ_ASSERT(operand.isMemoryOrEffectiveAddress()); 53 54 if (operand.base() != StackPointer) { 55 return Address(operand.base(), operand.disp()); 56 } 57 58 MOZ_ASSERT(operand.disp() >= 0); 59 60 // Otherwise, the stack offset may need to be adjusted. 61 return Address(StackPointer, 62 operand.disp() + (masm.framePushed() - pushedAtStart_)); 63 } 64 65 Register MoveEmitterARM::tempReg() { 66 if (spilledReg_ != InvalidReg) { 67 return spilledReg_; 68 } 69 70 // For now, just pick r12/ip as the eviction point. This is totally random, 71 // and if it ends up being bad, we can use actual heuristics later. r12 is 72 // actually a bad choice. It is the scratch register, which is frequently 73 // used for address computations, such as those found when we attempt to 74 // access values more than 4096 off of the stack pointer. Instead, use lr, 75 // the LinkRegister. 76 spilledReg_ = r14; 77 if (pushedAtSpill_ == -1) { 78 masm.Push(spilledReg_); 79 pushedAtSpill_ = masm.framePushed(); 80 } else { 81 ScratchRegisterScope scratch(masm); 82 masm.ma_str(spilledReg_, spillSlot(), scratch); 83 } 84 return spilledReg_; 85 } 86 87 void MoveEmitterARM::breakCycle(const MoveOperand& from, const MoveOperand& to, 88 MoveOp::Type type, uint32_t slotId) { 89 // There is some pattern: 90 // (A -> B) 91 // (B -> A) 92 // 93 // This case handles (A -> B), which we reach first. We save B, then allow 94 // the original move to continue. 95 96 ScratchRegisterScope scratch(masm); 97 98 switch (type) { 99 case MoveOp::FLOAT32: 100 if (to.isMemory()) { 101 ScratchFloat32Scope scratchFloat32(masm); 102 masm.ma_vldr(toAddress(to), scratchFloat32, scratch); 103 // Since it is uncertain if the load will be aligned or not 104 // just fill both of them with the same value. 105 masm.ma_vstr(scratchFloat32, cycleSlot(slotId, 0), scratch); 106 masm.ma_vstr(scratchFloat32, cycleSlot(slotId, 4), scratch); 107 } else if (to.isGeneralReg()) { 108 // Since it is uncertain if the load will be aligned or not 109 // just fill both of them with the same value. 110 masm.ma_str(to.reg(), cycleSlot(slotId, 0), scratch); 111 masm.ma_str(to.reg(), cycleSlot(slotId, 4), scratch); 112 } else { 113 FloatRegister src = to.floatReg(); 114 // Just always store the largest possible size. Currently, this is 115 // a double. When SIMD is added, two doubles will need to be stored. 116 masm.ma_vstr(src.doubleOverlay(), cycleSlot(slotId, 0), scratch); 117 } 118 break; 119 case MoveOp::DOUBLE: 120 if (to.isMemory()) { 121 ScratchDoubleScope scratchDouble(masm); 122 masm.ma_vldr(toAddress(to), scratchDouble, scratch); 123 masm.ma_vstr(scratchDouble, cycleSlot(slotId, 0), scratch); 124 } else if (to.isGeneralRegPair()) { 125 ScratchDoubleScope scratchDouble(masm); 126 masm.ma_vxfer(to.evenReg(), to.oddReg(), scratchDouble); 127 masm.ma_vstr(scratchDouble, cycleSlot(slotId, 0), scratch); 128 } else { 129 masm.ma_vstr(to.floatReg().doubleOverlay(), cycleSlot(slotId, 0), 130 scratch); 131 } 132 break; 133 case MoveOp::INT32: 134 case MoveOp::GENERAL: 135 // an non-vfp value 136 if (to.isMemory()) { 137 Register temp = tempReg(); 138 masm.ma_ldr(toAddress(to), temp, scratch); 139 masm.ma_str(temp, cycleSlot(0, 0), scratch); 140 } else { 141 if (to.reg() == spilledReg_) { 142 // If the destination was spilled, restore it first. 143 masm.ma_ldr(spillSlot(), spilledReg_, scratch); 144 spilledReg_ = InvalidReg; 145 } 146 masm.ma_str(to.reg(), cycleSlot(0, 0), scratch); 147 } 148 break; 149 default: 150 MOZ_CRASH("Unexpected move type"); 151 } 152 } 153 154 void MoveEmitterARM::completeCycle(const MoveOperand& from, 155 const MoveOperand& to, MoveOp::Type type, 156 uint32_t slotId) { 157 // There is some pattern: 158 // (A -> B) 159 // (B -> A) 160 // 161 // This case handles (B -> A), which we reach last. We emit a move from the 162 // saved value of B, to A. 163 164 ScratchRegisterScope scratch(masm); 165 166 switch (type) { 167 case MoveOp::FLOAT32: 168 MOZ_ASSERT(!to.isGeneralRegPair()); 169 if (to.isMemory()) { 170 ScratchFloat32Scope scratchFloat32(masm); 171 masm.ma_vldr(cycleSlot(slotId, 0), scratchFloat32, scratch); 172 masm.ma_vstr(scratchFloat32, toAddress(to), scratch); 173 } else if (to.isGeneralReg()) { 174 MOZ_ASSERT(type == MoveOp::FLOAT32); 175 masm.ma_ldr(toAddress(from), to.reg(), scratch); 176 } else { 177 uint32_t offset = 0; 178 if ((!from.isMemory()) && from.floatReg().numAlignedAliased() == 1) { 179 offset = sizeof(float); 180 } 181 masm.ma_vldr(cycleSlot(slotId, offset), to.floatReg(), scratch); 182 } 183 break; 184 case MoveOp::DOUBLE: 185 MOZ_ASSERT(!to.isGeneralReg()); 186 if (to.isMemory()) { 187 ScratchDoubleScope scratchDouble(masm); 188 masm.ma_vldr(cycleSlot(slotId, 0), scratchDouble, scratch); 189 masm.ma_vstr(scratchDouble, toAddress(to), scratch); 190 } else if (to.isGeneralRegPair()) { 191 MOZ_ASSERT(type == MoveOp::DOUBLE); 192 ScratchDoubleScope scratchDouble(masm); 193 masm.ma_vldr(toAddress(from), scratchDouble, scratch); 194 masm.ma_vxfer(scratchDouble, to.evenReg(), to.oddReg()); 195 } else { 196 uint32_t offset = 0; 197 if ((!from.isMemory()) && from.floatReg().numAlignedAliased() == 1) { 198 offset = sizeof(float); 199 } 200 masm.ma_vldr(cycleSlot(slotId, offset), to.floatReg(), scratch); 201 } 202 break; 203 case MoveOp::INT32: 204 case MoveOp::GENERAL: 205 MOZ_ASSERT(slotId == 0); 206 if (to.isMemory()) { 207 Register temp = tempReg(); 208 masm.ma_ldr(cycleSlot(slotId, 0), temp, scratch); 209 masm.ma_str(temp, toAddress(to), scratch); 210 } else { 211 if (to.reg() == spilledReg_) { 212 // Make sure we don't re-clobber the spilled register later. 213 spilledReg_ = InvalidReg; 214 } 215 masm.ma_ldr(cycleSlot(slotId, 0), to.reg(), scratch); 216 } 217 break; 218 default: 219 MOZ_CRASH("Unexpected move type"); 220 } 221 } 222 223 void MoveEmitterARM::emitMove(const MoveOperand& from, const MoveOperand& to) { 224 // Register pairs are used to store Double values during calls. 225 MOZ_ASSERT(!from.isGeneralRegPair()); 226 MOZ_ASSERT(!to.isGeneralRegPair()); 227 228 ScratchRegisterScope scratch(masm); 229 230 if (to.isGeneralReg() && to.reg() == spilledReg_) { 231 // If the destination is the spilled register, make sure we 232 // don't re-clobber its value. 233 spilledReg_ = InvalidReg; 234 } 235 236 if (from.isGeneralReg()) { 237 if (from.reg() == spilledReg_) { 238 // If the source is a register that has been spilled, make sure 239 // to load the source back into that register. 240 masm.ma_ldr(spillSlot(), spilledReg_, scratch); 241 spilledReg_ = InvalidReg; 242 } 243 if (to.isMemoryOrEffectiveAddress()) { 244 masm.ma_str(from.reg(), toAddress(to), scratch); 245 } else { 246 masm.ma_mov(from.reg(), to.reg()); 247 } 248 } else if (to.isGeneralReg()) { 249 MOZ_ASSERT(from.isMemoryOrEffectiveAddress()); 250 if (from.isMemory()) { 251 masm.ma_ldr(toAddress(from), to.reg(), scratch); 252 } else { 253 masm.ma_add(from.base(), Imm32(from.disp()), to.reg(), scratch); 254 } 255 } else { 256 // Memory to memory gpr move. 257 Register reg = tempReg(); 258 259 MOZ_ASSERT(from.isMemoryOrEffectiveAddress()); 260 if (from.isMemory()) { 261 masm.ma_ldr(toAddress(from), reg, scratch); 262 } else { 263 masm.ma_add(from.base(), Imm32(from.disp()), reg, scratch); 264 } 265 MOZ_ASSERT(to.base() != reg); 266 masm.ma_str(reg, toAddress(to), scratch); 267 } 268 } 269 270 void MoveEmitterARM::emitFloat32Move(const MoveOperand& from, 271 const MoveOperand& to) { 272 // Register pairs are used to store Double values during calls. 273 MOZ_ASSERT(!from.isGeneralRegPair()); 274 MOZ_ASSERT(!to.isGeneralRegPair()); 275 276 ScratchRegisterScope scratch(masm); 277 278 if (from.isFloatReg()) { 279 if (to.isFloatReg()) { 280 masm.ma_vmov_f32(from.floatReg(), to.floatReg()); 281 } else if (to.isGeneralReg()) { 282 masm.ma_vxfer(from.floatReg(), to.reg()); 283 } else { 284 masm.ma_vstr(VFPRegister(from.floatReg()).singleOverlay(), toAddress(to), 285 scratch); 286 } 287 } else if (from.isGeneralReg()) { 288 if (to.isFloatReg()) { 289 masm.ma_vxfer(from.reg(), to.floatReg()); 290 } else if (to.isGeneralReg()) { 291 masm.ma_mov(from.reg(), to.reg()); 292 } else { 293 masm.ma_str(from.reg(), toAddress(to), scratch); 294 } 295 } else if (to.isFloatReg()) { 296 masm.ma_vldr(toAddress(from), VFPRegister(to.floatReg()).singleOverlay(), 297 scratch); 298 } else if (to.isGeneralReg()) { 299 masm.ma_ldr(toAddress(from), to.reg(), scratch); 300 } else { 301 // Memory to memory move. 302 MOZ_ASSERT(from.isMemory()); 303 ScratchFloat32Scope scratchFloat32(masm); 304 masm.ma_vldr(toAddress(from), scratchFloat32, scratch); 305 masm.ma_vstr(scratchFloat32, toAddress(to), scratch); 306 } 307 } 308 309 void MoveEmitterARM::emitDoubleMove(const MoveOperand& from, 310 const MoveOperand& to) { 311 // Registers are used to store pointers / int32 / float32 values. 312 MOZ_ASSERT(!from.isGeneralReg()); 313 MOZ_ASSERT(!to.isGeneralReg()); 314 315 ScratchRegisterScope scratch(masm); 316 317 if (from.isFloatReg()) { 318 if (to.isFloatReg()) { 319 masm.ma_vmov(from.floatReg(), to.floatReg()); 320 } else if (to.isGeneralRegPair()) { 321 masm.ma_vxfer(from.floatReg(), to.evenReg(), to.oddReg()); 322 } else { 323 masm.ma_vstr(from.floatReg(), toAddress(to), scratch); 324 } 325 } else if (from.isGeneralRegPair()) { 326 if (to.isFloatReg()) { 327 masm.ma_vxfer(from.evenReg(), from.oddReg(), to.floatReg()); 328 } else if (to.isGeneralRegPair()) { 329 MOZ_ASSERT(!from.aliases(to)); 330 masm.ma_mov(from.evenReg(), to.evenReg()); 331 masm.ma_mov(from.oddReg(), to.oddReg()); 332 } else { 333 ScratchDoubleScope scratchDouble(masm); 334 masm.ma_vxfer(from.evenReg(), from.oddReg(), scratchDouble); 335 masm.ma_vstr(scratchDouble, toAddress(to), scratch); 336 } 337 } else if (to.isFloatReg()) { 338 masm.ma_vldr(toAddress(from), to.floatReg(), scratch); 339 } else if (to.isGeneralRegPair()) { 340 MOZ_ASSERT(from.isMemory()); 341 Address src = toAddress(from); 342 // Note: We can safely use the MoveOperand's displacement here, 343 // even if the base is SP: MoveEmitter::toOperand adjusts 344 // SP-relative operands by the difference between the current 345 // stack usage and stackAdjust, which emitter.finish() resets to 346 // 0. 347 // 348 // Warning: if the offset isn't within [-255,+255] then this 349 // will assert-fail (or, if non-debug, load the wrong words). 350 // Nothing uses such an offset at the time of this writing. 351 masm.ma_ldrd(EDtrAddr(src.base, EDtrOffImm(src.offset)), to.evenReg(), 352 to.oddReg()); 353 } else { 354 // Memory to memory move. 355 MOZ_ASSERT(from.isMemory()); 356 ScratchDoubleScope scratchDouble(masm); 357 masm.ma_vldr(toAddress(from), scratchDouble, scratch); 358 masm.ma_vstr(scratchDouble, toAddress(to), scratch); 359 } 360 } 361 362 void MoveEmitterARM::emit(const MoveOp& move) { 363 const MoveOperand& from = move.from(); 364 const MoveOperand& to = move.to(); 365 366 if (move.isCycleEnd() && move.isCycleBegin()) { 367 // A fun consequence of aliased registers is you can have multiple 368 // cycles at once, and one can end exactly where another begins. 369 breakCycle(from, to, move.endCycleType(), move.cycleBeginSlot()); 370 completeCycle(from, to, move.type(), move.cycleEndSlot()); 371 return; 372 } 373 374 if (move.isCycleEnd()) { 375 MOZ_ASSERT(inCycle_); 376 completeCycle(from, to, move.type(), move.cycleEndSlot()); 377 MOZ_ASSERT(inCycle_ > 0); 378 inCycle_--; 379 return; 380 } 381 382 if (move.isCycleBegin()) { 383 breakCycle(from, to, move.endCycleType(), move.cycleBeginSlot()); 384 inCycle_++; 385 } 386 387 switch (move.type()) { 388 case MoveOp::FLOAT32: 389 emitFloat32Move(from, to); 390 break; 391 case MoveOp::DOUBLE: 392 emitDoubleMove(from, to); 393 break; 394 case MoveOp::INT32: 395 case MoveOp::GENERAL: 396 emitMove(from, to); 397 break; 398 default: 399 MOZ_CRASH("Unexpected move type"); 400 } 401 } 402 403 void MoveEmitterARM::assertDone() { MOZ_ASSERT(inCycle_ == 0); } 404 405 void MoveEmitterARM::finish() { 406 assertDone(); 407 408 if (pushedAtSpill_ != -1 && spilledReg_ != InvalidReg) { 409 ScratchRegisterScope scratch(masm); 410 masm.ma_ldr(spillSlot(), spilledReg_, scratch); 411 } 412 masm.freeStack(masm.framePushed() - pushedAtStart_); 413 }