MacroAssembler-vixl.cpp (67802B)
1 // Copyright 2015, ARM Limited 2 // All rights reserved. 3 // 4 // Redistribution and use in source and binary forms, with or without 5 // modification, are permitted provided that the following conditions are met: 6 // 7 // * Redistributions of source code must retain the above copyright notice, 8 // this list of conditions and the following disclaimer. 9 // * Redistributions in binary form must reproduce the above copyright notice, 10 // this list of conditions and the following disclaimer in the documentation 11 // and/or other materials provided with the distribution. 12 // * Neither the name of ARM Limited nor the names of its contributors may be 13 // used to endorse or promote products derived from this software without 14 // specific prior written permission. 15 // 16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND 17 // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE 20 // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 22 // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 23 // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 25 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 27 #include "jit/arm64/vixl/MacroAssembler-vixl.h" 28 29 #include <ctype.h> 30 #include <limits> 31 32 #include "jit/RegisterSets.h" 33 34 namespace vixl { 35 36 MacroAssembler::MacroAssembler() 37 : js::jit::Assembler(), 38 sp_(x28), 39 tmp_list_(ip0, ip1), 40 fptmp_list_(d31) 41 { 42 } 43 44 45 void MacroAssembler::FinalizeCode() { 46 Assembler::FinalizeCode(); 47 } 48 49 50 int MacroAssembler::MoveImmediateHelper(MacroAssembler* masm, 51 const Register &rd, 52 uint64_t imm) { 53 bool emit_code = (masm != NULL); 54 VIXL_ASSERT(IsUint32(imm) || IsInt32(imm) || rd.Is64Bits()); 55 // The worst case for size is mov 64-bit immediate to sp: 56 // * up to 4 instructions to materialise the constant 57 // * 1 instruction to move to sp 58 MacroEmissionCheckScope guard(masm); 59 60 // Immediates on Aarch64 can be produced using an initial value, and zero to 61 // three move keep operations. 62 // 63 // Initial values can be generated with: 64 // 1. 64-bit move zero (movz). 65 // 2. 32-bit move inverted (movn). 66 // 3. 64-bit move inverted. 67 // 4. 32-bit orr immediate. 68 // 5. 64-bit orr immediate. 69 // Move-keep may then be used to modify each of the 16-bit half words. 70 // 71 // The code below supports all five initial value generators, and 72 // applying move-keep operations to move-zero and move-inverted initial 73 // values. 74 75 // Try to move the immediate in one instruction, and if that fails, switch to 76 // using multiple instructions. 77 if (OneInstrMoveImmediateHelper(masm, rd, imm)) { 78 return 1; 79 } else { 80 int instruction_count = 0; 81 unsigned reg_size = rd.size(); 82 83 // Generic immediate case. Imm will be represented by 84 // [imm3, imm2, imm1, imm0], where each imm is 16 bits. 85 // A move-zero or move-inverted is generated for the first non-zero or 86 // non-0xffff immX, and a move-keep for subsequent non-zero immX. 87 88 uint64_t ignored_halfword = 0; 89 bool invert_move = false; 90 // If the number of 0xffff halfwords is greater than the number of 0x0000 91 // halfwords, it's more efficient to use move-inverted. 92 if (CountClearHalfWords(~imm, reg_size) > 93 CountClearHalfWords(imm, reg_size)) { 94 ignored_halfword = 0xffff; 95 invert_move = true; 96 } 97 98 // Mov instructions can't move values into the stack pointer, so set up a 99 // temporary register, if needed. 100 UseScratchRegisterScope temps; 101 Register temp; 102 if (emit_code) { 103 temps.Open(masm); 104 temp = rd.IsSP() ? temps.AcquireSameSizeAs(rd) : rd; 105 } 106 107 // Iterate through the halfwords. Use movn/movz for the first non-ignored 108 // halfword, and movk for subsequent halfwords. 109 VIXL_ASSERT((reg_size % 16) == 0); 110 bool first_mov_done = false; 111 for (unsigned i = 0; i < (temp.size() / 16); i++) { 112 uint64_t imm16 = (imm >> (16 * i)) & 0xffff; 113 if (imm16 != ignored_halfword) { 114 if (!first_mov_done) { 115 if (invert_move) { 116 if (emit_code) masm->movn(temp, ~imm16 & 0xffff, 16 * i); 117 instruction_count++; 118 } else { 119 if (emit_code) masm->movz(temp, imm16, 16 * i); 120 instruction_count++; 121 } 122 first_mov_done = true; 123 } else { 124 // Construct a wider constant. 125 if (emit_code) masm->movk(temp, imm16, 16 * i); 126 instruction_count++; 127 } 128 } 129 } 130 131 VIXL_ASSERT(first_mov_done); 132 133 // Move the temporary if the original destination register was the stack 134 // pointer. 135 if (rd.IsSP()) { 136 if (emit_code) masm->mov(rd, temp); 137 instruction_count++; 138 } 139 return instruction_count; 140 } 141 } 142 143 144 bool MacroAssembler::OneInstrMoveImmediateHelper(MacroAssembler* masm, 145 const Register& dst, 146 int64_t imm) { 147 bool emit_code = masm != NULL; 148 unsigned n, imm_s, imm_r; 149 int reg_size = dst.size(); 150 151 if (IsImmMovz(imm, reg_size) && !dst.IsSP()) { 152 // Immediate can be represented in a move zero instruction. Movz can't write 153 // to the stack pointer. 154 if (emit_code) { 155 masm->movz(dst, imm); 156 } 157 return true; 158 } else if (IsImmMovn(imm, reg_size) && !dst.IsSP()) { 159 // Immediate can be represented in a move negative instruction. Movn can't 160 // write to the stack pointer. 161 if (emit_code) { 162 masm->movn(dst, dst.Is64Bits() ? ~imm : (~imm & kWRegMask)); 163 } 164 return true; 165 } else if (IsImmLogical(imm, reg_size, &n, &imm_s, &imm_r)) { 166 // Immediate can be represented in a logical orr instruction. 167 VIXL_ASSERT(!dst.IsZero()); 168 if (emit_code) { 169 masm->LogicalImmediate( 170 dst, AppropriateZeroRegFor(dst), n, imm_s, imm_r, ORR); 171 } 172 return true; 173 } 174 return false; 175 } 176 177 178 void MacroAssembler::B(Label* label, BranchType type, Register reg, int bit) { 179 VIXL_ASSERT((reg.Is(NoReg) || (type >= kBranchTypeFirstUsingReg)) && 180 ((bit == -1) || (type >= kBranchTypeFirstUsingBit))); 181 if (kBranchTypeFirstCondition <= type && type <= kBranchTypeLastCondition) { 182 B(static_cast<Condition>(type), label); 183 } else { 184 switch (type) { 185 case always: B(label); break; 186 case never: break; 187 case reg_zero: Cbz(reg, label); break; 188 case reg_not_zero: Cbnz(reg, label); break; 189 case reg_bit_clear: Tbz(reg, bit, label); break; 190 case reg_bit_set: Tbnz(reg, bit, label); break; 191 default: 192 VIXL_UNREACHABLE(); 193 } 194 } 195 } 196 197 198 void MacroAssembler::B(Label* label) { 199 SingleEmissionCheckScope guard(this); 200 b(label); 201 } 202 203 204 void MacroAssembler::B(Label* label, Condition cond) { 205 VIXL_ASSERT((cond != al) && (cond != nv)); 206 EmissionCheckScope guard(this, 2 * kInstructionSize); 207 208 if (label->bound() && LabelIsOutOfRange(label, CondBranchType)) { 209 Label done; 210 b(&done, InvertCondition(cond)); 211 b(label); 212 bind(&done); 213 } else { 214 b(label, cond); 215 } 216 } 217 218 219 void MacroAssembler::Cbnz(const Register& rt, Label* label) { 220 VIXL_ASSERT(!rt.IsZero()); 221 EmissionCheckScope guard(this, 2 * kInstructionSize); 222 223 if (label->bound() && LabelIsOutOfRange(label, CondBranchType)) { 224 Label done; 225 cbz(rt, &done); 226 b(label); 227 bind(&done); 228 } else { 229 cbnz(rt, label); 230 } 231 } 232 233 234 void MacroAssembler::Cbz(const Register& rt, Label* label) { 235 VIXL_ASSERT(!rt.IsZero()); 236 EmissionCheckScope guard(this, 2 * kInstructionSize); 237 238 if (label->bound() && LabelIsOutOfRange(label, CondBranchType)) { 239 Label done; 240 cbnz(rt, &done); 241 b(label); 242 bind(&done); 243 } else { 244 cbz(rt, label); 245 } 246 } 247 248 249 void MacroAssembler::Tbnz(const Register& rt, unsigned bit_pos, Label* label) { 250 VIXL_ASSERT(!rt.IsZero()); 251 EmissionCheckScope guard(this, 2 * kInstructionSize); 252 253 if (label->bound() && LabelIsOutOfRange(label, TestBranchType)) { 254 Label done; 255 tbz(rt, bit_pos, &done); 256 b(label); 257 bind(&done); 258 } else { 259 tbnz(rt, bit_pos, label); 260 } 261 } 262 263 264 void MacroAssembler::Tbz(const Register& rt, unsigned bit_pos, Label* label) { 265 VIXL_ASSERT(!rt.IsZero()); 266 EmissionCheckScope guard(this, 2 * kInstructionSize); 267 268 if (label->bound() && LabelIsOutOfRange(label, TestBranchType)) { 269 Label done; 270 tbnz(rt, bit_pos, &done); 271 b(label); 272 bind(&done); 273 } else { 274 tbz(rt, bit_pos, label); 275 } 276 } 277 278 279 void MacroAssembler::And(const Register& rd, 280 const Register& rn, 281 const Operand& operand) { 282 LogicalMacro(rd, rn, operand, AND); 283 } 284 285 286 void MacroAssembler::Ands(const Register& rd, 287 const Register& rn, 288 const Operand& operand) { 289 LogicalMacro(rd, rn, operand, ANDS); 290 } 291 292 293 void MacroAssembler::Tst(const Register& rn, 294 const Operand& operand) { 295 Ands(AppropriateZeroRegFor(rn), rn, operand); 296 } 297 298 299 void MacroAssembler::Bic(const Register& rd, 300 const Register& rn, 301 const Operand& operand) { 302 LogicalMacro(rd, rn, operand, BIC); 303 } 304 305 306 void MacroAssembler::Bics(const Register& rd, 307 const Register& rn, 308 const Operand& operand) { 309 LogicalMacro(rd, rn, operand, BICS); 310 } 311 312 313 void MacroAssembler::Orr(const Register& rd, 314 const Register& rn, 315 const Operand& operand) { 316 LogicalMacro(rd, rn, operand, ORR); 317 } 318 319 320 void MacroAssembler::Orn(const Register& rd, 321 const Register& rn, 322 const Operand& operand) { 323 LogicalMacro(rd, rn, operand, ORN); 324 } 325 326 327 void MacroAssembler::Eor(const Register& rd, 328 const Register& rn, 329 const Operand& operand) { 330 LogicalMacro(rd, rn, operand, EOR); 331 } 332 333 334 void MacroAssembler::Eon(const Register& rd, 335 const Register& rn, 336 const Operand& operand) { 337 LogicalMacro(rd, rn, operand, EON); 338 } 339 340 341 void MacroAssembler::LogicalMacro(const Register& rd, 342 const Register& rn, 343 const Operand& operand, 344 LogicalOp op) { 345 // The worst case for size is logical immediate to sp: 346 // * up to 4 instructions to materialise the constant 347 // * 1 instruction to do the operation 348 // * 1 instruction to move to sp 349 MacroEmissionCheckScope guard(this); 350 UseScratchRegisterScope temps(this); 351 352 if (operand.IsImmediate()) { 353 int64_t immediate = operand.immediate(); 354 unsigned reg_size = rd.size(); 355 356 // If the operation is NOT, invert the operation and immediate. 357 if ((op & NOT) == NOT) { 358 op = static_cast<LogicalOp>(op & ~NOT); 359 immediate = ~immediate; 360 } 361 362 // Ignore the top 32 bits of an immediate if we're moving to a W register. 363 if (rd.Is32Bits()) { 364 // Check that the top 32 bits are consistent. 365 VIXL_ASSERT(((immediate >> kWRegSize) == 0) || 366 ((immediate >> kWRegSize) == -1)); 367 immediate &= kWRegMask; 368 } 369 370 VIXL_ASSERT(rd.Is64Bits() || IsUint32(immediate)); 371 372 // Special cases for all set or all clear immediates. 373 if (immediate == 0) { 374 switch (op) { 375 case AND: 376 Mov(rd, 0); 377 return; 378 case ORR: 379 VIXL_FALLTHROUGH(); 380 case EOR: 381 Mov(rd, rn); 382 return; 383 case ANDS: 384 VIXL_FALLTHROUGH(); 385 case BICS: 386 break; 387 default: 388 VIXL_UNREACHABLE(); 389 } 390 } else if ((rd.Is64Bits() && (immediate == -1)) || 391 (rd.Is32Bits() && (immediate == 0xffffffff))) { 392 switch (op) { 393 case AND: 394 Mov(rd, rn); 395 return; 396 case ORR: 397 Mov(rd, immediate); 398 return; 399 case EOR: 400 Mvn(rd, rn); 401 return; 402 case ANDS: 403 VIXL_FALLTHROUGH(); 404 case BICS: 405 break; 406 default: 407 VIXL_UNREACHABLE(); 408 } 409 } 410 411 unsigned n, imm_s, imm_r; 412 if (IsImmLogical(immediate, reg_size, &n, &imm_s, &imm_r)) { 413 // Immediate can be encoded in the instruction. 414 LogicalImmediate(rd, rn, n, imm_s, imm_r, op); 415 } else { 416 // Immediate can't be encoded: synthesize using move immediate. 417 Register temp = temps.AcquireSameSizeAs(rn); 418 419 // If the left-hand input is the stack pointer, we can't pre-shift the 420 // immediate, as the encoding won't allow the subsequent post shift. 421 PreShiftImmMode mode = rn.IsSP() ? kNoShift : kAnyShift; 422 Operand imm_operand = MoveImmediateForShiftedOp(temp, immediate, mode); 423 424 // VIXL can acquire temp registers. Assert that the caller is aware. 425 VIXL_ASSERT(!temp.Is(rd) && !temp.Is(rn)); 426 VIXL_ASSERT(!temp.Is(operand.maybeReg())); 427 428 if (rd.Is(sp)) { 429 // If rd is the stack pointer we cannot use it as the destination 430 // register so we use the temp register as an intermediate again. 431 Logical(temp, rn, imm_operand, op); 432 Mov(sp, temp); 433 } else { 434 Logical(rd, rn, imm_operand, op); 435 } 436 } 437 } else if (operand.IsExtendedRegister()) { 438 VIXL_ASSERT(operand.reg().size() <= rd.size()); 439 // Add/sub extended supports shift <= 4. We want to support exactly the 440 // same modes here. 441 VIXL_ASSERT(operand.shift_amount() <= 4); 442 VIXL_ASSERT(operand.reg().Is64Bits() || 443 ((operand.extend() != UXTX) && (operand.extend() != SXTX))); 444 445 temps.Exclude(operand.reg()); 446 Register temp = temps.AcquireSameSizeAs(rn); 447 448 // VIXL can acquire temp registers. Assert that the caller is aware. 449 VIXL_ASSERT(!temp.Is(rd) && !temp.Is(rn)); 450 VIXL_ASSERT(!temp.Is(operand.maybeReg())); 451 452 EmitExtendShift(temp, operand.reg(), operand.extend(), 453 operand.shift_amount()); 454 Logical(rd, rn, Operand(temp), op); 455 } else { 456 // The operand can be encoded in the instruction. 457 VIXL_ASSERT(operand.IsShiftedRegister()); 458 Logical(rd, rn, operand, op); 459 } 460 } 461 462 463 void MacroAssembler::Mov(const Register& rd, 464 const Operand& operand, 465 DiscardMoveMode discard_mode) { 466 // The worst case for size is mov immediate with up to 4 instructions. 467 MacroEmissionCheckScope guard(this); 468 469 if (operand.IsImmediate()) { 470 // Call the macro assembler for generic immediates. 471 Mov(rd, operand.immediate()); 472 } else if (operand.IsShiftedRegister() && (operand.shift_amount() != 0)) { 473 // Emit a shift instruction if moving a shifted register. This operation 474 // could also be achieved using an orr instruction (like orn used by Mvn), 475 // but using a shift instruction makes the disassembly clearer. 476 EmitShift(rd, operand.reg(), operand.shift(), operand.shift_amount()); 477 } else if (operand.IsExtendedRegister()) { 478 // Emit an extend instruction if moving an extended register. This handles 479 // extend with post-shift operations, too. 480 EmitExtendShift(rd, operand.reg(), operand.extend(), 481 operand.shift_amount()); 482 } else { 483 Mov(rd, operand.reg(), discard_mode); 484 } 485 } 486 487 488 void MacroAssembler::Movi16bitHelper(const VRegister& vd, uint64_t imm) { 489 VIXL_ASSERT(IsUint16(imm)); 490 int byte1 = (imm & 0xff); 491 int byte2 = ((imm >> 8) & 0xff); 492 if (byte1 == byte2) { 493 movi(vd.Is64Bits() ? vd.V8B() : vd.V16B(), byte1); 494 } else if (byte1 == 0) { 495 movi(vd, byte2, LSL, 8); 496 } else if (byte2 == 0) { 497 movi(vd, byte1); 498 } else if (byte1 == 0xff) { 499 mvni(vd, ~byte2 & 0xff, LSL, 8); 500 } else if (byte2 == 0xff) { 501 mvni(vd, ~byte1 & 0xff); 502 } else { 503 UseScratchRegisterScope temps(this); 504 Register temp = temps.AcquireW(); 505 movz(temp, imm); 506 dup(vd, temp); 507 } 508 } 509 510 511 void MacroAssembler::Movi32bitHelper(const VRegister& vd, uint64_t imm) { 512 VIXL_ASSERT(IsUint32(imm)); 513 514 uint8_t bytes[sizeof(imm)]; 515 memcpy(bytes, &imm, sizeof(imm)); 516 517 // All bytes are either 0x00 or 0xff. 518 { 519 bool all0orff = true; 520 for (int i = 0; i < 4; ++i) { 521 if ((bytes[i] != 0) && (bytes[i] != 0xff)) { 522 all0orff = false; 523 break; 524 } 525 } 526 527 if (all0orff == true) { 528 movi(vd.Is64Bits() ? vd.V1D() : vd.V2D(), ((imm << 32) | imm)); 529 return; 530 } 531 } 532 533 // Of the 4 bytes, only one byte is non-zero. 534 for (int i = 0; i < 4; i++) { 535 if ((imm & (0xff << (i * 8))) == imm) { 536 movi(vd, bytes[i], LSL, i * 8); 537 return; 538 } 539 } 540 541 // Of the 4 bytes, only one byte is not 0xff. 542 for (int i = 0; i < 4; i++) { 543 uint32_t mask = ~(0xff << (i * 8)); 544 if ((imm & mask) == mask) { 545 mvni(vd, ~bytes[i] & 0xff, LSL, i * 8); 546 return; 547 } 548 } 549 550 // Immediate is of the form 0x00MMFFFF. 551 if ((imm & 0xff00ffff) == 0x0000ffff) { 552 movi(vd, bytes[2], MSL, 16); 553 return; 554 } 555 556 // Immediate is of the form 0x0000MMFF. 557 if ((imm & 0xffff00ff) == 0x000000ff) { 558 movi(vd, bytes[1], MSL, 8); 559 return; 560 } 561 562 // Immediate is of the form 0xFFMM0000. 563 if ((imm & 0xff00ffff) == 0xff000000) { 564 mvni(vd, ~bytes[2] & 0xff, MSL, 16); 565 return; 566 } 567 // Immediate is of the form 0xFFFFMM00. 568 if ((imm & 0xffff00ff) == 0xffff0000) { 569 mvni(vd, ~bytes[1] & 0xff, MSL, 8); 570 return; 571 } 572 573 // Top and bottom 16-bits are equal. 574 if (((imm >> 16) & 0xffff) == (imm & 0xffff)) { 575 Movi16bitHelper(vd.Is64Bits() ? vd.V4H() : vd.V8H(), imm & 0xffff); 576 return; 577 } 578 579 // Default case. 580 { 581 UseScratchRegisterScope temps(this); 582 Register temp = temps.AcquireW(); 583 Mov(temp, imm); 584 dup(vd, temp); 585 } 586 } 587 588 589 void MacroAssembler::Movi64bitHelper(const VRegister& vd, uint64_t imm) { 590 // All bytes are either 0x00 or 0xff. 591 { 592 bool all0orff = true; 593 for (int i = 0; i < 8; ++i) { 594 int byteval = (imm >> (i * 8)) & 0xff; 595 if (byteval != 0 && byteval != 0xff) { 596 all0orff = false; 597 break; 598 } 599 } 600 if (all0orff == true) { 601 movi(vd, imm); 602 return; 603 } 604 } 605 606 // Top and bottom 32-bits are equal. 607 if (((imm >> 32) & 0xffffffff) == (imm & 0xffffffff)) { 608 Movi32bitHelper(vd.Is64Bits() ? vd.V2S() : vd.V4S(), imm & 0xffffffff); 609 return; 610 } 611 612 // Default case. 613 { 614 UseScratchRegisterScope temps(this); 615 Register temp = temps.AcquireX(); 616 Mov(temp, imm); 617 if (vd.Is1D()) { 618 mov(vd.D(), 0, temp); 619 } else { 620 dup(vd.V2D(), temp); 621 } 622 } 623 } 624 625 626 void MacroAssembler::Movi(const VRegister& vd, 627 uint64_t imm, 628 Shift shift, 629 int shift_amount) { 630 MacroEmissionCheckScope guard(this); 631 if (shift_amount != 0 || shift != LSL) { 632 movi(vd, imm, shift, shift_amount); 633 } else if (vd.Is8B() || vd.Is16B()) { 634 // 8-bit immediate. 635 VIXL_ASSERT(IsUint8(imm)); 636 movi(vd, imm); 637 } else if (vd.Is4H() || vd.Is8H()) { 638 // 16-bit immediate. 639 Movi16bitHelper(vd, imm); 640 } else if (vd.Is2S() || vd.Is4S()) { 641 // 32-bit immediate. 642 Movi32bitHelper(vd, imm); 643 } else { 644 // 64-bit immediate. 645 Movi64bitHelper(vd, imm); 646 } 647 } 648 649 650 void MacroAssembler::Movi(const VRegister& vd, 651 uint64_t hi, 652 uint64_t lo) { 653 VIXL_ASSERT(vd.Is128Bits()); 654 UseScratchRegisterScope temps(this); 655 656 // When hi == lo, the following generates good code. 657 // 658 // In situations where the constants are complex and hi != lo, the following 659 // can turn into up to 10 instructions: 2*(mov + 3*movk + dup/insert). To do 660 // any better, we could try to estimate whether splatting the high value and 661 // updating the low value would generate fewer instructions than vice versa 662 // (what we do now). 663 // 664 // (A PC-relative load from memory to the vector register (ADR + LD2) is going 665 // to have fairly high latency but is fairly compact; not clear what the best 666 // tradeoff is.) 667 668 Movi(vd.V2D(), lo); 669 if (hi != lo) { 670 Register temp = temps.AcquireX(); 671 Mov(temp, hi); 672 Ins(vd.V2D(), 1, temp); 673 } 674 } 675 676 677 void MacroAssembler::Mvn(const Register& rd, const Operand& operand) { 678 // The worst case for size is mvn immediate with up to 4 instructions. 679 MacroEmissionCheckScope guard(this); 680 681 if (operand.IsImmediate()) { 682 // Call the macro assembler for generic immediates. 683 Mvn(rd, operand.immediate()); 684 } else if (operand.IsExtendedRegister()) { 685 UseScratchRegisterScope temps(this); 686 temps.Exclude(operand.reg()); 687 688 // Emit two instructions for the extend case. This differs from Mov, as 689 // the extend and invert can't be achieved in one instruction. 690 Register temp = temps.AcquireSameSizeAs(rd); 691 692 // VIXL can acquire temp registers. Assert that the caller is aware. 693 VIXL_ASSERT(!temp.Is(rd) && !temp.Is(operand.maybeReg())); 694 695 EmitExtendShift(temp, operand.reg(), operand.extend(), 696 operand.shift_amount()); 697 mvn(rd, Operand(temp)); 698 } else { 699 // Otherwise, register and shifted register cases can be handled by the 700 // assembler directly, using orn. 701 mvn(rd, operand); 702 } 703 } 704 705 706 void MacroAssembler::Mov(const Register& rd, uint64_t imm) { 707 MoveImmediateHelper(this, rd, imm); 708 } 709 710 711 void MacroAssembler::Ccmp(const Register& rn, 712 const Operand& operand, 713 StatusFlags nzcv, 714 Condition cond) { 715 if (operand.IsImmediate()) { 716 int64_t imm = operand.immediate(); 717 if (imm < 0 && imm != std::numeric_limits<int64_t>::min()) { 718 ConditionalCompareMacro(rn, -imm, nzcv, cond, CCMN); 719 return; 720 } 721 } 722 ConditionalCompareMacro(rn, operand, nzcv, cond, CCMP); 723 } 724 725 726 void MacroAssembler::Ccmn(const Register& rn, 727 const Operand& operand, 728 StatusFlags nzcv, 729 Condition cond) { 730 if (operand.IsImmediate()) { 731 int64_t imm = operand.immediate(); 732 if (imm < 0 && imm != std::numeric_limits<int64_t>::min()) { 733 ConditionalCompareMacro(rn, -imm, nzcv, cond, CCMP); 734 return; 735 } 736 } 737 ConditionalCompareMacro(rn, operand, nzcv, cond, CCMN); 738 } 739 740 741 void MacroAssembler::ConditionalCompareMacro(const Register& rn, 742 const Operand& operand, 743 StatusFlags nzcv, 744 Condition cond, 745 ConditionalCompareOp op) { 746 VIXL_ASSERT((cond != al) && (cond != nv)); 747 // The worst case for size is ccmp immediate: 748 // * up to 4 instructions to materialise the constant 749 // * 1 instruction for ccmp 750 MacroEmissionCheckScope guard(this); 751 752 if ((operand.IsShiftedRegister() && (operand.shift_amount() == 0)) || 753 (operand.IsImmediate() && IsImmConditionalCompare(operand.immediate()))) { 754 // The immediate can be encoded in the instruction, or the operand is an 755 // unshifted register: call the assembler. 756 ConditionalCompare(rn, operand, nzcv, cond, op); 757 } else { 758 UseScratchRegisterScope temps(this); 759 // The operand isn't directly supported by the instruction: perform the 760 // operation on a temporary register. 761 Register temp = temps.AcquireSameSizeAs(rn); 762 VIXL_ASSERT(!temp.Is(rn) && !temp.Is(operand.maybeReg())); 763 Mov(temp, operand); 764 ConditionalCompare(rn, temp, nzcv, cond, op); 765 } 766 } 767 768 769 void MacroAssembler::Csel(const Register& rd, 770 const Register& rn, 771 const Operand& operand, 772 Condition cond) { 773 VIXL_ASSERT(!rd.IsZero()); 774 VIXL_ASSERT(!rn.IsZero()); 775 VIXL_ASSERT((cond != al) && (cond != nv)); 776 // The worst case for size is csel immediate: 777 // * up to 4 instructions to materialise the constant 778 // * 1 instruction for csel 779 MacroEmissionCheckScope guard(this); 780 781 if (operand.IsImmediate()) { 782 // Immediate argument. Handle special cases of 0, 1 and -1 using zero 783 // register. 784 int64_t imm = operand.immediate(); 785 Register zr = AppropriateZeroRegFor(rn); 786 if (imm == 0) { 787 csel(rd, rn, zr, cond); 788 } else if (imm == 1) { 789 csinc(rd, rn, zr, cond); 790 } else if (imm == -1) { 791 csinv(rd, rn, zr, cond); 792 } else { 793 UseScratchRegisterScope temps(this); 794 Register temp = temps.AcquireSameSizeAs(rn); 795 VIXL_ASSERT(!temp.Is(rd) && !temp.Is(rn)); 796 VIXL_ASSERT(!temp.Is(operand.maybeReg())); 797 Mov(temp, operand.immediate()); 798 csel(rd, rn, temp, cond); 799 } 800 } else if (operand.IsShiftedRegister() && (operand.shift_amount() == 0)) { 801 // Unshifted register argument. 802 csel(rd, rn, operand.reg(), cond); 803 } else { 804 // All other arguments. 805 UseScratchRegisterScope temps(this); 806 Register temp = temps.AcquireSameSizeAs(rn); 807 VIXL_ASSERT(!temp.Is(rd) && !temp.Is(rn)); 808 VIXL_ASSERT(!temp.Is(operand.maybeReg())); 809 Mov(temp, operand); 810 csel(rd, rn, temp, cond); 811 } 812 } 813 814 815 void MacroAssembler::Add(const Register& rd, 816 const Register& rn, 817 const Operand& operand, 818 FlagsUpdate S) { 819 if (operand.IsImmediate()) { 820 int64_t imm = operand.immediate(); 821 if (imm < 0 && imm != std::numeric_limits<int64_t>::min() && 822 IsImmAddSub(-imm)) { 823 AddSubMacro(rd, rn, -imm, S, SUB); 824 return; 825 } 826 } 827 AddSubMacro(rd, rn, operand, S, ADD); 828 } 829 830 831 void MacroAssembler::Adds(const Register& rd, 832 const Register& rn, 833 const Operand& operand) { 834 Add(rd, rn, operand, SetFlags); 835 } 836 837 #define MINMAX(V) \ 838 V(Smax, smax, IsInt8) \ 839 V(Smin, smin, IsInt8) \ 840 V(Umax, umax, IsUint8) \ 841 V(Umin, umin, IsUint8) 842 843 #define VIXL_DEFINE_MASM_FUNC(MASM, ASM, RANGE) \ 844 void MacroAssembler::MASM(const Register& rd, \ 845 const Register& rn, \ 846 const Operand& op) { \ 847 if (op.IsImmediate()) { \ 848 int64_t imm = op.GetImmediate(); \ 849 if (!RANGE(imm)) { \ 850 UseScratchRegisterScope temps(this); \ 851 Register temp = temps.AcquireSameSizeAs(rd); \ 852 Mov(temp, imm); \ 853 MASM(rd, rn, temp); \ 854 return; \ 855 } \ 856 } \ 857 SingleEmissionCheckScope guard(this); \ 858 ASM(rd, rn, op); \ 859 } 860 MINMAX(VIXL_DEFINE_MASM_FUNC) 861 #undef VIXL_DEFINE_MASM_FUNC 862 863 // Mozilla change: Undefine MINMAX 864 #undef MINMAX 865 866 void MacroAssembler::Sub(const Register& rd, 867 const Register& rn, 868 const Operand& operand, 869 FlagsUpdate S) { 870 if (operand.IsImmediate()) { 871 int64_t imm = operand.immediate(); 872 if (imm < 0 && imm != std::numeric_limits<int64_t>::min() && 873 IsImmAddSub(-imm)) { 874 AddSubMacro(rd, rn, -imm, S, ADD); 875 return; 876 } 877 } 878 AddSubMacro(rd, rn, operand, S, SUB); 879 } 880 881 882 void MacroAssembler::Subs(const Register& rd, 883 const Register& rn, 884 const Operand& operand) { 885 Sub(rd, rn, operand, SetFlags); 886 } 887 888 889 void MacroAssembler::Cmn(const Register& rn, const Operand& operand) { 890 Adds(AppropriateZeroRegFor(rn), rn, operand); 891 } 892 893 894 void MacroAssembler::Cmp(const Register& rn, const Operand& operand) { 895 Subs(AppropriateZeroRegFor(rn), rn, operand); 896 } 897 898 899 void MacroAssembler::Fcmp(const FPRegister& fn, double value, 900 FPTrapFlags trap) { 901 // The worst case for size is: 902 // * 1 to materialise the constant, using literal pool if necessary 903 // * 1 instruction for fcmp{e} 904 MacroEmissionCheckScope guard(this); 905 if (value != 0.0) { 906 UseScratchRegisterScope temps(this); 907 FPRegister tmp = temps.AcquireSameSizeAs(fn); 908 VIXL_ASSERT(!tmp.Is(fn)); 909 Fmov(tmp, value); 910 FPCompareMacro(fn, tmp, trap); 911 } else { 912 FPCompareMacro(fn, value, trap); 913 } 914 } 915 916 917 void MacroAssembler::Fcmpe(const FPRegister& fn, double value) { 918 Fcmp(fn, value, EnableTrap); 919 } 920 921 922 void MacroAssembler::Fmov(VRegister vd, double imm) { 923 // Floating point immediates are loaded through the literal pool. 924 MacroEmissionCheckScope guard(this); 925 926 if (vd.Is1S() || vd.Is2S() || vd.Is4S()) { 927 Fmov(vd, static_cast<float>(imm)); 928 return; 929 } 930 931 VIXL_ASSERT(vd.Is1D() || vd.Is2D()); 932 if (IsImmFP64(imm)) { 933 fmov(vd, imm); 934 } else { 935 uint64_t rawbits = DoubleToRawbits(imm); 936 if (vd.IsScalar()) { 937 if (rawbits == 0) { 938 fmov(vd, xzr); 939 } else { 940 Assembler::fImmPool64(vd, imm); 941 } 942 } else { 943 // TODO: consider NEON support for load literal. 944 Movi(vd, rawbits); 945 } 946 } 947 } 948 949 950 void MacroAssembler::Fmov(VRegister vd, float imm) { 951 // Floating point immediates are loaded through the literal pool. 952 MacroEmissionCheckScope guard(this); 953 954 if (vd.Is1D() || vd.Is2D()) { 955 Fmov(vd, static_cast<double>(imm)); 956 return; 957 } 958 959 VIXL_ASSERT(vd.Is1S() || vd.Is2S() || vd.Is4S()); 960 if (IsImmFP32(imm)) { 961 fmov(vd, imm); 962 } else { 963 uint32_t rawbits = FloatToRawbits(imm); 964 if (vd.IsScalar()) { 965 if (rawbits == 0) { 966 fmov(vd, wzr); 967 } else { 968 Assembler::fImmPool32(vd, imm); 969 } 970 } else { 971 // TODO: consider NEON support for load literal. 972 Movi(vd, rawbits); 973 } 974 } 975 } 976 977 978 979 void MacroAssembler::Neg(const Register& rd, 980 const Operand& operand) { 981 if (operand.IsImmediate()) { 982 int64_t imm = operand.immediate(); 983 if (imm != std::numeric_limits<int64_t>::min()) { 984 Mov(rd, -imm); 985 return; 986 } 987 } 988 Sub(rd, AppropriateZeroRegFor(rd), operand); 989 } 990 991 992 void MacroAssembler::Negs(const Register& rd, 993 const Operand& operand) { 994 Subs(rd, AppropriateZeroRegFor(rd), operand); 995 } 996 997 998 bool MacroAssembler::TryOneInstrMoveImmediate(const Register& dst, 999 int64_t imm) { 1000 return OneInstrMoveImmediateHelper(this, dst, imm); 1001 } 1002 1003 1004 Operand MacroAssembler::MoveImmediateForShiftedOp(const Register& dst, 1005 int64_t imm, 1006 PreShiftImmMode mode) { 1007 int reg_size = dst.size(); 1008 1009 // Encode the immediate in a single move instruction, if possible. 1010 if (TryOneInstrMoveImmediate(dst, imm)) { 1011 // The move was successful; nothing to do here. 1012 } else { 1013 // Pre-shift the immediate to the least-significant bits of the register. 1014 int shift_low = CountTrailingZeros(imm, reg_size); 1015 if (mode == kLimitShiftForSP) { 1016 // When applied to the stack pointer, the subsequent arithmetic operation 1017 // can use the extend form to shift left by a maximum of four bits. Right 1018 // shifts are not allowed, so we filter them out later before the new 1019 // immediate is tested. 1020 shift_low = std::min(shift_low, 4); 1021 } 1022 1023 int64_t imm_low = imm >> shift_low; 1024 1025 // Pre-shift the immediate to the most-significant bits of the register, 1026 // inserting set bits in the least-significant bits. 1027 int shift_high = CountLeadingZeros(imm, reg_size); 1028 int64_t imm_high = (imm << shift_high) | ((INT64_C(1) << shift_high) - 1); 1029 1030 if ((mode != kNoShift) && TryOneInstrMoveImmediate(dst, imm_low)) { 1031 // The new immediate has been moved into the destination's low bits: 1032 // return a new leftward-shifting operand. 1033 return Operand(dst, LSL, shift_low); 1034 } else if ((mode == kAnyShift) && TryOneInstrMoveImmediate(dst, imm_high)) { 1035 // The new immediate has been moved into the destination's high bits: 1036 // return a new rightward-shifting operand. 1037 return Operand(dst, LSR, shift_high); 1038 } else { 1039 Mov(dst, imm); 1040 } 1041 } 1042 return Operand(dst); 1043 } 1044 1045 1046 void MacroAssembler::ComputeAddress(const Register& dst, 1047 const MemOperand& mem_op) { 1048 // We cannot handle pre-indexing or post-indexing. 1049 VIXL_ASSERT(mem_op.addrmode() == Offset); 1050 Register base = mem_op.base(); 1051 if (mem_op.IsImmediateOffset()) { 1052 Add(dst, base, mem_op.offset()); 1053 } else { 1054 VIXL_ASSERT(mem_op.IsRegisterOffset()); 1055 Register reg_offset = mem_op.regoffset(); 1056 Shift shift = mem_op.shift(); 1057 Extend extend = mem_op.extend(); 1058 if (shift == NO_SHIFT) { 1059 VIXL_ASSERT(extend != NO_EXTEND); 1060 Add(dst, base, Operand(reg_offset, extend, mem_op.shift_amount())); 1061 } else { 1062 VIXL_ASSERT(extend == NO_EXTEND); 1063 Add(dst, base, Operand(reg_offset, shift, mem_op.shift_amount())); 1064 } 1065 } 1066 } 1067 1068 1069 void MacroAssembler::AddSubMacro(const Register& rd, 1070 const Register& rn, 1071 const Operand& operand, 1072 FlagsUpdate S, 1073 AddSubOp op) { 1074 // Worst case is add/sub immediate: 1075 // * up to 4 instructions to materialise the constant 1076 // * 1 instruction for add/sub 1077 MacroEmissionCheckScope guard(this); 1078 1079 if (operand.IsZero() && rd.Is(rn) && rd.Is64Bits() && rn.Is64Bits() && 1080 (S == LeaveFlags)) { 1081 // The instruction would be a nop. Avoid generating useless code. 1082 return; 1083 } 1084 1085 if ((operand.IsImmediate() && !IsImmAddSub(operand.immediate())) || 1086 (rn.IsZero() && !operand.IsShiftedRegister()) || 1087 (operand.IsShiftedRegister() && (operand.shift() == ROR))) { 1088 UseScratchRegisterScope temps(this); 1089 Register temp = temps.AcquireSameSizeAs(rn); 1090 if (operand.IsImmediate()) { 1091 PreShiftImmMode mode = kAnyShift; 1092 1093 // If the destination or source register is the stack pointer, we can 1094 // only pre-shift the immediate right by values supported in the add/sub 1095 // extend encoding. 1096 if (rd.IsSP()) { 1097 // If the destination is SP and flags will be set, we can't pre-shift 1098 // the immediate at all. 1099 mode = (S == SetFlags) ? kNoShift : kLimitShiftForSP; 1100 } else if (rn.IsSP()) { 1101 mode = kLimitShiftForSP; 1102 } 1103 1104 Operand imm_operand = 1105 MoveImmediateForShiftedOp(temp, operand.immediate(), mode); 1106 AddSub(rd, rn, imm_operand, S, op); 1107 } else { 1108 Mov(temp, operand); 1109 AddSub(rd, rn, temp, S, op); 1110 } 1111 } else { 1112 AddSub(rd, rn, operand, S, op); 1113 } 1114 } 1115 1116 1117 void MacroAssembler::Adc(const Register& rd, 1118 const Register& rn, 1119 const Operand& operand) { 1120 AddSubWithCarryMacro(rd, rn, operand, LeaveFlags, ADC); 1121 } 1122 1123 1124 void MacroAssembler::Adcs(const Register& rd, 1125 const Register& rn, 1126 const Operand& operand) { 1127 AddSubWithCarryMacro(rd, rn, operand, SetFlags, ADC); 1128 } 1129 1130 1131 void MacroAssembler::Sbc(const Register& rd, 1132 const Register& rn, 1133 const Operand& operand) { 1134 AddSubWithCarryMacro(rd, rn, operand, LeaveFlags, SBC); 1135 } 1136 1137 1138 void MacroAssembler::Sbcs(const Register& rd, 1139 const Register& rn, 1140 const Operand& operand) { 1141 AddSubWithCarryMacro(rd, rn, operand, SetFlags, SBC); 1142 } 1143 1144 1145 void MacroAssembler::Ngc(const Register& rd, 1146 const Operand& operand) { 1147 Register zr = AppropriateZeroRegFor(rd); 1148 Sbc(rd, zr, operand); 1149 } 1150 1151 1152 void MacroAssembler::Ngcs(const Register& rd, 1153 const Operand& operand) { 1154 Register zr = AppropriateZeroRegFor(rd); 1155 Sbcs(rd, zr, operand); 1156 } 1157 1158 1159 void MacroAssembler::AddSubWithCarryMacro(const Register& rd, 1160 const Register& rn, 1161 const Operand& operand, 1162 FlagsUpdate S, 1163 AddSubWithCarryOp op) { 1164 VIXL_ASSERT(rd.size() == rn.size()); 1165 // Worst case is addc/subc immediate: 1166 // * up to 4 instructions to materialise the constant 1167 // * 1 instruction for add/sub 1168 MacroEmissionCheckScope guard(this); 1169 UseScratchRegisterScope temps(this); 1170 1171 if (operand.IsImmediate() || 1172 (operand.IsShiftedRegister() && (operand.shift() == ROR))) { 1173 // Add/sub with carry (immediate or ROR shifted register.) 1174 Register temp = temps.AcquireSameSizeAs(rn); 1175 VIXL_ASSERT(!temp.Is(rd) && !temp.Is(rn) && !temp.Is(operand.maybeReg())); 1176 Mov(temp, operand); 1177 AddSubWithCarry(rd, rn, Operand(temp), S, op); 1178 } else if (operand.IsShiftedRegister() && (operand.shift_amount() != 0)) { 1179 // Add/sub with carry (shifted register). 1180 VIXL_ASSERT(operand.reg().size() == rd.size()); 1181 VIXL_ASSERT(operand.shift() != ROR); 1182 VIXL_ASSERT(IsUintN(rd.size() == kXRegSize ? kXRegSizeLog2 : kWRegSizeLog2, 1183 operand.shift_amount())); 1184 temps.Exclude(operand.reg()); 1185 Register temp = temps.AcquireSameSizeAs(rn); 1186 VIXL_ASSERT(!temp.Is(rd) && !temp.Is(rn) && !temp.Is(operand.maybeReg())); 1187 EmitShift(temp, operand.reg(), operand.shift(), operand.shift_amount()); 1188 AddSubWithCarry(rd, rn, Operand(temp), S, op); 1189 } else if (operand.IsExtendedRegister()) { 1190 // Add/sub with carry (extended register). 1191 VIXL_ASSERT(operand.reg().size() <= rd.size()); 1192 // Add/sub extended supports a shift <= 4. We want to support exactly the 1193 // same modes. 1194 VIXL_ASSERT(operand.shift_amount() <= 4); 1195 VIXL_ASSERT(operand.reg().Is64Bits() || 1196 ((operand.extend() != UXTX) && (operand.extend() != SXTX))); 1197 temps.Exclude(operand.reg()); 1198 Register temp = temps.AcquireSameSizeAs(rn); 1199 VIXL_ASSERT(!temp.Is(rd) && !temp.Is(rn) && !temp.Is(operand.maybeReg())); 1200 EmitExtendShift(temp, operand.reg(), operand.extend(), 1201 operand.shift_amount()); 1202 AddSubWithCarry(rd, rn, Operand(temp), S, op); 1203 } else { 1204 // The addressing mode is directly supported by the instruction. 1205 AddSubWithCarry(rd, rn, operand, S, op); 1206 } 1207 } 1208 1209 #define DEFINE_FUNCTION(FN, REGTYPE, REG, OP) \ 1210 js::wasm::FaultingCodeOffset MacroAssembler::FN(const REGTYPE REG, \ 1211 const MemOperand& addr) { \ 1212 return LoadStoreMacro(REG, addr, OP); \ 1213 } 1214 LS_MACRO_LIST(DEFINE_FUNCTION) 1215 #undef DEFINE_FUNCTION 1216 1217 js::wasm::FaultingCodeOffset MacroAssembler::LoadStoreMacro( 1218 const CPURegister& rt, 1219 const MemOperand& addr, 1220 LoadStoreOp op) { 1221 // Worst case is ldr/str pre/post index: 1222 // * 1 instruction for ldr/str 1223 // * up to 4 instructions to materialise the constant 1224 // * 1 instruction to update the base 1225 MacroEmissionCheckScope guard(this); 1226 1227 int64_t offset = addr.offset(); 1228 unsigned access_size = CalcLSDataSize(op); 1229 1230 // Check if an immediate offset fits in the immediate field of the 1231 // appropriate instruction. If not, emit two instructions to perform 1232 // the operation. 1233 js::wasm::FaultingCodeOffset fco; 1234 if (addr.IsImmediateOffset() && !IsImmLSScaled(offset, access_size) && 1235 !IsImmLSUnscaled(offset)) { 1236 // Immediate offset that can't be encoded using unsigned or unscaled 1237 // addressing modes. 1238 VIXL_ASSERT(addr.regoffset().Is(NoReg)); 1239 UseScratchRegisterScope temps(this); 1240 int64_t offset = addr.offset(); 1241 Register recycle0 = NoReg; 1242 Register recycle1 = NoReg; 1243 if (!temps.HasAvailableRegister()) { 1244 // Recycle the first pair of registers which are not aliasing the operands. 1245 js::jit::AllocatableGeneralRegisterSet 1246 freeRegs(js::jit::GeneralRegisterSet::Volatile()); 1247 freeRegs.takeUnchecked(addr.base().asUnsized()); 1248 freeRegs.takeUnchecked(rt.X().asUnsized()); 1249 recycle0 = Register(freeRegs.takeFirst(), kXRegSize); 1250 recycle1 = Register(freeRegs.takeFirst(), kXRegSize); 1251 Push(recycle0, recycle1); 1252 temps.Include(recycle0, recycle1); 1253 if (addr.base().Is(GetStackPointer64())) { 1254 offset += 2 * sizeof(uintptr_t); 1255 } 1256 } 1257 Register temp = temps.AcquireSameSizeAs(addr.base()); 1258 VIXL_ASSERT(!temp.Is(rt)); 1259 VIXL_ASSERT(!temp.Is(addr.base())); 1260 Mov(temp, offset); 1261 { 1262 js::jit::AutoForbidPoolsAndNops afp(this, 1); 1263 fco = js::wasm::FaultingCodeOffset(currentOffset()); 1264 LoadStore(rt, MemOperand(addr.base(), temp), op); 1265 } 1266 if (!recycle0.Is(NoReg)) { 1267 temps.Exclude(recycle0, recycle1); 1268 Pop(recycle1, recycle0); 1269 } 1270 } else if (addr.IsPostIndex() && !IsImmLSUnscaled(offset)) { 1271 // Post-index beyond unscaled addressing range. 1272 { 1273 js::jit::AutoForbidPoolsAndNops afp(this, 1); 1274 fco = js::wasm::FaultingCodeOffset(currentOffset()); 1275 LoadStore(rt, MemOperand(addr.base()), op); 1276 } 1277 Add(addr.base(), addr.base(), Operand(offset)); 1278 } else if (addr.IsPreIndex() && !IsImmLSUnscaled(offset)) { 1279 // Pre-index beyond unscaled addressing range. 1280 Add(addr.base(), addr.base(), Operand(offset)); 1281 { 1282 js::jit::AutoForbidPoolsAndNops afp(this, 1); 1283 fco = js::wasm::FaultingCodeOffset(currentOffset()); 1284 LoadStore(rt, MemOperand(addr.base()), op); 1285 } 1286 } else { 1287 // Encodable in one load/store instruction. 1288 js::jit::AutoForbidPoolsAndNops afp(this, 1); 1289 fco = js::wasm::FaultingCodeOffset(currentOffset()); 1290 LoadStore(rt, addr, op); 1291 } 1292 1293 return fco; 1294 } 1295 1296 #define DEFINE_FUNCTION(FN, REGTYPE, REG, REG2, OP) \ 1297 void MacroAssembler::FN(const REGTYPE REG, \ 1298 const REGTYPE REG2, \ 1299 const MemOperand& addr) { \ 1300 LoadStorePairMacro(REG, REG2, addr, OP); \ 1301 } 1302 LSPAIR_MACRO_LIST(DEFINE_FUNCTION) 1303 #undef DEFINE_FUNCTION 1304 1305 void MacroAssembler::LoadStorePairMacro(const CPURegister& rt, 1306 const CPURegister& rt2, 1307 const MemOperand& addr, 1308 LoadStorePairOp op) { 1309 // TODO(all): Should we support register offset for load-store-pair? 1310 VIXL_ASSERT(!addr.IsRegisterOffset()); 1311 // Worst case is ldp/stp immediate: 1312 // * 1 instruction for ldp/stp 1313 // * up to 4 instructions to materialise the constant 1314 // * 1 instruction to update the base 1315 MacroEmissionCheckScope guard(this); 1316 1317 int64_t offset = addr.offset(); 1318 unsigned access_size = CalcLSPairDataSize(op); 1319 1320 // Check if the offset fits in the immediate field of the appropriate 1321 // instruction. If not, emit two instructions to perform the operation. 1322 if (IsImmLSPair(offset, access_size)) { 1323 // Encodable in one load/store pair instruction. 1324 LoadStorePair(rt, rt2, addr, op); 1325 } else { 1326 Register base = addr.base(); 1327 if (addr.IsImmediateOffset()) { 1328 UseScratchRegisterScope temps(this); 1329 Register temp = temps.AcquireSameSizeAs(base); 1330 Add(temp, base, offset); 1331 LoadStorePair(rt, rt2, MemOperand(temp), op); 1332 } else if (addr.IsPostIndex()) { 1333 LoadStorePair(rt, rt2, MemOperand(base), op); 1334 Add(base, base, offset); 1335 } else { 1336 VIXL_ASSERT(addr.IsPreIndex()); 1337 Add(base, base, offset); 1338 LoadStorePair(rt, rt2, MemOperand(base), op); 1339 } 1340 } 1341 } 1342 1343 1344 void MacroAssembler::Prfm(PrefetchOperation op, const MemOperand& addr) { 1345 MacroEmissionCheckScope guard(this); 1346 1347 // There are no pre- or post-index modes for prfm. 1348 VIXL_ASSERT(addr.IsImmediateOffset() || addr.IsRegisterOffset()); 1349 1350 // The access size is implicitly 8 bytes for all prefetch operations. 1351 unsigned size = kXRegSizeInBytesLog2; 1352 1353 // Check if an immediate offset fits in the immediate field of the 1354 // appropriate instruction. If not, emit two instructions to perform 1355 // the operation. 1356 if (addr.IsImmediateOffset() && !IsImmLSScaled(addr.offset(), size) && 1357 !IsImmLSUnscaled(addr.offset())) { 1358 // Immediate offset that can't be encoded using unsigned or unscaled 1359 // addressing modes. 1360 UseScratchRegisterScope temps(this); 1361 Register temp = temps.AcquireSameSizeAs(addr.base()); 1362 Mov(temp, addr.offset()); 1363 Prefetch(op, MemOperand(addr.base(), temp)); 1364 } else { 1365 // Simple register-offsets are encodable in one instruction. 1366 Prefetch(op, addr); 1367 } 1368 } 1369 1370 1371 void MacroAssembler::PushStackPointer() { 1372 PrepareForPush(1, 8); 1373 1374 // Pushing a stack pointer leads to implementation-defined 1375 // behavior, which may be surprising. In particular, 1376 // str x28, [x28, #-8]! 1377 // pre-decrements the stack pointer, storing the decremented value. 1378 // Additionally, sp is read as xzr in this context, so it cannot be pushed. 1379 // So we must use a scratch register. 1380 UseScratchRegisterScope temps(this); 1381 Register scratch = temps.AcquireX(); 1382 1383 Mov(scratch, GetStackPointer64()); 1384 str(scratch, MemOperand(GetStackPointer64(), -8, PreIndex)); 1385 } 1386 1387 1388 void MacroAssembler::Push(const CPURegister& src0, const CPURegister& src1, 1389 const CPURegister& src2, const CPURegister& src3) { 1390 VIXL_ASSERT(AreSameSizeAndType(src0, src1, src2, src3)); 1391 VIXL_ASSERT(src0.IsValid()); 1392 1393 int count = 1 + src1.IsValid() + src2.IsValid() + src3.IsValid(); 1394 int size = src0.SizeInBytes(); 1395 1396 if (src0.Is(GetStackPointer64())) { 1397 VIXL_ASSERT(count == 1); 1398 VIXL_ASSERT(size == 8); 1399 PushStackPointer(); 1400 return; 1401 } 1402 1403 PrepareForPush(count, size); 1404 PushHelper(count, size, src0, src1, src2, src3); 1405 } 1406 1407 1408 void MacroAssembler::Pop(const CPURegister& dst0, const CPURegister& dst1, 1409 const CPURegister& dst2, const CPURegister& dst3) { 1410 // It is not valid to pop into the same register more than once in one 1411 // instruction, not even into the zero register. 1412 VIXL_ASSERT(!AreAliased(dst0, dst1, dst2, dst3)); 1413 VIXL_ASSERT(AreSameSizeAndType(dst0, dst1, dst2, dst3)); 1414 VIXL_ASSERT(dst0.IsValid()); 1415 1416 int count = 1 + dst1.IsValid() + dst2.IsValid() + dst3.IsValid(); 1417 int size = dst0.SizeInBytes(); 1418 1419 PrepareForPop(count, size); 1420 PopHelper(count, size, dst0, dst1, dst2, dst3); 1421 } 1422 1423 1424 void MacroAssembler::PushCPURegList(CPURegList registers) { 1425 VIXL_ASSERT(!registers.Overlaps(*TmpList())); 1426 VIXL_ASSERT(!registers.Overlaps(*FPTmpList())); 1427 1428 int reg_size = registers.RegisterSizeInBytes(); 1429 PrepareForPush(registers.Count(), reg_size); 1430 1431 // Bump the stack pointer and store two registers at the bottom. 1432 int size = registers.TotalSizeInBytes(); 1433 const CPURegister& bottom_0 = registers.PopLowestIndex(); 1434 const CPURegister& bottom_1 = registers.PopLowestIndex(); 1435 if (bottom_0.IsValid() && bottom_1.IsValid()) { 1436 Stp(bottom_0, bottom_1, MemOperand(GetStackPointer64(), -size, PreIndex)); 1437 } else if (bottom_0.IsValid()) { 1438 Str(bottom_0, MemOperand(GetStackPointer64(), -size, PreIndex)); 1439 } 1440 1441 int offset = 2 * reg_size; 1442 while (!registers.IsEmpty()) { 1443 const CPURegister& src0 = registers.PopLowestIndex(); 1444 const CPURegister& src1 = registers.PopLowestIndex(); 1445 if (src1.IsValid()) { 1446 Stp(src0, src1, MemOperand(GetStackPointer64(), offset)); 1447 } else { 1448 Str(src0, MemOperand(GetStackPointer64(), offset)); 1449 } 1450 offset += 2 * reg_size; 1451 } 1452 } 1453 1454 1455 void MacroAssembler::PopCPURegList(CPURegList registers) { 1456 VIXL_ASSERT(!registers.Overlaps(*TmpList())); 1457 VIXL_ASSERT(!registers.Overlaps(*FPTmpList())); 1458 1459 int reg_size = registers.RegisterSizeInBytes(); 1460 PrepareForPop(registers.Count(), reg_size); 1461 1462 1463 int size = registers.TotalSizeInBytes(); 1464 const CPURegister& bottom_0 = registers.PopLowestIndex(); 1465 const CPURegister& bottom_1 = registers.PopLowestIndex(); 1466 1467 int offset = 2 * reg_size; 1468 while (!registers.IsEmpty()) { 1469 const CPURegister& dst0 = registers.PopLowestIndex(); 1470 const CPURegister& dst1 = registers.PopLowestIndex(); 1471 if (dst1.IsValid()) { 1472 Ldp(dst0, dst1, MemOperand(GetStackPointer64(), offset)); 1473 } else { 1474 Ldr(dst0, MemOperand(GetStackPointer64(), offset)); 1475 } 1476 offset += 2 * reg_size; 1477 } 1478 1479 // Load the two registers at the bottom and drop the stack pointer. 1480 if (bottom_0.IsValid() && bottom_1.IsValid()) { 1481 Ldp(bottom_0, bottom_1, MemOperand(GetStackPointer64(), size, PostIndex)); 1482 } else if (bottom_0.IsValid()) { 1483 Ldr(bottom_0, MemOperand(GetStackPointer64(), size, PostIndex)); 1484 } 1485 } 1486 1487 1488 void MacroAssembler::PushMultipleTimes(int count, Register src) { 1489 int size = src.SizeInBytes(); 1490 1491 PrepareForPush(count, size); 1492 // Push up to four registers at a time if possible because if the current 1493 // stack pointer is sp and the register size is 32, registers must be pushed 1494 // in blocks of four in order to maintain the 16-byte alignment for sp. 1495 while (count >= 4) { 1496 PushHelper(4, size, src, src, src, src); 1497 count -= 4; 1498 } 1499 if (count >= 2) { 1500 PushHelper(2, size, src, src, NoReg, NoReg); 1501 count -= 2; 1502 } 1503 if (count == 1) { 1504 PushHelper(1, size, src, NoReg, NoReg, NoReg); 1505 count -= 1; 1506 } 1507 VIXL_ASSERT(count == 0); 1508 } 1509 1510 1511 void MacroAssembler::PushHelper(int count, int size, 1512 const CPURegister& src0, 1513 const CPURegister& src1, 1514 const CPURegister& src2, 1515 const CPURegister& src3) { 1516 // Ensure that we don't unintentionally modify scratch or debug registers. 1517 // Worst case for size is 2 stp. 1518 InstructionAccurateScope scope(this, 2, 1519 InstructionAccurateScope::kMaximumSize); 1520 1521 VIXL_ASSERT(AreSameSizeAndType(src0, src1, src2, src3)); 1522 VIXL_ASSERT(size == src0.SizeInBytes()); 1523 1524 // Pushing the stack pointer has unexpected behavior. See PushStackPointer(). 1525 VIXL_ASSERT(!src0.Is(GetStackPointer64()) && !src0.Is(sp)); 1526 VIXL_ASSERT(!src1.Is(GetStackPointer64()) && !src1.Is(sp)); 1527 VIXL_ASSERT(!src2.Is(GetStackPointer64()) && !src2.Is(sp)); 1528 VIXL_ASSERT(!src3.Is(GetStackPointer64()) && !src3.Is(sp)); 1529 1530 // The JS engine should never push 4 bytes. 1531 VIXL_ASSERT(size >= 8); 1532 1533 // When pushing multiple registers, the store order is chosen such that 1534 // Push(a, b) is equivalent to Push(a) followed by Push(b). 1535 switch (count) { 1536 case 1: 1537 VIXL_ASSERT(src1.IsNone() && src2.IsNone() && src3.IsNone()); 1538 str(src0, MemOperand(GetStackPointer64(), -1 * size, PreIndex)); 1539 break; 1540 case 2: 1541 VIXL_ASSERT(src2.IsNone() && src3.IsNone()); 1542 stp(src1, src0, MemOperand(GetStackPointer64(), -2 * size, PreIndex)); 1543 break; 1544 case 3: 1545 VIXL_ASSERT(src3.IsNone()); 1546 stp(src2, src1, MemOperand(GetStackPointer64(), -3 * size, PreIndex)); 1547 str(src0, MemOperand(GetStackPointer64(), 2 * size)); 1548 break; 1549 case 4: 1550 // Skip over 4 * size, then fill in the gap. This allows four W registers 1551 // to be pushed using sp, whilst maintaining 16-byte alignment for sp at 1552 // all times. 1553 stp(src3, src2, MemOperand(GetStackPointer64(), -4 * size, PreIndex)); 1554 stp(src1, src0, MemOperand(GetStackPointer64(), 2 * size)); 1555 break; 1556 default: 1557 VIXL_UNREACHABLE(); 1558 } 1559 } 1560 1561 1562 void MacroAssembler::PopHelper(int count, int size, 1563 const CPURegister& dst0, 1564 const CPURegister& dst1, 1565 const CPURegister& dst2, 1566 const CPURegister& dst3) { 1567 // Ensure that we don't unintentionally modify scratch or debug registers. 1568 // Worst case for size is 2 ldp. 1569 InstructionAccurateScope scope(this, 2, 1570 InstructionAccurateScope::kMaximumSize); 1571 1572 VIXL_ASSERT(AreSameSizeAndType(dst0, dst1, dst2, dst3)); 1573 VIXL_ASSERT(size == dst0.SizeInBytes()); 1574 1575 // When popping multiple registers, the load order is chosen such that 1576 // Pop(a, b) is equivalent to Pop(a) followed by Pop(b). 1577 switch (count) { 1578 case 1: 1579 VIXL_ASSERT(dst1.IsNone() && dst2.IsNone() && dst3.IsNone()); 1580 ldr(dst0, MemOperand(GetStackPointer64(), 1 * size, PostIndex)); 1581 break; 1582 case 2: 1583 VIXL_ASSERT(dst2.IsNone() && dst3.IsNone()); 1584 ldp(dst0, dst1, MemOperand(GetStackPointer64(), 2 * size, PostIndex)); 1585 break; 1586 case 3: 1587 VIXL_ASSERT(dst3.IsNone()); 1588 ldr(dst2, MemOperand(GetStackPointer64(), 2 * size)); 1589 ldp(dst0, dst1, MemOperand(GetStackPointer64(), 3 * size, PostIndex)); 1590 break; 1591 case 4: 1592 // Load the higher addresses first, then load the lower addresses and skip 1593 // the whole block in the second instruction. This allows four W registers 1594 // to be popped using sp, whilst maintaining 16-byte alignment for sp at 1595 // all times. 1596 ldp(dst2, dst3, MemOperand(GetStackPointer64(), 2 * size)); 1597 ldp(dst0, dst1, MemOperand(GetStackPointer64(), 4 * size, PostIndex)); 1598 break; 1599 default: 1600 VIXL_UNREACHABLE(); 1601 } 1602 } 1603 1604 1605 void MacroAssembler::PrepareForPush(int count, int size) { 1606 if (sp.Is(GetStackPointer64())) { 1607 // If the current stack pointer is sp, then it must be aligned to 16 bytes 1608 // on entry and the total size of the specified registers must also be a 1609 // multiple of 16 bytes. 1610 VIXL_ASSERT((count * size) % 16 == 0); 1611 } else { 1612 // Even if the current stack pointer is not the system stack pointer (sp), 1613 // the system stack pointer will still be modified in order to comply with 1614 // ABI rules about accessing memory below the system stack pointer. 1615 BumpSystemStackPointer(count * size); 1616 } 1617 } 1618 1619 1620 void MacroAssembler::PrepareForPop(int count, int size) { 1621 USE(count, size); 1622 if (sp.Is(GetStackPointer64())) { 1623 // If the current stack pointer is sp, then it must be aligned to 16 bytes 1624 // on entry and the total size of the specified registers must also be a 1625 // multiple of 16 bytes. 1626 VIXL_ASSERT((count * size) % 16 == 0); 1627 } 1628 } 1629 1630 void MacroAssembler::Poke(const Register& src, const Operand& offset) { 1631 if (offset.IsImmediate()) { 1632 VIXL_ASSERT(offset.immediate() >= 0); 1633 } 1634 1635 Str(src, MemOperand(GetStackPointer64(), offset)); 1636 } 1637 1638 1639 void MacroAssembler::Peek(const Register& dst, const Operand& offset) { 1640 if (offset.IsImmediate()) { 1641 VIXL_ASSERT(offset.immediate() >= 0); 1642 } 1643 1644 Ldr(dst, MemOperand(GetStackPointer64(), offset)); 1645 } 1646 1647 1648 void MacroAssembler::Claim(const Operand& size) { 1649 1650 if (size.IsZero()) { 1651 return; 1652 } 1653 1654 if (size.IsImmediate()) { 1655 VIXL_ASSERT(size.immediate() > 0); 1656 if (sp.Is(GetStackPointer64())) { 1657 VIXL_ASSERT((size.immediate() % 16) == 0); 1658 } 1659 } 1660 1661 Sub(GetStackPointer64(), GetStackPointer64(), size); 1662 1663 // Make sure the real stack pointer reflects the claimed stack space. 1664 // We can't use stack memory below the stack pointer, it could be clobbered by 1665 // interupts and signal handlers. 1666 if (!sp.Is(GetStackPointer64())) { 1667 Mov(sp, GetStackPointer64()); 1668 } 1669 } 1670 1671 1672 void MacroAssembler::Drop(const Operand& size) { 1673 1674 if (size.IsZero()) { 1675 return; 1676 } 1677 1678 if (size.IsImmediate()) { 1679 VIXL_ASSERT(size.immediate() > 0); 1680 if (sp.Is(GetStackPointer64())) { 1681 VIXL_ASSERT((size.immediate() % 16) == 0); 1682 } 1683 } 1684 1685 Add(GetStackPointer64(), GetStackPointer64(), size); 1686 } 1687 1688 1689 void MacroAssembler::PushCalleeSavedRegisters() { 1690 // Ensure that the macro-assembler doesn't use any scratch registers. 1691 // 10 stp will be emitted. 1692 // TODO(all): Should we use GetCalleeSaved and SavedFP. 1693 InstructionAccurateScope scope(this, 10); 1694 1695 // This method must not be called unless the current stack pointer is sp. 1696 VIXL_ASSERT(sp.Is(GetStackPointer64())); 1697 1698 MemOperand tos(sp, -2 * static_cast<int>(kXRegSizeInBytes), PreIndex); 1699 1700 stp(x29, x30, tos); 1701 stp(x27, x28, tos); 1702 stp(x25, x26, tos); 1703 stp(x23, x24, tos); 1704 stp(x21, x22, tos); 1705 stp(x19, x20, tos); 1706 1707 stp(d14, d15, tos); 1708 stp(d12, d13, tos); 1709 stp(d10, d11, tos); 1710 stp(d8, d9, tos); 1711 } 1712 1713 1714 void MacroAssembler::PopCalleeSavedRegisters() { 1715 // Ensure that the macro-assembler doesn't use any scratch registers. 1716 // 10 ldp will be emitted. 1717 // TODO(all): Should we use GetCalleeSaved and SavedFP. 1718 InstructionAccurateScope scope(this, 10); 1719 1720 // This method must not be called unless the current stack pointer is sp. 1721 VIXL_ASSERT(sp.Is(GetStackPointer64())); 1722 1723 MemOperand tos(sp, 2 * kXRegSizeInBytes, PostIndex); 1724 1725 ldp(d8, d9, tos); 1726 ldp(d10, d11, tos); 1727 ldp(d12, d13, tos); 1728 ldp(d14, d15, tos); 1729 1730 ldp(x19, x20, tos); 1731 ldp(x21, x22, tos); 1732 ldp(x23, x24, tos); 1733 ldp(x25, x26, tos); 1734 ldp(x27, x28, tos); 1735 ldp(x29, x30, tos); 1736 } 1737 1738 void MacroAssembler::LoadCPURegList(CPURegList registers, 1739 const MemOperand& src) { 1740 LoadStoreCPURegListHelper(kLoad, registers, src); 1741 } 1742 1743 void MacroAssembler::StoreCPURegList(CPURegList registers, 1744 const MemOperand& dst) { 1745 LoadStoreCPURegListHelper(kStore, registers, dst); 1746 } 1747 1748 1749 void MacroAssembler::LoadStoreCPURegListHelper(LoadStoreCPURegListAction op, 1750 CPURegList registers, 1751 const MemOperand& mem) { 1752 // We do not handle pre-indexing or post-indexing. 1753 VIXL_ASSERT(!(mem.IsPreIndex() || mem.IsPostIndex())); 1754 VIXL_ASSERT(!registers.Overlaps(tmp_list_)); 1755 VIXL_ASSERT(!registers.Overlaps(fptmp_list_)); 1756 VIXL_ASSERT(!registers.IncludesAliasOf(sp)); 1757 1758 UseScratchRegisterScope temps(this); 1759 1760 MemOperand loc = BaseMemOperandForLoadStoreCPURegList(registers, 1761 mem, 1762 &temps); 1763 1764 while (registers.Count() >= 2) { 1765 const CPURegister& dst0 = registers.PopLowestIndex(); 1766 const CPURegister& dst1 = registers.PopLowestIndex(); 1767 if (op == kStore) { 1768 Stp(dst0, dst1, loc); 1769 } else { 1770 VIXL_ASSERT(op == kLoad); 1771 Ldp(dst0, dst1, loc); 1772 } 1773 loc.AddOffset(2 * registers.RegisterSizeInBytes()); 1774 } 1775 if (!registers.IsEmpty()) { 1776 if (op == kStore) { 1777 Str(registers.PopLowestIndex(), loc); 1778 } else { 1779 VIXL_ASSERT(op == kLoad); 1780 Ldr(registers.PopLowestIndex(), loc); 1781 } 1782 } 1783 } 1784 1785 MemOperand MacroAssembler::BaseMemOperandForLoadStoreCPURegList( 1786 const CPURegList& registers, 1787 const MemOperand& mem, 1788 UseScratchRegisterScope* scratch_scope) { 1789 // If necessary, pre-compute the base address for the accesses. 1790 if (mem.IsRegisterOffset()) { 1791 Register reg_base = scratch_scope->AcquireX(); 1792 ComputeAddress(reg_base, mem); 1793 return MemOperand(reg_base); 1794 1795 } else if (mem.IsImmediateOffset()) { 1796 int reg_size = registers.RegisterSizeInBytes(); 1797 int total_size = registers.TotalSizeInBytes(); 1798 int64_t min_offset = mem.offset(); 1799 int64_t max_offset = mem.offset() + std::max(0, total_size - 2 * reg_size); 1800 if ((registers.Count() >= 2) && 1801 (!Assembler::IsImmLSPair(min_offset, WhichPowerOf2(reg_size)) || 1802 !Assembler::IsImmLSPair(max_offset, WhichPowerOf2(reg_size)))) { 1803 Register reg_base = scratch_scope->AcquireX(); 1804 ComputeAddress(reg_base, mem); 1805 return MemOperand(reg_base); 1806 } 1807 } 1808 1809 return mem; 1810 } 1811 1812 void MacroAssembler::BumpSystemStackPointer(const Operand& space) { 1813 VIXL_ASSERT(!sp.Is(GetStackPointer64())); 1814 // TODO: Several callers rely on this not using scratch registers, so we use 1815 // the assembler directly here. However, this means that large immediate 1816 // values of 'space' cannot be handled. 1817 InstructionAccurateScope scope(this, 1); 1818 sub(sp, GetStackPointer64(), space); 1819 } 1820 1821 1822 void MacroAssembler::Trace(TraceParameters parameters, TraceCommand command) { 1823 1824 #ifdef JS_SIMULATOR_ARM64 1825 // The arguments to the trace pseudo instruction need to be contiguous in 1826 // memory, so make sure we don't try to emit a literal pool. 1827 InstructionAccurateScope scope(this, kTraceLength / kInstructionSize); 1828 1829 Label start; 1830 bind(&start); 1831 1832 // Refer to simulator-a64.h for a description of the marker and its 1833 // arguments. 1834 hlt(kTraceOpcode); 1835 1836 // VIXL_ASSERT(SizeOfCodeGeneratedSince(&start) == kTraceParamsOffset); 1837 dc32(parameters); 1838 1839 // VIXL_ASSERT(SizeOfCodeGeneratedSince(&start) == kTraceCommandOffset); 1840 dc32(command); 1841 #else 1842 // Emit nothing on real hardware. 1843 USE(parameters, command); 1844 #endif 1845 } 1846 1847 1848 void MacroAssembler::Log(TraceParameters parameters) { 1849 1850 #ifdef JS_SIMULATOR_ARM64 1851 // The arguments to the log pseudo instruction need to be contiguous in 1852 // memory, so make sure we don't try to emit a literal pool. 1853 InstructionAccurateScope scope(this, kLogLength / kInstructionSize); 1854 1855 Label start; 1856 bind(&start); 1857 1858 // Refer to simulator-a64.h for a description of the marker and its 1859 // arguments. 1860 hlt(kLogOpcode); 1861 1862 // VIXL_ASSERT(SizeOfCodeGeneratedSince(&start) == kLogParamsOffset); 1863 dc32(parameters); 1864 #else 1865 // Emit nothing on real hardware. 1866 USE(parameters); 1867 #endif 1868 } 1869 1870 1871 void MacroAssembler::EnableInstrumentation() { 1872 VIXL_ASSERT(!isprint(InstrumentStateEnable)); 1873 InstructionAccurateScope scope(this, 1); 1874 movn(xzr, InstrumentStateEnable); 1875 } 1876 1877 1878 void MacroAssembler::DisableInstrumentation() { 1879 VIXL_ASSERT(!isprint(InstrumentStateDisable)); 1880 InstructionAccurateScope scope(this, 1); 1881 movn(xzr, InstrumentStateDisable); 1882 } 1883 1884 1885 void MacroAssembler::AnnotateInstrumentation(const char* marker_name) { 1886 VIXL_ASSERT(strlen(marker_name) == 2); 1887 1888 // We allow only printable characters in the marker names. Unprintable 1889 // characters are reserved for controlling features of the instrumentation. 1890 VIXL_ASSERT(isprint(marker_name[0]) && isprint(marker_name[1])); 1891 1892 InstructionAccurateScope scope(this, 1); 1893 movn(xzr, (marker_name[1] << 8) | marker_name[0]); 1894 } 1895 1896 1897 void UseScratchRegisterScope::Open(MacroAssembler* masm) { 1898 VIXL_ASSERT(!initialised_); 1899 available_ = masm->TmpList(); 1900 availablefp_ = masm->FPTmpList(); 1901 old_available_ = available_->list(); 1902 old_availablefp_ = availablefp_->list(); 1903 VIXL_ASSERT(available_->type() == CPURegister::kRegister); 1904 VIXL_ASSERT(availablefp_->type() == CPURegister::kVRegister); 1905 #ifdef DEBUG 1906 initialised_ = true; 1907 #endif 1908 } 1909 1910 1911 void UseScratchRegisterScope::Close() { 1912 if (available_) { 1913 available_->set_list(old_available_); 1914 available_ = NULL; 1915 } 1916 if (availablefp_) { 1917 availablefp_->set_list(old_availablefp_); 1918 availablefp_ = NULL; 1919 } 1920 #ifdef DEBUG 1921 initialised_ = false; 1922 #endif 1923 } 1924 1925 1926 UseScratchRegisterScope::UseScratchRegisterScope(MacroAssembler* masm) { 1927 #ifdef DEBUG 1928 initialised_ = false; 1929 #endif 1930 Open(masm); 1931 } 1932 1933 // This allows deferred (and optional) initialisation of the scope. 1934 UseScratchRegisterScope::UseScratchRegisterScope() 1935 : available_(NULL), availablefp_(NULL), 1936 old_available_(0), old_availablefp_(0) { 1937 #ifdef DEBUG 1938 initialised_ = false; 1939 #endif 1940 } 1941 1942 UseScratchRegisterScope::~UseScratchRegisterScope() { 1943 Close(); 1944 } 1945 1946 1947 bool UseScratchRegisterScope::IsAvailable(const CPURegister& reg) const { 1948 return available_->IncludesAliasOf(reg) || availablefp_->IncludesAliasOf(reg); 1949 } 1950 1951 bool UseScratchRegisterScope::HasAvailableRegister() const { 1952 return !available_->IsEmpty(); 1953 } 1954 1955 Register UseScratchRegisterScope::AcquireSameSizeAs(const Register& reg) { 1956 int code = AcquireNextAvailable(available_).code(); 1957 return Register(code, reg.size()); 1958 } 1959 1960 1961 FPRegister UseScratchRegisterScope::AcquireSameSizeAs(const FPRegister& reg) { 1962 int code = AcquireNextAvailable(availablefp_).code(); 1963 return FPRegister(code, reg.size()); 1964 } 1965 1966 1967 void UseScratchRegisterScope::Release(const CPURegister& reg) { 1968 VIXL_ASSERT(initialised_); 1969 if (reg.IsRegister()) { 1970 ReleaseByCode(available_, reg.code()); 1971 } else if (reg.IsFPRegister()) { 1972 ReleaseByCode(availablefp_, reg.code()); 1973 } else { 1974 VIXL_ASSERT(reg.IsNone()); 1975 } 1976 } 1977 1978 1979 void UseScratchRegisterScope::Include(const CPURegList& list) { 1980 VIXL_ASSERT(initialised_); 1981 if (list.type() == CPURegister::kRegister) { 1982 // Make sure that neither sp nor xzr are included the list. 1983 IncludeByRegList(available_, list.list() & ~(xzr.Bit() | sp.Bit())); 1984 } else { 1985 VIXL_ASSERT(list.type() == CPURegister::kVRegister); 1986 IncludeByRegList(availablefp_, list.list()); 1987 } 1988 } 1989 1990 1991 void UseScratchRegisterScope::Include(const Register& reg1, 1992 const Register& reg2, 1993 const Register& reg3, 1994 const Register& reg4) { 1995 VIXL_ASSERT(initialised_); 1996 RegList include = reg1.Bit() | reg2.Bit() | reg3.Bit() | reg4.Bit(); 1997 // Make sure that neither sp nor xzr are included the list. 1998 include &= ~(xzr.Bit() | sp.Bit()); 1999 2000 IncludeByRegList(available_, include); 2001 } 2002 2003 2004 void UseScratchRegisterScope::Include(const FPRegister& reg1, 2005 const FPRegister& reg2, 2006 const FPRegister& reg3, 2007 const FPRegister& reg4) { 2008 RegList include = reg1.Bit() | reg2.Bit() | reg3.Bit() | reg4.Bit(); 2009 IncludeByRegList(availablefp_, include); 2010 } 2011 2012 2013 void UseScratchRegisterScope::Exclude(const CPURegList& list) { 2014 if (list.type() == CPURegister::kRegister) { 2015 ExcludeByRegList(available_, list.list()); 2016 } else { 2017 VIXL_ASSERT(list.type() == CPURegister::kVRegister); 2018 ExcludeByRegList(availablefp_, list.list()); 2019 } 2020 } 2021 2022 2023 void UseScratchRegisterScope::Exclude(const Register& reg1, 2024 const Register& reg2, 2025 const Register& reg3, 2026 const Register& reg4) { 2027 RegList exclude = reg1.Bit() | reg2.Bit() | reg3.Bit() | reg4.Bit(); 2028 ExcludeByRegList(available_, exclude); 2029 } 2030 2031 2032 void UseScratchRegisterScope::Exclude(const FPRegister& reg1, 2033 const FPRegister& reg2, 2034 const FPRegister& reg3, 2035 const FPRegister& reg4) { 2036 RegList excludefp = reg1.Bit() | reg2.Bit() | reg3.Bit() | reg4.Bit(); 2037 ExcludeByRegList(availablefp_, excludefp); 2038 } 2039 2040 2041 void UseScratchRegisterScope::Exclude(const CPURegister& reg1, 2042 const CPURegister& reg2, 2043 const CPURegister& reg3, 2044 const CPURegister& reg4) { 2045 RegList exclude = 0; 2046 RegList excludefp = 0; 2047 2048 const CPURegister regs[] = {reg1, reg2, reg3, reg4}; 2049 2050 for (unsigned i = 0; i < (sizeof(regs) / sizeof(regs[0])); i++) { 2051 if (regs[i].IsRegister()) { 2052 exclude |= regs[i].Bit(); 2053 } else if (regs[i].IsFPRegister()) { 2054 excludefp |= regs[i].Bit(); 2055 } else { 2056 VIXL_ASSERT(regs[i].IsNone()); 2057 } 2058 } 2059 2060 ExcludeByRegList(available_, exclude); 2061 ExcludeByRegList(availablefp_, excludefp); 2062 } 2063 2064 2065 void UseScratchRegisterScope::ExcludeAll() { 2066 ExcludeByRegList(available_, available_->list()); 2067 ExcludeByRegList(availablefp_, availablefp_->list()); 2068 } 2069 2070 2071 CPURegister UseScratchRegisterScope::AcquireNextAvailable( 2072 CPURegList* available) { 2073 VIXL_CHECK(!available->IsEmpty()); 2074 CPURegister result = available->PopLowestIndex(); 2075 VIXL_ASSERT(!AreAliased(result, xzr, sp)); 2076 return result; 2077 } 2078 2079 2080 void UseScratchRegisterScope::ReleaseByCode(CPURegList* available, int code) { 2081 ReleaseByRegList(available, static_cast<RegList>(1) << code); 2082 } 2083 2084 2085 void UseScratchRegisterScope::ReleaseByRegList(CPURegList* available, 2086 RegList regs) { 2087 available->set_list(available->list() | regs); 2088 } 2089 2090 2091 void UseScratchRegisterScope::IncludeByRegList(CPURegList* available, 2092 RegList regs) { 2093 available->set_list(available->list() | regs); 2094 } 2095 2096 2097 void UseScratchRegisterScope::ExcludeByRegList(CPURegList* available, 2098 RegList exclude) { 2099 available->set_list(available->list() & ~exclude); 2100 } 2101 2102 } // namespace vixl