MacroAssembler-riscv64.cpp (231499B)
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 // Copyright 2021 the V8 project authors. All rights reserved. 8 // Use of this source code is governed by a BSD-style license that can be 9 // found in the LICENSE file. 10 #include "jit/riscv64/MacroAssembler-riscv64.h" 11 12 #include "jsmath.h" 13 #include "jit/Bailouts.h" 14 #include "jit/BaselineFrame.h" 15 #include "jit/JitFrames.h" 16 #include "jit/JitRuntime.h" 17 #include "jit/MacroAssembler.h" 18 #include "jit/MoveEmitter.h" 19 #include "jit/riscv64/SharedICRegisters-riscv64.h" 20 #include "util/Memory.h" 21 #include "vm/JitActivation.h" // jit::JitActivation 22 #include "vm/JSContext.h" 23 #include "wasm/WasmStubs.h" 24 25 #include "jit/MacroAssembler-inl.h" 26 27 namespace js { 28 namespace jit { 29 30 MacroAssembler& MacroAssemblerRiscv64::asMasm() { 31 return *static_cast<MacroAssembler*>(this); 32 } 33 34 const MacroAssembler& MacroAssemblerRiscv64::asMasm() const { 35 return *static_cast<const MacroAssembler*>(this); 36 } 37 38 void MacroAssemblerRiscv64::ma_cmp_set(Register rd, Register rj, ImmWord imm, 39 Condition c) { 40 if (imm.value <= INT32_MAX) { 41 ma_cmp_set(rd, rj, Imm32(uint32_t(imm.value)), c); 42 } else { 43 UseScratchRegisterScope temps(this); 44 Register scratch = temps.Acquire(); 45 ma_li(scratch, imm); 46 ma_cmp_set(rd, rj, scratch, c); 47 } 48 } 49 50 void MacroAssemblerRiscv64::ma_cmp_set(Register rd, Register rj, ImmPtr imm, 51 Condition c) { 52 ma_cmp_set(rd, rj, ImmWord(uintptr_t(imm.value)), c); 53 } 54 55 void MacroAssemblerRiscv64::ma_cmp_set(Register rd, Register rj, ImmGCPtr imm, 56 Condition c) { 57 UseScratchRegisterScope temps(this); 58 Register scratch = temps.Acquire(); 59 ma_li(scratch, imm); 60 ma_cmp_set(rd, rj, scratch, c); 61 } 62 63 void MacroAssemblerRiscv64::ma_cmp_set(Register rd, Address address, 64 Register rhs, Condition c) { 65 UseScratchRegisterScope temps(this); 66 Register scratch2 = temps.Acquire(); 67 ma_load(scratch2, address, SizeDouble); 68 ma_cmp_set(rd, Register(scratch2), rhs, c); 69 } 70 71 void MacroAssemblerRiscv64::ma_cmp_set(Register rd, Address address, Imm32 imm, 72 Condition c) { 73 // TODO(riscv): 32-bit ma_cmp_set? 74 UseScratchRegisterScope temps(this); 75 Register scratch2 = temps.Acquire(); 76 ma_load(scratch2, address, SizeWord); 77 ma_cmp_set(rd, Register(scratch2), imm, c); 78 } 79 80 void MacroAssemblerRiscv64::ma_cmp_set(Register rd, Address address, 81 ImmWord imm, Condition c) { 82 UseScratchRegisterScope temps(this); 83 Register scratch2 = temps.Acquire(); 84 ma_load(scratch2, address, SizeDouble); 85 ma_cmp_set(rd, Register(scratch2), imm, c); 86 } 87 88 void MacroAssemblerRiscv64::ma_cmp_set(Register rd, Register rj, Imm32 imm, 89 Condition c) { 90 if (imm.value == 0) { 91 switch (c) { 92 case Equal: 93 case BelowOrEqual: 94 ma_sltu(rd, rj, Operand(1)); 95 break; 96 case NotEqual: 97 case Above: 98 sltu(rd, zero, rj); 99 break; 100 case AboveOrEqual: 101 case Below: 102 ori(rd, zero, c == AboveOrEqual ? 1 : 0); 103 break; 104 case GreaterThan: 105 case LessThanOrEqual: 106 slt(rd, zero, rj); 107 if (c == LessThanOrEqual) { 108 xori(rd, rd, 1); 109 } 110 break; 111 case LessThan: 112 case GreaterThanOrEqual: 113 slt(rd, rj, zero); 114 if (c == GreaterThanOrEqual) { 115 xori(rd, rd, 1); 116 } 117 break; 118 case Zero: 119 ma_sltu(rd, rj, Operand(1)); 120 break; 121 case NonZero: 122 sltu(rd, zero, rj); 123 break; 124 case Signed: 125 slt(rd, rj, zero); 126 break; 127 case NotSigned: 128 slt(rd, rj, zero); 129 xori(rd, rd, 1); 130 break; 131 default: 132 MOZ_CRASH("Invalid condition."); 133 } 134 return; 135 } 136 137 switch (c) { 138 case Equal: 139 case NotEqual: 140 ma_xor(rd, rj, imm); 141 if (c == Equal) { 142 ma_sltu(rd, rd, Operand(1)); 143 } else { 144 sltu(rd, zero, rd); 145 } 146 break; 147 case Zero: 148 case NonZero: 149 case Signed: 150 case NotSigned: 151 MOZ_CRASH("Invalid condition."); 152 default: 153 Condition cond = ma_cmp(rd, rj, imm, c); 154 MOZ_ASSERT(cond == Equal || cond == NotEqual); 155 156 if (cond == Equal) xori(rd, rd, 1); 157 } 158 } 159 160 Assembler::Condition MacroAssemblerRiscv64::ma_cmp(Register dest, Register lhs, 161 Register rhs, Condition c) { 162 switch (c) { 163 case Above: 164 // bgtu s,t,label => 165 // sltu at,t,s 166 // bne at,$zero,offs 167 sltu(dest, rhs, lhs); 168 return NotEqual; 169 case AboveOrEqual: 170 // bgeu s,t,label => 171 // sltu at,s,t 172 // beq at,$zero,offs 173 sltu(dest, lhs, rhs); 174 return Equal; 175 case Below: 176 // bltu s,t,label => 177 // sltu at,s,t 178 // bne at,$zero,offs 179 sltu(dest, lhs, rhs); 180 return NotEqual; 181 case BelowOrEqual: 182 // bleu s,t,label => 183 // sltu at,t,s 184 // beq at,$zero,offs 185 sltu(dest, rhs, lhs); 186 return Equal; 187 case GreaterThan: 188 // bgt s,t,label => 189 // slt at,t,s 190 // bne at,$zero,offs 191 slt(dest, rhs, lhs); 192 return NotEqual; 193 case GreaterThanOrEqual: 194 // bge s,t,label => 195 // slt at,s,t 196 // beq at,$zero,offs 197 slt(dest, lhs, rhs); 198 return Equal; 199 case LessThan: 200 // blt s,t,label => 201 // slt at,s,t 202 // bne at,$zero,offs 203 slt(dest, lhs, rhs); 204 return NotEqual; 205 case LessThanOrEqual: 206 // ble s,t,label => 207 // slt at,t,s 208 // beq at,$zero,offs 209 slt(dest, rhs, lhs); 210 return Equal; 211 default: 212 MOZ_CRASH("Invalid condition."); 213 } 214 return Always; 215 } 216 217 Assembler::Condition MacroAssemblerRiscv64::ma_cmp(Register dest, Register lhs, 218 Imm32 imm, Condition c) { 219 UseScratchRegisterScope temps(this); 220 Register scratch = temps.Acquire(); 221 MOZ_RELEASE_ASSERT(lhs != scratch); 222 223 switch (c) { 224 case Above: 225 case BelowOrEqual: 226 if (imm.value != 0x7fffffff && is_intn(imm.value + 1, 12) && 227 imm.value != -1) { 228 // lhs <= rhs via lhs < rhs + 1 if rhs + 1 does not overflow 229 ma_sltu(dest, lhs, Operand(imm.value + 1)); 230 231 return (c == BelowOrEqual ? NotEqual : Equal); 232 } else { 233 ma_li(scratch, imm); 234 sltu(dest, scratch, lhs); 235 return (c == BelowOrEqual ? Equal : NotEqual); 236 } 237 case AboveOrEqual: 238 case Below: 239 if (is_intn(imm.value, 12)) { 240 ma_sltu(dest, lhs, Operand(imm.value)); 241 } else { 242 ma_li(scratch, imm); 243 sltu(dest, lhs, scratch); 244 } 245 return (c == AboveOrEqual ? Equal : NotEqual); 246 case GreaterThan: 247 case LessThanOrEqual: 248 if (imm.value != 0x7fffffff && is_intn(imm.value + 1, 12)) { 249 // lhs <= rhs via lhs < rhs + 1. 250 ma_slt(dest, lhs, Operand(imm.value + 1)); 251 return (c == LessThanOrEqual ? NotEqual : Equal); 252 } else { 253 ma_li(scratch, imm); 254 slt(dest, scratch, lhs); 255 return (c == LessThanOrEqual ? Equal : NotEqual); 256 } 257 case GreaterThanOrEqual: 258 case LessThan: 259 if (is_intn(imm.value, 12)) { 260 ma_slt(dest, lhs, imm); 261 } else { 262 ma_li(scratch, imm); 263 slt(dest, lhs, scratch); 264 } 265 return (c == GreaterThanOrEqual ? Equal : NotEqual); 266 default: 267 MOZ_CRASH("Invalid condition."); 268 } 269 return Always; 270 } 271 272 void MacroAssemblerRiscv64::ma_cmp_set(Register rd, Register rj, Register rk, 273 Condition c) { 274 switch (c) { 275 case Equal: 276 // seq d,s,t => 277 // xor d,s,t 278 // sltiu d,d,1 279 xor_(rd, rj, rk); 280 ma_sltu(rd, rd, Operand(1)); 281 break; 282 case NotEqual: 283 // sne d,s,t => 284 // xor d,s,t 285 // sltu d,$zero,d 286 xor_(rd, rj, rk); 287 sltu(rd, zero, rd); 288 break; 289 case Above: 290 // sgtu d,s,t => 291 // sltu d,t,s 292 sltu(rd, rk, rj); 293 break; 294 case AboveOrEqual: 295 // sgeu d,s,t => 296 // sltu d,s,t 297 // xori d,d,1 298 sltu(rd, rj, rk); 299 xori(rd, rd, 1); 300 break; 301 case Below: 302 // sltu d,s,t 303 sltu(rd, rj, rk); 304 break; 305 case BelowOrEqual: 306 // sleu d,s,t => 307 // sltu d,t,s 308 // xori d,d,1 309 sltu(rd, rk, rj); 310 xori(rd, rd, 1); 311 break; 312 case GreaterThan: 313 // sgt d,s,t => 314 // slt d,t,s 315 slt(rd, rk, rj); 316 break; 317 case GreaterThanOrEqual: 318 // sge d,s,t => 319 // slt d,s,t 320 // xori d,d,1 321 slt(rd, rj, rk); 322 xori(rd, rd, 1); 323 break; 324 case LessThan: 325 // slt d,s,t 326 slt(rd, rj, rk); 327 break; 328 case LessThanOrEqual: 329 // sle d,s,t => 330 // slt d,t,s 331 // xori d,d,1 332 slt(rd, rk, rj); 333 xori(rd, rd, 1); 334 break; 335 case Zero: 336 MOZ_ASSERT(rj == rk); 337 // seq d,s,$zero => 338 // sltiu d,s,1 339 ma_sltu(rd, rj, Operand(1)); 340 break; 341 case NonZero: 342 MOZ_ASSERT(rj == rk); 343 // sne d,s,$zero => 344 // sltu d,$zero,s 345 sltu(rd, zero, rj); 346 break; 347 case Signed: 348 MOZ_ASSERT(rj == rk); 349 slt(rd, rj, zero); 350 break; 351 case NotSigned: 352 MOZ_ASSERT(rj == rk); 353 // sge d,s,$zero => 354 // slt d,s,$zero 355 // xori d,d,1 356 slt(rd, rj, zero); 357 xori(rd, rd, 1); 358 break; 359 default: 360 MOZ_CRASH("Invalid condition."); 361 } 362 } 363 364 void MacroAssemblerRiscv64::ma_compareF32(Register rd, DoubleCondition cc, 365 FloatRegister cmp1, 366 FloatRegister cmp2) { 367 switch (cc) { 368 case DoubleEqual: 369 feq_s(rd, cmp1, cmp2); 370 return; 371 case DoubleEqualOrUnordered: { 372 UseScratchRegisterScope temps(this); 373 Register scratch = temps.Acquire(); 374 flt_s(rd, cmp1, cmp2); 375 flt_s(scratch, cmp2, cmp1); 376 or_(rd, rd, scratch); 377 NegateBool(rd, rd); 378 return; 379 } 380 case DoubleNotEqual: { 381 UseScratchRegisterScope temps(this); 382 Register scratch = temps.Acquire(); 383 flt_s(rd, cmp1, cmp2); 384 flt_s(scratch, cmp2, cmp1); 385 or_(rd, rd, scratch); 386 return; 387 } 388 case DoubleNotEqualOrUnordered: 389 feq_s(rd, cmp1, cmp2); 390 NegateBool(rd, rd); 391 return; 392 case DoubleLessThan: 393 flt_s(rd, cmp1, cmp2); 394 return; 395 case DoubleLessThanOrUnordered: 396 fle_s(rd, cmp2, cmp1); 397 NegateBool(rd, rd); 398 return; 399 case DoubleGreaterThanOrEqual: 400 fle_s(rd, cmp2, cmp1); 401 return; 402 case DoubleGreaterThanOrEqualOrUnordered: 403 flt_s(rd, cmp1, cmp2); 404 NegateBool(rd, rd); 405 return; 406 case DoubleLessThanOrEqual: 407 fle_s(rd, cmp1, cmp2); 408 return; 409 case DoubleLessThanOrEqualOrUnordered: 410 flt_s(rd, cmp2, cmp1); 411 NegateBool(rd, rd); 412 return; 413 case DoubleGreaterThan: 414 flt_s(rd, cmp2, cmp1); 415 return; 416 case DoubleGreaterThanOrUnordered: 417 fle_s(rd, cmp1, cmp2); 418 NegateBool(rd, rd); 419 return; 420 case DoubleOrdered: 421 CompareIsNotNanF32(rd, cmp1, cmp2); 422 return; 423 case DoubleUnordered: 424 CompareIsNanF32(rd, cmp1, cmp2); 425 return; 426 } 427 } 428 429 void MacroAssemblerRiscv64::ma_compareF64(Register rd, DoubleCondition cc, 430 FloatRegister cmp1, 431 FloatRegister cmp2) { 432 switch (cc) { 433 case DoubleEqual: 434 feq_d(rd, cmp1, cmp2); 435 return; 436 case DoubleEqualOrUnordered: { 437 UseScratchRegisterScope temps(this); 438 Register scratch = temps.Acquire(); 439 flt_d(rd, cmp1, cmp2); 440 flt_d(scratch, cmp2, cmp1); 441 or_(rd, rd, scratch); 442 NegateBool(rd, rd); 443 return; 444 } 445 case DoubleNotEqual: { 446 UseScratchRegisterScope temps(this); 447 Register scratch = temps.Acquire(); 448 flt_d(rd, cmp1, cmp2); 449 flt_d(scratch, cmp2, cmp1); 450 or_(rd, rd, scratch); 451 return; 452 } 453 case DoubleNotEqualOrUnordered: 454 feq_d(rd, cmp1, cmp2); 455 NegateBool(rd, rd); 456 return; 457 case DoubleLessThan: 458 flt_d(rd, cmp1, cmp2); 459 return; 460 case DoubleLessThanOrUnordered: 461 fle_d(rd, cmp2, cmp1); 462 NegateBool(rd, rd); 463 return; 464 case DoubleGreaterThanOrEqual: 465 fle_d(rd, cmp2, cmp1); 466 return; 467 case DoubleGreaterThanOrEqualOrUnordered: 468 flt_d(rd, cmp1, cmp2); 469 NegateBool(rd, rd); 470 return; 471 case DoubleLessThanOrEqual: 472 fle_d(rd, cmp1, cmp2); 473 return; 474 case DoubleLessThanOrEqualOrUnordered: 475 flt_d(rd, cmp2, cmp1); 476 NegateBool(rd, rd); 477 return; 478 case DoubleGreaterThan: 479 flt_d(rd, cmp2, cmp1); 480 return; 481 case DoubleGreaterThanOrUnordered: 482 fle_d(rd, cmp1, cmp2); 483 NegateBool(rd, rd); 484 return; 485 case DoubleOrdered: 486 CompareIsNotNanF64(rd, cmp1, cmp2); 487 return; 488 case DoubleUnordered: 489 CompareIsNanF64(rd, cmp1, cmp2); 490 return; 491 } 492 } 493 494 void MacroAssemblerRiscv64Compat::movePtr(Register src, Register dest) { 495 mv(dest, src); 496 } 497 void MacroAssemblerRiscv64Compat::movePtr(ImmWord imm, Register dest) { 498 ma_li(dest, imm); 499 } 500 501 void MacroAssemblerRiscv64Compat::movePtr(ImmGCPtr imm, Register dest) { 502 ma_li(dest, imm); 503 } 504 505 void MacroAssemblerRiscv64Compat::movePtr(ImmPtr imm, Register dest) { 506 movePtr(ImmWord(uintptr_t(imm.value)), dest); 507 } 508 void MacroAssemblerRiscv64Compat::movePtr(wasm::SymbolicAddress imm, 509 Register dest) { 510 DEBUG_PRINTF("[ %s\n", __FUNCTION__); 511 BlockTrampolinePoolScope block_trampoline_pool(this, 8); 512 append(wasm::SymbolicAccess(CodeOffset(nextOffset().getOffset()), imm)); 513 ma_liPatchable(dest, ImmWord(-1), Li64); 514 DEBUG_PRINTF("]\n"); 515 } 516 517 bool MacroAssemblerRiscv64Compat::buildOOLFakeExitFrame(void* fakeReturnAddr) { 518 asMasm().Push(FrameDescriptor(FrameType::IonJS)); // descriptor_ 519 asMasm().Push(ImmPtr(fakeReturnAddr)); 520 asMasm().Push(FramePointer); 521 return true; 522 } 523 524 void MacroAssemblerRiscv64Compat::convertUInt32ToDouble(Register src, 525 FloatRegister dest) { 526 fcvt_d_wu(dest, src); 527 } 528 529 void MacroAssemblerRiscv64Compat::convertUInt64ToDouble(Register src, 530 FloatRegister dest) { 531 fcvt_d_lu(dest, src); 532 } 533 534 void MacroAssemblerRiscv64Compat::convertUInt32ToFloat32(Register src, 535 FloatRegister dest) { 536 fcvt_s_wu(dest, src); 537 } 538 539 void MacroAssemblerRiscv64Compat::convertDoubleToFloat32(FloatRegister src, 540 FloatRegister dest) { 541 fcvt_s_d(dest, src); 542 } 543 544 void MacroAssemblerRiscv64Compat::minMax32(Register lhs, Register rhs, 545 Register dest, bool isMax) { 546 if (rhs == dest) { 547 std::swap(lhs, rhs); 548 } 549 550 auto cond = isMax ? Assembler::GreaterThan : Assembler::LessThan; 551 if (lhs != dest) { 552 move32(lhs, dest); 553 } 554 asMasm().cmp32Move32(cond, rhs, lhs, rhs, dest); 555 } 556 557 void MacroAssemblerRiscv64Compat::minMax32(Register lhs, Imm32 rhs, 558 Register dest, bool isMax) { 559 if (rhs.value == 0) { 560 UseScratchRegisterScope temps(this); 561 Register scratch = temps.Acquire(); 562 563 if (isMax) { 564 // dest = -(lhs > 0 ? 1 : 0) & lhs 565 sgtz(scratch, lhs); 566 neg(scratch, scratch); 567 and_(dest, lhs, scratch); 568 } else { 569 // dest = (lhs >> 31) & lhs 570 sraiw(scratch, lhs, 31); 571 and_(dest, lhs, scratch); 572 } 573 return; 574 } 575 576 // Uses branches because "Zicond" extension isn't yet supported. 577 578 auto cond = 579 isMax ? Assembler::GreaterThanOrEqual : Assembler::LessThanOrEqual; 580 if (lhs != dest) { 581 move32(lhs, dest); 582 } 583 Label done; 584 asMasm().branch32(cond, lhs, rhs, &done); 585 move32(rhs, dest); 586 bind(&done); 587 } 588 589 void MacroAssemblerRiscv64Compat::minMaxPtr(Register lhs, Register rhs, 590 Register dest, bool isMax) { 591 if (rhs == dest) { 592 std::swap(lhs, rhs); 593 } 594 595 auto cond = isMax ? Assembler::GreaterThan : Assembler::LessThan; 596 if (lhs != dest) { 597 movePtr(lhs, dest); 598 } 599 asMasm().cmpPtrMovePtr(cond, rhs, lhs, rhs, dest); 600 } 601 602 void MacroAssemblerRiscv64Compat::minMaxPtr(Register lhs, ImmWord rhs, 603 Register dest, bool isMax) { 604 if (rhs.value == 0) { 605 UseScratchRegisterScope temps(this); 606 Register scratch = temps.Acquire(); 607 608 if (isMax) { 609 // dest = -(lhs > 0 ? 1 : 0) & lhs 610 sgtz(scratch, lhs); 611 neg(scratch, scratch); 612 and_(dest, lhs, scratch); 613 } else { 614 // dest = (lhs >> 63) & lhs 615 srai(scratch, lhs, 63); 616 and_(dest, lhs, scratch); 617 } 618 return; 619 } 620 621 // Uses branches because "Zicond" extension isn't yet supported. 622 623 auto cond = 624 isMax ? Assembler::GreaterThanOrEqual : Assembler::LessThanOrEqual; 625 if (lhs != dest) { 626 movePtr(lhs, dest); 627 } 628 Label done; 629 asMasm().branchPtr(cond, lhs, rhs, &done); 630 movePtr(rhs, dest); 631 bind(&done); 632 } 633 634 template <typename F> 635 void MacroAssemblerRiscv64::RoundHelper(FPURegister dst, FPURegister src, 636 FPURegister fpu_scratch, 637 FPURoundingMode frm) { 638 BlockTrampolinePoolScope block_trampoline_pool(this, 20, 2); 639 UseScratchRegisterScope temps(this); 640 Register scratch2 = temps.Acquire(); 641 642 MOZ_ASSERT((std::is_same<float, F>::value) || 643 (std::is_same<double, F>::value)); 644 // Need at least two FPRs, so check against dst == src == fpu_scratch 645 MOZ_ASSERT(!(dst == src && dst == fpu_scratch)); 646 647 const int kFloatMantissaBits = 648 sizeof(F) == 4 ? kFloat32MantissaBits : kFloat64MantissaBits; 649 const int kFloatExponentBits = 650 sizeof(F) == 4 ? kFloat32ExponentBits : kFloat64ExponentBits; 651 const int kFloatExponentBias = 652 sizeof(F) == 4 ? kFloat32ExponentBias : kFloat64ExponentBias; 653 Label done; 654 655 { 656 UseScratchRegisterScope temps2(this); 657 Register scratch = temps2.Acquire(); 658 // extract exponent value of the source floating-point to scratch 659 if (std::is_same<F, double>::value) { 660 fmv_x_d(scratch, src); 661 } else { 662 fmv_x_w(scratch, src); 663 } 664 ExtractBits(scratch2, scratch, kFloatMantissaBits, kFloatExponentBits); 665 } 666 667 // if src is NaN/+-Infinity/+-Zero or if the exponent is larger than # of bits 668 // in mantissa, the result is the same as src, so move src to dest (to avoid 669 // generating another branch) 670 if (dst != src) { 671 if (std::is_same<F, double>::value) { 672 fmv_d(dst, src); 673 } else { 674 fmv_s(dst, src); 675 } 676 } 677 { 678 Label not_NaN; 679 UseScratchRegisterScope temps2(this); 680 Register scratch = temps2.Acquire(); 681 // According to the wasm spec 682 // (https://webassembly.github.io/spec/core/exec/numerics.html#aux-nans) 683 // if input is canonical NaN, then output is canonical NaN, and if input is 684 // any other NaN, then output is any NaN with most significant bit of 685 // payload is 1. In RISC-V, feq_d will set scratch to 0 if src is a NaN. If 686 // src is not a NaN, branch to the label and do nothing, but if it is, 687 // fmin_d will set dst to the canonical NaN. 688 if (std::is_same<F, double>::value) { 689 feq_d(scratch, src, src); 690 bnez(scratch, ¬_NaN); 691 fmin_d(dst, src, src); 692 } else { 693 feq_s(scratch, src, src); 694 bnez(scratch, ¬_NaN); 695 fmin_s(dst, src, src); 696 } 697 bind(¬_NaN); 698 } 699 700 // If real exponent (i.e., scratch2 - kFloatExponentBias) is greater than 701 // kFloat32MantissaBits, it means the floating-point value has no fractional 702 // part, thus the input is already rounded, jump to done. Note that, NaN and 703 // Infinity in floating-point representation sets maximal exponent value, so 704 // they also satisfy (scratch2 - kFloatExponentBias >= kFloatMantissaBits), 705 // and JS round semantics specify that rounding of NaN (Infinity) returns NaN 706 // (Infinity), so NaN and Infinity are considered rounded value too. 707 ma_branch(&done, GreaterThanOrEqual, scratch2, 708 Operand(kFloatExponentBias + kFloatMantissaBits)); 709 710 // Actual rounding is needed along this path 711 712 // old_src holds the original input, needed for the case of src == dst 713 FPURegister old_src = src; 714 if (src == dst) { 715 MOZ_ASSERT(fpu_scratch != dst); 716 fmv_d(fpu_scratch, src); 717 old_src = fpu_scratch; 718 } 719 720 // Since only input whose real exponent value is less than kMantissaBits 721 // (i.e., 23 or 52-bits) falls into this path, the value range of the input 722 // falls into that of 23- or 53-bit integers. So we round the input to integer 723 // values, then convert them back to floating-point. 724 { 725 UseScratchRegisterScope temps(this); 726 Register scratch = temps.Acquire(); 727 if (std::is_same<F, double>::value) { 728 fcvt_l_d(scratch, src, frm); 729 fcvt_d_l(dst, scratch, frm); 730 } else { 731 fcvt_w_s(scratch, src, frm); 732 fcvt_s_w(dst, scratch, frm); 733 } 734 } 735 // A special handling is needed if the input is a very small positive/negative 736 // number that rounds to zero. JS semantics requires that the rounded result 737 // retains the sign of the input, so a very small positive (negative) 738 // floating-point number should be rounded to positive (negative) 0. 739 // Therefore, we use sign-bit injection to produce +/-0 correctly. Instead of 740 // testing for zero w/ a branch, we just insert sign-bit for everyone on this 741 // path (this is where old_src is needed) 742 if (std::is_same<F, double>::value) { 743 fsgnj_d(dst, dst, old_src); 744 } else { 745 fsgnj_s(dst, dst, old_src); 746 } 747 748 bind(&done); 749 } 750 751 template <typename CvtFunc> 752 void MacroAssemblerRiscv64::RoundFloatingPointToInteger(Register rd, 753 FPURegister fs, 754 Register result, 755 CvtFunc fcvt_generator, 756 bool Inexact) { 757 // Save csr_fflags to scratch & clear exception flags 758 if (result != Register::Invalid()) { 759 BlockTrampolinePoolScope block_trampoline_pool(this, 6); 760 UseScratchRegisterScope temps(this); 761 Register scratch = temps.Acquire(); 762 763 int exception_flags = kInvalidOperation; 764 if (Inexact) exception_flags |= kInexact; 765 csrrci(scratch, csr_fflags, exception_flags); 766 767 // actual conversion instruction 768 fcvt_generator(this, rd, fs); 769 770 // check kInvalidOperation flag (out-of-range, NaN) 771 // set result to 1 if normal, otherwise set result to 0 for abnormal 772 frflags(result); 773 andi(result, result, exception_flags); 774 seqz(result, result); // result <-- 1 (normal), result <-- 0 (abnormal) 775 776 // restore csr_fflags 777 csrw(csr_fflags, scratch); 778 } else { 779 // actual conversion instruction 780 fcvt_generator(this, rd, fs); 781 } 782 } 783 784 void MacroAssemblerRiscv64::Trunc_uw_d(Register rd, FPURegister fs, 785 Register result, bool Inexact) { 786 RoundFloatingPointToInteger( 787 rd, fs, result, 788 [](MacroAssemblerRiscv64* masm, Register dst, FPURegister src) { 789 masm->fcvt_wu_d(dst, src, RTZ); 790 }, 791 Inexact); 792 } 793 794 void MacroAssemblerRiscv64::Trunc_w_d(Register rd, FPURegister fs, 795 Register result, bool Inexact) { 796 RoundFloatingPointToInteger( 797 rd, fs, result, 798 [](MacroAssemblerRiscv64* masm, Register dst, FPURegister src) { 799 masm->fcvt_w_d(dst, src, RTZ); 800 }, 801 Inexact); 802 } 803 804 void MacroAssemblerRiscv64::Trunc_uw_s(Register rd, FPURegister fs, 805 Register result, bool Inexact) { 806 RoundFloatingPointToInteger( 807 rd, fs, result, 808 [](MacroAssemblerRiscv64* masm, Register dst, FPURegister src) { 809 masm->fcvt_wu_s(dst, src, RTZ); 810 }, 811 Inexact); 812 } 813 814 void MacroAssemblerRiscv64::Trunc_w_s(Register rd, FPURegister fs, 815 Register result, bool Inexact) { 816 RoundFloatingPointToInteger( 817 rd, fs, result, 818 [](MacroAssemblerRiscv64* masm, Register dst, FPURegister src) { 819 masm->fcvt_w_s(dst, src, RTZ); 820 }, 821 Inexact); 822 } 823 void MacroAssemblerRiscv64::Trunc_ul_d(Register rd, FPURegister fs, 824 Register result, bool Inexact) { 825 RoundFloatingPointToInteger( 826 rd, fs, result, 827 [](MacroAssemblerRiscv64* masm, Register dst, FPURegister src) { 828 masm->fcvt_lu_d(dst, src, RTZ); 829 }, 830 Inexact); 831 } 832 833 void MacroAssemblerRiscv64::Trunc_l_d(Register rd, FPURegister fs, 834 Register result, bool Inexact) { 835 RoundFloatingPointToInteger( 836 rd, fs, result, 837 [](MacroAssemblerRiscv64* masm, Register dst, FPURegister src) { 838 masm->fcvt_l_d(dst, src, RTZ); 839 }, 840 Inexact); 841 } 842 843 void MacroAssemblerRiscv64::Trunc_ul_s(Register rd, FPURegister fs, 844 Register result, bool Inexact) { 845 RoundFloatingPointToInteger( 846 rd, fs, result, 847 [](MacroAssemblerRiscv64* masm, Register dst, FPURegister src) { 848 masm->fcvt_lu_s(dst, src, RTZ); 849 }, 850 Inexact); 851 } 852 853 void MacroAssemblerRiscv64::Trunc_l_s(Register rd, FPURegister fs, 854 Register result, bool Inexact) { 855 RoundFloatingPointToInteger( 856 rd, fs, result, 857 [](MacroAssemblerRiscv64* masm, Register dst, FPURegister src) { 858 masm->fcvt_l_s(dst, src, RTZ); 859 }, 860 Inexact); 861 } 862 863 void MacroAssemblerRiscv64::Floor_d_d(FPURegister dst, FPURegister src, 864 FPURegister fpu_scratch) { 865 RoundHelper<double>(dst, src, fpu_scratch, RDN); 866 } 867 868 void MacroAssemblerRiscv64::Ceil_d_d(FPURegister dst, FPURegister src, 869 FPURegister fpu_scratch) { 870 RoundHelper<double>(dst, src, fpu_scratch, RUP); 871 } 872 873 void MacroAssemblerRiscv64::Trunc_d_d(FPURegister dst, FPURegister src, 874 FPURegister fpu_scratch) { 875 RoundHelper<double>(dst, src, fpu_scratch, RTZ); 876 } 877 878 void MacroAssemblerRiscv64::Round_d_d(FPURegister dst, FPURegister src, 879 FPURegister fpu_scratch) { 880 RoundHelper<double>(dst, src, fpu_scratch, RNE); 881 } 882 883 void MacroAssemblerRiscv64::Floor_s_s(FPURegister dst, FPURegister src, 884 FPURegister fpu_scratch) { 885 RoundHelper<float>(dst, src, fpu_scratch, RDN); 886 } 887 888 void MacroAssemblerRiscv64::Ceil_s_s(FPURegister dst, FPURegister src, 889 FPURegister fpu_scratch) { 890 RoundHelper<float>(dst, src, fpu_scratch, RUP); 891 } 892 893 void MacroAssemblerRiscv64::Trunc_s_s(FPURegister dst, FPURegister src, 894 FPURegister fpu_scratch) { 895 RoundHelper<float>(dst, src, fpu_scratch, RTZ); 896 } 897 898 void MacroAssemblerRiscv64::Round_s_s(FPURegister dst, FPURegister src, 899 FPURegister fpu_scratch) { 900 RoundHelper<float>(dst, src, fpu_scratch, RNE); 901 } 902 903 void MacroAssemblerRiscv64::Round_w_s(Register rd, FPURegister fs, 904 Register result, bool Inexact) { 905 RoundFloatingPointToInteger( 906 rd, fs, result, 907 [](MacroAssemblerRiscv64* masm, Register dst, FPURegister src) { 908 masm->fcvt_w_s(dst, src, RNE); 909 }, 910 Inexact); 911 } 912 913 void MacroAssemblerRiscv64::Round_w_d(Register rd, FPURegister fs, 914 Register result, bool Inexact) { 915 RoundFloatingPointToInteger( 916 rd, fs, result, 917 [](MacroAssemblerRiscv64* masm, Register dst, FPURegister src) { 918 masm->fcvt_w_d(dst, src, RNE); 919 }, 920 Inexact); 921 } 922 923 void MacroAssemblerRiscv64::Ceil_w_s(Register rd, FPURegister fs, 924 Register result, bool Inexact) { 925 RoundFloatingPointToInteger( 926 rd, fs, result, 927 [](MacroAssemblerRiscv64* masm, Register dst, FPURegister src) { 928 masm->fcvt_w_s(dst, src, RUP); 929 }, 930 Inexact); 931 } 932 933 void MacroAssemblerRiscv64::Ceil_l_d(Register rd, FPURegister fs, 934 Register result, bool Inexact) { 935 RoundFloatingPointToInteger( 936 rd, fs, result, 937 [](MacroAssemblerRiscv64* masm, Register dst, FPURegister src) { 938 masm->fcvt_l_d(dst, src, RUP); 939 }, 940 Inexact); 941 } 942 943 void MacroAssemblerRiscv64::Ceil_l_s(Register rd, FPURegister fs, 944 Register result, bool Inexact) { 945 RoundFloatingPointToInteger( 946 rd, fs, result, 947 [](MacroAssemblerRiscv64* masm, Register dst, FPURegister src) { 948 masm->fcvt_l_s(dst, src, RUP); 949 }, 950 Inexact); 951 } 952 953 void MacroAssemblerRiscv64::Ceil_w_d(Register rd, FPURegister fs, 954 Register result, bool Inexact) { 955 RoundFloatingPointToInteger( 956 rd, fs, result, 957 [](MacroAssemblerRiscv64* masm, Register dst, FPURegister src) { 958 masm->fcvt_w_d(dst, src, RUP); 959 }, 960 Inexact); 961 } 962 963 void MacroAssemblerRiscv64::Floor_w_s(Register rd, FPURegister fs, 964 Register result, bool Inexact) { 965 RoundFloatingPointToInteger( 966 rd, fs, result, 967 [](MacroAssemblerRiscv64* masm, Register dst, FPURegister src) { 968 masm->fcvt_w_s(dst, src, RDN); 969 }, 970 Inexact); 971 } 972 973 void MacroAssemblerRiscv64::Floor_w_d(Register rd, FPURegister fs, 974 Register result, bool Inexact) { 975 RoundFloatingPointToInteger( 976 rd, fs, result, 977 [](MacroAssemblerRiscv64* masm, Register dst, FPURegister src) { 978 masm->fcvt_w_d(dst, src, RDN); 979 }, 980 Inexact); 981 } 982 983 void MacroAssemblerRiscv64::Floor_l_s(Register rd, FPURegister fs, 984 Register result, bool Inexact) { 985 RoundFloatingPointToInteger( 986 rd, fs, result, 987 [](MacroAssemblerRiscv64* masm, Register dst, FPURegister src) { 988 masm->fcvt_l_s(dst, src, RDN); 989 }, 990 Inexact); 991 } 992 993 void MacroAssemblerRiscv64::Floor_l_d(Register rd, FPURegister fs, 994 Register result, bool Inexact) { 995 RoundFloatingPointToInteger( 996 rd, fs, result, 997 [](MacroAssemblerRiscv64* masm, Register dst, FPURegister src) { 998 masm->fcvt_l_d(dst, src, RDN); 999 }, 1000 Inexact); 1001 } 1002 1003 void MacroAssemblerRiscv64::RoundMaxMag_l_s(Register rd, FPURegister fs, 1004 Register result, bool Inexact) { 1005 RoundFloatingPointToInteger( 1006 rd, fs, result, 1007 [](MacroAssemblerRiscv64* masm, Register dst, FPURegister src) { 1008 masm->fcvt_l_s(dst, src, RMM); 1009 }, 1010 Inexact); 1011 } 1012 1013 void MacroAssemblerRiscv64::RoundMaxMag_l_d(Register rd, FPURegister fs, 1014 Register result, bool Inexact) { 1015 RoundFloatingPointToInteger( 1016 rd, fs, result, 1017 [](MacroAssemblerRiscv64* masm, Register dst, FPURegister src) { 1018 masm->fcvt_l_d(dst, src, RMM); 1019 }, 1020 Inexact); 1021 } 1022 1023 // Checks whether a double is representable as a 32-bit integer. If so, the 1024 // integer is written to the output register. Otherwise, a bailout is taken to 1025 // the given snapshot. This function overwrites the scratch float register. 1026 void MacroAssemblerRiscv64Compat::convertDoubleToInt32(FloatRegister src, 1027 Register dest, 1028 Label* fail, 1029 bool negativeZeroCheck) { 1030 if (negativeZeroCheck) { 1031 fclass_d(dest, src); 1032 ma_b(dest, Imm32(kNegativeZero), fail, Equal); 1033 } 1034 UseScratchRegisterScope temps(this); 1035 Register scratch = temps.Acquire(); 1036 Trunc_w_d(dest, src, scratch, true); 1037 ma_b(scratch, Imm32(0), fail, Equal); 1038 } 1039 1040 void MacroAssemblerRiscv64Compat::convertDoubleToPtr(FloatRegister src, 1041 Register dest, Label* fail, 1042 bool negativeZeroCheck) { 1043 if (negativeZeroCheck) { 1044 fclass_d(dest, src); 1045 ma_b(dest, Imm32(kNegativeZero), fail, Equal); 1046 } 1047 UseScratchRegisterScope temps(this); 1048 Register scratch = temps.Acquire(); 1049 Trunc_l_d(dest, src, scratch, true); 1050 ma_b(scratch, Imm32(0), fail, Equal); 1051 } 1052 1053 // Checks whether a float32 is representable as a 32-bit integer. If so, the 1054 // integer is written to the output register. Otherwise, a bailout is taken to 1055 // the given snapshot. This function overwrites the scratch float register. 1056 void MacroAssemblerRiscv64Compat::convertFloat32ToInt32( 1057 FloatRegister src, Register dest, Label* fail, bool negativeZeroCheck) { 1058 if (negativeZeroCheck) { 1059 fclass_d(dest, src); 1060 ma_b(dest, Imm32(kNegativeZero), fail, Equal); 1061 } 1062 UseScratchRegisterScope temps(this); 1063 Register scratch = temps.Acquire(); 1064 Trunc_w_s(dest, src, scratch, true); 1065 ma_b(scratch, Imm32(0), fail, Equal); 1066 } 1067 1068 void MacroAssemblerRiscv64Compat::convertFloat32ToDouble(FloatRegister src, 1069 FloatRegister dest) { 1070 fcvt_d_s(dest, src); 1071 } 1072 1073 void MacroAssemblerRiscv64Compat::convertInt32ToFloat32(Register src, 1074 FloatRegister dest) { 1075 fcvt_s_w(dest, src); 1076 } 1077 1078 void MacroAssemblerRiscv64Compat::convertInt32ToFloat32(const Address& src, 1079 FloatRegister dest) { 1080 UseScratchRegisterScope temps(this); 1081 Register scratch = temps.Acquire(); 1082 load32(src, scratch); 1083 fcvt_s_w(dest, scratch); 1084 } 1085 1086 void MacroAssemblerRiscv64Compat::truncateFloat32ModUint32(FloatRegister src, 1087 Register dest) { 1088 UseScratchRegisterScope temps(this); 1089 Register scratch = temps.Acquire(); 1090 1091 // Convert scalar to signed 64-bit fixed-point, rounding toward zero. 1092 // In the case of overflow or NaN, the output is saturated. 1093 // In the case of -0, the output is zero. 1094 Trunc_l_s(dest, src); 1095 1096 // Unsigned subtraction of INT64_MAX returns 1 resp. 0 for INT64_{MIN,MAX}. 1097 ma_li(scratch, Imm64(0x7fff'ffff'ffff'ffff)); 1098 ma_sub64(scratch, dest, scratch); 1099 1100 // If scratch u< 2, then scratch = 0; else scratch = -1. 1101 ma_sltu(scratch, scratch, Imm32(2)); 1102 ma_add32(scratch, scratch, Imm32(-1)); 1103 1104 // Clear |dest| if the truncation result was saturated. 1105 ma_and(dest, dest, scratch); 1106 1107 // Clear upper 32 bits. 1108 SignExtendWord(dest, dest); 1109 } 1110 1111 // Memory. 1112 FaultingCodeOffset MacroAssemblerRiscv64::ma_loadDouble(FloatRegister dest, 1113 Address address) { 1114 UseScratchRegisterScope temps(this); 1115 int16_t encodedOffset; 1116 Register base; 1117 1118 if (!is_int12(address.offset)) { 1119 Register scratch = temps.Acquire(); 1120 ma_li(scratch, Imm32(address.offset)); 1121 add(scratch, address.base, scratch); 1122 base = scratch; 1123 encodedOffset = 0; 1124 } else { 1125 encodedOffset = address.offset; 1126 base = address.base; 1127 } 1128 FaultingCodeOffset fco = FaultingCodeOffset(currentOffset()); 1129 fld(dest, base, encodedOffset); 1130 return fco; 1131 } 1132 1133 FaultingCodeOffset MacroAssemblerRiscv64::ma_loadFloat(FloatRegister dest, 1134 Address address) { 1135 UseScratchRegisterScope temps(this); 1136 int16_t encodedOffset; 1137 Register base; 1138 1139 if (!is_int12(address.offset)) { 1140 Register scratch = temps.Acquire(); 1141 ma_li(scratch, Imm32(address.offset)); 1142 add(scratch, address.base, scratch); 1143 base = scratch; 1144 encodedOffset = 0; 1145 } else { 1146 encodedOffset = address.offset; 1147 base = address.base; 1148 } 1149 FaultingCodeOffset fco = FaultingCodeOffset(currentOffset()); 1150 flw(dest, base, encodedOffset); 1151 return fco; 1152 } 1153 1154 FaultingCodeOffset MacroAssemblerRiscv64::ma_load( 1155 Register dest, Address address, LoadStoreSize size, 1156 LoadStoreExtension extension) { 1157 UseScratchRegisterScope temps(this); 1158 int16_t encodedOffset; 1159 Register base; 1160 1161 if (!is_int12(address.offset)) { 1162 Register scratch = temps.Acquire(); 1163 ma_li(scratch, Imm32(address.offset)); 1164 add(scratch, address.base, scratch); 1165 base = scratch; 1166 encodedOffset = 0; 1167 } else { 1168 encodedOffset = address.offset; 1169 base = address.base; 1170 } 1171 FaultingCodeOffset fco = FaultingCodeOffset(currentOffset()); 1172 switch (size) { 1173 case SizeByte: 1174 if (ZeroExtend == extension) { 1175 lbu(dest, base, encodedOffset); 1176 } else { 1177 lb(dest, base, encodedOffset); 1178 } 1179 break; 1180 case SizeHalfWord: 1181 if (ZeroExtend == extension) { 1182 lhu(dest, base, encodedOffset); 1183 } else { 1184 lh(dest, base, encodedOffset); 1185 } 1186 break; 1187 case SizeWord: 1188 if (ZeroExtend == extension) { 1189 lwu(dest, base, encodedOffset); 1190 } else { 1191 lw(dest, base, encodedOffset); 1192 } 1193 break; 1194 case SizeDouble: 1195 ld(dest, base, encodedOffset); 1196 break; 1197 default: 1198 MOZ_CRASH("Invalid argument for ma_load"); 1199 } 1200 return fco; 1201 } 1202 1203 FaultingCodeOffset MacroAssemblerRiscv64::ma_store( 1204 Register data, const BaseIndex& dest, LoadStoreSize size, 1205 LoadStoreExtension extension) { 1206 UseScratchRegisterScope temps(this); 1207 Register scratch2 = temps.Acquire(); 1208 computeScaledAddress(dest, scratch2); 1209 return ma_store(data, Address(scratch2, dest.offset), size, extension); 1210 } 1211 1212 FaultingCodeOffset MacroAssemblerRiscv64::ma_store( 1213 Imm32 imm, const BaseIndex& dest, LoadStoreSize size, 1214 LoadStoreExtension extension) { 1215 UseScratchRegisterScope temps(this); 1216 1217 Register address = temps.Acquire(); 1218 computeScaledAddress(dest, address); 1219 1220 Register scratch = temps.Acquire(); 1221 ma_li(scratch, imm); 1222 1223 return ma_store(scratch, Address(address, dest.offset), size, extension); 1224 } 1225 1226 FaultingCodeOffset MacroAssemblerRiscv64::ma_store( 1227 Imm32 imm, Address address, LoadStoreSize size, 1228 LoadStoreExtension extension) { 1229 UseScratchRegisterScope temps(this); 1230 Register scratch = temps.Acquire(); 1231 ma_li(scratch, imm); 1232 return ma_store(scratch, address, size, extension); 1233 } 1234 1235 FaultingCodeOffset MacroAssemblerRiscv64::ma_store( 1236 Register data, Address address, LoadStoreSize size, 1237 LoadStoreExtension extension) { 1238 UseScratchRegisterScope temps(this); 1239 1240 int16_t encodedOffset; 1241 Register base; 1242 1243 if (!is_int12(address.offset)) { 1244 Register scratch = temps.Acquire(); 1245 ma_li(scratch, Imm32(address.offset)); 1246 add(scratch, address.base, scratch); 1247 base = scratch; 1248 encodedOffset = 0; 1249 } else { 1250 encodedOffset = address.offset; 1251 base = address.base; 1252 } 1253 FaultingCodeOffset fco = FaultingCodeOffset(currentOffset()); 1254 switch (size) { 1255 case SizeByte: 1256 sb(data, base, encodedOffset); 1257 break; 1258 case SizeHalfWord: 1259 sh(data, base, encodedOffset); 1260 break; 1261 case SizeWord: 1262 sw(data, base, encodedOffset); 1263 break; 1264 case SizeDouble: 1265 sd(data, base, encodedOffset); 1266 break; 1267 default: 1268 MOZ_CRASH("Invalid argument for ma_store"); 1269 } 1270 return fco; 1271 } 1272 1273 // Memory. 1274 void MacroAssemblerRiscv64::ma_storeDouble(FloatRegister dest, 1275 Address address) { 1276 UseScratchRegisterScope temps(this); 1277 int16_t encodedOffset; 1278 Register base; 1279 1280 if (!is_int12(address.offset)) { 1281 Register scratch = temps.Acquire(); 1282 ma_li(scratch, Imm32(address.offset)); 1283 add(scratch, address.base, scratch); 1284 base = scratch; 1285 encodedOffset = 0; 1286 } else { 1287 encodedOffset = address.offset; 1288 base = address.base; 1289 } 1290 fsd(dest, base, encodedOffset); 1291 } 1292 1293 void MacroAssemblerRiscv64::ma_storeFloat(FloatRegister dest, Address address) { 1294 UseScratchRegisterScope temps(this); 1295 int16_t encodedOffset; 1296 Register base; 1297 1298 if (!is_int12(address.offset)) { 1299 Register scratch = temps.Acquire(); 1300 ma_li(scratch, Imm32(address.offset)); 1301 add(scratch, address.base, scratch); 1302 base = scratch; 1303 encodedOffset = 0; 1304 } else { 1305 encodedOffset = address.offset; 1306 base = address.base; 1307 } 1308 fsw(dest, base, encodedOffset); 1309 } 1310 1311 void MacroAssemblerRiscv64::computeScaledAddress(const BaseIndex& address, 1312 Register dest) { 1313 Register base = address.base; 1314 Register index = address.index; 1315 int32_t shift = Imm32::ShiftOf(address.scale).value; 1316 UseScratchRegisterScope temps(this); 1317 if (shift && base == zero) { 1318 MOZ_ASSERT(shift <= 4); 1319 slli(dest, index, shift); 1320 } else if (shift) { 1321 Register tmp = dest == base ? temps.Acquire() : dest; 1322 MOZ_ASSERT(shift <= 4); 1323 slli(tmp, index, shift); 1324 add(dest, base, tmp); 1325 } else { 1326 add(dest, base, index); 1327 } 1328 } 1329 1330 void MacroAssemblerRiscv64::computeScaledAddress32(const BaseIndex& address, 1331 Register dest) { 1332 Register base = address.base; 1333 Register index = address.index; 1334 int32_t shift = Imm32::ShiftOf(address.scale).value; 1335 UseScratchRegisterScope temps(this); 1336 if (shift && base == zero) { 1337 MOZ_ASSERT(shift <= 4); 1338 slliw(dest, index, shift); 1339 } else if (shift) { 1340 Register tmp = dest == base ? temps.Acquire() : dest; 1341 MOZ_ASSERT(shift <= 4); 1342 slliw(tmp, index, shift); 1343 addw(dest, base, tmp); 1344 } else { 1345 addw(dest, base, index); 1346 } 1347 } 1348 1349 void MacroAssemblerRiscv64Compat::wasmLoadI64Impl( 1350 const wasm::MemoryAccessDesc& access, Register memoryBase, Register ptr, 1351 Register ptrScratch, Register64 output, Register tmp) { 1352 access.assertOffsetInGuardPages(); 1353 uint32_t offset = access.offset32(); 1354 MOZ_ASSERT_IF(offset, ptrScratch != InvalidReg); 1355 1356 // Maybe add the offset. 1357 if (offset) { 1358 asMasm().addPtr(ImmWord(offset), ptrScratch); 1359 ptr = ptrScratch; 1360 } 1361 1362 asMasm().memoryBarrierBefore(access.sync()); 1363 UseScratchRegisterScope temps(this); 1364 Register scratch = temps.Acquire(); 1365 FaultingCodeOffset fco; 1366 switch (access.type()) { 1367 case Scalar::Int8: 1368 add(scratch, memoryBase, ptr); 1369 fco = FaultingCodeOffset(currentOffset()); 1370 lb(output.reg, scratch, 0); 1371 break; 1372 case Scalar::Uint8: 1373 add(scratch, memoryBase, ptr); 1374 fco = FaultingCodeOffset(currentOffset()); 1375 lbu(output.reg, scratch, 0); 1376 break; 1377 case Scalar::Int16: 1378 add(scratch, memoryBase, ptr); 1379 fco = FaultingCodeOffset(currentOffset()); 1380 lh(output.reg, scratch, 0); 1381 break; 1382 case Scalar::Uint16: 1383 add(scratch, memoryBase, ptr); 1384 fco = FaultingCodeOffset(currentOffset()); 1385 lhu(output.reg, scratch, 0); 1386 break; 1387 case Scalar::Int32: 1388 add(scratch, memoryBase, ptr); 1389 fco = FaultingCodeOffset(currentOffset()); 1390 lw(output.reg, scratch, 0); 1391 break; 1392 case Scalar::Uint32: 1393 // TODO(riscv): Why need zero-extension here? 1394 add(scratch, memoryBase, ptr); 1395 fco = FaultingCodeOffset(currentOffset()); 1396 lwu(output.reg, scratch, 0); 1397 break; 1398 case Scalar::Int64: 1399 add(scratch, memoryBase, ptr); 1400 fco = FaultingCodeOffset(currentOffset()); 1401 ld(output.reg, scratch, 0); 1402 break; 1403 default: 1404 MOZ_CRASH("unexpected array type"); 1405 } 1406 1407 append(access, js::wasm::TrapMachineInsnForLoad(access.byteSize()), fco); 1408 asMasm().memoryBarrierAfter(access.sync()); 1409 } 1410 1411 void MacroAssemblerRiscv64Compat::wasmStoreI64Impl( 1412 const wasm::MemoryAccessDesc& access, Register64 value, Register memoryBase, 1413 Register ptr, Register ptrScratch, Register tmp) { 1414 access.assertOffsetInGuardPages(); 1415 uint32_t offset = access.offset32(); 1416 MOZ_ASSERT_IF(offset, ptrScratch != InvalidReg); 1417 1418 // Maybe add the offset. 1419 if (offset) { 1420 asMasm().addPtr(ImmWord(offset), ptrScratch); 1421 ptr = ptrScratch; 1422 } 1423 1424 asMasm().memoryBarrierBefore(access.sync()); 1425 UseScratchRegisterScope temps(this); 1426 Register scratch = temps.Acquire(); 1427 FaultingCodeOffset fco; 1428 switch (access.type()) { 1429 case Scalar::Int8: 1430 case Scalar::Uint8: 1431 add(scratch, memoryBase, ptr); 1432 fco = FaultingCodeOffset(currentOffset()); 1433 sb(value.reg, scratch, 0); 1434 break; 1435 case Scalar::Int16: 1436 case Scalar::Uint16: 1437 add(scratch, memoryBase, ptr); 1438 fco = FaultingCodeOffset(currentOffset()); 1439 sh(value.reg, scratch, 0); 1440 break; 1441 case Scalar::Int32: 1442 case Scalar::Uint32: 1443 add(scratch, memoryBase, ptr); 1444 fco = FaultingCodeOffset(currentOffset()); 1445 sw(value.reg, scratch, 0); 1446 break; 1447 case Scalar::Int64: 1448 add(scratch, memoryBase, ptr); 1449 fco = FaultingCodeOffset(currentOffset()); 1450 sd(value.reg, scratch, 0); 1451 break; 1452 default: 1453 MOZ_CRASH("unexpected array type"); 1454 } 1455 1456 append(access, js::wasm::TrapMachineInsnForLoad(access.byteSize()), fco); 1457 asMasm().memoryBarrierAfter(access.sync()); 1458 } 1459 1460 void MacroAssemblerRiscv64Compat::profilerEnterFrame(Register framePtr, 1461 Register scratch) { 1462 asMasm().loadJSContext(scratch); 1463 loadPtr(Address(scratch, offsetof(JSContext, profilingActivation_)), scratch); 1464 storePtr(framePtr, 1465 Address(scratch, JitActivation::offsetOfLastProfilingFrame())); 1466 storePtr(ImmPtr(nullptr), 1467 Address(scratch, JitActivation::offsetOfLastProfilingCallSite())); 1468 } 1469 1470 void MacroAssemblerRiscv64Compat::profilerExitFrame() { 1471 jump(asMasm().runtime()->jitRuntime()->getProfilerExitFrameTail()); 1472 } 1473 1474 void MacroAssemblerRiscv64Compat::move32(Imm32 imm, Register dest) { 1475 ma_li(dest, imm); 1476 } 1477 1478 void MacroAssemblerRiscv64Compat::move32(Register src, Register dest) { 1479 SignExtendWord(dest, src); 1480 } 1481 1482 FaultingCodeOffset MacroAssemblerRiscv64Compat::load8ZeroExtend( 1483 const Address& address, Register dest) { 1484 return ma_load(dest, address, SizeByte, ZeroExtend); 1485 } 1486 1487 FaultingCodeOffset MacroAssemblerRiscv64Compat::load8ZeroExtend( 1488 const BaseIndex& src, Register dest) { 1489 return ma_load(dest, src, SizeByte, ZeroExtend); 1490 } 1491 1492 FaultingCodeOffset MacroAssemblerRiscv64Compat::load8SignExtend( 1493 const Address& address, Register dest) { 1494 return ma_load(dest, address, SizeByte, SignExtend); 1495 } 1496 1497 FaultingCodeOffset MacroAssemblerRiscv64Compat::load8SignExtend( 1498 const BaseIndex& src, Register dest) { 1499 return ma_load(dest, src, SizeByte, SignExtend); 1500 } 1501 1502 FaultingCodeOffset MacroAssemblerRiscv64Compat::load16ZeroExtend( 1503 const Address& address, Register dest) { 1504 return ma_load(dest, address, SizeHalfWord, ZeroExtend); 1505 } 1506 1507 FaultingCodeOffset MacroAssemblerRiscv64Compat::load16ZeroExtend( 1508 const BaseIndex& src, Register dest) { 1509 return ma_load(dest, src, SizeHalfWord, ZeroExtend); 1510 } 1511 1512 FaultingCodeOffset MacroAssemblerRiscv64Compat::load16SignExtend( 1513 const Address& address, Register dest) { 1514 return ma_load(dest, address, SizeHalfWord, SignExtend); 1515 } 1516 1517 FaultingCodeOffset MacroAssemblerRiscv64Compat::load16SignExtend( 1518 const BaseIndex& src, Register dest) { 1519 return ma_load(dest, src, SizeHalfWord, SignExtend); 1520 } 1521 1522 FaultingCodeOffset MacroAssemblerRiscv64Compat::load32(const Address& address, 1523 Register dest) { 1524 return ma_load(dest, address, SizeWord); 1525 } 1526 1527 FaultingCodeOffset MacroAssemblerRiscv64Compat::load32(const BaseIndex& address, 1528 Register dest) { 1529 return ma_load(dest, address, SizeWord); 1530 } 1531 1532 FaultingCodeOffset MacroAssemblerRiscv64Compat::load32(AbsoluteAddress address, 1533 Register dest) { 1534 UseScratchRegisterScope temps(this); 1535 Register scratch = temps.Acquire(); 1536 movePtr(ImmPtr(address.addr), scratch); 1537 return load32(Address(scratch, 0), dest); 1538 } 1539 1540 FaultingCodeOffset MacroAssemblerRiscv64Compat::load32( 1541 wasm::SymbolicAddress address, Register dest) { 1542 UseScratchRegisterScope temps(this); 1543 Register scratch = temps.Acquire(); 1544 movePtr(address, scratch); 1545 return load32(Address(scratch, 0), dest); 1546 } 1547 1548 FaultingCodeOffset MacroAssemblerRiscv64Compat::loadPtr(const Address& address, 1549 Register dest) { 1550 return ma_load(dest, address, SizeDouble); 1551 } 1552 1553 FaultingCodeOffset MacroAssemblerRiscv64Compat::loadPtr(const BaseIndex& src, 1554 Register dest) { 1555 return ma_load(dest, src, SizeDouble); 1556 } 1557 1558 FaultingCodeOffset MacroAssemblerRiscv64Compat::loadPtr(AbsoluteAddress address, 1559 Register dest) { 1560 UseScratchRegisterScope temps(this); 1561 Register scratch = temps.Acquire(); 1562 movePtr(ImmPtr(address.addr), scratch); 1563 return loadPtr(Address(scratch, 0), dest); 1564 } 1565 1566 FaultingCodeOffset MacroAssemblerRiscv64Compat::loadPtr( 1567 wasm::SymbolicAddress address, Register dest) { 1568 UseScratchRegisterScope temps(this); 1569 Register scratch = temps.Acquire(); 1570 movePtr(address, scratch); 1571 return loadPtr(Address(scratch, 0), dest); 1572 } 1573 1574 FaultingCodeOffset MacroAssemblerRiscv64Compat::loadPrivate( 1575 const Address& address, Register dest) { 1576 return loadPtr(address, dest); 1577 } 1578 1579 FaultingCodeOffset MacroAssemblerRiscv64Compat::store8(Imm32 imm, 1580 const Address& address) { 1581 UseScratchRegisterScope temps(this); 1582 Register scratch = temps.Acquire(); 1583 ma_li(scratch, imm); 1584 return ma_store(scratch, address, SizeByte); 1585 } 1586 1587 FaultingCodeOffset MacroAssemblerRiscv64Compat::store8(Register src, 1588 const Address& address) { 1589 return ma_store(src, address, SizeByte); 1590 } 1591 1592 FaultingCodeOffset MacroAssemblerRiscv64Compat::store8(Imm32 imm, 1593 const BaseIndex& dest) { 1594 return ma_store(imm, dest, SizeByte); 1595 } 1596 1597 FaultingCodeOffset MacroAssemblerRiscv64Compat::store8(Register src, 1598 const BaseIndex& dest) { 1599 return ma_store(src, dest, SizeByte); 1600 } 1601 1602 FaultingCodeOffset MacroAssemblerRiscv64Compat::store16( 1603 Imm32 imm, const Address& address) { 1604 UseScratchRegisterScope temps(this); 1605 Register scratch = temps.Acquire(); 1606 ma_li(scratch, imm); 1607 return ma_store(scratch, address, SizeHalfWord); 1608 } 1609 1610 FaultingCodeOffset MacroAssemblerRiscv64Compat::store16( 1611 Register src, const Address& address) { 1612 return ma_store(src, address, SizeHalfWord); 1613 } 1614 1615 FaultingCodeOffset MacroAssemblerRiscv64Compat::store16(Imm32 imm, 1616 const BaseIndex& dest) { 1617 return ma_store(imm, dest, SizeHalfWord); 1618 } 1619 1620 FaultingCodeOffset MacroAssemblerRiscv64Compat::store16( 1621 Register src, const BaseIndex& address) { 1622 return ma_store(src, address, SizeHalfWord); 1623 } 1624 1625 FaultingCodeOffset MacroAssemblerRiscv64Compat::store32( 1626 Register src, AbsoluteAddress address) { 1627 UseScratchRegisterScope temps(this); 1628 Register scratch = temps.Acquire(); 1629 movePtr(ImmPtr(address.addr), scratch); 1630 return store32(src, Address(scratch, 0)); 1631 } 1632 1633 FaultingCodeOffset MacroAssemblerRiscv64Compat::store32( 1634 Register src, const Address& address) { 1635 return ma_store(src, address, SizeWord); 1636 } 1637 1638 FaultingCodeOffset MacroAssemblerRiscv64Compat::store32( 1639 Imm32 src, const Address& address) { 1640 UseScratchRegisterScope temps(this); 1641 Register scratch = temps.Acquire(); 1642 move32(src, scratch); 1643 return ma_store(scratch, address, SizeWord); 1644 } 1645 1646 FaultingCodeOffset MacroAssemblerRiscv64Compat::store32(Imm32 imm, 1647 const BaseIndex& dest) { 1648 return ma_store(imm, dest, SizeWord); 1649 } 1650 1651 FaultingCodeOffset MacroAssemblerRiscv64Compat::store32(Register src, 1652 const BaseIndex& dest) { 1653 return ma_store(src, dest, SizeWord); 1654 } 1655 1656 template <typename T> 1657 FaultingCodeOffset MacroAssemblerRiscv64Compat::storePtr(ImmWord imm, 1658 T address) { 1659 UseScratchRegisterScope temps(this); 1660 Register scratch = temps.Acquire(); 1661 ma_li(scratch, imm); 1662 return ma_store(scratch, address, SizeDouble); 1663 } 1664 1665 template FaultingCodeOffset MacroAssemblerRiscv64Compat::storePtr<Address>( 1666 ImmWord imm, Address address); 1667 template FaultingCodeOffset MacroAssemblerRiscv64Compat::storePtr<BaseIndex>( 1668 ImmWord imm, BaseIndex address); 1669 1670 template <typename T> 1671 FaultingCodeOffset MacroAssemblerRiscv64Compat::storePtr(ImmPtr imm, 1672 T address) { 1673 return storePtr(ImmWord(uintptr_t(imm.value)), address); 1674 } 1675 1676 template FaultingCodeOffset MacroAssemblerRiscv64Compat::storePtr<Address>( 1677 ImmPtr imm, Address address); 1678 template FaultingCodeOffset MacroAssemblerRiscv64Compat::storePtr<BaseIndex>( 1679 ImmPtr imm, BaseIndex address); 1680 1681 template <typename T> 1682 FaultingCodeOffset MacroAssemblerRiscv64Compat::storePtr(ImmGCPtr imm, 1683 T address) { 1684 UseScratchRegisterScope temps(this); 1685 Register scratch = temps.Acquire(); 1686 movePtr(imm, scratch); 1687 return storePtr(scratch, address); 1688 } 1689 1690 template FaultingCodeOffset MacroAssemblerRiscv64Compat::storePtr<Address>( 1691 ImmGCPtr imm, Address address); 1692 template FaultingCodeOffset MacroAssemblerRiscv64Compat::storePtr<BaseIndex>( 1693 ImmGCPtr imm, BaseIndex address); 1694 1695 FaultingCodeOffset MacroAssemblerRiscv64Compat::storePtr( 1696 Register src, const Address& address) { 1697 return ma_store(src, address, SizeDouble); 1698 } 1699 1700 FaultingCodeOffset MacroAssemblerRiscv64Compat::storePtr( 1701 Register src, const BaseIndex& address) { 1702 return ma_store(src, address, SizeDouble); 1703 } 1704 1705 FaultingCodeOffset MacroAssemblerRiscv64Compat::storePtr(Register src, 1706 AbsoluteAddress dest) { 1707 UseScratchRegisterScope temps(this); 1708 Register scratch = temps.Acquire(); 1709 movePtr(ImmPtr(dest.addr), scratch); 1710 return storePtr(src, Address(scratch, 0)); 1711 } 1712 1713 void MacroAssemblerRiscv64Compat::testNullSet(Condition cond, 1714 const ValueOperand& value, 1715 Register dest) { 1716 MOZ_ASSERT(cond == Equal || cond == NotEqual); 1717 UseScratchRegisterScope temps(this); 1718 Register scratch = temps.Acquire(); 1719 splitTag(value, scratch); 1720 ma_cmp_set(dest, scratch, ImmTag(JSVAL_TAG_NULL), cond); 1721 } 1722 1723 void MacroAssemblerRiscv64Compat::testObjectSet(Condition cond, 1724 const ValueOperand& value, 1725 Register dest) { 1726 MOZ_ASSERT(cond == Equal || cond == NotEqual); 1727 UseScratchRegisterScope temps(this); 1728 Register scratch = temps.Acquire(); 1729 splitTag(value, scratch); 1730 ma_cmp_set(dest, scratch, ImmTag(JSVAL_TAG_OBJECT), cond); 1731 } 1732 1733 void MacroAssemblerRiscv64Compat::testUndefinedSet(Condition cond, 1734 const ValueOperand& value, 1735 Register dest) { 1736 MOZ_ASSERT(cond == Equal || cond == NotEqual); 1737 UseScratchRegisterScope temps(this); 1738 Register scratch = temps.Acquire(); 1739 splitTag(value, scratch); 1740 ma_cmp_set(dest, scratch, ImmTag(JSVAL_TAG_UNDEFINED), cond); 1741 } 1742 1743 void MacroAssemblerRiscv64Compat::unboxInt32(const ValueOperand& operand, 1744 Register dest) { 1745 SignExtendWord(dest, operand.valueReg()); 1746 } 1747 1748 void MacroAssemblerRiscv64Compat::unboxInt32(Register src, Register dest) { 1749 SignExtendWord(dest, src); 1750 } 1751 1752 void MacroAssemblerRiscv64Compat::unboxInt32(const Address& src, 1753 Register dest) { 1754 load32(Address(src.base, src.offset), dest); 1755 } 1756 1757 void MacroAssemblerRiscv64Compat::unboxInt32(const BaseIndex& src, 1758 Register dest) { 1759 UseScratchRegisterScope temps(this); 1760 Register scratch = temps.Acquire(); 1761 computeScaledAddress(src, scratch); 1762 load32(Address(scratch, src.offset), dest); 1763 } 1764 1765 void MacroAssemblerRiscv64Compat::unboxBoolean(const ValueOperand& operand, 1766 Register dest) { 1767 ExtractBits(dest, operand.valueReg(), 0, 32); 1768 } 1769 1770 void MacroAssemblerRiscv64Compat::unboxBoolean(Register src, Register dest) { 1771 ExtractBits(dest, src, 0, 32); 1772 } 1773 1774 void MacroAssemblerRiscv64Compat::unboxBoolean(const Address& src, 1775 Register dest) { 1776 ma_load(dest, Address(src.base, src.offset), SizeWord, ZeroExtend); 1777 } 1778 1779 void MacroAssemblerRiscv64Compat::unboxBoolean(const BaseIndex& src, 1780 Register dest) { 1781 UseScratchRegisterScope temps(this); 1782 Register scratch = temps.Acquire(); 1783 computeScaledAddress(src, scratch); 1784 ma_load(dest, Address(scratch, src.offset), SizeWord, ZeroExtend); 1785 } 1786 1787 void MacroAssemblerRiscv64Compat::unboxDouble(const ValueOperand& operand, 1788 FloatRegister dest) { 1789 fmv_d_x(dest, operand.valueReg()); 1790 } 1791 1792 void MacroAssemblerRiscv64Compat::unboxDouble(const Address& src, 1793 FloatRegister dest) { 1794 ma_loadDouble(dest, Address(src.base, src.offset)); 1795 } 1796 1797 void MacroAssemblerRiscv64Compat::unboxDouble(const BaseIndex& src, 1798 FloatRegister dest) { 1799 UseScratchRegisterScope temps(this); 1800 Register scratch = temps.Acquire(); 1801 loadPtr(src, scratch); 1802 unboxDouble(ValueOperand(scratch), dest); 1803 } 1804 1805 void MacroAssemblerRiscv64Compat::unboxString(const ValueOperand& operand, 1806 Register dest) { 1807 unboxNonDouble(operand, dest, JSVAL_TYPE_STRING); 1808 } 1809 1810 void MacroAssemblerRiscv64Compat::unboxString(Register src, Register dest) { 1811 unboxNonDouble(src, dest, JSVAL_TYPE_STRING); 1812 } 1813 1814 void MacroAssemblerRiscv64Compat::unboxString(const Address& src, 1815 Register dest) { 1816 unboxNonDouble(src, dest, JSVAL_TYPE_STRING); 1817 } 1818 1819 void MacroAssemblerRiscv64Compat::unboxSymbol(const ValueOperand& operand, 1820 Register dest) { 1821 unboxNonDouble(operand, dest, JSVAL_TYPE_SYMBOL); 1822 } 1823 1824 void MacroAssemblerRiscv64Compat::unboxSymbol(Register src, Register dest) { 1825 unboxNonDouble(src, dest, JSVAL_TYPE_SYMBOL); 1826 } 1827 1828 void MacroAssemblerRiscv64Compat::unboxSymbol(const Address& src, 1829 Register dest) { 1830 unboxNonDouble(src, dest, JSVAL_TYPE_SYMBOL); 1831 } 1832 1833 void MacroAssemblerRiscv64Compat::unboxBigInt(const ValueOperand& operand, 1834 Register dest) { 1835 unboxNonDouble(operand, dest, JSVAL_TYPE_BIGINT); 1836 } 1837 1838 void MacroAssemblerRiscv64Compat::unboxBigInt(Register src, Register dest) { 1839 unboxNonDouble(src, dest, JSVAL_TYPE_BIGINT); 1840 } 1841 1842 void MacroAssemblerRiscv64Compat::unboxBigInt(const Address& src, 1843 Register dest) { 1844 unboxNonDouble(src, dest, JSVAL_TYPE_BIGINT); 1845 } 1846 1847 void MacroAssemblerRiscv64Compat::unboxObject(const ValueOperand& src, 1848 Register dest) { 1849 unboxNonDouble(src, dest, JSVAL_TYPE_OBJECT); 1850 } 1851 1852 void MacroAssemblerRiscv64Compat::unboxObject(Register src, Register dest) { 1853 unboxNonDouble(src, dest, JSVAL_TYPE_OBJECT); 1854 } 1855 1856 void MacroAssemblerRiscv64Compat::unboxObject(const Address& src, 1857 Register dest) { 1858 unboxNonDouble(src, dest, JSVAL_TYPE_OBJECT); 1859 } 1860 1861 void MacroAssemblerRiscv64Compat::unboxValue(const ValueOperand& src, 1862 AnyRegister dest, 1863 JSValueType type) { 1864 if (dest.isFloat()) { 1865 Label notInt32, end; 1866 asMasm().branchTestInt32(Assembler::NotEqual, src, ¬Int32); 1867 convertInt32ToDouble(src.valueReg(), dest.fpu()); 1868 ma_branch(&end); 1869 bind(¬Int32); 1870 unboxDouble(src, dest.fpu()); 1871 bind(&end); 1872 } else { 1873 unboxNonDouble(src, dest.gpr(), type); 1874 } 1875 } 1876 1877 void MacroAssemblerRiscv64Compat::boxDouble(FloatRegister src, 1878 const ValueOperand& dest, 1879 FloatRegister) { 1880 fmv_x_d(dest.valueReg(), src); 1881 } 1882 1883 #ifdef DEBUG 1884 static constexpr int32_t PayloadSize(JSValueType type) { 1885 switch (type) { 1886 case JSVAL_TYPE_UNDEFINED: 1887 case JSVAL_TYPE_NULL: 1888 return 0; 1889 case JSVAL_TYPE_BOOLEAN: 1890 return 1; 1891 case JSVAL_TYPE_INT32: 1892 case JSVAL_TYPE_MAGIC: 1893 return 32; 1894 case JSVAL_TYPE_STRING: 1895 case JSVAL_TYPE_SYMBOL: 1896 case JSVAL_TYPE_PRIVATE_GCTHING: 1897 case JSVAL_TYPE_BIGINT: 1898 case JSVAL_TYPE_OBJECT: 1899 return JSVAL_TAG_SHIFT; 1900 case JSVAL_TYPE_DOUBLE: 1901 case JSVAL_TYPE_UNKNOWN: 1902 break; 1903 } 1904 MOZ_CRASH("bad value type"); 1905 } 1906 #endif 1907 1908 static void AssertValidPayload(MacroAssemblerRiscv64Compat& masm, 1909 JSValueType type, Register payload, 1910 Register scratch) { 1911 #ifdef DEBUG 1912 if (type == JSVAL_TYPE_INT32) { 1913 // Ensure the payload is a properly sign-extended int32. 1914 Label signExtended; 1915 masm.SignExtendWord(scratch, payload); 1916 masm.ma_b(payload, scratch, &signExtended, Assembler::Equal, ShortJump); 1917 masm.breakpoint(); 1918 masm.bind(&signExtended); 1919 } else { 1920 // All bits above the payload must be zeroed. 1921 Label zeroed; 1922 masm.srli(scratch, payload, PayloadSize(type)); 1923 masm.ma_b(scratch, Imm32(0), &zeroed, Assembler::Equal, ShortJump); 1924 masm.breakpoint(); 1925 masm.bind(&zeroed); 1926 } 1927 #endif 1928 } 1929 1930 void MacroAssemblerRiscv64Compat::boxValue(JSValueType type, Register src, 1931 Register dest) { 1932 MOZ_ASSERT(type != JSVAL_TYPE_UNDEFINED && type != JSVAL_TYPE_NULL); 1933 MOZ_ASSERT(src != dest); 1934 1935 AssertValidPayload(*this, type, src, dest); 1936 1937 switch (type) { 1938 case JSVAL_TYPE_INT32: { 1939 // Loading the shifted tag requires only two instructions. 1940 ma_li(dest, ImmShiftedTag(type)); 1941 1942 UseScratchRegisterScope temps(this); 1943 Register scratch = temps.Acquire(); 1944 1945 // Insert low 32 bits as payload, removing all high bits from |src|. 1946 ZeroExtendWord(scratch, src); 1947 or_(dest, dest, scratch); 1948 return; 1949 } 1950 case JSVAL_TYPE_BOOLEAN: 1951 case JSVAL_TYPE_MAGIC: 1952 case JSVAL_TYPE_STRING: 1953 case JSVAL_TYPE_SYMBOL: 1954 case JSVAL_TYPE_PRIVATE_GCTHING: 1955 case JSVAL_TYPE_BIGINT: 1956 case JSVAL_TYPE_OBJECT: { 1957 // Loading the shifted tag requires only two instructions. 1958 ma_li(dest, ImmShiftedTag(type)); 1959 1960 // Insert payload. 1961 or_(dest, dest, src); 1962 return; 1963 } 1964 case JSVAL_TYPE_DOUBLE: 1965 case JSVAL_TYPE_UNDEFINED: 1966 case JSVAL_TYPE_NULL: 1967 case JSVAL_TYPE_UNKNOWN: 1968 break; 1969 } 1970 MOZ_CRASH("bad value type"); 1971 } 1972 1973 void MacroAssemblerRiscv64Compat::boxValue(Register type, Register src, 1974 Register dest) { 1975 MOZ_ASSERT(src != dest); 1976 1977 #ifdef DEBUG 1978 Label done, isNullOrUndefined, isBoolean, isInt32OrMagic, isPointerSized; 1979 1980 asMasm().branch32(Assembler::Equal, type, Imm32(JSVAL_TYPE_NULL), 1981 &isNullOrUndefined); 1982 asMasm().branch32(Assembler::Equal, type, Imm32(JSVAL_TYPE_UNDEFINED), 1983 &isNullOrUndefined); 1984 asMasm().branch32(Assembler::Equal, type, Imm32(JSVAL_TYPE_BOOLEAN), 1985 &isBoolean); 1986 asMasm().branch32(Assembler::Equal, type, Imm32(JSVAL_TYPE_INT32), 1987 &isInt32OrMagic); 1988 asMasm().branch32(Assembler::Equal, type, Imm32(JSVAL_TYPE_MAGIC), 1989 &isInt32OrMagic); 1990 // GCThing types aren't currently supported, because SignExtendWord truncates 1991 // payloads above UINT32_MAX. 1992 breakpoint(); 1993 { 1994 bind(&isNullOrUndefined); 1995 1996 // Ensure no payload for null and undefined. 1997 ma_b(src, src, &done, Assembler::Zero, ShortJump); 1998 breakpoint(); 1999 } 2000 { 2001 bind(&isBoolean); 2002 2003 // Ensure boolean values are either 0 or 1. 2004 ma_b(src, Imm32(1), &done, Assembler::BelowOrEqual, ShortJump); 2005 breakpoint(); 2006 } 2007 { 2008 bind(&isInt32OrMagic); 2009 2010 // Ensure |src| is sign-extended. 2011 UseScratchRegisterScope temps(this); 2012 Register scratch = temps.Acquire(); 2013 SignExtendWord(scratch, src); 2014 ma_b(src, scratch, &done, Assembler::Equal, ShortJump); 2015 breakpoint(); 2016 } 2017 bind(&done); 2018 #endif 2019 2020 // JSVAL_TAG_MAX_DOUBLE can't be directly encoded in a single `ori` 2021 // instruction. Sign-extend the tag, taking the bits into account which will 2022 // later be shifted out, into a shorter immediate which fits into `ori`. 2023 constexpr int64_t tag = 2024 int64_t(uint64_t(JSVAL_TAG_MAX_DOUBLE) << JSVAL_TAG_SHIFT) >> 2025 JSVAL_TAG_SHIFT; 2026 static_assert(is_int12(tag), "ori requires int12 immediate"); 2027 2028 ori(dest, type, tag); 2029 slli(dest, dest, JSVAL_TAG_SHIFT); 2030 2031 // Insert low 32 bits as payload, removing all high bits from |src|. 2032 UseScratchRegisterScope temps(this); 2033 Register scratch = temps.Acquire(); 2034 ZeroExtendWord(scratch, src); 2035 2036 ma_or(dest, dest, scratch); 2037 } 2038 2039 void MacroAssemblerRiscv64Compat::loadConstantFloat32(float f, 2040 FloatRegister dest) { 2041 ma_lis(dest, f); 2042 } 2043 2044 void MacroAssemblerRiscv64Compat::loadInt32OrDouble(const Address& src, 2045 FloatRegister dest) { 2046 UseScratchRegisterScope temps(this); 2047 Register scratch = temps.Acquire(); 2048 2049 Label notInt32, end; 2050 { 2051 // Inlined |branchTestInt32| to use a short-jump. 2052 Register tag = extractTag(src, scratch); 2053 ma_b(tag, ImmTag(JSVAL_TAG_INT32), ¬Int32, Assembler::NotEqual, 2054 ShortJump); 2055 } 2056 { 2057 // If it's an int, convert it to double. 2058 unboxInt32(src, scratch); 2059 convertInt32ToDouble(scratch, dest); 2060 ma_branch(&end); 2061 } 2062 bind(¬Int32); 2063 { 2064 // Not an int, just load as double. 2065 unboxDouble(src, dest); 2066 } 2067 bind(&end); 2068 } 2069 2070 void MacroAssemblerRiscv64Compat::loadInt32OrDouble(const BaseIndex& addr, 2071 FloatRegister dest) { 2072 UseScratchRegisterScope temps(this); 2073 Register scratch = temps.Acquire(); 2074 2075 computeScaledAddress(addr, scratch); 2076 loadInt32OrDouble(Address(scratch, addr.offset), dest); 2077 } 2078 2079 void MacroAssemblerRiscv64Compat::loadConstantDouble(double dp, 2080 FloatRegister dest) { 2081 ma_lid(dest, dp); 2082 } 2083 2084 Register MacroAssemblerRiscv64Compat::extractObject(const Address& address, 2085 Register scratch) { 2086 loadPtr(address, scratch); 2087 ExtractBits(scratch, scratch, 0, JSVAL_TAG_SHIFT); 2088 return scratch; 2089 } 2090 2091 Register MacroAssemblerRiscv64Compat::extractTag(const Address& address, 2092 Register scratch) { 2093 loadPtr(address, scratch); 2094 ExtractBits(scratch, scratch, JSVAL_TAG_SHIFT, 64 - JSVAL_TAG_SHIFT); 2095 return scratch; 2096 } 2097 2098 Register MacroAssemblerRiscv64Compat::extractTag(const BaseIndex& address, 2099 Register scratch) { 2100 computeScaledAddress(address, scratch); 2101 return extractTag(Address(scratch, address.offset), scratch); 2102 } 2103 2104 ///////////////////////////////////////////////////////////////// 2105 // X86/X64-common/ARM/LoongArch interface. 2106 ///////////////////////////////////////////////////////////////// 2107 ///////////////////////////////////////////////////////////////// 2108 // X86/X64-common/ARM/MIPS interface. 2109 ///////////////////////////////////////////////////////////////// 2110 void MacroAssemblerRiscv64Compat::storeValue(ValueOperand val, 2111 const BaseIndex& dest) { 2112 UseScratchRegisterScope temps(this); 2113 Register scratch = temps.Acquire(); 2114 computeScaledAddress(dest, scratch); 2115 storeValue(val, Address(scratch, dest.offset)); 2116 } 2117 2118 void MacroAssemblerRiscv64Compat::storeValue(JSValueType type, Register reg, 2119 BaseIndex dest) { 2120 UseScratchRegisterScope temps(this); 2121 Register scratch = temps.Acquire(); 2122 2123 computeScaledAddress(dest, scratch); 2124 2125 int32_t offset = dest.offset; 2126 if (!is_int12(offset)) { 2127 UseScratchRegisterScope temps(this); 2128 Register scratch2 = temps.Acquire(); 2129 ma_li(scratch2, Imm32(offset)); 2130 add(scratch, scratch, scratch2); 2131 offset = 0; 2132 } 2133 2134 storeValue(type, reg, Address(scratch, offset)); 2135 } 2136 2137 void MacroAssemblerRiscv64Compat::storeValue(ValueOperand val, 2138 const Address& dest) { 2139 storePtr(val.valueReg(), Address(dest.base, dest.offset)); 2140 } 2141 2142 void MacroAssemblerRiscv64Compat::storeValue(JSValueType type, Register reg, 2143 Address dest) { 2144 if (type == JSVAL_TYPE_INT32 || type == JSVAL_TYPE_BOOLEAN) { 2145 #ifdef DEBUG 2146 { 2147 UseScratchRegisterScope temps(this); 2148 Register scratch = temps.Acquire(); 2149 2150 AssertValidPayload(*this, type, reg, scratch); 2151 } 2152 #endif 2153 2154 store32(reg, dest); 2155 JSValueShiftedTag tag = (JSValueShiftedTag)JSVAL_TYPE_TO_SHIFTED_TAG(type); 2156 store32(Imm64(tag).hi(), Address(dest.base, dest.offset + 4)); 2157 } else { 2158 UseScratchRegisterScope temps(this); 2159 Register scratch = temps.Acquire(); 2160 MOZ_ASSERT(dest.base != scratch); 2161 boxValue(type, reg, scratch); 2162 storePtr(scratch, Address(dest.base, dest.offset)); 2163 } 2164 } 2165 2166 void MacroAssemblerRiscv64Compat::storeValue(const Value& val, Address dest) { 2167 UseScratchRegisterScope temps(this); 2168 Register scratch2 = temps.Acquire(); 2169 if (val.isGCThing()) { 2170 writeDataRelocation(val); 2171 movWithPatch(ImmWord(val.asRawBits()), scratch2); 2172 } else { 2173 ma_li(scratch2, ImmWord(val.asRawBits())); 2174 } 2175 storePtr(scratch2, Address(dest.base, dest.offset)); 2176 } 2177 2178 void MacroAssemblerRiscv64Compat::storeValue(const Value& val, BaseIndex dest) { 2179 UseScratchRegisterScope temps(this); 2180 Register scratch = temps.Acquire(); 2181 computeScaledAddress(dest, scratch); 2182 2183 int32_t offset = dest.offset; 2184 if (!is_int12(offset)) { 2185 Register scratch2 = temps.Acquire(); 2186 ma_li(scratch2, Imm32(offset)); 2187 add(scratch, scratch, scratch2); 2188 offset = 0; 2189 } 2190 storeValue(val, Address(scratch, offset)); 2191 } 2192 2193 void MacroAssemblerRiscv64Compat::loadValue(const BaseIndex& addr, 2194 ValueOperand val) { 2195 UseScratchRegisterScope temps(this); 2196 Register scratch = temps.Acquire(); 2197 computeScaledAddress(addr, scratch); 2198 loadValue(Address(scratch, addr.offset), val); 2199 } 2200 2201 void MacroAssemblerRiscv64Compat::loadValue(Address src, ValueOperand val) { 2202 loadPtr(Address(src.base, src.offset), val.valueReg()); 2203 } 2204 2205 void MacroAssemblerRiscv64Compat::tagValue(JSValueType type, Register payload, 2206 ValueOperand dest) { 2207 MOZ_ASSERT(type != JSVAL_TYPE_UNDEFINED && type != JSVAL_TYPE_NULL); 2208 2209 JitSpew(JitSpew_Codegen, "[ tagValue"); 2210 2211 if (payload == dest.valueReg()) { 2212 UseScratchRegisterScope temps(this); 2213 Register scratch = temps.Acquire(); 2214 MOZ_ASSERT(dest.valueReg() != scratch); 2215 2216 AssertValidPayload(*this, type, payload, scratch); 2217 2218 switch (type) { 2219 case JSVAL_TYPE_INT32: { 2220 // Loading the shifted tag requires only two instructions. 2221 ma_li(scratch, ImmShiftedTag(type)); 2222 2223 // Insert low 32 bits as payload, removing all high bits from |payload|. 2224 ZeroExtendWord(payload, payload); 2225 or_(dest.valueReg(), payload, scratch); 2226 break; 2227 } 2228 case JSVAL_TYPE_BOOLEAN: 2229 case JSVAL_TYPE_MAGIC: 2230 case JSVAL_TYPE_STRING: 2231 case JSVAL_TYPE_SYMBOL: 2232 case JSVAL_TYPE_PRIVATE_GCTHING: 2233 case JSVAL_TYPE_BIGINT: 2234 case JSVAL_TYPE_OBJECT: { 2235 // Loading the shifted tag requires only two instructions. 2236 ma_li(scratch, ImmShiftedTag(type)); 2237 2238 // Insert payload. 2239 or_(dest.valueReg(), payload, scratch); 2240 break; 2241 } 2242 case JSVAL_TYPE_DOUBLE: 2243 case JSVAL_TYPE_UNDEFINED: 2244 case JSVAL_TYPE_NULL: 2245 case JSVAL_TYPE_UNKNOWN: 2246 MOZ_CRASH("bad value type"); 2247 } 2248 } else { 2249 boxNonDouble(type, payload, dest); 2250 } 2251 2252 JitSpew(JitSpew_Codegen, "]"); 2253 } 2254 2255 void MacroAssemblerRiscv64Compat::pushValue(ValueOperand val) { 2256 // Allocate stack slots for Value. One for each. 2257 asMasm().subPtr(Imm32(sizeof(Value)), StackPointer); 2258 // Store Value 2259 storeValue(val, Address(StackPointer, 0)); 2260 } 2261 2262 void MacroAssemblerRiscv64Compat::pushValue(const Address& addr) { 2263 // Load value before allocate stack, addr.base may be is sp. 2264 UseScratchRegisterScope temps(this); 2265 Register scratch = temps.Acquire(); 2266 loadPtr(Address(addr.base, addr.offset), scratch); 2267 ma_sub64(StackPointer, StackPointer, Imm32(sizeof(Value))); 2268 storePtr(scratch, Address(StackPointer, 0)); 2269 } 2270 2271 void MacroAssemblerRiscv64Compat::popValue(ValueOperand val) { 2272 ld(val.valueReg(), StackPointer, 0); 2273 ma_add64(StackPointer, StackPointer, Imm32(sizeof(Value))); 2274 } 2275 2276 void MacroAssemblerRiscv64Compat::breakpoint(uint32_t value) { break_(value); } 2277 2278 void MacroAssemblerRiscv64Compat::handleFailureWithHandlerTail( 2279 Label* profilerExitTail, Label* bailoutTail, 2280 uint32_t* returnValueCheckOffset) { 2281 // Reserve space for exception information. 2282 int size = (sizeof(ResumeFromException) + ABIStackAlignment) & 2283 ~(ABIStackAlignment - 1); 2284 asMasm().subPtr(Imm32(size), StackPointer); 2285 mv(a0, StackPointer); // Use a0 since it is a first function argument 2286 2287 // Call the handler. 2288 using Fn = void (*)(ResumeFromException* rfe); 2289 asMasm().setupUnalignedABICall(a1); 2290 asMasm().passABIArg(a0); 2291 asMasm().callWithABI<Fn, HandleException>( 2292 ABIType::General, CheckUnsafeCallWithABI::DontCheckHasExitFrame); 2293 2294 *returnValueCheckOffset = currentOffset(); 2295 2296 Label entryFrame; 2297 Label catch_; 2298 Label finally; 2299 Label returnBaseline; 2300 Label returnIon; 2301 Label bailout; 2302 Label wasmInterpEntry; 2303 Label wasmCatch; 2304 2305 // Already clobbered a0, so use it... 2306 load32(Address(StackPointer, ResumeFromException::offsetOfKind()), a0); 2307 asMasm().branch32(Assembler::Equal, a0, 2308 Imm32(ExceptionResumeKind::EntryFrame), &entryFrame); 2309 asMasm().branch32(Assembler::Equal, a0, Imm32(ExceptionResumeKind::Catch), 2310 &catch_); 2311 asMasm().branch32(Assembler::Equal, a0, Imm32(ExceptionResumeKind::Finally), 2312 &finally); 2313 asMasm().branch32(Assembler::Equal, a0, 2314 Imm32(ExceptionResumeKind::ForcedReturnBaseline), 2315 &returnBaseline); 2316 asMasm().branch32(Assembler::Equal, a0, 2317 Imm32(ExceptionResumeKind::ForcedReturnIon), &returnIon); 2318 asMasm().branch32(Assembler::Equal, a0, Imm32(ExceptionResumeKind::Bailout), 2319 &bailout); 2320 asMasm().branch32(Assembler::Equal, a0, 2321 Imm32(ExceptionResumeKind::WasmInterpEntry), 2322 &wasmInterpEntry); 2323 asMasm().branch32(Assembler::Equal, a0, Imm32(ExceptionResumeKind::WasmCatch), 2324 &wasmCatch); 2325 2326 breakpoint(); // Invalid kind. 2327 2328 // No exception handler. Load the error value, restore state and return from 2329 // the entry frame. 2330 bind(&entryFrame); 2331 asMasm().moveValue(MagicValue(JS_ION_ERROR), JSReturnOperand); 2332 loadPtr(Address(StackPointer, ResumeFromException::offsetOfFramePointer()), 2333 FramePointer); 2334 loadPtr(Address(StackPointer, ResumeFromException::offsetOfStackPointer()), 2335 StackPointer); 2336 2337 // We're going to be returning by the ion calling convention 2338 ma_pop(ra); 2339 jump(ra); 2340 nop(); 2341 2342 // If we found a catch handler, this must be a baseline frame. Restore 2343 // state and jump to the catch block. 2344 bind(&catch_); 2345 loadPtr(Address(StackPointer, ResumeFromException::offsetOfTarget()), a0); 2346 loadPtr(Address(StackPointer, ResumeFromException::offsetOfFramePointer()), 2347 FramePointer); 2348 loadPtr(Address(StackPointer, ResumeFromException::offsetOfStackPointer()), 2349 StackPointer); 2350 jump(a0); 2351 2352 // If we found a finally block, this must be a baseline frame. Push three 2353 // values expected by the finally block: the exception, the exception stack, 2354 // and BooleanValue(true). 2355 bind(&finally); 2356 ValueOperand exception = ValueOperand(a1); 2357 loadValue(Address(sp, ResumeFromException::offsetOfException()), exception); 2358 2359 ValueOperand exceptionStack = ValueOperand(a2); 2360 loadValue(Address(sp, ResumeFromException::offsetOfExceptionStack()), 2361 exceptionStack); 2362 2363 loadPtr(Address(sp, ResumeFromException::offsetOfTarget()), a0); 2364 loadPtr(Address(sp, ResumeFromException::offsetOfFramePointer()), 2365 FramePointer); 2366 loadPtr(Address(sp, ResumeFromException::offsetOfStackPointer()), sp); 2367 2368 pushValue(exception); 2369 pushValue(exceptionStack); 2370 pushValue(BooleanValue(true)); 2371 jump(a0); 2372 2373 // Return BaselineFrame->returnValue() to the caller. 2374 // Used in debug mode and for GeneratorReturn. 2375 Label profilingInstrumentation; 2376 bind(&returnBaseline); 2377 loadPtr(Address(StackPointer, ResumeFromException::offsetOfFramePointer()), 2378 FramePointer); 2379 loadPtr(Address(StackPointer, ResumeFromException::offsetOfStackPointer()), 2380 StackPointer); 2381 loadValue(Address(FramePointer, BaselineFrame::reverseOffsetOfReturnValue()), 2382 JSReturnOperand); 2383 jump(&profilingInstrumentation); 2384 2385 // Return the given value to the caller. 2386 bind(&returnIon); 2387 loadValue(Address(StackPointer, ResumeFromException::offsetOfException()), 2388 JSReturnOperand); 2389 loadPtr(Address(StackPointer, ResumeFromException::offsetOfFramePointer()), 2390 FramePointer); 2391 loadPtr(Address(StackPointer, ResumeFromException::offsetOfStackPointer()), 2392 StackPointer); 2393 2394 // If profiling is enabled, then update the lastProfilingFrame to refer to 2395 // caller frame before returning. This code is shared by ForcedReturnIon 2396 // and ForcedReturnBaseline. 2397 bind(&profilingInstrumentation); 2398 { 2399 Label skipProfilingInstrumentation; 2400 // Test if profiler enabled. 2401 AbsoluteAddress addressOfEnabled( 2402 asMasm().runtime()->geckoProfiler().addressOfEnabled()); 2403 asMasm().branch32(Assembler::Equal, addressOfEnabled, Imm32(0), 2404 &skipProfilingInstrumentation); 2405 jump(profilerExitTail); 2406 bind(&skipProfilingInstrumentation); 2407 } 2408 2409 mv(StackPointer, FramePointer); 2410 pop(FramePointer); 2411 ret(); 2412 2413 // If we are bailing out to baseline to handle an exception, jump to 2414 // the bailout tail stub. Load 1 (true) in ReturnReg to indicate success. 2415 bind(&bailout); 2416 loadPtr(Address(sp, ResumeFromException::offsetOfBailoutInfo()), a2); 2417 loadPtr(Address(StackPointer, ResumeFromException::offsetOfStackPointer()), 2418 StackPointer); 2419 ma_li(ReturnReg, Imm32(1)); 2420 jump(bailoutTail); 2421 2422 // Reset SP and FP; SP is pointing to the unwound return address to the wasm 2423 // interpreter entry, so we can just ret(). 2424 bind(&wasmInterpEntry); 2425 loadPtr(Address(StackPointer, ResumeFromException::offsetOfFramePointer()), 2426 FramePointer); 2427 loadPtr(Address(StackPointer, ResumeFromException::offsetOfStackPointer()), 2428 StackPointer); 2429 ma_li(InstanceReg, ImmWord(wasm::InterpFailInstanceReg)); 2430 ret(); 2431 2432 // Found a wasm catch handler, restore state and jump to it. 2433 bind(&wasmCatch); 2434 wasm::GenerateJumpToCatchHandler(asMasm(), sp, a1, a2); 2435 } 2436 2437 CodeOffset MacroAssemblerRiscv64Compat::toggledJump(Label* label) { 2438 CodeOffset ret(nextOffset().getOffset()); 2439 BranchShort(label); 2440 return ret; 2441 } 2442 2443 CodeOffset MacroAssemblerRiscv64Compat::toggledCall(JitCode* target, 2444 bool enabled) { 2445 DEBUG_PRINTF("\ttoggledCall\n"); 2446 UseScratchRegisterScope temps(this); 2447 Register scratch = temps.Acquire(); 2448 BlockTrampolinePoolScope block_trampoline_pool(this, 8); 2449 BufferOffset bo = nextOffset(); 2450 CodeOffset offset(bo.getOffset()); 2451 addPendingJump(bo, ImmPtr(target->raw()), RelocationKind::JITCODE); 2452 ma_liPatchable(scratch, ImmPtr(target->raw())); 2453 if (enabled) { 2454 jalr(scratch); 2455 } else { 2456 nop(); 2457 } 2458 MOZ_ASSERT_IF(!oom(), nextOffset().getOffset() - offset.offset() == 2459 ToggledCallSize(nullptr)); 2460 return offset; 2461 } 2462 2463 void MacroAssembler::subFromStackPtr(Imm32 imm32) { 2464 if (imm32.value) { 2465 asMasm().subPtr(imm32, StackPointer); 2466 } 2467 } 2468 2469 void MacroAssembler::clampDoubleToUint8(FloatRegister input, Register output) { 2470 Round_w_d(output, input); 2471 Clear_if_nan_d(output, input); 2472 clampIntToUint8(output); 2473 } 2474 2475 //{{{ check_macroassembler_style 2476 // =============================================================== 2477 // MacroAssembler high-level usage. 2478 bool MacroAssembler::convertUInt64ToDoubleNeedsTemp() { return false; } 2479 CodeOffset MacroAssembler::call(Label* label) { return BranchAndLink(label); } 2480 CodeOffset MacroAssembler::call(Register reg) { 2481 jalr(reg, 0); 2482 return CodeOffset(currentOffset()); 2483 } 2484 CodeOffset MacroAssembler::call(wasm::SymbolicAddress target) { 2485 UseScratchRegisterScope temps(this); 2486 temps.Exclude(GeneralRegisterSet(1 << CallReg.code())); 2487 movePtr(target, CallReg); 2488 return call(CallReg); 2489 } 2490 CodeOffset MacroAssembler::farJumpWithPatch() { 2491 UseScratchRegisterScope temps(this); 2492 Register scratch = temps.Acquire(); 2493 Register scratch2 = temps.Acquire(); 2494 // Allocate space which will be patched by patchFarJump(). 2495 CodeOffset farJump(nextInstrOffset(5).getOffset()); 2496 auipc(scratch, 0); 2497 lw(scratch2, scratch, 4 * sizeof(Instr)); 2498 add(scratch, scratch, scratch2); 2499 jr(scratch, 0); 2500 spew(".space 32bit initValue 0xffff ffff"); 2501 emit(UINT32_MAX); 2502 return farJump; 2503 } 2504 CodeOffset MacroAssembler::moveNearAddressWithPatch(Register dest) { 2505 return movWithPatch(ImmPtr(nullptr), dest); 2506 } 2507 CodeOffset MacroAssembler::nopPatchableToCall() { 2508 BlockTrampolinePoolScope block_trampoline_pool(this, 7); 2509 // riscv64 2510 nop(); // lui(rd, (int32_t)high_20); 2511 nop(); // addi(rd, rd, low_12); // 31 bits in rd. 2512 nop(); // slli(rd, rd, 11); // Space for next 11 bis 2513 nop(); // ori(rd, rd, b11); // 11 bits are put in. 42 bit in rd 2514 nop(); // slli(rd, rd, 6); // Space for next 6 bits 2515 nop(); // ori(rd, rd, a6); // 6 bits are put in. 48 bis in rd 2516 nop(); // jirl 2517 return CodeOffset(currentOffset()); 2518 } 2519 FaultingCodeOffset MacroAssembler::wasmTrapInstruction() { 2520 BlockTrampolinePoolScope block_trampoline_pool(this, 2); 2521 FaultingCodeOffset fco = FaultingCodeOffset(currentOffset()); 2522 illegal_trap(kWasmTrapCode); 2523 ebreak(); 2524 return fco; 2525 } 2526 size_t MacroAssembler::PushRegsInMaskSizeInBytes(LiveRegisterSet set) { 2527 return set.gprs().size() * sizeof(intptr_t) + set.fpus().getPushSizeInBytes(); 2528 } 2529 2530 template <typename T> 2531 void MacroAssembler::branchValueIsNurseryCellImpl(Condition cond, 2532 const T& value, Register temp, 2533 Label* label) { 2534 MOZ_ASSERT(cond == Assembler::Equal || cond == Assembler::NotEqual); 2535 Label done; 2536 branchTestGCThing(Assembler::NotEqual, value, 2537 cond == Assembler::Equal ? &done : label); 2538 2539 // temp may be InvalidReg, use scratch2 instead. 2540 UseScratchRegisterScope temps(this); 2541 Register scratch2 = temps.Acquire(); 2542 2543 getGCThingValueChunk(value, scratch2); 2544 loadPtr(Address(scratch2, gc::ChunkStoreBufferOffset), scratch2); 2545 branchPtr(InvertCondition(cond), scratch2, ImmWord(0), label); 2546 2547 bind(&done); 2548 } 2549 2550 template <typename T> 2551 void MacroAssembler::storeUnboxedValue(const ConstantOrRegister& value, 2552 MIRType valueType, const T& dest) { 2553 MOZ_ASSERT(valueType < MIRType::Value); 2554 2555 if (valueType == MIRType::Double) { 2556 boxDouble(value.reg().typedReg().fpu(), dest); 2557 return; 2558 } 2559 2560 if (value.constant()) { 2561 storeValue(value.value(), dest); 2562 } else { 2563 storeValue(ValueTypeFromMIRType(valueType), value.reg().typedReg().gpr(), 2564 dest); 2565 } 2566 } 2567 2568 template void MacroAssembler::storeUnboxedValue(const ConstantOrRegister& value, 2569 MIRType valueType, 2570 const Address& dest); 2571 template void MacroAssembler::storeUnboxedValue( 2572 const ConstantOrRegister& value, MIRType valueType, 2573 const BaseObjectElementIndex& dest); 2574 2575 // =============================================================== 2576 // Jit Frames. 2577 2578 uint32_t MacroAssembler::pushFakeReturnAddress(Register scratch) { 2579 CodeLabel cl; 2580 2581 ma_li(scratch, &cl); 2582 Push(scratch); 2583 bind(&cl); 2584 uint32_t retAddr = currentOffset(); 2585 2586 addCodeLabel(cl); 2587 return retAddr; 2588 } 2589 2590 //=============================== 2591 // AtomicOp 2592 2593 template <typename T> 2594 static void AtomicExchange(MacroAssembler& masm, 2595 const wasm::MemoryAccessDesc* access, 2596 Scalar::Type type, Synchronization sync, 2597 const T& mem, Register value, Register valueTemp, 2598 Register offsetTemp, Register maskTemp, 2599 Register output) { 2600 bool signExtend = Scalar::isSignedIntType(type); 2601 unsigned nbytes = Scalar::byteSize(type); 2602 2603 UseScratchRegisterScope temps(&masm); 2604 2605 switch (nbytes) { 2606 case 1: 2607 case 2: 2608 break; 2609 case 4: 2610 MOZ_ASSERT(valueTemp == InvalidReg); 2611 MOZ_ASSERT(offsetTemp == InvalidReg); 2612 MOZ_ASSERT(maskTemp == InvalidReg); 2613 break; 2614 default: 2615 MOZ_CRASH(); 2616 } 2617 2618 Label again; 2619 2620 Register scratch = temps.Acquire(); 2621 masm.computeEffectiveAddress(mem, scratch); 2622 2623 Register scratch2 = temps.Acquire(); 2624 2625 if (nbytes == 4) { 2626 masm.memoryBarrierBefore(sync); 2627 masm.bind(&again); 2628 BlockTrampolinePoolScope block_trampoline_pool(&masm, 2629 /* 1 + 1 + 1 + 4 + 1 = */ 8, 2630 1); 2631 if (access) { 2632 masm.append(*access, wasm::TrapMachineInsn::Atomic, 2633 FaultingCodeOffset(masm.currentOffset())); 2634 } 2635 2636 masm.lr_w(true, true, output, scratch); 2637 masm.or_(scratch2, value, zero); 2638 masm.sc_w(true, true, scratch2, scratch, scratch2); 2639 masm.ma_b(scratch2, Register(scratch2), &again, Assembler::NonZero, 2640 ShortJump); 2641 2642 masm.memoryBarrierAfter(sync); 2643 2644 return; 2645 } 2646 2647 masm.andi(offsetTemp, scratch, 3); 2648 masm.subPtr(offsetTemp, scratch); 2649 masm.slliw(offsetTemp, offsetTemp, 3); 2650 masm.ma_li(maskTemp, Imm32(UINT32_MAX >> ((4 - nbytes) * 8))); 2651 masm.sllw(maskTemp, maskTemp, offsetTemp); 2652 masm.not_(maskTemp, maskTemp); 2653 switch (nbytes) { 2654 case 1: 2655 masm.andi(valueTemp, value, 0xff); 2656 break; 2657 case 2: 2658 masm.ma_and(valueTemp, value, Imm32(0xffff)); 2659 break; 2660 } 2661 masm.sllw(valueTemp, valueTemp, offsetTemp); 2662 2663 masm.memoryBarrierBefore(sync); 2664 2665 masm.bind(&again); 2666 2667 BlockTrampolinePoolScope block_trampoline_pool( 2668 &masm, /* 1 + 1 + 1 + 1 + 4 + 1 + 2 + 1 = */ 12, 1); 2669 if (access) { 2670 masm.append(*access, wasm::TrapMachineInsn::Atomic, 2671 FaultingCodeOffset(masm.currentOffset())); 2672 } 2673 2674 masm.lr_w(true, true, output, scratch); 2675 masm.and_(scratch2, output, maskTemp); 2676 masm.or_(scratch2, scratch2, valueTemp); 2677 2678 masm.sc_w(true, true, scratch2, scratch, scratch2); 2679 2680 masm.ma_b(scratch2, Register(scratch2), &again, Assembler::NonZero, 2681 ShortJump); 2682 2683 masm.srlw(output, output, offsetTemp); 2684 2685 switch (nbytes) { 2686 case 1: 2687 if (signExtend) { 2688 masm.SignExtendByte(output, output); 2689 } else { 2690 masm.andi(output, output, 0xff); 2691 } 2692 break; 2693 case 2: 2694 if (signExtend) { 2695 masm.SignExtendShort(output, output); 2696 } else { 2697 masm.ma_and(output, Imm32(0xffff)); 2698 } 2699 break; 2700 } 2701 2702 masm.memoryBarrierAfter(sync); 2703 } 2704 2705 template <typename T> 2706 static void AtomicExchange64(MacroAssembler& masm, 2707 const wasm::MemoryAccessDesc* access, 2708 Synchronization sync, const T& mem, 2709 Register64 value, Register64 output) { 2710 MOZ_ASSERT(value != output); 2711 UseScratchRegisterScope temps(&masm); 2712 Register scratch = temps.Acquire(); 2713 Register scratch2 = temps.Acquire(); 2714 masm.computeEffectiveAddress(mem, scratch2); 2715 2716 Label tryAgain; 2717 2718 masm.memoryBarrierBefore(sync); 2719 2720 masm.bind(&tryAgain); 2721 BlockTrampolinePoolScope block_trampoline_pool(&masm, 2722 /* 1 + 1 + 1 + 4 + 1 = */ 8, 2723 1); 2724 if (access) { 2725 masm.append(*access, js::wasm::TrapMachineInsn::Load64, 2726 FaultingCodeOffset(masm.currentOffset())); 2727 } 2728 2729 masm.lr_d(true, true, output.reg, scratch2); 2730 masm.movePtr(value.reg, scratch); 2731 masm.sc_d(true, true, scratch, scratch2, scratch); 2732 masm.ma_b(scratch, scratch, &tryAgain, Assembler::NonZero, ShortJump); 2733 2734 masm.memoryBarrierAfter(sync); 2735 } 2736 2737 template <typename T> 2738 static void AtomicFetchOp64(MacroAssembler& masm, 2739 const wasm::MemoryAccessDesc* access, 2740 Synchronization sync, AtomicOp op, Register64 value, 2741 const T& mem, Register64 temp, Register64 output) { 2742 MOZ_ASSERT(value != output); 2743 MOZ_ASSERT(value != temp); 2744 UseScratchRegisterScope temps(&masm); 2745 Register scratch2 = temps.Acquire(); 2746 masm.computeEffectiveAddress(mem, scratch2); 2747 2748 Label tryAgain; 2749 2750 masm.memoryBarrierBefore(sync); 2751 2752 masm.bind(&tryAgain); 2753 BlockTrampolinePoolScope block_trampoline_pool(&masm, 2754 /* 1 + 1 + 1 + 4 + 1 = */ 8, 2755 1); 2756 if (access) { 2757 masm.append(*access, js::wasm::TrapMachineInsn::Load64, 2758 FaultingCodeOffset(masm.currentOffset())); 2759 } 2760 2761 masm.lr_d(true, true, output.reg, scratch2); 2762 2763 switch (op) { 2764 case AtomicOp::Add: 2765 masm.add(temp.reg, output.reg, value.reg); 2766 break; 2767 case AtomicOp::Sub: 2768 masm.sub(temp.reg, output.reg, value.reg); 2769 break; 2770 case AtomicOp::And: 2771 masm.and_(temp.reg, output.reg, value.reg); 2772 break; 2773 case AtomicOp::Or: 2774 masm.or_(temp.reg, output.reg, value.reg); 2775 break; 2776 case AtomicOp::Xor: 2777 masm.xor_(temp.reg, output.reg, value.reg); 2778 break; 2779 default: 2780 MOZ_CRASH(); 2781 } 2782 2783 masm.sc_d(true, true, temp.reg, scratch2, temp.reg); 2784 masm.ma_b(temp.reg, temp.reg, &tryAgain, Assembler::NonZero, ShortJump); 2785 2786 masm.memoryBarrierAfter(sync); 2787 } 2788 2789 template <typename T> 2790 static void AtomicEffectOp(MacroAssembler& masm, 2791 const wasm::MemoryAccessDesc* access, 2792 Scalar::Type type, Synchronization sync, AtomicOp op, 2793 const T& mem, Register value, Register valueTemp, 2794 Register offsetTemp, Register maskTemp) { 2795 UseScratchRegisterScope temps(&masm); 2796 unsigned nbytes = Scalar::byteSize(type); 2797 2798 switch (nbytes) { 2799 case 1: 2800 case 2: 2801 break; 2802 case 4: 2803 MOZ_ASSERT(valueTemp == InvalidReg); 2804 MOZ_ASSERT(offsetTemp == InvalidReg); 2805 MOZ_ASSERT(maskTemp == InvalidReg); 2806 break; 2807 default: 2808 MOZ_CRASH(); 2809 } 2810 2811 Label again; 2812 2813 Register scratch = temps.Acquire(); 2814 masm.computeEffectiveAddress(mem, scratch); 2815 2816 Register scratch2 = temps.Acquire(); 2817 2818 if (nbytes == 4) { 2819 masm.memoryBarrierBefore(sync); 2820 masm.bind(&again); 2821 2822 if (access) { 2823 masm.append(*access, wasm::TrapMachineInsn::Atomic, 2824 FaultingCodeOffset(masm.currentOffset())); 2825 } 2826 2827 masm.lr_w(true, true, scratch2, scratch); 2828 2829 switch (op) { 2830 case AtomicOp::Add: 2831 masm.addw(scratch2, scratch2, value); 2832 break; 2833 case AtomicOp::Sub: 2834 masm.subw(scratch2, scratch2, value); 2835 break; 2836 case AtomicOp::And: 2837 masm.and_(scratch2, scratch2, value); 2838 break; 2839 case AtomicOp::Or: 2840 masm.or_(scratch2, scratch2, value); 2841 break; 2842 case AtomicOp::Xor: 2843 masm.xor_(scratch2, scratch2, value); 2844 break; 2845 default: 2846 MOZ_CRASH(); 2847 } 2848 2849 masm.sc_w(true, true, scratch2, scratch, scratch2); 2850 masm.ma_b(scratch2, Register(scratch2), &again, Assembler::NonZero, 2851 ShortJump); 2852 2853 masm.memoryBarrierAfter(sync); 2854 2855 return; 2856 } 2857 2858 masm.andi(offsetTemp, scratch, 3); 2859 masm.subPtr(offsetTemp, scratch); 2860 masm.slliw(offsetTemp, offsetTemp, 3); 2861 masm.ma_li(maskTemp, Imm32(UINT32_MAX >> ((4 - nbytes) * 8))); 2862 masm.sllw(maskTemp, maskTemp, offsetTemp); 2863 masm.not_(maskTemp, maskTemp); 2864 2865 masm.memoryBarrierBefore(sync); 2866 2867 masm.bind(&again); 2868 2869 if (access) { 2870 masm.append(*access, wasm::TrapMachineInsn::Atomic, 2871 FaultingCodeOffset(masm.currentOffset())); 2872 } 2873 2874 masm.lr_w(true, true, scratch2, scratch); 2875 masm.srlw(valueTemp, scratch2, offsetTemp); 2876 2877 switch (op) { 2878 case AtomicOp::Add: 2879 masm.addw(valueTemp, valueTemp, value); 2880 break; 2881 case AtomicOp::Sub: 2882 masm.subw(valueTemp, valueTemp, value); 2883 break; 2884 case AtomicOp::And: 2885 masm.and_(valueTemp, valueTemp, value); 2886 break; 2887 case AtomicOp::Or: 2888 masm.or_(valueTemp, valueTemp, value); 2889 break; 2890 case AtomicOp::Xor: 2891 masm.xor_(valueTemp, valueTemp, value); 2892 break; 2893 default: 2894 MOZ_CRASH(); 2895 } 2896 2897 switch (nbytes) { 2898 case 1: 2899 masm.andi(valueTemp, valueTemp, 0xff); 2900 break; 2901 case 2: 2902 masm.ma_and(valueTemp, valueTemp, Imm32(0xffff)); 2903 break; 2904 } 2905 2906 masm.sllw(valueTemp, valueTemp, offsetTemp); 2907 2908 masm.and_(scratch2, scratch2, maskTemp); 2909 masm.or_(scratch2, scratch2, valueTemp); 2910 2911 masm.sc_w(true, true, scratch2, scratch, scratch2); 2912 2913 masm.ma_b(scratch2, Register(scratch2), &again, Assembler::NonZero, 2914 ShortJump); 2915 2916 masm.memoryBarrierAfter(sync); 2917 } 2918 2919 template <typename T> 2920 static void AtomicFetchOp(MacroAssembler& masm, 2921 const wasm::MemoryAccessDesc* access, 2922 Scalar::Type type, Synchronization sync, AtomicOp op, 2923 const T& mem, Register value, Register valueTemp, 2924 Register offsetTemp, Register maskTemp, 2925 Register output) { 2926 UseScratchRegisterScope temps(&masm); 2927 bool signExtend = Scalar::isSignedIntType(type); 2928 unsigned nbytes = Scalar::byteSize(type); 2929 2930 switch (nbytes) { 2931 case 1: 2932 case 2: 2933 break; 2934 case 4: 2935 MOZ_ASSERT(valueTemp == InvalidReg); 2936 MOZ_ASSERT(offsetTemp == InvalidReg); 2937 MOZ_ASSERT(maskTemp == InvalidReg); 2938 break; 2939 default: 2940 MOZ_CRASH(); 2941 } 2942 2943 Label again; 2944 2945 Register scratch = temps.Acquire(); 2946 masm.computeEffectiveAddress(mem, scratch); 2947 2948 Register scratch2 = temps.Acquire(); 2949 2950 if (nbytes == 4) { 2951 masm.memoryBarrierBefore(sync); 2952 masm.bind(&again); 2953 2954 if (access) { 2955 masm.append(*access, wasm::TrapMachineInsn::Atomic, 2956 FaultingCodeOffset(masm.currentOffset())); 2957 } 2958 2959 masm.lr_w(true, true, output, scratch); 2960 2961 switch (op) { 2962 case AtomicOp::Add: 2963 masm.addw(scratch2, output, value); 2964 break; 2965 case AtomicOp::Sub: 2966 masm.subw(scratch2, output, value); 2967 break; 2968 case AtomicOp::And: 2969 masm.and_(scratch2, output, value); 2970 break; 2971 case AtomicOp::Or: 2972 masm.or_(scratch2, output, value); 2973 break; 2974 case AtomicOp::Xor: 2975 masm.xor_(scratch2, output, value); 2976 break; 2977 default: 2978 MOZ_CRASH(); 2979 } 2980 2981 masm.sc_w(true, true, scratch2, scratch, scratch2); 2982 masm.ma_b(scratch2, Register(scratch2), &again, Assembler::NonZero, 2983 ShortJump); 2984 2985 masm.memoryBarrierAfter(sync); 2986 2987 return; 2988 } 2989 2990 masm.andi(offsetTemp, scratch, 3); 2991 masm.subPtr(offsetTemp, scratch); 2992 masm.slliw(offsetTemp, offsetTemp, 3); 2993 masm.ma_li(maskTemp, Imm32(UINT32_MAX >> ((4 - nbytes) * 8))); 2994 masm.sllw(maskTemp, maskTemp, offsetTemp); 2995 masm.not_(maskTemp, maskTemp); 2996 2997 masm.memoryBarrierBefore(sync); 2998 2999 masm.bind(&again); 3000 3001 if (access) { 3002 masm.append(*access, wasm::TrapMachineInsn::Atomic, 3003 FaultingCodeOffset(masm.currentOffset())); 3004 } 3005 3006 masm.lr_w(true, true, scratch2, scratch); 3007 masm.srlw(output, scratch2, offsetTemp); 3008 3009 switch (op) { 3010 case AtomicOp::Add: 3011 masm.addw(valueTemp, output, value); 3012 break; 3013 case AtomicOp::Sub: 3014 masm.subw(valueTemp, output, value); 3015 break; 3016 case AtomicOp::And: 3017 masm.and_(valueTemp, output, value); 3018 break; 3019 case AtomicOp::Or: 3020 masm.or_(valueTemp, output, value); 3021 break; 3022 case AtomicOp::Xor: 3023 masm.xor_(valueTemp, output, value); 3024 break; 3025 default: 3026 MOZ_CRASH(); 3027 } 3028 3029 switch (nbytes) { 3030 case 1: 3031 masm.andi(valueTemp, valueTemp, 0xff); 3032 break; 3033 case 2: 3034 masm.ma_and(valueTemp, Imm32(0xffff)); 3035 break; 3036 } 3037 3038 masm.sllw(valueTemp, valueTemp, offsetTemp); 3039 3040 masm.and_(scratch2, scratch2, maskTemp); 3041 masm.or_(scratch2, scratch2, valueTemp); 3042 3043 masm.sc_w(true, true, scratch2, scratch, scratch2); 3044 3045 masm.ma_b(scratch2, Register(scratch2), &again, Assembler::NonZero, 3046 ShortJump); 3047 3048 switch (nbytes) { 3049 case 1: 3050 if (signExtend) { 3051 masm.SignExtendByte(output, output); 3052 } else { 3053 masm.andi(output, output, 0xff); 3054 } 3055 break; 3056 case 2: 3057 if (signExtend) { 3058 masm.SignExtendShort(output, output); 3059 } else { 3060 masm.ma_and(output, Imm32(0xffff)); 3061 } 3062 break; 3063 } 3064 3065 masm.memoryBarrierAfter(sync); 3066 } 3067 3068 // ======================================================================== 3069 // JS atomic operations. 3070 3071 template <typename T> 3072 static void CompareExchangeJS(MacroAssembler& masm, Scalar::Type arrayType, 3073 Synchronization sync, const T& mem, 3074 Register oldval, Register newval, 3075 Register valueTemp, Register offsetTemp, 3076 Register maskTemp, Register temp, 3077 AnyRegister output) { 3078 if (arrayType == Scalar::Uint32) { 3079 masm.compareExchange(arrayType, sync, mem, oldval, newval, valueTemp, 3080 offsetTemp, maskTemp, temp); 3081 masm.convertUInt32ToDouble(temp, output.fpu()); 3082 } else { 3083 masm.compareExchange(arrayType, sync, mem, oldval, newval, valueTemp, 3084 offsetTemp, maskTemp, output.gpr()); 3085 } 3086 } 3087 3088 template <typename T> 3089 static void AtomicExchangeJS(MacroAssembler& masm, Scalar::Type arrayType, 3090 Synchronization sync, const T& mem, Register value, 3091 Register valueTemp, Register offsetTemp, 3092 Register maskTemp, Register temp, 3093 AnyRegister output) { 3094 if (arrayType == Scalar::Uint32) { 3095 masm.atomicExchange(arrayType, sync, mem, value, valueTemp, offsetTemp, 3096 maskTemp, temp); 3097 masm.convertUInt32ToDouble(temp, output.fpu()); 3098 } else { 3099 masm.atomicExchange(arrayType, sync, mem, value, valueTemp, offsetTemp, 3100 maskTemp, output.gpr()); 3101 } 3102 } 3103 3104 template <typename T> 3105 static void AtomicFetchOpJS(MacroAssembler& masm, Scalar::Type arrayType, 3106 Synchronization sync, AtomicOp op, Register value, 3107 const T& mem, Register valueTemp, 3108 Register offsetTemp, Register maskTemp, 3109 Register temp, AnyRegister output) { 3110 if (arrayType == Scalar::Uint32) { 3111 masm.atomicFetchOp(arrayType, sync, op, value, mem, valueTemp, offsetTemp, 3112 maskTemp, temp); 3113 masm.convertUInt32ToDouble(temp, output.fpu()); 3114 } else { 3115 masm.atomicFetchOp(arrayType, sync, op, value, mem, valueTemp, offsetTemp, 3116 maskTemp, output.gpr()); 3117 } 3118 } 3119 3120 void MacroAssembler::atomicEffectOpJS(Scalar::Type arrayType, 3121 Synchronization sync, AtomicOp op, 3122 Register value, const BaseIndex& mem, 3123 Register valueTemp, Register offsetTemp, 3124 Register maskTemp) { 3125 AtomicEffectOp(*this, nullptr, arrayType, sync, op, mem, value, valueTemp, 3126 offsetTemp, maskTemp); 3127 } 3128 3129 void MacroAssembler::atomicEffectOpJS(Scalar::Type arrayType, 3130 Synchronization sync, AtomicOp op, 3131 Register value, const Address& mem, 3132 Register valueTemp, Register offsetTemp, 3133 Register maskTemp) { 3134 AtomicEffectOp(*this, nullptr, arrayType, sync, op, mem, value, valueTemp, 3135 offsetTemp, maskTemp); 3136 } 3137 void MacroAssembler::atomicExchange64(Synchronization sync, const Address& mem, 3138 Register64 value, Register64 output) { 3139 AtomicExchange64(*this, nullptr, sync, mem, value, output); 3140 } 3141 3142 void MacroAssembler::atomicExchange64(Synchronization sync, 3143 const BaseIndex& mem, Register64 value, 3144 Register64 output) { 3145 AtomicExchange64(*this, nullptr, sync, mem, value, output); 3146 } 3147 3148 void MacroAssembler::atomicExchangeJS(Scalar::Type arrayType, 3149 Synchronization sync, const Address& mem, 3150 Register value, Register valueTemp, 3151 Register offsetTemp, Register maskTemp, 3152 Register temp, AnyRegister output) { 3153 AtomicExchangeJS(*this, arrayType, sync, mem, value, valueTemp, offsetTemp, 3154 maskTemp, temp, output); 3155 } 3156 3157 void MacroAssembler::atomicExchangeJS(Scalar::Type arrayType, 3158 Synchronization sync, 3159 const BaseIndex& mem, Register value, 3160 Register valueTemp, Register offsetTemp, 3161 Register maskTemp, Register temp, 3162 AnyRegister output) { 3163 AtomicExchangeJS(*this, arrayType, sync, mem, value, valueTemp, offsetTemp, 3164 maskTemp, temp, output); 3165 } 3166 3167 void MacroAssembler::atomicExchange(Scalar::Type type, Synchronization sync, 3168 const Address& mem, Register value, 3169 Register valueTemp, Register offsetTemp, 3170 Register maskTemp, Register output) { 3171 AtomicExchange(*this, nullptr, type, sync, mem, value, valueTemp, offsetTemp, 3172 maskTemp, output); 3173 } 3174 3175 void MacroAssembler::atomicExchange(Scalar::Type type, Synchronization sync, 3176 const BaseIndex& mem, Register value, 3177 Register valueTemp, Register offsetTemp, 3178 Register maskTemp, Register output) { 3179 AtomicExchange(*this, nullptr, type, sync, mem, value, valueTemp, offsetTemp, 3180 maskTemp, output); 3181 } 3182 3183 void MacroAssembler::atomicFetchOpJS(Scalar::Type arrayType, 3184 Synchronization sync, AtomicOp op, 3185 Register value, const Address& mem, 3186 Register valueTemp, Register offsetTemp, 3187 Register maskTemp, Register temp, 3188 AnyRegister output) { 3189 AtomicFetchOpJS(*this, arrayType, sync, op, value, mem, valueTemp, offsetTemp, 3190 maskTemp, temp, output); 3191 } 3192 3193 void MacroAssembler::atomicFetchOpJS(Scalar::Type arrayType, 3194 Synchronization sync, AtomicOp op, 3195 Register value, const BaseIndex& mem, 3196 Register valueTemp, Register offsetTemp, 3197 Register maskTemp, Register temp, 3198 AnyRegister output) { 3199 AtomicFetchOpJS(*this, arrayType, sync, op, value, mem, valueTemp, offsetTemp, 3200 maskTemp, temp, output); 3201 } 3202 3203 void MacroAssembler::atomicFetchOp(Scalar::Type type, Synchronization sync, 3204 AtomicOp op, Register value, 3205 const Address& mem, Register valueTemp, 3206 Register offsetTemp, Register maskTemp, 3207 Register output) { 3208 AtomicFetchOp(*this, nullptr, type, sync, op, mem, value, valueTemp, 3209 offsetTemp, maskTemp, output); 3210 } 3211 3212 void MacroAssembler::atomicFetchOp(Scalar::Type type, Synchronization sync, 3213 AtomicOp op, Register value, 3214 const BaseIndex& mem, Register valueTemp, 3215 Register offsetTemp, Register maskTemp, 3216 Register output) { 3217 AtomicFetchOp(*this, nullptr, type, sync, op, mem, value, valueTemp, 3218 offsetTemp, maskTemp, output); 3219 } 3220 3221 void MacroAssembler::atomicPause() { 3222 // `pause` hint defined in Zihintpause extension. 3223 // It is encoded as `fence w, 0`. 3224 fence(0b0001, 0b0000); 3225 } 3226 3227 void MacroAssembler::branchPtrInNurseryChunk(Condition cond, Register ptr, 3228 Register temp, Label* label) { 3229 MOZ_ASSERT(cond == Assembler::Equal || cond == Assembler::NotEqual); 3230 MOZ_ASSERT(ptr != temp); 3231 MOZ_ASSERT(temp != InvalidReg); 3232 3233 ma_and(temp, ptr, Imm32(int32_t(~gc::ChunkMask))); 3234 branchPtr(InvertCondition(cond), Address(temp, gc::ChunkStoreBufferOffset), 3235 zero, label); 3236 } 3237 void MacroAssembler::branchTestValue(Condition cond, const ValueOperand& lhs, 3238 const Value& rhs, Label* label) { 3239 MOZ_ASSERT(cond == Equal || cond == NotEqual); 3240 MOZ_ASSERT(!rhs.isNaN()); 3241 3242 if (!rhs.isGCThing()) { 3243 ma_b(lhs.valueReg(), ImmWord(rhs.asRawBits()), label, cond); 3244 } else { 3245 UseScratchRegisterScope temps(this); 3246 Register scratch = temps.Acquire(); 3247 MOZ_ASSERT(lhs.valueReg() != scratch); 3248 moveValue(rhs, ValueOperand(scratch)); 3249 ma_b(lhs.valueReg(), scratch, label, cond); 3250 } 3251 } 3252 3253 void MacroAssembler::branchTestNaNValue(Condition cond, const ValueOperand& val, 3254 Register temp, Label* label) { 3255 MOZ_ASSERT(cond == Equal || cond == NotEqual); 3256 UseScratchRegisterScope temps(this); 3257 Register scratch = temps.Acquire(); 3258 MOZ_ASSERT(val.valueReg() != scratch); 3259 3260 // When testing for NaN, we want to ignore the sign bit. 3261 // Clear the top bit by shifting left and then right. 3262 slli(temp, val.valueReg(), 1); 3263 srli(temp, temp, 1); 3264 3265 // Compare against a NaN with sign bit 0. 3266 static_assert(JS::detail::CanonicalizedNaNSignBit == 0); 3267 moveValue(DoubleValue(JS::GenericNaN()), ValueOperand(scratch)); 3268 ma_b(temp, scratch, label, cond); 3269 } 3270 3271 void MacroAssembler::branchValueIsNurseryCell(Condition cond, 3272 const Address& address, 3273 Register temp, Label* label) { 3274 branchValueIsNurseryCellImpl(cond, address, temp, label); 3275 } 3276 3277 void MacroAssembler::branchValueIsNurseryCell(Condition cond, 3278 ValueOperand value, Register temp, 3279 Label* label) { 3280 branchValueIsNurseryCellImpl(cond, value, temp, label); 3281 } 3282 CodeOffset MacroAssembler::call(const Address& addr) { 3283 UseScratchRegisterScope temps(this); 3284 temps.Exclude(GeneralRegisterSet(1 << CallReg.code())); 3285 loadPtr(addr, CallReg); 3286 return call(CallReg); 3287 } 3288 void MacroAssembler::call(ImmPtr target) { 3289 BufferOffset bo = m_buffer.nextOffset(); 3290 addPendingJump(bo, target, RelocationKind::HARDCODED); 3291 ma_call(target); 3292 } 3293 void MacroAssembler::call(ImmWord target) { call(ImmPtr((void*)target.value)); } 3294 3295 void MacroAssembler::call(JitCode* c) { 3296 DEBUG_PRINTF("[ %s\n", __FUNCTION__); 3297 BlockTrampolinePoolScope block_trampoline_pool(this, 8); 3298 UseScratchRegisterScope temps(this); 3299 Register scratch = temps.Acquire(); 3300 BufferOffset bo = m_buffer.nextOffset(); 3301 addPendingJump(bo, ImmPtr(c->raw()), RelocationKind::JITCODE); 3302 ma_liPatchable(scratch, ImmPtr(c->raw())); 3303 callJitNoProfiler(scratch); 3304 DEBUG_PRINTF("]\n"); 3305 } 3306 3307 void MacroAssembler::callWithABIPre(uint32_t* stackAdjust, bool callFromWasm) { 3308 MOZ_ASSERT(inCall_); 3309 uint32_t stackForCall = abiArgs_.stackBytesConsumedSoFar(); 3310 3311 // Reserve place for $ra. 3312 stackForCall += sizeof(intptr_t); 3313 3314 if (dynamicAlignment_) { 3315 stackForCall += ComputeByteAlignment(stackForCall, ABIStackAlignment); 3316 } else { 3317 uint32_t alignmentAtPrologue = callFromWasm ? sizeof(wasm::Frame) : 0; 3318 stackForCall += ComputeByteAlignment( 3319 stackForCall + framePushed() + alignmentAtPrologue, ABIStackAlignment); 3320 } 3321 3322 *stackAdjust = stackForCall; 3323 reserveStack(stackForCall); 3324 3325 // Save $ra because call is going to clobber it. Restore it in 3326 // callWithABIPost. NOTE: This is needed for calls from SharedIC. 3327 // Maybe we can do this differently. 3328 storePtr(ra, Address(StackPointer, stackForCall - sizeof(intptr_t))); 3329 3330 // Position all arguments. 3331 { 3332 enoughMemory_ &= moveResolver_.resolve(); 3333 if (!enoughMemory_) { 3334 return; 3335 } 3336 3337 MoveEmitter emitter(asMasm()); 3338 emitter.emit(moveResolver_); 3339 emitter.finish(); 3340 } 3341 3342 assertStackAlignment(ABIStackAlignment); 3343 } 3344 3345 void MacroAssembler::callWithABIPost(uint32_t stackAdjust, ABIType result) { 3346 // Restore ra value (as stored in callWithABIPre()). 3347 loadPtr(Address(StackPointer, stackAdjust - sizeof(intptr_t)), ra); 3348 3349 if (dynamicAlignment_) { 3350 // Restore sp value from stack (as stored in setupUnalignedABICall()). 3351 loadPtr(Address(StackPointer, stackAdjust), StackPointer); 3352 // Use adjustFrame instead of freeStack because we already restored sp. 3353 adjustFrame(-stackAdjust); 3354 } else { 3355 freeStack(stackAdjust); 3356 } 3357 3358 #ifdef DEBUG 3359 MOZ_ASSERT(inCall_); 3360 inCall_ = false; 3361 #endif 3362 } 3363 3364 void MacroAssembler::callWithABINoProfiler(Register fun, ABIType result) { 3365 // Load the callee in scratch2, no instruction between the movePtr and 3366 // call should clobber it. Note that we can't use fun because it may be 3367 // one of the IntArg registers clobbered before the call. 3368 UseScratchRegisterScope temps(this); 3369 temps.Exclude(GeneralRegisterSet(1 << CallReg.code())); 3370 movePtr(fun, CallReg); 3371 3372 uint32_t stackAdjust; 3373 callWithABIPre(&stackAdjust); 3374 call(CallReg); 3375 callWithABIPost(stackAdjust, result); 3376 } 3377 3378 void MacroAssembler::callWithABINoProfiler(const Address& fun, ABIType result) { 3379 // Load the callee in scratch2, as above. 3380 UseScratchRegisterScope temps(this); 3381 temps.Exclude(GeneralRegisterSet(1 << CallReg.code())); 3382 loadPtr(fun, CallReg); 3383 3384 uint32_t stackAdjust; 3385 callWithABIPre(&stackAdjust); 3386 call(CallReg); 3387 callWithABIPost(stackAdjust, result); 3388 } 3389 3390 void MacroAssembler::ceilDoubleToInt32(FloatRegister src, Register dest, 3391 Label* fail) { 3392 UseScratchRegisterScope temps(this); 3393 Register scratch = temps.Acquire(); 3394 3395 // Round toward positive infinity. 3396 Ceil_l_d(dest, src); 3397 3398 // Sign extend lower 32 bits to test if the result isn't an Int32. 3399 { 3400 move32SignExtendToPtr(dest, scratch); 3401 branchPtr(Assembler::NotEqual, dest, scratch, fail); 3402 } 3403 3404 // We have to check for (-1, -0] when the result is zero. 3405 Label notZero; 3406 ma_b(dest, zero, ¬Zero, Assembler::NotEqual, ShortJump); 3407 { 3408 fmv_x_d(scratch, src); 3409 ma_b(scratch, scratch, fail, Assembler::Signed); 3410 } 3411 bind(¬Zero); 3412 } 3413 3414 void MacroAssembler::ceilFloat32ToInt32(FloatRegister src, Register dest, 3415 Label* fail) { 3416 UseScratchRegisterScope temps(this); 3417 Register scratch = temps.Acquire(); 3418 3419 // Round toward positive infinity. 3420 Ceil_l_s(dest, src); 3421 3422 // Sign extend lower 32 bits to test if the result isn't an Int32. 3423 { 3424 move32SignExtendToPtr(dest, scratch); 3425 branchPtr(Assembler::NotEqual, dest, scratch, fail); 3426 } 3427 3428 // We have to check for (-1, -0] when the result is zero. 3429 Label notZero; 3430 ma_b(dest, zero, ¬Zero, Assembler::NotEqual, ShortJump); 3431 { 3432 fmv_x_w(scratch, src); 3433 ma_b(scratch, scratch, fail, Assembler::Signed); 3434 } 3435 bind(¬Zero); 3436 } 3437 3438 void MacroAssembler::comment(const char* msg) { Assembler::comment(msg); } 3439 3440 template <typename T> 3441 static void CompareExchange64(MacroAssembler& masm, 3442 const wasm::MemoryAccessDesc* access, 3443 Synchronization sync, const T& mem, 3444 Register64 expect, Register64 replace, 3445 Register64 output) { 3446 MOZ_ASSERT(expect != output && replace != output); 3447 UseScratchRegisterScope temps(&masm); 3448 Register scratch = temps.Acquire(); 3449 masm.computeEffectiveAddress(mem, scratch); 3450 3451 Register scratch2 = temps.Acquire(); 3452 3453 Label tryAgain; 3454 Label exit; 3455 3456 masm.memoryBarrierBefore(sync); 3457 3458 masm.bind(&tryAgain); 3459 3460 if (access) { 3461 masm.append(*access, wasm::TrapMachineInsn::Atomic, 3462 FaultingCodeOffset(masm.currentOffset())); 3463 } 3464 3465 masm.lr_d(true, true, output.reg, scratch); 3466 3467 masm.ma_b(output.reg, expect.reg, &exit, Assembler::NotEqual, ShortJump); 3468 masm.movePtr(replace.reg, scratch2); 3469 masm.sc_d(true, true, scratch2, scratch, scratch2); 3470 masm.ma_b(scratch2, Register(scratch2), &tryAgain, Assembler::NonZero, 3471 ShortJump); 3472 3473 masm.memoryBarrierAfter(sync); 3474 3475 masm.bind(&exit); 3476 } 3477 3478 void MacroAssembler::compareExchange64(Synchronization sync, const Address& mem, 3479 Register64 expect, Register64 replace, 3480 Register64 output) { 3481 CompareExchange64(*this, nullptr, sync, mem, expect, replace, output); 3482 } 3483 3484 void MacroAssembler::compareExchange64(Synchronization sync, 3485 const BaseIndex& mem, Register64 expect, 3486 Register64 replace, Register64 output) { 3487 CompareExchange64(*this, nullptr, sync, mem, expect, replace, output); 3488 } 3489 3490 void MacroAssembler::compareExchangeJS(Scalar::Type arrayType, 3491 Synchronization sync, const Address& mem, 3492 Register oldval, Register newval, 3493 Register valueTemp, Register offsetTemp, 3494 Register maskTemp, Register temp, 3495 AnyRegister output) { 3496 CompareExchangeJS(*this, arrayType, sync, mem, oldval, newval, valueTemp, 3497 offsetTemp, maskTemp, temp, output); 3498 } 3499 3500 void MacroAssembler::compareExchangeJS(Scalar::Type arrayType, 3501 Synchronization sync, 3502 const BaseIndex& mem, Register oldval, 3503 Register newval, Register valueTemp, 3504 Register offsetTemp, Register maskTemp, 3505 Register temp, AnyRegister output) { 3506 CompareExchangeJS(*this, arrayType, sync, mem, oldval, newval, valueTemp, 3507 offsetTemp, maskTemp, temp, output); 3508 } 3509 3510 void MacroAssembler::convertInt64ToDouble(Register64 src, FloatRegister dest) { 3511 fcvt_d_l(dest, src.scratchReg()); 3512 } 3513 void MacroAssembler::convertInt64ToFloat32(Register64 src, FloatRegister dest) { 3514 fcvt_s_l(dest, src.scratchReg()); 3515 } 3516 void MacroAssembler::convertIntPtrToDouble(Register src, FloatRegister dest) { 3517 fcvt_d_l(dest, src); 3518 } 3519 void MacroAssembler::convertUInt64ToDouble(Register64 src, FloatRegister dest, 3520 Register tmp) { 3521 fcvt_d_lu(dest, src.scratchReg()); 3522 } 3523 void MacroAssembler::convertUInt64ToFloat32(Register64 src, FloatRegister dest, 3524 Register tmp) { 3525 fcvt_s_lu(dest, src.scratchReg()); 3526 } 3527 void MacroAssembler::copySignDouble(FloatRegister lhs, FloatRegister rhs, 3528 FloatRegister output) { 3529 fsgnj_d(output, lhs, rhs); 3530 } 3531 void MacroAssembler::copySignFloat32(FloatRegister lhs, FloatRegister rhs, 3532 FloatRegister output) { 3533 fsgnj_s(output, lhs, rhs); 3534 } 3535 void MacroAssembler::enterFakeExitFrameForWasm(Register cxreg, Register scratch, 3536 ExitFrameType type) { 3537 enterFakeExitFrame(cxreg, scratch, type); 3538 } 3539 CodeOffset MacroAssembler::sub32FromMemAndBranchIfNegativeWithPatch( 3540 Address address, Label* label) { 3541 UseScratchRegisterScope temps(this); 3542 Register scratch = temps.Acquire(); 3543 MOZ_ASSERT(scratch != address.base); 3544 ma_load(scratch, address); 3545 addiw(scratch, scratch, 128); 3546 CodeOffset patchPoint = CodeOffset(currentOffset()); 3547 ma_store(scratch, address); 3548 ma_b(scratch, scratch, label, Assembler::Signed); 3549 return patchPoint; 3550 } 3551 void MacroAssembler::patchSub32FromMemAndBranchIfNegative(CodeOffset offset, 3552 Imm32 imm) { 3553 int32_t val = imm.value; 3554 MOZ_RELEASE_ASSERT(val >= 1 && val <= 127); 3555 auto* inst = m_buffer.getInst(BufferOffset(offset.offset() - 4)); 3556 inst->InstructionOpcodeType(); 3557 MOZ_ASSERT(IsAddiw(inst->InstructionBits())); 3558 /* 3559 * | imm[11:0] | rs1 | 000 | rd | 0011011 | 3560 */ 3561 inst->SetInstructionBits(((uint32_t)inst->InstructionBits() & ~kImm12Mask) | 3562 (((uint32_t)(-val) & 0xfff) << kImm12Shift)); 3563 } 3564 void MacroAssembler::flexibleDivMod32(Register lhs, Register rhs, 3565 Register divOutput, Register remOutput, 3566 bool isUnsigned, const LiveRegisterSet&) { 3567 MOZ_ASSERT(lhs != divOutput && lhs != remOutput, "lhs is preserved"); 3568 MOZ_ASSERT(rhs != divOutput && rhs != remOutput, "rhs is preserved"); 3569 3570 // The recommended code sequence to obtain both the quotient and remainder 3571 // is div[u] followed by mod[u]. 3572 if (isUnsigned) { 3573 ma_divu32(divOutput, lhs, rhs); 3574 ma_modu32(remOutput, lhs, rhs); 3575 } else { 3576 ma_div32(divOutput, lhs, rhs); 3577 ma_mod32(remOutput, lhs, rhs); 3578 } 3579 } 3580 void MacroAssembler::flexibleQuotient32(Register lhs, Register rhs, 3581 Register dest, bool isUnsigned, 3582 const LiveRegisterSet&) { 3583 quotient32(lhs, rhs, dest, isUnsigned); 3584 } 3585 3586 void MacroAssembler::flexibleQuotientPtr(Register lhs, Register rhs, 3587 Register dest, bool isUnsigned, 3588 const LiveRegisterSet&) { 3589 quotient64(lhs, rhs, dest, isUnsigned); 3590 } 3591 3592 void MacroAssembler::flexibleRemainder32(Register lhs, Register rhs, 3593 Register dest, bool isUnsigned, 3594 const LiveRegisterSet&) { 3595 remainder32(lhs, rhs, dest, isUnsigned); 3596 } 3597 3598 void MacroAssembler::flexibleRemainderPtr(Register lhs, Register rhs, 3599 Register dest, bool isUnsigned, 3600 const LiveRegisterSet&) { 3601 remainder64(lhs, rhs, dest, isUnsigned); 3602 } 3603 3604 void MacroAssembler::floorDoubleToInt32(FloatRegister src, Register dest, 3605 Label* fail) { 3606 UseScratchRegisterScope temps(this); 3607 Register scratch = temps.Acquire(); 3608 3609 // Round toward negative infinity. 3610 Floor_l_d(dest, src); 3611 3612 // Sign extend lower 32 bits to test if the result isn't an Int32. 3613 { 3614 move32SignExtendToPtr(dest, scratch); 3615 branchPtr(Assembler::NotEqual, dest, scratch, fail); 3616 } 3617 3618 // Fail if the input is negative zero. 3619 { 3620 fclass_d(scratch, src); 3621 ma_b(scratch, Imm32(FClassFlag::kNegativeZero), fail, Equal); 3622 } 3623 } 3624 3625 void MacroAssembler::floorFloat32ToInt32(FloatRegister src, Register dest, 3626 Label* fail) { 3627 UseScratchRegisterScope temps(this); 3628 Register scratch = temps.Acquire(); 3629 3630 // Round toward negative infinity. 3631 Floor_l_s(dest, src); 3632 3633 // Sign extend lower 32 bits to test if the result isn't an Int32. 3634 { 3635 move32SignExtendToPtr(dest, scratch); 3636 branchPtr(Assembler::NotEqual, dest, scratch, fail); 3637 } 3638 3639 // Fail if the input is negative zero. 3640 { 3641 fclass_s(scratch, src); 3642 ma_b(scratch, Imm32(FClassFlag::kNegativeZero), fail, Equal); 3643 } 3644 } 3645 3646 void MacroAssembler::flush() {} 3647 void MacroAssembler::loadStoreBuffer(Register ptr, Register buffer) { 3648 ma_and(buffer, ptr, Imm32(int32_t(~gc::ChunkMask))); 3649 loadPtr(Address(buffer, gc::ChunkStoreBufferOffset), buffer); 3650 } 3651 3652 void MacroAssembler::moveValue(const ValueOperand& src, 3653 const ValueOperand& dest) { 3654 if (src == dest) { 3655 return; 3656 } 3657 movePtr(src.valueReg(), dest.valueReg()); 3658 } 3659 3660 void MacroAssembler::moveValue(const Value& src, const ValueOperand& dest) { 3661 if (!src.isGCThing()) { 3662 ma_li(dest.valueReg(), ImmWord(src.asRawBits())); 3663 return; 3664 } 3665 3666 writeDataRelocation(src); 3667 movWithPatch(ImmWord(src.asRawBits()), dest.valueReg()); 3668 } 3669 3670 void MacroAssembler::nearbyIntDouble(RoundingMode mode, FloatRegister src, 3671 FloatRegister dest) { 3672 MOZ_ASSERT(HasRoundInstruction(mode)); 3673 3674 ScratchDoubleScope2 fscratch(*this); 3675 3676 switch (mode) { 3677 case RoundingMode::Down: 3678 Floor_d_d(dest, src, fscratch); 3679 break; 3680 case RoundingMode::Up: 3681 Ceil_d_d(dest, src, fscratch); 3682 break; 3683 case RoundingMode::NearestTiesToEven: 3684 Round_d_d(dest, src, fscratch); 3685 break; 3686 case RoundingMode::TowardsZero: 3687 Trunc_d_d(dest, src, fscratch); 3688 break; 3689 } 3690 } 3691 3692 void MacroAssembler::nearbyIntFloat32(RoundingMode mode, FloatRegister src, 3693 FloatRegister dest) { 3694 MOZ_ASSERT(HasRoundInstruction(mode)); 3695 3696 ScratchFloat32Scope2 fscratch(*this); 3697 3698 switch (mode) { 3699 case RoundingMode::Down: 3700 Floor_s_s(dest, src, fscratch); 3701 break; 3702 case RoundingMode::Up: 3703 Ceil_s_s(dest, src, fscratch); 3704 break; 3705 case RoundingMode::NearestTiesToEven: 3706 Round_s_s(dest, src, fscratch); 3707 break; 3708 case RoundingMode::TowardsZero: 3709 Trunc_s_s(dest, src, fscratch); 3710 break; 3711 } 3712 } 3713 3714 void MacroAssembler::oolWasmTruncateCheckF32ToI32( 3715 FloatRegister input, Register output, TruncFlags flags, 3716 const wasm::TrapSiteDesc& trapSiteDesc, Label* rejoin) { 3717 MOZ_ASSERT(!(flags & TRUNC_SATURATING)); 3718 3719 Label notNaN; 3720 BranchFloat32(Assembler::DoubleOrdered, input, input, ¬NaN, ShortJump); 3721 wasmTrap(wasm::Trap::InvalidConversionToInteger, trapSiteDesc); 3722 bind(¬NaN); 3723 3724 wasmTrap(wasm::Trap::IntegerOverflow, trapSiteDesc); 3725 } 3726 3727 void MacroAssembler::oolWasmTruncateCheckF64ToI32( 3728 FloatRegister input, Register output, TruncFlags flags, 3729 const wasm::TrapSiteDesc& trapSiteDesc, Label* rejoin) { 3730 MOZ_ASSERT(!(flags & TRUNC_SATURATING)); 3731 3732 Label notNaN; 3733 BranchFloat64(Assembler::DoubleOrdered, input, input, ¬NaN, ShortJump); 3734 wasmTrap(wasm::Trap::InvalidConversionToInteger, trapSiteDesc); 3735 bind(¬NaN); 3736 3737 wasmTrap(wasm::Trap::IntegerOverflow, trapSiteDesc); 3738 } 3739 3740 void MacroAssembler::oolWasmTruncateCheckF32ToI64( 3741 FloatRegister input, Register64 output, TruncFlags flags, 3742 const wasm::TrapSiteDesc& trapSiteDesc, Label* rejoin) { 3743 MOZ_ASSERT(!(flags & TRUNC_SATURATING)); 3744 3745 Label notNaN; 3746 BranchFloat32(Assembler::DoubleOrdered, input, input, ¬NaN, ShortJump); 3747 wasmTrap(wasm::Trap::InvalidConversionToInteger, trapSiteDesc); 3748 bind(¬NaN); 3749 3750 wasmTrap(wasm::Trap::IntegerOverflow, trapSiteDesc); 3751 } 3752 3753 void MacroAssembler::oolWasmTruncateCheckF64ToI64( 3754 FloatRegister input, Register64 output, TruncFlags flags, 3755 const wasm::TrapSiteDesc& trapSiteDesc, Label* rejoin) { 3756 MOZ_ASSERT(!(flags & TRUNC_SATURATING)); 3757 3758 Label notNaN; 3759 BranchFloat64(Assembler::DoubleOrdered, input, input, ¬NaN, ShortJump); 3760 wasmTrap(wasm::Trap::InvalidConversionToInteger, trapSiteDesc); 3761 bind(¬NaN); 3762 3763 wasmTrap(wasm::Trap::IntegerOverflow, trapSiteDesc); 3764 } 3765 3766 void MacroAssembler::patchCallToNop(uint8_t* call) { 3767 uint32_t* p = reinterpret_cast<uint32_t*>(call) - 7; 3768 *reinterpret_cast<Instr*>(p) = kNopByte; 3769 *reinterpret_cast<Instr*>(p + 1) = kNopByte; 3770 *reinterpret_cast<Instr*>(p + 2) = kNopByte; 3771 *reinterpret_cast<Instr*>(p + 3) = kNopByte; 3772 *reinterpret_cast<Instr*>(p + 4) = kNopByte; 3773 *reinterpret_cast<Instr*>(p + 5) = kNopByte; 3774 *reinterpret_cast<Instr*>(p + 6) = kNopByte; 3775 } 3776 3777 CodeOffset MacroAssembler::callWithPatch() { 3778 BlockTrampolinePoolScope block_trampoline_pool(this, 2); 3779 DEBUG_PRINTF("\tcallWithPatch\n"); 3780 UseScratchRegisterScope temps(this); 3781 Register scratch = temps.Acquire(); 3782 int32_t imm32 = 1 * sizeof(uint32_t); 3783 int32_t Hi20 = ((imm32 + 0x800) >> 12); 3784 int32_t Lo12 = imm32 << 20 >> 20; 3785 auipc(scratch, Hi20); // Read PC + Hi20 into scratch. 3786 jalr(scratch, Lo12); // jump PC + Hi20 + Lo12 3787 DEBUG_PRINTF("\tret %d\n", currentOffset()); 3788 return CodeOffset(currentOffset()); 3789 } 3790 3791 void MacroAssembler::patchCall(uint32_t callerOffset, uint32_t calleeOffset) { 3792 DEBUG_PRINTF("\tpatchCall\n"); 3793 BufferOffset call(callerOffset - 2 * sizeof(uint32_t)); 3794 DEBUG_PRINTF("\tcallerOffset %d\n", callerOffset); 3795 int32_t offset = BufferOffset(calleeOffset).getOffset() - call.getOffset(); 3796 if (is_int32(offset)) { 3797 Instruction* auipc_ = (Instruction*)editSrc(call); 3798 Instruction* jalr_ = (Instruction*)editSrc( 3799 BufferOffset(callerOffset - 1 * sizeof(uint32_t))); 3800 DEBUG_PRINTF("\t%p %zu\n\t", auipc_, callerOffset - 2 * sizeof(uint32_t)); 3801 disassembleInstr(auipc_->InstructionBits()); 3802 DEBUG_PRINTF("\t%p %zu\n\t", jalr_, callerOffset - 1 * sizeof(uint32_t)); 3803 disassembleInstr(jalr_->InstructionBits()); 3804 DEBUG_PRINTF("\t\n"); 3805 MOZ_ASSERT(IsJalr(jalr_->InstructionBits()) && 3806 IsAuipc(auipc_->InstructionBits())); 3807 MOZ_ASSERT(auipc_->RdValue() == jalr_->Rs1Value()); 3808 int32_t Hi20 = (((int32_t)offset + 0x800) >> 12); 3809 int32_t Lo12 = (int32_t)offset << 20 >> 20; 3810 instr_at_put(call, SetAuipcOffset(Hi20, auipc_->InstructionBits())); 3811 instr_at_put(BufferOffset(callerOffset - 1 * sizeof(uint32_t)), 3812 SetJalrOffset(Lo12, jalr_->InstructionBits())); 3813 } else { 3814 MOZ_CRASH(); 3815 } 3816 } 3817 3818 void MacroAssembler::patchFarJump(CodeOffset farJump, uint32_t targetOffset) { 3819 uint32_t* u32 = reinterpret_cast<uint32_t*>( 3820 editSrc(BufferOffset(farJump.offset() + 4 * kInstrSize))); 3821 MOZ_ASSERT(*u32 == UINT32_MAX); 3822 *u32 = targetOffset - farJump.offset(); 3823 } 3824 3825 void MacroAssembler::patchFarJump(uint8_t* farJump, uint8_t* target) { 3826 uint32_t* u32 = reinterpret_cast<uint32_t*>(farJump + 4 * kInstrSize); 3827 MOZ_ASSERT(*u32 == UINT32_MAX); 3828 *u32 = (int64_t)target - (int64_t)farJump; 3829 } 3830 3831 void MacroAssembler::patchNearAddressMove(CodeLocationLabel loc, 3832 CodeLocationLabel target) { 3833 PatchDataWithValueCheck(loc, ImmPtr(target.raw()), ImmPtr(nullptr)); 3834 } 3835 3836 void MacroAssembler::patchNopToCall(uint8_t* call, uint8_t* target) { 3837 uint32_t* p = reinterpret_cast<uint32_t*>(call) - 7; 3838 Assembler::WriteLoad64Instructions((Instruction*)p, SavedScratchRegister, 3839 (uint64_t)target); 3840 DEBUG_PRINTF("\tpatchNopToCall %" PRIu64 " %" PRIu64 "\n", (uint64_t)target, 3841 ExtractLoad64Value((Instruction*)p)); 3842 MOZ_ASSERT(ExtractLoad64Value((Instruction*)p) == (uint64_t)target); 3843 Instr jalr_ = JALR | (ra.code() << kRdShift) | (0x0 << kFunct3Shift) | 3844 (SavedScratchRegister.code() << kRs1Shift) | 3845 (0x0 << kImm12Shift); 3846 *reinterpret_cast<Instr*>(p + 6) = jalr_; 3847 } 3848 void MacroAssembler::Pop(Register reg) { 3849 pop(reg); 3850 adjustFrame(-int32_t(sizeof(intptr_t))); 3851 } 3852 3853 void MacroAssembler::Pop(FloatRegister f) { 3854 pop(f); 3855 // See MacroAssemblerRiscv64::ma_pop(FloatRegister) for why we use 3856 // sizeof(double). 3857 adjustFrame(-int32_t(sizeof(double))); 3858 } 3859 3860 void MacroAssembler::Pop(const ValueOperand& val) { 3861 popValue(val); 3862 adjustFrame(-int32_t(sizeof(Value))); 3863 } 3864 3865 void MacroAssembler::PopRegsInMaskIgnore(LiveRegisterSet set, 3866 LiveRegisterSet ignore) { 3867 int32_t diff = 3868 set.gprs().size() * sizeof(intptr_t) + set.fpus().getPushSizeInBytes(); 3869 const int32_t reserved = diff; 3870 3871 for (GeneralRegisterBackwardIterator iter(set.gprs()); iter.more(); ++iter) { 3872 diff -= sizeof(intptr_t); 3873 if (!ignore.has(*iter)) { 3874 loadPtr(Address(StackPointer, diff), *iter); 3875 } 3876 } 3877 3878 #ifdef ENABLE_WASM_SIMD 3879 # error "Needs more careful logic if SIMD is enabled" 3880 #endif 3881 3882 for (FloatRegisterBackwardIterator iter(set.fpus().reduceSetForPush()); 3883 iter.more(); ++iter) { 3884 diff -= sizeof(double); 3885 if (!ignore.has(*iter)) { 3886 loadDouble(Address(StackPointer, diff), *iter); 3887 } 3888 } 3889 MOZ_ASSERT(diff == 0); 3890 freeStack(reserved); 3891 } 3892 3893 CodeOffset MacroAssembler::move32WithPatch(Register dest) { 3894 CodeOffset offs = CodeOffset(currentOffset()); 3895 ma_liPatchable(dest, Imm32(0)); 3896 return offs; 3897 } 3898 3899 void MacroAssembler::patchMove32(CodeOffset offset, Imm32 n) { 3900 patchSub32FromStackPtr(offset, n); 3901 } 3902 3903 void MacroAssembler::pushReturnAddress() { push(ra); } 3904 3905 void MacroAssembler::popReturnAddress() { pop(ra); } 3906 void MacroAssembler::PopStackPtr() { 3907 loadPtr(Address(StackPointer, 0), StackPointer); 3908 adjustFrame(-int32_t(sizeof(intptr_t))); 3909 } 3910 void MacroAssembler::freeStackTo(uint32_t framePushed) { 3911 MOZ_ASSERT(framePushed <= framePushed_); 3912 ma_sub64(StackPointer, FramePointer, Imm32(framePushed)); 3913 framePushed_ = framePushed; 3914 } 3915 void MacroAssembler::PushBoxed(FloatRegister reg) { 3916 subFromStackPtr(Imm32(sizeof(double))); 3917 boxDouble(reg, Address(getStackPointer(), 0)); 3918 adjustFrame(sizeof(double)); 3919 } 3920 3921 void MacroAssembler::Push(Register reg) { 3922 push(reg); 3923 adjustFrame(int32_t(sizeof(intptr_t))); 3924 } 3925 3926 void MacroAssembler::Push(const Imm32 imm) { 3927 push(imm); 3928 adjustFrame(int32_t(sizeof(intptr_t))); 3929 } 3930 3931 void MacroAssembler::Push(const ImmWord imm) { 3932 push(imm); 3933 adjustFrame(int32_t(sizeof(intptr_t))); 3934 } 3935 3936 void MacroAssembler::Push(const ImmPtr imm) { 3937 Push(ImmWord(uintptr_t(imm.value))); 3938 } 3939 3940 void MacroAssembler::Push(const ImmGCPtr ptr) { 3941 push(ptr); 3942 adjustFrame(int32_t(sizeof(intptr_t))); 3943 } 3944 3945 void MacroAssembler::Push(FloatRegister f) { 3946 push(f); 3947 // See MacroAssemblerRiscv64::ma_push(FloatRegister) for why we use 3948 // sizeof(double). 3949 adjustFrame(int32_t(sizeof(double))); 3950 } 3951 3952 void MacroAssembler::PushRegsInMask(LiveRegisterSet set) { 3953 int32_t diff = 3954 set.gprs().size() * sizeof(intptr_t) + set.fpus().getPushSizeInBytes(); 3955 const int32_t reserved = diff; 3956 3957 reserveStack(reserved); 3958 for (GeneralRegisterBackwardIterator iter(set.gprs()); iter.more(); ++iter) { 3959 diff -= sizeof(intptr_t); 3960 storePtr(*iter, Address(StackPointer, diff)); 3961 } 3962 3963 #ifdef ENABLE_WASM_SIMD 3964 # error "Needs more careful logic if SIMD is enabled" 3965 #endif 3966 3967 for (FloatRegisterBackwardIterator iter(set.fpus().reduceSetForPush()); 3968 iter.more(); ++iter) { 3969 diff -= sizeof(double); 3970 storeDouble(*iter, Address(StackPointer, diff)); 3971 } 3972 MOZ_ASSERT(diff == 0); 3973 } 3974 3975 void MacroAssembler::roundFloat32ToInt32(FloatRegister src, Register dest, 3976 FloatRegister temp, Label* fail) { 3977 JitSpew(JitSpew_Codegen, "[ %s", __FUNCTION__); 3978 Label negative, done; 3979 3980 // Branch to a slow path if input < 0.0 due to complicated rounding rules. 3981 { 3982 loadConstantFloat32(0.0f, temp); 3983 BranchFloat32(Assembler::DoubleLessThan, src, temp, &negative, ShortJump); 3984 } 3985 3986 // Fail if the input is negative zero. 3987 { 3988 UseScratchRegisterScope temps(this); 3989 Register scratch = temps.Acquire(); 3990 3991 fclass_s(scratch, src); 3992 ma_b(scratch, Imm32(FClassFlag::kNegativeZero), fail, Assembler::Equal); 3993 } 3994 3995 // Handle the simple case of a positive input and NaN. 3996 // Rounding proceeds with consideration of the fractional part of the input: 3997 // 1. If > 0.5, round to integer with higher absolute value (so, up). 3998 // 2. If < 0.5, round to integer with lower absolute value (so, down). 3999 // 3. If = 0.5, round to +Infinity (so, up). 4000 { 4001 // Round, ties away from zero. 4002 RoundMaxMag_l_s(dest, src); 4003 ma_branch(&done); 4004 } 4005 4006 // Handle the complicated case of a negative input. 4007 // Rounding proceeds with consideration of the fractional part of the input: 4008 // 1. If > 0.5, round to integer with higher absolute value (so, down). 4009 // 2. If < 0.5, round to integer with lower absolute value (so, up). 4010 // 3. If = 0.5, round to +Infinity (so, up). 4011 bind(&negative); 4012 { 4013 // Inputs in [-0.5, 0) are rounded to -0. Fail. 4014 loadConstantFloat32(-0.5f, temp); 4015 branchFloat(Assembler::DoubleGreaterThanOrEqual, src, temp, fail); 4016 4017 // Other negative inputs need the biggest float less than 0.5 added. 4018 loadConstantFloat32(GetBiggestNumberLessThan(0.5f), temp); 4019 fadd_s(temp, src, temp); 4020 4021 // Round toward negative infinity. 4022 Floor_l_s(dest, temp); 4023 } 4024 4025 // Sign extend lower 32 bits to test if the result isn't an Int32. 4026 bind(&done); 4027 { 4028 UseScratchRegisterScope temps(this); 4029 Register scratch = temps.Acquire(); 4030 4031 move32SignExtendToPtr(dest, scratch); 4032 branchPtr(Assembler::NotEqual, dest, scratch, fail); 4033 } 4034 JitSpew(JitSpew_Codegen, "]"); 4035 } 4036 4037 void MacroAssembler::roundDoubleToInt32(FloatRegister src, Register dest, 4038 FloatRegister temp, Label* fail) { 4039 JitSpew(JitSpew_Codegen, "[ %s", __FUNCTION__); 4040 Label negative, done; 4041 4042 // Branch to a slow path if input < 0.0 due to complicated rounding rules. 4043 { 4044 loadConstantDouble(0.0, temp); 4045 BranchFloat64(Assembler::DoubleLessThan, src, temp, &negative, ShortJump); 4046 } 4047 4048 // Fail if the input is negative zero. 4049 { 4050 UseScratchRegisterScope temps(this); 4051 Register scratch = temps.Acquire(); 4052 4053 fclass_d(scratch, src); 4054 ma_b(scratch, Imm32(FClassFlag::kNegativeZero), fail, Equal); 4055 } 4056 4057 // Handle the simple case of a positive input and NaN. 4058 // Rounding proceeds with consideration of the fractional part of the input: 4059 // 1. If > 0.5, round to integer with higher absolute value (so, up). 4060 // 2. If < 0.5, round to integer with lower absolute value (so, down). 4061 // 3. If = 0.5, round to +Infinity (so, up). 4062 { 4063 // Round, ties away from zero. 4064 RoundMaxMag_l_d(dest, src); 4065 ma_branch(&done); 4066 } 4067 4068 // Handle the complicated case of a negative input. 4069 // Rounding proceeds with consideration of the fractional part of the input: 4070 // 1. If > 0.5, round to integer with higher absolute value (so, down). 4071 // 2. If < 0.5, round to integer with lower absolute value (so, up). 4072 // 3. If = 0.5, round to +Infinity (so, up). 4073 bind(&negative); 4074 { 4075 // Inputs in [-0.5, 0) are rounded to -0. Fail. 4076 loadConstantDouble(-0.5, temp); 4077 branchDouble(Assembler::DoubleGreaterThanOrEqual, src, temp, fail); 4078 4079 // Other negative inputs need the biggest double less than 0.5 added. 4080 loadConstantDouble(GetBiggestNumberLessThan(0.5), temp); 4081 fadd_d(temp, src, temp); 4082 4083 // Round toward negative infinity. 4084 Floor_l_d(dest, temp); 4085 } 4086 4087 // Sign extend lower 32 bits to test if the result isn't an Int32. 4088 bind(&done); 4089 { 4090 UseScratchRegisterScope temps(this); 4091 Register scratch = temps.Acquire(); 4092 4093 move32SignExtendToPtr(dest, scratch); 4094 branchPtr(Assembler::NotEqual, dest, scratch, fail); 4095 } 4096 JitSpew(JitSpew_Codegen, "]"); 4097 } 4098 4099 void MacroAssembler::setupUnalignedABICall(Register scratch) { 4100 MOZ_ASSERT(!IsCompilingWasm(), "wasm should only use aligned ABI calls"); 4101 setupNativeABICall(); 4102 dynamicAlignment_ = true; 4103 4104 or_(scratch, StackPointer, zero); 4105 4106 // Force sp to be aligned 4107 asMasm().subPtr(Imm32(sizeof(uintptr_t)), StackPointer); 4108 ma_and(StackPointer, StackPointer, Imm32(~(ABIStackAlignment - 1))); 4109 storePtr(scratch, Address(StackPointer, 0)); 4110 } 4111 void MacroAssembler::shiftIndex32AndAdd(Register indexTemp32, int shift, 4112 Register pointer) { 4113 if (IsShiftInScaleRange(shift)) { 4114 computeEffectiveAddress( 4115 BaseIndex(pointer, indexTemp32, ShiftToScale(shift)), pointer); 4116 return; 4117 } 4118 lshift32(Imm32(shift), indexTemp32); 4119 addPtr(indexTemp32, pointer); 4120 } 4121 void MacroAssembler::speculationBarrier() { MOZ_CRASH(); } 4122 void MacroAssembler::storeRegsInMask(LiveRegisterSet set, Address dest, 4123 Register) { 4124 FloatRegisterSet fpuSet(set.fpus().reduceSetForPush()); 4125 mozilla::DebugOnly<unsigned> numFpu = fpuSet.size(); 4126 int32_t diffF = fpuSet.getPushSizeInBytes(); 4127 mozilla::DebugOnly<int32_t> diffG = set.gprs().size() * sizeof(intptr_t); 4128 4129 MOZ_ASSERT(dest.offset >= diffG + diffF); 4130 4131 for (GeneralRegisterBackwardIterator iter(set.gprs()); iter.more(); ++iter) { 4132 diffG -= sizeof(intptr_t); 4133 dest.offset -= sizeof(intptr_t); 4134 storePtr(*iter, dest); 4135 } 4136 MOZ_ASSERT(diffG == 0); 4137 4138 #ifdef ENABLE_WASM_SIMD 4139 # error "Needs more careful logic if SIMD is enabled" 4140 #endif 4141 4142 for (FloatRegisterBackwardIterator iter(fpuSet); iter.more(); ++iter) { 4143 FloatRegister reg = *iter; 4144 diffF -= reg.size(); 4145 numFpu -= 1; 4146 dest.offset -= reg.size(); 4147 if (reg.isDouble()) { 4148 storeDouble(reg, dest); 4149 } else if (reg.isSingle()) { 4150 storeFloat32(reg, dest); 4151 } else { 4152 MOZ_CRASH("Unknown register type."); 4153 } 4154 } 4155 MOZ_ASSERT(numFpu == 0); 4156 4157 diffF -= diffF % sizeof(uintptr_t); 4158 MOZ_ASSERT(diffF == 0); 4159 } 4160 void MacroAssembler::truncDoubleToInt32(FloatRegister src, Register dest, 4161 Label* fail) { 4162 UseScratchRegisterScope temps(*this); 4163 Register scratch = temps.Acquire(); 4164 4165 // Round toward zero. 4166 Trunc_l_d(dest, src); 4167 4168 // Sign extend lower 32 bits to test if the result isn't an Int32. 4169 { 4170 move32SignExtendToPtr(dest, scratch); 4171 branchPtr(Assembler::NotEqual, dest, scratch, fail); 4172 } 4173 4174 // We have to check for (-1, -0] when the result is zero. 4175 Label notZero; 4176 ma_b(dest, zero, ¬Zero, Assembler::NotEqual, ShortJump); 4177 { 4178 fmv_x_d(scratch, src); 4179 ma_b(scratch, scratch, fail, Assembler::Signed); 4180 } 4181 bind(¬Zero); 4182 } 4183 void MacroAssembler::truncFloat32ToInt32(FloatRegister src, Register dest, 4184 Label* fail) { 4185 UseScratchRegisterScope temps(this); 4186 Register scratch = temps.Acquire(); 4187 4188 // Round toward zero. 4189 Trunc_l_s(dest, src); 4190 4191 // Sign extend lower 32 bits to test if the result isn't an Int32. 4192 { 4193 move32SignExtendToPtr(dest, scratch); 4194 branchPtr(Assembler::NotEqual, dest, scratch, fail); 4195 } 4196 4197 // We have to check for (-1, -0] when the result is zero. 4198 Label notZero; 4199 ma_b(dest, zero, ¬Zero, Assembler::NotEqual, ShortJump); 4200 { 4201 fmv_x_w(scratch, src); 4202 ma_b(scratch, scratch, fail, Assembler::Signed); 4203 } 4204 bind(¬Zero); 4205 } 4206 4207 void MacroAssembler::wasmAtomicEffectOp(const wasm::MemoryAccessDesc& access, 4208 AtomicOp op, Register value, 4209 const Address& mem, Register valueTemp, 4210 Register offsetTemp, 4211 Register maskTemp) { 4212 AtomicEffectOp(*this, &access, access.type(), access.sync(), op, mem, value, 4213 valueTemp, offsetTemp, maskTemp); 4214 } 4215 4216 void MacroAssembler::wasmAtomicEffectOp(const wasm::MemoryAccessDesc& access, 4217 AtomicOp op, Register value, 4218 const BaseIndex& mem, 4219 Register valueTemp, Register offsetTemp, 4220 Register maskTemp) { 4221 AtomicEffectOp(*this, &access, access.type(), access.sync(), op, mem, value, 4222 valueTemp, offsetTemp, maskTemp); 4223 } 4224 template <typename T> 4225 static void WasmAtomicExchange64(MacroAssembler& masm, 4226 const wasm::MemoryAccessDesc& access, 4227 const T& mem, Register64 value, 4228 Register64 output) { 4229 AtomicExchange64(masm, &access, access.sync(), mem, value, output); 4230 } 4231 4232 void MacroAssembler::wasmAtomicExchange64(const wasm::MemoryAccessDesc& access, 4233 const Address& mem, Register64 src, 4234 Register64 output) { 4235 WasmAtomicExchange64(*this, access, mem, src, output); 4236 } 4237 4238 void MacroAssembler::wasmAtomicExchange64(const wasm::MemoryAccessDesc& access, 4239 const BaseIndex& mem, Register64 src, 4240 Register64 output) { 4241 WasmAtomicExchange64(*this, access, mem, src, output); 4242 } 4243 void MacroAssembler::wasmAtomicExchange(const wasm::MemoryAccessDesc& access, 4244 const Address& mem, Register value, 4245 Register valueTemp, Register offsetTemp, 4246 Register maskTemp, Register output) { 4247 AtomicExchange(*this, &access, access.type(), access.sync(), mem, value, 4248 valueTemp, offsetTemp, maskTemp, output); 4249 } 4250 4251 void MacroAssembler::wasmAtomicExchange(const wasm::MemoryAccessDesc& access, 4252 const BaseIndex& mem, Register value, 4253 Register valueTemp, Register offsetTemp, 4254 Register maskTemp, Register output) { 4255 AtomicExchange(*this, &access, access.type(), access.sync(), mem, value, 4256 valueTemp, offsetTemp, maskTemp, output); 4257 } 4258 void MacroAssembler::wasmAtomicFetchOp64(const wasm::MemoryAccessDesc& access, 4259 AtomicOp op, Register64 value, 4260 const Address& mem, Register64 temp, 4261 Register64 output) { 4262 AtomicFetchOp64(*this, &access, access.sync(), op, value, mem, temp, output); 4263 } 4264 void MacroAssembler::wasmAtomicFetchOp64(const wasm::MemoryAccessDesc& access, 4265 AtomicOp op, Register64 value, 4266 const BaseIndex& mem, Register64 temp, 4267 Register64 output) { 4268 AtomicFetchOp64(*this, &access, access.sync(), op, value, mem, temp, output); 4269 } 4270 4271 void MacroAssembler::atomicFetchOp64(Synchronization sync, AtomicOp op, 4272 Register64 value, const Address& mem, 4273 Register64 temp, Register64 output) { 4274 AtomicFetchOp64(*this, nullptr, sync, op, value, mem, temp, output); 4275 } 4276 4277 void MacroAssembler::atomicFetchOp64(Synchronization sync, AtomicOp op, 4278 Register64 value, const BaseIndex& mem, 4279 Register64 temp, Register64 output) { 4280 AtomicFetchOp64(*this, nullptr, sync, op, value, mem, temp, output); 4281 } 4282 4283 void MacroAssembler::atomicEffectOp64(Synchronization sync, AtomicOp op, 4284 Register64 value, const Address& mem, 4285 Register64 temp) { 4286 AtomicFetchOp64(*this, nullptr, sync, op, value, mem, temp, temp); 4287 } 4288 4289 void MacroAssembler::atomicEffectOp64(Synchronization sync, AtomicOp op, 4290 Register64 value, const BaseIndex& mem, 4291 Register64 temp) { 4292 AtomicFetchOp64(*this, nullptr, sync, op, value, mem, temp, temp); 4293 } 4294 void MacroAssembler::wasmAtomicFetchOp(const wasm::MemoryAccessDesc& access, 4295 AtomicOp op, Register value, 4296 const Address& mem, Register valueTemp, 4297 Register offsetTemp, Register maskTemp, 4298 Register output) { 4299 AtomicFetchOp(*this, &access, access.type(), access.sync(), op, mem, value, 4300 valueTemp, offsetTemp, maskTemp, output); 4301 } 4302 4303 void MacroAssembler::wasmAtomicFetchOp(const wasm::MemoryAccessDesc& access, 4304 AtomicOp op, Register value, 4305 const BaseIndex& mem, Register valueTemp, 4306 Register offsetTemp, Register maskTemp, 4307 Register output) { 4308 AtomicFetchOp(*this, &access, access.type(), access.sync(), op, mem, value, 4309 valueTemp, offsetTemp, maskTemp, output); 4310 } 4311 void MacroAssembler::wasmBoundsCheck32(Condition cond, Register index, 4312 Register boundsCheckLimit, 4313 Label* label) { 4314 ma_b(index, boundsCheckLimit, label, cond); 4315 } 4316 4317 void MacroAssembler::wasmBoundsCheck32(Condition cond, Register index, 4318 Address boundsCheckLimit, Label* label) { 4319 UseScratchRegisterScope temps(this); 4320 Register scratch2 = temps.Acquire(); 4321 load32(boundsCheckLimit, scratch2); 4322 ma_b(index, Register(scratch2), label, cond); 4323 } 4324 4325 void MacroAssembler::wasmBoundsCheck64(Condition cond, Register64 index, 4326 Register64 boundsCheckLimit, 4327 Label* label) { 4328 ma_b(index.reg, boundsCheckLimit.reg, label, cond); 4329 } 4330 4331 void MacroAssembler::wasmBoundsCheck64(Condition cond, Register64 index, 4332 Address boundsCheckLimit, Label* label) { 4333 UseScratchRegisterScope temps(this); 4334 Register scratch2 = temps.Acquire(); 4335 loadPtr(boundsCheckLimit, scratch2); 4336 ma_b(index.reg, scratch2, label, cond); 4337 } 4338 4339 void MacroAssembler::wasmCompareExchange64(const wasm::MemoryAccessDesc& access, 4340 const Address& mem, 4341 Register64 expect, 4342 Register64 replace, 4343 Register64 output) { 4344 CompareExchange64(*this, &access, access.sync(), mem, expect, replace, 4345 output); 4346 } 4347 4348 void MacroAssembler::wasmCompareExchange64(const wasm::MemoryAccessDesc& access, 4349 const BaseIndex& mem, 4350 Register64 expect, 4351 Register64 replace, 4352 Register64 output) { 4353 CompareExchange64(*this, &access, access.sync(), mem, expect, replace, 4354 output); 4355 } 4356 4357 template <typename T> 4358 static void CompareExchange(MacroAssembler& masm, 4359 const wasm::MemoryAccessDesc* access, 4360 Scalar::Type type, Synchronization sync, 4361 const T& mem, Register oldval, Register newval, 4362 Register valueTemp, Register offsetTemp, 4363 Register maskTemp, Register output) { 4364 bool signExtend = Scalar::isSignedIntType(type); 4365 unsigned nbytes = Scalar::byteSize(type); 4366 4367 switch (nbytes) { 4368 case 1: 4369 case 2: 4370 break; 4371 case 4: 4372 MOZ_ASSERT(valueTemp == InvalidReg); 4373 MOZ_ASSERT(offsetTemp == InvalidReg); 4374 MOZ_ASSERT(maskTemp == InvalidReg); 4375 break; 4376 default: 4377 MOZ_CRASH(); 4378 } 4379 4380 Label again, end; 4381 UseScratchRegisterScope temps(&masm); 4382 Register scratch1 = temps.Acquire(); 4383 Register scratch2 = temps.Acquire(); 4384 masm.computeEffectiveAddress(mem, scratch2); 4385 4386 if (nbytes == 4) { 4387 masm.memoryBarrierBefore(sync); 4388 masm.bind(&again); 4389 4390 if (access) { 4391 masm.append(*access, wasm::TrapMachineInsn::Atomic, 4392 FaultingCodeOffset(masm.currentOffset())); 4393 } 4394 4395 masm.lr_w(true, true, output, scratch2); 4396 masm.SignExtendWord(scratch1, oldval); 4397 masm.ma_b(output, scratch1, &end, Assembler::NotEqual, ShortJump); 4398 masm.mv(scratch1, newval); 4399 masm.sc_w(true, true, scratch1, scratch2, scratch1); 4400 masm.ma_b(scratch1, scratch1, &again, Assembler::NonZero, ShortJump); 4401 4402 masm.memoryBarrierAfter(sync); 4403 masm.bind(&end); 4404 4405 return; 4406 } 4407 4408 masm.andi(offsetTemp, scratch2, 3); 4409 masm.subPtr(offsetTemp, scratch2); 4410 #if !MOZ_LITTLE_ENDIAN() 4411 masm.as_xori(offsetTemp, offsetTemp, 3); 4412 #endif 4413 masm.slli(offsetTemp, offsetTemp, 3); 4414 masm.ma_li(maskTemp, Imm32(UINT32_MAX >> ((4 - nbytes) * 8))); 4415 masm.sll(maskTemp, maskTemp, offsetTemp); 4416 masm.not_(maskTemp, maskTemp); 4417 4418 masm.memoryBarrierBefore(sync); 4419 4420 masm.bind(&again); 4421 4422 if (access) { 4423 masm.append(*access, wasm::TrapMachineInsn::Atomic, 4424 FaultingCodeOffset(masm.currentOffset())); 4425 } 4426 4427 masm.lr_w(true, true, scratch1, scratch2); 4428 4429 masm.srl(output, scratch1, offsetTemp); 4430 4431 switch (nbytes) { 4432 case 1: 4433 if (signExtend) { 4434 masm.SignExtendByte(valueTemp, oldval); 4435 masm.SignExtendByte(output, output); 4436 masm.SignExtendByte(newval, newval); 4437 } else { 4438 masm.andi(valueTemp, oldval, 0xff); 4439 masm.andi(output, output, 0xff); 4440 masm.andi(newval, newval, 0xff); 4441 } 4442 break; 4443 case 2: 4444 if (signExtend) { 4445 masm.SignExtendShort(valueTemp, oldval); 4446 masm.SignExtendShort(output, output); 4447 masm.SignExtendShort(newval, newval); 4448 } else { 4449 UseScratchRegisterScope temps(&masm); 4450 Register mask = temps.Acquire(); 4451 masm.ma_li(mask, Imm32(0xffff)); 4452 masm.and_(valueTemp, oldval, mask); 4453 masm.and_(output, output, mask); 4454 masm.and_(newval, newval, mask); 4455 } 4456 break; 4457 } 4458 4459 masm.ma_b(output, valueTemp, &end, Assembler::NotEqual, ShortJump); 4460 4461 masm.sllw(valueTemp, newval, offsetTemp); 4462 masm.and_(scratch1, scratch1, maskTemp); 4463 masm.or_(scratch1, scratch1, valueTemp); 4464 masm.sc_w(true, true, scratch1, scratch2, scratch1); 4465 4466 masm.ma_b(scratch1, scratch1, &again, Assembler::NonZero, ShortJump); 4467 4468 masm.memoryBarrierAfter(sync); 4469 4470 masm.bind(&end); 4471 } 4472 4473 void MacroAssembler::compareExchange(Scalar::Type type, Synchronization sync, 4474 const Address& mem, Register oldval, 4475 Register newval, Register valueTemp, 4476 Register offsetTemp, Register maskTemp, 4477 Register output) { 4478 CompareExchange(*this, nullptr, type, sync, mem, oldval, newval, valueTemp, 4479 offsetTemp, maskTemp, output); 4480 } 4481 4482 void MacroAssembler::compareExchange(Scalar::Type type, Synchronization sync, 4483 const BaseIndex& mem, Register oldval, 4484 Register newval, Register valueTemp, 4485 Register offsetTemp, Register maskTemp, 4486 Register output) { 4487 CompareExchange(*this, nullptr, type, sync, mem, oldval, newval, valueTemp, 4488 offsetTemp, maskTemp, output); 4489 } 4490 4491 void MacroAssembler::wasmCompareExchange(const wasm::MemoryAccessDesc& access, 4492 const Address& mem, Register oldval, 4493 Register newval, Register valueTemp, 4494 Register offsetTemp, Register maskTemp, 4495 Register output) { 4496 CompareExchange(*this, &access, access.type(), access.sync(), mem, oldval, 4497 newval, valueTemp, offsetTemp, maskTemp, output); 4498 } 4499 4500 void MacroAssembler::wasmCompareExchange(const wasm::MemoryAccessDesc& access, 4501 const BaseIndex& mem, Register oldval, 4502 Register newval, Register valueTemp, 4503 Register offsetTemp, Register maskTemp, 4504 Register output) { 4505 CompareExchange(*this, &access, access.type(), access.sync(), mem, oldval, 4506 newval, valueTemp, offsetTemp, maskTemp, output); 4507 } 4508 4509 void MacroAssembler::wasmLoad(const wasm::MemoryAccessDesc& access, 4510 Register memoryBase, Register ptr, 4511 Register ptrScratch, AnyRegister output) { 4512 wasmLoadImpl(access, memoryBase, ptr, ptrScratch, output, InvalidReg); 4513 } 4514 4515 void MacroAssembler::wasmLoadI64(const wasm::MemoryAccessDesc& access, 4516 Register memoryBase, Register ptr, 4517 Register ptrScratch, Register64 output) { 4518 wasmLoadI64Impl(access, memoryBase, ptr, ptrScratch, output, InvalidReg); 4519 } 4520 4521 void MacroAssembler::wasmStore(const wasm::MemoryAccessDesc& access, 4522 AnyRegister value, Register memoryBase, 4523 Register ptr, Register ptrScratch) { 4524 wasmStoreImpl(access, value, memoryBase, ptr, ptrScratch, InvalidReg); 4525 } 4526 4527 void MacroAssembler::wasmStoreI64(const wasm::MemoryAccessDesc& access, 4528 Register64 value, Register memoryBase, 4529 Register ptr, Register ptrScratch) { 4530 wasmStoreI64Impl(access, value, memoryBase, ptr, ptrScratch, InvalidReg); 4531 } 4532 4533 void MacroAssemblerRiscv64::Clear_if_nan_d(Register rd, FPURegister fs) { 4534 UseScratchRegisterScope temps(this); 4535 Register scratch = temps.Acquire(); 4536 4537 feq_d(scratch, fs, fs); 4538 neg(scratch, scratch); 4539 and_(rd, rd, scratch); 4540 } 4541 4542 void MacroAssemblerRiscv64::Clear_if_nan_s(Register rd, FPURegister fs) { 4543 UseScratchRegisterScope temps(this); 4544 Register scratch = temps.Acquire(); 4545 4546 feq_s(scratch, fs, fs); 4547 neg(scratch, scratch); 4548 and_(rd, rd, scratch); 4549 } 4550 4551 void MacroAssembler::wasmTruncateDoubleToInt32(FloatRegister input, 4552 Register output, 4553 bool isSaturating, 4554 Label* oolEntry) { 4555 if (isSaturating) { 4556 Trunc_w_d(output, input); 4557 Clear_if_nan_d(output, input); 4558 } else { 4559 UseScratchRegisterScope temps(this); 4560 Register scratch = temps.Acquire(); 4561 4562 Trunc_l_d(output, input); 4563 4564 // Sign extend lower 32 bits to test if the result isn't an Int32. 4565 move32SignExtendToPtr(output, scratch); 4566 branchPtr(Assembler::NotEqual, output, scratch, oolEntry); 4567 } 4568 } 4569 4570 void MacroAssembler::wasmTruncateDoubleToInt64( 4571 FloatRegister input, Register64 output, bool isSaturating, Label* oolEntry, 4572 Label* oolRejoin, FloatRegister tempDouble) { 4573 if (isSaturating) { 4574 Trunc_l_d(output.reg, input); 4575 Clear_if_nan_d(output.reg, input); 4576 } else { 4577 UseScratchRegisterScope temps(this); 4578 Register scratch = temps.Acquire(); 4579 Trunc_l_d(output.reg, input, scratch); 4580 ma_b(scratch, Imm32(0), oolEntry, Assembler::Equal); 4581 } 4582 } 4583 4584 void MacroAssembler::wasmTruncateDoubleToUInt32(FloatRegister input, 4585 Register output, 4586 bool isSaturating, 4587 Label* oolEntry) { 4588 if (isSaturating) { 4589 Trunc_uw_d(output, input); 4590 Clear_if_nan_d(output, input); 4591 } else { 4592 UseScratchRegisterScope temps(this); 4593 Register scratch = temps.Acquire(); 4594 Trunc_uw_d(output, input, scratch); 4595 ma_b(scratch, Imm32(0), oolEntry, Assembler::Equal); 4596 } 4597 } 4598 4599 void MacroAssembler::wasmTruncateDoubleToUInt64( 4600 FloatRegister input, Register64 output, bool isSaturating, Label* oolEntry, 4601 Label* oolRejoin, FloatRegister tempDouble) { 4602 if (isSaturating) { 4603 Trunc_ul_d(output.reg, input); 4604 Clear_if_nan_d(output.reg, input); 4605 } else { 4606 UseScratchRegisterScope temps(this); 4607 Register scratch = temps.Acquire(); 4608 Trunc_ul_d(output.reg, input, scratch); 4609 ma_b(scratch, Imm32(0), oolEntry, Assembler::Equal); 4610 } 4611 } 4612 4613 void MacroAssembler::wasmTruncateFloat32ToInt32(FloatRegister input, 4614 Register output, 4615 bool isSaturating, 4616 Label* oolEntry) { 4617 if (isSaturating) { 4618 Trunc_w_s(output, input); 4619 Clear_if_nan_s(output, input); 4620 } else { 4621 UseScratchRegisterScope temps(this); 4622 Register scratch = temps.Acquire(); 4623 4624 Trunc_l_s(output, input, scratch); 4625 4626 // Sign extend lower 32 bits to test if the result isn't an Int32. 4627 move32SignExtendToPtr(output, scratch); 4628 branchPtr(Assembler::NotEqual, output, scratch, oolEntry); 4629 } 4630 } 4631 4632 void MacroAssembler::wasmTruncateFloat32ToInt64( 4633 FloatRegister input, Register64 output, bool isSaturating, Label* oolEntry, 4634 Label* oolRejoin, FloatRegister tempFloat) { 4635 if (isSaturating) { 4636 Trunc_l_s(output.reg, input); 4637 Clear_if_nan_s(output.reg, input); 4638 } else { 4639 UseScratchRegisterScope temps(this); 4640 Register scratch = temps.Acquire(); 4641 Trunc_l_s(output.reg, input, scratch); 4642 ma_b(scratch, Imm32(0), oolEntry, Assembler::Equal); 4643 } 4644 } 4645 4646 void MacroAssembler::wasmTruncateFloat32ToUInt32(FloatRegister input, 4647 Register output, 4648 bool isSaturating, 4649 Label* oolEntry) { 4650 if (isSaturating) { 4651 Trunc_uw_s(output, input); 4652 Clear_if_nan_s(output, input); 4653 } else { 4654 UseScratchRegisterScope temps(this); 4655 Register scratch = temps.Acquire(); 4656 Trunc_uw_s(output, input, scratch); 4657 ma_b(scratch, Imm32(0), oolEntry, Assembler::Equal); 4658 } 4659 } 4660 4661 void MacroAssembler::wasmTruncateFloat32ToUInt64( 4662 FloatRegister input, Register64 output, bool isSaturating, Label* oolEntry, 4663 Label* oolRejoin, FloatRegister tempFloat) { 4664 if (isSaturating) { 4665 Trunc_ul_s(output.reg, input); 4666 Clear_if_nan_s(output.reg, input); 4667 } else { 4668 UseScratchRegisterScope temps(this); 4669 Register scratch = temps.Acquire(); 4670 Trunc_ul_s(output.reg, input, scratch); 4671 ma_b(scratch, Imm32(0), oolEntry, Assembler::Equal); 4672 } 4673 } 4674 4675 // TODO(riscv64): widenInt32 should be nop? 4676 void MacroAssembler::widenInt32(Register r) { 4677 move32To64SignExtend(r, Register64(r)); 4678 } 4679 4680 void MacroAssembler::wasmMarkCallAsSlow() { mv(ra, ra); } 4681 4682 const int32_t SlowCallMarker = 0x8093; // addi ra, ra, 0 4683 4684 void MacroAssembler::wasmCheckSlowCallsite(Register ra_, Label* notSlow, 4685 Register temp1, Register temp2) { 4686 MOZ_ASSERT(ra_ != temp2); 4687 4688 UseScratchRegisterScope temps(*this); 4689 // temp1 aliases ra_, so allocating a new register. 4690 const Register scratchMarker = temps.Acquire(); 4691 move32(Imm32(SlowCallMarker), scratchMarker); 4692 4693 Label slow; 4694 // Handle `jalr; (ra_ here) marker`. 4695 load32(Address(ra_, 0), temp2); 4696 branch32(Assembler::Equal, temp2, scratchMarker, &slow); 4697 // Handle `jal; (ra_ here) nop; marker`. 4698 // See also: AssemblerRISCVI::jal(Register rd, int32_t imm21); Bug 1996840 4699 branch32(Assembler::NotEqual, temp2, Imm32(kNopByte), notSlow); 4700 load32(Address(ra_, 4), temp2); 4701 branch32(Assembler::NotEqual, temp2, scratchMarker, notSlow); 4702 bind(&slow); 4703 } 4704 4705 CodeOffset MacroAssembler::wasmMarkedSlowCall(const wasm::CallSiteDesc& desc, 4706 const Register reg) { 4707 BlockTrampolinePoolScope block_trampoline_pool(this, 2); 4708 CodeOffset offset = call(desc, reg); 4709 wasmMarkCallAsSlow(); 4710 return offset; 4711 } 4712 //}}} check_macroassembler_style 4713 4714 // This method generates lui, dsll and ori instruction block that can be 4715 // modified by UpdateLoad64Value, either during compilation (eg. 4716 // Assembler::bind), or during execution (eg. jit::PatchJump). 4717 void MacroAssemblerRiscv64::ma_liPatchable(Register dest, Imm32 imm) { 4718 m_buffer.ensureSpace(2 * sizeof(uint32_t)); 4719 int64_t value = imm.value; 4720 int64_t high_20 = ((value + 0x800) >> 12); 4721 int64_t low_12 = value << 52 >> 52; 4722 lui(dest, high_20); 4723 addi(dest, dest, low_12); 4724 } 4725 4726 void MacroAssemblerRiscv64::ma_liPatchable(Register dest, ImmPtr imm) { 4727 return ma_liPatchable(dest, ImmWord(uintptr_t(imm.value))); 4728 } 4729 4730 void MacroAssemblerRiscv64::ma_liPatchable(Register dest, ImmWord imm, 4731 LiFlags flags) { 4732 DEBUG_PRINTF("\tma_liPatchable\n"); 4733 if (Li64 == flags) { 4734 li_constant(dest, imm.value); 4735 } else { 4736 li_ptr(dest, imm.value); 4737 } 4738 } 4739 4740 void MacroAssemblerRiscv64::ma_li(Register dest, ImmGCPtr ptr) { 4741 BlockTrampolinePoolScope block_trampoline_pool(this, 6); 4742 writeDataRelocation(ptr); 4743 ma_liPatchable(dest, ImmPtr(ptr.value)); 4744 } 4745 void MacroAssemblerRiscv64::ma_li(Register dest, Imm32 imm) { 4746 RV_li(dest, imm.value); 4747 } 4748 void MacroAssemblerRiscv64::ma_li(Register dest, Imm64 imm) { 4749 RV_li(dest, imm.value); 4750 } 4751 void MacroAssemblerRiscv64::ma_li(Register dest, CodeLabel* label) { 4752 DEBUG_PRINTF("[ %s\n", __FUNCTION__); 4753 BlockTrampolinePoolScope block_trampoline_pool(this, 7); 4754 BufferOffset bo = m_buffer.nextOffset(); 4755 JitSpew(JitSpew_Codegen, ".load CodeLabel %p", label); 4756 ma_liPatchable(dest, ImmWord(/* placeholder */ 0)); 4757 label->patchAt()->bind(bo.getOffset()); 4758 label->setLinkMode(CodeLabel::MoveImmediate); 4759 DEBUG_PRINTF("]\n"); 4760 } 4761 void MacroAssemblerRiscv64::ma_li(Register dest, ImmWord imm) { 4762 RV_li(dest, imm.value); 4763 } 4764 4765 // Shortcut for when we know we're transferring 32 bits of data. 4766 void MacroAssemblerRiscv64::ma_pop(Register r) { 4767 ld(r, StackPointer, 0); 4768 addi(StackPointer, StackPointer, sizeof(intptr_t)); 4769 } 4770 4771 void MacroAssemblerRiscv64::ma_push(Register r) { 4772 UseScratchRegisterScope temps(this); 4773 if (r == sp) { 4774 Register scratch = temps.Acquire(); 4775 // Pushing sp requires one more instruction. 4776 mv(scratch, sp); 4777 r = scratch; 4778 } 4779 4780 addi(StackPointer, StackPointer, (int32_t)-sizeof(intptr_t)); 4781 sd(r, StackPointer, 0); 4782 } 4783 4784 void MacroAssemblerRiscv64::ma_mul32TestOverflow(Register rd, Register rj, 4785 Register rk, Label* overflow) { 4786 UseScratchRegisterScope temps(this); 4787 Register scratch = temps.Acquire(); 4788 MOZ_ASSERT(rd != scratch); 4789 4790 mul(rd, rj, rk); 4791 sext_w(scratch, rd); 4792 ma_b(scratch, rd, overflow, Assembler::NotEqual); 4793 } 4794 void MacroAssemblerRiscv64::ma_mul32TestOverflow(Register rd, Register rj, 4795 Imm32 imm, Label* overflow) { 4796 UseScratchRegisterScope temps(this); 4797 Register scratch = temps.Acquire(); 4798 MOZ_ASSERT(rd != scratch && rj != scratch); 4799 4800 ma_li(scratch, imm); 4801 4802 mul(rd, rj, scratch); 4803 sext_w(scratch, rd); 4804 ma_b(scratch, rd, overflow, Assembler::NotEqual); 4805 } 4806 4807 void MacroAssemblerRiscv64::ma_mulPtrTestOverflow(Register rd, Register rj, 4808 Register rk, 4809 Label* overflow) { 4810 UseScratchRegisterScope temps(this); 4811 Register scratch = temps.Acquire(); 4812 Register scratch2 = temps.Acquire(); 4813 MOZ_ASSERT(rd != scratch); 4814 4815 if (rd == rj) { 4816 or_(scratch, rj, zero); 4817 rj = scratch; 4818 rk = (rd == rk) ? rj : rk; 4819 } else if (rd == rk) { 4820 or_(scratch, rk, zero); 4821 rk = scratch; 4822 } 4823 4824 mul(rd, rj, rk); 4825 mulh(scratch, rj, rk); 4826 srai(scratch2, rd, 63); 4827 ma_b(scratch, Register(scratch2), overflow, Assembler::NotEqual); 4828 } 4829 4830 int32_t MacroAssemblerRiscv64::GetOffset(int32_t offset, Label* L, 4831 OffsetSize bits) { 4832 if (L) { 4833 offset = branchOffsetHelper(L, bits); 4834 } else { 4835 MOZ_ASSERT(is_intn(offset, bits)); 4836 } 4837 return offset; 4838 } 4839 4840 bool MacroAssemblerRiscv64::CalculateOffset(Label* L, OffsetSize bits, 4841 int32_t* offset) { 4842 if (!is_near(L, bits)) return false; 4843 *offset = GetOffset(*offset, L, bits); 4844 return true; 4845 } 4846 4847 void MacroAssemblerRiscv64::BranchShortHelper(int32_t offset, Label* L) { 4848 MOZ_ASSERT(L == nullptr || offset == 0); 4849 BlockTrampolinePoolScope block_trampoline_pool(this, 2, 1); 4850 offset = GetOffset(offset, L, OffsetSize::kOffset21); 4851 Assembler::j(offset); 4852 } 4853 4854 bool MacroAssemblerRiscv64::BranchShortHelper(int32_t offset, Label* L, 4855 Condition cond, Register rs, 4856 const Operand& rt) { 4857 MOZ_ASSERT(L == nullptr || offset == 0); 4858 MOZ_ASSERT(rt.is_reg() || rt.is_imm()); 4859 UseScratchRegisterScope temps(this); 4860 Register scratch = Register(); 4861 if (rt.is_imm()) { 4862 if (rt.immediate() == 0) { 4863 scratch = zero; 4864 } else { 4865 scratch = temps.Acquire(); 4866 ma_li(scratch, Imm64(rt.immediate())); 4867 } 4868 } else { 4869 MOZ_ASSERT(rt.is_reg()); 4870 scratch = rt.rm(); 4871 } 4872 { 4873 BlockTrampolinePoolScope block_trampoline_pool(this, 2, 1); 4874 switch (cond) { 4875 case Always: 4876 if (!CalculateOffset(L, OffsetSize::kOffset21, &offset)) return false; 4877 Assembler::j(offset); 4878 break; 4879 case Equal: 4880 // rs == rt 4881 if (rt.is_reg() && rs == rt.rm()) { 4882 if (!CalculateOffset(L, OffsetSize::kOffset21, &offset)) return false; 4883 Assembler::j(offset); 4884 } else { 4885 if (!CalculateOffset(L, OffsetSize::kOffset13, &offset)) return false; 4886 Assembler::beq(rs, scratch, offset); 4887 } 4888 break; 4889 case NotEqual: 4890 // rs != rt 4891 if (rt.is_reg() && rs == rt.rm()) { 4892 break; // No code needs to be emitted 4893 } else { 4894 if (!CalculateOffset(L, OffsetSize::kOffset13, &offset)) return false; 4895 Assembler::bne(rs, scratch, offset); 4896 } 4897 break; 4898 4899 // Signed comparison. 4900 case GreaterThan: 4901 // rs > rt 4902 if (rt.is_reg() && rs == rt.rm()) { 4903 break; // No code needs to be emitted. 4904 } else { 4905 if (!CalculateOffset(L, OffsetSize::kOffset13, &offset)) return false; 4906 Assembler::bgt(rs, scratch, offset); 4907 } 4908 break; 4909 case GreaterThanOrEqual: 4910 // rs >= rt 4911 if (rt.is_reg() && rs == rt.rm()) { 4912 if (!CalculateOffset(L, OffsetSize::kOffset21, &offset)) return false; 4913 Assembler::j(offset); 4914 } else { 4915 if (!CalculateOffset(L, OffsetSize::kOffset13, &offset)) return false; 4916 Assembler::bge(rs, scratch, offset); 4917 } 4918 break; 4919 case LessThan: 4920 // rs < rt 4921 if (rt.is_reg() && rs == rt.rm()) { 4922 break; // No code needs to be emitted. 4923 } else { 4924 if (!CalculateOffset(L, OffsetSize::kOffset13, &offset)) return false; 4925 Assembler::blt(rs, scratch, offset); 4926 } 4927 break; 4928 case LessThanOrEqual: 4929 // rs <= rt 4930 if (rt.is_reg() && rs == rt.rm()) { 4931 if (!CalculateOffset(L, OffsetSize::kOffset21, &offset)) return false; 4932 Assembler::j(offset); 4933 } else { 4934 if (!CalculateOffset(L, OffsetSize::kOffset13, &offset)) return false; 4935 Assembler::ble(rs, scratch, offset); 4936 } 4937 break; 4938 4939 // Unsigned comparison. 4940 case Above: 4941 // rs > rt 4942 if (rt.is_reg() && rs == rt.rm()) { 4943 break; // No code needs to be emitted. 4944 } else { 4945 if (!CalculateOffset(L, OffsetSize::kOffset13, &offset)) return false; 4946 Assembler::bgtu(rs, scratch, offset); 4947 } 4948 break; 4949 case AboveOrEqual: 4950 // rs >= rt 4951 if (rt.is_reg() && rs == rt.rm()) { 4952 if (!CalculateOffset(L, OffsetSize::kOffset21, &offset)) return false; 4953 Assembler::j(offset); 4954 } else { 4955 if (!CalculateOffset(L, OffsetSize::kOffset13, &offset)) return false; 4956 Assembler::bgeu(rs, scratch, offset); 4957 } 4958 break; 4959 case Below: 4960 // rs < rt 4961 if (rt.is_reg() && rs == rt.rm()) { 4962 break; // No code needs to be emitted. 4963 } else { 4964 if (!CalculateOffset(L, OffsetSize::kOffset13, &offset)) return false; 4965 bltu(rs, scratch, offset); 4966 } 4967 break; 4968 case BelowOrEqual: 4969 // rs <= rt 4970 if (rt.is_reg() && rs == rt.rm()) { 4971 if (!CalculateOffset(L, OffsetSize::kOffset21, &offset)) return false; 4972 Assembler::j(offset); 4973 } else { 4974 if (!CalculateOffset(L, OffsetSize::kOffset13, &offset)) return false; 4975 Assembler::bleu(rs, scratch, offset); 4976 } 4977 break; 4978 default: 4979 MOZ_CRASH("UNREACHABLE"); 4980 } 4981 } 4982 return true; 4983 } 4984 4985 // BRANCH_ARGS_CHECK checks that conditional jump arguments are correct. 4986 #define BRANCH_ARGS_CHECK(cond, rs, rt) \ 4987 MOZ_ASSERT((cond == Always && rs == zero && rt.rm() == zero) || \ 4988 (cond != Always && (rs != zero || rt.rm() != zero))) 4989 4990 bool MacroAssemblerRiscv64::BranchShortCheck(int32_t offset, Label* L, 4991 Condition cond, Register rs, 4992 const Operand& rt) { 4993 BRANCH_ARGS_CHECK(cond, rs, rt); 4994 4995 if (!L) { 4996 MOZ_ASSERT(is_int13(offset)); 4997 return BranchShortHelper(offset, nullptr, cond, rs, rt); 4998 } else { 4999 MOZ_ASSERT(offset == 0); 5000 return BranchShortHelper(0, L, cond, rs, rt); 5001 } 5002 } 5003 5004 void MacroAssemblerRiscv64::BranchShort(Label* L) { BranchShortHelper(0, L); } 5005 5006 bool MacroAssemblerRiscv64::BranchShort(int32_t offset, Condition cond, 5007 Register rs, const Operand& rt) { 5008 return BranchShortCheck(offset, nullptr, cond, rs, rt); 5009 } 5010 5011 bool MacroAssemblerRiscv64::BranchShort(Label* L, Condition cond, Register rs, 5012 const Operand& rt) { 5013 return BranchShortCheck(0, L, cond, rs, rt); 5014 } 5015 5016 void MacroAssemblerRiscv64::BranchLong(Label* L) { 5017 // Generate position independent long branch. 5018 UseScratchRegisterScope temps(this); 5019 Register scratch = temps.Acquire(); 5020 int32_t imm = branchLongOffsetHelper(L); 5021 GenPCRelativeJump(scratch, imm); 5022 } 5023 5024 CodeOffset MacroAssemblerRiscv64::BranchAndLinkLong(Label* L) { 5025 // Generate position independent long branch and link. 5026 int32_t imm = branchLongOffsetHelper(L); 5027 UseScratchRegisterScope temps(this); 5028 Register scratch = temps.Acquire(); 5029 GenPCRelativeJumpAndLink(scratch, imm); 5030 return CodeOffset(currentOffset()); 5031 } 5032 5033 void MacroAssemblerRiscv64::ma_branch(Label* L, Condition cond, Register rs, 5034 const Operand& rt, JumpKind jumpKind) { 5035 // Always prefer short jumps when the label is already bound. (If the label is 5036 // bound, BranchShort can cheaply determine if short jumps are possible.) 5037 if (L->bound()) { 5038 jumpKind = ShortJump; 5039 } 5040 5041 if (jumpKind == ShortJump && BranchShort(L, cond, rs, rt)) { 5042 return; 5043 } 5044 5045 if (cond != Always) { 5046 Label skip; 5047 Condition neg_cond = InvertCondition(cond); 5048 MOZ_ALWAYS_TRUE( 5049 BranchShort(&skip, neg_cond, rs, rt)); // Guaranteed to be short. 5050 BranchLong(L); 5051 bind(&skip); 5052 } else { 5053 BranchLong(L); 5054 } 5055 } 5056 5057 // Branches when done from within riscv code. 5058 void MacroAssemblerRiscv64::ma_b(Register lhs, Address addr, Label* label, 5059 Condition c, JumpKind jumpKind) { 5060 UseScratchRegisterScope temps(this); 5061 Register scratch = temps.Acquire(); 5062 MOZ_ASSERT(lhs != scratch); 5063 ma_load(scratch, addr, SizeDouble); 5064 ma_b(lhs, Register(scratch), label, c, jumpKind); 5065 } 5066 5067 void MacroAssemblerRiscv64::ma_b(Register lhs, ImmPtr imm, Label* l, 5068 Condition c, JumpKind jumpKind) { 5069 ma_b(lhs, ImmWord(uintptr_t(imm.value)), l, c, jumpKind); 5070 } 5071 5072 // Branches when done from within riscv code. 5073 void MacroAssemblerRiscv64::ma_b(Register lhs, ImmWord imm, Label* label, 5074 Condition c, JumpKind jumpKind) { 5075 switch (c) { 5076 case Always: 5077 ma_branch(label, c, zero, Operand(zero), jumpKind); 5078 break; 5079 case Zero: 5080 case NonZero: 5081 case Signed: 5082 case NotSigned: 5083 MOZ_ASSERT(imm.value == 0); 5084 ma_b(lhs, lhs, label, c, jumpKind); 5085 break; 5086 default: 5087 ma_branch(label, c, lhs, Operand(imm.value), jumpKind); 5088 break; 5089 } 5090 } 5091 5092 void MacroAssemblerRiscv64::ma_b(Register lhs, Imm32 imm, Label* label, 5093 Condition c, JumpKind jumpKind) { 5094 switch (c) { 5095 case Always: 5096 ma_branch(label, c, zero, Operand(zero), jumpKind); 5097 break; 5098 case Zero: 5099 case NonZero: 5100 case Signed: 5101 case NotSigned: 5102 MOZ_ASSERT(imm.value == 0); 5103 ma_b(lhs, lhs, label, c, jumpKind); 5104 break; 5105 default: 5106 ma_branch(label, c, lhs, Operand(imm.value), jumpKind); 5107 break; 5108 } 5109 } 5110 5111 void MacroAssemblerRiscv64::ma_b(Address addr, Imm32 imm, Label* label, 5112 Condition c, JumpKind jumpKind) { 5113 UseScratchRegisterScope temps(this); 5114 Register scratch2 = temps.Acquire(); 5115 ma_load(scratch2, addr); 5116 ma_b(Register(scratch2), imm, label, c, jumpKind); 5117 } 5118 5119 void MacroAssemblerRiscv64::ma_b(Register lhs, Register rhs, Label* label, 5120 Condition c, JumpKind jumpKind) { 5121 switch (c) { 5122 case Always: 5123 ma_branch(label, c, zero, Operand(zero), jumpKind); 5124 break; 5125 case Zero: 5126 MOZ_ASSERT(lhs == rhs); 5127 ma_branch(label, Equal, lhs, Operand(zero), jumpKind); 5128 break; 5129 case NonZero: 5130 MOZ_ASSERT(lhs == rhs); 5131 ma_branch(label, NotEqual, lhs, Operand(zero), jumpKind); 5132 break; 5133 case Signed: 5134 MOZ_ASSERT(lhs == rhs); 5135 ma_branch(label, LessThan, lhs, Operand(zero), jumpKind); 5136 break; 5137 case NotSigned: 5138 MOZ_ASSERT(lhs == rhs); 5139 ma_branch(label, GreaterThanOrEqual, lhs, Operand(zero), jumpKind); 5140 break; 5141 default: 5142 ma_branch(label, c, lhs, rhs, jumpKind); 5143 break; 5144 } 5145 } 5146 5147 void MacroAssemblerRiscv64::ExtractBits(Register rt, Register rs, uint16_t pos, 5148 uint16_t size, bool sign_extend) { 5149 #if JS_CODEGEN_RISCV64 5150 constexpr uint16_t MaxBits = 64; 5151 #elif JS_CODEGEN_RISCV32 5152 constexpr uint16_t MaxBits = 32; 5153 #endif 5154 5155 MOZ_ASSERT(pos < MaxBits); 5156 MOZ_ASSERT(size > 0); 5157 MOZ_ASSERT(size <= MaxBits); 5158 MOZ_ASSERT((pos + size) > 0); 5159 MOZ_ASSERT((pos + size) <= MaxBits); 5160 5161 Register src; 5162 if (uint16_t shift = MaxBits - (pos + size)) { 5163 slli(rt, rs, shift); 5164 src = rt; 5165 } else { 5166 src = rs; 5167 } 5168 5169 if (sign_extend) { 5170 srai(rt, src, MaxBits - size); 5171 } else { 5172 srli(rt, src, MaxBits - size); 5173 } 5174 } 5175 5176 void MacroAssemblerRiscv64::InsertBits(Register dest, Register source, int pos, 5177 int size) { 5178 #if JS_CODEGEN_RISCV64 5179 MOZ_ASSERT(size < 64); 5180 #elif JS_CODEGEN_RISCV32 5181 MOZ_ASSERT(size < 32); 5182 #endif 5183 UseScratchRegisterScope temps(this); 5184 BlockTrampolinePoolScope block_trampoline_pool(this, 9); 5185 Register source_ = temps.Acquire(); 5186 if (pos != 0) { 5187 Register mask = temps.Acquire(); 5188 // Create a mask of the length=size. 5189 ma_li(mask, Imm32(1)); 5190 slli(mask, mask, size); 5191 addi(mask, mask, -1); 5192 and_(source_, mask, source); 5193 slli(source_, source_, pos); 5194 // Make a mask containing 0's. 0's start at "pos" with length=size. 5195 slli(mask, mask, pos); 5196 not_(mask, mask); 5197 // cut area for insertion of source. 5198 and_(dest, mask, dest); 5199 } else { 5200 // clear top bits from source and bottom bits from dest. 5201 slli(source_, source, 64 - size); 5202 srli(source_, source_, 64 - size); 5203 srli(dest, dest, size); 5204 slli(dest, dest, size); 5205 } 5206 // insert source 5207 or_(dest, dest, source_); 5208 } 5209 5210 void MacroAssemblerRiscv64::InsertBits(Register dest, Register source, 5211 Register pos, int size) { 5212 #if JS_CODEGEN_RISCV64 5213 MOZ_ASSERT(size < 64); 5214 #elif JS_CODEGEN_RISCV32 5215 MOZ_ASSERT(size < 32); 5216 #endif 5217 UseScratchRegisterScope temps(this); 5218 Register mask = temps.Acquire(); 5219 BlockTrampolinePoolScope block_trampoline_pool(this, 9); 5220 Register source_ = temps.Acquire(); 5221 // Create a mask of the length=size. 5222 ma_li(mask, Imm32(1)); 5223 slli(mask, mask, size); 5224 addi(mask, mask, -1); 5225 and_(source_, mask, source); 5226 sll(source_, source_, pos); 5227 // Make a mask containing 0's. 0's start at "pos" with length=size. 5228 sll(mask, mask, pos); 5229 not_(mask, mask); 5230 // cut area for insertion of source. 5231 and_(dest, mask, dest); 5232 // insert source 5233 or_(dest, dest, source_); 5234 } 5235 5236 void MacroAssemblerRiscv64::ma_add32(Register rd, Register rs, Operand rt) { 5237 if (rt.is_imm()) { 5238 if (is_int12(rt.immediate())) { 5239 addiw(rd, rs, static_cast<int32_t>(rt.immediate())); 5240 } else if ((-4096 <= rt.immediate() && rt.immediate() <= -2049) || 5241 (2048 <= rt.immediate() && rt.immediate() <= 4094)) { 5242 addiw(rd, rs, rt.immediate() / 2); 5243 addiw(rd, rd, rt.immediate() - (rt.immediate() / 2)); 5244 } else { 5245 // li handles the relocation. 5246 UseScratchRegisterScope temps(this); 5247 Register scratch = temps.Acquire(); 5248 BlockTrampolinePoolScope block_trampoline_pool(this, 9); 5249 ma_li(scratch, rt.immediate()); 5250 addw(rd, rs, scratch); 5251 } 5252 } else { 5253 MOZ_ASSERT(rt.is_reg()); 5254 addw(rd, rs, rt.rm()); 5255 } 5256 } 5257 5258 void MacroAssemblerRiscv64::ma_add64(Register rd, Register rs, Operand rt) { 5259 if (rt.is_imm()) { 5260 if (is_int12(rt.immediate())) { 5261 addi(rd, rs, static_cast<int32_t>(rt.immediate())); 5262 } else if ((-4096 <= rt.immediate() && rt.immediate() <= -2049) || 5263 (2048 <= rt.immediate() && rt.immediate() <= 4094)) { 5264 addi(rd, rs, rt.immediate() / 2); 5265 addi(rd, rd, rt.immediate() - (rt.immediate() / 2)); 5266 } else { 5267 // li handles the relocation. 5268 UseScratchRegisterScope temps(this); 5269 Register scratch = temps.Acquire(); 5270 BlockTrampolinePoolScope block_trampoline_pool(this, 9); 5271 ma_li(scratch, rt.immediate()); 5272 add(rd, rs, scratch); 5273 } 5274 } else { 5275 MOZ_ASSERT(rt.is_reg()); 5276 add(rd, rs, rt.rm()); 5277 } 5278 } 5279 5280 void MacroAssemblerRiscv64::ma_sub32(Register rd, Register rs, Operand rt) { 5281 if (rt.is_imm()) { 5282 if (is_int12(-rt.immediate())) { 5283 addiw(rd, rs, 5284 static_cast<int32_t>( 5285 -rt.immediate())); // No subi instr, use addi(x, y, -imm). 5286 } else if ((-4096 <= -rt.immediate() && -rt.immediate() <= -2049) || 5287 (2048 <= -rt.immediate() && -rt.immediate() <= 4094)) { 5288 addiw(rd, rs, -rt.immediate() / 2); 5289 addiw(rd, rd, -rt.immediate() - (-rt.immediate() / 2)); 5290 } else { 5291 // li handles the relocation. 5292 UseScratchRegisterScope temps(this); 5293 Register scratch = temps.Acquire(); 5294 ma_li(scratch, rt.immediate()); 5295 subw(rd, rs, scratch); 5296 } 5297 } else { 5298 MOZ_ASSERT(rt.is_reg()); 5299 subw(rd, rs, rt.rm()); 5300 } 5301 } 5302 5303 void MacroAssemblerRiscv64::ma_sub64(Register rd, Register rs, Operand rt) { 5304 if (rt.is_imm()) { 5305 if (is_int12(-rt.immediate())) { 5306 addi(rd, rs, 5307 static_cast<int32_t>( 5308 -rt.immediate())); // No subi instr, use addi(x, y, -imm). 5309 } else if ((-4096 <= -rt.immediate() && -rt.immediate() <= -2049) || 5310 (2048 <= -rt.immediate() && -rt.immediate() <= 4094)) { 5311 addi(rd, rs, -rt.immediate() / 2); 5312 addi(rd, rd, -rt.immediate() - (-rt.immediate() / 2)); 5313 } else { 5314 // li handles the relocation. 5315 UseScratchRegisterScope temps(this); 5316 Register scratch = temps.Acquire(); 5317 ma_li(scratch, rt.immediate()); 5318 sub(rd, rs, scratch); 5319 } 5320 } else { 5321 MOZ_ASSERT(rt.is_reg()); 5322 sub(rd, rs, rt.rm()); 5323 } 5324 } 5325 5326 void MacroAssemblerRiscv64::ma_and(Register rd, Register rs, Operand rt) { 5327 if (rt.is_imm()) { 5328 if (is_int12(rt.immediate())) { 5329 andi(rd, rs, rt.immediate()); 5330 } else { 5331 UseScratchRegisterScope temps(this); 5332 Register scratch = temps.Acquire(); 5333 ma_li(scratch, rt.immediate()); 5334 and_(rd, rs, scratch); 5335 } 5336 } else { 5337 MOZ_ASSERT(rt.is_reg()); 5338 and_(rd, rs, rt.rm()); 5339 } 5340 } 5341 5342 void MacroAssemblerRiscv64::ma_or(Register rd, Register rs, Operand rt) { 5343 if (rt.is_imm()) { 5344 if (is_int12(rt.immediate())) { 5345 ori(rd, rs, rt.immediate()); 5346 } else { 5347 UseScratchRegisterScope temps(this); 5348 Register scratch = temps.Acquire(); 5349 ma_li(scratch, rt.immediate()); 5350 or_(rd, rs, scratch); 5351 } 5352 } else { 5353 MOZ_ASSERT(rt.is_reg()); 5354 or_(rd, rs, rt.rm()); 5355 } 5356 } 5357 5358 void MacroAssemblerRiscv64::ma_xor(Register rd, Register rs, Operand rt) { 5359 if (rt.is_imm()) { 5360 if (is_int12(rt.immediate())) { 5361 xori(rd, rs, rt.immediate()); 5362 } else { 5363 UseScratchRegisterScope temps(this); 5364 Register scratch = temps.Acquire(); 5365 ma_li(scratch, rt.immediate()); 5366 xor_(rd, rs, scratch); 5367 } 5368 } else { 5369 MOZ_ASSERT(rt.is_reg()); 5370 xor_(rd, rs, rt.rm()); 5371 } 5372 } 5373 5374 void MacroAssemblerRiscv64::ma_nor(Register rd, Register rs, Operand rt) { 5375 if (rt.is_imm()) { 5376 UseScratchRegisterScope temps(this); 5377 Register scratch = temps.Acquire(); 5378 ma_li(scratch, rt.immediate()); 5379 nor(rd, rs, scratch); 5380 } else { 5381 MOZ_ASSERT(rt.is_reg()); 5382 nor(rd, rs, rt.rm()); 5383 } 5384 } 5385 5386 void MacroAssemblerRiscv64::ma_div32(Register rd, Register rs, Operand rt) { 5387 if (rt.is_imm()) { 5388 UseScratchRegisterScope temps(this); 5389 Register scratch = temps.Acquire(); 5390 ma_li(scratch, rt.immediate()); 5391 divw(rd, rs, scratch); 5392 } else { 5393 MOZ_ASSERT(rt.is_reg()); 5394 divw(rd, rs, rt.rm()); 5395 } 5396 } 5397 5398 void MacroAssemblerRiscv64::ma_divu32(Register rd, Register rs, Operand rt) { 5399 if (rt.is_imm()) { 5400 UseScratchRegisterScope temps(this); 5401 Register scratch = temps.Acquire(); 5402 ma_li(scratch, rt.immediate()); 5403 divuw(rd, rs, scratch); 5404 } else { 5405 MOZ_ASSERT(rt.is_reg()); 5406 divuw(rd, rs, rt.rm()); 5407 } 5408 } 5409 5410 void MacroAssemblerRiscv64::ma_div64(Register rd, Register rs, Operand rt) { 5411 if (rt.is_imm()) { 5412 UseScratchRegisterScope temps(this); 5413 Register scratch = temps.Acquire(); 5414 ma_li(scratch, rt.immediate()); 5415 div(rd, rs, scratch); 5416 } else { 5417 MOZ_ASSERT(rt.is_reg()); 5418 div(rd, rs, rt.rm()); 5419 } 5420 } 5421 5422 void MacroAssemblerRiscv64::ma_divu64(Register rd, Register rs, Operand rt) { 5423 if (rt.is_imm()) { 5424 UseScratchRegisterScope temps(this); 5425 Register scratch = temps.Acquire(); 5426 ma_li(scratch, rt.immediate()); 5427 divu(rd, rs, scratch); 5428 } else { 5429 MOZ_ASSERT(rt.is_reg()); 5430 divu(rd, rs, rt.rm()); 5431 } 5432 } 5433 5434 void MacroAssemblerRiscv64::ma_mod32(Register rd, Register rs, Operand rt) { 5435 if (rt.is_imm()) { 5436 UseScratchRegisterScope temps(this); 5437 Register scratch = temps.Acquire(); 5438 ma_li(scratch, rt.immediate()); 5439 remw(rd, rs, scratch); 5440 } else { 5441 MOZ_ASSERT(rt.is_reg()); 5442 remw(rd, rs, rt.rm()); 5443 } 5444 } 5445 5446 void MacroAssemblerRiscv64::ma_modu32(Register rd, Register rs, Operand rt) { 5447 if (rt.is_imm()) { 5448 UseScratchRegisterScope temps(this); 5449 Register scratch = temps.Acquire(); 5450 ma_li(scratch, rt.immediate()); 5451 remuw(rd, rs, scratch); 5452 } else { 5453 MOZ_ASSERT(rt.is_reg()); 5454 remuw(rd, rs, rt.rm()); 5455 } 5456 } 5457 5458 void MacroAssemblerRiscv64::ma_mod64(Register rd, Register rs, Operand rt) { 5459 if (rt.is_imm()) { 5460 UseScratchRegisterScope temps(this); 5461 Register scratch = temps.Acquire(); 5462 ma_li(scratch, rt.immediate()); 5463 rem(rd, rs, scratch); 5464 } else { 5465 MOZ_ASSERT(rt.is_reg()); 5466 rem(rd, rs, rt.rm()); 5467 } 5468 } 5469 5470 void MacroAssemblerRiscv64::ma_modu64(Register rd, Register rs, Operand rt) { 5471 if (rt.is_imm()) { 5472 UseScratchRegisterScope temps(this); 5473 Register scratch = temps.Acquire(); 5474 ma_li(scratch, rt.immediate()); 5475 remu(rd, rs, scratch); 5476 } else { 5477 MOZ_ASSERT(rt.is_reg()); 5478 remu(rd, rs, rt.rm()); 5479 } 5480 } 5481 5482 void MacroAssemblerRiscv64::ma_mul32(Register rd, Register rs, Operand rt) { 5483 if (rt.is_imm()) { 5484 UseScratchRegisterScope temps(this); 5485 Register scratch = temps.Acquire(); 5486 ma_li(scratch, rt.immediate()); 5487 mulw(rd, rs, scratch); 5488 } else { 5489 MOZ_ASSERT(rt.is_reg()); 5490 mulw(rd, rs, rt.rm()); 5491 } 5492 } 5493 5494 void MacroAssemblerRiscv64::ma_mulh32(Register rd, Register rs, Operand rt) { 5495 if (rt.is_imm()) { 5496 UseScratchRegisterScope temps(this); 5497 Register scratch = temps.Acquire(); 5498 ma_li(scratch, rt.immediate()); 5499 mul(rd, rs, scratch); 5500 } else { 5501 MOZ_ASSERT(rt.is_reg()); 5502 mul(rd, rs, rt.rm()); 5503 } 5504 srai(rd, rd, 32); 5505 } 5506 5507 void MacroAssemblerRiscv64::ma_mul64(Register rd, Register rs, Operand rt) { 5508 if (rt.is_imm()) { 5509 UseScratchRegisterScope temps(this); 5510 Register scratch = temps.Acquire(); 5511 ma_li(scratch, rt.immediate()); 5512 mul(rd, rs, scratch); 5513 } else { 5514 MOZ_ASSERT(rt.is_reg()); 5515 mul(rd, rs, rt.rm()); 5516 } 5517 } 5518 5519 void MacroAssemblerRiscv64::ma_mulh64(Register rd, Register rs, Operand rt) { 5520 if (rt.is_imm()) { 5521 UseScratchRegisterScope temps(this); 5522 Register scratch = temps.Acquire(); 5523 ma_li(scratch, rt.immediate()); 5524 mulh(rd, rs, scratch); 5525 } else { 5526 MOZ_ASSERT(rt.is_reg()); 5527 mulh(rd, rs, rt.rm()); 5528 } 5529 } 5530 5531 void MacroAssemblerRiscv64::ma_sll64(Register rd, Register rs, Operand rt) { 5532 if (rt.is_reg()) { 5533 sll(rd, rs, rt.rm()); 5534 } else { 5535 MOZ_ASSERT(rt.is_imm()); 5536 uint8_t shamt = static_cast<uint8_t>(rt.immediate()); 5537 slli(rd, rs, shamt); 5538 } 5539 } 5540 5541 void MacroAssemblerRiscv64::ma_sll32(Register rd, Register rs, Operand rt) { 5542 if (rt.is_reg()) { 5543 sllw(rd, rs, rt.rm()); 5544 } else { 5545 MOZ_ASSERT(rt.is_imm()); 5546 uint8_t shamt = static_cast<uint8_t>(rt.immediate()); 5547 slliw(rd, rs, shamt); 5548 } 5549 } 5550 5551 void MacroAssemblerRiscv64::ma_sra64(Register rd, Register rs, Operand rt) { 5552 if (rt.is_reg()) { 5553 sra(rd, rs, rt.rm()); 5554 } else { 5555 MOZ_ASSERT(rt.is_imm()); 5556 uint8_t shamt = static_cast<uint8_t>(rt.immediate()); 5557 srai(rd, rs, shamt); 5558 } 5559 } 5560 5561 void MacroAssemblerRiscv64::ma_sra32(Register rd, Register rs, Operand rt) { 5562 if (rt.is_reg()) { 5563 sraw(rd, rs, rt.rm()); 5564 } else { 5565 MOZ_ASSERT(rt.is_imm()); 5566 uint8_t shamt = static_cast<uint8_t>(rt.immediate()); 5567 sraiw(rd, rs, shamt); 5568 } 5569 } 5570 5571 void MacroAssemblerRiscv64::ma_srl64(Register rd, Register rs, Operand rt) { 5572 if (rt.is_reg()) { 5573 srl(rd, rs, rt.rm()); 5574 } else { 5575 MOZ_ASSERT(rt.is_imm()); 5576 uint8_t shamt = static_cast<uint8_t>(rt.immediate()); 5577 srli(rd, rs, shamt); 5578 } 5579 } 5580 5581 void MacroAssemblerRiscv64::ma_srl32(Register rd, Register rs, Operand rt) { 5582 if (rt.is_reg()) { 5583 srlw(rd, rs, rt.rm()); 5584 } else { 5585 MOZ_ASSERT(rt.is_imm()); 5586 uint8_t shamt = static_cast<uint8_t>(rt.immediate()); 5587 srliw(rd, rs, shamt); 5588 } 5589 } 5590 5591 void MacroAssemblerRiscv64::ma_slt(Register rd, Register rs, Operand rt) { 5592 if (rt.is_reg()) { 5593 slt(rd, rs, rt.rm()); 5594 } else { 5595 MOZ_ASSERT(rt.is_imm()); 5596 if (is_int12(rt.immediate())) { 5597 slti(rd, rs, static_cast<int32_t>(rt.immediate())); 5598 } else { 5599 // li handles the relocation. 5600 UseScratchRegisterScope temps(this); 5601 Register scratch = temps.Acquire(); 5602 BlockTrampolinePoolScope block_trampoline_pool(this, 9); 5603 ma_li(scratch, rt.immediate()); 5604 slt(rd, rs, scratch); 5605 } 5606 } 5607 } 5608 5609 void MacroAssemblerRiscv64::ma_sltu(Register rd, Register rs, Operand rt) { 5610 if (rt.is_reg()) { 5611 sltu(rd, rs, rt.rm()); 5612 } else { 5613 MOZ_ASSERT(rt.is_imm()); 5614 if (is_int12(rt.immediate())) { 5615 sltiu(rd, rs, static_cast<int32_t>(rt.immediate())); 5616 } else { 5617 // li handles the relocation. 5618 UseScratchRegisterScope temps(this); 5619 Register scratch = temps.Acquire(); 5620 BlockTrampolinePoolScope block_trampoline_pool(this, 9); 5621 ma_li(scratch, rt.immediate()); 5622 sltu(rd, rs, scratch); 5623 } 5624 } 5625 } 5626 5627 void MacroAssemblerRiscv64::ma_sle(Register rd, Register rs, Operand rt) { 5628 if (rt.is_reg()) { 5629 slt(rd, rt.rm(), rs); 5630 } else { 5631 MOZ_ASSERT(rt.is_imm()); 5632 // li handles the relocation. 5633 UseScratchRegisterScope temps(this); 5634 Register scratch = temps.Acquire(); 5635 BlockTrampolinePoolScope block_trampoline_pool(this, 9); 5636 ma_li(scratch, rt.immediate()); 5637 slt(rd, scratch, rs); 5638 } 5639 xori(rd, rd, 1); 5640 } 5641 5642 void MacroAssemblerRiscv64::ma_sleu(Register rd, Register rs, Operand rt) { 5643 if (rt.is_reg()) { 5644 sltu(rd, rt.rm(), rs); 5645 } else { 5646 MOZ_ASSERT(rt.is_imm()); 5647 // li handles the relocation. 5648 UseScratchRegisterScope temps(this); 5649 Register scratch = temps.Acquire(); 5650 BlockTrampolinePoolScope block_trampoline_pool(this, 9); 5651 ma_li(scratch, rt.immediate()); 5652 sltu(rd, scratch, rs); 5653 } 5654 xori(rd, rd, 1); 5655 } 5656 5657 void MacroAssemblerRiscv64::ma_sgt(Register rd, Register rs, Operand rt) { 5658 if (rt.is_reg()) { 5659 slt(rd, rt.rm(), rs); 5660 } else { 5661 MOZ_ASSERT(rt.is_imm()); 5662 // li handles the relocation. 5663 UseScratchRegisterScope temps(this); 5664 Register scratch = temps.Acquire(); 5665 BlockTrampolinePoolScope block_trampoline_pool(this, 9); 5666 ma_li(scratch, rt.immediate()); 5667 slt(rd, scratch, rs); 5668 } 5669 } 5670 5671 void MacroAssemblerRiscv64::ma_sgtu(Register rd, Register rs, Operand rt) { 5672 if (rt.is_reg()) { 5673 sltu(rd, rt.rm(), rs); 5674 } else { 5675 MOZ_ASSERT(rt.is_imm()); 5676 // li handles the relocation. 5677 UseScratchRegisterScope temps(this); 5678 Register scratch = temps.Acquire(); 5679 BlockTrampolinePoolScope block_trampoline_pool(this, 9); 5680 ma_li(scratch, rt.immediate()); 5681 sltu(rd, scratch, rs); 5682 } 5683 } 5684 5685 void MacroAssemblerRiscv64::ma_sge(Register rd, Register rs, Operand rt) { 5686 ma_slt(rd, rs, rt); 5687 xori(rd, rd, 1); 5688 } 5689 5690 void MacroAssemblerRiscv64::ma_sgeu(Register rd, Register rs, Operand rt) { 5691 ma_sltu(rd, rs, rt); 5692 xori(rd, rd, 1); 5693 } 5694 5695 static inline bool IsZero(const Operand& rt) { 5696 if (rt.is_reg()) { 5697 return rt.rm() == zero_reg; 5698 } else { 5699 MOZ_ASSERT(rt.is_imm()); 5700 return rt.immediate() == 0; 5701 } 5702 } 5703 5704 void MacroAssemblerRiscv64::ma_seq(Register rd, Register rs, Operand rt) { 5705 if (rs == zero_reg) { 5706 ma_seqz(rd, rt); 5707 } else if (IsZero(rt)) { 5708 seqz(rd, rs); 5709 } else { 5710 ma_sub64(rd, rs, rt); 5711 seqz(rd, rd); 5712 } 5713 } 5714 5715 void MacroAssemblerRiscv64::ma_sne(Register rd, Register rs, Operand rt) { 5716 if (rs == zero_reg) { 5717 ma_snez(rd, rt); 5718 } else if (IsZero(rt)) { 5719 snez(rd, rs); 5720 } else { 5721 ma_sub64(rd, rs, rt); 5722 snez(rd, rd); 5723 } 5724 } 5725 5726 void MacroAssemblerRiscv64::ma_seqz(Register rd, const Operand& rt) { 5727 if (rt.is_reg()) { 5728 seqz(rd, rt.rm()); 5729 } else { 5730 ma_li(rd, rt.immediate() == 0); 5731 } 5732 } 5733 5734 void MacroAssemblerRiscv64::ma_snez(Register rd, const Operand& rt) { 5735 if (rt.is_reg()) { 5736 snez(rd, rt.rm()); 5737 } else { 5738 ma_li(rd, rt.immediate() != 0); 5739 } 5740 } 5741 5742 void MacroAssemblerRiscv64::ma_neg(Register rd, const Operand& rt) { 5743 MOZ_ASSERT(rt.is_reg()); 5744 neg(rd, rt.rm()); 5745 } 5746 5747 void MacroAssemblerRiscv64::ma_jump(ImmPtr dest) { 5748 DEBUG_PRINTF("[ %s\n", __FUNCTION__); 5749 BlockTrampolinePoolScope block_trampoline_pool(this, 8); 5750 UseScratchRegisterScope temps(this); 5751 Register scratch = temps.Acquire(); 5752 ma_liPatchable(scratch, dest); 5753 jr(scratch, 0); 5754 DEBUG_PRINTF("]\n"); 5755 } 5756 // fp instructions 5757 void MacroAssemblerRiscv64::ma_lid(FloatRegister dest, double value) { 5758 ImmWord imm(mozilla::BitwiseCast<uint64_t>(value)); 5759 5760 if (imm.value != 0) { 5761 UseScratchRegisterScope temps(this); 5762 Register scratch = temps.Acquire(); 5763 ma_li(scratch, imm); 5764 fmv_d_x(dest, scratch); 5765 } else { 5766 fmv_d_x(dest, zero); 5767 } 5768 } 5769 // fp instructions 5770 void MacroAssemblerRiscv64::ma_lis(FloatRegister dest, float value) { 5771 Imm32 imm(mozilla::BitwiseCast<uint32_t>(value)); 5772 5773 if (imm.value != 0) { 5774 UseScratchRegisterScope temps(this); 5775 Register scratch = temps.Acquire(); 5776 ma_li(scratch, imm); 5777 fmv_w_x(dest, scratch); 5778 } else { 5779 fmv_w_x(dest, zero); 5780 } 5781 } 5782 5783 void MacroAssemblerRiscv64::ma_sub32TestOverflow(Register rd, Register rj, 5784 Register rk, Label* overflow) { 5785 UseScratchRegisterScope temps(this); 5786 Register scratch = temps.Acquire(); 5787 sub(scratch, rj, rk); 5788 subw(rd, rj, rk); 5789 ma_b(rd, Register(scratch), overflow, Assembler::NotEqual); 5790 } 5791 5792 void MacroAssemblerRiscv64::ma_sub32TestOverflow(Register rd, Register rj, 5793 Imm32 imm, Label* overflow) { 5794 if (imm.value != INT32_MIN) { 5795 ma_add32TestOverflow(rd, rj, Imm32(-imm.value), overflow); 5796 } else { 5797 UseScratchRegisterScope temps(this); 5798 Register scratch = temps.Acquire(); 5799 MOZ_ASSERT(rj != scratch); 5800 ma_li(scratch, Imm32(imm.value)); 5801 ma_sub32TestOverflow(rd, rj, scratch, overflow); 5802 } 5803 } 5804 5805 void MacroAssemblerRiscv64::ma_add32TestOverflow(Register rd, Register rj, 5806 Register rk, Label* overflow) { 5807 UseScratchRegisterScope temps(this); 5808 Register scratch = temps.Acquire(); 5809 add(scratch, rj, rk); 5810 addw(rd, rj, rk); 5811 ma_b(rd, Register(scratch), overflow, Assembler::NotEqual); 5812 } 5813 5814 void MacroAssemblerRiscv64::ma_add32TestOverflow(Register rd, Register rj, 5815 Imm32 imm, Label* overflow) { 5816 // Check for signed range because of addi 5817 if (is_intn(imm.value, 12)) { 5818 UseScratchRegisterScope temps(this); 5819 Register scratch = temps.Acquire(); 5820 addi(scratch, rj, imm.value); 5821 addiw(rd, rj, imm.value); 5822 ma_b(rd, scratch, overflow, Assembler::NotEqual); 5823 } else { 5824 UseScratchRegisterScope temps(this); 5825 Register scratch2 = temps.Acquire(); 5826 ma_li(scratch2, imm); 5827 ma_add32TestOverflow(rd, rj, scratch2, overflow); 5828 } 5829 } 5830 5831 void MacroAssemblerRiscv64::ma_subPtrTestOverflow(Register rd, Register rj, 5832 Register rk, 5833 Label* overflow) { 5834 UseScratchRegisterScope temps(this); 5835 Register scratch2 = temps.Acquire(); 5836 MOZ_ASSERT_IF(rj == rd, rj != rk); 5837 MOZ_ASSERT(rj != scratch2); 5838 MOZ_ASSERT(rk != scratch2); 5839 MOZ_ASSERT(rd != scratch2); 5840 5841 Register rj_copy = rj; 5842 5843 if (rj == rd) { 5844 ma_or(scratch2, rj, zero); 5845 rj_copy = scratch2; 5846 } 5847 5848 { 5849 Register scratch = temps.Acquire(); 5850 MOZ_ASSERT(rd != scratch); 5851 5852 sub(rd, rj, rk); 5853 // If the sign of rj and rk are the same, no overflow 5854 ma_xor(scratch, rj_copy, rk); 5855 // Check if the sign of rd and rj are the same 5856 ma_xor(scratch2, rd, rj_copy); 5857 ma_and(scratch2, scratch2, scratch); 5858 } 5859 5860 ma_b(scratch2, zero, overflow, Assembler::LessThan); 5861 } 5862 5863 void MacroAssemblerRiscv64::ma_addPtrTestOverflow(Register rd, Register rj, 5864 Register rk, 5865 Label* overflow) { 5866 UseScratchRegisterScope temps(this); 5867 Register scratch = temps.Acquire(); 5868 MOZ_ASSERT(rd != scratch); 5869 5870 if (rj == rk) { 5871 if (rj == rd) { 5872 ma_or(scratch, rj, zero); 5873 rj = scratch; 5874 } 5875 5876 add(rd, rj, rj); 5877 ma_xor(scratch, rj, rd); 5878 ma_b(scratch, zero, overflow, Assembler::LessThan); 5879 } else { 5880 UseScratchRegisterScope temps(this); 5881 Register scratch2 = temps.Acquire(); 5882 MOZ_ASSERT(rj != scratch); 5883 MOZ_ASSERT(rd != scratch2); 5884 5885 if (rj == rd) { 5886 ma_or(scratch2, rj, zero); 5887 rj = scratch2; 5888 } 5889 5890 add(rd, rj, rk); 5891 slti(scratch, rj, 0); 5892 slt(scratch2, rd, rk); 5893 ma_b(scratch, Register(scratch2), overflow, Assembler::NotEqual); 5894 } 5895 } 5896 5897 void MacroAssemblerRiscv64::ma_addPtrTestOverflow(Register rd, Register rj, 5898 Imm32 imm, Label* overflow) { 5899 UseScratchRegisterScope temps(this); 5900 Register scratch2 = temps.Acquire(); 5901 5902 if (imm.value == 0) { 5903 ori(rd, rj, 0); 5904 return; 5905 } 5906 5907 if (rj == rd) { 5908 ori(scratch2, rj, 0); 5909 rj = scratch2; 5910 } 5911 5912 ma_add64(rd, rj, imm); 5913 5914 if (imm.value > 0) { 5915 ma_b(rd, rj, overflow, Assembler::LessThan); 5916 } else { 5917 MOZ_ASSERT(imm.value < 0); 5918 ma_b(rd, rj, overflow, Assembler::GreaterThan); 5919 } 5920 } 5921 5922 void MacroAssemblerRiscv64::ma_addPtrTestOverflow(Register rd, Register rj, 5923 ImmWord imm, 5924 Label* overflow) { 5925 UseScratchRegisterScope temps(this); 5926 Register scratch2 = temps.Acquire(); 5927 5928 if (imm.value == 0) { 5929 ori(rd, rj, 0); 5930 return; 5931 } 5932 5933 if (rj == rd) { 5934 MOZ_ASSERT(rj != scratch2); 5935 ori(scratch2, rj, 0); 5936 rj = scratch2; 5937 } 5938 5939 ma_li(rd, imm); 5940 add(rd, rj, rd); 5941 5942 if (imm.value > 0) { 5943 ma_b(rd, rj, overflow, Assembler::LessThan); 5944 } else { 5945 MOZ_ASSERT(imm.value < 0); 5946 ma_b(rd, rj, overflow, Assembler::GreaterThan); 5947 } 5948 } 5949 5950 void MacroAssemblerRiscv64::ma_add32TestCarry(Condition cond, Register rd, 5951 Register rj, Register rk, 5952 Label* overflow) { 5953 MOZ_ASSERT(cond == Assembler::CarrySet || cond == Assembler::CarryClear); 5954 MOZ_ASSERT_IF(rd == rj, rk != rd); 5955 UseScratchRegisterScope temps(this); 5956 Register scratch = temps.Acquire(); 5957 addw(rd, rj, rk); 5958 sltu(scratch, rd, rd == rj ? rk : rj); 5959 ma_b(Register(scratch), Register(scratch), overflow, 5960 cond == Assembler::CarrySet ? Assembler::NonZero : Assembler::Zero); 5961 } 5962 5963 void MacroAssemblerRiscv64::ma_add32TestCarry(Condition cond, Register rd, 5964 Register rj, Imm32 imm, 5965 Label* overflow) { 5966 UseScratchRegisterScope temps(this); 5967 Register scratch2 = temps.Acquire(); 5968 MOZ_ASSERT(rj != scratch2); 5969 ma_li(scratch2, imm); 5970 ma_add32TestCarry(cond, rd, rj, scratch2, overflow); 5971 } 5972 5973 void MacroAssemblerRiscv64::ma_subPtrTestOverflow(Register rd, Register rj, 5974 Imm32 imm, Label* overflow) { 5975 // TODO(riscv): Check subPtrTestOverflow 5976 MOZ_ASSERT(imm.value != INT32_MIN); 5977 ma_addPtrTestOverflow(rd, rj, Imm32(-imm.value), overflow); 5978 } 5979 5980 void MacroAssemblerRiscv64::ma_addPtrTestCarry(Condition cond, Register rd, 5981 Register rj, Register rk, 5982 Label* label) { 5983 UseScratchRegisterScope temps(this); 5984 Register scratch = temps.Acquire(); 5985 MOZ_ASSERT(rd != rk); 5986 MOZ_ASSERT(rd != scratch); 5987 add(rd, rj, rk); 5988 sltu(scratch, rd, rk); 5989 ma_b(scratch, Register(scratch), label, 5990 cond == Assembler::CarrySet ? Assembler::NonZero : Assembler::Zero); 5991 } 5992 5993 void MacroAssemblerRiscv64::ma_addPtrTestCarry(Condition cond, Register rd, 5994 Register rj, Imm32 imm, 5995 Label* label) { 5996 UseScratchRegisterScope temps(this); 5997 Register scratch2 = temps.Acquire(); 5998 5999 // Check for signed range because of addi 6000 if (is_intn(imm.value, 12)) { 6001 addi(rd, rj, imm.value); 6002 sltiu(scratch2, rd, imm.value); 6003 ma_b(scratch2, scratch2, label, 6004 cond == Assembler::CarrySet ? Assembler::NonZero : Assembler::Zero); 6005 } else { 6006 ma_li(scratch2, imm); 6007 ma_addPtrTestCarry(cond, rd, rj, scratch2, label); 6008 } 6009 } 6010 6011 void MacroAssemblerRiscv64::ma_addPtrTestCarry(Condition cond, Register rd, 6012 Register rj, ImmWord imm, 6013 Label* label) { 6014 UseScratchRegisterScope temps(this); 6015 Register scratch2 = temps.Acquire(); 6016 6017 // Check for signed range because of addi_d 6018 if (is_intn(imm.value, 12)) { 6019 uint32_t value = imm.value; 6020 addi(rd, rj, value); 6021 ma_sltu(scratch2, rd, Operand(value)); 6022 ma_b(scratch2, scratch2, label, 6023 cond == Assembler::CarrySet ? Assembler::NonZero : Assembler::Zero); 6024 } else { 6025 ma_li(scratch2, imm); 6026 ma_addPtrTestCarry(cond, rd, rj, scratch2, label); 6027 } 6028 } 6029 6030 void MacroAssemblerRiscv64::ma_addPtrTestSigned(Condition cond, Register rd, 6031 Register rj, Register rk, 6032 Label* taken) { 6033 MOZ_ASSERT(cond == Assembler::Signed || cond == Assembler::NotSigned); 6034 6035 ma_add64(rd, rj, rk); 6036 ma_b(rd, rd, taken, cond); 6037 } 6038 6039 void MacroAssemblerRiscv64::ma_addPtrTestSigned(Condition cond, Register rd, 6040 Register rj, Imm32 imm, 6041 Label* taken) { 6042 MOZ_ASSERT(cond == Assembler::Signed || cond == Assembler::NotSigned); 6043 6044 ma_add64(rd, rj, imm); 6045 ma_b(rd, rd, taken, cond); 6046 } 6047 6048 void MacroAssemblerRiscv64::ma_addPtrTestSigned(Condition cond, Register rd, 6049 Register rj, ImmWord imm, 6050 Label* taken) { 6051 MOZ_ASSERT(cond == Assembler::Signed || cond == Assembler::NotSigned); 6052 6053 ma_add64(rd, rj, Operand(imm.value)); 6054 ma_b(rd, rd, taken, cond); 6055 } 6056 6057 FaultingCodeOffset MacroAssemblerRiscv64::ma_load( 6058 Register dest, const BaseIndex& src, LoadStoreSize size, 6059 LoadStoreExtension extension) { 6060 UseScratchRegisterScope temps(this); 6061 Register scratch2 = temps.Acquire(); 6062 computeScaledAddress(src, scratch2); 6063 return ma_load(dest, Address(scratch2, src.offset), size, extension); 6064 } 6065 void MacroAssemblerRiscv64::ma_pop(FloatRegister f) { 6066 if (f.isDouble()) { 6067 fld(f, StackPointer, 0); 6068 } else { 6069 MOZ_ASSERT(f.isSingle(), "simd128 is not supported"); 6070 flw(f, StackPointer, 0); 6071 } 6072 // See also MacroAssemblerRiscv64::ma_push -- Free space for double even when 6073 // storing a float. 6074 addi(StackPointer, StackPointer, sizeof(double)); 6075 } 6076 6077 void MacroAssemblerRiscv64::ma_push(FloatRegister f) { 6078 // We allocate space for double even when storing a float. 6079 addi(StackPointer, StackPointer, (int32_t)-sizeof(double)); 6080 if (f.isDouble()) { 6081 fsd(f, StackPointer, 0); 6082 } else { 6083 MOZ_ASSERT(f.isSingle(), "simd128 is not supported"); 6084 fsw(f, StackPointer, 0); 6085 } 6086 } 6087 6088 FaultingCodeOffset MacroAssemblerRiscv64::ma_fld_s(FloatRegister ft, 6089 Address address) { 6090 int32_t offset = address.offset; 6091 Register base = address.base; 6092 6093 FaultingCodeOffset fco; 6094 if (is_intn(offset, 12)) { 6095 fco = FaultingCodeOffset(currentOffset()); 6096 flw(ft, base, offset); 6097 } else { 6098 UseScratchRegisterScope temps(this); 6099 Register scratch = temps.Acquire(); 6100 MOZ_ASSERT(base != scratch); 6101 ma_li(scratch, Imm32(offset)); 6102 ma_add64(scratch, base, scratch); 6103 fco = FaultingCodeOffset(currentOffset()); 6104 flw(ft, scratch, 0); 6105 } 6106 return fco; 6107 } 6108 FaultingCodeOffset MacroAssemblerRiscv64::ma_fld_d(FloatRegister ft, 6109 Address address) { 6110 int32_t offset = address.offset; 6111 Register base = address.base; 6112 6113 FaultingCodeOffset fco; 6114 if (is_intn(offset, 12)) { 6115 fco = FaultingCodeOffset(currentOffset()); 6116 fld(ft, base, offset); 6117 } else { 6118 UseScratchRegisterScope temps(this); 6119 Register scratch = temps.Acquire(); 6120 MOZ_ASSERT(base != scratch); 6121 ma_li(scratch, Imm32(offset)); 6122 ma_add64(scratch, base, scratch); 6123 fco = FaultingCodeOffset(currentOffset()); 6124 fld(ft, scratch, 0); 6125 } 6126 return fco; 6127 } 6128 FaultingCodeOffset MacroAssemblerRiscv64::ma_fst_d(FloatRegister ft, 6129 Address address) { 6130 int32_t offset = address.offset; 6131 Register base = address.base; 6132 6133 FaultingCodeOffset fco; 6134 if (is_intn(offset, 12)) { 6135 fco = FaultingCodeOffset(currentOffset()); 6136 fsd(ft, base, offset); 6137 } else { 6138 UseScratchRegisterScope temps(this); 6139 Register scratch = temps.Acquire(); 6140 MOZ_ASSERT(base != scratch); 6141 ma_li(scratch, Imm32(offset)); 6142 ma_add64(scratch, base, scratch); 6143 fco = FaultingCodeOffset(currentOffset()); 6144 fsd(ft, scratch, 0); 6145 } 6146 return fco; 6147 } 6148 FaultingCodeOffset MacroAssemblerRiscv64::ma_fst_s(FloatRegister ft, 6149 Address address) { 6150 int32_t offset = address.offset; 6151 Register base = address.base; 6152 FaultingCodeOffset fco; 6153 if (is_intn(offset, 12)) { 6154 fco = FaultingCodeOffset(currentOffset()); 6155 fsw(ft, base, offset); 6156 } else { 6157 UseScratchRegisterScope temps(this); 6158 Register scratch = temps.Acquire(); 6159 MOZ_ASSERT(base != scratch); 6160 ma_li(scratch, Imm32(offset)); 6161 ma_add64(scratch, base, scratch); 6162 fco = FaultingCodeOffset(currentOffset()); 6163 fsw(ft, scratch, 0); 6164 } 6165 return fco; 6166 } 6167 6168 FaultingCodeOffset MacroAssemblerRiscv64::ma_fst_d(FloatRegister ft, 6169 BaseIndex address) { 6170 UseScratchRegisterScope temps(this); 6171 Register scratch = temps.Acquire(); 6172 computeScaledAddress(address, scratch); 6173 FaultingCodeOffset fco = FaultingCodeOffset(currentOffset()); 6174 ma_fst_d(ft, Address(scratch, address.offset)); 6175 return fco; 6176 } 6177 6178 FaultingCodeOffset MacroAssemblerRiscv64::ma_fst_s(FloatRegister ft, 6179 BaseIndex address) { 6180 UseScratchRegisterScope temps(this); 6181 Register scratch = temps.Acquire(); 6182 computeScaledAddress(address, scratch); 6183 FaultingCodeOffset fco = FaultingCodeOffset(currentOffset()); 6184 ma_fst_s(ft, Address(scratch, address.offset)); 6185 return fco; 6186 } 6187 6188 void MacroAssemblerRiscv64::ma_fld_d(FloatRegister ft, const BaseIndex& src) { 6189 UseScratchRegisterScope temps(this); 6190 Register scratch = temps.Acquire(); 6191 computeScaledAddress(src, scratch); 6192 ma_fld_d(ft, Address(scratch, src.offset)); 6193 } 6194 6195 void MacroAssemblerRiscv64::ma_fld_s(FloatRegister ft, const BaseIndex& src) { 6196 UseScratchRegisterScope temps(this); 6197 Register scratch = temps.Acquire(); 6198 computeScaledAddress(src, scratch); 6199 ma_fld_s(ft, Address(scratch, src.offset)); 6200 } 6201 6202 void MacroAssemblerRiscv64::ma_call(ImmPtr dest) { 6203 DEBUG_PRINTF("[ %s\n", __FUNCTION__); 6204 BlockTrampolinePoolScope block_trampoline_pool(this, 8); 6205 UseScratchRegisterScope temps(this); 6206 temps.Exclude(GeneralRegisterSet(1 << CallReg.code())); 6207 ma_liPatchable(CallReg, dest); 6208 jalr(CallReg, 0); 6209 DEBUG_PRINTF("]\n"); 6210 } 6211 6212 void MacroAssemblerRiscv64::CompareIsNotNanF32(Register rd, FPURegister cmp1, 6213 FPURegister cmp2) { 6214 feq_s(rd, cmp1, cmp1); // rd <- !isNan(cmp1) 6215 if (cmp1 != cmp2) { 6216 UseScratchRegisterScope temps(this); 6217 Register scratch = temps.Acquire(); 6218 6219 feq_s(scratch, cmp2, cmp2); // scratch <- !isNaN(cmp2) 6220 ma_and(rd, rd, scratch); // rd <- !isNan(cmp1) && !isNan(cmp2) 6221 } 6222 } 6223 6224 void MacroAssemblerRiscv64::CompareIsNotNanF64(Register rd, FPURegister cmp1, 6225 FPURegister cmp2) { 6226 feq_d(rd, cmp1, cmp1); // rd <- !isNan(cmp1) 6227 if (cmp1 != cmp2) { 6228 UseScratchRegisterScope temps(this); 6229 Register scratch = temps.Acquire(); 6230 6231 feq_d(scratch, cmp2, cmp2); // scratch <- !isNaN(cmp2) 6232 ma_and(rd, rd, scratch); // rd <- !isNan(cmp1) && !isNan(cmp2) 6233 } 6234 } 6235 6236 void MacroAssemblerRiscv64::CompareIsNanF32(Register rd, FPURegister cmp1, 6237 FPURegister cmp2) { 6238 CompareIsNotNanF32(rd, cmp1, cmp2); // rd <- !isNan(cmp1) && !isNan(cmp2) 6239 ma_xor(rd, rd, Operand(1)); // rd <- isNan(cmp1) || isNan(cmp2) 6240 } 6241 6242 void MacroAssemblerRiscv64::CompareIsNanF64(Register rd, FPURegister cmp1, 6243 FPURegister cmp2) { 6244 CompareIsNotNanF64(rd, cmp1, cmp2); // rd <- !isNan(cmp1) && !isNan(cmp2) 6245 ma_xor(rd, rd, Operand(1)); // rd <- isNan(cmp1) || isNan(cmp2) 6246 } 6247 6248 void MacroAssemblerRiscv64::BranchFloat32(DoubleCondition cc, 6249 FloatRegister frs1, 6250 FloatRegister frs2, Label* L, 6251 JumpKind jumpKind) { 6252 UseScratchRegisterScope temps(this); 6253 Register scratch = temps.Acquire(); 6254 ma_compareF32(scratch, cc, frs1, frs2); 6255 ma_b(scratch, Imm32(0), L, NotEqual, jumpKind); 6256 } 6257 6258 void MacroAssemblerRiscv64::BranchFloat64(DoubleCondition cc, 6259 FloatRegister frs1, 6260 FloatRegister frs2, Label* L, 6261 JumpKind jumpKind) { 6262 UseScratchRegisterScope temps(this); 6263 Register scratch = temps.Acquire(); 6264 ma_compareF64(scratch, cc, frs1, frs2); 6265 ma_b(scratch, Imm32(0), L, NotEqual, jumpKind); 6266 } 6267 6268 void MacroAssemblerRiscv64::Clz32(Register rd, Register xx) { 6269 if (HasZbbExtension()) { 6270 #if JS_CODEGEN_RISCV64 6271 clzw(rd, xx); 6272 #else 6273 clz(rd, xx); 6274 #endif 6275 return; 6276 } 6277 6278 // 32 bit unsigned in lower word: count number of leading zeros. 6279 // int n = 32; 6280 // unsigned y; 6281 6282 // y = x >>16; if (y != 0) { n = n -16; x = y; } 6283 // y = x >> 8; if (y != 0) { n = n - 8; x = y; } 6284 // y = x >> 4; if (y != 0) { n = n - 4; x = y; } 6285 // y = x >> 2; if (y != 0) { n = n - 2; x = y; } 6286 // y = x >> 1; if (y != 0) {rd = n - 2; return;} 6287 // rd = n - x; 6288 6289 Label L0, L1, L2, L3, L4; 6290 UseScratchRegisterScope temps(this); 6291 Register x = rd; 6292 Register y = temps.Acquire(); 6293 Register n = temps.Acquire(); 6294 MOZ_ASSERT(xx != y && xx != n); 6295 mv(x, xx); 6296 ma_li(n, Imm32(32)); 6297 #if JS_CODEGEN_RISCV64 6298 srliw(y, x, 16); 6299 ma_branch(&L0, Equal, y, Operand(zero_reg)); 6300 mv(x, y); 6301 addiw(n, n, -16); 6302 bind(&L0); 6303 srliw(y, x, 8); 6304 ma_branch(&L1, Equal, y, Operand(zero_reg)); 6305 addiw(n, n, -8); 6306 mv(x, y); 6307 bind(&L1); 6308 srliw(y, x, 4); 6309 ma_branch(&L2, Equal, y, Operand(zero_reg)); 6310 addiw(n, n, -4); 6311 mv(x, y); 6312 bind(&L2); 6313 srliw(y, x, 2); 6314 ma_branch(&L3, Equal, y, Operand(zero_reg)); 6315 addiw(n, n, -2); 6316 mv(x, y); 6317 bind(&L3); 6318 srliw(y, x, 1); 6319 subw(rd, n, x); 6320 ma_branch(&L4, Equal, y, Operand(zero_reg)); 6321 addiw(rd, n, -2); 6322 bind(&L4); 6323 #elif JS_CODEGEN_RISCV32 6324 srli(y, x, 16); 6325 ma_branch(&L0, Equal, y, Operand(zero_reg)); 6326 mv(x, y); 6327 addi(n, n, -16); 6328 bind(&L0); 6329 srli(y, x, 8); 6330 ma_branch(&L1, Equal, y, Operand(zero_reg)); 6331 addi(n, n, -8); 6332 mv(x, y); 6333 bind(&L1); 6334 srli(y, x, 4); 6335 ma_branch(&L2, Equal, y, Operand(zero_reg)); 6336 addi(n, n, -4); 6337 mv(x, y); 6338 bind(&L2); 6339 srli(y, x, 2); 6340 ma_branch(&L3, Equal, y, Operand(zero_reg)); 6341 addi(n, n, -2); 6342 mv(x, y); 6343 bind(&L3); 6344 srli(y, x, 1); 6345 sub(rd, n, x); 6346 ma_branch(&L4, Equal, y, Operand(zero_reg)); 6347 addi(rd, n, -2); 6348 bind(&L4); 6349 #endif 6350 } 6351 6352 #if JS_CODEGEN_RISCV64 6353 void MacroAssemblerRiscv64::Clz64(Register rd, Register xx) { 6354 // 64 bit: count number of leading zeros. 6355 // int n = 64; 6356 // unsigned y; 6357 6358 // y = x >>32; if (y != 0) { n = n - 32; x = y; } 6359 // y = x >>16; if (y != 0) { n = n - 16; x = y; } 6360 // y = x >> 8; if (y != 0) { n = n - 8; x = y; } 6361 // y = x >> 4; if (y != 0) { n = n - 4; x = y; } 6362 // y = x >> 2; if (y != 0) { n = n - 2; x = y; } 6363 // y = x >> 1; if (y != 0) {rd = n - 2; return;} 6364 // rd = n - x; 6365 6366 Label L0, L1, L2, L3, L4, L5; 6367 UseScratchRegisterScope temps(this); 6368 Register x = rd; 6369 Register y = temps.Acquire(); 6370 Register n = temps.Acquire(); 6371 MOZ_ASSERT(xx != y && xx != n); 6372 mv(x, xx); 6373 ma_li(n, Imm32(64)); 6374 srli(y, x, 32); 6375 ma_branch(&L0, Equal, y, Operand(zero_reg)); 6376 addiw(n, n, -32); 6377 mv(x, y); 6378 bind(&L0); 6379 srli(y, x, 16); 6380 ma_branch(&L1, Equal, y, Operand(zero_reg)); 6381 addiw(n, n, -16); 6382 mv(x, y); 6383 bind(&L1); 6384 srli(y, x, 8); 6385 ma_branch(&L2, Equal, y, Operand(zero_reg)); 6386 addiw(n, n, -8); 6387 mv(x, y); 6388 bind(&L2); 6389 srli(y, x, 4); 6390 ma_branch(&L3, Equal, y, Operand(zero_reg)); 6391 addiw(n, n, -4); 6392 mv(x, y); 6393 bind(&L3); 6394 srli(y, x, 2); 6395 ma_branch(&L4, Equal, y, Operand(zero_reg)); 6396 addiw(n, n, -2); 6397 mv(x, y); 6398 bind(&L4); 6399 srli(y, x, 1); 6400 subw(rd, n, x); 6401 ma_branch(&L5, Equal, y, Operand(zero_reg)); 6402 addiw(rd, n, -2); 6403 bind(&L5); 6404 } 6405 #endif 6406 void MacroAssemblerRiscv64::Ctz32(Register rd, Register rs) { 6407 if (HasZbbExtension()) { 6408 #if JS_CODEGEN_RISCV64 6409 ctzw(rd, rs); 6410 #else 6411 ctz(rd, rs); 6412 #endif 6413 return; 6414 } 6415 6416 // Convert trailing zeroes to trailing ones, and bits to their left 6417 // to zeroes. 6418 6419 { 6420 UseScratchRegisterScope temps(this); 6421 Register scratch = temps.Acquire(); 6422 ma_add64(scratch, rs, Operand(-1)); 6423 ma_xor(rd, scratch, rs); 6424 ma_and(rd, rd, scratch); 6425 // Count number of leading zeroes. 6426 } 6427 Clz32(rd, rd); 6428 { 6429 // Subtract number of leading zeroes from 32 to get number of trailing 6430 // ones. Remember that the trailing ones were formerly trailing zeroes. 6431 UseScratchRegisterScope temps(this); 6432 Register scratch = temps.Acquire(); 6433 ma_li(scratch, Imm32(32)); 6434 ma_sub32(rd, scratch, rd); 6435 } 6436 } 6437 #if JS_CODEGEN_RISCV64 6438 void MacroAssemblerRiscv64::Ctz64(Register rd, Register rs) { 6439 if (HasZbbExtension()) { 6440 ctz(rd, rs); 6441 return; 6442 } 6443 6444 // Convert trailing zeroes to trailing ones, and bits to their left 6445 // to zeroes. 6446 { 6447 UseScratchRegisterScope temps(this); 6448 Register scratch = temps.Acquire(); 6449 ma_add64(scratch, rs, Operand(-1)); 6450 ma_xor(rd, scratch, rs); 6451 ma_and(rd, rd, scratch); 6452 // Count number of leading zeroes. 6453 } 6454 Clz64(rd, rd); 6455 { 6456 // Subtract number of leading zeroes from 64 to get number of trailing 6457 // ones. Remember that the trailing ones were formerly trailing zeroes. 6458 UseScratchRegisterScope temps(this); 6459 Register scratch = temps.Acquire(); 6460 ma_li(scratch, 64); 6461 ma_sub64(rd, scratch, rd); 6462 } 6463 } 6464 #endif 6465 void MacroAssemblerRiscv64::Popcnt32(Register rd, Register rs, 6466 Register scratch) { 6467 if (HasZbbExtension()) { 6468 #if JS_CODEGEN_RISCV64 6469 cpopw(rd, rs); 6470 #else 6471 cpop(rd, rs); 6472 #endif 6473 return; 6474 } 6475 6476 MOZ_ASSERT(scratch != rs); 6477 MOZ_ASSERT(scratch != rd); 6478 // https://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetParallel 6479 // 6480 // A generalization of the best bit counting method to integers of 6481 // bit-widths up to 128 (parameterized by type T) is this: 6482 // 6483 // v = v - ((v >> 1) & (T)~(T)0/3); // temp 6484 // v = (v & (T)~(T)0/15*3) + ((v >> 2) & (T)~(T)0/15*3); // temp 6485 // v = (v + (v >> 4)) & (T)~(T)0/255*15; // temp 6486 // c = (T)(v * ((T)~(T)0/255)) >> (sizeof(T) - 1) * BITS_PER_BYTE; //count 6487 // 6488 // There are algorithms which are faster in the cases where very few 6489 // bits are set but the algorithm here attempts to minimize the total 6490 // number of instructions executed even when a large number of bits 6491 // are set. 6492 // The number of instruction is 20. 6493 // uint32_t B0 = 0x55555555; // (T)~(T)0/3 6494 // uint32_t B1 = 0x33333333; // (T)~(T)0/15*3 6495 // uint32_t B2 = 0x0F0F0F0F; // (T)~(T)0/255*15 6496 // uint32_t value = 0x01010101; // (T)~(T)0/255 6497 6498 uint32_t shift = 24; 6499 UseScratchRegisterScope temps(this); 6500 Register scratch2 = temps.Acquire(); 6501 Register value = temps.Acquire(); 6502 MOZ_ASSERT((rd != value) && (rs != value)); 6503 ma_li(value, 0x01010101); // value = 0x01010101; 6504 ma_li(scratch2, 0x55555555); // B0 = 0x55555555; 6505 ma_srl32(scratch, rs, Operand(1)); 6506 ma_and(scratch, scratch, scratch2); 6507 ma_sub32(scratch, rs, scratch); 6508 ma_li(scratch2, 0x33333333); // B1 = 0x33333333; 6509 slli(rd, scratch2, 4); 6510 or_(scratch2, scratch2, rd); 6511 ma_and(rd, scratch, scratch2); 6512 ma_srl32(scratch, scratch, Operand(2)); 6513 ma_and(scratch, scratch, scratch2); 6514 ma_add32(scratch, rd, scratch); 6515 ma_srl32(rd, scratch, Operand(4)); 6516 ma_add32(rd, rd, scratch); 6517 ma_li(scratch2, 0xF); 6518 ma_mul32(scratch2, value, scratch2); // B2 = 0x0F0F0F0F; 6519 ma_and(rd, rd, scratch2); 6520 ma_mul32(rd, rd, value); 6521 ma_srl32(rd, rd, Operand(shift)); 6522 } 6523 6524 #if JS_CODEGEN_RISCV64 6525 void MacroAssemblerRiscv64::Popcnt64(Register rd, Register rs, 6526 Register scratch) { 6527 if (HasZbbExtension()) { 6528 cpop(rd, rs); 6529 return; 6530 } 6531 6532 MOZ_ASSERT(scratch != rs); 6533 MOZ_ASSERT(scratch != rd); 6534 // uint64_t B0 = 0x5555555555555555l; // (T)~(T)0/3 6535 // uint64_t B1 = 0x3333333333333333l; // (T)~(T)0/15*3 6536 // uint64_t B2 = 0x0F0F0F0F0F0F0F0Fl; // (T)~(T)0/255*15 6537 // uint64_t value = 0x0101010101010101l; // (T)~(T)0/255 6538 // uint64_t shift = 24; // (sizeof(T) - 1) * BITS_PER_BYTE 6539 uint64_t shift = 24; 6540 UseScratchRegisterScope temps(this); 6541 Register scratch2 = temps.Acquire(); 6542 Register value = temps.Acquire(); 6543 MOZ_ASSERT((rd != value) && (rs != value)); 6544 ma_li(value, 0x1111111111111111l); // value = 0x1111111111111111l; 6545 ma_li(scratch2, 5); 6546 ma_mul64(scratch2, value, scratch2); // B0 = 0x5555555555555555l; 6547 ma_srl64(scratch, rs, Operand(1)); 6548 ma_and(scratch, scratch, scratch2); 6549 ma_sub64(scratch, rs, scratch); 6550 ma_li(scratch2, 3); 6551 ma_mul64(scratch2, value, scratch2); // B1 = 0x3333333333333333l; 6552 ma_and(rd, scratch, scratch2); 6553 ma_srl64(scratch, scratch, Operand(2)); 6554 ma_and(scratch, scratch, scratch2); 6555 ma_add64(scratch, rd, scratch); 6556 ma_srl64(rd, scratch, Operand(4)); 6557 ma_add64(rd, rd, scratch); 6558 ma_li(scratch2, 0xF); 6559 ma_li(value, 0x0101010101010101l); // value = 0x0101010101010101l; 6560 ma_mul64(scratch2, value, scratch2); // B2 = 0x0F0F0F0F0F0F0F0Fl; 6561 ma_and(rd, rd, scratch2); 6562 ma_mul64(rd, rd, value); 6563 srli(rd, rd, 32 + shift); 6564 } 6565 #endif 6566 6567 void MacroAssemblerRiscv64::ma_mod_mask(Register src, Register dest, 6568 Register hold, Register remain, 6569 int32_t shift, Label* negZero) { 6570 // MATH: 6571 // We wish to compute x % (1<<y) - 1 for a known constant, y. 6572 // First, let b = (1<<y) and C = (1<<y)-1, then think of the 32 bit 6573 // dividend as a number in base b, namely 6574 // c_0*1 + c_1*b + c_2*b^2 ... c_n*b^n 6575 // now, since both addition and multiplication commute with modulus, 6576 // x % C == (c_0 + c_1*b + ... + c_n*b^n) % C == 6577 // (c_0 % C) + (c_1%C) * (b % C) + (c_2 % C) * (b^2 % C)... 6578 // now, since b == C + 1, b % C == 1, and b^n % C == 1 6579 // this means that the whole thing simplifies to: 6580 // c_0 + c_1 + c_2 ... c_n % C 6581 // each c_n can easily be computed by a shift/bitextract, and the modulus 6582 // can be maintained by simply subtracting by C whenever the number gets 6583 // over C. 6584 int32_t mask = (1 << shift) - 1; 6585 Label head, negative, sumSigned, done; 6586 6587 // hold holds -1 if the value was negative, 1 otherwise. 6588 // remain holds the remaining bits that have not been processed 6589 // scratch2 serves as a temporary location to store extracted bits 6590 // into as well as holding the trial subtraction as a temp value dest is 6591 // the accumulator (and holds the final result) 6592 6593 // move the whole value into the remain. 6594 or_(remain, src, zero); 6595 // Zero out the dest. 6596 ma_li(dest, Imm32(0)); 6597 // Set the hold appropriately. 6598 ma_b(remain, remain, &negative, Signed, ShortJump); 6599 ma_li(hold, Imm32(1)); 6600 ma_branch(&head, ShortJump); 6601 6602 bind(&negative); 6603 ma_li(hold, Imm32(-1)); 6604 negw(remain, remain); 6605 6606 // Begin the main loop. 6607 bind(&head); 6608 6609 UseScratchRegisterScope temps(this); 6610 Register scratch2 = temps.Acquire(); 6611 // Extract the bottom bits into scratch2. 6612 ma_and(scratch2, remain, Imm32(mask)); 6613 // Add those bits to the accumulator. 6614 addw(dest, dest, scratch2); 6615 // Do a trial subtraction 6616 ma_sub32(scratch2, dest, Imm32(mask)); 6617 // If (sum - C) > 0, store sum - C back into sum, thus performing a 6618 // modulus. 6619 ma_b(scratch2, Register(scratch2), &sumSigned, Signed, ShortJump); 6620 or_(dest, scratch2, zero); 6621 bind(&sumSigned); 6622 // Get rid of the bits that we extracted before. 6623 srliw(remain, remain, shift); 6624 // If the shift produced zero, finish, otherwise, continue in the loop. 6625 ma_b(remain, remain, &head, NonZero, ShortJump); 6626 // Check the hold to see if we need to negate the result. 6627 ma_b(hold, hold, &done, NotSigned, ShortJump); 6628 6629 if (negZero != nullptr) { 6630 // Jump out in case of negative zero. 6631 ma_b(dest, dest, negZero, Zero); 6632 } 6633 // If the hold was non-zero, negate the result to be in line with 6634 // what JS wants 6635 negw(dest, dest); 6636 6637 bind(&done); 6638 } 6639 6640 void MacroAssemblerRiscv64::ma_fmovz(FloatFormat fmt, FloatRegister fd, 6641 FloatRegister fj, Register rk) { 6642 Label done; 6643 ma_b(rk, zero, &done, Assembler::NotEqual); 6644 if (fmt == SingleFloat) { 6645 fmv_s(fd, fj); 6646 } else { 6647 fmv_d(fd, fj); 6648 } 6649 bind(&done); 6650 } 6651 6652 void MacroAssemblerRiscv64::ByteSwap(Register rd, Register rs, int operand_size, 6653 Register scratch) { 6654 MOZ_ASSERT(operand_size == 4 || operand_size == 8); 6655 #if JS_CODEGEN_RISCV64 6656 if (HasZbbExtension()) { 6657 rev8(rd, rs); 6658 if (operand_size == 4) { 6659 srai(rd, rd, 32); 6660 } 6661 return; 6662 } 6663 #endif 6664 6665 MOZ_ASSERT(scratch != rs); 6666 MOZ_ASSERT(scratch != rd); 6667 if (operand_size == 4) { 6668 // Uint32_t x1 = 0x00FF00FF; 6669 // x0 = (x0 << 16 | x0 >> 16); 6670 // x0 = (((x0 & x1) << 8) | ((x0 & (x1 << 8)) >> 8)); 6671 UseScratchRegisterScope temps(this); 6672 BlockTrampolinePoolScope block_trampoline_pool(this, 17); 6673 MOZ_ASSERT((rd != t6) && (rs != t6)); 6674 Register x0 = temps.Acquire(); 6675 Register x1 = temps.Acquire(); 6676 Register x2 = scratch; 6677 RV_li(x1, 0x00FF00FF); 6678 slliw(x0, rs, 16); 6679 srliw(rd, rs, 16); 6680 or_(x0, rd, x0); // x0 <- x0 << 16 | x0 >> 16 6681 and_(x2, x0, x1); // x2 <- x0 & 0x00FF00FF 6682 slliw(x2, x2, 8); // x2 <- (x0 & x1) << 8 6683 slliw(x1, x1, 8); // x1 <- 0xFF00FF00 6684 and_(rd, x0, x1); // x0 & 0xFF00FF00 6685 srliw(rd, rd, 8); 6686 or_(rd, rd, x2); // (((x0 & x1) << 8) | ((x0 & (x1 << 8)) >> 8)) 6687 } else { 6688 // uinx24_t x1 = 0x0000FFFF0000FFFFl; 6689 // uinx24_t x1 = 0x00FF00FF00FF00FFl; 6690 // x0 = (x0 << 32 | x0 >> 32); 6691 // x0 = (x0 & x1) << 16 | (x0 & (x1 << 16)) >> 16; 6692 // x0 = (x0 & x1) << 8 | (x0 & (x1 << 8)) >> 8; 6693 UseScratchRegisterScope temps(this); 6694 BlockTrampolinePoolScope block_trampoline_pool(this, 30); 6695 MOZ_ASSERT((rd != t6) && (rs != t6)); 6696 Register x0 = temps.Acquire(); 6697 Register x1 = temps.Acquire(); 6698 Register x2 = scratch; 6699 RV_li(x1, 0x0000FFFF0000FFFFl); 6700 slli(x0, rs, 32); 6701 srli(rd, rs, 32); 6702 or_(x0, rd, x0); // x0 <- x0 << 32 | x0 >> 32 6703 and_(x2, x0, x1); // x2 <- x0 & 0x0000FFFF0000FFFF 6704 slli(x2, x2, 16); // x2 <- (x0 & 0x0000FFFF0000FFFF) << 16 6705 slli(x1, x1, 16); // x1 <- 0xFFFF0000FFFF0000 6706 and_(rd, x0, x1); // rd <- x0 & 0xFFFF0000FFFF0000 6707 srli(rd, rd, 16); // rd <- x0 & (x1 << 16)) >> 16 6708 or_(x0, rd, x2); // (x0 & x1) << 16 | (x0 & (x1 << 16)) >> 16; 6709 RV_li(x1, 0x00FF00FF00FF00FFl); 6710 and_(x2, x0, x1); // x2 <- x0 & 0x00FF00FF00FF00FF 6711 slli(x2, x2, 8); // x2 <- (x0 & x1) << 8 6712 slli(x1, x1, 8); // x1 <- 0xFF00FF00FF00FF00 6713 and_(rd, x0, x1); 6714 srli(rd, rd, 8); // rd <- (x0 & (x1 << 8)) >> 8 6715 or_(rd, rd, x2); // (((x0 & x1) << 8) | ((x0 & (x1 << 8)) >> 8)) 6716 } 6717 } 6718 6719 template <typename F_TYPE> 6720 void MacroAssemblerRiscv64::FloatMinMaxHelper(FPURegister dst, FPURegister src1, 6721 FPURegister src2, 6722 MaxMinKind kind) { 6723 MOZ_ASSERT((std::is_same<F_TYPE, float>::value) || 6724 (std::is_same<F_TYPE, double>::value)); 6725 6726 if (src1 == src2) { 6727 if (dst != src1) { 6728 if (std::is_same<float, F_TYPE>::value) { 6729 fmv_s(dst, src1); 6730 } else { 6731 fmv_d(dst, src1); 6732 } 6733 } 6734 return; 6735 } 6736 6737 Label done, nan; 6738 6739 // For RISCV, fmin_s returns the other non-NaN operand as result if only one 6740 // operand is NaN; but for JS, if any operand is NaN, result is Nan. The 6741 // following handles the discrepency between handling of NaN between ISA and 6742 // JS semantics 6743 if (std::is_same<float, F_TYPE>::value) { 6744 BranchFloat32(Assembler::DoubleUnordered, src1, src2, &nan, ShortJump); 6745 } else { 6746 BranchFloat64(Assembler::DoubleUnordered, src1, src2, &nan, ShortJump); 6747 } 6748 6749 if (kind == MaxMinKind::kMax) { 6750 if (std::is_same<float, F_TYPE>::value) { 6751 fmax_s(dst, src1, src2); 6752 } else { 6753 fmax_d(dst, src1, src2); 6754 } 6755 } else { 6756 if (std::is_same<float, F_TYPE>::value) { 6757 fmin_s(dst, src1, src2); 6758 } else { 6759 fmin_d(dst, src1, src2); 6760 } 6761 } 6762 jump(&done); 6763 6764 bind(&nan); 6765 // if any operand is NaN, return NaN (fadd returns NaN if any operand is NaN) 6766 if (std::is_same<float, F_TYPE>::value) { 6767 fadd_s(dst, src1, src2); 6768 } else { 6769 fadd_d(dst, src1, src2); 6770 } 6771 6772 bind(&done); 6773 } 6774 6775 void MacroAssemblerRiscv64::Float32Max(FPURegister dst, FPURegister src1, 6776 FPURegister src2) { 6777 comment(__FUNCTION__); 6778 FloatMinMaxHelper<float>(dst, src1, src2, MaxMinKind::kMax); 6779 } 6780 6781 void MacroAssemblerRiscv64::Float32Min(FPURegister dst, FPURegister src1, 6782 FPURegister src2) { 6783 comment(__FUNCTION__); 6784 FloatMinMaxHelper<float>(dst, src1, src2, MaxMinKind::kMin); 6785 } 6786 6787 void MacroAssemblerRiscv64::Float64Max(FPURegister dst, FPURegister src1, 6788 FPURegister src2) { 6789 comment(__FUNCTION__); 6790 FloatMinMaxHelper<double>(dst, src1, src2, MaxMinKind::kMax); 6791 } 6792 6793 void MacroAssemblerRiscv64::Float64Min(FPURegister dst, FPURegister src1, 6794 FPURegister src2) { 6795 comment(__FUNCTION__); 6796 FloatMinMaxHelper<double>(dst, src1, src2, MaxMinKind::kMin); 6797 } 6798 6799 void MacroAssemblerRiscv64::Rol(Register rd, Register rs, const Operand& rt) { 6800 if (rt.is_reg()) { 6801 UseScratchRegisterScope temps(this); 6802 Register scratch = temps.Acquire(); 6803 6804 negw(scratch, rt.rm()); 6805 srlw(scratch, rs, scratch); 6806 sllw(rd, rs, rt.rm()); 6807 or_(rd, scratch, rd); 6808 sext_w(rd, rd); 6809 } else { 6810 Ror(rd, rs, Operand(32 - (rt.immediate() & 0x1f))); 6811 } 6812 } 6813 6814 void MacroAssemblerRiscv64::Ror(Register rd, Register rs, const Operand& rt) { 6815 if (HasZbbExtension()) { 6816 if (rt.is_reg()) { 6817 rorw(rd, rs, rt.rm()); 6818 } else { 6819 int64_t ror_value = rt.immediate() % 32; 6820 if (ror_value < 0) { 6821 ror_value += 32; 6822 } 6823 roriw(rd, rs, ror_value); 6824 } 6825 return; 6826 } 6827 UseScratchRegisterScope temps(this); 6828 Register scratch = temps.Acquire(); 6829 if (rt.is_reg()) { 6830 negw(scratch, rt.rm()); 6831 sllw(scratch, rs, scratch); 6832 srlw(rd, rs, rt.rm()); 6833 or_(rd, scratch, rd); 6834 sext_w(rd, rd); 6835 } else { 6836 int64_t ror_value = rt.immediate() & 0x1f; 6837 if (ror_value == 0) { 6838 mv(rd, rs); 6839 return; 6840 } 6841 srliw(scratch, rs, ror_value); 6842 slliw(rd, rs, 32 - ror_value); 6843 or_(rd, scratch, rd); 6844 sext_w(rd, rd); 6845 } 6846 } 6847 6848 void MacroAssemblerRiscv64::Drol(Register rd, Register rs, const Operand& rt) { 6849 if (rt.is_reg()) { 6850 UseScratchRegisterScope temps(this); 6851 Register scratch = temps.Acquire(); 6852 6853 negw(scratch, rt.rm()); 6854 srl(scratch, rs, scratch); 6855 sll(rd, rs, rt.rm()); 6856 or_(rd, scratch, rd); 6857 } else { 6858 Dror(rd, rs, Operand(64 - (rt.immediate() & 0x3f))); 6859 } 6860 } 6861 6862 void MacroAssemblerRiscv64::Dror(Register rd, Register rs, const Operand& rt) { 6863 if (HasZbbExtension()) { 6864 if (rt.is_reg()) { 6865 ror(rd, rs, rt.rm()); 6866 } else { 6867 int64_t dror_value = rt.immediate() % 64; 6868 if (dror_value < 0) { 6869 dror_value += 64; 6870 } 6871 rori(rd, rs, dror_value); 6872 } 6873 return; 6874 } 6875 UseScratchRegisterScope temps(this); 6876 Register scratch = temps.Acquire(); 6877 if (rt.is_reg()) { 6878 negw(scratch, rt.rm()); 6879 sll(scratch, rs, scratch); 6880 srl(rd, rs, rt.rm()); 6881 or_(rd, scratch, rd); 6882 } else { 6883 int64_t dror_value = rt.immediate() & 0x3f; 6884 if (dror_value == 0) { 6885 mv(rd, rs); 6886 return; 6887 } 6888 srli(scratch, rs, dror_value); 6889 slli(rd, rs, 64 - dror_value); 6890 or_(rd, scratch, rd); 6891 } 6892 } 6893 6894 void MacroAssemblerRiscv64::wasmLoadImpl(const wasm::MemoryAccessDesc& access, 6895 Register memoryBase, Register ptr, 6896 Register ptrScratch, 6897 AnyRegister output, Register tmp) { 6898 access.assertOffsetInGuardPages(); 6899 uint32_t offset = access.offset32(); 6900 MOZ_ASSERT_IF(offset, ptrScratch != InvalidReg); 6901 6902 // Maybe add the offset. 6903 if (offset) { 6904 asMasm().addPtr(ImmWord(offset), ptrScratch); 6905 ptr = ptrScratch; 6906 } 6907 6908 asMasm().memoryBarrierBefore(access.sync()); 6909 UseScratchRegisterScope temps(this); 6910 Register scratch = temps.Acquire(); 6911 FaultingCodeOffset fco; 6912 switch (access.type()) { 6913 case Scalar::Int8: 6914 add(scratch, memoryBase, ptr); 6915 fco = FaultingCodeOffset(currentOffset()); 6916 lb(output.gpr(), scratch, 0); 6917 break; 6918 case Scalar::Uint8: 6919 add(scratch, memoryBase, ptr); 6920 fco = FaultingCodeOffset(currentOffset()); 6921 lbu(output.gpr(), scratch, 0); 6922 break; 6923 case Scalar::Int16: 6924 add(scratch, memoryBase, ptr); 6925 fco = FaultingCodeOffset(currentOffset()); 6926 lh(output.gpr(), scratch, 0); 6927 break; 6928 case Scalar::Uint16: 6929 add(scratch, memoryBase, ptr); 6930 fco = FaultingCodeOffset(currentOffset()); 6931 lhu(output.gpr(), scratch, 0); 6932 break; 6933 case Scalar::Int32: 6934 add(scratch, memoryBase, ptr); 6935 fco = FaultingCodeOffset(currentOffset()); 6936 lw(output.gpr(), scratch, 0); 6937 break; 6938 case Scalar::Uint32: 6939 add(scratch, memoryBase, ptr); 6940 fco = FaultingCodeOffset(currentOffset()); 6941 lwu(output.gpr(), scratch, 0); 6942 break; 6943 case Scalar::Float64: 6944 add(scratch, memoryBase, ptr); 6945 fco = FaultingCodeOffset(currentOffset()); 6946 fld(output.fpu(), scratch, 0); 6947 break; 6948 case Scalar::Float32: 6949 add(scratch, memoryBase, ptr); 6950 fco = FaultingCodeOffset(currentOffset()); 6951 flw(output.fpu(), scratch, 0); 6952 break; 6953 default: 6954 MOZ_CRASH("unexpected array type"); 6955 } 6956 6957 append(access, js::wasm::TrapMachineInsnForLoad(access.byteSize()), fco); 6958 asMasm().memoryBarrierAfter(access.sync()); 6959 } 6960 6961 void MacroAssemblerRiscv64::wasmStoreImpl(const wasm::MemoryAccessDesc& access, 6962 AnyRegister value, 6963 Register memoryBase, Register ptr, 6964 Register ptrScratch, Register tmp) { 6965 access.assertOffsetInGuardPages(); 6966 uint32_t offset = access.offset32(); 6967 MOZ_ASSERT_IF(offset, ptrScratch != InvalidReg); 6968 6969 // Maybe add the offset. 6970 if (offset) { 6971 asMasm().addPtr(ImmWord(offset), ptrScratch); 6972 ptr = ptrScratch; 6973 } 6974 6975 unsigned byteSize = access.byteSize(); 6976 bool isSigned = Scalar::isSignedIntType(access.type()); 6977 bool isFloat = Scalar::isFloatingType(access.type()); 6978 6979 BaseIndex address(memoryBase, ptr, TimesOne); 6980 asMasm().memoryBarrierBefore(access.sync()); 6981 FaultingCodeOffset fco; 6982 if (isFloat) { 6983 if (byteSize == 4) { 6984 fco = ma_fst_s(value.fpu(), address); 6985 } else { 6986 fco = ma_fst_d(value.fpu(), address); 6987 } 6988 } else { 6989 fco = 6990 ma_store(value.gpr(), address, static_cast<LoadStoreSize>(8 * byteSize), 6991 isSigned ? SignExtend : ZeroExtend); 6992 } 6993 // Only the last emitted instruction is a memory access. 6994 append(access, js::wasm::TrapMachineInsnForStore(access.byteSize()), fco); 6995 asMasm().memoryBarrierAfter(access.sync()); 6996 } 6997 6998 void MacroAssemblerRiscv64::GenPCRelativeJumpAndLink(Register rd, 6999 int32_t imm32) { 7000 MOZ_ASSERT(is_int32(imm32 + 0x800)); 7001 int32_t Hi20 = ((imm32 + 0x800) >> 12); 7002 int32_t Lo12 = imm32 << 20 >> 20; 7003 auipc(rd, Hi20); // Read PC + Hi20 into scratch. 7004 jalr(rd, Lo12); // jump PC + Hi20 + Lo12 7005 } 7006 7007 CodeOffset MacroAssemblerRiscv64::BranchAndLinkShortHelper(int32_t offset, 7008 Label* L) { 7009 MOZ_ASSERT(L == nullptr || offset == 0); 7010 BlockTrampolinePoolScope block_trampoline_pool(this, 2, 1); 7011 offset = GetOffset(offset, L, OffsetSize::kOffset21); 7012 return jal(offset); 7013 } 7014 7015 CodeOffset MacroAssemblerRiscv64::BranchAndLinkShort(int32_t offset) { 7016 MOZ_ASSERT(is_int21(offset)); 7017 return BranchAndLinkShortHelper(offset, nullptr); 7018 } 7019 7020 CodeOffset MacroAssemblerRiscv64::BranchAndLinkShort(Label* L) { 7021 return BranchAndLinkShortHelper(0, L); 7022 } 7023 7024 CodeOffset MacroAssemblerRiscv64::BranchAndLink(Label* L) { 7025 if (L->bound() && !is_near(L)) { 7026 return BranchAndLinkLong(L); 7027 } 7028 return BranchAndLinkShort(L); 7029 } 7030 7031 void MacroAssemblerRiscv64::ma_fmv_d(FloatRegister src, ValueOperand dest) { 7032 fmv_x_d(dest.valueReg(), src); 7033 } 7034 7035 void MacroAssemblerRiscv64::ma_fmv_d(ValueOperand src, FloatRegister dest) { 7036 fmv_d_x(dest, src.valueReg()); 7037 } 7038 7039 void MacroAssemblerRiscv64::ma_fmv_w(FloatRegister src, ValueOperand dest) { 7040 fmv_x_w(dest.valueReg(), src); 7041 } 7042 7043 void MacroAssemblerRiscv64::ma_fmv_w(ValueOperand src, FloatRegister dest) { 7044 fmv_w_x(dest, src.valueReg()); 7045 } 7046 7047 } // namespace jit 7048 } // namespace js