MacroAssembler-arm.cpp (235109B)
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/MacroAssembler-arm.h" 8 9 #include "mozilla/Casting.h" 10 #include "mozilla/DebugOnly.h" 11 #include "mozilla/MathAlgorithms.h" 12 #include "mozilla/Maybe.h" 13 14 #include "jsmath.h" 15 16 #include "jit/arm/Simulator-arm.h" 17 #include "jit/AtomicOp.h" 18 #include "jit/AtomicOperations.h" 19 #include "jit/Bailouts.h" 20 #include "jit/BaselineFrame.h" 21 #include "jit/JitFrames.h" 22 #include "jit/JitRuntime.h" 23 #include "jit/MacroAssembler.h" 24 #include "jit/MoveEmitter.h" 25 #include "jit/ProcessExecutableMemory.h" 26 #include "js/ScalarType.h" // js::Scalar::Type 27 #include "util/Memory.h" 28 #include "vm/BigIntType.h" 29 #include "vm/JitActivation.h" // js::jit::JitActivation 30 #include "vm/JSContext.h" 31 #include "vm/StringType.h" 32 #include "wasm/WasmStubs.h" 33 34 #include "jit/MacroAssembler-inl.h" 35 36 using namespace js; 37 using namespace jit; 38 39 using mozilla::Abs; 40 using mozilla::BitwiseCast; 41 using mozilla::DebugOnly; 42 using mozilla::IsPositiveZero; 43 using mozilla::Maybe; 44 45 bool isValueDTRDCandidate(ValueOperand& val) { 46 // In order to be used for a DTRD memory function, the two target registers 47 // need to be a) Adjacent, with the tag larger than the payload, and b) 48 // Aligned to a multiple of two. 49 if ((val.typeReg().code() != (val.payloadReg().code() + 1))) { 50 return false; 51 } 52 if ((val.payloadReg().code() & 1) != 0) { 53 return false; 54 } 55 return true; 56 } 57 58 void MacroAssemblerARM::convertBoolToInt32(Register source, Register dest) { 59 // Note that C++ bool is only 1 byte, so zero extend it to clear the 60 // higher-order bits. 61 as_and(dest, source, Imm8(0xff)); 62 } 63 64 void MacroAssemblerARM::convertInt32ToDouble(Register src, 65 FloatRegister dest_) { 66 // Direct conversions aren't possible. 67 VFPRegister dest = VFPRegister(dest_); 68 as_vxfer(src, InvalidReg, dest.sintOverlay(), CoreToFloat); 69 as_vcvt(dest, dest.sintOverlay()); 70 } 71 72 void MacroAssemblerARM::convertInt32ToDouble(const Address& src, 73 FloatRegister dest) { 74 ScratchDoubleScope scratch(asMasm()); 75 SecondScratchRegisterScope scratch2(asMasm()); 76 ma_vldr(src, scratch, scratch2); 77 as_vcvt(dest, VFPRegister(scratch).sintOverlay()); 78 } 79 80 void MacroAssemblerARM::convertInt32ToDouble(const BaseIndex& src, 81 FloatRegister dest) { 82 Register base = src.base; 83 uint32_t scale = Imm32::ShiftOf(src.scale).value; 84 85 ScratchRegisterScope scratch(asMasm()); 86 SecondScratchRegisterScope scratch2(asMasm()); 87 88 if (src.offset != 0) { 89 ma_add(base, Imm32(src.offset), scratch, scratch2); 90 base = scratch; 91 } 92 ma_ldr(DTRAddr(base, DtrRegImmShift(src.index, LSL, scale)), scratch); 93 convertInt32ToDouble(scratch, dest); 94 } 95 96 void MacroAssemblerARM::convertUInt32ToDouble(Register src, 97 FloatRegister dest_) { 98 // Direct conversions aren't possible. 99 VFPRegister dest = VFPRegister(dest_); 100 as_vxfer(src, InvalidReg, dest.uintOverlay(), CoreToFloat); 101 as_vcvt(dest, dest.uintOverlay()); 102 } 103 104 static const double TO_DOUBLE_HIGH_SCALE = 0x100000000; 105 106 void MacroAssemblerARM::convertUInt32ToFloat32(Register src, 107 FloatRegister dest_) { 108 // Direct conversions aren't possible. 109 VFPRegister dest = VFPRegister(dest_); 110 as_vxfer(src, InvalidReg, dest.uintOverlay(), CoreToFloat); 111 as_vcvt(VFPRegister(dest).singleOverlay(), dest.uintOverlay()); 112 } 113 114 void MacroAssemblerARM::convertDoubleToFloat32(FloatRegister src, 115 FloatRegister dest, 116 Condition c) { 117 as_vcvt(VFPRegister(dest).singleOverlay(), VFPRegister(src), false, c); 118 } 119 120 // Checks whether a double is representable as a 32-bit integer. If so, the 121 // integer is written to the output register. Otherwise, a bailout is taken to 122 // the given snapshot. This function overwrites the scratch float register. 123 void MacroAssemblerARM::convertDoubleToInt32(FloatRegister src, Register dest, 124 Label* fail, 125 bool negativeZeroCheck) { 126 // Convert the floating point value to an integer, if it did not fit, then 127 // when we convert it *back* to a float, it will have a different value, 128 // which we can test. 129 ScratchDoubleScope scratchDouble(asMasm()); 130 ScratchRegisterScope scratch(asMasm()); 131 132 FloatRegister scratchSIntReg = scratchDouble.sintOverlay(); 133 134 ma_vcvt_F64_I32(src, scratchSIntReg); 135 // Move the value into the dest register. 136 ma_vxfer(scratchSIntReg, dest); 137 ma_vcvt_I32_F64(scratchSIntReg, scratchDouble); 138 ma_vcmp(src, scratchDouble); 139 as_vmrs(pc); 140 ma_b(fail, Assembler::VFP_NotEqualOrUnordered); 141 142 if (negativeZeroCheck) { 143 Label nonzero; 144 as_cmp(dest, Imm8(0)); 145 ma_b(&nonzero, Assembler::NotEqual); 146 // Test and bail for -0.0, when integer result is 0. Move the top word 147 // of the double into the output reg, if it is non-zero, then the 148 // original value was -0.0. 149 as_vxfer(dest, InvalidReg, src, FloatToCore, Assembler::Always, 1); 150 as_cmp(dest, Imm8(0)); 151 ma_b(fail, Assembler::LessThan); 152 ma_mov(Imm32(0), dest); 153 bind(&nonzero); 154 } 155 } 156 157 // Checks whether a float32 is representable as a 32-bit integer. If so, the 158 // integer is written to the output register. Otherwise, a bailout is taken to 159 // the given snapshot. This function overwrites the scratch float register. 160 void MacroAssemblerARM::convertFloat32ToInt32(FloatRegister src, Register dest, 161 Label* fail, 162 bool negativeZeroCheck) { 163 // Converting the floating point value to an integer and then converting it 164 // back to a float32 would not work, as float to int32 conversions are 165 // clamping (e.g. float(INT32_MAX + 1) would get converted into INT32_MAX 166 // and then back to float(INT32_MAX + 1)). If this ever happens, we just 167 // bail out. 168 ScratchFloat32Scope scratchFloat(asMasm()); 169 ScratchRegisterScope scratch(asMasm()); 170 171 FloatRegister ScratchSIntReg = scratchFloat.sintOverlay(); 172 ma_vcvt_F32_I32(src, ScratchSIntReg); 173 174 // Store the result 175 ma_vxfer(ScratchSIntReg, dest); 176 177 ma_vcvt_I32_F32(ScratchSIntReg, scratchFloat); 178 ma_vcmp(src, scratchFloat); 179 as_vmrs(pc); 180 ma_b(fail, Assembler::VFP_NotEqualOrUnordered); 181 182 // Bail out in the clamped cases. 183 ma_cmp(dest, Imm32(0x7fffffff), scratch); 184 ma_cmp(dest, Imm32(0x80000000), scratch, Assembler::NotEqual); 185 ma_b(fail, Assembler::Equal); 186 187 if (negativeZeroCheck) { 188 Label nonzero; 189 as_cmp(dest, Imm8(0)); 190 ma_b(&nonzero, Assembler::NotEqual); 191 // Test and bail for -0.0, when integer result is 0. Move the float into 192 // the output reg, and if it is non-zero then the original value was 193 // -0.0 194 as_vxfer(dest, InvalidReg, VFPRegister(src).singleOverlay(), FloatToCore, 195 Assembler::Always, 0); 196 as_cmp(dest, Imm8(0)); 197 ma_b(fail, Assembler::LessThan); 198 ma_mov(Imm32(0), dest); 199 bind(&nonzero); 200 } 201 } 202 203 void MacroAssemblerARM::convertFloat32ToDouble(FloatRegister src, 204 FloatRegister dest) { 205 MOZ_ASSERT(dest.isDouble()); 206 MOZ_ASSERT(src.isSingle()); 207 as_vcvt(VFPRegister(dest), VFPRegister(src).singleOverlay()); 208 } 209 210 void MacroAssemblerARM::convertInt32ToFloat32(Register src, 211 FloatRegister dest) { 212 // Direct conversions aren't possible. 213 as_vxfer(src, InvalidReg, dest.sintOverlay(), CoreToFloat); 214 as_vcvt(dest.singleOverlay(), dest.sintOverlay()); 215 } 216 217 void MacroAssemblerARM::convertInt32ToFloat32(const Address& src, 218 FloatRegister dest) { 219 ScratchFloat32Scope scratch(asMasm()); 220 SecondScratchRegisterScope scratch2(asMasm()); 221 ma_vldr(src, scratch, scratch2); 222 as_vcvt(dest, VFPRegister(scratch).sintOverlay()); 223 } 224 225 void MacroAssemblerARM::convertFloat32ToFloat16(FloatRegister src, 226 FloatRegister dest) { 227 MOZ_ASSERT(ARMFlags::HasFPHalfPrecision()); 228 MOZ_ASSERT(src.isSingle()); 229 MOZ_ASSERT(dest.isSingle()); 230 231 as_vcvtb_s2h(dest, src); 232 } 233 234 void MacroAssemblerARM::convertFloat16ToFloat32(FloatRegister src, 235 FloatRegister dest) { 236 MOZ_ASSERT(ARMFlags::HasFPHalfPrecision()); 237 MOZ_ASSERT(src.isSingle()); 238 MOZ_ASSERT(dest.isSingle()); 239 240 as_vcvtb_h2s(dest, src); 241 } 242 243 void MacroAssemblerARM::convertInt32ToFloat16(Register src, 244 FloatRegister dest) { 245 // Convert Int32 to Float32. 246 convertInt32ToFloat32(src, dest); 247 248 // Convert Float32 to Float16. 249 convertFloat32ToFloat16(dest, dest); 250 } 251 252 bool MacroAssemblerARM::alu_dbl(Register src1, Imm32 imm, Register dest, 253 ALUOp op, SBit s, Condition c) { 254 if ((s == SetCC && !condsAreSafe(op)) || !can_dbl(op)) { 255 return false; 256 } 257 258 ALUOp interop = getDestVariant(op); 259 Imm8::TwoImm8mData both = Imm8::EncodeTwoImms(imm.value); 260 if (both.fst().invalid()) { 261 return false; 262 } 263 264 // For the most part, there is no good reason to set the condition codes for 265 // the first instruction. We can do better things if the second instruction 266 // doesn't have a dest, such as check for overflow by doing first operation 267 // don't do second operation if first operation overflowed. This preserves 268 // the overflow condition code. Unfortunately, it is horribly brittle. 269 as_alu(dest, src1, Operand2(both.fst()), interop, LeaveCC, c); 270 as_alu(dest, dest, Operand2(both.snd()), op, s, c); 271 return true; 272 } 273 274 void MacroAssemblerARM::ma_alu(Register src1, Imm32 imm, Register dest, 275 AutoRegisterScope& scratch, ALUOp op, SBit s, 276 Condition c) { 277 // ma_mov should be used for moves. 278 MOZ_ASSERT(op != OpMov); 279 MOZ_ASSERT(op != OpMvn); 280 MOZ_ASSERT(src1 != scratch); 281 282 // As it turns out, if you ask for a compare-like instruction you *probably* 283 // want it to set condition codes. 284 MOZ_ASSERT_IF(dest == InvalidReg, s == SetCC); 285 286 // The operator gives us the ability to determine how this can be used. 287 Imm8 imm8 = Imm8(imm.value); 288 // One instruction: If we can encode it using an imm8m, then do so. 289 if (!imm8.invalid()) { 290 as_alu(dest, src1, imm8, op, s, c); 291 return; 292 } 293 294 // One instruction, negated: 295 Imm32 negImm = imm; 296 Register negDest; 297 ALUOp negOp = ALUNeg(op, dest, scratch, &negImm, &negDest); 298 Imm8 negImm8 = Imm8(negImm.value); 299 // 'add r1, r2, -15' can be replaced with 'sub r1, r2, 15'. 300 // The dest can be replaced (InvalidReg => scratch). 301 // This is useful if we wish to negate tst. tst has an invalid (aka not 302 // used) dest, but its negation bic requires a dest. 303 if (negOp != OpInvalid && !negImm8.invalid()) { 304 as_alu(negDest, src1, negImm8, negOp, s, c); 305 return; 306 } 307 308 // Start by attempting to generate a two instruction form. Some things 309 // cannot be made into two-inst forms correctly. Namely, adds dest, src, 310 // 0xffff. Since we want the condition codes (and don't know which ones 311 // will be checked), we need to assume that the overflow flag will be 312 // checked and add{,s} dest, src, 0xff00; add{,s} dest, dest, 0xff is not 313 // guaranteed to set the overflof flag the same as the (theoretical) one 314 // instruction variant. 315 if (alu_dbl(src1, imm, dest, op, s, c)) { 316 return; 317 } 318 319 // And try with its negative. 320 if (negOp != OpInvalid && alu_dbl(src1, negImm, negDest, negOp, s, c)) { 321 return; 322 } 323 324 ma_mov(imm, scratch, c); 325 as_alu(dest, src1, O2Reg(scratch), op, s, c); 326 } 327 328 void MacroAssemblerARM::ma_alu(Register src1, Operand op2, Register dest, 329 ALUOp op, SBit s, Assembler::Condition c) { 330 MOZ_ASSERT(op2.tag() == Operand::Tag::OP2); 331 as_alu(dest, src1, op2.toOp2(), op, s, c); 332 } 333 334 void MacroAssemblerARM::ma_alu(Register src1, Operand2 op2, Register dest, 335 ALUOp op, SBit s, Condition c) { 336 as_alu(dest, src1, op2, op, s, c); 337 } 338 339 void MacroAssemblerARM::ma_nop() { as_nop(); } 340 341 BufferOffset MacroAssemblerARM::ma_movPatchable(Imm32 imm_, Register dest, 342 Assembler::Condition c) { 343 int32_t imm = imm_.value; 344 if (ARMFlags::HasMOVWT()) { 345 AutoForbidPoolsAndNops afp(this, 346 /* max number of instructions in scope = */ 2); 347 BufferOffset offset = as_movw(dest, Imm16(imm & 0xffff), c); 348 as_movt(dest, Imm16(imm >> 16 & 0xffff), c); 349 return offset; 350 } else { 351 return as_Imm32Pool(dest, imm, c); 352 } 353 } 354 355 BufferOffset MacroAssemblerARM::ma_movPatchable(ImmPtr imm, Register dest, 356 Assembler::Condition c) { 357 return ma_movPatchable(Imm32(int32_t(imm.value)), dest, c); 358 } 359 360 /* static */ 361 template <class Iter> 362 void MacroAssemblerARM::ma_mov_patch(Imm32 imm32, Register dest, 363 Assembler::Condition c, RelocStyle rs, 364 Iter iter) { 365 // The current instruction must be an actual instruction, 366 // not automatically-inserted boilerplate. 367 MOZ_ASSERT(iter.cur()); 368 MOZ_ASSERT(iter.cur() == iter.maybeSkipAutomaticInstructions()); 369 370 int32_t imm = imm32.value; 371 switch (rs) { 372 case L_MOVWT: 373 Assembler::as_movw_patch(dest, Imm16(imm & 0xffff), c, iter.cur()); 374 Assembler::as_movt_patch(dest, Imm16(imm >> 16 & 0xffff), c, iter.next()); 375 break; 376 case L_LDR: 377 Assembler::WritePoolEntry(iter.cur(), c, imm); 378 break; 379 } 380 } 381 382 template void MacroAssemblerARM::ma_mov_patch(Imm32 imm32, Register dest, 383 Assembler::Condition c, 384 RelocStyle rs, 385 InstructionIterator iter); 386 template void MacroAssemblerARM::ma_mov_patch(Imm32 imm32, Register dest, 387 Assembler::Condition c, 388 RelocStyle rs, 389 BufferInstructionIterator iter); 390 391 void MacroAssemblerARM::ma_mov(Register src, Register dest, SBit s, 392 Assembler::Condition c) { 393 if (s == SetCC || dest != src) { 394 as_mov(dest, O2Reg(src), s, c); 395 } 396 } 397 398 void MacroAssemblerARM::ma_mov(Imm32 imm, Register dest, 399 Assembler::Condition c) { 400 // Try mov with Imm8 operand. 401 Imm8 imm8 = Imm8(imm.value); 402 if (!imm8.invalid()) { 403 as_alu(dest, InvalidReg, imm8, OpMov, LeaveCC, c); 404 return; 405 } 406 407 // Try mvn with Imm8 operand. 408 Imm8 negImm8 = Imm8(~imm.value); 409 if (!negImm8.invalid()) { 410 as_alu(dest, InvalidReg, negImm8, OpMvn, LeaveCC, c); 411 return; 412 } 413 414 // Try movw/movt. 415 if (ARMFlags::HasMOVWT()) { 416 // ARMv7 supports movw/movt. movw zero-extends its 16 bit argument, 417 // so we can set the register this way. movt leaves the bottom 16 418 // bits in tact, so we always need a movw. 419 as_movw(dest, Imm16(imm.value & 0xffff), c); 420 if (uint32_t(imm.value) >> 16) { 421 as_movt(dest, Imm16(uint32_t(imm.value) >> 16), c); 422 } 423 return; 424 } 425 426 // If we don't have movw/movt, we need a load. 427 as_Imm32Pool(dest, imm.value, c); 428 } 429 430 void MacroAssemblerARM::ma_mov(ImmWord imm, Register dest, 431 Assembler::Condition c) { 432 ma_mov(Imm32(imm.value), dest, c); 433 } 434 435 void MacroAssemblerARM::ma_mov(ImmGCPtr ptr, Register dest) { 436 BufferOffset offset = 437 ma_movPatchable(Imm32(uintptr_t(ptr.value)), dest, Always); 438 writeDataRelocation(offset, ptr); 439 } 440 441 // Shifts (just a move with a shifting op2) 442 void MacroAssemblerARM::ma_lsl(Imm32 shift, Register src, Register dst) { 443 as_mov(dst, lsl(src, shift.value)); 444 } 445 446 void MacroAssemblerARM::ma_lsr(Imm32 shift, Register src, Register dst) { 447 as_mov(dst, lsr(src, shift.value)); 448 } 449 450 void MacroAssemblerARM::ma_asr(Imm32 shift, Register src, Register dst) { 451 as_mov(dst, asr(src, shift.value)); 452 } 453 454 void MacroAssemblerARM::ma_ror(Imm32 shift, Register src, Register dst) { 455 as_mov(dst, ror(src, shift.value)); 456 } 457 458 void MacroAssemblerARM::ma_rol(Imm32 shift, Register src, Register dst) { 459 as_mov(dst, rol(src, shift.value)); 460 } 461 462 // Shifts (just a move with a shifting op2) 463 void MacroAssemblerARM::ma_lsl(Register shift, Register src, Register dst) { 464 as_mov(dst, lsl(src, shift)); 465 } 466 467 void MacroAssemblerARM::ma_lsr(Register shift, Register src, Register dst) { 468 as_mov(dst, lsr(src, shift)); 469 } 470 471 void MacroAssemblerARM::ma_asr(Register shift, Register src, Register dst) { 472 as_mov(dst, asr(src, shift)); 473 } 474 475 void MacroAssemblerARM::ma_ror(Register shift, Register src, Register dst) { 476 as_mov(dst, ror(src, shift)); 477 } 478 479 void MacroAssemblerARM::ma_rol(Register shift, Register src, Register dst, 480 AutoRegisterScope& scratch) { 481 as_rsb(scratch, shift, Imm8(32)); 482 as_mov(dst, ror(src, scratch)); 483 } 484 485 // Move not (dest <- ~src) 486 void MacroAssemblerARM::ma_mvn(Register src1, Register dest, SBit s, 487 Assembler::Condition c) { 488 as_alu(dest, InvalidReg, O2Reg(src1), OpMvn, s, c); 489 } 490 491 // Negate (dest <- -src), src is a register, rather than a general op2. 492 void MacroAssemblerARM::ma_neg(Register src1, Register dest, SBit s, 493 Assembler::Condition c) { 494 as_rsb(dest, src1, Imm8(0), s, c); 495 } 496 497 void MacroAssemblerARM::ma_neg(Register64 src, Register64 dest) { 498 as_rsb(dest.low, src.low, Imm8(0), SetCC); 499 as_rsc(dest.high, src.high, Imm8(0)); 500 } 501 502 // And. 503 void MacroAssemblerARM::ma_and(Register src, Register dest, SBit s, 504 Assembler::Condition c) { 505 ma_and(dest, src, dest); 506 } 507 508 void MacroAssemblerARM::ma_and(Register src1, Register src2, Register dest, 509 SBit s, Assembler::Condition c) { 510 as_and(dest, src1, O2Reg(src2), s, c); 511 } 512 513 void MacroAssemblerARM::ma_and(Imm32 imm, Register dest, 514 AutoRegisterScope& scratch, SBit s, 515 Assembler::Condition c) { 516 ma_alu(dest, imm, dest, scratch, OpAnd, s, c); 517 } 518 519 void MacroAssemblerARM::ma_and(Imm32 imm, Register src1, Register dest, 520 AutoRegisterScope& scratch, SBit s, 521 Assembler::Condition c) { 522 ma_alu(src1, imm, dest, scratch, OpAnd, s, c); 523 } 524 525 // Bit clear (dest <- dest & ~imm) or (dest <- src1 & ~src2). 526 void MacroAssemblerARM::ma_bic(Imm32 imm, Register dest, 527 AutoRegisterScope& scratch, SBit s, 528 Assembler::Condition c) { 529 ma_alu(dest, imm, dest, scratch, OpBic, s, c); 530 } 531 532 // Exclusive or. 533 void MacroAssemblerARM::ma_eor(Register src, Register dest, SBit s, 534 Assembler::Condition c) { 535 ma_eor(dest, src, dest, s, c); 536 } 537 538 void MacroAssemblerARM::ma_eor(Register src1, Register src2, Register dest, 539 SBit s, Assembler::Condition c) { 540 as_eor(dest, src1, O2Reg(src2), s, c); 541 } 542 543 void MacroAssemblerARM::ma_eor(Imm32 imm, Register dest, 544 AutoRegisterScope& scratch, SBit s, 545 Assembler::Condition c) { 546 ma_alu(dest, imm, dest, scratch, OpEor, s, c); 547 } 548 549 void MacroAssemblerARM::ma_eor(Imm32 imm, Register src1, Register dest, 550 AutoRegisterScope& scratch, SBit s, 551 Assembler::Condition c) { 552 ma_alu(src1, imm, dest, scratch, OpEor, s, c); 553 } 554 555 // Or. 556 void MacroAssemblerARM::ma_orr(Register src, Register dest, SBit s, 557 Assembler::Condition c) { 558 ma_orr(dest, src, dest, s, c); 559 } 560 561 void MacroAssemblerARM::ma_orr(Register src1, Register src2, Register dest, 562 SBit s, Assembler::Condition c) { 563 as_orr(dest, src1, O2Reg(src2), s, c); 564 } 565 566 void MacroAssemblerARM::ma_orr(Imm32 imm, Register dest, 567 AutoRegisterScope& scratch, SBit s, 568 Assembler::Condition c) { 569 ma_alu(dest, imm, dest, scratch, OpOrr, s, c); 570 } 571 572 void MacroAssemblerARM::ma_orr(Imm32 imm, Register src1, Register dest, 573 AutoRegisterScope& scratch, SBit s, 574 Assembler::Condition c) { 575 ma_alu(src1, imm, dest, scratch, OpOrr, s, c); 576 } 577 578 // Arithmetic-based ops. 579 // Add with carry. 580 void MacroAssemblerARM::ma_adc(Imm32 imm, Register dest, 581 AutoRegisterScope& scratch, SBit s, 582 Condition c) { 583 ma_alu(dest, imm, dest, scratch, OpAdc, s, c); 584 } 585 586 void MacroAssemblerARM::ma_adc(Register src, Register dest, SBit s, 587 Condition c) { 588 as_alu(dest, dest, O2Reg(src), OpAdc, s, c); 589 } 590 591 void MacroAssemblerARM::ma_adc(Register src1, Register src2, Register dest, 592 SBit s, Condition c) { 593 as_alu(dest, src1, O2Reg(src2), OpAdc, s, c); 594 } 595 596 void MacroAssemblerARM::ma_adc(Register src1, Imm32 op, Register dest, 597 AutoRegisterScope& scratch, SBit s, 598 Condition c) { 599 ma_alu(src1, op, dest, scratch, OpAdc, s, c); 600 } 601 602 // Add. 603 void MacroAssemblerARM::ma_add(Imm32 imm, Register dest, 604 AutoRegisterScope& scratch, SBit s, 605 Condition c) { 606 ma_alu(dest, imm, dest, scratch, OpAdd, s, c); 607 } 608 609 void MacroAssemblerARM::ma_add(Register src1, Register dest, SBit s, 610 Condition c) { 611 ma_alu(dest, O2Reg(src1), dest, OpAdd, s, c); 612 } 613 614 void MacroAssemblerARM::ma_add(Register src1, Register src2, Register dest, 615 SBit s, Condition c) { 616 as_alu(dest, src1, O2Reg(src2), OpAdd, s, c); 617 } 618 619 void MacroAssemblerARM::ma_add(Register src1, Operand op, Register dest, SBit s, 620 Condition c) { 621 ma_alu(src1, op, dest, OpAdd, s, c); 622 } 623 624 void MacroAssemblerARM::ma_add(Register src1, Imm32 op, Register dest, 625 AutoRegisterScope& scratch, SBit s, 626 Condition c) { 627 ma_alu(src1, op, dest, scratch, OpAdd, s, c); 628 } 629 630 // Subtract with carry. 631 void MacroAssemblerARM::ma_sbc(Imm32 imm, Register dest, 632 AutoRegisterScope& scratch, SBit s, 633 Condition c) { 634 ma_alu(dest, imm, dest, scratch, OpSbc, s, c); 635 } 636 637 void MacroAssemblerARM::ma_sbc(Register src1, Register dest, SBit s, 638 Condition c) { 639 as_alu(dest, dest, O2Reg(src1), OpSbc, s, c); 640 } 641 642 void MacroAssemblerARM::ma_sbc(Register src1, Register src2, Register dest, 643 SBit s, Condition c) { 644 as_alu(dest, src1, O2Reg(src2), OpSbc, s, c); 645 } 646 647 // Subtract. 648 void MacroAssemblerARM::ma_sub(Imm32 imm, Register dest, 649 AutoRegisterScope& scratch, SBit s, 650 Condition c) { 651 ma_alu(dest, imm, dest, scratch, OpSub, s, c); 652 } 653 654 void MacroAssemblerARM::ma_sub(Register src1, Register dest, SBit s, 655 Condition c) { 656 ma_alu(dest, Operand(src1), dest, OpSub, s, c); 657 } 658 659 void MacroAssemblerARM::ma_sub(Register src1, Register src2, Register dest, 660 SBit s, Condition c) { 661 ma_alu(src1, Operand(src2), dest, OpSub, s, c); 662 } 663 664 void MacroAssemblerARM::ma_sub(Register src1, Operand op, Register dest, SBit s, 665 Condition c) { 666 ma_alu(src1, op, dest, OpSub, s, c); 667 } 668 669 void MacroAssemblerARM::ma_sub(Register src1, Imm32 op, Register dest, 670 AutoRegisterScope& scratch, SBit s, 671 Condition c) { 672 ma_alu(src1, op, dest, scratch, OpSub, s, c); 673 } 674 675 // Reverse subtract. 676 void MacroAssemblerARM::ma_rsb(Imm32 imm, Register dest, 677 AutoRegisterScope& scratch, SBit s, 678 Condition c) { 679 ma_alu(dest, imm, dest, scratch, OpRsb, s, c); 680 } 681 682 void MacroAssemblerARM::ma_rsb(Register src1, Register dest, SBit s, 683 Condition c) { 684 as_alu(dest, src1, O2Reg(dest), OpRsb, s, c); 685 } 686 687 void MacroAssemblerARM::ma_rsb(Register src1, Register src2, Register dest, 688 SBit s, Condition c) { 689 as_alu(dest, src1, O2Reg(src2), OpRsb, s, c); 690 } 691 692 void MacroAssemblerARM::ma_rsb(Register src1, Imm32 op2, Register dest, 693 AutoRegisterScope& scratch, SBit s, 694 Condition c) { 695 ma_alu(src1, op2, dest, scratch, OpRsb, s, c); 696 } 697 698 // Reverse subtract with carry. 699 void MacroAssemblerARM::ma_rsc(Imm32 imm, Register dest, 700 AutoRegisterScope& scratch, SBit s, 701 Condition c) { 702 ma_alu(dest, imm, dest, scratch, OpRsc, s, c); 703 } 704 705 void MacroAssemblerARM::ma_rsc(Register src1, Register dest, SBit s, 706 Condition c) { 707 as_alu(dest, dest, O2Reg(src1), OpRsc, s, c); 708 } 709 710 void MacroAssemblerARM::ma_rsc(Register src1, Register src2, Register dest, 711 SBit s, Condition c) { 712 as_alu(dest, src1, O2Reg(src2), OpRsc, s, c); 713 } 714 715 // Compares/tests. 716 // Compare negative (sets condition codes as src1 + src2 would). 717 void MacroAssemblerARM::ma_cmn(Register src1, Imm32 imm, 718 AutoRegisterScope& scratch, Condition c) { 719 ma_alu(src1, imm, InvalidReg, scratch, OpCmn, SetCC, c); 720 } 721 722 void MacroAssemblerARM::ma_cmn(Register src1, Register src2, Condition c) { 723 as_alu(InvalidReg, src2, O2Reg(src1), OpCmn, SetCC, c); 724 } 725 726 void MacroAssemblerARM::ma_cmn(Register src1, Operand op, Condition c) { 727 MOZ_CRASH("Feature NYI"); 728 } 729 730 // Compare (src - src2). 731 void MacroAssemblerARM::ma_cmp(Register src1, Imm32 imm, 732 AutoRegisterScope& scratch, Condition c) { 733 ma_alu(src1, imm, InvalidReg, scratch, OpCmp, SetCC, c); 734 } 735 736 void MacroAssemblerARM::ma_cmp(Register src1, ImmTag tag, Condition c) { 737 // ImmTag comparisons can always be done without use of a scratch register. 738 Imm8 negtag = Imm8(-tag.value); 739 MOZ_ASSERT(!negtag.invalid()); 740 as_cmn(src1, negtag, c); 741 } 742 743 void MacroAssemblerARM::ma_cmp(Register src1, ImmWord ptr, 744 AutoRegisterScope& scratch, Condition c) { 745 ma_cmp(src1, Imm32(ptr.value), scratch, c); 746 } 747 748 void MacroAssemblerARM::ma_cmp(Register src1, ImmGCPtr ptr, 749 AutoRegisterScope& scratch, Condition c) { 750 ma_mov(ptr, scratch); 751 ma_cmp(src1, scratch, c); 752 } 753 754 void MacroAssemblerARM::ma_cmp(Register src1, Operand op, 755 AutoRegisterScope& scratch, 756 AutoRegisterScope& scratch2, Condition c) { 757 switch (op.tag()) { 758 case Operand::Tag::OP2: 759 as_cmp(src1, op.toOp2(), c); 760 break; 761 case Operand::Tag::MEM: 762 ma_ldr(op.toAddress(), scratch, scratch2); 763 as_cmp(src1, O2Reg(scratch), c); 764 break; 765 default: 766 MOZ_CRASH("trying to compare FP and integer registers"); 767 } 768 } 769 770 void MacroAssemblerARM::ma_cmp(Register src1, Register src2, Condition c) { 771 as_cmp(src1, O2Reg(src2), c); 772 } 773 774 // Test for equality, (src1 ^ src2). 775 void MacroAssemblerARM::ma_teq(Register src1, Imm32 imm, 776 AutoRegisterScope& scratch, Condition c) { 777 ma_alu(src1, imm, InvalidReg, scratch, OpTeq, SetCC, c); 778 } 779 780 void MacroAssemblerARM::ma_teq(Register src1, Register src2, Condition c) { 781 as_tst(src1, O2Reg(src2), c); 782 } 783 784 void MacroAssemblerARM::ma_teq(Register src1, Operand op, Condition c) { 785 as_teq(src1, op.toOp2(), c); 786 } 787 788 // Test (src1 & src2). 789 void MacroAssemblerARM::ma_tst(Register src1, Imm32 imm, 790 AutoRegisterScope& scratch, Condition c) { 791 ma_alu(src1, imm, InvalidReg, scratch, OpTst, SetCC, c); 792 } 793 794 void MacroAssemblerARM::ma_tst(Register src1, Register src2, Condition c) { 795 as_tst(src1, O2Reg(src2), c); 796 } 797 798 void MacroAssemblerARM::ma_tst(Register src1, Operand op, Condition c) { 799 as_tst(src1, op.toOp2(), c); 800 } 801 802 void MacroAssemblerARM::ma_mul(Register src1, Register src2, Register dest) { 803 as_mul(dest, src1, src2); 804 } 805 806 void MacroAssemblerARM::ma_mul(Register src1, Imm32 imm, Register dest, 807 AutoRegisterScope& scratch) { 808 ma_mov(imm, scratch); 809 as_mul(dest, src1, scratch); 810 } 811 812 Assembler::Condition MacroAssemblerARM::ma_check_mul(Register src1, 813 Register src2, 814 Register dest, 815 AutoRegisterScope& scratch, 816 Condition cond) { 817 // TODO: this operation is illegal on armv6 and earlier 818 // if src2 == scratch or src2 == dest. 819 if (cond == Equal || cond == NotEqual) { 820 as_smull(scratch, dest, src1, src2, SetCC); 821 return cond; 822 } 823 824 if (cond == Overflow) { 825 as_smull(scratch, dest, src1, src2); 826 as_cmp(scratch, asr(dest, 31)); 827 return NotEqual; 828 } 829 830 MOZ_CRASH("Condition NYI"); 831 } 832 833 Assembler::Condition MacroAssemblerARM::ma_check_mul(Register src1, Imm32 imm, 834 Register dest, 835 AutoRegisterScope& scratch, 836 Condition cond) { 837 ma_mov(imm, scratch); 838 839 if (cond == Equal || cond == NotEqual) { 840 as_smull(scratch, dest, scratch, src1, SetCC); 841 return cond; 842 } 843 844 if (cond == Overflow) { 845 as_smull(scratch, dest, scratch, src1); 846 as_cmp(scratch, asr(dest, 31)); 847 return NotEqual; 848 } 849 850 MOZ_CRASH("Condition NYI"); 851 } 852 853 void MacroAssemblerARM::ma_umull(Register src1, Imm32 imm, Register destHigh, 854 Register destLow, AutoRegisterScope& scratch) { 855 ma_mov(imm, scratch); 856 as_umull(destHigh, destLow, src1, scratch); 857 } 858 859 void MacroAssemblerARM::ma_umull(Register src1, Register src2, 860 Register destHigh, Register destLow) { 861 as_umull(destHigh, destLow, src1, src2); 862 } 863 864 void MacroAssemblerARM::ma_mod_mask(Register src, Register dest, Register hold, 865 Register tmp, AutoRegisterScope& scratch, 866 AutoRegisterScope& scratch2, 867 int32_t shift) { 868 // We wish to compute x % (1<<y) - 1 for a known constant, y. 869 // 870 // 1. Let b = (1<<y) and C = (1<<y)-1, then think of the 32 bit dividend as 871 // a number in base b, namely c_0*1 + c_1*b + c_2*b^2 ... c_n*b^n 872 // 873 // 2. Since both addition and multiplication commute with modulus: 874 // x % C == (c_0 + c_1*b + ... + c_n*b^n) % C == 875 // (c_0 % C) + (c_1%C) * (b % C) + (c_2 % C) * (b^2 % C)... 876 // 877 // 3. Since b == C + 1, b % C == 1, and b^n % C == 1 the whole thing 878 // simplifies to: c_0 + c_1 + c_2 ... c_n % C 879 // 880 // Each c_n can easily be computed by a shift/bitextract, and the modulus 881 // can be maintained by simply subtracting by C whenever the number gets 882 // over C. 883 int32_t mask = (1 << shift) - 1; 884 Label head; 885 886 // Register 'hold' holds -1 if the value was negative, 1 otherwise. The 887 // scratch reg holds the remaining bits that have not been processed lr 888 // serves as a temporary location to store extracted bits into as well as 889 // holding the trial subtraction as a temp value dest is the accumulator 890 // (and holds the final result) 891 // 892 // Move the whole value into tmp, setting the codition codes so we can muck 893 // with them later. 894 as_mov(tmp, O2Reg(src), SetCC); 895 // Zero out the dest. 896 ma_mov(Imm32(0), dest); 897 // Set the hold appropriately. 898 ma_mov(Imm32(1), hold); 899 ma_mov(Imm32(-1), hold, Signed); 900 as_rsb(tmp, tmp, Imm8(0), SetCC, Signed); 901 902 // Begin the main loop. 903 bind(&head); 904 { 905 // Extract the bottom bits. 906 ma_and(Imm32(mask), tmp, scratch, scratch2); 907 // Add those bits to the accumulator. 908 ma_add(scratch, dest, dest); 909 // Do a trial subtraction, this is the same operation as cmp, but we store 910 // the dest. 911 ma_sub(dest, Imm32(mask), scratch, scratch2, SetCC); 912 // If (sum - C) > 0, store sum - C back into sum, thus performing a modulus. 913 ma_mov(scratch, dest, LeaveCC, NotSigned); 914 // Get rid of the bits that we extracted before, and set the condition 915 // codes. 916 as_mov(tmp, lsr(tmp, shift), SetCC); 917 // If the shift produced zero, finish, otherwise, continue in the loop. 918 ma_b(&head, NonZero); 919 } 920 921 // Check the hold to see if we need to negate the result. Hold can only be 922 // 1 or -1, so this will never set the 0 flag. 923 as_cmp(hold, Imm8(0)); 924 // If the hold was non-zero, negate the result to be in line with what JS 925 // wants this will set the condition codes if we try to negate. 926 as_rsb(dest, dest, Imm8(0), SetCC, Signed); 927 // Since the Zero flag is not set by the compare, we can *only* set the Zero 928 // flag in the rsb, so Zero is set iff we negated zero (e.g. the result of 929 // the computation was -0.0). 930 } 931 932 void MacroAssemblerARM::ma_smod(Register num, Register div, Register dest, 933 AutoRegisterScope& scratch) { 934 as_sdiv(scratch, num, div); 935 as_mls(dest, num, scratch, div); 936 } 937 938 void MacroAssemblerARM::ma_umod(Register num, Register div, Register dest, 939 AutoRegisterScope& scratch) { 940 as_udiv(scratch, num, div); 941 as_mls(dest, num, scratch, div); 942 } 943 944 // Division 945 void MacroAssemblerARM::ma_sdiv(Register num, Register div, Register dest, 946 Condition cond) { 947 as_sdiv(dest, num, div, cond); 948 } 949 950 void MacroAssemblerARM::ma_udiv(Register num, Register div, Register dest, 951 Condition cond) { 952 as_udiv(dest, num, div, cond); 953 } 954 955 // Miscellaneous instructions. 956 void MacroAssemblerARM::ma_clz(Register src, Register dest, Condition cond) { 957 as_clz(dest, src, cond); 958 } 959 960 void MacroAssemblerARM::ma_ctz(Register src, Register dest, 961 AutoRegisterScope& scratch) { 962 // int c = __clz(a & -a); 963 // return a ? 31 - c : c; 964 as_rsb(scratch, src, Imm8(0), SetCC); 965 as_and(dest, src, O2Reg(scratch), LeaveCC); 966 as_clz(dest, dest); 967 as_rsb(dest, dest, Imm8(0x1F), LeaveCC, Assembler::NotEqual); 968 } 969 970 // Memory. 971 // Shortcut for when we know we're transferring 32 bits of data. 972 void MacroAssemblerARM::ma_dtr(LoadStore ls, Register rn, Imm32 offset, 973 Register rt, AutoRegisterScope& scratch, 974 Index mode, Assembler::Condition cc) { 975 ma_dataTransferN(ls, 32, true, rn, offset, rt, scratch, mode, cc); 976 } 977 978 FaultingCodeOffset MacroAssemblerARM::ma_dtr(LoadStore ls, Register rt, 979 const Address& addr, 980 AutoRegisterScope& scratch, 981 Index mode, Condition cc) { 982 BufferOffset offset = ma_dataTransferN( 983 ls, 32, true, addr.base, Imm32(addr.offset), rt, scratch, mode, cc); 984 return FaultingCodeOffset(offset.getOffset()); 985 } 986 987 FaultingCodeOffset MacroAssemblerARM::ma_str(Register rt, DTRAddr addr, 988 Index mode, Condition cc) { 989 BufferOffset offset = as_dtr(IsStore, 32, mode, rt, addr, cc); 990 return FaultingCodeOffset(offset.getOffset()); 991 } 992 993 FaultingCodeOffset MacroAssemblerARM::ma_str(Register rt, const Address& addr, 994 AutoRegisterScope& scratch, 995 Index mode, Condition cc) { 996 return ma_dtr(IsStore, rt, addr, scratch, mode, cc); 997 } 998 999 void MacroAssemblerARM::ma_strd(Register rt, DebugOnly<Register> rt2, 1000 EDtrAddr addr, Index mode, Condition cc) { 1001 MOZ_ASSERT((rt.code() & 1) == 0); 1002 MOZ_ASSERT(rt2.value.code() == rt.code() + 1); 1003 as_extdtr(IsStore, 64, true, mode, rt, addr, cc); 1004 } 1005 1006 FaultingCodeOffset MacroAssemblerARM::ma_ldr(DTRAddr addr, Register rt, 1007 Index mode, Condition cc) { 1008 BufferOffset offset = as_dtr(IsLoad, 32, mode, rt, addr, cc); 1009 return FaultingCodeOffset(offset.getOffset()); 1010 } 1011 1012 FaultingCodeOffset MacroAssemblerARM::ma_ldr(const Address& addr, Register rt, 1013 AutoRegisterScope& scratch, 1014 Index mode, Condition cc) { 1015 return ma_dtr(IsLoad, rt, addr, scratch, mode, cc); 1016 } 1017 1018 FaultingCodeOffset MacroAssemblerARM::ma_ldrb(DTRAddr addr, Register rt, 1019 Index mode, Condition cc) { 1020 BufferOffset offset = as_dtr(IsLoad, 8, mode, rt, addr, cc); 1021 return FaultingCodeOffset(offset.getOffset()); 1022 } 1023 1024 FaultingCodeOffset MacroAssemblerARM::ma_ldrsh(EDtrAddr addr, Register rt, 1025 Index mode, Condition cc) { 1026 BufferOffset offset = as_extdtr(IsLoad, 16, true, mode, rt, addr, cc); 1027 return FaultingCodeOffset(offset.getOffset()); 1028 } 1029 1030 FaultingCodeOffset MacroAssemblerARM::ma_ldrh(EDtrAddr addr, Register rt, 1031 Index mode, Condition cc) { 1032 BufferOffset offset = as_extdtr(IsLoad, 16, false, mode, rt, addr, cc); 1033 return FaultingCodeOffset(offset.getOffset()); 1034 } 1035 1036 FaultingCodeOffset MacroAssemblerARM::ma_ldrsb(EDtrAddr addr, Register rt, 1037 Index mode, Condition cc) { 1038 BufferOffset offset = as_extdtr(IsLoad, 8, true, mode, rt, addr, cc); 1039 return FaultingCodeOffset(offset.getOffset()); 1040 } 1041 1042 void MacroAssemblerARM::ma_ldrd(EDtrAddr addr, Register rt, 1043 DebugOnly<Register> rt2, Index mode, 1044 Condition cc) { 1045 MOZ_ASSERT((rt.code() & 1) == 0); 1046 MOZ_ASSERT(rt2.value.code() == rt.code() + 1); 1047 MOZ_ASSERT(addr.maybeOffsetRegister() != 1048 rt); // Undefined behavior if rm == rt/rt2. 1049 MOZ_ASSERT(addr.maybeOffsetRegister() != rt2); 1050 as_extdtr(IsLoad, 64, true, mode, rt, addr, cc); 1051 } 1052 1053 FaultingCodeOffset MacroAssemblerARM::ma_strh(Register rt, EDtrAddr addr, 1054 Index mode, Condition cc) { 1055 BufferOffset offset = as_extdtr(IsStore, 16, false, mode, rt, addr, cc); 1056 return FaultingCodeOffset(offset.getOffset()); 1057 } 1058 1059 FaultingCodeOffset MacroAssemblerARM::ma_strb(Register rt, DTRAddr addr, 1060 Index mode, Condition cc) { 1061 BufferOffset offset = as_dtr(IsStore, 8, mode, rt, addr, cc); 1062 return FaultingCodeOffset(offset.getOffset()); 1063 } 1064 1065 // Specialty for moving N bits of data, where n == 8,16,32,64. 1066 BufferOffset MacroAssemblerARM::ma_dataTransferN( 1067 LoadStore ls, int size, bool IsSigned, Register rn, Register rm, 1068 Register rt, AutoRegisterScope& scratch, Index mode, 1069 Assembler::Condition cc, Scale scale) { 1070 MOZ_ASSERT(size == 8 || size == 16 || size == 32 || size == 64); 1071 1072 if (size == 32 || (size == 8 && !IsSigned)) { 1073 return as_dtr(ls, size, mode, rt, 1074 DTRAddr(rn, DtrRegImmShift(rm, LSL, scale)), cc); 1075 } 1076 1077 if (scale != TimesOne) { 1078 ma_lsl(Imm32(scale), rm, scratch); 1079 rm = scratch; 1080 } 1081 1082 return as_extdtr(ls, size, IsSigned, mode, rt, EDtrAddr(rn, EDtrOffReg(rm)), 1083 cc); 1084 } 1085 1086 // No scratch register is required if scale is TimesOne. 1087 BufferOffset MacroAssemblerARM::ma_dataTransferN(LoadStore ls, int size, 1088 bool IsSigned, Register rn, 1089 Register rm, Register rt, 1090 Index mode, 1091 Assembler::Condition cc) { 1092 MOZ_ASSERT(size == 8 || size == 16 || size == 32 || size == 64); 1093 if (size == 32 || (size == 8 && !IsSigned)) { 1094 return as_dtr(ls, size, mode, rt, 1095 DTRAddr(rn, DtrRegImmShift(rm, LSL, TimesOne)), cc); 1096 } 1097 return as_extdtr(ls, size, IsSigned, mode, rt, EDtrAddr(rn, EDtrOffReg(rm)), 1098 cc); 1099 } 1100 1101 BufferOffset MacroAssemblerARM::ma_dataTransferN(LoadStore ls, int size, 1102 bool IsSigned, Register rn, 1103 Imm32 offset, Register rt, 1104 AutoRegisterScope& scratch, 1105 Index mode, 1106 Assembler::Condition cc) { 1107 MOZ_ASSERT(!(ls == IsLoad && mode == PostIndex && rt == pc), 1108 "Large-offset PostIndex loading into PC requires special logic: " 1109 "see ma_popn_pc()."); 1110 1111 int off = offset.value; 1112 1113 // We can encode this as a standard ldr. 1114 if (size == 32 || (size == 8 && !IsSigned)) { 1115 if (off < 4096 && off > -4096) { 1116 // This encodes as a single instruction, Emulating mode's behavior 1117 // in a multi-instruction sequence is not necessary. 1118 return as_dtr(ls, size, mode, rt, DTRAddr(rn, DtrOffImm(off)), cc); 1119 } 1120 1121 // We cannot encode this offset in a single ldr. For mode == index, 1122 // try to encode it as |add scratch, base, imm; ldr dest, [scratch, 1123 // +offset]|. This does not wark for mode == PreIndex or mode == PostIndex. 1124 // PreIndex is simple, just do the add into the base register first, 1125 // then do a PreIndex'ed load. PostIndexed loads can be tricky. 1126 // Normally, doing the load with an index of 0, then doing an add would 1127 // work, but if the destination is the PC, you don't get to execute the 1128 // instruction after the branch, which will lead to the base register 1129 // not being updated correctly. Explicitly handle this case, without 1130 // doing anything fancy, then handle all of the other cases. 1131 1132 // mode == Offset 1133 // add scratch, base, offset_hi 1134 // ldr dest, [scratch, +offset_lo] 1135 // 1136 // mode == PreIndex 1137 // add base, base, offset_hi 1138 // ldr dest, [base, +offset_lo]! 1139 1140 int bottom = off & 0xfff; 1141 int neg_bottom = 0x1000 - bottom; 1142 1143 MOZ_ASSERT(rn != scratch); 1144 MOZ_ASSERT(mode != PostIndex); 1145 1146 // At this point, both off - bottom and off + neg_bottom will be 1147 // reasonable-ish quantities. 1148 // 1149 // Note a neg_bottom of 0x1000 can not be encoded as an immediate 1150 // negative offset in the instruction and this occurs when bottom is 1151 // zero, so this case is guarded against below. 1152 if (off < 0) { 1153 Operand2 sub_off = Imm8(-(off - bottom)); // sub_off = bottom - off 1154 if (!sub_off.invalid()) { 1155 // - sub_off = off - bottom 1156 as_sub(scratch, rn, sub_off, LeaveCC, cc); 1157 return as_dtr(ls, size, Offset, rt, DTRAddr(scratch, DtrOffImm(bottom)), 1158 cc); 1159 } 1160 1161 // sub_off = -neg_bottom - off 1162 sub_off = Imm8(-(off + neg_bottom)); 1163 if (!sub_off.invalid() && bottom != 0) { 1164 // Guarded against by: bottom != 0 1165 MOZ_ASSERT(neg_bottom < 0x1000); 1166 // - sub_off = neg_bottom + off 1167 as_sub(scratch, rn, sub_off, LeaveCC, cc); 1168 return as_dtr(ls, size, Offset, rt, 1169 DTRAddr(scratch, DtrOffImm(-neg_bottom)), cc); 1170 } 1171 } else { 1172 // sub_off = off - bottom 1173 Operand2 sub_off = Imm8(off - bottom); 1174 if (!sub_off.invalid()) { 1175 // sub_off = off - bottom 1176 as_add(scratch, rn, sub_off, LeaveCC, cc); 1177 return as_dtr(ls, size, Offset, rt, DTRAddr(scratch, DtrOffImm(bottom)), 1178 cc); 1179 } 1180 1181 // sub_off = neg_bottom + off 1182 sub_off = Imm8(off + neg_bottom); 1183 if (!sub_off.invalid() && bottom != 0) { 1184 // Guarded against by: bottom != 0 1185 MOZ_ASSERT(neg_bottom < 0x1000); 1186 // sub_off = neg_bottom + off 1187 as_add(scratch, rn, sub_off, LeaveCC, cc); 1188 return as_dtr(ls, size, Offset, rt, 1189 DTRAddr(scratch, DtrOffImm(-neg_bottom)), cc); 1190 } 1191 } 1192 1193 ma_mov(offset, scratch); 1194 return as_dtr(ls, size, mode, rt, 1195 DTRAddr(rn, DtrRegImmShift(scratch, LSL, 0))); 1196 } else { 1197 // Should attempt to use the extended load/store instructions. 1198 if (off < 256 && off > -256) { 1199 return as_extdtr(ls, size, IsSigned, mode, rt, 1200 EDtrAddr(rn, EDtrOffImm(off)), cc); 1201 } 1202 1203 // We cannot encode this offset in a single extldr. Try to encode it as 1204 // an add scratch, base, imm; extldr dest, [scratch, +offset]. 1205 int bottom = off & 0xff; 1206 int neg_bottom = 0x100 - bottom; 1207 // At this point, both off - bottom and off + neg_bottom will be 1208 // reasonable-ish quantities. 1209 // 1210 // Note a neg_bottom of 0x100 can not be encoded as an immediate 1211 // negative offset in the instruction and this occurs when bottom is 1212 // zero, so this case is guarded against below. 1213 if (off < 0) { 1214 // sub_off = bottom - off 1215 Operand2 sub_off = Imm8(-(off - bottom)); 1216 if (!sub_off.invalid()) { 1217 // - sub_off = off - bottom 1218 as_sub(scratch, rn, sub_off, LeaveCC, cc); 1219 return as_extdtr(ls, size, IsSigned, Offset, rt, 1220 EDtrAddr(scratch, EDtrOffImm(bottom)), cc); 1221 } 1222 // sub_off = -neg_bottom - off 1223 sub_off = Imm8(-(off + neg_bottom)); 1224 if (!sub_off.invalid() && bottom != 0) { 1225 // Guarded against by: bottom != 0 1226 MOZ_ASSERT(neg_bottom < 0x100); 1227 // - sub_off = neg_bottom + off 1228 as_sub(scratch, rn, sub_off, LeaveCC, cc); 1229 return as_extdtr(ls, size, IsSigned, Offset, rt, 1230 EDtrAddr(scratch, EDtrOffImm(-neg_bottom)), cc); 1231 } 1232 } else { 1233 // sub_off = off - bottom 1234 Operand2 sub_off = Imm8(off - bottom); 1235 if (!sub_off.invalid()) { 1236 // sub_off = off - bottom 1237 as_add(scratch, rn, sub_off, LeaveCC, cc); 1238 return as_extdtr(ls, size, IsSigned, Offset, rt, 1239 EDtrAddr(scratch, EDtrOffImm(bottom)), cc); 1240 } 1241 // sub_off = neg_bottom + off 1242 sub_off = Imm8(off + neg_bottom); 1243 if (!sub_off.invalid() && bottom != 0) { 1244 // Guarded against by: bottom != 0 1245 MOZ_ASSERT(neg_bottom < 0x100); 1246 // sub_off = neg_bottom + off 1247 as_add(scratch, rn, sub_off, LeaveCC, cc); 1248 return as_extdtr(ls, size, IsSigned, Offset, rt, 1249 EDtrAddr(scratch, EDtrOffImm(-neg_bottom)), cc); 1250 } 1251 } 1252 ma_mov(offset, scratch); 1253 return as_extdtr(ls, size, IsSigned, mode, rt, 1254 EDtrAddr(rn, EDtrOffReg(scratch)), cc); 1255 } 1256 } 1257 1258 BufferOffset MacroAssemblerARM::ma_pop(Register r) { 1259 return as_dtr(IsLoad, 32, PostIndex, r, DTRAddr(sp, DtrOffImm(4))); 1260 } 1261 1262 void MacroAssemblerARM::ma_popn_pc(Imm32 n, AutoRegisterScope& scratch, 1263 AutoRegisterScope& scratch2) { 1264 // pc <- [sp]; sp += n 1265 int32_t nv = n.value; 1266 1267 if (nv < 4096 && nv >= -4096) { 1268 as_dtr(IsLoad, 32, PostIndex, pc, DTRAddr(sp, DtrOffImm(nv))); 1269 } else { 1270 ma_mov(sp, scratch); 1271 ma_add(Imm32(n), sp, scratch2); 1272 as_dtr(IsLoad, 32, Offset, pc, DTRAddr(scratch, DtrOffImm(0))); 1273 } 1274 } 1275 1276 void MacroAssemblerARM::ma_push(Register r) { 1277 MOZ_ASSERT(r != sp, "Use ma_push_sp()."); 1278 as_dtr(IsStore, 32, PreIndex, r, DTRAddr(sp, DtrOffImm(-4))); 1279 } 1280 1281 void MacroAssemblerARM::ma_push_sp(Register r, AutoRegisterScope& scratch) { 1282 // Pushing sp is not well-defined: use two instructions. 1283 MOZ_ASSERT(r == sp); 1284 ma_mov(sp, scratch); 1285 as_dtr(IsStore, 32, PreIndex, scratch, DTRAddr(sp, DtrOffImm(-4))); 1286 } 1287 1288 void MacroAssemblerARM::ma_vpop(VFPRegister r) { 1289 startFloatTransferM(IsLoad, sp, IA, WriteBack); 1290 transferFloatReg(r); 1291 finishFloatTransfer(); 1292 } 1293 1294 void MacroAssemblerARM::ma_vpush(VFPRegister r) { 1295 startFloatTransferM(IsStore, sp, DB, WriteBack); 1296 transferFloatReg(r); 1297 finishFloatTransfer(); 1298 } 1299 1300 // Barriers 1301 void MacroAssemblerARM::ma_dmb(BarrierOption option) { 1302 if (ARMFlags::HasDMBDSBISB()) { 1303 as_dmb(option); 1304 } else { 1305 as_dmb_trap(); 1306 } 1307 } 1308 1309 void MacroAssemblerARM::ma_dsb(BarrierOption option) { 1310 if (ARMFlags::HasDMBDSBISB()) { 1311 as_dsb(option); 1312 } else { 1313 as_dsb_trap(); 1314 } 1315 } 1316 1317 // Branches when done from within arm-specific code. 1318 BufferOffset MacroAssemblerARM::ma_b(Label* dest, Assembler::Condition c) { 1319 return as_b(dest, c); 1320 } 1321 1322 void MacroAssemblerARM::ma_bx(Register dest, Assembler::Condition c) { 1323 as_bx(dest, c); 1324 } 1325 1326 void MacroAssemblerARM::ma_b(void* target, Assembler::Condition c) { 1327 // An immediate pool is used for easier patching. 1328 as_Imm32Pool(pc, uint32_t(target), c); 1329 } 1330 1331 // This is almost NEVER necessary: we'll basically never be calling a label, 1332 // except possibly in the crazy bailout-table case. 1333 void MacroAssemblerARM::ma_bl(Label* dest, Assembler::Condition c) { 1334 as_bl(dest, c); 1335 } 1336 1337 void MacroAssemblerARM::ma_blx(Register reg, Assembler::Condition c) { 1338 as_blx(reg, c); 1339 } 1340 1341 // VFP/ALU 1342 void MacroAssemblerARM::ma_vadd(FloatRegister src1, FloatRegister src2, 1343 FloatRegister dst) { 1344 as_vadd(VFPRegister(dst), VFPRegister(src1), VFPRegister(src2)); 1345 } 1346 1347 void MacroAssemblerARM::ma_vadd_f32(FloatRegister src1, FloatRegister src2, 1348 FloatRegister dst) { 1349 as_vadd(VFPRegister(dst).singleOverlay(), VFPRegister(src1).singleOverlay(), 1350 VFPRegister(src2).singleOverlay()); 1351 } 1352 1353 void MacroAssemblerARM::ma_vsub(FloatRegister src1, FloatRegister src2, 1354 FloatRegister dst) { 1355 as_vsub(VFPRegister(dst), VFPRegister(src1), VFPRegister(src2)); 1356 } 1357 1358 void MacroAssemblerARM::ma_vsub_f32(FloatRegister src1, FloatRegister src2, 1359 FloatRegister dst) { 1360 as_vsub(VFPRegister(dst).singleOverlay(), VFPRegister(src1).singleOverlay(), 1361 VFPRegister(src2).singleOverlay()); 1362 } 1363 1364 void MacroAssemblerARM::ma_vmul(FloatRegister src1, FloatRegister src2, 1365 FloatRegister dst) { 1366 as_vmul(VFPRegister(dst), VFPRegister(src1), VFPRegister(src2)); 1367 } 1368 1369 void MacroAssemblerARM::ma_vmul_f32(FloatRegister src1, FloatRegister src2, 1370 FloatRegister dst) { 1371 as_vmul(VFPRegister(dst).singleOverlay(), VFPRegister(src1).singleOverlay(), 1372 VFPRegister(src2).singleOverlay()); 1373 } 1374 1375 void MacroAssemblerARM::ma_vdiv(FloatRegister src1, FloatRegister src2, 1376 FloatRegister dst) { 1377 as_vdiv(VFPRegister(dst), VFPRegister(src1), VFPRegister(src2)); 1378 } 1379 1380 void MacroAssemblerARM::ma_vdiv_f32(FloatRegister src1, FloatRegister src2, 1381 FloatRegister dst) { 1382 as_vdiv(VFPRegister(dst).singleOverlay(), VFPRegister(src1).singleOverlay(), 1383 VFPRegister(src2).singleOverlay()); 1384 } 1385 1386 void MacroAssemblerARM::ma_vmov(FloatRegister src, FloatRegister dest, 1387 Condition cc) { 1388 as_vmov(dest, src, cc); 1389 } 1390 1391 void MacroAssemblerARM::ma_vmov_f32(FloatRegister src, FloatRegister dest, 1392 Condition cc) { 1393 as_vmov(VFPRegister(dest).singleOverlay(), VFPRegister(src).singleOverlay(), 1394 cc); 1395 } 1396 1397 void MacroAssemblerARM::ma_vneg(FloatRegister src, FloatRegister dest, 1398 Condition cc) { 1399 as_vneg(dest, src, cc); 1400 } 1401 1402 void MacroAssemblerARM::ma_vneg_f32(FloatRegister src, FloatRegister dest, 1403 Condition cc) { 1404 as_vneg(VFPRegister(dest).singleOverlay(), VFPRegister(src).singleOverlay(), 1405 cc); 1406 } 1407 1408 void MacroAssemblerARM::ma_vabs(FloatRegister src, FloatRegister dest, 1409 Condition cc) { 1410 as_vabs(dest, src, cc); 1411 } 1412 1413 void MacroAssemblerARM::ma_vabs_f32(FloatRegister src, FloatRegister dest, 1414 Condition cc) { 1415 as_vabs(VFPRegister(dest).singleOverlay(), VFPRegister(src).singleOverlay(), 1416 cc); 1417 } 1418 1419 void MacroAssemblerARM::ma_vsqrt(FloatRegister src, FloatRegister dest, 1420 Condition cc) { 1421 as_vsqrt(dest, src, cc); 1422 } 1423 1424 void MacroAssemblerARM::ma_vsqrt_f32(FloatRegister src, FloatRegister dest, 1425 Condition cc) { 1426 as_vsqrt(VFPRegister(dest).singleOverlay(), VFPRegister(src).singleOverlay(), 1427 cc); 1428 } 1429 1430 static inline uint32_t DoubleHighWord(double d) { 1431 return static_cast<uint32_t>(BitwiseCast<uint64_t>(d) >> 32); 1432 } 1433 1434 static inline uint32_t DoubleLowWord(double d) { 1435 return static_cast<uint32_t>(BitwiseCast<uint64_t>(d)) & uint32_t(0xffffffff); 1436 } 1437 1438 void MacroAssemblerARM::ma_vimm(double value, FloatRegister dest, 1439 Condition cc) { 1440 if (ARMFlags::HasVFPv3()) { 1441 if (DoubleLowWord(value) == 0) { 1442 if (DoubleHighWord(value) == 0) { 1443 // To zero a register, load 1.0, then execute dN <- dN - dN 1444 as_vimm(dest, VFPImm::One, cc); 1445 as_vsub(dest, dest, dest, cc); 1446 return; 1447 } 1448 1449 VFPImm enc(DoubleHighWord(value)); 1450 if (enc.isValid()) { 1451 as_vimm(dest, enc, cc); 1452 return; 1453 } 1454 } 1455 } 1456 // Fall back to putting the value in a pool. 1457 as_FImm64Pool(dest, value, cc); 1458 } 1459 1460 void MacroAssemblerARM::ma_vimm_f32(float value, FloatRegister dest, 1461 Condition cc) { 1462 VFPRegister vd = VFPRegister(dest).singleOverlay(); 1463 if (ARMFlags::HasVFPv3()) { 1464 if (IsPositiveZero(value)) { 1465 // To zero a register, load 1.0, then execute sN <- sN - sN. 1466 as_vimm(vd, VFPImm::One, cc); 1467 as_vsub(vd, vd, vd, cc); 1468 return; 1469 } 1470 1471 // Note that the vimm immediate float32 instruction encoding differs 1472 // from the vimm immediate double encoding, but this difference matches 1473 // the difference in the floating point formats, so it is possible to 1474 // convert the float32 to a double and then use the double encoding 1475 // paths. It is still necessary to firstly check that the double low 1476 // word is zero because some float32 numbers set these bits and this can 1477 // not be ignored. 1478 double doubleValue(value); 1479 if (DoubleLowWord(doubleValue) == 0) { 1480 VFPImm enc(DoubleHighWord(doubleValue)); 1481 if (enc.isValid()) { 1482 as_vimm(vd, enc, cc); 1483 return; 1484 } 1485 } 1486 } 1487 1488 // Fall back to putting the value in a pool. 1489 as_FImm32Pool(vd, value, cc); 1490 } 1491 1492 void MacroAssemblerARM::ma_vcmp(FloatRegister src1, FloatRegister src2, 1493 Condition cc) { 1494 as_vcmp(VFPRegister(src1), VFPRegister(src2), cc); 1495 } 1496 1497 void MacroAssemblerARM::ma_vcmp_f32(FloatRegister src1, FloatRegister src2, 1498 Condition cc) { 1499 as_vcmp(VFPRegister(src1).singleOverlay(), VFPRegister(src2).singleOverlay(), 1500 cc); 1501 } 1502 1503 void MacroAssemblerARM::ma_vcmpz(FloatRegister src1, Condition cc) { 1504 as_vcmpz(VFPRegister(src1), cc); 1505 } 1506 1507 void MacroAssemblerARM::ma_vcmpz_f32(FloatRegister src1, Condition cc) { 1508 as_vcmpz(VFPRegister(src1).singleOverlay(), cc); 1509 } 1510 1511 void MacroAssemblerARM::ma_vcvt_F64_I32(FloatRegister src, FloatRegister dest, 1512 Condition cc) { 1513 MOZ_ASSERT(src.isDouble()); 1514 MOZ_ASSERT(dest.isSInt()); 1515 as_vcvt(dest, src, false, cc); 1516 } 1517 1518 void MacroAssemblerARM::ma_vcvt_F64_U32(FloatRegister src, FloatRegister dest, 1519 Condition cc) { 1520 MOZ_ASSERT(src.isDouble()); 1521 MOZ_ASSERT(dest.isUInt()); 1522 as_vcvt(dest, src, false, cc); 1523 } 1524 1525 void MacroAssemblerARM::ma_vcvt_I32_F64(FloatRegister src, FloatRegister dest, 1526 Condition cc) { 1527 MOZ_ASSERT(src.isSInt()); 1528 MOZ_ASSERT(dest.isDouble()); 1529 as_vcvt(dest, src, false, cc); 1530 } 1531 1532 void MacroAssemblerARM::ma_vcvt_U32_F64(FloatRegister src, FloatRegister dest, 1533 Condition cc) { 1534 MOZ_ASSERT(src.isUInt()); 1535 MOZ_ASSERT(dest.isDouble()); 1536 as_vcvt(dest, src, false, cc); 1537 } 1538 1539 void MacroAssemblerARM::ma_vcvt_F32_I32(FloatRegister src, FloatRegister dest, 1540 Condition cc) { 1541 MOZ_ASSERT(src.isSingle()); 1542 MOZ_ASSERT(dest.isSInt()); 1543 as_vcvt(VFPRegister(dest).sintOverlay(), VFPRegister(src).singleOverlay(), 1544 false, cc); 1545 } 1546 1547 void MacroAssemblerARM::ma_vcvt_F32_U32(FloatRegister src, FloatRegister dest, 1548 Condition cc) { 1549 MOZ_ASSERT(src.isSingle()); 1550 MOZ_ASSERT(dest.isUInt()); 1551 as_vcvt(VFPRegister(dest).uintOverlay(), VFPRegister(src).singleOverlay(), 1552 false, cc); 1553 } 1554 1555 void MacroAssemblerARM::ma_vcvt_I32_F32(FloatRegister src, FloatRegister dest, 1556 Condition cc) { 1557 MOZ_ASSERT(src.isSInt()); 1558 MOZ_ASSERT(dest.isSingle()); 1559 as_vcvt(VFPRegister(dest).singleOverlay(), VFPRegister(src).sintOverlay(), 1560 false, cc); 1561 } 1562 1563 void MacroAssemblerARM::ma_vcvt_U32_F32(FloatRegister src, FloatRegister dest, 1564 Condition cc) { 1565 MOZ_ASSERT(src.isUInt()); 1566 MOZ_ASSERT(dest.isSingle()); 1567 as_vcvt(VFPRegister(dest).singleOverlay(), VFPRegister(src).uintOverlay(), 1568 false, cc); 1569 } 1570 1571 void MacroAssemblerARM::ma_vxfer(FloatRegister src, Register dest, 1572 Condition cc) { 1573 as_vxfer(dest, InvalidReg, VFPRegister(src).singleOverlay(), FloatToCore, cc); 1574 } 1575 1576 void MacroAssemblerARM::ma_vxfer(FloatRegister src, Register dest1, 1577 Register dest2, Condition cc) { 1578 as_vxfer(dest1, dest2, VFPRegister(src), FloatToCore, cc); 1579 } 1580 1581 void MacroAssemblerARM::ma_vxfer(Register src, FloatRegister dest, 1582 Condition cc) { 1583 as_vxfer(src, InvalidReg, VFPRegister(dest).singleOverlay(), CoreToFloat, cc); 1584 } 1585 1586 void MacroAssemblerARM::ma_vxfer(Register src1, Register src2, 1587 FloatRegister dest, Condition cc) { 1588 as_vxfer(src1, src2, VFPRegister(dest), CoreToFloat, cc); 1589 } 1590 1591 BufferOffset MacroAssemblerARM::ma_vdtr(LoadStore ls, const Address& addr, 1592 VFPRegister rt, 1593 AutoRegisterScope& scratch, 1594 Condition cc) { 1595 int off = addr.offset; 1596 MOZ_ASSERT((off & 3) == 0); 1597 Register base = addr.base; 1598 if (off > -1024 && off < 1024) { 1599 return as_vdtr(ls, rt, Operand(addr).toVFPAddr(), cc); 1600 } 1601 1602 // We cannot encode this offset in a a single ldr. Try to encode it as an 1603 // add scratch, base, imm; ldr dest, [scratch, +offset]. 1604 int bottom = off & (0xff << 2); 1605 int neg_bottom = (0x100 << 2) - bottom; 1606 // At this point, both off - bottom and off + neg_bottom will be 1607 // reasonable-ish quantities. 1608 // 1609 // Note a neg_bottom of 0x400 can not be encoded as an immediate negative 1610 // offset in the instruction and this occurs when bottom is zero, so this 1611 // case is guarded against below. 1612 if (off < 0) { 1613 // sub_off = bottom - off 1614 Operand2 sub_off = Imm8(-(off - bottom)); 1615 if (!sub_off.invalid()) { 1616 // - sub_off = off - bottom 1617 as_sub(scratch, base, sub_off, LeaveCC, cc); 1618 return as_vdtr(ls, rt, VFPAddr(scratch, VFPOffImm(bottom)), cc); 1619 } 1620 // sub_off = -neg_bottom - off 1621 sub_off = Imm8(-(off + neg_bottom)); 1622 if (!sub_off.invalid() && bottom != 0) { 1623 // Guarded against by: bottom != 0 1624 MOZ_ASSERT(neg_bottom < 0x400); 1625 // - sub_off = neg_bottom + off 1626 as_sub(scratch, base, sub_off, LeaveCC, cc); 1627 return as_vdtr(ls, rt, VFPAddr(scratch, VFPOffImm(-neg_bottom)), cc); 1628 } 1629 } else { 1630 // sub_off = off - bottom 1631 Operand2 sub_off = Imm8(off - bottom); 1632 if (!sub_off.invalid()) { 1633 // sub_off = off - bottom 1634 as_add(scratch, base, sub_off, LeaveCC, cc); 1635 return as_vdtr(ls, rt, VFPAddr(scratch, VFPOffImm(bottom)), cc); 1636 } 1637 // sub_off = neg_bottom + off 1638 sub_off = Imm8(off + neg_bottom); 1639 if (!sub_off.invalid() && bottom != 0) { 1640 // Guarded against by: bottom != 0 1641 MOZ_ASSERT(neg_bottom < 0x400); 1642 // sub_off = neg_bottom + off 1643 as_add(scratch, base, sub_off, LeaveCC, cc); 1644 return as_vdtr(ls, rt, VFPAddr(scratch, VFPOffImm(-neg_bottom)), cc); 1645 } 1646 } 1647 1648 // Safe to use scratch as dest, since ma_add() overwrites dest at the end 1649 // and can't use it as internal scratch since it may also == base. 1650 ma_add(base, Imm32(off), scratch, scratch, LeaveCC, cc); 1651 return as_vdtr(ls, rt, VFPAddr(scratch, VFPOffImm(0)), cc); 1652 } 1653 1654 BufferOffset MacroAssemblerARM::ma_vldr(VFPAddr addr, VFPRegister dest, 1655 Condition cc) { 1656 return as_vdtr(IsLoad, dest, addr, cc); 1657 } 1658 1659 BufferOffset MacroAssemblerARM::ma_vldr(const Address& addr, VFPRegister dest, 1660 AutoRegisterScope& scratch, 1661 Condition cc) { 1662 return ma_vdtr(IsLoad, addr, dest, scratch, cc); 1663 } 1664 1665 BufferOffset MacroAssemblerARM::ma_vldr(VFPRegister src, Register base, 1666 Register index, 1667 AutoRegisterScope& scratch, 1668 int32_t shift, Condition cc) { 1669 as_add(scratch, base, lsl(index, shift), LeaveCC, cc); 1670 return as_vdtr(IsLoad, src, Operand(Address(scratch, 0)).toVFPAddr(), cc); 1671 } 1672 1673 BufferOffset MacroAssemblerARM::ma_vstr(VFPRegister src, VFPAddr addr, 1674 Condition cc) { 1675 return as_vdtr(IsStore, src, addr, cc); 1676 } 1677 1678 BufferOffset MacroAssemblerARM::ma_vstr(VFPRegister src, const Address& addr, 1679 AutoRegisterScope& scratch, 1680 Condition cc) { 1681 return ma_vdtr(IsStore, addr, src, scratch, cc); 1682 } 1683 1684 BufferOffset MacroAssemblerARM::ma_vstr( 1685 VFPRegister src, Register base, Register index, AutoRegisterScope& scratch, 1686 AutoRegisterScope& scratch2, int32_t shift, int32_t offset, Condition cc) { 1687 as_add(scratch, base, lsl(index, shift), LeaveCC, cc); 1688 return ma_vstr(src, Address(scratch, offset), scratch2, cc); 1689 } 1690 1691 // Without an offset, no second scratch register is necessary. 1692 BufferOffset MacroAssemblerARM::ma_vstr(VFPRegister src, Register base, 1693 Register index, 1694 AutoRegisterScope& scratch, 1695 int32_t shift, Condition cc) { 1696 as_add(scratch, base, lsl(index, shift), LeaveCC, cc); 1697 return as_vdtr(IsStore, src, Operand(Address(scratch, 0)).toVFPAddr(), cc); 1698 } 1699 1700 bool MacroAssemblerARMCompat::buildOOLFakeExitFrame(void* fakeReturnAddr) { 1701 asMasm().Push(FrameDescriptor(FrameType::IonJS)); // descriptor_ 1702 asMasm().Push(ImmPtr(fakeReturnAddr)); 1703 asMasm().Push(FramePointer); 1704 return true; 1705 } 1706 1707 void MacroAssemblerARMCompat::move32(Imm32 imm, Register dest) { 1708 ma_mov(imm, dest); 1709 } 1710 1711 void MacroAssemblerARMCompat::move32(Register src, Register dest) { 1712 ma_mov(src, dest); 1713 } 1714 1715 void MacroAssemblerARMCompat::movePtr(Register src, Register dest) { 1716 ma_mov(src, dest); 1717 } 1718 1719 void MacroAssemblerARMCompat::movePtr(ImmWord imm, Register dest) { 1720 ma_mov(Imm32(imm.value), dest); 1721 } 1722 1723 void MacroAssemblerARMCompat::movePtr(ImmGCPtr imm, Register dest) { 1724 ma_mov(imm, dest); 1725 } 1726 1727 void MacroAssemblerARMCompat::movePtr(ImmPtr imm, Register dest) { 1728 movePtr(ImmWord(uintptr_t(imm.value)), dest); 1729 } 1730 1731 void MacroAssemblerARMCompat::movePtr(wasm::SymbolicAddress imm, 1732 Register dest) { 1733 append(wasm::SymbolicAccess(CodeOffset(currentOffset()), imm)); 1734 ma_movPatchable(Imm32(-1), dest, Always); 1735 } 1736 1737 FaultingCodeOffset MacroAssemblerARMCompat::load8ZeroExtend( 1738 const Address& address, Register dest) { 1739 ScratchRegisterScope scratch(asMasm()); 1740 BufferOffset offset = ma_dataTransferN(IsLoad, 8, false, address.base, 1741 Imm32(address.offset), dest, scratch); 1742 return FaultingCodeOffset(offset.getOffset()); 1743 } 1744 1745 FaultingCodeOffset MacroAssemblerARMCompat::load8ZeroExtend( 1746 const BaseIndex& src, Register dest) { 1747 Register base = src.base; 1748 uint32_t scale = Imm32::ShiftOf(src.scale).value; 1749 1750 ScratchRegisterScope scratch(asMasm()); 1751 SecondScratchRegisterScope scratch2(asMasm()); 1752 1753 FaultingCodeOffset fco; 1754 if (src.offset == 0) { 1755 fco = ma_ldrb(DTRAddr(base, DtrRegImmShift(src.index, LSL, scale)), dest); 1756 } else { 1757 ma_add(base, Imm32(src.offset), scratch, scratch2); 1758 fco = 1759 ma_ldrb(DTRAddr(scratch, DtrRegImmShift(src.index, LSL, scale)), dest); 1760 } 1761 return fco; 1762 } 1763 1764 FaultingCodeOffset MacroAssemblerARMCompat::load8SignExtend( 1765 const Address& address, Register dest) { 1766 ScratchRegisterScope scratch(asMasm()); 1767 BufferOffset offset = ma_dataTransferN(IsLoad, 8, true, address.base, 1768 Imm32(address.offset), dest, scratch); 1769 return FaultingCodeOffset(offset.getOffset()); 1770 } 1771 1772 FaultingCodeOffset MacroAssemblerARMCompat::load8SignExtend( 1773 const BaseIndex& src, Register dest) { 1774 Register index = src.index; 1775 1776 ScratchRegisterScope scratch(asMasm()); 1777 SecondScratchRegisterScope scratch2(asMasm()); 1778 1779 // ARMv7 does not have LSL on an index register with an extended load. 1780 if (src.scale != TimesOne) { 1781 ma_lsl(Imm32::ShiftOf(src.scale), index, scratch); 1782 index = scratch; 1783 } 1784 1785 if (src.offset != 0) { 1786 if (index != scratch) { 1787 ma_mov(index, scratch); 1788 index = scratch; 1789 } 1790 ma_add(Imm32(src.offset), index, scratch2); 1791 } 1792 return ma_ldrsb(EDtrAddr(src.base, EDtrOffReg(index)), dest); 1793 } 1794 1795 FaultingCodeOffset MacroAssemblerARMCompat::load16ZeroExtend( 1796 const Address& address, Register dest) { 1797 ScratchRegisterScope scratch(asMasm()); 1798 BufferOffset offset = ma_dataTransferN(IsLoad, 16, false, address.base, 1799 Imm32(address.offset), dest, scratch); 1800 return FaultingCodeOffset(offset.getOffset()); 1801 } 1802 1803 FaultingCodeOffset MacroAssemblerARMCompat::load16ZeroExtend( 1804 const BaseIndex& src, Register dest) { 1805 Register index = src.index; 1806 1807 ScratchRegisterScope scratch(asMasm()); 1808 SecondScratchRegisterScope scratch2(asMasm()); 1809 1810 // ARMv7 does not have LSL on an index register with an extended load. 1811 if (src.scale != TimesOne) { 1812 ma_lsl(Imm32::ShiftOf(src.scale), index, scratch); 1813 index = scratch; 1814 } 1815 1816 if (src.offset != 0) { 1817 if (index != scratch) { 1818 ma_mov(index, scratch); 1819 index = scratch; 1820 } 1821 ma_add(Imm32(src.offset), index, scratch2); 1822 } 1823 return ma_ldrh(EDtrAddr(src.base, EDtrOffReg(index)), dest); 1824 } 1825 1826 FaultingCodeOffset MacroAssemblerARMCompat::load16SignExtend( 1827 const Address& address, Register dest) { 1828 ScratchRegisterScope scratch(asMasm()); 1829 BufferOffset offset = ma_dataTransferN(IsLoad, 16, true, address.base, 1830 Imm32(address.offset), dest, scratch); 1831 return FaultingCodeOffset(offset.getOffset()); 1832 } 1833 1834 FaultingCodeOffset MacroAssemblerARMCompat::load16SignExtend( 1835 const BaseIndex& src, Register dest) { 1836 Register index = src.index; 1837 1838 ScratchRegisterScope scratch(asMasm()); 1839 SecondScratchRegisterScope scratch2(asMasm()); 1840 1841 // We don't have LSL on index register yet. 1842 if (src.scale != TimesOne) { 1843 ma_lsl(Imm32::ShiftOf(src.scale), index, scratch); 1844 index = scratch; 1845 } 1846 1847 if (src.offset != 0) { 1848 if (index != scratch) { 1849 ma_mov(index, scratch); 1850 index = scratch; 1851 } 1852 ma_add(Imm32(src.offset), index, scratch2); 1853 } 1854 return ma_ldrsh(EDtrAddr(src.base, EDtrOffReg(index)), dest); 1855 } 1856 1857 FaultingCodeOffset MacroAssemblerARMCompat::load32(const Address& address, 1858 Register dest) { 1859 return loadPtr(address, dest); 1860 } 1861 1862 FaultingCodeOffset MacroAssemblerARMCompat::load32(const BaseIndex& address, 1863 Register dest) { 1864 return loadPtr(address, dest); 1865 } 1866 1867 void MacroAssemblerARMCompat::load32(AbsoluteAddress address, Register dest) { 1868 loadPtr(address, dest); 1869 } 1870 1871 FaultingCodeOffset MacroAssemblerARMCompat::loadPtr(const Address& address, 1872 Register dest) { 1873 ScratchRegisterScope scratch(asMasm()); 1874 return ma_ldr(address, dest, scratch); 1875 } 1876 1877 FaultingCodeOffset MacroAssemblerARMCompat::loadPtr(const BaseIndex& src, 1878 Register dest) { 1879 Register base = src.base; 1880 uint32_t scale = Imm32::ShiftOf(src.scale).value; 1881 1882 ScratchRegisterScope scratch(asMasm()); 1883 SecondScratchRegisterScope scratch2(asMasm()); 1884 1885 FaultingCodeOffset fco; 1886 if (src.offset != 0) { 1887 ma_add(base, Imm32(src.offset), scratch, scratch2); 1888 fco = ma_ldr(DTRAddr(scratch, DtrRegImmShift(src.index, LSL, scale)), dest); 1889 } else { 1890 fco = ma_ldr(DTRAddr(base, DtrRegImmShift(src.index, LSL, scale)), dest); 1891 } 1892 return fco; 1893 } 1894 1895 void MacroAssemblerARMCompat::loadPtr(AbsoluteAddress address, Register dest) { 1896 MOZ_ASSERT(dest != pc); // Use dest as a scratch register. 1897 movePtr(ImmWord(uintptr_t(address.addr)), dest); 1898 loadPtr(Address(dest, 0), dest); 1899 } 1900 1901 void MacroAssemblerARMCompat::loadPtr(wasm::SymbolicAddress address, 1902 Register dest) { 1903 MOZ_ASSERT(dest != pc); // Use dest as a scratch register. 1904 movePtr(address, dest); 1905 loadPtr(Address(dest, 0), dest); 1906 } 1907 1908 void MacroAssemblerARMCompat::loadPrivate(const Address& address, 1909 Register dest) { 1910 ScratchRegisterScope scratch(asMasm()); 1911 ma_ldr(ToPayload(address), dest, scratch); 1912 } 1913 1914 FaultingCodeOffset MacroAssemblerARMCompat::loadDouble(const Address& address, 1915 FloatRegister dest) { 1916 ScratchRegisterScope scratch(asMasm()); 1917 BufferOffset offset = ma_vldr(address, dest, scratch); 1918 return FaultingCodeOffset(offset.getOffset()); 1919 } 1920 1921 FaultingCodeOffset MacroAssemblerARMCompat::loadDouble(const BaseIndex& src, 1922 FloatRegister dest) { 1923 // VFP instructions don't even support register Base + register Index modes, 1924 // so just add the index, then handle the offset like normal. 1925 Register base = src.base; 1926 Register index = src.index; 1927 uint32_t scale = Imm32::ShiftOf(src.scale).value; 1928 int32_t offset = src.offset; 1929 1930 ScratchRegisterScope scratch(asMasm()); 1931 SecondScratchRegisterScope scratch2(asMasm()); 1932 1933 as_add(scratch, base, lsl(index, scale)); 1934 BufferOffset boffset = ma_vldr(Address(scratch, offset), dest, scratch2); 1935 return FaultingCodeOffset(boffset.getOffset()); 1936 } 1937 1938 FaultingCodeOffset MacroAssemblerARMCompat::loadFloat32(const Address& address, 1939 FloatRegister dest) { 1940 ScratchRegisterScope scratch(asMasm()); 1941 BufferOffset offset = 1942 ma_vldr(address, VFPRegister(dest).singleOverlay(), scratch); 1943 return FaultingCodeOffset(offset.getOffset()); 1944 } 1945 1946 FaultingCodeOffset MacroAssemblerARMCompat::loadFloat32(const BaseIndex& src, 1947 FloatRegister dest) { 1948 // VFP instructions don't even support register Base + register Index modes, 1949 // so just add the index, then handle the offset like normal. 1950 Register base = src.base; 1951 Register index = src.index; 1952 uint32_t scale = Imm32::ShiftOf(src.scale).value; 1953 int32_t offset = src.offset; 1954 1955 ScratchRegisterScope scratch(asMasm()); 1956 SecondScratchRegisterScope scratch2(asMasm()); 1957 1958 as_add(scratch, base, lsl(index, scale)); 1959 BufferOffset boffset = ma_vldr(Address(scratch, offset), 1960 VFPRegister(dest).singleOverlay(), scratch2); 1961 return FaultingCodeOffset(boffset.getOffset()); 1962 } 1963 1964 FaultingCodeOffset MacroAssemblerARMCompat::loadFloat16(const Address& address, 1965 FloatRegister dest, 1966 Register scratch) { 1967 auto fco = load16ZeroExtend(address, scratch); 1968 ma_vxfer(scratch, dest); 1969 return fco; 1970 } 1971 1972 FaultingCodeOffset MacroAssemblerARMCompat::loadFloat16(const BaseIndex& src, 1973 FloatRegister dest, 1974 Register scratch) { 1975 auto fco = load16ZeroExtend(src, scratch); 1976 ma_vxfer(scratch, dest); 1977 return fco; 1978 } 1979 1980 void MacroAssemblerARMCompat::store8(Imm32 imm, const Address& address) { 1981 SecondScratchRegisterScope scratch2(asMasm()); 1982 ma_mov(imm, scratch2); 1983 store8(scratch2, address); 1984 } 1985 1986 FaultingCodeOffset MacroAssemblerARMCompat::store8(Register src, 1987 const Address& address) { 1988 ScratchRegisterScope scratch(asMasm()); 1989 BufferOffset offset = ma_dataTransferN(IsStore, 8, false, address.base, 1990 Imm32(address.offset), src, scratch); 1991 return FaultingCodeOffset(offset.getOffset()); 1992 } 1993 1994 void MacroAssemblerARMCompat::store8(Imm32 imm, const BaseIndex& dest) { 1995 Register base = dest.base; 1996 uint32_t scale = Imm32::ShiftOf(dest.scale).value; 1997 1998 ScratchRegisterScope scratch(asMasm()); 1999 SecondScratchRegisterScope scratch2(asMasm()); 2000 2001 if (dest.offset != 0) { 2002 ma_add(base, Imm32(dest.offset), scratch, scratch2); 2003 ma_mov(imm, scratch2); 2004 ma_strb(scratch2, DTRAddr(scratch, DtrRegImmShift(dest.index, LSL, scale))); 2005 } else { 2006 ma_mov(imm, scratch2); 2007 ma_strb(scratch2, DTRAddr(base, DtrRegImmShift(dest.index, LSL, scale))); 2008 } 2009 } 2010 2011 FaultingCodeOffset MacroAssemblerARMCompat::store8(Register src, 2012 const BaseIndex& dest) { 2013 Register base = dest.base; 2014 uint32_t scale = Imm32::ShiftOf(dest.scale).value; 2015 2016 ScratchRegisterScope scratch(asMasm()); 2017 SecondScratchRegisterScope scratch2(asMasm()); 2018 2019 FaultingCodeOffset fco; 2020 if (dest.offset != 0) { 2021 ma_add(base, Imm32(dest.offset), scratch, scratch2); 2022 fco = 2023 ma_strb(src, DTRAddr(scratch, DtrRegImmShift(dest.index, LSL, scale))); 2024 } else { 2025 fco = ma_strb(src, DTRAddr(base, DtrRegImmShift(dest.index, LSL, scale))); 2026 } 2027 return fco; 2028 } 2029 2030 void MacroAssemblerARMCompat::store16(Imm32 imm, const Address& address) { 2031 SecondScratchRegisterScope scratch2(asMasm()); 2032 ma_mov(imm, scratch2); 2033 store16(scratch2, address); 2034 } 2035 2036 FaultingCodeOffset MacroAssemblerARMCompat::store16(Register src, 2037 const Address& address) { 2038 ScratchRegisterScope scratch(asMasm()); 2039 BufferOffset offset = ma_dataTransferN(IsStore, 16, false, address.base, 2040 Imm32(address.offset), src, scratch); 2041 return FaultingCodeOffset(offset.getOffset()); 2042 } 2043 2044 void MacroAssemblerARMCompat::store16(Imm32 imm, const BaseIndex& dest) { 2045 Register index = dest.index; 2046 2047 ScratchRegisterScope scratch(asMasm()); 2048 SecondScratchRegisterScope scratch2(asMasm()); 2049 2050 // We don't have LSL on index register yet. 2051 if (dest.scale != TimesOne) { 2052 ma_lsl(Imm32::ShiftOf(dest.scale), index, scratch); 2053 index = scratch; 2054 } 2055 2056 if (dest.offset != 0) { 2057 ma_add(index, Imm32(dest.offset), scratch, scratch2); 2058 index = scratch; 2059 } 2060 2061 ma_mov(imm, scratch2); 2062 ma_strh(scratch2, EDtrAddr(dest.base, EDtrOffReg(index))); 2063 } 2064 2065 FaultingCodeOffset MacroAssemblerARMCompat::store16(Register src, 2066 const BaseIndex& address) { 2067 Register index = address.index; 2068 2069 ScratchRegisterScope scratch(asMasm()); 2070 SecondScratchRegisterScope scratch2(asMasm()); 2071 2072 // We don't have LSL on index register yet. 2073 if (address.scale != TimesOne) { 2074 ma_lsl(Imm32::ShiftOf(address.scale), index, scratch); 2075 index = scratch; 2076 } 2077 2078 if (address.offset != 0) { 2079 ma_add(index, Imm32(address.offset), scratch, scratch2); 2080 index = scratch; 2081 } 2082 return ma_strh(src, EDtrAddr(address.base, EDtrOffReg(index))); 2083 } 2084 2085 void MacroAssemblerARMCompat::store32(Register src, AbsoluteAddress address) { 2086 storePtr(src, address); 2087 } 2088 2089 FaultingCodeOffset MacroAssemblerARMCompat::store32(Register src, 2090 const Address& address) { 2091 return storePtr(src, address); 2092 } 2093 2094 void MacroAssemblerARMCompat::store32(Imm32 src, const Address& address) { 2095 ScratchRegisterScope scratch(asMasm()); 2096 SecondScratchRegisterScope scratch2(asMasm()); 2097 move32(src, scratch); 2098 ma_str(scratch, address, scratch2); 2099 } 2100 2101 void MacroAssemblerARMCompat::store32(Imm32 imm, const BaseIndex& dest) { 2102 Register base = dest.base; 2103 uint32_t scale = Imm32::ShiftOf(dest.scale).value; 2104 2105 ScratchRegisterScope scratch(asMasm()); 2106 SecondScratchRegisterScope scratch2(asMasm()); 2107 2108 if (dest.offset != 0) { 2109 ma_add(base, Imm32(dest.offset), scratch, scratch2); 2110 ma_mov(imm, scratch2); 2111 ma_str(scratch2, DTRAddr(scratch, DtrRegImmShift(dest.index, LSL, scale))); 2112 } else { 2113 ma_mov(imm, scratch); 2114 ma_str(scratch, DTRAddr(base, DtrRegImmShift(dest.index, LSL, scale))); 2115 } 2116 } 2117 2118 FaultingCodeOffset MacroAssemblerARMCompat::store32(Register src, 2119 const BaseIndex& dest) { 2120 Register base = dest.base; 2121 uint32_t scale = Imm32::ShiftOf(dest.scale).value; 2122 2123 ScratchRegisterScope scratch(asMasm()); 2124 SecondScratchRegisterScope scratch2(asMasm()); 2125 2126 FaultingCodeOffset fco; 2127 if (dest.offset != 0) { 2128 ma_add(base, Imm32(dest.offset), scratch, scratch2); 2129 fco = ma_str(src, DTRAddr(scratch, DtrRegImmShift(dest.index, LSL, scale))); 2130 } else { 2131 fco = ma_str(src, DTRAddr(base, DtrRegImmShift(dest.index, LSL, scale))); 2132 } 2133 return fco; 2134 } 2135 2136 void MacroAssemblerARMCompat::storePtr(ImmWord imm, const Address& address) { 2137 store32(Imm32(imm.value), address); 2138 } 2139 2140 void MacroAssemblerARMCompat::storePtr(ImmWord imm, const BaseIndex& address) { 2141 store32(Imm32(imm.value), address); 2142 } 2143 2144 void MacroAssemblerARMCompat::storePtr(ImmPtr imm, const Address& address) { 2145 store32(Imm32(uintptr_t(imm.value)), address); 2146 } 2147 2148 void MacroAssemblerARMCompat::storePtr(ImmPtr imm, const BaseIndex& address) { 2149 store32(Imm32(uintptr_t(imm.value)), address); 2150 } 2151 2152 void MacroAssemblerARMCompat::storePtr(ImmGCPtr imm, const Address& address) { 2153 ScratchRegisterScope scratch(asMasm()); 2154 SecondScratchRegisterScope scratch2(asMasm()); 2155 ma_mov(imm, scratch); 2156 ma_str(scratch, address, scratch2); 2157 } 2158 2159 void MacroAssemblerARMCompat::storePtr(ImmGCPtr imm, const BaseIndex& address) { 2160 Register base = address.base; 2161 uint32_t scale = Imm32::ShiftOf(address.scale).value; 2162 2163 ScratchRegisterScope scratch(asMasm()); 2164 SecondScratchRegisterScope scratch2(asMasm()); 2165 2166 if (address.offset != 0) { 2167 ma_add(base, Imm32(address.offset), scratch, scratch2); 2168 ma_mov(imm, scratch2); 2169 ma_str(scratch2, 2170 DTRAddr(scratch, DtrRegImmShift(address.index, LSL, scale))); 2171 } else { 2172 ma_mov(imm, scratch); 2173 ma_str(scratch, DTRAddr(base, DtrRegImmShift(address.index, LSL, scale))); 2174 } 2175 } 2176 2177 FaultingCodeOffset MacroAssemblerARMCompat::storePtr(Register src, 2178 const Address& address) { 2179 SecondScratchRegisterScope scratch2(asMasm()); 2180 return ma_str(src, address, scratch2); 2181 } 2182 2183 FaultingCodeOffset MacroAssemblerARMCompat::storePtr(Register src, 2184 const BaseIndex& address) { 2185 return store32(src, address); 2186 } 2187 2188 void MacroAssemblerARMCompat::storePtr(Register src, AbsoluteAddress dest) { 2189 ScratchRegisterScope scratch(asMasm()); 2190 movePtr(ImmWord(uintptr_t(dest.addr)), scratch); 2191 ma_str(src, DTRAddr(scratch, DtrOffImm(0))); 2192 } 2193 2194 // Note: this function clobbers the input register. 2195 void MacroAssembler::clampDoubleToUint8(FloatRegister input, Register output) { 2196 ScratchDoubleScope scratchDouble(*this); 2197 MOZ_ASSERT(input != scratchDouble); 2198 2199 Label done; 2200 2201 // Set to zero if NaN. 2202 compareDouble(input, NoVFPRegister); 2203 ma_mov(Imm32(0), output, VFP_Unordered); 2204 ma_b(&done, VFP_Unordered); 2205 2206 // Do the conversion to an integer. 2207 as_vcvt(VFPRegister(scratchDouble).uintOverlay(), VFPRegister(input)); 2208 2209 // Copy the converted value out. 2210 as_vxfer(output, InvalidReg, scratchDouble, FloatToCore); 2211 2212 // Clamp to 255. 2213 as_cmp(output, Imm8(0xff)); 2214 ma_mov(Imm32(0xff), output, Above); 2215 ma_b(&done, AboveOrEqual); 2216 2217 // Convert it back to see if we got the same value back. 2218 as_vcvt(scratchDouble, VFPRegister(scratchDouble).uintOverlay()); 2219 ma_vsub(input, scratchDouble, input); 2220 2221 loadConstantDouble(0.5, scratchDouble); 2222 2223 // Do the check. 2224 compareDouble(input, scratchDouble); 2225 2226 // Round up if > 0.5. 2227 as_add(output, output, Imm8(1), LeaveCC, VFP_GreaterThan); 2228 2229 // Round up if == 0.5 and output is odd. 2230 as_add(output, output, Imm8(1), LeaveCC, VFP_Equal); 2231 as_bic(output, output, Imm8(1), LeaveCC, VFP_Equal); 2232 2233 bind(&done); 2234 } 2235 2236 void MacroAssemblerARMCompat::cmp32(Register lhs, Imm32 rhs) { 2237 ScratchRegisterScope scratch(asMasm()); 2238 ma_cmp(lhs, rhs, scratch); 2239 } 2240 2241 void MacroAssemblerARMCompat::cmp32(Register lhs, Register rhs) { 2242 ma_cmp(lhs, rhs); 2243 } 2244 2245 void MacroAssemblerARMCompat::cmp32(const Address& lhs, Imm32 rhs) { 2246 ScratchRegisterScope scratch(asMasm()); 2247 SecondScratchRegisterScope scratch2(asMasm()); 2248 ma_ldr(lhs, scratch, scratch2); 2249 ma_cmp(scratch, rhs, scratch2); 2250 } 2251 2252 void MacroAssemblerARMCompat::cmp32(const Address& lhs, Register rhs) { 2253 ScratchRegisterScope scratch(asMasm()); 2254 SecondScratchRegisterScope scratch2(asMasm()); 2255 ma_ldr(lhs, scratch, scratch2); 2256 ma_cmp(scratch, rhs); 2257 } 2258 2259 void MacroAssemblerARMCompat::cmpPtr(Register lhs, ImmWord rhs) { 2260 cmp32(lhs, Imm32(rhs.value)); 2261 } 2262 2263 void MacroAssemblerARMCompat::cmpPtr(Register lhs, ImmPtr rhs) { 2264 cmpPtr(lhs, ImmWord(uintptr_t(rhs.value))); 2265 } 2266 2267 void MacroAssemblerARMCompat::cmpPtr(Register lhs, Register rhs) { 2268 ma_cmp(lhs, rhs); 2269 } 2270 2271 void MacroAssemblerARMCompat::cmpPtr(Register lhs, ImmGCPtr rhs) { 2272 ScratchRegisterScope scratch(asMasm()); 2273 ma_cmp(lhs, rhs, scratch); 2274 } 2275 2276 void MacroAssemblerARMCompat::cmpPtr(Register lhs, Imm32 rhs) { 2277 cmp32(lhs, rhs); 2278 } 2279 2280 void MacroAssemblerARMCompat::cmpPtr(const Address& lhs, Register rhs) { 2281 ScratchRegisterScope scratch(asMasm()); 2282 SecondScratchRegisterScope scratch2(asMasm()); 2283 ma_ldr(lhs, scratch, scratch2); 2284 ma_cmp(scratch, rhs); 2285 } 2286 2287 void MacroAssemblerARMCompat::cmpPtr(const Address& lhs, ImmWord rhs) { 2288 ScratchRegisterScope scratch(asMasm()); 2289 SecondScratchRegisterScope scratch2(asMasm()); 2290 ma_ldr(lhs, scratch, scratch2); 2291 ma_cmp(scratch, Imm32(rhs.value), scratch2); 2292 } 2293 2294 void MacroAssemblerARMCompat::cmpPtr(const Address& lhs, ImmPtr rhs) { 2295 cmpPtr(lhs, ImmWord(uintptr_t(rhs.value))); 2296 } 2297 2298 void MacroAssemblerARMCompat::cmpPtr(const Address& lhs, ImmGCPtr rhs) { 2299 ScratchRegisterScope scratch(asMasm()); 2300 SecondScratchRegisterScope scratch2(asMasm()); 2301 ma_ldr(lhs, scratch, scratch2); 2302 ma_cmp(scratch, rhs, scratch2); 2303 } 2304 2305 void MacroAssemblerARMCompat::cmpPtr(const Address& lhs, Imm32 rhs) { 2306 ScratchRegisterScope scratch(asMasm()); 2307 SecondScratchRegisterScope scratch2(asMasm()); 2308 ma_ldr(lhs, scratch, scratch2); 2309 ma_cmp(scratch, rhs, scratch2); 2310 } 2311 2312 void MacroAssemblerARMCompat::setStackArg(Register reg, uint32_t arg) { 2313 ScratchRegisterScope scratch(asMasm()); 2314 ma_dataTransferN(IsStore, 32, true, sp, Imm32(arg * sizeof(intptr_t)), reg, 2315 scratch); 2316 } 2317 2318 void MacroAssemblerARMCompat::minMaxDouble(FloatRegister srcDest, 2319 FloatRegister second, bool canBeNaN, 2320 bool isMax) { 2321 FloatRegister first = srcDest; 2322 2323 Label nan, equal, returnSecond, done; 2324 2325 Assembler::Condition cond = isMax ? Assembler::VFP_LessThanOrEqual 2326 : Assembler::VFP_GreaterThanOrEqual; 2327 2328 compareDouble(first, second); 2329 // First or second is NaN, result is NaN. 2330 ma_b(&nan, Assembler::VFP_Unordered); 2331 // Make sure we handle -0 and 0 right. 2332 ma_b(&equal, Assembler::VFP_Equal); 2333 ma_b(&returnSecond, cond); 2334 ma_b(&done); 2335 2336 // Check for zero. 2337 bind(&equal); 2338 compareDouble(first, NoVFPRegister); 2339 // First wasn't 0 or -0, so just return it. 2340 ma_b(&done, Assembler::VFP_NotEqualOrUnordered); 2341 // So now both operands are either -0 or 0. 2342 if (isMax) { 2343 // -0 + -0 = -0 and -0 + 0 = 0. 2344 ma_vadd(second, first, first); 2345 } else { 2346 ma_vneg(first, first); 2347 ma_vsub(first, second, first); 2348 ma_vneg(first, first); 2349 } 2350 ma_b(&done); 2351 2352 bind(&nan); 2353 // If the first argument is the NaN, return it; otherwise return the second 2354 // operand. 2355 compareDouble(first, first); 2356 ma_vmov(first, srcDest, Assembler::VFP_Unordered); 2357 ma_b(&done, Assembler::VFP_Unordered); 2358 2359 bind(&returnSecond); 2360 ma_vmov(second, srcDest); 2361 2362 bind(&done); 2363 } 2364 2365 void MacroAssemblerARMCompat::minMax32(Register lhs, Register rhs, 2366 Register dest, bool isMax) { 2367 if (rhs == dest) { 2368 std::swap(lhs, rhs); 2369 } 2370 2371 auto cond = isMax ? Assembler::LessThan : Assembler::GreaterThan; 2372 if (lhs != dest) { 2373 move32(lhs, dest); 2374 } 2375 cmp32(lhs, rhs); 2376 ma_mov(rhs, dest, LeaveCC, cond); 2377 } 2378 2379 void MacroAssemblerARMCompat::minMax32(Register lhs, Imm32 rhs, Register dest, 2380 bool isMax) { 2381 // We need a scratch register when |rhs| can't be encoded in the compare 2382 // instruction. 2383 if (Imm8(rhs.value).invalid() && Imm8(~rhs.value).invalid()) { 2384 ScratchRegisterScope scratch(asMasm()); 2385 move32(rhs, scratch); 2386 minMax32(lhs, scratch, dest, isMax); 2387 return; 2388 } 2389 2390 auto cond = isMax ? Assembler::LessThan : Assembler::GreaterThan; 2391 if (lhs != dest) { 2392 move32(lhs, dest); 2393 } 2394 cmp32(lhs, rhs); 2395 ma_mov(rhs, dest, cond); 2396 } 2397 2398 void MacroAssemblerARMCompat::minMaxFloat32(FloatRegister srcDest, 2399 FloatRegister second, bool canBeNaN, 2400 bool isMax) { 2401 FloatRegister first = srcDest; 2402 2403 Label nan, equal, returnSecond, done; 2404 2405 Assembler::Condition cond = isMax ? Assembler::VFP_LessThanOrEqual 2406 : Assembler::VFP_GreaterThanOrEqual; 2407 2408 compareFloat(first, second); 2409 // First or second is NaN, result is NaN. 2410 ma_b(&nan, Assembler::VFP_Unordered); 2411 // Make sure we handle -0 and 0 right. 2412 ma_b(&equal, Assembler::VFP_Equal); 2413 ma_b(&returnSecond, cond); 2414 ma_b(&done); 2415 2416 // Check for zero. 2417 bind(&equal); 2418 compareFloat(first, NoVFPRegister); 2419 // First wasn't 0 or -0, so just return it. 2420 ma_b(&done, Assembler::VFP_NotEqualOrUnordered); 2421 // So now both operands are either -0 or 0. 2422 if (isMax) { 2423 // -0 + -0 = -0 and -0 + 0 = 0. 2424 ma_vadd_f32(second, first, first); 2425 } else { 2426 ma_vneg_f32(first, first); 2427 ma_vsub_f32(first, second, first); 2428 ma_vneg_f32(first, first); 2429 } 2430 ma_b(&done); 2431 2432 bind(&nan); 2433 // See comment in minMaxDouble. 2434 compareFloat(first, first); 2435 ma_vmov_f32(first, srcDest, Assembler::VFP_Unordered); 2436 ma_b(&done, Assembler::VFP_Unordered); 2437 2438 bind(&returnSecond); 2439 ma_vmov_f32(second, srcDest); 2440 2441 bind(&done); 2442 } 2443 2444 void MacroAssemblerARMCompat::compareDouble(FloatRegister lhs, 2445 FloatRegister rhs) { 2446 // Compare the doubles, setting vector status flags. 2447 if (rhs.isMissing()) { 2448 ma_vcmpz(lhs); 2449 } else { 2450 ma_vcmp(lhs, rhs); 2451 } 2452 2453 // Move vector status bits to normal status flags. 2454 as_vmrs(pc); 2455 } 2456 2457 void MacroAssemblerARMCompat::compareFloat(FloatRegister lhs, 2458 FloatRegister rhs) { 2459 // Compare the doubles, setting vector status flags. 2460 if (rhs.isMissing()) { 2461 as_vcmpz(VFPRegister(lhs).singleOverlay()); 2462 } else { 2463 as_vcmp(VFPRegister(lhs).singleOverlay(), VFPRegister(rhs).singleOverlay()); 2464 } 2465 2466 // Move vector status bits to normal status flags. 2467 as_vmrs(pc); 2468 } 2469 2470 Assembler::Condition MacroAssemblerARMCompat::testInt32( 2471 Assembler::Condition cond, const ValueOperand& value) { 2472 MOZ_ASSERT(cond == Assembler::Equal || cond == Assembler::NotEqual); 2473 ma_cmp(value.typeReg(), ImmType(JSVAL_TYPE_INT32)); 2474 return cond; 2475 } 2476 2477 Assembler::Condition MacroAssemblerARMCompat::testBoolean( 2478 Assembler::Condition cond, const ValueOperand& value) { 2479 MOZ_ASSERT(cond == Assembler::Equal || cond == Assembler::NotEqual); 2480 ma_cmp(value.typeReg(), ImmType(JSVAL_TYPE_BOOLEAN)); 2481 return cond; 2482 } 2483 2484 Assembler::Condition MacroAssemblerARMCompat::testDouble( 2485 Assembler::Condition cond, const ValueOperand& value) { 2486 MOZ_ASSERT(cond == Assembler::Equal || cond == Assembler::NotEqual); 2487 Assembler::Condition actual = (cond == Equal) ? Below : AboveOrEqual; 2488 ScratchRegisterScope scratch(asMasm()); 2489 ma_cmp(value.typeReg(), ImmTag(JSVAL_TAG_CLEAR), scratch); 2490 return actual; 2491 } 2492 2493 Assembler::Condition MacroAssemblerARMCompat::testNull( 2494 Assembler::Condition cond, const ValueOperand& value) { 2495 MOZ_ASSERT(cond == Assembler::Equal || cond == Assembler::NotEqual); 2496 ma_cmp(value.typeReg(), ImmType(JSVAL_TYPE_NULL)); 2497 return cond; 2498 } 2499 2500 Assembler::Condition MacroAssemblerARMCompat::testUndefined( 2501 Assembler::Condition cond, const ValueOperand& value) { 2502 MOZ_ASSERT(cond == Assembler::Equal || cond == Assembler::NotEqual); 2503 ma_cmp(value.typeReg(), ImmType(JSVAL_TYPE_UNDEFINED)); 2504 return cond; 2505 } 2506 2507 Assembler::Condition MacroAssemblerARMCompat::testString( 2508 Assembler::Condition cond, const ValueOperand& value) { 2509 return testString(cond, value.typeReg()); 2510 } 2511 2512 Assembler::Condition MacroAssemblerARMCompat::testSymbol( 2513 Assembler::Condition cond, const ValueOperand& value) { 2514 return testSymbol(cond, value.typeReg()); 2515 } 2516 2517 Assembler::Condition MacroAssemblerARMCompat::testBigInt( 2518 Assembler::Condition cond, const ValueOperand& value) { 2519 return testBigInt(cond, value.typeReg()); 2520 } 2521 2522 Assembler::Condition MacroAssemblerARMCompat::testObject( 2523 Assembler::Condition cond, const ValueOperand& value) { 2524 return testObject(cond, value.typeReg()); 2525 } 2526 2527 Assembler::Condition MacroAssemblerARMCompat::testNumber( 2528 Assembler::Condition cond, const ValueOperand& value) { 2529 return testNumber(cond, value.typeReg()); 2530 } 2531 2532 Assembler::Condition MacroAssemblerARMCompat::testMagic( 2533 Assembler::Condition cond, const ValueOperand& value) { 2534 return testMagic(cond, value.typeReg()); 2535 } 2536 2537 Assembler::Condition MacroAssemblerARMCompat::testPrimitive( 2538 Assembler::Condition cond, const ValueOperand& value) { 2539 return testPrimitive(cond, value.typeReg()); 2540 } 2541 2542 Assembler::Condition MacroAssemblerARMCompat::testGCThing( 2543 Assembler::Condition cond, const ValueOperand& value) { 2544 return testGCThing(cond, value.typeReg()); 2545 } 2546 2547 // Register-based tests. 2548 Assembler::Condition MacroAssemblerARMCompat::testInt32( 2549 Assembler::Condition cond, Register tag) { 2550 MOZ_ASSERT(cond == Equal || cond == NotEqual); 2551 ma_cmp(tag, ImmTag(JSVAL_TAG_INT32)); 2552 return cond; 2553 } 2554 2555 Assembler::Condition MacroAssemblerARMCompat::testBoolean( 2556 Assembler::Condition cond, Register tag) { 2557 MOZ_ASSERT(cond == Equal || cond == NotEqual); 2558 ma_cmp(tag, ImmTag(JSVAL_TAG_BOOLEAN)); 2559 return cond; 2560 } 2561 2562 Assembler::Condition MacroAssemblerARMCompat::testNull( 2563 Assembler::Condition cond, Register tag) { 2564 MOZ_ASSERT(cond == Equal || cond == NotEqual); 2565 ma_cmp(tag, ImmTag(JSVAL_TAG_NULL)); 2566 return cond; 2567 } 2568 2569 Assembler::Condition MacroAssemblerARMCompat::testUndefined( 2570 Assembler::Condition cond, Register tag) { 2571 MOZ_ASSERT(cond == Equal || cond == NotEqual); 2572 ma_cmp(tag, ImmTag(JSVAL_TAG_UNDEFINED)); 2573 return cond; 2574 } 2575 2576 Assembler::Condition MacroAssemblerARMCompat::testString( 2577 Assembler::Condition cond, Register tag) { 2578 MOZ_ASSERT(cond == Equal || cond == NotEqual); 2579 ma_cmp(tag, ImmTag(JSVAL_TAG_STRING)); 2580 return cond; 2581 } 2582 2583 Assembler::Condition MacroAssemblerARMCompat::testSymbol( 2584 Assembler::Condition cond, Register tag) { 2585 MOZ_ASSERT(cond == Equal || cond == NotEqual); 2586 ma_cmp(tag, ImmTag(JSVAL_TAG_SYMBOL)); 2587 return cond; 2588 } 2589 2590 Assembler::Condition MacroAssemblerARMCompat::testBigInt( 2591 Assembler::Condition cond, Register tag) { 2592 MOZ_ASSERT(cond == Equal || cond == NotEqual); 2593 ma_cmp(tag, ImmTag(JSVAL_TAG_BIGINT)); 2594 return cond; 2595 } 2596 2597 Assembler::Condition MacroAssemblerARMCompat::testObject( 2598 Assembler::Condition cond, Register tag) { 2599 MOZ_ASSERT(cond == Equal || cond == NotEqual); 2600 ma_cmp(tag, ImmTag(JSVAL_TAG_OBJECT)); 2601 return cond; 2602 } 2603 2604 Assembler::Condition MacroAssemblerARMCompat::testMagic( 2605 Assembler::Condition cond, Register tag) { 2606 MOZ_ASSERT(cond == Equal || cond == NotEqual); 2607 ma_cmp(tag, ImmTag(JSVAL_TAG_MAGIC)); 2608 return cond; 2609 } 2610 2611 Assembler::Condition MacroAssemblerARMCompat::testPrimitive( 2612 Assembler::Condition cond, Register tag) { 2613 MOZ_ASSERT(cond == Equal || cond == NotEqual); 2614 ma_cmp(tag, ImmTag(JS::detail::ValueUpperExclPrimitiveTag)); 2615 return cond == Equal ? Below : AboveOrEqual; 2616 } 2617 2618 Assembler::Condition MacroAssemblerARMCompat::testGCThing( 2619 Assembler::Condition cond, Register tag) { 2620 MOZ_ASSERT(cond == Equal || cond == NotEqual); 2621 ma_cmp(tag, ImmTag(JS::detail::ValueLowerInclGCThingTag)); 2622 return cond == Equal ? AboveOrEqual : Below; 2623 } 2624 2625 Assembler::Condition MacroAssemblerARMCompat::testGCThing( 2626 Assembler::Condition cond, const Address& address) { 2627 MOZ_ASSERT(cond == Equal || cond == NotEqual); 2628 ScratchRegisterScope scratch(asMasm()); 2629 Register tag = extractTag(address, scratch); 2630 ma_cmp(tag, ImmTag(JS::detail::ValueLowerInclGCThingTag)); 2631 return cond == Equal ? AboveOrEqual : Below; 2632 } 2633 2634 Assembler::Condition MacroAssemblerARMCompat::testMagic( 2635 Assembler::Condition cond, const Address& address) { 2636 MOZ_ASSERT(cond == Equal || cond == NotEqual); 2637 ScratchRegisterScope scratch(asMasm()); 2638 Register tag = extractTag(address, scratch); 2639 ma_cmp(tag, ImmTag(JSVAL_TAG_MAGIC)); 2640 return cond; 2641 } 2642 2643 Assembler::Condition MacroAssemblerARMCompat::testInt32( 2644 Assembler::Condition cond, const Address& address) { 2645 MOZ_ASSERT(cond == Equal || cond == NotEqual); 2646 ScratchRegisterScope scratch(asMasm()); 2647 Register tag = extractTag(address, scratch); 2648 ma_cmp(tag, ImmTag(JSVAL_TAG_INT32)); 2649 return cond; 2650 } 2651 2652 Assembler::Condition MacroAssemblerARMCompat::testDouble( 2653 Condition cond, const Address& address) { 2654 MOZ_ASSERT(cond == Equal || cond == NotEqual); 2655 ScratchRegisterScope scratch(asMasm()); 2656 Register tag = extractTag(address, scratch); 2657 return testDouble(cond, tag); 2658 } 2659 2660 Assembler::Condition MacroAssemblerARMCompat::testBoolean( 2661 Condition cond, const Address& address) { 2662 MOZ_ASSERT(cond == Equal || cond == NotEqual); 2663 ScratchRegisterScope scratch(asMasm()); 2664 Register tag = extractTag(address, scratch); 2665 return testBoolean(cond, tag); 2666 } 2667 2668 Assembler::Condition MacroAssemblerARMCompat::testNull(Condition cond, 2669 const Address& address) { 2670 MOZ_ASSERT(cond == Equal || cond == NotEqual); 2671 ScratchRegisterScope scratch(asMasm()); 2672 Register tag = extractTag(address, scratch); 2673 return testNull(cond, tag); 2674 } 2675 2676 Assembler::Condition MacroAssemblerARMCompat::testUndefined( 2677 Condition cond, const Address& address) { 2678 MOZ_ASSERT(cond == Equal || cond == NotEqual); 2679 ScratchRegisterScope scratch(asMasm()); 2680 Register tag = extractTag(address, scratch); 2681 return testUndefined(cond, tag); 2682 } 2683 2684 Assembler::Condition MacroAssemblerARMCompat::testString( 2685 Condition cond, const Address& address) { 2686 MOZ_ASSERT(cond == Equal || cond == NotEqual); 2687 ScratchRegisterScope scratch(asMasm()); 2688 Register tag = extractTag(address, scratch); 2689 return testString(cond, tag); 2690 } 2691 2692 Assembler::Condition MacroAssemblerARMCompat::testSymbol( 2693 Condition cond, const Address& address) { 2694 MOZ_ASSERT(cond == Equal || cond == NotEqual); 2695 ScratchRegisterScope scratch(asMasm()); 2696 Register tag = extractTag(address, scratch); 2697 return testSymbol(cond, tag); 2698 } 2699 2700 Assembler::Condition MacroAssemblerARMCompat::testBigInt( 2701 Condition cond, const Address& address) { 2702 MOZ_ASSERT(cond == Equal || cond == NotEqual); 2703 ScratchRegisterScope scratch(asMasm()); 2704 Register tag = extractTag(address, scratch); 2705 return testBigInt(cond, tag); 2706 } 2707 2708 Assembler::Condition MacroAssemblerARMCompat::testObject( 2709 Condition cond, const Address& address) { 2710 MOZ_ASSERT(cond == Equal || cond == NotEqual); 2711 ScratchRegisterScope scratch(asMasm()); 2712 Register tag = extractTag(address, scratch); 2713 return testObject(cond, tag); 2714 } 2715 2716 Assembler::Condition MacroAssemblerARMCompat::testNumber( 2717 Condition cond, const Address& address) { 2718 MOZ_ASSERT(cond == Equal || cond == NotEqual); 2719 ScratchRegisterScope scratch(asMasm()); 2720 Register tag = extractTag(address, scratch); 2721 return testNumber(cond, tag); 2722 } 2723 2724 Assembler::Condition MacroAssemblerARMCompat::testDouble(Condition cond, 2725 Register tag) { 2726 MOZ_ASSERT(cond == Assembler::Equal || cond == Assembler::NotEqual); 2727 Condition actual = (cond == Equal) ? Below : AboveOrEqual; 2728 ma_cmp(tag, ImmTag(JSVAL_TAG_CLEAR)); 2729 return actual; 2730 } 2731 2732 Assembler::Condition MacroAssemblerARMCompat::testNumber(Condition cond, 2733 Register tag) { 2734 MOZ_ASSERT(cond == Equal || cond == NotEqual); 2735 ma_cmp(tag, ImmTag(JS::detail::ValueUpperInclNumberTag)); 2736 return cond == Equal ? BelowOrEqual : Above; 2737 } 2738 2739 Assembler::Condition MacroAssemblerARMCompat::testUndefined( 2740 Condition cond, const BaseIndex& src) { 2741 MOZ_ASSERT(cond == Equal || cond == NotEqual); 2742 ScratchRegisterScope scratch(asMasm()); 2743 Register tag = extractTag(src, scratch); 2744 ma_cmp(tag, ImmTag(JSVAL_TAG_UNDEFINED)); 2745 return cond; 2746 } 2747 2748 Assembler::Condition MacroAssemblerARMCompat::testNull(Condition cond, 2749 const BaseIndex& src) { 2750 MOZ_ASSERT(cond == Equal || cond == NotEqual); 2751 ScratchRegisterScope scratch(asMasm()); 2752 Register tag = extractTag(src, scratch); 2753 ma_cmp(tag, ImmTag(JSVAL_TAG_NULL)); 2754 return cond; 2755 } 2756 2757 Assembler::Condition MacroAssemblerARMCompat::testBoolean( 2758 Condition cond, const BaseIndex& src) { 2759 MOZ_ASSERT(cond == Equal || cond == NotEqual); 2760 ScratchRegisterScope scratch(asMasm()); 2761 Register tag = extractTag(src, scratch); 2762 ma_cmp(tag, ImmTag(JSVAL_TAG_BOOLEAN)); 2763 return cond; 2764 } 2765 2766 Assembler::Condition MacroAssemblerARMCompat::testString(Condition cond, 2767 const BaseIndex& src) { 2768 MOZ_ASSERT(cond == Equal || cond == NotEqual); 2769 ScratchRegisterScope scratch(asMasm()); 2770 Register tag = extractTag(src, scratch); 2771 ma_cmp(tag, ImmTag(JSVAL_TAG_STRING)); 2772 return cond; 2773 } 2774 2775 Assembler::Condition MacroAssemblerARMCompat::testSymbol(Condition cond, 2776 const BaseIndex& src) { 2777 MOZ_ASSERT(cond == Equal || cond == NotEqual); 2778 ScratchRegisterScope scratch(asMasm()); 2779 Register tag = extractTag(src, scratch); 2780 ma_cmp(tag, ImmTag(JSVAL_TAG_SYMBOL)); 2781 return cond; 2782 } 2783 2784 Assembler::Condition MacroAssemblerARMCompat::testBigInt(Condition cond, 2785 const BaseIndex& src) { 2786 MOZ_ASSERT(cond == Equal || cond == NotEqual); 2787 ScratchRegisterScope scratch(asMasm()); 2788 Register tag = extractTag(src, scratch); 2789 ma_cmp(tag, ImmTag(JSVAL_TAG_BIGINT)); 2790 return cond; 2791 } 2792 2793 Assembler::Condition MacroAssemblerARMCompat::testInt32(Condition cond, 2794 const BaseIndex& src) { 2795 MOZ_ASSERT(cond == Equal || cond == NotEqual); 2796 ScratchRegisterScope scratch(asMasm()); 2797 Register tag = extractTag(src, scratch); 2798 ma_cmp(tag, ImmTag(JSVAL_TAG_INT32)); 2799 return cond; 2800 } 2801 2802 Assembler::Condition MacroAssemblerARMCompat::testObject(Condition cond, 2803 const BaseIndex& src) { 2804 MOZ_ASSERT(cond == Equal || cond == NotEqual); 2805 ScratchRegisterScope scratch(asMasm()); 2806 Register tag = extractTag(src, scratch); 2807 ma_cmp(tag, ImmTag(JSVAL_TAG_OBJECT)); 2808 return cond; 2809 } 2810 2811 Assembler::Condition MacroAssemblerARMCompat::testDouble(Condition cond, 2812 const BaseIndex& src) { 2813 MOZ_ASSERT(cond == Equal || cond == NotEqual); 2814 Assembler::Condition actual = (cond == Equal) ? Below : AboveOrEqual; 2815 ScratchRegisterScope scratch(asMasm()); 2816 Register tag = extractTag(src, scratch); 2817 ma_cmp(tag, ImmTag(JSVAL_TAG_CLEAR)); 2818 return actual; 2819 } 2820 2821 Assembler::Condition MacroAssemblerARMCompat::testMagic( 2822 Condition cond, const BaseIndex& address) { 2823 MOZ_ASSERT(cond == Equal || cond == NotEqual); 2824 ScratchRegisterScope scratch(asMasm()); 2825 Register tag = extractTag(address, scratch); 2826 ma_cmp(tag, ImmTag(JSVAL_TAG_MAGIC)); 2827 return cond; 2828 } 2829 2830 Assembler::Condition MacroAssemblerARMCompat::testGCThing( 2831 Condition cond, const BaseIndex& address) { 2832 MOZ_ASSERT(cond == Equal || cond == NotEqual); 2833 ScratchRegisterScope scratch(asMasm()); 2834 Register tag = extractTag(address, scratch); 2835 ma_cmp(tag, ImmTag(JS::detail::ValueLowerInclGCThingTag)); 2836 return cond == Equal ? AboveOrEqual : Below; 2837 } 2838 2839 // Unboxing code. 2840 void MacroAssemblerARMCompat::unboxNonDouble(const ValueOperand& operand, 2841 Register dest, JSValueType type) { 2842 auto movPayloadToDest = [&]() { 2843 if (operand.payloadReg() != dest) { 2844 ma_mov(operand.payloadReg(), dest, LeaveCC); 2845 } 2846 }; 2847 if (!JitOptions.spectreValueMasking) { 2848 movPayloadToDest(); 2849 return; 2850 } 2851 2852 // Spectre mitigation: We zero the payload if the tag does not match the 2853 // expected type and if this is a pointer type. 2854 if (type == JSVAL_TYPE_INT32 || type == JSVAL_TYPE_BOOLEAN) { 2855 movPayloadToDest(); 2856 return; 2857 } 2858 2859 // We zero the destination register and move the payload into it if 2860 // the tag corresponds to the given type. 2861 ma_cmp(operand.typeReg(), ImmType(type)); 2862 movPayloadToDest(); 2863 ma_mov(Imm32(0), dest, NotEqual); 2864 } 2865 2866 void MacroAssemblerARMCompat::unboxNonDouble(const Address& src, Register dest, 2867 JSValueType type) { 2868 ScratchRegisterScope scratch(asMasm()); 2869 if (!JitOptions.spectreValueMasking) { 2870 ma_ldr(ToPayload(src), dest, scratch); 2871 return; 2872 } 2873 2874 // Spectre mitigation: We zero the payload if the tag does not match the 2875 // expected type and if this is a pointer type. 2876 if (type == JSVAL_TYPE_INT32 || type == JSVAL_TYPE_BOOLEAN) { 2877 ma_ldr(ToPayload(src), dest, scratch); 2878 return; 2879 } 2880 2881 // We zero the destination register and move the payload into it if 2882 // the tag corresponds to the given type. 2883 ma_ldr(ToType(src), scratch, scratch); 2884 ma_cmp(scratch, ImmType(type)); 2885 ma_ldr(ToPayload(src), dest, scratch, Offset, Equal); 2886 ma_mov(Imm32(0), dest, NotEqual); 2887 } 2888 2889 void MacroAssemblerARMCompat::unboxNonDouble(const BaseIndex& src, 2890 Register dest, JSValueType type) { 2891 SecondScratchRegisterScope scratch2(asMasm()); 2892 ma_alu(src.base, lsl(src.index, src.scale), scratch2, OpAdd); 2893 Address value(scratch2, src.offset); 2894 unboxNonDouble(value, dest, type); 2895 } 2896 2897 void MacroAssemblerARMCompat::unboxDouble(const ValueOperand& operand, 2898 FloatRegister dest) { 2899 MOZ_ASSERT(dest.isDouble()); 2900 as_vxfer(operand.payloadReg(), operand.typeReg(), VFPRegister(dest), 2901 CoreToFloat); 2902 } 2903 2904 void MacroAssemblerARMCompat::unboxDouble(const Address& src, 2905 FloatRegister dest) { 2906 MOZ_ASSERT(dest.isDouble()); 2907 loadDouble(src, dest); 2908 } 2909 2910 void MacroAssemblerARMCompat::unboxDouble(const BaseIndex& src, 2911 FloatRegister dest) { 2912 MOZ_ASSERT(dest.isDouble()); 2913 loadDouble(src, dest); 2914 } 2915 2916 void MacroAssemblerARMCompat::unboxValue(const ValueOperand& src, 2917 AnyRegister dest, JSValueType type) { 2918 if (dest.isFloat()) { 2919 Label notInt32, end; 2920 asMasm().branchTestInt32(Assembler::NotEqual, src, ¬Int32); 2921 convertInt32ToDouble(src.payloadReg(), dest.fpu()); 2922 ma_b(&end); 2923 bind(¬Int32); 2924 unboxDouble(src, dest.fpu()); 2925 bind(&end); 2926 } else { 2927 unboxNonDouble(src, dest.gpr(), type); 2928 } 2929 } 2930 2931 void MacroAssemblerARMCompat::boxDouble(FloatRegister src, 2932 const ValueOperand& dest, 2933 FloatRegister) { 2934 as_vxfer(dest.payloadReg(), dest.typeReg(), VFPRegister(src), FloatToCore); 2935 } 2936 2937 void MacroAssemblerARMCompat::boxNonDouble(JSValueType type, Register src, 2938 const ValueOperand& dest) { 2939 MOZ_ASSERT(type != JSVAL_TYPE_UNDEFINED && type != JSVAL_TYPE_NULL); 2940 MOZ_ASSERT(dest.typeReg() != dest.payloadReg()); 2941 2942 #ifdef DEBUG 2943 if (type == JSVAL_TYPE_BOOLEAN) { 2944 Label upperBitsZeroed; 2945 as_cmp(src, Imm8(1)); 2946 ma_b(&upperBitsZeroed, Assembler::BelowOrEqual); 2947 breakpoint(); 2948 bind(&upperBitsZeroed); 2949 } 2950 #endif 2951 2952 if (src != dest.payloadReg()) { 2953 ma_mov(src, dest.payloadReg()); 2954 } 2955 ma_mov(ImmType(type), dest.typeReg()); 2956 } 2957 2958 void MacroAssemblerARMCompat::boxNonDouble(Register type, Register src, 2959 const ValueOperand& dest) { 2960 MOZ_ASSERT(type != dest.payloadReg() && src != dest.typeReg()); 2961 2962 #ifdef DEBUG 2963 Label ok, isNullOrUndefined, isBoolean; 2964 2965 asMasm().branch32(Assembler::Equal, type, Imm32(JSVAL_TYPE_NULL), 2966 &isNullOrUndefined); 2967 asMasm().branch32(Assembler::Equal, type, Imm32(JSVAL_TYPE_UNDEFINED), 2968 &isNullOrUndefined); 2969 asMasm().branch32(Assembler::Equal, type, Imm32(JSVAL_TYPE_BOOLEAN), 2970 &isBoolean); 2971 asMasm().branch32(Assembler::Equal, type, Imm32(JSVAL_TYPE_INT32), &ok); 2972 asMasm().branch32(Assembler::Equal, type, Imm32(JSVAL_TYPE_MAGIC), &ok); 2973 asMasm().branch32(Assembler::Equal, type, Imm32(JSVAL_TYPE_STRING), &ok); 2974 asMasm().branch32(Assembler::Equal, type, Imm32(JSVAL_TYPE_SYMBOL), &ok); 2975 asMasm().branch32(Assembler::Equal, type, Imm32(JSVAL_TYPE_PRIVATE_GCTHING), 2976 &ok); 2977 asMasm().branch32(Assembler::Equal, type, Imm32(JSVAL_TYPE_BIGINT), &ok); 2978 asMasm().branch32(Assembler::Equal, type, Imm32(JSVAL_TYPE_OBJECT), &ok); 2979 breakpoint(); 2980 { 2981 bind(&isNullOrUndefined); 2982 as_cmp(src, Imm8(0)); 2983 ma_b(&ok, Assembler::Zero); 2984 breakpoint(); 2985 } 2986 { 2987 bind(&isBoolean); 2988 as_cmp(src, Imm8(1)); 2989 ma_b(&ok, Assembler::BelowOrEqual); 2990 breakpoint(); 2991 } 2992 bind(&ok); 2993 #endif 2994 2995 if (src != dest.payloadReg()) { 2996 ma_mov(src, dest.payloadReg()); 2997 } 2998 ScratchRegisterScope scratch(asMasm()); 2999 ma_orr(Imm32(JSVAL_TAG_CLEAR), type, dest.typeReg(), scratch); 3000 } 3001 3002 void MacroAssemblerARMCompat::loadConstantFloat32(float f, FloatRegister dest) { 3003 ma_vimm_f32(f, dest); 3004 } 3005 3006 void MacroAssemblerARMCompat::loadInt32OrDouble(const Address& src, 3007 FloatRegister dest) { 3008 Label notInt32, end; 3009 3010 // If it's an int, convert to a double. 3011 { 3012 ScratchRegisterScope scratch(asMasm()); 3013 SecondScratchRegisterScope scratch2(asMasm()); 3014 3015 ma_ldr(ToType(src), scratch, scratch2); 3016 asMasm().branchTestInt32(Assembler::NotEqual, scratch, ¬Int32); 3017 ma_ldr(ToPayload(src), scratch, scratch2); 3018 convertInt32ToDouble(scratch, dest); 3019 ma_b(&end); 3020 } 3021 3022 // Not an int, just load as double. 3023 bind(¬Int32); 3024 { 3025 ScratchRegisterScope scratch(asMasm()); 3026 ma_vldr(src, dest, scratch); 3027 } 3028 bind(&end); 3029 } 3030 3031 void MacroAssemblerARMCompat::loadInt32OrDouble(Register base, Register index, 3032 FloatRegister dest, 3033 int32_t shift) { 3034 Label notInt32, end; 3035 3036 static_assert(NUNBOX32_PAYLOAD_OFFSET == 0); 3037 3038 ScratchRegisterScope scratch(asMasm()); 3039 3040 // If it's an int, convert it to double. 3041 ma_alu(base, lsl(index, shift), scratch, OpAdd); 3042 3043 // Since we only have one scratch register, we need to stomp over it with 3044 // the tag. 3045 ma_ldr(DTRAddr(scratch, DtrOffImm(NUNBOX32_TYPE_OFFSET)), scratch); 3046 asMasm().branchTestInt32(Assembler::NotEqual, scratch, ¬Int32); 3047 3048 // Implicitly requires NUNBOX32_PAYLOAD_OFFSET == 0: no offset provided 3049 ma_ldr(DTRAddr(base, DtrRegImmShift(index, LSL, shift)), scratch); 3050 convertInt32ToDouble(scratch, dest); 3051 ma_b(&end); 3052 3053 // Not an int, just load as double. 3054 bind(¬Int32); 3055 // First, recompute the offset that had been stored in the scratch register 3056 // since the scratch register was overwritten loading in the type. 3057 ma_alu(base, lsl(index, shift), scratch, OpAdd); 3058 ma_vldr(VFPAddr(scratch, VFPOffImm(0)), dest); 3059 bind(&end); 3060 } 3061 3062 void MacroAssemblerARMCompat::loadConstantDouble(double dp, 3063 FloatRegister dest) { 3064 ma_vimm(dp, dest); 3065 } 3066 3067 // Treat the value as a boolean, and set condition codes accordingly. 3068 Assembler::Condition MacroAssemblerARMCompat::testInt32Truthy( 3069 bool truthy, const ValueOperand& operand) { 3070 ma_tst(operand.payloadReg(), operand.payloadReg()); 3071 return truthy ? NonZero : Zero; 3072 } 3073 3074 Assembler::Condition MacroAssemblerARMCompat::testBooleanTruthy( 3075 bool truthy, const ValueOperand& operand) { 3076 ma_tst(operand.payloadReg(), operand.payloadReg()); 3077 return truthy ? NonZero : Zero; 3078 } 3079 3080 Assembler::Condition MacroAssemblerARMCompat::testDoubleTruthy( 3081 bool truthy, FloatRegister reg) { 3082 as_vcmpz(VFPRegister(reg)); 3083 as_vmrs(pc); 3084 as_cmp(r0, O2Reg(r0), Overflow); 3085 return truthy ? NonZero : Zero; 3086 } 3087 3088 Register MacroAssemblerARMCompat::extractObject(const Address& address, 3089 Register scratch) { 3090 SecondScratchRegisterScope scratch2(asMasm()); 3091 ma_ldr(ToPayload(address), scratch, scratch2); 3092 return scratch; 3093 } 3094 3095 Register MacroAssemblerARMCompat::extractTag(const Address& address, 3096 Register scratch) { 3097 SecondScratchRegisterScope scratch2(asMasm()); 3098 ma_ldr(ToType(address), scratch, scratch2); 3099 return scratch; 3100 } 3101 3102 Register MacroAssemblerARMCompat::extractTag(const BaseIndex& address, 3103 Register scratch) { 3104 ma_alu(address.base, lsl(address.index, address.scale), scratch, OpAdd, 3105 LeaveCC); 3106 return extractTag(Address(scratch, address.offset), scratch); 3107 } 3108 3109 ///////////////////////////////////////////////////////////////// 3110 // X86/X64-common (ARM too now) interface. 3111 ///////////////////////////////////////////////////////////////// 3112 void MacroAssemblerARMCompat::storeValue(ValueOperand val, const Address& dst) { 3113 SecondScratchRegisterScope scratch2(asMasm()); 3114 ma_str(val.payloadReg(), ToPayload(dst), scratch2); 3115 ma_str(val.typeReg(), ToType(dst), scratch2); 3116 } 3117 3118 void MacroAssemblerARMCompat::storeValue(ValueOperand val, 3119 const BaseIndex& dest) { 3120 ScratchRegisterScope scratch(asMasm()); 3121 3122 if (isValueDTRDCandidate(val) && Abs(dest.offset) <= 255) { 3123 Register tmpIdx; 3124 if (dest.offset == 0) { 3125 if (dest.scale == TimesOne) { 3126 tmpIdx = dest.index; 3127 } else { 3128 ma_lsl(Imm32(dest.scale), dest.index, scratch); 3129 tmpIdx = scratch; 3130 } 3131 ma_strd(val.payloadReg(), val.typeReg(), 3132 EDtrAddr(dest.base, EDtrOffReg(tmpIdx))); 3133 } else { 3134 ma_alu(dest.base, lsl(dest.index, dest.scale), scratch, OpAdd); 3135 ma_strd(val.payloadReg(), val.typeReg(), 3136 EDtrAddr(scratch, EDtrOffImm(dest.offset))); 3137 } 3138 } else { 3139 ma_alu(dest.base, lsl(dest.index, dest.scale), scratch, OpAdd); 3140 storeValue(val, Address(scratch, dest.offset)); 3141 } 3142 } 3143 3144 void MacroAssemblerARMCompat::loadValue(const BaseIndex& addr, 3145 ValueOperand val) { 3146 ScratchRegisterScope scratch(asMasm()); 3147 3148 if (isValueDTRDCandidate(val) && Abs(addr.offset) <= 255) { 3149 Register tmpIdx; 3150 if (addr.offset == 0) { 3151 if (addr.scale == TimesOne) { 3152 // If the offset register is the same as one of the destination 3153 // registers, LDRD's behavior is undefined. Use the scratch 3154 // register to avoid this. 3155 if (val.aliases(addr.index)) { 3156 ma_mov(addr.index, scratch); 3157 tmpIdx = scratch; 3158 } else { 3159 tmpIdx = addr.index; 3160 } 3161 } else { 3162 ma_lsl(Imm32(addr.scale), addr.index, scratch); 3163 tmpIdx = scratch; 3164 } 3165 ma_ldrd(EDtrAddr(addr.base, EDtrOffReg(tmpIdx)), val.payloadReg(), 3166 val.typeReg()); 3167 } else { 3168 ma_alu(addr.base, lsl(addr.index, addr.scale), scratch, OpAdd); 3169 ma_ldrd(EDtrAddr(scratch, EDtrOffImm(addr.offset)), val.payloadReg(), 3170 val.typeReg()); 3171 } 3172 } else { 3173 ma_alu(addr.base, lsl(addr.index, addr.scale), scratch, OpAdd); 3174 loadValue(Address(scratch, addr.offset), val); 3175 } 3176 } 3177 3178 void MacroAssemblerARMCompat::loadValue(Address src, ValueOperand val) { 3179 // TODO: copy this code into a generic function that acts on all sequences 3180 // of memory accesses 3181 if (isValueDTRDCandidate(val)) { 3182 // If the value we want is in two consecutive registers starting with an 3183 // even register, they can be combined as a single ldrd. 3184 int offset = src.offset; 3185 if (offset < 256 && offset > -256) { 3186 ma_ldrd(EDtrAddr(src.base, EDtrOffImm(src.offset)), val.payloadReg(), 3187 val.typeReg()); 3188 return; 3189 } 3190 } 3191 // If the value is lower than the type, then we may be able to use an ldm 3192 // instruction. 3193 3194 if (val.payloadReg().code() < val.typeReg().code()) { 3195 if (src.offset <= 4 && src.offset >= -8 && (src.offset & 3) == 0) { 3196 // Turns out each of the 4 value -8, -4, 0, 4 corresponds exactly 3197 // with one of LDM{DB, DA, IA, IB} 3198 DTMMode mode; 3199 switch (src.offset) { 3200 case -8: 3201 mode = DB; 3202 break; 3203 case -4: 3204 mode = DA; 3205 break; 3206 case 0: 3207 mode = IA; 3208 break; 3209 case 4: 3210 mode = IB; 3211 break; 3212 default: 3213 MOZ_CRASH("Bogus Offset for LoadValue as DTM"); 3214 } 3215 startDataTransferM(IsLoad, src.base, mode); 3216 transferReg(val.payloadReg()); 3217 transferReg(val.typeReg()); 3218 finishDataTransfer(); 3219 return; 3220 } 3221 } 3222 3223 loadUnalignedValue(src, val); 3224 } 3225 3226 void MacroAssemblerARMCompat::loadUnalignedValue(const Address& src, 3227 ValueOperand dest) { 3228 Address payload = ToPayload(src); 3229 Address type = ToType(src); 3230 3231 // Ensure that loading the payload does not erase the pointer to the Value 3232 // in memory. 3233 if (type.base != dest.payloadReg()) { 3234 SecondScratchRegisterScope scratch2(asMasm()); 3235 ma_ldr(payload, dest.payloadReg(), scratch2); 3236 ma_ldr(type, dest.typeReg(), scratch2); 3237 } else { 3238 SecondScratchRegisterScope scratch2(asMasm()); 3239 ma_ldr(type, dest.typeReg(), scratch2); 3240 ma_ldr(payload, dest.payloadReg(), scratch2); 3241 } 3242 } 3243 3244 void MacroAssemblerARMCompat::pushValue(ValueOperand val) { 3245 ma_push(val.typeReg()); 3246 ma_push(val.payloadReg()); 3247 } 3248 3249 void MacroAssemblerARMCompat::pushValue(const Address& addr) { 3250 ScratchRegisterScope scratch(asMasm()); 3251 SecondScratchRegisterScope scratch2(asMasm()); 3252 3253 ma_ldr(ToType(addr), scratch, scratch2); 3254 ma_push(scratch); 3255 ma_ldr(ToPayloadAfterStackPush(addr), scratch, scratch2); 3256 ma_push(scratch); 3257 } 3258 3259 void MacroAssemblerARMCompat::pushValue(const BaseIndex& addr, 3260 Register scratch) { 3261 computeEffectiveAddress(addr, scratch); 3262 pushValue(Address(scratch, 0)); 3263 } 3264 3265 void MacroAssemblerARMCompat::popValue(ValueOperand val) { 3266 ma_pop(val.payloadReg()); 3267 ma_pop(val.typeReg()); 3268 } 3269 3270 void MacroAssemblerARMCompat::storePayload(const Value& val, 3271 const Address& dest) { 3272 ScratchRegisterScope scratch(asMasm()); 3273 SecondScratchRegisterScope scratch2(asMasm()); 3274 3275 if (val.isGCThing()) { 3276 ma_mov(ImmGCPtr(val.toGCThing()), scratch); 3277 } else { 3278 ma_mov(Imm32(val.toNunboxPayload()), scratch); 3279 } 3280 ma_str(scratch, ToPayload(dest), scratch2); 3281 } 3282 3283 void MacroAssemblerARMCompat::storePayload(Register src, const Address& dest) { 3284 ScratchRegisterScope scratch(asMasm()); 3285 ma_str(src, ToPayload(dest), scratch); 3286 } 3287 3288 void MacroAssemblerARMCompat::storePayload(const Value& val, 3289 const BaseIndex& dest) { 3290 unsigned shift = ScaleToShift(dest.scale); 3291 3292 ScratchRegisterScope scratch(asMasm()); 3293 SecondScratchRegisterScope scratch2(asMasm()); 3294 3295 if (val.isGCThing()) { 3296 ma_mov(ImmGCPtr(val.toGCThing()), scratch); 3297 } else { 3298 ma_mov(Imm32(val.toNunboxPayload()), scratch); 3299 } 3300 3301 // If NUNBOX32_PAYLOAD_OFFSET is not zero, the memory operand [base + index 3302 // << shift + imm] cannot be encoded into a single instruction, and cannot 3303 // be integrated into the as_dtr call. 3304 static_assert(NUNBOX32_PAYLOAD_OFFSET == 0); 3305 3306 // If an offset is used, modify the base so that a [base + index << shift] 3307 // instruction format can be used. 3308 if (dest.offset != 0) { 3309 ma_add(dest.base, Imm32(dest.offset), dest.base, scratch2); 3310 } 3311 3312 as_dtr(IsStore, 32, Offset, scratch, 3313 DTRAddr(dest.base, DtrRegImmShift(dest.index, LSL, shift))); 3314 3315 // Restore the original value of the base, if necessary. 3316 if (dest.offset != 0) { 3317 ma_sub(dest.base, Imm32(dest.offset), dest.base, scratch); 3318 } 3319 } 3320 3321 void MacroAssemblerARMCompat::storePayload(Register src, 3322 const BaseIndex& dest) { 3323 unsigned shift = ScaleToShift(dest.scale); 3324 MOZ_ASSERT(shift < 32); 3325 3326 ScratchRegisterScope scratch(asMasm()); 3327 3328 // If NUNBOX32_PAYLOAD_OFFSET is not zero, the memory operand [base + index 3329 // << shift + imm] cannot be encoded into a single instruction, and cannot 3330 // be integrated into the as_dtr call. 3331 static_assert(NUNBOX32_PAYLOAD_OFFSET == 0); 3332 3333 // Save/restore the base if the BaseIndex has an offset, as above. 3334 if (dest.offset != 0) { 3335 ma_add(dest.base, Imm32(dest.offset), dest.base, scratch); 3336 } 3337 3338 // Technically, shift > -32 can be handle by changing LSL to ASR, but should 3339 // never come up, and this is one less code path to get wrong. 3340 as_dtr(IsStore, 32, Offset, src, 3341 DTRAddr(dest.base, DtrRegImmShift(dest.index, LSL, shift))); 3342 3343 if (dest.offset != 0) { 3344 ma_sub(dest.base, Imm32(dest.offset), dest.base, scratch); 3345 } 3346 } 3347 3348 void MacroAssemblerARMCompat::storeTypeTag(ImmTag tag, const Address& dest) { 3349 ScratchRegisterScope scratch(asMasm()); 3350 SecondScratchRegisterScope scratch2(asMasm()); 3351 3352 ma_mov(tag, scratch); 3353 ma_str(scratch, ToType(dest), scratch2); 3354 } 3355 3356 void MacroAssemblerARMCompat::storeTypeTag(ImmTag tag, const BaseIndex& dest) { 3357 Register base = dest.base; 3358 Register index = dest.index; 3359 unsigned shift = ScaleToShift(dest.scale); 3360 3361 ScratchRegisterScope scratch(asMasm()); 3362 SecondScratchRegisterScope scratch2(asMasm()); 3363 3364 MOZ_ASSERT(base != scratch && base != scratch2); 3365 MOZ_ASSERT(index != scratch && index != scratch2); 3366 3367 ma_add(base, Imm32(dest.offset + NUNBOX32_TYPE_OFFSET), scratch2, scratch); 3368 ma_mov(tag, scratch); 3369 ma_str(scratch, DTRAddr(scratch2, DtrRegImmShift(index, LSL, shift))); 3370 } 3371 3372 void MacroAssemblerARM::ma_call(ImmPtr dest) { 3373 ma_movPatchable(dest, CallReg, Always); 3374 as_blx(CallReg); 3375 } 3376 3377 void MacroAssemblerARMCompat::breakpoint() { as_bkpt(); } 3378 3379 void MacroAssemblerARMCompat::simulatorStop(const char* msg) { 3380 #ifdef JS_SIMULATOR_ARM 3381 MOZ_ASSERT(sizeof(char*) == 4); 3382 writeInst(0xefffffff); 3383 writeInst((int)msg); 3384 #endif 3385 } 3386 3387 void MacroAssemblerARMCompat::breakpoint(Condition cc) { 3388 ma_ldr(DTRAddr(r12, DtrRegImmShift(r12, LSL, 0, IsDown)), r12, Offset, cc); 3389 } 3390 3391 void MacroAssemblerARMCompat::checkStackAlignment() { 3392 asMasm().assertStackAlignment(ABIStackAlignment); 3393 } 3394 3395 void MacroAssemblerARMCompat::handleFailureWithHandlerTail( 3396 Label* profilerExitTail, Label* bailoutTail, 3397 uint32_t* returnValueCheckOffset) { 3398 // Reserve space for exception information. 3399 int size = (sizeof(ResumeFromException) + 7) & ~7; 3400 3401 Imm8 size8(size); 3402 as_sub(sp, sp, size8); 3403 ma_mov(sp, r0); 3404 3405 // Call the handler. 3406 using Fn = void (*)(ResumeFromException* rfe); 3407 asMasm().setupUnalignedABICall(r1); 3408 asMasm().passABIArg(r0); 3409 asMasm().callWithABI<Fn, HandleException>( 3410 ABIType::General, CheckUnsafeCallWithABI::DontCheckHasExitFrame); 3411 3412 *returnValueCheckOffset = asMasm().currentOffset(); 3413 3414 Label entryFrame; 3415 Label catch_; 3416 Label finally; 3417 Label returnBaseline; 3418 Label returnIon; 3419 Label bailout; 3420 Label wasmInterpEntry; 3421 Label wasmCatch; 3422 3423 { 3424 ScratchRegisterScope scratch(asMasm()); 3425 ma_ldr(Address(sp, ResumeFromException::offsetOfKind()), r0, scratch); 3426 } 3427 3428 asMasm().branch32(Assembler::Equal, r0, 3429 Imm32(ExceptionResumeKind::EntryFrame), &entryFrame); 3430 asMasm().branch32(Assembler::Equal, r0, Imm32(ExceptionResumeKind::Catch), 3431 &catch_); 3432 asMasm().branch32(Assembler::Equal, r0, Imm32(ExceptionResumeKind::Finally), 3433 &finally); 3434 asMasm().branch32(Assembler::Equal, r0, 3435 Imm32(ExceptionResumeKind::ForcedReturnBaseline), 3436 &returnBaseline); 3437 asMasm().branch32(Assembler::Equal, r0, 3438 Imm32(ExceptionResumeKind::ForcedReturnIon), &returnIon); 3439 asMasm().branch32(Assembler::Equal, r0, Imm32(ExceptionResumeKind::Bailout), 3440 &bailout); 3441 asMasm().branch32(Assembler::Equal, r0, 3442 Imm32(ExceptionResumeKind::WasmInterpEntry), 3443 &wasmInterpEntry); 3444 asMasm().branch32(Assembler::Equal, r0, Imm32(ExceptionResumeKind::WasmCatch), 3445 &wasmCatch); 3446 3447 breakpoint(); // Invalid kind. 3448 3449 // No exception handler. Load the error value, restore state and return from 3450 // the entry frame. 3451 bind(&entryFrame); 3452 asMasm().moveValue(MagicValue(JS_ION_ERROR), JSReturnOperand); 3453 { 3454 ScratchRegisterScope scratch(asMasm()); 3455 ma_ldr(Address(sp, ResumeFromException::offsetOfFramePointer()), r11, 3456 scratch); 3457 ma_ldr(Address(sp, ResumeFromException::offsetOfStackPointer()), sp, 3458 scratch); 3459 } 3460 3461 // We're going to be returning by the ion calling convention, which returns 3462 // by ??? (for now, I think ldr pc, [sp]!) 3463 as_dtr(IsLoad, 32, PostIndex, pc, DTRAddr(sp, DtrOffImm(4))); 3464 3465 // If we found a catch handler, this must be a baseline frame. Restore state 3466 // and jump to the catch block. 3467 bind(&catch_); 3468 { 3469 ScratchRegisterScope scratch(asMasm()); 3470 ma_ldr(Address(sp, ResumeFromException::offsetOfTarget()), r0, scratch); 3471 ma_ldr(Address(sp, ResumeFromException::offsetOfFramePointer()), r11, 3472 scratch); 3473 ma_ldr(Address(sp, ResumeFromException::offsetOfStackPointer()), sp, 3474 scratch); 3475 } 3476 jump(r0); 3477 3478 // If we found a finally block, this must be a baseline frame. Push three 3479 // values expected by the finally block: the exception, the exception stack, 3480 // and BooleanValue(true). 3481 bind(&finally); 3482 ValueOperand exception = ValueOperand(r1, r2); 3483 loadValue(Operand(sp, ResumeFromException::offsetOfException()), exception); 3484 3485 ValueOperand exceptionStack = ValueOperand(r3, r4); 3486 loadValue(Operand(sp, ResumeFromException::offsetOfExceptionStack()), 3487 exceptionStack); 3488 3489 { 3490 ScratchRegisterScope scratch(asMasm()); 3491 ma_ldr(Address(sp, ResumeFromException::offsetOfTarget()), r0, scratch); 3492 ma_ldr(Address(sp, ResumeFromException::offsetOfFramePointer()), r11, 3493 scratch); 3494 ma_ldr(Address(sp, ResumeFromException::offsetOfStackPointer()), sp, 3495 scratch); 3496 } 3497 3498 pushValue(exception); 3499 pushValue(exceptionStack); 3500 pushValue(BooleanValue(true)); 3501 jump(r0); 3502 3503 // Return BaselineFrame->returnValue() to the caller. 3504 // Used in debug mode and for GeneratorReturn. 3505 Label profilingInstrumentation; 3506 bind(&returnBaseline); 3507 { 3508 ScratchRegisterScope scratch(asMasm()); 3509 ma_ldr(Address(sp, ResumeFromException::offsetOfFramePointer()), r11, 3510 scratch); 3511 ma_ldr(Address(sp, ResumeFromException::offsetOfStackPointer()), sp, 3512 scratch); 3513 } 3514 loadValue(Address(r11, BaselineFrame::reverseOffsetOfReturnValue()), 3515 JSReturnOperand); 3516 jump(&profilingInstrumentation); 3517 3518 // Return the given value to the caller. 3519 bind(&returnIon); 3520 loadValue(Address(sp, ResumeFromException::offsetOfException()), 3521 JSReturnOperand); 3522 { 3523 ScratchRegisterScope scratch(asMasm()); 3524 ma_ldr(Address(sp, ResumeFromException::offsetOfFramePointer()), r11, 3525 scratch); 3526 ma_ldr(Address(sp, ResumeFromException::offsetOfStackPointer()), sp, 3527 scratch); 3528 } 3529 3530 // If profiling is enabled, then update the lastProfilingFrame to refer to 3531 // caller frame before returning. This code is shared by ForcedReturnIon 3532 // and ForcedReturnBaseline. 3533 bind(&profilingInstrumentation); 3534 { 3535 Label skipProfilingInstrumentation; 3536 // Test if profiler enabled. 3537 AbsoluteAddress addressOfEnabled( 3538 asMasm().runtime()->geckoProfiler().addressOfEnabled()); 3539 asMasm().branch32(Assembler::Equal, addressOfEnabled, Imm32(0), 3540 &skipProfilingInstrumentation); 3541 jump(profilerExitTail); 3542 bind(&skipProfilingInstrumentation); 3543 } 3544 3545 ma_mov(r11, sp); 3546 pop(r11); 3547 ret(); 3548 3549 // If we are bailing out to baseline to handle an exception, jump to the 3550 // bailout tail stub. Load 1 (true) in ReturnReg to indicate success. 3551 bind(&bailout); 3552 { 3553 ScratchRegisterScope scratch(asMasm()); 3554 ma_ldr(Address(sp, ResumeFromException::offsetOfBailoutInfo()), r2, 3555 scratch); 3556 ma_ldr(Address(sp, ResumeFromException::offsetOfStackPointer()), sp, 3557 scratch); 3558 ma_mov(Imm32(1), ReturnReg); 3559 } 3560 jump(bailoutTail); 3561 3562 // Reset SP and FP; SP is pointing to the unwound return address to the wasm 3563 // interpreter entry, so we can just ret(). 3564 bind(&wasmInterpEntry); 3565 { 3566 ScratchRegisterScope scratch(asMasm()); 3567 ma_ldr(Address(sp, ResumeFromException::offsetOfFramePointer()), r11, 3568 scratch); 3569 ma_ldr(Address(sp, ResumeFromException::offsetOfStackPointer()), sp, 3570 scratch); 3571 ma_mov(Imm32(int32_t(wasm::InterpFailInstanceReg)), InstanceReg); 3572 } 3573 as_dtr(IsLoad, 32, PostIndex, pc, DTRAddr(sp, DtrOffImm(4))); 3574 3575 // Found a wasm catch handler, restore state and jump to it. 3576 bind(&wasmCatch); 3577 wasm::GenerateJumpToCatchHandler(asMasm(), sp, r0, r1); 3578 } 3579 3580 Assembler::Condition MacroAssemblerARMCompat::testStringTruthy( 3581 bool truthy, const ValueOperand& value) { 3582 Register string = value.payloadReg(); 3583 ScratchRegisterScope scratch(asMasm()); 3584 SecondScratchRegisterScope scratch2(asMasm()); 3585 3586 ma_dtr(IsLoad, string, Imm32(JSString::offsetOfLength()), scratch, scratch2); 3587 as_cmp(scratch, Imm8(0)); 3588 return truthy ? Assembler::NotEqual : Assembler::Equal; 3589 } 3590 3591 Assembler::Condition MacroAssemblerARMCompat::testBigIntTruthy( 3592 bool truthy, const ValueOperand& value) { 3593 Register bi = value.payloadReg(); 3594 ScratchRegisterScope scratch(asMasm()); 3595 SecondScratchRegisterScope scratch2(asMasm()); 3596 3597 ma_dtr(IsLoad, bi, Imm32(BigInt::offsetOfDigitLength()), scratch, scratch2); 3598 as_cmp(scratch, Imm8(0)); 3599 return truthy ? Assembler::NotEqual : Assembler::Equal; 3600 } 3601 3602 void MacroAssemblerARMCompat::floor(FloatRegister input, Register output, 3603 Label* bail) { 3604 Label handleZero; 3605 Label handleNeg; 3606 Label fin; 3607 3608 ScratchDoubleScope scratchDouble(asMasm()); 3609 3610 compareDouble(input, NoVFPRegister); 3611 ma_b(&handleZero, Assembler::Equal); 3612 ma_b(&handleNeg, Assembler::Signed); 3613 // NaN is always a bail condition, just bail directly. 3614 ma_b(bail, Assembler::Overflow); 3615 3616 // The argument is a positive number, truncation is the path to glory. Since 3617 // it is known to be > 0.0, explicitly convert to a larger range, then a 3618 // value that rounds to INT_MAX is explicitly different from an argument 3619 // that clamps to INT_MAX. 3620 ma_vcvt_F64_U32(input, scratchDouble.uintOverlay()); 3621 ma_vxfer(scratchDouble.uintOverlay(), output); 3622 ma_mov(output, output, SetCC); 3623 ma_b(bail, Signed); 3624 ma_b(&fin); 3625 3626 bind(&handleZero); 3627 // Move the top word of the double into the output reg, if it is non-zero, 3628 // then the original value was -0.0. 3629 as_vxfer(output, InvalidReg, input, FloatToCore, Always, 1); 3630 as_cmp(output, Imm8(0)); 3631 ma_b(bail, NonZero); 3632 ma_b(&fin); 3633 3634 bind(&handleNeg); 3635 // Negative case, negate, then start dancing. 3636 ma_vneg(input, input); 3637 ma_vcvt_F64_U32(input, scratchDouble.uintOverlay()); 3638 ma_vxfer(scratchDouble.uintOverlay(), output); 3639 ma_vcvt_U32_F64(scratchDouble.uintOverlay(), scratchDouble); 3640 compareDouble(scratchDouble, input); 3641 as_add(output, output, Imm8(1), LeaveCC, NotEqual); 3642 // Negate the output. Since INT_MIN < -INT_MAX, even after adding 1, the 3643 // result will still be a negative number. 3644 as_rsb(output, output, Imm8(0), SetCC); 3645 // Flip the negated input back to its original value. 3646 ma_vneg(input, input); 3647 // If the result looks non-negative, then this value didn't actually fit 3648 // into the int range, and special handling is required. Zero is also caught 3649 // by this case, but floor of a negative number should never be zero. 3650 ma_b(bail, NotSigned); 3651 3652 bind(&fin); 3653 } 3654 3655 void MacroAssemblerARMCompat::floorf(FloatRegister input, Register output, 3656 Label* bail) { 3657 Label handleZero; 3658 Label handleNeg; 3659 Label fin; 3660 compareFloat(input, NoVFPRegister); 3661 ma_b(&handleZero, Assembler::Equal); 3662 ma_b(&handleNeg, Assembler::Signed); 3663 // NaN is always a bail condition, just bail directly. 3664 ma_b(bail, Assembler::Overflow); 3665 3666 // The argument is a positive number, truncation is the path to glory; Since 3667 // it is known to be > 0.0, explicitly convert to a larger range, then a 3668 // value that rounds to INT_MAX is explicitly different from an argument 3669 // that clamps to INT_MAX. 3670 { 3671 ScratchFloat32Scope scratch(asMasm()); 3672 ma_vcvt_F32_U32(input, scratch.uintOverlay()); 3673 ma_vxfer(VFPRegister(scratch).uintOverlay(), output); 3674 } 3675 ma_mov(output, output, SetCC); 3676 ma_b(bail, Signed); 3677 ma_b(&fin); 3678 3679 bind(&handleZero); 3680 // Move the top word of the double into the output reg, if it is non-zero, 3681 // then the original value was -0.0. 3682 as_vxfer(output, InvalidReg, VFPRegister(input).singleOverlay(), FloatToCore, 3683 Always, 0); 3684 as_cmp(output, Imm8(0)); 3685 ma_b(bail, NonZero); 3686 ma_b(&fin); 3687 3688 bind(&handleNeg); 3689 // Negative case, negate, then start dancing. 3690 { 3691 ScratchFloat32Scope scratch(asMasm()); 3692 ma_vneg_f32(input, input); 3693 ma_vcvt_F32_U32(input, scratch.uintOverlay()); 3694 ma_vxfer(VFPRegister(scratch).uintOverlay(), output); 3695 ma_vcvt_U32_F32(scratch.uintOverlay(), scratch); 3696 compareFloat(scratch, input); 3697 as_add(output, output, Imm8(1), LeaveCC, NotEqual); 3698 } 3699 // Negate the output. Since INT_MIN < -INT_MAX, even after adding 1, the 3700 // result will still be a negative number. 3701 as_rsb(output, output, Imm8(0), SetCC); 3702 // Flip the negated input back to its original value. 3703 ma_vneg_f32(input, input); 3704 // If the result looks non-negative, then this value didn't actually fit 3705 // into the int range, and special handling is required. Zero is also caught 3706 // by this case, but floor of a negative number should never be zero. 3707 ma_b(bail, NotSigned); 3708 3709 bind(&fin); 3710 } 3711 3712 void MacroAssemblerARMCompat::ceil(FloatRegister input, Register output, 3713 Label* bail) { 3714 Label handleZero; 3715 Label handlePos; 3716 Label fin; 3717 3718 compareDouble(input, NoVFPRegister); 3719 // NaN is always a bail condition, just bail directly. 3720 ma_b(bail, Assembler::Overflow); 3721 ma_b(&handleZero, Assembler::Equal); 3722 ma_b(&handlePos, Assembler::NotSigned); 3723 3724 ScratchDoubleScope scratchDouble(asMasm()); 3725 3726 // We are in the ]-Inf; 0[ range 3727 // If we are in the ]-1; 0[ range => bailout 3728 loadConstantDouble(-1.0, scratchDouble); 3729 compareDouble(input, scratchDouble); 3730 ma_b(bail, Assembler::GreaterThan); 3731 3732 // We are in the ]-Inf; -1] range: ceil(x) == -floor(-x) and floor can be 3733 // computed with direct truncation here (x > 0). 3734 ma_vneg(input, scratchDouble); 3735 FloatRegister ScratchUIntReg = scratchDouble.uintOverlay(); 3736 ma_vcvt_F64_U32(scratchDouble, ScratchUIntReg); 3737 ma_vxfer(ScratchUIntReg, output); 3738 ma_neg(output, output, SetCC); 3739 ma_b(bail, NotSigned); 3740 ma_b(&fin); 3741 3742 // Test for 0.0 / -0.0: if the top word of the input double is not zero, 3743 // then it was -0 and we need to bail out. 3744 bind(&handleZero); 3745 as_vxfer(output, InvalidReg, input, FloatToCore, Always, 1); 3746 as_cmp(output, Imm8(0)); 3747 ma_b(bail, NonZero); 3748 ma_b(&fin); 3749 3750 // We are in the ]0; +inf] range: truncate integer values, maybe add 1 for 3751 // non integer values, maybe bail if overflow. 3752 bind(&handlePos); 3753 ma_vcvt_F64_U32(input, ScratchUIntReg); 3754 ma_vxfer(ScratchUIntReg, output); 3755 ma_vcvt_U32_F64(ScratchUIntReg, scratchDouble); 3756 compareDouble(scratchDouble, input); 3757 as_add(output, output, Imm8(1), LeaveCC, NotEqual); 3758 // Bail out if the add overflowed or the result is non positive. 3759 ma_mov(output, output, SetCC); 3760 ma_b(bail, Signed); 3761 ma_b(bail, Zero); 3762 3763 bind(&fin); 3764 } 3765 3766 void MacroAssemblerARMCompat::ceilf(FloatRegister input, Register output, 3767 Label* bail) { 3768 Label handleZero; 3769 Label handlePos; 3770 Label fin; 3771 3772 compareFloat(input, NoVFPRegister); 3773 // NaN is always a bail condition, just bail directly. 3774 ma_b(bail, Assembler::Overflow); 3775 ma_b(&handleZero, Assembler::Equal); 3776 ma_b(&handlePos, Assembler::NotSigned); 3777 3778 // We are in the ]-Inf; 0[ range 3779 // If we are in the ]-1; 0[ range => bailout 3780 { 3781 ScratchFloat32Scope scratch(asMasm()); 3782 loadConstantFloat32(-1.f, scratch); 3783 compareFloat(input, scratch); 3784 ma_b(bail, Assembler::GreaterThan); 3785 } 3786 3787 // We are in the ]-Inf; -1] range: ceil(x) == -floor(-x) and floor can be 3788 // computed with direct truncation here (x > 0). 3789 { 3790 ScratchDoubleScope scratchDouble(asMasm()); 3791 FloatRegister scratchFloat = scratchDouble.asSingle(); 3792 FloatRegister scratchUInt = scratchDouble.uintOverlay(); 3793 3794 ma_vneg_f32(input, scratchFloat); 3795 ma_vcvt_F32_U32(scratchFloat, scratchUInt); 3796 ma_vxfer(scratchUInt, output); 3797 ma_neg(output, output, SetCC); 3798 ma_b(bail, NotSigned); 3799 ma_b(&fin); 3800 } 3801 3802 // Test for 0.0 / -0.0: if the top word of the input double is not zero, 3803 // then it was -0 and we need to bail out. 3804 bind(&handleZero); 3805 as_vxfer(output, InvalidReg, VFPRegister(input).singleOverlay(), FloatToCore, 3806 Always, 0); 3807 as_cmp(output, Imm8(0)); 3808 ma_b(bail, NonZero); 3809 ma_b(&fin); 3810 3811 // We are in the ]0; +inf] range: truncate integer values, maybe add 1 for 3812 // non integer values, maybe bail if overflow. 3813 bind(&handlePos); 3814 { 3815 ScratchDoubleScope scratchDouble(asMasm()); 3816 FloatRegister scratchFloat = scratchDouble.asSingle(); 3817 FloatRegister scratchUInt = scratchDouble.uintOverlay(); 3818 3819 ma_vcvt_F32_U32(input, scratchUInt); 3820 ma_vxfer(scratchUInt, output); 3821 ma_vcvt_U32_F32(scratchUInt, scratchFloat); 3822 compareFloat(scratchFloat, input); 3823 as_add(output, output, Imm8(1), LeaveCC, NotEqual); 3824 3825 // Bail on overflow or non-positive result. 3826 ma_mov(output, output, SetCC); 3827 ma_b(bail, Signed); 3828 ma_b(bail, Zero); 3829 } 3830 3831 bind(&fin); 3832 } 3833 3834 void MacroAssemblerARMCompat::mov(CodeLabel* label, Register dest) { 3835 BufferOffset bo = 3836 ma_movPatchable(ImmPtr(/* placeholder */ nullptr), dest, Always); 3837 label->patchAt()->bind(bo.getOffset()); 3838 label->setLinkMode(CodeLabel::MoveImmediate); 3839 } 3840 3841 CodeOffset MacroAssemblerARMCompat::toggledJump(Label* label) { 3842 // Emit a B that can be toggled to a CMP. See ToggleToJmp(), ToggleToCmp(). 3843 BufferOffset b = ma_b(label, Always); 3844 CodeOffset ret(b.getOffset()); 3845 return ret; 3846 } 3847 3848 CodeOffset MacroAssemblerARMCompat::toggledCall(JitCode* target, bool enabled) { 3849 BufferOffset bo = nextOffset(); 3850 addPendingJump(bo, ImmPtr(target->raw()), RelocationKind::JITCODE); 3851 ScratchRegisterScope scratch(asMasm()); 3852 ma_movPatchable(ImmPtr(target->raw()), scratch, Always); 3853 if (enabled) { 3854 ma_blx(scratch); 3855 } else { 3856 ma_nop(); 3857 } 3858 return CodeOffset(bo.getOffset()); 3859 } 3860 3861 void MacroAssemblerARMCompat::round(FloatRegister input, Register output, 3862 Label* bail, FloatRegister tmp) { 3863 Label handleZero; 3864 Label handleNeg; 3865 Label fin; 3866 3867 ScratchDoubleScope scratchDouble(asMasm()); 3868 3869 // Do a compare based on the original value, then do most other things based 3870 // on the shifted value. 3871 ma_vcmpz(input); 3872 // Since we already know the sign bit, flip all numbers to be positive, 3873 // stored in tmp. 3874 ma_vabs(input, tmp); 3875 as_vmrs(pc); 3876 ma_b(&handleZero, Assembler::Equal); 3877 ma_b(&handleNeg, Assembler::Signed); 3878 // NaN is always a bail condition, just bail directly. 3879 ma_b(bail, Assembler::Overflow); 3880 3881 // The argument is a positive number, truncation is the path to glory; Since 3882 // it is known to be > 0.0, explicitly convert to a larger range, then a 3883 // value that rounds to INT_MAX is explicitly different from an argument 3884 // that clamps to INT_MAX. 3885 3886 // Add the biggest number less than 0.5 (not 0.5, because adding that to 3887 // the biggest number less than 0.5 would undesirably round up to 1), and 3888 // store the result into tmp. 3889 loadConstantDouble(GetBiggestNumberLessThan(0.5), scratchDouble); 3890 ma_vadd(scratchDouble, tmp, tmp); 3891 3892 ma_vcvt_F64_U32(tmp, scratchDouble.uintOverlay()); 3893 ma_vxfer(VFPRegister(scratchDouble).uintOverlay(), output); 3894 ma_mov(output, output, SetCC); 3895 ma_b(bail, Signed); 3896 ma_b(&fin); 3897 3898 bind(&handleZero); 3899 // Move the top word of the double into the output reg, if it is non-zero, 3900 // then the original value was -0.0 3901 as_vxfer(output, InvalidReg, input, FloatToCore, Always, 1); 3902 as_cmp(output, Imm8(0)); 3903 ma_b(bail, NonZero); 3904 ma_b(&fin); 3905 3906 bind(&handleNeg); 3907 // Negative case, negate, then start dancing. This number may be positive, 3908 // since we added 0.5. 3909 3910 // Add 0.5 to negative numbers, store the result into tmp 3911 loadConstantDouble(0.5, scratchDouble); 3912 ma_vadd(scratchDouble, tmp, tmp); 3913 3914 ma_vcvt_F64_U32(tmp, scratchDouble.uintOverlay()); 3915 ma_vxfer(VFPRegister(scratchDouble).uintOverlay(), output); 3916 3917 // -output is now a correctly rounded value, unless the original value was 3918 // exactly halfway between two integers, at which point, it has been rounded 3919 // away from zero, when it should be rounded towards \infty. 3920 ma_vcvt_U32_F64(scratchDouble.uintOverlay(), scratchDouble); 3921 compareDouble(scratchDouble, tmp); 3922 as_sub(output, output, Imm8(1), LeaveCC, Equal); 3923 // Negate the output. Since INT_MIN < -INT_MAX, even after adding 1, the 3924 // result will still be a negative number. 3925 as_rsb(output, output, Imm8(0), SetCC); 3926 3927 // If the result looks non-negative, then this value didn't actually fit 3928 // into the int range, and special handling is required, or it was zero, 3929 // which means the result is actually -0.0 which also requires special 3930 // handling. 3931 ma_b(bail, NotSigned); 3932 3933 bind(&fin); 3934 } 3935 3936 void MacroAssemblerARMCompat::roundf(FloatRegister input, Register output, 3937 Label* bail, FloatRegister tmp) { 3938 Label handleZero; 3939 Label handleNeg; 3940 Label fin; 3941 3942 ScratchFloat32Scope scratchFloat(asMasm()); 3943 3944 // Do a compare based on the original value, then do most other things based 3945 // on the shifted value. 3946 compareFloat(input, NoVFPRegister); 3947 ma_b(&handleZero, Assembler::Equal); 3948 ma_b(&handleNeg, Assembler::Signed); 3949 3950 // NaN is always a bail condition, just bail directly. 3951 ma_b(bail, Assembler::Overflow); 3952 3953 // The argument is a positive number, truncation is the path to glory; Since 3954 // it is known to be > 0.0, explicitly convert to a larger range, then a 3955 // value that rounds to INT_MAX is explicitly different from an argument 3956 // that clamps to INT_MAX. 3957 3958 // Add the biggest number less than 0.5f (not 0.5f, because adding that to 3959 // the biggest number less than 0.5f would undesirably round up to 1), and 3960 // store the result into tmp. 3961 loadConstantFloat32(GetBiggestNumberLessThan(0.5f), scratchFloat); 3962 ma_vadd_f32(scratchFloat, input, tmp); 3963 3964 // Note: it doesn't matter whether x + .5 === x or not here, as it doesn't 3965 // affect the semantics of the float to unsigned conversion (in particular, 3966 // we are not applying any fixup after the operation). 3967 ma_vcvt_F32_U32(tmp, scratchFloat.uintOverlay()); 3968 ma_vxfer(VFPRegister(scratchFloat).uintOverlay(), output); 3969 ma_mov(output, output, SetCC); 3970 ma_b(bail, Signed); 3971 ma_b(&fin); 3972 3973 bind(&handleZero); 3974 3975 // Move the whole float32 into the output reg, if it is non-zero, then the 3976 // original value was -0.0. 3977 as_vxfer(output, InvalidReg, input, FloatToCore, Always, 0); 3978 as_cmp(output, Imm8(0)); 3979 ma_b(bail, NonZero); 3980 ma_b(&fin); 3981 3982 bind(&handleNeg); 3983 3984 // Add 0.5 to negative numbers, storing the result into tmp. 3985 ma_vneg_f32(input, tmp); 3986 loadConstantFloat32(0.5f, scratchFloat); 3987 ma_vadd_f32(tmp, scratchFloat, scratchFloat); 3988 3989 // Adding 0.5 to a float input has chances to yield the wrong result, if 3990 // the input is too large. In this case, skip the -1 adjustment made below. 3991 compareFloat(scratchFloat, tmp); 3992 3993 // Negative case, negate, then start dancing. This number may be positive, 3994 // since we added 0.5. 3995 // /!\ The conditional jump afterwards depends on these two instructions 3996 // *not* setting the status flags. They need to not change after the 3997 // comparison above. 3998 ma_vcvt_F32_U32(scratchFloat, tmp.uintOverlay()); 3999 ma_vxfer(VFPRegister(tmp).uintOverlay(), output); 4000 4001 Label flipSign; 4002 ma_b(&flipSign, Equal); 4003 4004 // -output is now a correctly rounded value, unless the original value was 4005 // exactly halfway between two integers, at which point, it has been rounded 4006 // away from zero, when it should be rounded towards \infty. 4007 ma_vcvt_U32_F32(tmp.uintOverlay(), tmp); 4008 compareFloat(tmp, scratchFloat); 4009 as_sub(output, output, Imm8(1), LeaveCC, Equal); 4010 4011 // Negate the output. Since INT_MIN < -INT_MAX, even after adding 1, the 4012 // result will still be a negative number. 4013 bind(&flipSign); 4014 as_rsb(output, output, Imm8(0), SetCC); 4015 4016 // If the result looks non-negative, then this value didn't actually fit 4017 // into the int range, and special handling is required, or it was zero, 4018 // which means the result is actually -0.0 which also requires special 4019 // handling. 4020 ma_b(bail, NotSigned); 4021 4022 bind(&fin); 4023 } 4024 4025 void MacroAssemblerARMCompat::trunc(FloatRegister input, Register output, 4026 Label* bail) { 4027 Label handleZero; 4028 Label handlePos; 4029 Label fin; 4030 4031 compareDouble(input, NoVFPRegister); 4032 // NaN is always a bail condition, just bail directly. 4033 ma_b(bail, Assembler::Overflow); 4034 ma_b(&handleZero, Assembler::Equal); 4035 ma_b(&handlePos, Assembler::NotSigned); 4036 4037 ScratchDoubleScope scratchDouble(asMasm()); 4038 4039 // We are in the ]-Inf; 0[ range 4040 // If we are in the ]-1; 0[ range => bailout 4041 loadConstantDouble(-1.0, scratchDouble); 4042 compareDouble(input, scratchDouble); 4043 ma_b(bail, Assembler::GreaterThan); 4044 4045 // We are in the ]-Inf; -1] range: trunc(x) == -floor(-x) and floor can be 4046 // computed with direct truncation here (x > 0). 4047 ma_vneg(input, scratchDouble); 4048 ma_vcvt_F64_U32(scratchDouble, scratchDouble.uintOverlay()); 4049 ma_vxfer(scratchDouble.uintOverlay(), output); 4050 ma_neg(output, output, SetCC); 4051 ma_b(bail, NotSigned); 4052 ma_b(&fin); 4053 4054 // Test for 0.0 / -0.0: if the top word of the input double is not zero, 4055 // then it was -0 and we need to bail out. 4056 bind(&handleZero); 4057 as_vxfer(output, InvalidReg, input, FloatToCore, Always, 1); 4058 as_cmp(output, Imm8(0)); 4059 ma_b(bail, NonZero); 4060 ma_b(&fin); 4061 4062 // We are in the ]0; +inf] range: truncation is the path to glory. Since 4063 // it is known to be > 0.0, explicitly convert to a larger range, then a 4064 // value that rounds to INT_MAX is explicitly different from an argument 4065 // that clamps to INT_MAX. 4066 bind(&handlePos); 4067 ma_vcvt_F64_U32(input, scratchDouble.uintOverlay()); 4068 ma_vxfer(scratchDouble.uintOverlay(), output); 4069 ma_mov(output, output, SetCC); 4070 ma_b(bail, Signed); 4071 4072 bind(&fin); 4073 } 4074 4075 void MacroAssemblerARMCompat::truncf(FloatRegister input, Register output, 4076 Label* bail) { 4077 Label handleZero; 4078 Label handlePos; 4079 Label fin; 4080 4081 compareFloat(input, NoVFPRegister); 4082 // NaN is always a bail condition, just bail directly. 4083 ma_b(bail, Assembler::Overflow); 4084 ma_b(&handleZero, Assembler::Equal); 4085 ma_b(&handlePos, Assembler::NotSigned); 4086 4087 // We are in the ]-Inf; 0[ range 4088 // If we are in the ]-1; 0[ range => bailout 4089 { 4090 ScratchFloat32Scope scratch(asMasm()); 4091 loadConstantFloat32(-1.f, scratch); 4092 compareFloat(input, scratch); 4093 ma_b(bail, Assembler::GreaterThan); 4094 } 4095 4096 // We are in the ]-Inf; -1] range: trunc(x) == -floor(-x) and floor can be 4097 // computed with direct truncation here (x > 0). 4098 { 4099 ScratchDoubleScope scratchDouble(asMasm()); 4100 FloatRegister scratchFloat = scratchDouble.asSingle(); 4101 FloatRegister scratchUInt = scratchDouble.uintOverlay(); 4102 4103 ma_vneg_f32(input, scratchFloat); 4104 ma_vcvt_F32_U32(scratchFloat, scratchUInt); 4105 ma_vxfer(scratchUInt, output); 4106 ma_neg(output, output, SetCC); 4107 ma_b(bail, NotSigned); 4108 ma_b(&fin); 4109 } 4110 4111 // Test for 0.0 / -0.0: if the top word of the input double is not zero, 4112 // then it was -0 and we need to bail out. 4113 bind(&handleZero); 4114 as_vxfer(output, InvalidReg, VFPRegister(input).singleOverlay(), FloatToCore, 4115 Always, 0); 4116 as_cmp(output, Imm8(0)); 4117 ma_b(bail, NonZero); 4118 ma_b(&fin); 4119 4120 // We are in the ]0; +inf] range: truncation is the path to glory; Since 4121 // it is known to be > 0.0, explicitly convert to a larger range, then a 4122 // value that rounds to INT_MAX is explicitly different from an argument 4123 bind(&handlePos); 4124 { 4125 // The argument is a positive number, 4126 // that clamps to INT_MAX. 4127 { 4128 ScratchFloat32Scope scratch(asMasm()); 4129 ma_vcvt_F32_U32(input, scratch.uintOverlay()); 4130 ma_vxfer(VFPRegister(scratch).uintOverlay(), output); 4131 } 4132 ma_mov(output, output, SetCC); 4133 ma_b(bail, Signed); 4134 } 4135 4136 bind(&fin); 4137 } 4138 4139 void MacroAssemblerARMCompat::profilerEnterFrame(Register framePtr, 4140 Register scratch) { 4141 asMasm().loadJSContext(scratch); 4142 loadPtr(Address(scratch, offsetof(JSContext, profilingActivation_)), scratch); 4143 storePtr(framePtr, 4144 Address(scratch, JitActivation::offsetOfLastProfilingFrame())); 4145 storePtr(ImmPtr(nullptr), 4146 Address(scratch, JitActivation::offsetOfLastProfilingCallSite())); 4147 } 4148 4149 void MacroAssemblerARMCompat::profilerExitFrame() { 4150 jump(asMasm().runtime()->jitRuntime()->getProfilerExitFrameTail()); 4151 } 4152 4153 MacroAssembler& MacroAssemblerARM::asMasm() { 4154 return *static_cast<MacroAssembler*>(this); 4155 } 4156 4157 const MacroAssembler& MacroAssemblerARM::asMasm() const { 4158 return *static_cast<const MacroAssembler*>(this); 4159 } 4160 4161 MacroAssembler& MacroAssemblerARMCompat::asMasm() { 4162 return *static_cast<MacroAssembler*>(this); 4163 } 4164 4165 const MacroAssembler& MacroAssemblerARMCompat::asMasm() const { 4166 return *static_cast<const MacroAssembler*>(this); 4167 } 4168 4169 void MacroAssembler::subFromStackPtr(Imm32 imm32) { 4170 ScratchRegisterScope scratch(*this); 4171 if (imm32.value) { 4172 ma_sub(imm32, sp, scratch); 4173 } 4174 } 4175 4176 //{{{ check_macroassembler_style 4177 // =============================================================== 4178 // MacroAssembler high-level usage. 4179 4180 void MacroAssembler::flush() { Assembler::flush(); } 4181 4182 void MacroAssembler::comment(const char* msg) { Assembler::comment(msg); } 4183 4184 // =============================================================== 4185 // Stack manipulation functions. 4186 4187 size_t MacroAssembler::PushRegsInMaskSizeInBytes(LiveRegisterSet set) { 4188 return set.gprs().size() * sizeof(intptr_t) + set.fpus().getPushSizeInBytes(); 4189 } 4190 4191 void MacroAssembler::PushRegsInMask(LiveRegisterSet set) { 4192 mozilla::DebugOnly<size_t> framePushedInitial = framePushed(); 4193 4194 int32_t diffF = set.fpus().getPushSizeInBytes(); 4195 int32_t diffG = set.gprs().size() * sizeof(intptr_t); 4196 4197 if (set.gprs().size() > 1) { 4198 adjustFrame(diffG); 4199 startDataTransferM(IsStore, StackPointer, DB, WriteBack); 4200 for (GeneralRegisterBackwardIterator iter(set.gprs()); iter.more(); 4201 ++iter) { 4202 diffG -= sizeof(intptr_t); 4203 transferReg(*iter); 4204 } 4205 finishDataTransfer(); 4206 } else { 4207 reserveStack(diffG); 4208 for (GeneralRegisterBackwardIterator iter(set.gprs()); iter.more(); 4209 ++iter) { 4210 diffG -= sizeof(intptr_t); 4211 storePtr(*iter, Address(StackPointer, diffG)); 4212 } 4213 } 4214 MOZ_ASSERT(diffG == 0); 4215 4216 // It's possible that the logic is just fine as it is if the reduced set 4217 // maps SIMD pairs to plain doubles and transferMultipleByRuns() stores 4218 // and loads doubles. 4219 #ifdef ENABLE_WASM_SIMD 4220 # error "Needs more careful logic if SIMD is enabled" 4221 #endif 4222 4223 adjustFrame(diffF); 4224 diffF += transferMultipleByRuns(set.fpus(), IsStore, StackPointer, DB); 4225 MOZ_ASSERT(diffF == 0); 4226 4227 MOZ_ASSERT(framePushed() - framePushedInitial == 4228 PushRegsInMaskSizeInBytes(set)); 4229 } 4230 4231 void MacroAssembler::storeRegsInMask(LiveRegisterSet set, Address dest, 4232 Register scratch) { 4233 mozilla::DebugOnly<size_t> offsetInitial = dest.offset; 4234 4235 int32_t diffF = set.fpus().getPushSizeInBytes(); 4236 int32_t diffG = set.gprs().size() * sizeof(intptr_t); 4237 4238 MOZ_ASSERT(dest.offset >= diffF + diffG); 4239 4240 if (set.gprs().size() > 1) { 4241 computeEffectiveAddress(dest, scratch); 4242 4243 startDataTransferM(IsStore, scratch, DB, WriteBack); 4244 for (GeneralRegisterBackwardIterator iter(set.gprs()); iter.more(); 4245 ++iter) { 4246 diffG -= sizeof(intptr_t); 4247 dest.offset -= sizeof(intptr_t); 4248 transferReg(*iter); 4249 } 4250 finishDataTransfer(); 4251 } else { 4252 for (GeneralRegisterBackwardIterator iter(set.gprs()); iter.more(); 4253 ++iter) { 4254 diffG -= sizeof(intptr_t); 4255 dest.offset -= sizeof(intptr_t); 4256 storePtr(*iter, dest); 4257 } 4258 } 4259 MOZ_ASSERT(diffG == 0); 4260 (void)diffG; 4261 4262 // See above. 4263 #ifdef ENABLE_WASM_SIMD 4264 # error "Needs more careful logic if SIMD is enabled" 4265 #endif 4266 4267 MOZ_ASSERT(diffF >= 0); 4268 if (diffF > 0) { 4269 computeEffectiveAddress(dest, scratch); 4270 diffF += transferMultipleByRuns(set.fpus(), IsStore, scratch, DB); 4271 } 4272 4273 MOZ_ASSERT(diffF == 0); 4274 4275 // "The amount of space actually used does not exceed what 4276 // `PushRegsInMaskSizeInBytes` claims will be used." 4277 MOZ_ASSERT(offsetInitial - dest.offset <= PushRegsInMaskSizeInBytes(set)); 4278 } 4279 4280 void MacroAssembler::PopRegsInMaskIgnore(LiveRegisterSet set, 4281 LiveRegisterSet ignore) { 4282 mozilla::DebugOnly<size_t> framePushedInitial = framePushed(); 4283 4284 int32_t diffG = set.gprs().size() * sizeof(intptr_t); 4285 int32_t diffF = set.fpus().getPushSizeInBytes(); 4286 const int32_t reservedG = diffG; 4287 const int32_t reservedF = diffF; 4288 4289 // See above. 4290 #ifdef ENABLE_WASM_SIMD 4291 # error "Needs more careful logic if SIMD is enabled" 4292 #endif 4293 4294 // ARM can load multiple registers at once, but only if we want back all 4295 // the registers we previously saved to the stack. 4296 if (ignore.emptyFloat()) { 4297 diffF -= transferMultipleByRuns(set.fpus(), IsLoad, StackPointer, IA); 4298 adjustFrame(-reservedF); 4299 } else { 4300 LiveFloatRegisterSet fpset(set.fpus().reduceSetForPush()); 4301 LiveFloatRegisterSet fpignore(ignore.fpus().reduceSetForPush()); 4302 for (FloatRegisterBackwardIterator iter(fpset); iter.more(); ++iter) { 4303 diffF -= (*iter).size(); 4304 if (!fpignore.has(*iter)) { 4305 loadDouble(Address(StackPointer, diffF), *iter); 4306 } 4307 } 4308 freeStack(reservedF); 4309 } 4310 MOZ_ASSERT(diffF == 0); 4311 4312 if (set.gprs().size() > 1 && ignore.emptyGeneral()) { 4313 startDataTransferM(IsLoad, StackPointer, IA, WriteBack); 4314 for (GeneralRegisterBackwardIterator iter(set.gprs()); iter.more(); 4315 ++iter) { 4316 diffG -= sizeof(intptr_t); 4317 transferReg(*iter); 4318 } 4319 finishDataTransfer(); 4320 adjustFrame(-reservedG); 4321 } else { 4322 for (GeneralRegisterBackwardIterator iter(set.gprs()); iter.more(); 4323 ++iter) { 4324 diffG -= sizeof(intptr_t); 4325 if (!ignore.has(*iter)) { 4326 loadPtr(Address(StackPointer, diffG), *iter); 4327 } 4328 } 4329 freeStack(reservedG); 4330 } 4331 MOZ_ASSERT(diffG == 0); 4332 4333 MOZ_ASSERT(framePushedInitial - framePushed() == 4334 PushRegsInMaskSizeInBytes(set)); 4335 } 4336 4337 void MacroAssembler::Push(Register reg) { 4338 push(reg); 4339 adjustFrame(sizeof(intptr_t)); 4340 } 4341 4342 void MacroAssembler::Push(const Imm32 imm) { 4343 push(imm); 4344 adjustFrame(sizeof(intptr_t)); 4345 } 4346 4347 void MacroAssembler::Push(const ImmWord imm) { 4348 push(imm); 4349 adjustFrame(sizeof(intptr_t)); 4350 } 4351 4352 void MacroAssembler::Push(const ImmPtr imm) { 4353 Push(ImmWord(uintptr_t(imm.value))); 4354 } 4355 4356 void MacroAssembler::Push(const ImmGCPtr ptr) { 4357 push(ptr); 4358 adjustFrame(sizeof(intptr_t)); 4359 } 4360 4361 void MacroAssembler::Push(FloatRegister reg) { 4362 MOZ_ASSERT(reg.isFloat(), "simd128 not supported"); 4363 VFPRegister r = VFPRegister(reg); 4364 ma_vpush(VFPRegister(reg)); 4365 adjustFrame(r.size()); 4366 } 4367 4368 void MacroAssembler::PushBoxed(FloatRegister reg) { 4369 MOZ_ASSERT(reg.isDouble()); 4370 Push(reg); 4371 } 4372 4373 void MacroAssembler::Pop(Register reg) { 4374 ma_pop(reg); 4375 adjustFrame(-sizeof(intptr_t)); 4376 } 4377 4378 void MacroAssembler::Pop(FloatRegister reg) { 4379 MOZ_ASSERT(reg.isFloat(), "simd128 not supported"); 4380 ma_vpop(reg); 4381 adjustFrame(-reg.size()); 4382 } 4383 4384 void MacroAssembler::Pop(const ValueOperand& val) { 4385 popValue(val); 4386 adjustFrame(-sizeof(Value)); 4387 } 4388 4389 void MacroAssembler::PopStackPtr() { 4390 as_dtr(IsLoad, 32, Offset, sp, DTRAddr(sp, DtrOffImm(0))); 4391 adjustFrame(-sizeof(intptr_t)); 4392 } 4393 4394 void MacroAssembler::freeStackTo(uint32_t framePushed) { 4395 MOZ_ASSERT(framePushed <= framePushed_); 4396 ScratchRegisterScope scratch(*this); 4397 ma_sub(FramePointer, Imm32(int32_t(framePushed)), sp, scratch, LeaveCC, 4398 Always); 4399 framePushed_ = framePushed; 4400 } 4401 4402 // =============================================================== 4403 // Simple call functions. 4404 4405 CodeOffset MacroAssembler::call(Register reg) { 4406 as_blx(reg); 4407 return CodeOffset(currentOffset()); 4408 } 4409 4410 CodeOffset MacroAssembler::call(Label* label) { 4411 // For now, assume that it'll be nearby. 4412 as_bl(label, Always); 4413 return CodeOffset(currentOffset()); 4414 } 4415 4416 void MacroAssembler::call(ImmWord imm) { call(ImmPtr((void*)imm.value)); } 4417 4418 void MacroAssembler::call(ImmPtr imm) { 4419 BufferOffset bo = m_buffer.nextOffset(); 4420 addPendingJump(bo, imm, RelocationKind::HARDCODED); 4421 ma_call(imm); 4422 } 4423 4424 CodeOffset MacroAssembler::call(wasm::SymbolicAddress imm) { 4425 movePtr(imm, CallReg); 4426 return call(CallReg); 4427 } 4428 4429 CodeOffset MacroAssembler::call(const Address& addr) { 4430 loadPtr(addr, CallReg); 4431 return call(CallReg); 4432 } 4433 4434 void MacroAssembler::call(JitCode* c) { 4435 BufferOffset bo = m_buffer.nextOffset(); 4436 addPendingJump(bo, ImmPtr(c->raw()), RelocationKind::JITCODE); 4437 ScratchRegisterScope scratch(*this); 4438 ma_movPatchable(ImmPtr(c->raw()), scratch, Always); 4439 callJitNoProfiler(scratch); 4440 } 4441 4442 CodeOffset MacroAssembler::callWithPatch() { 4443 // The caller ensures that the call is always in range using thunks (below) 4444 // as necessary. 4445 as_bl(BOffImm(), Always, /* documentation */ nullptr); 4446 return CodeOffset(currentOffset()); 4447 } 4448 4449 void MacroAssembler::patchCall(uint32_t callerOffset, uint32_t calleeOffset) { 4450 BufferOffset inst(callerOffset - 4); 4451 BOffImm off = BufferOffset(calleeOffset).diffB<BOffImm>(inst); 4452 MOZ_RELEASE_ASSERT(!off.isInvalid(), 4453 "Failed to insert necessary far jump islands"); 4454 as_bl(off, Always, inst); 4455 } 4456 4457 CodeOffset MacroAssembler::farJumpWithPatch() { 4458 static_assert(32 * 1024 * 1024 - JumpImmediateRange > 4459 wasm::MaxFuncs * 3 * sizeof(Instruction), 4460 "always enough space for thunks"); 4461 4462 // The goal of the thunk is to be able to jump to any address without the 4463 // usual 32MiB branch range limitation. Additionally, to make the thunk 4464 // simple to use, the thunk does not use the constant pool or require 4465 // patching an absolute address. Instead, a relative offset is used which 4466 // can be patched during compilation. 4467 4468 // Inhibit pools since these three words must be contiguous so that the offset 4469 // calculations below are valid. 4470 AutoForbidPoolsAndNops afp(this, 3); 4471 4472 // When pc is used, the read value is the address of the instruction + 8. 4473 // This is exactly the address of the uint32 word we want to load. 4474 ScratchRegisterScope scratch(*this); 4475 ma_ldr(DTRAddr(pc, DtrOffImm(0)), scratch); 4476 4477 // Branch by making pc the destination register. 4478 ma_add(pc, scratch, pc, LeaveCC, Always); 4479 4480 // Allocate space which will be patched by patchFarJump(). 4481 CodeOffset farJump(currentOffset()); 4482 writeInst(UINT32_MAX); 4483 4484 return farJump; 4485 } 4486 4487 void MacroAssembler::patchFarJump(CodeOffset farJump, uint32_t targetOffset) { 4488 uint32_t* u32 = 4489 reinterpret_cast<uint32_t*>(editSrc(BufferOffset(farJump.offset()))); 4490 MOZ_ASSERT(*u32 == UINT32_MAX); 4491 4492 uint32_t addOffset = farJump.offset() - 4; 4493 MOZ_ASSERT(editSrc(BufferOffset(addOffset))->is<InstALU>()); 4494 4495 // When pc is read as the operand of the add, its value is the address of 4496 // the add instruction + 8. 4497 *u32 = (targetOffset - addOffset) - 8; 4498 } 4499 4500 void MacroAssembler::patchFarJump(uint8_t* farJump, uint8_t* target) { 4501 uint32_t* u32 = reinterpret_cast<uint32_t*>(farJump); 4502 MOZ_ASSERT(*u32 == UINT32_MAX); 4503 4504 uint8_t* addPtr = reinterpret_cast<uint8_t*>(u32) - 4; 4505 MOZ_ASSERT(reinterpret_cast<Instruction*>(addPtr)->is<InstALU>()); 4506 4507 int32_t distance = target - addPtr; 4508 MOZ_RELEASE_ASSERT(mozilla::Abs(distance) <= 4509 (intptr_t)jit::MaxCodeBytesPerProcess); 4510 4511 // When pc is read as the operand of the add, its value is the address of 4512 // the add instruction + 8. 4513 *u32 = distance - 8; 4514 } 4515 4516 CodeOffset MacroAssembler::nopPatchableToCall() { 4517 AutoForbidPoolsAndNops afp(this, 4518 /* max number of instructions in scope = */ 1); 4519 ma_nop(); 4520 return CodeOffset(currentOffset()); 4521 } 4522 4523 void MacroAssembler::patchNopToCall(uint8_t* call, uint8_t* target) { 4524 uint8_t* inst = call - 4; 4525 MOZ_ASSERT(reinterpret_cast<Instruction*>(inst)->is<InstBLImm>() || 4526 reinterpret_cast<Instruction*>(inst)->is<InstNOP>()); 4527 4528 new (inst) InstBLImm(BOffImm(target - inst), Assembler::Always); 4529 } 4530 4531 void MacroAssembler::patchCallToNop(uint8_t* call) { 4532 uint8_t* inst = call - 4; 4533 MOZ_ASSERT(reinterpret_cast<Instruction*>(inst)->is<InstBLImm>() || 4534 reinterpret_cast<Instruction*>(inst)->is<InstNOP>()); 4535 new (inst) InstNOP(); 4536 } 4537 4538 CodeOffset MacroAssembler::move32WithPatch(Register dest) { 4539 return movWithPatch(ImmWord(uintptr_t(-1)), dest); 4540 } 4541 4542 void MacroAssembler::patchMove32(CodeOffset offset, Imm32 n) { 4543 Register dest; 4544 Assembler::RelocStyle rs; 4545 4546 { 4547 BufferInstructionIterator iter(BufferOffset(offset.offset()), &m_buffer); 4548 DebugOnly<const uint32_t*> val = GetPtr32Target(iter, &dest, &rs); 4549 MOZ_ASSERT(uint32_t((const uint32_t*)val) == uint32_t(-1)); 4550 } 4551 4552 // Patch over actual instructions. 4553 { 4554 BufferInstructionIterator iter(BufferOffset(offset.offset()), &m_buffer); 4555 MacroAssembler::ma_mov_patch(n, dest, Always, rs, iter); 4556 } 4557 } 4558 4559 void MacroAssembler::pushReturnAddress() { push(lr); } 4560 4561 void MacroAssembler::popReturnAddress() { pop(lr); } 4562 4563 // =============================================================== 4564 // ABI function calls. 4565 4566 void MacroAssembler::setupUnalignedABICall(Register scratch) { 4567 setupNativeABICall(); 4568 dynamicAlignment_ = true; 4569 4570 ma_mov(sp, scratch); 4571 // Force sp to be aligned. 4572 as_bic(sp, sp, Imm8(ABIStackAlignment - 1)); 4573 ma_push(scratch); 4574 } 4575 4576 void MacroAssembler::callWithABIPre(uint32_t* stackAdjust, bool callFromWasm) { 4577 MOZ_ASSERT(inCall_); 4578 uint32_t stackForCall = abiArgs_.stackBytesConsumedSoFar(); 4579 4580 if (dynamicAlignment_) { 4581 // sizeof(intptr_t) accounts for the saved stack pointer pushed by 4582 // setupUnalignedABICall. 4583 stackForCall += ComputeByteAlignment(stackForCall + sizeof(intptr_t), 4584 ABIStackAlignment); 4585 } else { 4586 uint32_t alignmentAtPrologue = callFromWasm ? sizeof(wasm::Frame) : 0; 4587 stackForCall += ComputeByteAlignment( 4588 stackForCall + framePushed() + alignmentAtPrologue, ABIStackAlignment); 4589 } 4590 4591 *stackAdjust = stackForCall; 4592 reserveStack(stackForCall); 4593 4594 // Position all arguments. 4595 { 4596 enoughMemory_ &= moveResolver_.resolve(); 4597 if (!enoughMemory_) { 4598 return; 4599 } 4600 4601 MoveEmitter emitter(*this); 4602 emitter.emit(moveResolver_); 4603 emitter.finish(); 4604 } 4605 4606 assertStackAlignment(ABIStackAlignment); 4607 4608 // Save the lr register if we need to preserve it. 4609 if (secondScratchReg_ != lr) { 4610 ma_mov(lr, secondScratchReg_); 4611 } 4612 } 4613 4614 void MacroAssembler::callWithABIPost(uint32_t stackAdjust, ABIType result) { 4615 if (secondScratchReg_ != lr) { 4616 ma_mov(secondScratchReg_, lr); 4617 } 4618 4619 if (abiArgs_.abi() == ABIKind::System && !ARMFlags::UseHardFpABI()) { 4620 switch (result) { 4621 case ABIType::Float64: 4622 // Move double from r0/r1 to ReturnFloatReg. 4623 ma_vxfer(r0, r1, ReturnDoubleReg); 4624 break; 4625 case ABIType::Float32: 4626 // Move float32 from r0 to ReturnFloatReg. 4627 ma_vxfer(r0, ReturnFloat32Reg); 4628 break; 4629 case ABIType::General: 4630 case ABIType::Int64: 4631 break; 4632 default: 4633 MOZ_CRASH("unexpected callWithABI result"); 4634 } 4635 } 4636 4637 freeStack(stackAdjust); 4638 4639 if (dynamicAlignment_) { 4640 // While the x86 supports pop esp, on ARM that isn't well defined, so 4641 // just do it manually. 4642 as_dtr(IsLoad, 32, Offset, sp, DTRAddr(sp, DtrOffImm(0))); 4643 } 4644 4645 #ifdef DEBUG 4646 MOZ_ASSERT(inCall_); 4647 inCall_ = false; 4648 #endif 4649 } 4650 4651 void MacroAssembler::callWithABINoProfiler(Register fun, ABIType result) { 4652 // Load the callee in r12, as above. 4653 ma_mov(fun, r12); 4654 uint32_t stackAdjust; 4655 callWithABIPre(&stackAdjust); 4656 call(r12); 4657 callWithABIPost(stackAdjust, result); 4658 } 4659 4660 void MacroAssembler::callWithABINoProfiler(const Address& fun, ABIType result) { 4661 // Load the callee in r12, no instruction between the ldr and call should 4662 // clobber it. Note that we can't use fun.base because it may be one of the 4663 // IntArg registers clobbered before the call. 4664 { 4665 ScratchRegisterScope scratch(*this); 4666 ma_ldr(fun, r12, scratch); 4667 } 4668 uint32_t stackAdjust; 4669 callWithABIPre(&stackAdjust); 4670 call(r12); 4671 callWithABIPost(stackAdjust, result); 4672 } 4673 4674 // =============================================================== 4675 // Jit Frames. 4676 4677 uint32_t MacroAssembler::pushFakeReturnAddress(Register scratch) { 4678 // On ARM any references to the pc, adds an additional 8 to it, which 4679 // correspond to 2 instructions of 4 bytes. Thus we use an additional nop 4680 // to pad until we reach the pushed pc. 4681 // 4682 // Note: In practice this should not be necessary, as this fake return 4683 // address is never used for resuming any execution. Thus theoriticaly we 4684 // could just do a Push(pc), and ignore the nop as well as the pool. 4685 enterNoPool(2); 4686 DebugOnly<uint32_t> offsetBeforePush = currentOffset(); 4687 Push(pc); // actually pushes $pc + 8. 4688 ma_nop(); 4689 uint32_t pseudoReturnOffset = currentOffset(); 4690 leaveNoPool(); 4691 4692 MOZ_ASSERT_IF(!oom(), pseudoReturnOffset - offsetBeforePush == 8); 4693 return pseudoReturnOffset; 4694 } 4695 4696 void MacroAssembler::enterFakeExitFrameForWasm(Register cxreg, Register scratch, 4697 ExitFrameType type) { 4698 enterFakeExitFrame(cxreg, scratch, type); 4699 } 4700 4701 CodeOffset MacroAssembler::sub32FromMemAndBranchIfNegativeWithPatch( 4702 Address address, Label* label) { 4703 ScratchRegisterScope value32(*this); 4704 SecondScratchRegisterScope scratch(*this); 4705 MOZ_ASSERT(scratch != address.base); 4706 ma_ldr(address, value32, scratch); 4707 // -128 is arbitrary, but makes `*address` count upwards, which may help 4708 // to identify cases where the subsequent ::patch..() call was forgotten. 4709 ma_sub(Imm32(-128), value32, scratch, SetCC); 4710 // Points immediately after the insn to patch 4711 CodeOffset patchPoint = CodeOffset(currentOffset()); 4712 // This assumes that ma_str does not change the condition codes. 4713 ma_str(value32, address, scratch); 4714 ma_b(label, Assembler::Signed); 4715 return patchPoint; 4716 } 4717 4718 void MacroAssembler::patchSub32FromMemAndBranchIfNegative(CodeOffset offset, 4719 Imm32 imm) { 4720 int32_t val = imm.value; 4721 // Patching it to zero would make the insn pointless 4722 MOZ_RELEASE_ASSERT(val >= 1 && val <= 127); 4723 BufferInstructionIterator iter(BufferOffset(offset.offset() - 4), &m_buffer); 4724 uint32_t* instrPtr = const_cast<uint32_t*>(iter.cur()->raw()); 4725 // 31 27 23 19 15 11 4726 // | | | | | | 4727 // 1110 0010 1001 Rn Rd imm12 = ADDS Rd, Rn, #imm12 // (expected) 4728 // 1110 0010 0101 Rn Rd imm12 = SUBS Rd, Rn, #imm12 // (replacement) 4729 uint32_t oldInstr = *instrPtr; 4730 // Check opcode bits and imm field are as expected 4731 MOZ_ASSERT((oldInstr >> 20) == 0b1110'0010'1001U); 4732 MOZ_ASSERT((oldInstr & 0xFFF) == 128); // as created above 4733 uint32_t newInstr = (0b1110'0010'0101U << 20) | // opcode bits 4734 (oldInstr & 0x000FF000U) | // existing register fields 4735 (val & 0xFFF); // #val 4736 *instrPtr = newInstr; 4737 } 4738 4739 // =============================================================== 4740 // Move instructions 4741 4742 void MacroAssembler::moveValue(const ValueOperand& src, 4743 const ValueOperand& dest) { 4744 Register s0 = src.typeReg(); 4745 Register s1 = src.payloadReg(); 4746 Register d0 = dest.typeReg(); 4747 Register d1 = dest.payloadReg(); 4748 4749 // Either one or both of the source registers could be the same as a 4750 // destination register. 4751 if (s1 == d0) { 4752 if (s0 == d1) { 4753 // If both are, this is just a swap of two registers. 4754 ScratchRegisterScope scratch(*this); 4755 MOZ_ASSERT(d1 != scratch); 4756 MOZ_ASSERT(d0 != scratch); 4757 ma_mov(d1, scratch); 4758 ma_mov(d0, d1); 4759 ma_mov(scratch, d0); 4760 return; 4761 } 4762 // If only one is, copy that source first. 4763 std::swap(s0, s1); 4764 std::swap(d0, d1); 4765 } 4766 4767 if (s0 != d0) { 4768 ma_mov(s0, d0); 4769 } 4770 if (s1 != d1) { 4771 ma_mov(s1, d1); 4772 } 4773 } 4774 4775 void MacroAssembler::moveValue(const Value& src, const ValueOperand& dest) { 4776 ma_mov(Imm32(src.toNunboxTag()), dest.typeReg()); 4777 if (src.isGCThing()) { 4778 ma_mov(ImmGCPtr(src.toGCThing()), dest.payloadReg()); 4779 } else { 4780 ma_mov(Imm32(src.toNunboxPayload()), dest.payloadReg()); 4781 } 4782 } 4783 4784 // =============================================================== 4785 // Branch functions 4786 4787 void MacroAssembler::loadStoreBuffer(Register ptr, Register buffer) { 4788 ma_lsr(Imm32(gc::ChunkShift), ptr, buffer); 4789 ma_lsl(Imm32(gc::ChunkShift), buffer, buffer); 4790 load32(Address(buffer, gc::ChunkStoreBufferOffset), buffer); 4791 } 4792 4793 void MacroAssembler::branchPtrInNurseryChunk(Condition cond, Register ptr, 4794 Register temp, Label* label) { 4795 Maybe<SecondScratchRegisterScope> scratch2; 4796 if (temp == Register::Invalid()) { 4797 scratch2.emplace(*this); 4798 temp = scratch2.ref(); 4799 } 4800 4801 MOZ_ASSERT(cond == Assembler::Equal || cond == Assembler::NotEqual); 4802 MOZ_ASSERT(ptr != temp); 4803 4804 ma_lsr(Imm32(gc::ChunkShift), ptr, temp); 4805 ma_lsl(Imm32(gc::ChunkShift), temp, temp); 4806 loadPtr(Address(temp, gc::ChunkStoreBufferOffset), temp); 4807 branchPtr(InvertCondition(cond), temp, ImmWord(0), label); 4808 } 4809 4810 void MacroAssembler::branchValueIsNurseryCell(Condition cond, 4811 const Address& address, 4812 Register temp, Label* label) { 4813 MOZ_ASSERT(cond == Assembler::Equal || cond == Assembler::NotEqual); 4814 4815 Label done; 4816 4817 branchTestGCThing(Assembler::NotEqual, address, 4818 cond == Assembler::Equal ? &done : label); 4819 4820 loadPtr(ToPayload(address), temp); 4821 SecondScratchRegisterScope scratch2(*this); 4822 branchPtrInNurseryChunk(cond, temp, scratch2, label); 4823 4824 bind(&done); 4825 } 4826 4827 void MacroAssembler::branchValueIsNurseryCell(Condition cond, 4828 ValueOperand value, Register temp, 4829 Label* label) { 4830 MOZ_ASSERT(cond == Assembler::Equal || cond == Assembler::NotEqual); 4831 4832 Label done; 4833 4834 branchTestGCThing(Assembler::NotEqual, value, 4835 cond == Assembler::Equal ? &done : label); 4836 branchPtrInNurseryChunk(cond, value.payloadReg(), temp, label); 4837 4838 bind(&done); 4839 } 4840 4841 void MacroAssembler::branchTestValue(Condition cond, const ValueOperand& lhs, 4842 const Value& rhs, Label* label) { 4843 MOZ_ASSERT(cond == Equal || cond == NotEqual); 4844 MOZ_ASSERT(!rhs.isNaN()); 4845 // If cond == NotEqual, branch when a.payload != b.payload || a.tag != 4846 // b.tag. If the payloads are equal, compare the tags. If the payloads are 4847 // not equal, short circuit true (NotEqual). 4848 // 4849 // If cand == Equal, branch when a.payload == b.payload && a.tag == b.tag. 4850 // If the payloads are equal, compare the tags. If the payloads are not 4851 // equal, short circuit false (NotEqual). 4852 ScratchRegisterScope scratch(*this); 4853 4854 if (rhs.isGCThing()) { 4855 ma_cmp(lhs.payloadReg(), ImmGCPtr(rhs.toGCThing()), scratch); 4856 } else { 4857 ma_cmp(lhs.payloadReg(), Imm32(rhs.toNunboxPayload()), scratch); 4858 } 4859 ma_cmp(lhs.typeReg(), Imm32(rhs.toNunboxTag()), scratch, Equal); 4860 ma_b(label, cond); 4861 } 4862 4863 void MacroAssembler::branchTestNaNValue(Condition cond, const ValueOperand& val, 4864 Register temp, Label* label) { 4865 MOZ_ASSERT(cond == Equal || cond == NotEqual); 4866 ScratchRegisterScope scratch(*this); 4867 4868 // When testing for NaN, we want to ignore the sign bit. 4869 static_assert(JS::detail::CanonicalizedNaNSignBit == 0); 4870 const uint32_t SignBit = mozilla::FloatingPoint<double>::kSignBit >> 32; 4871 as_bic(temp, val.typeReg(), Imm8(SignBit)); 4872 4873 Value expected = DoubleValue(JS::GenericNaN()); 4874 ma_cmp(val.payloadReg(), Imm32(expected.toNunboxPayload()), scratch); 4875 ma_cmp(temp, Imm32(expected.toNunboxTag()), scratch, Equal); 4876 ma_b(label, cond); 4877 } 4878 4879 // ======================================================================== 4880 // Memory access primitives. 4881 template <typename T> 4882 void MacroAssembler::storeUnboxedValue(const ConstantOrRegister& value, 4883 MIRType valueType, const T& dest) { 4884 MOZ_ASSERT(valueType < MIRType::Value); 4885 4886 if (valueType == MIRType::Double) { 4887 storeDouble(value.reg().typedReg().fpu(), dest); 4888 return; 4889 } 4890 4891 // Store the type tag. 4892 storeTypeTag(ImmType(ValueTypeFromMIRType(valueType)), dest); 4893 4894 // Store the payload. 4895 if (value.constant()) { 4896 storePayload(value.value(), dest); 4897 } else { 4898 storePayload(value.reg().typedReg().gpr(), dest); 4899 } 4900 } 4901 4902 template void MacroAssembler::storeUnboxedValue(const ConstantOrRegister& value, 4903 MIRType valueType, 4904 const Address& dest); 4905 template void MacroAssembler::storeUnboxedValue( 4906 const ConstantOrRegister& value, MIRType valueType, 4907 const BaseObjectElementIndex& dest); 4908 4909 FaultingCodeOffset MacroAssembler::wasmTrapInstruction() { 4910 return FaultingCodeOffset(as_illegal_trap().getOffset()); 4911 } 4912 4913 void MacroAssembler::wasmBoundsCheck32(Condition cond, Register index, 4914 Register boundsCheckLimit, 4915 Label* label) { 4916 as_cmp(index, O2Reg(boundsCheckLimit)); 4917 as_b(label, cond); 4918 if (JitOptions.spectreIndexMasking) { 4919 ma_mov(boundsCheckLimit, index, LeaveCC, cond); 4920 } 4921 } 4922 4923 void MacroAssembler::wasmBoundsCheck32(Condition cond, Register index, 4924 Address boundsCheckLimit, Label* label) { 4925 ScratchRegisterScope scratch(*this); 4926 // We want to do a word load from 4927 // [boundsCheckLimit.base, #+boundsCheckLimit.offset], 4928 // but the offset might exceed 4095, so we can't use ma_ldr directly. 4929 // ma_dataTransferN will handle this correctly, but needs a scratch reg as 4930 // an address temporary for the big-offset case. The scratch reg is also 4931 // used in all cases for the loaded value; that's OK. 4932 ma_dataTransferN(IsLoad, /*size=*/32, /*IsSigned=*/false, 4933 boundsCheckLimit.base, Imm32(boundsCheckLimit.offset), 4934 scratch, scratch); 4935 as_cmp(index, O2Reg(scratch)); 4936 as_b(label, cond); 4937 if (JitOptions.spectreIndexMasking) { 4938 ma_mov(scratch, index, LeaveCC, cond); 4939 } 4940 } 4941 4942 void MacroAssembler::wasmBoundsCheck64(Condition cond, Register64 index, 4943 Register64 boundsCheckLimit, 4944 Label* label) { 4945 Label ifFalse; 4946 cmp32(index.high, Imm32(0)); 4947 j(Assembler::NonZero, &ifFalse); 4948 wasmBoundsCheck32(cond, index.low, boundsCheckLimit.low, label); 4949 bind(&ifFalse); 4950 } 4951 4952 void MacroAssembler::wasmBoundsCheck64(Condition cond, Register64 index, 4953 Address boundsCheckLimit, Label* label) { 4954 Label ifFalse; 4955 cmp32(index.high, Imm32(0)); 4956 j(Assembler::NonZero, &ifFalse); 4957 wasmBoundsCheck32(cond, index.low, boundsCheckLimit, label); 4958 bind(&ifFalse); 4959 } 4960 4961 void MacroAssembler::wasmTruncateDoubleToUInt32(FloatRegister input, 4962 Register output, 4963 bool isSaturating, 4964 Label* oolEntry) { 4965 wasmTruncateToInt32(input, output, MIRType::Double, /* isUnsigned= */ true, 4966 isSaturating, oolEntry); 4967 } 4968 4969 void MacroAssembler::wasmTruncateDoubleToInt32(FloatRegister input, 4970 Register output, 4971 bool isSaturating, 4972 Label* oolEntry) { 4973 wasmTruncateToInt32(input, output, MIRType::Double, /* isUnsigned= */ false, 4974 isSaturating, oolEntry); 4975 } 4976 4977 void MacroAssembler::wasmTruncateFloat32ToUInt32(FloatRegister input, 4978 Register output, 4979 bool isSaturating, 4980 Label* oolEntry) { 4981 wasmTruncateToInt32(input, output, MIRType::Float32, /* isUnsigned= */ true, 4982 isSaturating, oolEntry); 4983 } 4984 4985 void MacroAssembler::wasmTruncateFloat32ToInt32(FloatRegister input, 4986 Register output, 4987 bool isSaturating, 4988 Label* oolEntry) { 4989 wasmTruncateToInt32(input, output, MIRType::Float32, /* isUnsigned= */ false, 4990 isSaturating, oolEntry); 4991 } 4992 4993 void MacroAssembler::oolWasmTruncateCheckF32ToI32( 4994 FloatRegister input, Register output, TruncFlags flags, 4995 const wasm::TrapSiteDesc& trapSiteDesc, Label* rejoin) { 4996 outOfLineWasmTruncateToIntCheck(input, MIRType::Float32, MIRType::Int32, 4997 flags, rejoin, trapSiteDesc); 4998 } 4999 5000 void MacroAssembler::oolWasmTruncateCheckF64ToI32( 5001 FloatRegister input, Register output, TruncFlags flags, 5002 const wasm::TrapSiteDesc& trapSiteDesc, Label* rejoin) { 5003 outOfLineWasmTruncateToIntCheck(input, MIRType::Double, MIRType::Int32, flags, 5004 rejoin, trapSiteDesc); 5005 } 5006 5007 void MacroAssembler::oolWasmTruncateCheckF32ToI64( 5008 FloatRegister input, Register64 output, TruncFlags flags, 5009 const wasm::TrapSiteDesc& trapSiteDesc, Label* rejoin) { 5010 outOfLineWasmTruncateToIntCheck(input, MIRType::Float32, MIRType::Int64, 5011 flags, rejoin, trapSiteDesc); 5012 } 5013 5014 void MacroAssembler::oolWasmTruncateCheckF64ToI64( 5015 FloatRegister input, Register64 output, TruncFlags flags, 5016 const wasm::TrapSiteDesc& trapSiteDesc, Label* rejoin) { 5017 outOfLineWasmTruncateToIntCheck(input, MIRType::Double, MIRType::Int64, flags, 5018 rejoin, trapSiteDesc); 5019 } 5020 5021 void MacroAssembler::wasmLoad(const wasm::MemoryAccessDesc& access, 5022 Register memoryBase, Register ptr, 5023 Register ptrScratch, AnyRegister output) { 5024 wasmLoadImpl(access, memoryBase, ptr, ptrScratch, output, 5025 Register64::Invalid()); 5026 } 5027 5028 void MacroAssembler::wasmLoadI64(const wasm::MemoryAccessDesc& access, 5029 Register memoryBase, Register ptr, 5030 Register ptrScratch, Register64 output) { 5031 MOZ_ASSERT_IF(access.isAtomic(), access.byteSize() <= 4); 5032 wasmLoadImpl(access, memoryBase, ptr, ptrScratch, AnyRegister(), output); 5033 } 5034 5035 void MacroAssembler::wasmStore(const wasm::MemoryAccessDesc& access, 5036 AnyRegister value, Register memoryBase, 5037 Register ptr, Register ptrScratch) { 5038 wasmStoreImpl(access, value, Register64::Invalid(), memoryBase, ptr, 5039 ptrScratch); 5040 } 5041 5042 void MacroAssembler::wasmStoreI64(const wasm::MemoryAccessDesc& access, 5043 Register64 value, Register memoryBase, 5044 Register ptr, Register ptrScratch) { 5045 MOZ_ASSERT(!access.isAtomic()); 5046 wasmStoreImpl(access, AnyRegister(), value, memoryBase, ptr, ptrScratch); 5047 } 5048 5049 // ======================================================================== 5050 // Primitive atomic operations. 5051 5052 static Register ComputePointerForAtomic(MacroAssembler& masm, 5053 const BaseIndex& src, Register r) { 5054 Register base = src.base; 5055 Register index = src.index; 5056 uint32_t scale = Imm32::ShiftOf(src.scale).value; 5057 int32_t offset = src.offset; 5058 5059 ScratchRegisterScope scratch(masm); 5060 5061 masm.as_add(r, base, lsl(index, scale)); 5062 if (offset != 0) { 5063 masm.ma_add(r, Imm32(offset), r, scratch); 5064 } 5065 return r; 5066 } 5067 5068 static Register ComputePointerForAtomic(MacroAssembler& masm, 5069 const Address& src, Register r) { 5070 ScratchRegisterScope scratch(masm); 5071 if (src.offset == 0) { 5072 return src.base; 5073 } 5074 masm.ma_add(src.base, Imm32(src.offset), r, scratch); 5075 return r; 5076 } 5077 5078 // General algorithm: 5079 // 5080 // ... ptr, <addr> ; compute address of item 5081 // dmb 5082 // L0 ldrex* output, [ptr] 5083 // sxt* output, output, 0 ; sign-extend if applicable 5084 // *xt* tmp, oldval, 0 ; sign-extend or zero-extend if applicable 5085 // cmp output, tmp 5086 // bne L1 ; failed - values are different 5087 // strex* tmp, newval, [ptr] 5088 // cmp tmp, 1 5089 // beq L0 ; failed - location is dirty, retry 5090 // L1 dmb 5091 // 5092 // Discussion here: http://www.cl.cam.ac.uk/~pes20/cpp/cpp0xmappings.html. 5093 // However note that that discussion uses 'isb' as the trailing fence. 5094 // I've not quite figured out why, and I've gone with dmb here which 5095 // is safe. Also see the LLVM source, which uses 'dmb ish' generally. 5096 // (Apple's Swift CPU apparently handles ish in a non-default, faster 5097 // way.) 5098 5099 template <typename T> 5100 static void CompareExchange(MacroAssembler& masm, 5101 const wasm::MemoryAccessDesc* access, 5102 Scalar::Type type, Synchronization sync, 5103 const T& mem, Register oldval, Register newval, 5104 Register output) { 5105 bool signExtend = Scalar::isSignedIntType(type); 5106 unsigned nbytes = Scalar::byteSize(type); 5107 5108 MOZ_ASSERT(nbytes <= 4); 5109 5110 Label again; 5111 Label done; 5112 5113 SecondScratchRegisterScope scratch2(masm); 5114 Register ptr = ComputePointerForAtomic(masm, mem, scratch2); 5115 5116 ScratchRegisterScope scratch(masm); 5117 5118 // NOTE: the generated code must match the assembly code in gen_cmpxchg in 5119 // GenerateAtomicOperations.py 5120 masm.memoryBarrierBefore(sync); 5121 5122 masm.bind(&again); 5123 5124 BufferOffset firstAccess; 5125 switch (nbytes) { 5126 case 1: 5127 firstAccess = masm.as_ldrexb(output, ptr); 5128 if (signExtend) { 5129 masm.as_sxtb(output, output, 0); 5130 masm.as_sxtb(scratch, oldval, 0); 5131 } else { 5132 masm.as_uxtb(scratch, oldval, 0); 5133 } 5134 break; 5135 case 2: 5136 firstAccess = masm.as_ldrexh(output, ptr); 5137 if (signExtend) { 5138 masm.as_sxth(output, output, 0); 5139 masm.as_sxth(scratch, oldval, 0); 5140 } else { 5141 masm.as_uxth(scratch, oldval, 0); 5142 } 5143 break; 5144 case 4: 5145 firstAccess = masm.as_ldrex(output, ptr); 5146 break; 5147 default: 5148 MOZ_CRASH(); 5149 } 5150 if (access) { 5151 masm.append(*access, js::wasm::TrapMachineInsnForLoad(nbytes), 5152 FaultingCodeOffset(firstAccess.getOffset())); 5153 } 5154 5155 if (nbytes < 4) { 5156 masm.as_cmp(output, O2Reg(scratch)); 5157 } else { 5158 masm.as_cmp(output, O2Reg(oldval)); 5159 } 5160 masm.as_b(&done, MacroAssembler::NotEqual); 5161 switch (nbytes) { 5162 case 1: 5163 masm.as_strexb(scratch, newval, ptr); 5164 break; 5165 case 2: 5166 masm.as_strexh(scratch, newval, ptr); 5167 break; 5168 case 4: 5169 masm.as_strex(scratch, newval, ptr); 5170 break; 5171 default: 5172 MOZ_CRASH(); 5173 } 5174 masm.as_cmp(scratch, Imm8(1)); 5175 masm.as_b(&again, MacroAssembler::Equal); 5176 masm.bind(&done); 5177 5178 masm.memoryBarrierAfter(sync); 5179 } 5180 5181 void MacroAssembler::compareExchange(Scalar::Type type, Synchronization sync, 5182 const Address& address, Register oldval, 5183 Register newval, Register output) { 5184 CompareExchange(*this, nullptr, type, sync, address, oldval, newval, output); 5185 } 5186 5187 void MacroAssembler::compareExchange(Scalar::Type type, Synchronization sync, 5188 const BaseIndex& address, Register oldval, 5189 Register newval, Register output) { 5190 CompareExchange(*this, nullptr, type, sync, address, oldval, newval, output); 5191 } 5192 5193 void MacroAssembler::wasmCompareExchange(const wasm::MemoryAccessDesc& access, 5194 const Address& mem, Register oldval, 5195 Register newval, Register output) { 5196 CompareExchange(*this, &access, access.type(), access.sync(), mem, oldval, 5197 newval, output); 5198 } 5199 5200 void MacroAssembler::wasmCompareExchange(const wasm::MemoryAccessDesc& access, 5201 const BaseIndex& mem, Register oldval, 5202 Register newval, Register output) { 5203 CompareExchange(*this, &access, access.type(), access.sync(), mem, oldval, 5204 newval, output); 5205 } 5206 5207 template <typename T> 5208 static void AtomicExchange(MacroAssembler& masm, 5209 const wasm::MemoryAccessDesc* access, 5210 Scalar::Type type, Synchronization sync, 5211 const T& mem, Register value, Register output) { 5212 bool signExtend = Scalar::isSignedIntType(type); 5213 unsigned nbytes = Scalar::byteSize(type); 5214 5215 MOZ_ASSERT(nbytes <= 4); 5216 5217 // Bug 1077321: We may further optimize for ARMv8 (AArch32) here. 5218 Label again; 5219 Label done; 5220 5221 SecondScratchRegisterScope scratch2(masm); 5222 Register ptr = ComputePointerForAtomic(masm, mem, scratch2); 5223 5224 ScratchRegisterScope scratch(masm); 5225 5226 // NOTE: the generated code must match the assembly code in gen_exchange in 5227 // GenerateAtomicOperations.py 5228 masm.memoryBarrierBefore(sync); 5229 5230 masm.bind(&again); 5231 5232 BufferOffset firstAccess; 5233 switch (nbytes) { 5234 case 1: 5235 firstAccess = masm.as_ldrexb(output, ptr); 5236 if (signExtend) { 5237 masm.as_sxtb(output, output, 0); 5238 } 5239 masm.as_strexb(scratch, value, ptr); 5240 break; 5241 case 2: 5242 firstAccess = masm.as_ldrexh(output, ptr); 5243 if (signExtend) { 5244 masm.as_sxth(output, output, 0); 5245 } 5246 masm.as_strexh(scratch, value, ptr); 5247 break; 5248 case 4: 5249 firstAccess = masm.as_ldrex(output, ptr); 5250 masm.as_strex(scratch, value, ptr); 5251 break; 5252 default: 5253 MOZ_CRASH(); 5254 } 5255 if (access) { 5256 masm.append(*access, js::wasm::TrapMachineInsnForLoad(nbytes), 5257 FaultingCodeOffset(firstAccess.getOffset())); 5258 } 5259 5260 masm.as_cmp(scratch, Imm8(1)); 5261 masm.as_b(&again, MacroAssembler::Equal); 5262 masm.bind(&done); 5263 5264 masm.memoryBarrierAfter(sync); 5265 } 5266 5267 void MacroAssembler::atomicExchange(Scalar::Type type, Synchronization sync, 5268 const Address& address, Register value, 5269 Register output) { 5270 AtomicExchange(*this, nullptr, type, sync, address, value, output); 5271 } 5272 5273 void MacroAssembler::atomicExchange(Scalar::Type type, Synchronization sync, 5274 const BaseIndex& address, Register value, 5275 Register output) { 5276 AtomicExchange(*this, nullptr, type, sync, address, value, output); 5277 } 5278 5279 void MacroAssembler::wasmAtomicExchange(const wasm::MemoryAccessDesc& access, 5280 const Address& mem, Register value, 5281 Register output) { 5282 AtomicExchange(*this, &access, access.type(), access.sync(), mem, value, 5283 output); 5284 } 5285 5286 void MacroAssembler::wasmAtomicExchange(const wasm::MemoryAccessDesc& access, 5287 const BaseIndex& mem, Register value, 5288 Register output) { 5289 AtomicExchange(*this, &access, access.type(), access.sync(), mem, value, 5290 output); 5291 } 5292 5293 // General algorithm: 5294 // 5295 // ... ptr, <addr> ; compute address of item 5296 // dmb 5297 // L0 ldrex* output, [ptr] 5298 // sxt* output, output, 0 ; sign-extend if applicable 5299 // OP tmp, output, value ; compute value to store 5300 // strex* tmp2, tmp, [ptr] ; tmp2 required by strex 5301 // cmp tmp2, 1 5302 // beq L0 ; failed - location is dirty, retry 5303 // dmb ; ordering barrier required 5304 // 5305 // Also see notes above at compareExchange re the barrier strategy. 5306 // 5307 // Observe that the value being operated into the memory element need 5308 // not be sign-extended because no OP will make use of bits to the 5309 // left of the bits indicated by the width of the element, and neither 5310 // output nor the bits stored are affected by OP. 5311 5312 template <typename T> 5313 static void AtomicFetchOp(MacroAssembler& masm, 5314 const wasm::MemoryAccessDesc* access, 5315 Scalar::Type type, Synchronization sync, AtomicOp op, 5316 const Register& value, const T& mem, 5317 Register flagTemp, Register output) { 5318 bool signExtend = Scalar::isSignedIntType(type); 5319 unsigned nbytes = Scalar::byteSize(type); 5320 5321 MOZ_ASSERT(nbytes <= 4); 5322 MOZ_ASSERT(flagTemp != InvalidReg); 5323 MOZ_ASSERT(output != value); 5324 5325 Label again; 5326 5327 SecondScratchRegisterScope scratch2(masm); 5328 Register ptr = ComputePointerForAtomic(masm, mem, scratch2); 5329 5330 // NOTE: the generated code must match the assembly code in gen_fetchop in 5331 // GenerateAtomicOperations.py 5332 masm.memoryBarrierBefore(sync); 5333 5334 ScratchRegisterScope scratch(masm); 5335 5336 masm.bind(&again); 5337 5338 BufferOffset firstAccess; 5339 switch (nbytes) { 5340 case 1: 5341 firstAccess = masm.as_ldrexb(output, ptr); 5342 if (signExtend) { 5343 masm.as_sxtb(output, output, 0); 5344 } 5345 break; 5346 case 2: 5347 firstAccess = masm.as_ldrexh(output, ptr); 5348 if (signExtend) { 5349 masm.as_sxth(output, output, 0); 5350 } 5351 break; 5352 case 4: 5353 firstAccess = masm.as_ldrex(output, ptr); 5354 break; 5355 default: 5356 MOZ_CRASH(); 5357 } 5358 if (access) { 5359 masm.append(*access, js::wasm::TrapMachineInsnForLoad(nbytes), 5360 FaultingCodeOffset(firstAccess.getOffset())); 5361 } 5362 5363 switch (op) { 5364 case AtomicOp::Add: 5365 masm.as_add(scratch, output, O2Reg(value)); 5366 break; 5367 case AtomicOp::Sub: 5368 masm.as_sub(scratch, output, O2Reg(value)); 5369 break; 5370 case AtomicOp::And: 5371 masm.as_and(scratch, output, O2Reg(value)); 5372 break; 5373 case AtomicOp::Or: 5374 masm.as_orr(scratch, output, O2Reg(value)); 5375 break; 5376 case AtomicOp::Xor: 5377 masm.as_eor(scratch, output, O2Reg(value)); 5378 break; 5379 default: 5380 MOZ_CRASH(); 5381 } 5382 // Rd must differ from the two other arguments to strex. 5383 switch (nbytes) { 5384 case 1: 5385 masm.as_strexb(flagTemp, scratch, ptr); 5386 break; 5387 case 2: 5388 masm.as_strexh(flagTemp, scratch, ptr); 5389 break; 5390 case 4: 5391 masm.as_strex(flagTemp, scratch, ptr); 5392 break; 5393 default: 5394 MOZ_CRASH(); 5395 } 5396 masm.as_cmp(flagTemp, Imm8(1)); 5397 masm.as_b(&again, MacroAssembler::Equal); 5398 5399 masm.memoryBarrierAfter(sync); 5400 } 5401 5402 void MacroAssembler::atomicFetchOp(Scalar::Type type, Synchronization sync, 5403 AtomicOp op, Register value, 5404 const Address& mem, Register temp, 5405 Register output) { 5406 AtomicFetchOp(*this, nullptr, type, sync, op, value, mem, temp, output); 5407 } 5408 5409 void MacroAssembler::atomicFetchOp(Scalar::Type type, Synchronization sync, 5410 AtomicOp op, Register value, 5411 const BaseIndex& mem, Register temp, 5412 Register output) { 5413 AtomicFetchOp(*this, nullptr, type, sync, op, value, mem, temp, output); 5414 } 5415 5416 void MacroAssembler::wasmAtomicFetchOp(const wasm::MemoryAccessDesc& access, 5417 AtomicOp op, Register value, 5418 const Address& mem, Register temp, 5419 Register output) { 5420 AtomicFetchOp(*this, &access, access.type(), access.sync(), op, value, mem, 5421 temp, output); 5422 } 5423 5424 void MacroAssembler::wasmAtomicFetchOp(const wasm::MemoryAccessDesc& access, 5425 AtomicOp op, Register value, 5426 const BaseIndex& mem, Register temp, 5427 Register output) { 5428 AtomicFetchOp(*this, &access, access.type(), access.sync(), op, value, mem, 5429 temp, output); 5430 } 5431 5432 // Uses both scratch registers, one for the address and one for a temp, 5433 // but needs two temps for strex: 5434 // 5435 // ... ptr, <addr> ; compute address of item 5436 // dmb 5437 // L0 ldrex* temp, [ptr] 5438 // OP temp, temp, value ; compute value to store 5439 // strex* temp2, temp, [ptr] 5440 // cmp temp2, 1 5441 // beq L0 ; failed - location is dirty, retry 5442 // dmb ; ordering barrier required 5443 5444 template <typename T> 5445 static void AtomicEffectOp(MacroAssembler& masm, 5446 const wasm::MemoryAccessDesc* access, 5447 Scalar::Type type, Synchronization sync, AtomicOp op, 5448 const Register& value, const T& mem, 5449 Register flagTemp) { 5450 unsigned nbytes = Scalar::byteSize(type); 5451 5452 MOZ_ASSERT(nbytes <= 4); 5453 MOZ_ASSERT(flagTemp != InvalidReg); 5454 5455 Label again; 5456 5457 SecondScratchRegisterScope scratch2(masm); 5458 Register ptr = ComputePointerForAtomic(masm, mem, scratch2); 5459 5460 masm.memoryBarrierBefore(sync); 5461 5462 ScratchRegisterScope scratch(masm); 5463 5464 masm.bind(&again); 5465 5466 BufferOffset firstAccess; 5467 switch (nbytes) { 5468 case 1: 5469 firstAccess = masm.as_ldrexb(scratch, ptr); 5470 break; 5471 case 2: 5472 firstAccess = masm.as_ldrexh(scratch, ptr); 5473 break; 5474 case 4: 5475 firstAccess = masm.as_ldrex(scratch, ptr); 5476 break; 5477 default: 5478 MOZ_CRASH(); 5479 } 5480 if (access) { 5481 masm.append(*access, js::wasm::TrapMachineInsnForLoad(nbytes), 5482 FaultingCodeOffset(firstAccess.getOffset())); 5483 } 5484 5485 switch (op) { 5486 case AtomicOp::Add: 5487 masm.as_add(scratch, scratch, O2Reg(value)); 5488 break; 5489 case AtomicOp::Sub: 5490 masm.as_sub(scratch, scratch, O2Reg(value)); 5491 break; 5492 case AtomicOp::And: 5493 masm.as_and(scratch, scratch, O2Reg(value)); 5494 break; 5495 case AtomicOp::Or: 5496 masm.as_orr(scratch, scratch, O2Reg(value)); 5497 break; 5498 case AtomicOp::Xor: 5499 masm.as_eor(scratch, scratch, O2Reg(value)); 5500 break; 5501 default: 5502 MOZ_CRASH(); 5503 } 5504 // Rd must differ from the two other arguments to strex. 5505 switch (nbytes) { 5506 case 1: 5507 masm.as_strexb(flagTemp, scratch, ptr); 5508 break; 5509 case 2: 5510 masm.as_strexh(flagTemp, scratch, ptr); 5511 break; 5512 case 4: 5513 masm.as_strex(flagTemp, scratch, ptr); 5514 break; 5515 default: 5516 MOZ_CRASH(); 5517 } 5518 masm.as_cmp(flagTemp, Imm8(1)); 5519 masm.as_b(&again, MacroAssembler::Equal); 5520 5521 masm.memoryBarrierAfter(sync); 5522 } 5523 5524 void MacroAssembler::wasmAtomicEffectOp(const wasm::MemoryAccessDesc& access, 5525 AtomicOp op, Register value, 5526 const Address& mem, Register temp) { 5527 AtomicEffectOp(*this, &access, access.type(), access.sync(), op, value, mem, 5528 temp); 5529 } 5530 5531 void MacroAssembler::wasmAtomicEffectOp(const wasm::MemoryAccessDesc& access, 5532 AtomicOp op, Register value, 5533 const BaseIndex& mem, Register temp) { 5534 AtomicEffectOp(*this, &access, access.type(), access.sync(), op, value, mem, 5535 temp); 5536 } 5537 5538 template <typename T> 5539 static void AtomicLoad64(MacroAssembler& masm, 5540 const wasm::MemoryAccessDesc* access, 5541 Synchronization sync, const T& mem, 5542 Register64 output) { 5543 MOZ_ASSERT((output.low.code() & 1) == 0); 5544 MOZ_ASSERT(output.low.code() + 1 == output.high.code()); 5545 5546 masm.memoryBarrierBefore(sync); 5547 5548 SecondScratchRegisterScope scratch2(masm); 5549 Register ptr = ComputePointerForAtomic(masm, mem, scratch2); 5550 5551 BufferOffset load = masm.as_ldrexd(output.low, output.high, ptr); 5552 if (access) { 5553 masm.append(*access, js::wasm::TrapMachineInsn::Load64, 5554 FaultingCodeOffset(load.getOffset())); 5555 } 5556 masm.as_clrex(); 5557 5558 masm.memoryBarrierAfter(sync); 5559 } 5560 5561 template <typename T> 5562 static void WasmAtomicLoad64(MacroAssembler& masm, 5563 const wasm::MemoryAccessDesc& access, const T& mem, 5564 Register64 temp, Register64 output) { 5565 MOZ_ASSERT(temp.low == InvalidReg && temp.high == InvalidReg); 5566 5567 AtomicLoad64(masm, &access, access.sync(), mem, output); 5568 } 5569 5570 void MacroAssembler::wasmAtomicLoad64(const wasm::MemoryAccessDesc& access, 5571 const Address& mem, Register64 temp, 5572 Register64 output) { 5573 WasmAtomicLoad64(*this, access, mem, temp, output); 5574 } 5575 5576 void MacroAssembler::wasmAtomicLoad64(const wasm::MemoryAccessDesc& access, 5577 const BaseIndex& mem, Register64 temp, 5578 Register64 output) { 5579 WasmAtomicLoad64(*this, access, mem, temp, output); 5580 } 5581 5582 template <typename T> 5583 static void CompareExchange64(MacroAssembler& masm, 5584 const wasm::MemoryAccessDesc* access, 5585 Synchronization sync, const T& mem, 5586 Register64 expect, Register64 replace, 5587 Register64 output) { 5588 MOZ_ASSERT(expect != replace && replace != output && output != expect); 5589 5590 MOZ_ASSERT((replace.low.code() & 1) == 0); 5591 MOZ_ASSERT(replace.low.code() + 1 == replace.high.code()); 5592 5593 MOZ_ASSERT((output.low.code() & 1) == 0); 5594 MOZ_ASSERT(output.low.code() + 1 == output.high.code()); 5595 5596 Label again; 5597 Label done; 5598 5599 SecondScratchRegisterScope scratch2(masm); 5600 Register ptr = ComputePointerForAtomic(masm, mem, scratch2); 5601 5602 // NOTE: the generated code must match the assembly code in gen_cmpxchg in 5603 // GenerateAtomicOperations.py 5604 masm.memoryBarrierBefore(sync); 5605 5606 masm.bind(&again); 5607 BufferOffset load = masm.as_ldrexd(output.low, output.high, ptr); 5608 if (access) { 5609 masm.append(*access, js::wasm::TrapMachineInsn::Load64, 5610 FaultingCodeOffset(load.getOffset())); 5611 } 5612 5613 masm.as_cmp(output.low, O2Reg(expect.low)); 5614 masm.as_cmp(output.high, O2Reg(expect.high), MacroAssembler::Equal); 5615 masm.as_b(&done, MacroAssembler::NotEqual); 5616 5617 ScratchRegisterScope scratch(masm); 5618 5619 // Rd (temp) must differ from the two other arguments to strex. 5620 masm.as_strexd(scratch, replace.low, replace.high, ptr); 5621 masm.as_cmp(scratch, Imm8(1)); 5622 masm.as_b(&again, MacroAssembler::Equal); 5623 masm.bind(&done); 5624 5625 masm.memoryBarrierAfter(sync); 5626 } 5627 5628 void MacroAssembler::wasmCompareExchange64(const wasm::MemoryAccessDesc& access, 5629 const Address& mem, 5630 Register64 expect, 5631 Register64 replace, 5632 Register64 output) { 5633 CompareExchange64(*this, &access, access.sync(), mem, expect, replace, 5634 output); 5635 } 5636 5637 void MacroAssembler::wasmCompareExchange64(const wasm::MemoryAccessDesc& access, 5638 const BaseIndex& mem, 5639 Register64 expect, 5640 Register64 replace, 5641 Register64 output) { 5642 CompareExchange64(*this, &access, access.sync(), mem, expect, replace, 5643 output); 5644 } 5645 5646 void MacroAssembler::compareExchange64(Synchronization sync, const Address& mem, 5647 Register64 expect, Register64 replace, 5648 Register64 output) { 5649 CompareExchange64(*this, nullptr, sync, mem, expect, replace, output); 5650 } 5651 5652 void MacroAssembler::compareExchange64(Synchronization sync, 5653 const BaseIndex& mem, Register64 expect, 5654 Register64 replace, Register64 output) { 5655 CompareExchange64(*this, nullptr, sync, mem, expect, replace, output); 5656 } 5657 5658 template <typename T> 5659 static void AtomicExchange64(MacroAssembler& masm, 5660 const wasm::MemoryAccessDesc* access, 5661 Synchronization sync, const T& mem, 5662 Register64 value, Register64 output) { 5663 MOZ_ASSERT(output != value); 5664 5665 MOZ_ASSERT((value.low.code() & 1) == 0); 5666 MOZ_ASSERT(value.low.code() + 1 == value.high.code()); 5667 5668 MOZ_ASSERT((output.low.code() & 1) == 0); 5669 MOZ_ASSERT(output.low.code() + 1 == output.high.code()); 5670 5671 Label again; 5672 5673 SecondScratchRegisterScope scratch2(masm); 5674 Register ptr = ComputePointerForAtomic(masm, mem, scratch2); 5675 5676 masm.memoryBarrierBefore(sync); 5677 5678 masm.bind(&again); 5679 BufferOffset load = masm.as_ldrexd(output.low, output.high, ptr); 5680 if (access) { 5681 masm.append(*access, js::wasm::TrapMachineInsn::Load64, 5682 FaultingCodeOffset(load.getOffset())); 5683 } 5684 5685 ScratchRegisterScope scratch(masm); 5686 5687 masm.as_strexd(scratch, value.low, value.high, ptr); 5688 masm.as_cmp(scratch, Imm8(1)); 5689 masm.as_b(&again, MacroAssembler::Equal); 5690 5691 masm.memoryBarrierAfter(sync); 5692 } 5693 5694 template <typename T> 5695 static void WasmAtomicExchange64(MacroAssembler& masm, 5696 const wasm::MemoryAccessDesc& access, 5697 const T& mem, Register64 value, 5698 Register64 output) { 5699 AtomicExchange64(masm, &access, access.sync(), mem, value, output); 5700 } 5701 5702 void MacroAssembler::wasmAtomicExchange64(const wasm::MemoryAccessDesc& access, 5703 const Address& mem, Register64 value, 5704 Register64 output) { 5705 WasmAtomicExchange64(*this, access, mem, value, output); 5706 } 5707 5708 void MacroAssembler::wasmAtomicExchange64(const wasm::MemoryAccessDesc& access, 5709 const BaseIndex& mem, 5710 Register64 value, Register64 output) { 5711 WasmAtomicExchange64(*this, access, mem, value, output); 5712 } 5713 5714 void MacroAssembler::atomicExchange64(Synchronization sync, const Address& mem, 5715 Register64 value, Register64 output) { 5716 AtomicExchange64(*this, nullptr, sync, mem, value, output); 5717 } 5718 5719 void MacroAssembler::atomicExchange64(Synchronization sync, 5720 const BaseIndex& mem, Register64 value, 5721 Register64 output) { 5722 AtomicExchange64(*this, nullptr, sync, mem, value, output); 5723 } 5724 5725 template <typename T> 5726 static void AtomicFetchOp64(MacroAssembler& masm, 5727 const wasm::MemoryAccessDesc* access, 5728 Synchronization sync, AtomicOp op, Register64 value, 5729 const T& mem, Register64 temp, Register64 output) { 5730 MOZ_ASSERT(temp.low != InvalidReg && temp.high != InvalidReg); 5731 MOZ_ASSERT(output != value); 5732 MOZ_ASSERT(temp != value); 5733 5734 MOZ_ASSERT((temp.low.code() & 1) == 0); 5735 MOZ_ASSERT(temp.low.code() + 1 == temp.high.code()); 5736 5737 // We could avoid this pair requirement but in that case we would end up 5738 // with two moves in the loop to preserve the loaded value in output. The 5739 // prize would be less register spilling around this op since the pair 5740 // requirement will tend to force more spilling. 5741 5742 MOZ_ASSERT((output.low.code() & 1) == 0); 5743 MOZ_ASSERT(output.low.code() + 1 == output.high.code()); 5744 5745 Label again; 5746 5747 SecondScratchRegisterScope scratch2(masm); 5748 Register ptr = ComputePointerForAtomic(masm, mem, scratch2); 5749 5750 masm.memoryBarrierBefore(sync); 5751 5752 masm.bind(&again); 5753 BufferOffset load = masm.as_ldrexd(output.low, output.high, ptr); 5754 if (access) { 5755 masm.append(*access, js::wasm::TrapMachineInsn::Load64, 5756 FaultingCodeOffset(load.getOffset())); 5757 } 5758 switch (op) { 5759 case AtomicOp::Add: 5760 masm.as_add(temp.low, output.low, O2Reg(value.low), SetCC); 5761 masm.as_adc(temp.high, output.high, O2Reg(value.high)); 5762 break; 5763 case AtomicOp::Sub: 5764 masm.as_sub(temp.low, output.low, O2Reg(value.low), SetCC); 5765 masm.as_sbc(temp.high, output.high, O2Reg(value.high)); 5766 break; 5767 case AtomicOp::And: 5768 masm.as_and(temp.low, output.low, O2Reg(value.low)); 5769 masm.as_and(temp.high, output.high, O2Reg(value.high)); 5770 break; 5771 case AtomicOp::Or: 5772 masm.as_orr(temp.low, output.low, O2Reg(value.low)); 5773 masm.as_orr(temp.high, output.high, O2Reg(value.high)); 5774 break; 5775 case AtomicOp::Xor: 5776 masm.as_eor(temp.low, output.low, O2Reg(value.low)); 5777 masm.as_eor(temp.high, output.high, O2Reg(value.high)); 5778 break; 5779 } 5780 5781 ScratchRegisterScope scratch(masm); 5782 5783 // Rd (temp) must differ from the two other arguments to strex. 5784 masm.as_strexd(scratch, temp.low, temp.high, ptr); 5785 masm.as_cmp(scratch, Imm8(1)); 5786 masm.as_b(&again, MacroAssembler::Equal); 5787 5788 masm.memoryBarrierAfter(sync); 5789 } 5790 5791 template <typename T> 5792 static void WasmAtomicFetchOp64(MacroAssembler& masm, 5793 const wasm::MemoryAccessDesc& access, 5794 AtomicOp op, Register64 value, const T& mem, 5795 Register64 temp, Register64 output) { 5796 AtomicFetchOp64(masm, &access, access.sync(), op, value, mem, temp, output); 5797 } 5798 5799 void MacroAssembler::wasmAtomicFetchOp64(const wasm::MemoryAccessDesc& access, 5800 AtomicOp op, Register64 value, 5801 const Address& mem, Register64 temp, 5802 Register64 output) { 5803 WasmAtomicFetchOp64(*this, access, op, value, mem, temp, output); 5804 } 5805 5806 void MacroAssembler::wasmAtomicFetchOp64(const wasm::MemoryAccessDesc& access, 5807 AtomicOp op, Register64 value, 5808 const BaseIndex& mem, Register64 temp, 5809 Register64 output) { 5810 WasmAtomicFetchOp64(*this, access, op, value, mem, temp, output); 5811 } 5812 5813 void MacroAssembler::atomicFetchOp64(Synchronization sync, AtomicOp op, 5814 Register64 value, const Address& mem, 5815 Register64 temp, Register64 output) { 5816 AtomicFetchOp64(*this, nullptr, sync, op, value, mem, temp, output); 5817 } 5818 5819 void MacroAssembler::atomicFetchOp64(Synchronization sync, AtomicOp op, 5820 Register64 value, const BaseIndex& mem, 5821 Register64 temp, Register64 output) { 5822 AtomicFetchOp64(*this, nullptr, sync, op, value, mem, temp, output); 5823 } 5824 5825 void MacroAssembler::atomicEffectOp64(Synchronization sync, AtomicOp op, 5826 Register64 value, const Address& mem, 5827 Register64 temp) { 5828 AtomicFetchOp64(*this, nullptr, sync, op, value, mem, temp, temp); 5829 } 5830 5831 void MacroAssembler::atomicEffectOp64(Synchronization sync, AtomicOp op, 5832 Register64 value, const BaseIndex& mem, 5833 Register64 temp) { 5834 AtomicFetchOp64(*this, nullptr, sync, op, value, mem, temp, temp); 5835 } 5836 5837 // ======================================================================== 5838 // JS atomic operations. 5839 5840 template <typename T> 5841 static void CompareExchangeJS(MacroAssembler& masm, Scalar::Type arrayType, 5842 Synchronization sync, const T& mem, 5843 Register oldval, Register newval, Register temp, 5844 AnyRegister output) { 5845 if (arrayType == Scalar::Uint32) { 5846 masm.compareExchange(arrayType, sync, mem, oldval, newval, temp); 5847 masm.convertUInt32ToDouble(temp, output.fpu()); 5848 } else { 5849 masm.compareExchange(arrayType, sync, mem, oldval, newval, output.gpr()); 5850 } 5851 } 5852 5853 void MacroAssembler::compareExchangeJS(Scalar::Type arrayType, 5854 Synchronization sync, const Address& mem, 5855 Register oldval, Register newval, 5856 Register temp, AnyRegister output) { 5857 CompareExchangeJS(*this, arrayType, sync, mem, oldval, newval, temp, output); 5858 } 5859 5860 void MacroAssembler::compareExchangeJS(Scalar::Type arrayType, 5861 Synchronization sync, 5862 const BaseIndex& mem, Register oldval, 5863 Register newval, Register temp, 5864 AnyRegister output) { 5865 CompareExchangeJS(*this, arrayType, sync, mem, oldval, newval, temp, output); 5866 } 5867 5868 template <typename T> 5869 static void AtomicExchangeJS(MacroAssembler& masm, Scalar::Type arrayType, 5870 Synchronization sync, const T& mem, Register value, 5871 Register temp, AnyRegister output) { 5872 if (arrayType == Scalar::Uint32) { 5873 masm.atomicExchange(arrayType, sync, mem, value, temp); 5874 masm.convertUInt32ToDouble(temp, output.fpu()); 5875 } else { 5876 masm.atomicExchange(arrayType, sync, mem, value, output.gpr()); 5877 } 5878 } 5879 5880 void MacroAssembler::atomicExchangeJS(Scalar::Type arrayType, 5881 Synchronization sync, const Address& mem, 5882 Register value, Register temp, 5883 AnyRegister output) { 5884 AtomicExchangeJS(*this, arrayType, sync, mem, value, temp, output); 5885 } 5886 5887 void MacroAssembler::atomicExchangeJS(Scalar::Type arrayType, 5888 Synchronization sync, 5889 const BaseIndex& mem, Register value, 5890 Register temp, AnyRegister output) { 5891 AtomicExchangeJS(*this, arrayType, sync, mem, value, temp, output); 5892 } 5893 5894 template <typename T> 5895 static void AtomicFetchOpJS(MacroAssembler& masm, Scalar::Type arrayType, 5896 Synchronization sync, AtomicOp op, Register value, 5897 const T& mem, Register temp1, Register temp2, 5898 AnyRegister output) { 5899 if (arrayType == Scalar::Uint32) { 5900 masm.atomicFetchOp(arrayType, sync, op, value, mem, temp2, temp1); 5901 masm.convertUInt32ToDouble(temp1, output.fpu()); 5902 } else { 5903 masm.atomicFetchOp(arrayType, sync, op, value, mem, temp1, output.gpr()); 5904 } 5905 } 5906 5907 void MacroAssembler::atomicFetchOpJS(Scalar::Type arrayType, 5908 Synchronization sync, AtomicOp op, 5909 Register value, const Address& mem, 5910 Register temp1, Register temp2, 5911 AnyRegister output) { 5912 AtomicFetchOpJS(*this, arrayType, sync, op, value, mem, temp1, temp2, output); 5913 } 5914 5915 void MacroAssembler::atomicFetchOpJS(Scalar::Type arrayType, 5916 Synchronization sync, AtomicOp op, 5917 Register value, const BaseIndex& mem, 5918 Register temp1, Register temp2, 5919 AnyRegister output) { 5920 AtomicFetchOpJS(*this, arrayType, sync, op, value, mem, temp1, temp2, output); 5921 } 5922 5923 void MacroAssembler::atomicEffectOpJS(Scalar::Type arrayType, 5924 Synchronization sync, AtomicOp op, 5925 Register value, const BaseIndex& mem, 5926 Register temp) { 5927 AtomicEffectOp(*this, nullptr, arrayType, sync, op, value, mem, temp); 5928 } 5929 5930 void MacroAssembler::atomicEffectOpJS(Scalar::Type arrayType, 5931 Synchronization sync, AtomicOp op, 5932 Register value, const Address& mem, 5933 Register temp) { 5934 AtomicEffectOp(*this, nullptr, arrayType, sync, op, value, mem, temp); 5935 } 5936 5937 void MacroAssembler::atomicPause() { as_yield(); } 5938 5939 // ======================================================================== 5940 // Primitive atomic operations. 5941 5942 void MacroAssembler::atomicLoad64(Synchronization sync, const Address& mem, 5943 Register64 output) { 5944 AtomicLoad64(*this, nullptr, sync, mem, output); 5945 } 5946 5947 void MacroAssembler::atomicLoad64(Synchronization sync, const BaseIndex& mem, 5948 Register64 output) { 5949 AtomicLoad64(*this, nullptr, sync, mem, output); 5950 } 5951 5952 void MacroAssembler::atomicStore64(Synchronization sync, const Address& mem, 5953 Register64 value, Register64 temp) { 5954 AtomicExchange64(*this, nullptr, sync, mem, value, temp); 5955 } 5956 5957 void MacroAssembler::atomicStore64(Synchronization sync, const BaseIndex& mem, 5958 Register64 value, Register64 temp) { 5959 AtomicExchange64(*this, nullptr, sync, mem, value, temp); 5960 } 5961 5962 // ======================================================================== 5963 // Convert floating point. 5964 5965 bool MacroAssembler::convertUInt64ToDoubleNeedsTemp() { return false; } 5966 5967 void MacroAssembler::convertUInt64ToDouble(Register64 src, FloatRegister dest, 5968 Register temp) { 5969 MOZ_ASSERT(temp == Register::Invalid()); 5970 ScratchDoubleScope scratchDouble(*this); 5971 5972 convertUInt32ToDouble(src.high, dest); 5973 { 5974 ScratchRegisterScope scratch(*this); 5975 movePtr(ImmPtr(&TO_DOUBLE_HIGH_SCALE), scratch); 5976 ma_vldr(Operand(Address(scratch, 0)).toVFPAddr(), scratchDouble); 5977 } 5978 mulDouble(scratchDouble, dest); 5979 convertUInt32ToDouble(src.low, scratchDouble); 5980 addDouble(scratchDouble, dest); 5981 } 5982 5983 void MacroAssembler::convertInt64ToDouble(Register64 src, FloatRegister dest) { 5984 ScratchDoubleScope scratchDouble(*this); 5985 5986 convertInt32ToDouble(src.high, dest); 5987 { 5988 ScratchRegisterScope scratch(*this); 5989 movePtr(ImmPtr(&TO_DOUBLE_HIGH_SCALE), scratch); 5990 ma_vldr(Operand(Address(scratch, 0)).toVFPAddr(), scratchDouble); 5991 } 5992 mulDouble(scratchDouble, dest); 5993 convertUInt32ToDouble(src.low, scratchDouble); 5994 addDouble(scratchDouble, dest); 5995 } 5996 5997 void MacroAssembler::convertIntPtrToDouble(Register src, FloatRegister dest) { 5998 convertInt32ToDouble(src, dest); 5999 } 6000 6001 extern "C" { 6002 extern MOZ_EXPORT int64_t __aeabi_idivmod(int, int); 6003 extern MOZ_EXPORT int64_t __aeabi_uidivmod(int, int); 6004 } 6005 6006 static void EmitRemainderOrQuotient(bool isRemainder, MacroAssembler& masm, 6007 Register lhs, Register rhs, Register output, 6008 bool isUnsigned, 6009 const LiveRegisterSet& volatileLiveRegs) { 6010 if (ARMFlags::HasIDIV()) { 6011 if (isRemainder) { 6012 masm.remainder32(lhs, rhs, output, isUnsigned); 6013 } else { 6014 masm.quotient32(lhs, rhs, output, isUnsigned); 6015 } 6016 } else { 6017 // Ensure that the output registers are saved and restored properly. 6018 LiveRegisterSet liveRegs = volatileLiveRegs; 6019 liveRegs.addUnchecked(ReturnRegVal0); 6020 liveRegs.addUnchecked(ReturnRegVal1); 6021 6022 masm.PushRegsInMask(liveRegs); 6023 6024 using Fn = int64_t (*)(int, int); 6025 { 6026 ScratchRegisterScope scratch(masm); 6027 masm.setupUnalignedABICall(scratch); 6028 } 6029 masm.passABIArg(lhs); 6030 masm.passABIArg(rhs); 6031 if (isUnsigned) { 6032 masm.callWithABI<Fn, __aeabi_uidivmod>( 6033 ABIType::Int64, CheckUnsafeCallWithABI::DontCheckOther); 6034 } else { 6035 masm.callWithABI<Fn, __aeabi_idivmod>( 6036 ABIType::Int64, CheckUnsafeCallWithABI::DontCheckOther); 6037 } 6038 if (isRemainder) { 6039 masm.mov(ReturnRegVal1, output); 6040 } else { 6041 masm.mov(ReturnRegVal0, output); 6042 } 6043 6044 LiveRegisterSet ignore; 6045 ignore.add(output); 6046 masm.PopRegsInMaskIgnore(liveRegs, ignore); 6047 } 6048 } 6049 6050 void MacroAssembler::flexibleQuotient32( 6051 Register lhs, Register rhs, Register dest, bool isUnsigned, 6052 const LiveRegisterSet& volatileLiveRegs) { 6053 EmitRemainderOrQuotient(false, *this, lhs, rhs, dest, isUnsigned, 6054 volatileLiveRegs); 6055 } 6056 6057 void MacroAssembler::flexibleQuotientPtr( 6058 Register lhs, Register rhs, Register dest, bool isUnsigned, 6059 const LiveRegisterSet& volatileLiveRegs) { 6060 flexibleQuotient32(lhs, rhs, dest, isUnsigned, volatileLiveRegs); 6061 } 6062 6063 void MacroAssembler::flexibleRemainder32( 6064 Register lhs, Register rhs, Register dest, bool isUnsigned, 6065 const LiveRegisterSet& volatileLiveRegs) { 6066 EmitRemainderOrQuotient(true, *this, lhs, rhs, dest, isUnsigned, 6067 volatileLiveRegs); 6068 } 6069 6070 void MacroAssembler::flexibleRemainderPtr( 6071 Register lhs, Register rhs, Register dest, bool isUnsigned, 6072 const LiveRegisterSet& volatileLiveRegs) { 6073 flexibleRemainder32(lhs, rhs, dest, isUnsigned, volatileLiveRegs); 6074 } 6075 6076 void MacroAssembler::flexibleDivMod32(Register lhs, Register rhs, 6077 Register divOutput, Register remOutput, 6078 bool isUnsigned, 6079 const LiveRegisterSet& volatileLiveRegs) { 6080 MOZ_ASSERT(lhs != divOutput && lhs != remOutput, "lhs is preserved"); 6081 MOZ_ASSERT(rhs != divOutput && rhs != remOutput, "rhs is preserved"); 6082 6083 if (ARMFlags::HasIDIV()) { 6084 if (isUnsigned) { 6085 as_udiv(divOutput, lhs, rhs); 6086 } else { 6087 as_sdiv(divOutput, lhs, rhs); 6088 } 6089 as_mls(remOutput, lhs, divOutput, rhs); 6090 } else { 6091 // Ensure that the output registers are saved and restored properly. 6092 LiveRegisterSet liveRegs = volatileLiveRegs; 6093 liveRegs.addUnchecked(ReturnRegVal0); 6094 liveRegs.addUnchecked(ReturnRegVal1); 6095 6096 PushRegsInMask(liveRegs); 6097 6098 using Fn = int64_t (*)(int, int); 6099 { 6100 ScratchRegisterScope scratch(*this); 6101 setupUnalignedABICall(scratch); 6102 } 6103 passABIArg(lhs); 6104 passABIArg(rhs); 6105 if (isUnsigned) { 6106 callWithABI<Fn, __aeabi_uidivmod>(ABIType::Int64, 6107 CheckUnsafeCallWithABI::DontCheckOther); 6108 } else { 6109 callWithABI<Fn, __aeabi_idivmod>(ABIType::Int64, 6110 CheckUnsafeCallWithABI::DontCheckOther); 6111 } 6112 moveRegPair(ReturnRegVal0, ReturnRegVal1, divOutput, remOutput); 6113 6114 LiveRegisterSet ignore; 6115 ignore.add(remOutput); 6116 ignore.add(divOutput); 6117 PopRegsInMaskIgnore(liveRegs, ignore); 6118 } 6119 } 6120 6121 CodeOffset MacroAssembler::moveNearAddressWithPatch(Register dest) { 6122 return movWithPatch(ImmPtr(nullptr), dest); 6123 } 6124 6125 void MacroAssembler::patchNearAddressMove(CodeLocationLabel loc, 6126 CodeLocationLabel target) { 6127 PatchDataWithValueCheck(loc, ImmPtr(target.raw()), ImmPtr(nullptr)); 6128 } 6129 6130 // ======================================================================== 6131 // Spectre Mitigations. 6132 6133 void MacroAssembler::speculationBarrier() { 6134 // Spectre mitigation recommended by ARM for cases where csel/cmov cannot be 6135 // used. 6136 as_csdb(); 6137 } 6138 6139 void MacroAssembler::floorFloat32ToInt32(FloatRegister src, Register dest, 6140 Label* fail) { 6141 floorf(src, dest, fail); 6142 } 6143 6144 void MacroAssembler::floorDoubleToInt32(FloatRegister src, Register dest, 6145 Label* fail) { 6146 floor(src, dest, fail); 6147 } 6148 6149 void MacroAssembler::ceilFloat32ToInt32(FloatRegister src, Register dest, 6150 Label* fail) { 6151 ceilf(src, dest, fail); 6152 } 6153 6154 void MacroAssembler::ceilDoubleToInt32(FloatRegister src, Register dest, 6155 Label* fail) { 6156 ceil(src, dest, fail); 6157 } 6158 6159 void MacroAssembler::roundFloat32ToInt32(FloatRegister src, Register dest, 6160 FloatRegister temp, Label* fail) { 6161 roundf(src, dest, fail, temp); 6162 } 6163 6164 void MacroAssembler::roundDoubleToInt32(FloatRegister src, Register dest, 6165 FloatRegister temp, Label* fail) { 6166 round(src, dest, fail, temp); 6167 } 6168 6169 void MacroAssembler::truncFloat32ToInt32(FloatRegister src, Register dest, 6170 Label* fail) { 6171 truncf(src, dest, fail); 6172 } 6173 6174 void MacroAssembler::truncDoubleToInt32(FloatRegister src, Register dest, 6175 Label* fail) { 6176 trunc(src, dest, fail); 6177 } 6178 6179 void MacroAssembler::nearbyIntDouble(RoundingMode mode, FloatRegister src, 6180 FloatRegister dest) { 6181 MOZ_CRASH("not supported on this platform"); 6182 } 6183 6184 void MacroAssembler::nearbyIntFloat32(RoundingMode mode, FloatRegister src, 6185 FloatRegister dest) { 6186 MOZ_CRASH("not supported on this platform"); 6187 } 6188 6189 void MacroAssembler::copySignDouble(FloatRegister lhs, FloatRegister rhs, 6190 FloatRegister output) { 6191 ScratchRegisterScope scratch(*this); 6192 6193 // Transfer top word of |rhs| into GPR. 6194 as_vxfer(scratch, InvalidReg, rhs, Assembler::FloatToCore, Assembler::Always, 6195 1); 6196 6197 // Move |lhs| with sign-bit cleared into |output|. 6198 ma_vabs(lhs, output); 6199 6200 // Negate |output| if sign bit of |rhs| is set. 6201 as_cmp(scratch, Imm8(0)); 6202 ma_vneg(output, output, Assembler::LessThan); 6203 } 6204 6205 void MacroAssembler::copySignFloat32(FloatRegister lhs, FloatRegister rhs, 6206 FloatRegister output) { 6207 ScratchRegisterScope scratch(*this); 6208 6209 // Transfer |rhs| into GPR. 6210 ma_vxfer(rhs, scratch); 6211 6212 // Move |lhs| with sign-bit cleared into |output|. 6213 ma_vabs_f32(lhs, output); 6214 6215 // Negate |output| if sign bit of |rhs| is set. 6216 as_cmp(scratch, Imm8(0)); 6217 ma_vneg_f32(output, output, Assembler::LessThan); 6218 } 6219 6220 void MacroAssembler::shiftIndex32AndAdd(Register indexTemp32, int shift, 6221 Register pointer) { 6222 if (IsShiftInScaleRange(shift)) { 6223 computeEffectiveAddress( 6224 BaseIndex(pointer, indexTemp32, ShiftToScale(shift)), pointer); 6225 return; 6226 } 6227 lshift32(Imm32(shift), indexTemp32); 6228 addPtr(indexTemp32, pointer); 6229 } 6230 6231 void MacroAssembler::wasmMarkCallAsSlow() { ma_and(lr, lr, lr); } 6232 6233 const int32_t SlowCallMarker = 0xe00ee00e; 6234 6235 void MacroAssembler::wasmCheckSlowCallsite(Register ra, Label* notSlow, 6236 Register temp1, Register temp2) { 6237 MOZ_ASSERT(temp1 != temp2); 6238 // Check if RA has slow marker. 6239 load32(Address(ra, 0), temp2); 6240 ma_mov(Imm32(SlowCallMarker), temp1, Always); 6241 ma_cmp(temp2, temp1); 6242 j(Assembler::NotEqual, notSlow); 6243 } 6244 6245 CodeOffset MacroAssembler::wasmMarkedSlowCall(const wasm::CallSiteDesc& desc, 6246 const Register reg) { 6247 AutoForbidPoolsAndNops afp(this, 2); 6248 CodeOffset offset = call(desc, reg); 6249 wasmMarkCallAsSlow(); 6250 return offset; 6251 } 6252 6253 //}}} check_macroassembler_style 6254 6255 void MacroAssemblerARM::wasmTruncateToInt32(FloatRegister input, 6256 Register output, MIRType fromType, 6257 bool isUnsigned, bool isSaturating, 6258 Label* oolEntry) { 6259 ScratchDoubleScope scratchScope(asMasm()); 6260 ScratchRegisterScope scratchReg(asMasm()); 6261 FloatRegister scratch = scratchScope.uintOverlay(); 6262 6263 // ARM conversion instructions clamp the value to ensure it fits within the 6264 // target's type bounds, so every time we see those, we need to check the 6265 // input. A NaN check is not necessary because NaN is converted to zero and 6266 // on a zero result we branch out of line to do further processing anyway. 6267 if (isUnsigned) { 6268 if (fromType == MIRType::Double) { 6269 ma_vcvt_F64_U32(input, scratch); 6270 } else if (fromType == MIRType::Float32) { 6271 ma_vcvt_F32_U32(input, scratch); 6272 } else { 6273 MOZ_CRASH("unexpected type in visitWasmTruncateToInt32"); 6274 } 6275 6276 ma_vxfer(scratch, output); 6277 6278 if (!isSaturating) { 6279 // int32_t(UINT32_MAX) == -1. 6280 ma_cmp(output, Imm32(-1), scratchReg); 6281 as_cmp(output, Imm8(0), Assembler::NotEqual); 6282 ma_b(oolEntry, Assembler::Equal); 6283 } 6284 6285 return; 6286 } 6287 6288 // vcvt* converts NaN into 0, so check for NaNs here. 6289 if (!isSaturating) { 6290 if (fromType == MIRType::Double) { 6291 asMasm().compareDouble(input, input); 6292 } else if (fromType == MIRType::Float32) { 6293 asMasm().compareFloat(input, input); 6294 } else { 6295 MOZ_CRASH("unexpected type in visitWasmTruncateToInt32"); 6296 } 6297 6298 ma_b(oolEntry, Assembler::VFP_Unordered); 6299 } 6300 6301 scratch = scratchScope.sintOverlay(); 6302 6303 if (fromType == MIRType::Double) { 6304 ma_vcvt_F64_I32(input, scratch); 6305 } else if (fromType == MIRType::Float32) { 6306 ma_vcvt_F32_I32(input, scratch); 6307 } else { 6308 MOZ_CRASH("unexpected type in visitWasmTruncateToInt32"); 6309 } 6310 6311 ma_vxfer(scratch, output); 6312 6313 if (!isSaturating) { 6314 ma_cmp(output, Imm32(INT32_MAX), scratchReg); 6315 ma_cmp(output, Imm32(INT32_MIN), scratchReg, Assembler::NotEqual); 6316 ma_b(oolEntry, Assembler::Equal); 6317 } 6318 } 6319 6320 void MacroAssemblerARM::outOfLineWasmTruncateToIntCheck( 6321 FloatRegister input, MIRType fromType, MIRType toType, TruncFlags flags, 6322 Label* rejoin, const wasm::TrapSiteDesc& trapSiteDesc) { 6323 // On ARM, saturating truncation codegen handles saturating itself rather 6324 // than relying on out-of-line fixup code. 6325 if (flags & TRUNC_SATURATING) { 6326 return; 6327 } 6328 6329 bool isUnsigned = flags & TRUNC_UNSIGNED; 6330 ScratchDoubleScope scratchScope(asMasm()); 6331 FloatRegister scratch; 6332 6333 // Eagerly take care of NaNs. 6334 Label inputIsNaN; 6335 if (fromType == MIRType::Double) { 6336 asMasm().branchDouble(Assembler::DoubleUnordered, input, input, 6337 &inputIsNaN); 6338 } else if (fromType == MIRType::Float32) { 6339 asMasm().branchFloat(Assembler::DoubleUnordered, input, input, &inputIsNaN); 6340 } else { 6341 MOZ_CRASH("unexpected type in visitOutOfLineWasmTruncateCheck"); 6342 } 6343 6344 // Handle special values. 6345 Label fail; 6346 6347 // By default test for the following inputs and bail: 6348 // signed: ] -Inf, INTXX_MIN - 1.0 ] and [ INTXX_MAX + 1.0 : +Inf [ 6349 // unsigned: ] -Inf, -1.0 ] and [ UINTXX_MAX + 1.0 : +Inf [ 6350 // Note: we cannot always represent those exact values. As a result 6351 // this changes the actual comparison a bit. 6352 double minValue, maxValue; 6353 Assembler::DoubleCondition minCond = Assembler::DoubleLessThanOrEqual; 6354 Assembler::DoubleCondition maxCond = Assembler::DoubleGreaterThanOrEqual; 6355 if (toType == MIRType::Int64) { 6356 if (isUnsigned) { 6357 minValue = -1; 6358 maxValue = double(UINT64_MAX) + 1.0; 6359 } else { 6360 // In the float32/double range there exists no value between 6361 // INT64_MIN and INT64_MIN - 1.0. Making INT64_MIN the lower-bound. 6362 minValue = double(INT64_MIN); 6363 minCond = Assembler::DoubleLessThan; 6364 maxValue = double(INT64_MAX) + 1.0; 6365 } 6366 } else { 6367 if (isUnsigned) { 6368 minValue = -1; 6369 maxValue = double(UINT32_MAX) + 1.0; 6370 } else { 6371 if (fromType == MIRType::Float32) { 6372 // In the float32 range there exists no value between 6373 // INT32_MIN and INT32_MIN - 1.0. Making INT32_MIN the lower-bound. 6374 minValue = double(INT32_MIN); 6375 minCond = Assembler::DoubleLessThan; 6376 } else { 6377 minValue = double(INT32_MIN) - 1.0; 6378 } 6379 maxValue = double(INT32_MAX) + 1.0; 6380 } 6381 } 6382 6383 if (fromType == MIRType::Double) { 6384 scratch = scratchScope.doubleOverlay(); 6385 asMasm().loadConstantDouble(minValue, scratch); 6386 asMasm().branchDouble(minCond, input, scratch, &fail); 6387 6388 asMasm().loadConstantDouble(maxValue, scratch); 6389 asMasm().branchDouble(maxCond, input, scratch, &fail); 6390 } else { 6391 MOZ_ASSERT(fromType == MIRType::Float32); 6392 scratch = scratchScope.singleOverlay(); 6393 asMasm().loadConstantFloat32(float(minValue), scratch); 6394 asMasm().branchFloat(minCond, input, scratch, &fail); 6395 6396 asMasm().loadConstantFloat32(float(maxValue), scratch); 6397 asMasm().branchFloat(maxCond, input, scratch, &fail); 6398 } 6399 6400 // We had an actual correct value, get back to where we were. 6401 ma_b(rejoin); 6402 6403 // Handle errors. 6404 bind(&fail); 6405 asMasm().wasmTrap(wasm::Trap::IntegerOverflow, trapSiteDesc); 6406 6407 bind(&inputIsNaN); 6408 asMasm().wasmTrap(wasm::Trap::InvalidConversionToInteger, trapSiteDesc); 6409 } 6410 6411 void MacroAssemblerARM::wasmLoadImpl(const wasm::MemoryAccessDesc& access, 6412 Register memoryBase, Register ptr, 6413 Register ptrScratch, AnyRegister output, 6414 Register64 out64) { 6415 MOZ_ASSERT(memoryBase != ptr); 6416 MOZ_ASSERT_IF(out64 != Register64::Invalid(), memoryBase != out64.high); 6417 MOZ_ASSERT_IF(out64 != Register64::Invalid(), memoryBase != out64.low); 6418 MOZ_ASSERT(ptr == ptrScratch); 6419 MOZ_ASSERT(!access.isZeroExtendSimd128Load()); 6420 MOZ_ASSERT(!access.isSplatSimd128Load()); 6421 MOZ_ASSERT(!access.isWidenSimd128Load()); 6422 6423 access.assertOffsetInGuardPages(); 6424 uint32_t offset = access.offset32(); 6425 6426 Scalar::Type type = access.type(); 6427 6428 // Maybe add the offset. 6429 if (offset || type == Scalar::Int64) { 6430 ScratchRegisterScope scratch(asMasm()); 6431 if (offset) { 6432 ma_add(Imm32(offset), ptr, scratch); 6433 } 6434 } 6435 6436 bool isSigned = Scalar::isSignedIntType(type); 6437 unsigned byteSize = access.byteSize(); 6438 6439 // NOTE: the generated code must match the assembly code in gen_load in 6440 // GenerateAtomicOperations.py 6441 asMasm().memoryBarrierBefore(access.sync()); 6442 6443 BufferOffset load; 6444 if (out64 != Register64::Invalid()) { 6445 if (type == Scalar::Int64) { 6446 static_assert(INT64LOW_OFFSET == 0); 6447 6448 load = ma_dataTransferN(IsLoad, 32, /* signed = */ false, memoryBase, ptr, 6449 out64.low); 6450 append(access, js::wasm::TrapMachineInsn::Load32, 6451 FaultingCodeOffset(load.getOffset())); 6452 6453 as_add(ptr, ptr, Imm8(INT64HIGH_OFFSET)); 6454 6455 load = 6456 ma_dataTransferN(IsLoad, 32, isSigned, memoryBase, ptr, out64.high); 6457 append(access, js::wasm::TrapMachineInsn::Load32, 6458 FaultingCodeOffset(load.getOffset())); 6459 } else { 6460 load = ma_dataTransferN(IsLoad, byteSize * 8, isSigned, memoryBase, ptr, 6461 out64.low); 6462 append(access, js::wasm::TrapMachineInsnForLoad(byteSize), 6463 FaultingCodeOffset(load.getOffset())); 6464 6465 if (isSigned) { 6466 ma_asr(Imm32(31), out64.low, out64.high); 6467 } else { 6468 ma_mov(Imm32(0), out64.high); 6469 } 6470 } 6471 } else { 6472 bool isFloat = output.isFloat(); 6473 if (isFloat) { 6474 MOZ_ASSERT((byteSize == 4) == output.fpu().isSingle()); 6475 ScratchRegisterScope scratch(asMasm()); 6476 FloatRegister dest = output.fpu(); 6477 ma_add(memoryBase, ptr, scratch); 6478 6479 // FP loads can't use VLDR as that has stringent alignment checks and will 6480 // SIGBUS on unaligned accesses. Choose a different strategy depending on 6481 // the available hardware. We don't gate Wasm on the presence of NEON. 6482 if (ARMFlags::HasNEON()) { 6483 // NEON available: The VLD1 multiple-single-elements variant will only 6484 // trap if SCTRL.A==1, but we already assume (for integer accesses) that 6485 // the hardware/OS handles that transparently. 6486 // 6487 // An additional complication is that if we're targeting the high single 6488 // then an unaligned load is not possible, and we may need to go via the 6489 // FPR scratch. 6490 if (byteSize == 4 && dest.code() & 1) { 6491 ScratchFloat32Scope fscratch(asMasm()); 6492 load = as_vldr_unaligned(fscratch, scratch); 6493 as_vmov(dest, fscratch); 6494 } else { 6495 load = as_vldr_unaligned(dest, scratch); 6496 } 6497 append(access, js::wasm::TrapMachineInsnForLoad(byteSize), 6498 FaultingCodeOffset(load.getOffset())); 6499 } else { 6500 // NEON not available: Load to GPR scratch, move to FPR destination. We 6501 // don't have adjacent scratches for the f64, so use individual LDRs, 6502 // not LDRD. 6503 SecondScratchRegisterScope scratch2(asMasm()); 6504 if (byteSize == 4) { 6505 load = as_dtr(IsLoad, 32, Offset, scratch2, 6506 DTRAddr(scratch, DtrOffImm(0)), Always); 6507 as_vxfer(scratch2, InvalidReg, VFPRegister(dest), CoreToFloat, 6508 Always); 6509 append(access, js::wasm::TrapMachineInsn::Load32, 6510 FaultingCodeOffset(load.getOffset())); 6511 } else { 6512 // The trap information is associated with the load of the high word, 6513 // which must be done first. FIXME sewardj 20230825: is it still 6514 // safe to skip the low word, now that we support wasm-gc? 6515 load = as_dtr(IsLoad, 32, Offset, scratch2, 6516 DTRAddr(scratch, DtrOffImm(4)), Always); 6517 append(access, js::wasm::TrapMachineInsn::Load32, 6518 FaultingCodeOffset(load.getOffset())); 6519 as_dtr(IsLoad, 32, Offset, scratch, DTRAddr(scratch, DtrOffImm(0)), 6520 Always); 6521 as_vxfer(scratch, scratch2, VFPRegister(dest), CoreToFloat, Always); 6522 } 6523 } 6524 } else { 6525 load = ma_dataTransferN(IsLoad, byteSize * 8, isSigned, memoryBase, ptr, 6526 output.gpr()); 6527 append(access, js::wasm::TrapMachineInsnForLoad(byteSize), 6528 FaultingCodeOffset(load.getOffset())); 6529 } 6530 } 6531 6532 asMasm().memoryBarrierAfter(access.sync()); 6533 } 6534 6535 void MacroAssemblerARM::wasmStoreImpl(const wasm::MemoryAccessDesc& access, 6536 AnyRegister value, Register64 val64, 6537 Register memoryBase, Register ptr, 6538 Register ptrScratch) { 6539 static_assert(INT64LOW_OFFSET == 0); 6540 static_assert(INT64HIGH_OFFSET == 4); 6541 6542 MOZ_ASSERT(ptr == ptrScratch); 6543 6544 access.assertOffsetInGuardPages(); 6545 uint32_t offset = access.offset32(); 6546 unsigned byteSize = access.byteSize(); 6547 Scalar::Type type = access.type(); 6548 6549 // Maybe add the offset. 6550 if (offset || type == Scalar::Int64) { 6551 ScratchRegisterScope scratch(asMasm()); 6552 // We need to store the high word of an Int64 first, so always adjust the 6553 // pointer to point to the high word in this case. The adjustment is always 6554 // OK because wasmMaxOffsetGuardLimit is computed so that we can add up to 6555 // sizeof(LargestValue)-1 without skipping past the guard page, and we 6556 // assert above that offset < wasmMaxOffsetGuardLimit. 6557 if (type == Scalar::Int64) { 6558 offset += INT64HIGH_OFFSET; 6559 } 6560 if (offset) { 6561 ma_add(Imm32(offset), ptr, scratch); 6562 } 6563 } 6564 6565 // NOTE: the generated code must match the assembly code in gen_store in 6566 // GenerateAtomicOperations.py 6567 asMasm().memoryBarrierBefore(access.sync()); 6568 6569 BufferOffset store; 6570 if (type == Scalar::Int64) { 6571 store = ma_dataTransferN(IsStore, 32 /* bits */, /* signed */ false, 6572 memoryBase, ptr, val64.high); 6573 append(access, js::wasm::TrapMachineInsn::Store32, 6574 FaultingCodeOffset(store.getOffset())); 6575 6576 as_sub(ptr, ptr, Imm8(INT64HIGH_OFFSET)); 6577 6578 store = ma_dataTransferN(IsStore, 32 /* bits */, /* signed */ true, 6579 memoryBase, ptr, val64.low); 6580 append(access, js::wasm::TrapMachineInsn::Store32, 6581 FaultingCodeOffset(store.getOffset())); 6582 } else { 6583 if (value.isFloat()) { 6584 ScratchRegisterScope scratch(asMasm()); 6585 FloatRegister val = value.fpu(); 6586 MOZ_ASSERT((byteSize == 4) == val.isSingle()); 6587 ma_add(memoryBase, ptr, scratch); 6588 6589 // See comments above at wasmLoadImpl for more about this logic. 6590 if (ARMFlags::HasNEON()) { 6591 if (byteSize == 4 && (val.code() & 1)) { 6592 ScratchFloat32Scope fscratch(asMasm()); 6593 as_vmov(fscratch, val); 6594 store = as_vstr_unaligned(fscratch, scratch); 6595 } else { 6596 store = as_vstr_unaligned(val, scratch); 6597 } 6598 append(access, js::wasm::TrapMachineInsnForStore(byteSize), 6599 FaultingCodeOffset(store.getOffset())); 6600 } else { 6601 // NEON not available: Move FPR to GPR scratch, store GPR. We have only 6602 // one scratch to hold the value, so for f64 we must do two separate 6603 // moves. That's OK - this is really a corner case. If we really cared 6604 // we would pass in a temp to avoid the second move. 6605 SecondScratchRegisterScope scratch2(asMasm()); 6606 if (byteSize == 4) { 6607 as_vxfer(scratch2, InvalidReg, VFPRegister(val), FloatToCore, Always); 6608 store = as_dtr(IsStore, 32, Offset, scratch2, 6609 DTRAddr(scratch, DtrOffImm(0)), Always); 6610 append(access, js::wasm::TrapMachineInsn::Store32, 6611 FaultingCodeOffset(store.getOffset())); 6612 } else { 6613 // The trap information is associated with the store of the high word, 6614 // which must be done first. FIXME sewardj 20230825: is it still 6615 // safe to skip the low word, now that we support wasm-gc? 6616 as_vxfer(scratch2, InvalidReg, VFPRegister(val).singleOverlay(1), 6617 FloatToCore, Always); 6618 store = as_dtr(IsStore, 32, Offset, scratch2, 6619 DTRAddr(scratch, DtrOffImm(4)), Always); 6620 append(access, js::wasm::TrapMachineInsn::Store32, 6621 FaultingCodeOffset(store.getOffset())); 6622 as_vxfer(scratch2, InvalidReg, VFPRegister(val).singleOverlay(0), 6623 FloatToCore, Always); 6624 as_dtr(IsStore, 32, Offset, scratch2, DTRAddr(scratch, DtrOffImm(0)), 6625 Always); 6626 } 6627 } 6628 } else { 6629 bool isSigned = type == Scalar::Uint32 || 6630 type == Scalar::Int32; // see AsmJSStoreHeap; 6631 Register val = value.gpr(); 6632 6633 store = ma_dataTransferN(IsStore, 8 * byteSize /* bits */, isSigned, 6634 memoryBase, ptr, val); 6635 append(access, js::wasm::TrapMachineInsnForStore(byteSize), 6636 FaultingCodeOffset(store.getOffset())); 6637 } 6638 } 6639 6640 asMasm().memoryBarrierAfter(access.sync()); 6641 }