WasmBaselineCompile.cpp (411152B)
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 * 4 * Copyright 2016 Mozilla Foundation 5 * 6 * Licensed under the Apache License, Version 2.0 (the "License"); 7 * you may not use this file except in compliance with the License. 8 * You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, software 13 * distributed under the License is distributed on an "AS IS" BASIS, 14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 * See the License for the specific language governing permissions and 16 * limitations under the License. 17 */ 18 19 /* 20 * [SMDOC] WebAssembly baseline compiler (RabaldrMonkey) 21 * 22 * For now, see WasmBCClass.h for general comments about the compiler's 23 * structure. 24 * 25 * ---------------- 26 * 27 * General assumptions for 32-bit vs 64-bit code: 28 * 29 * - A 32-bit register can be extended in-place to a 64-bit register on 64-bit 30 * systems. 31 * 32 * - Code that knows that Register64 has a '.reg' member on 64-bit systems and 33 * '.high' and '.low' members on 32-bit systems, or knows the implications 34 * thereof, is #ifdef JS_PUNBOX64. All other code is #if(n)?def JS_64BIT. 35 * 36 * Coding standards are a little fluid: 37 * 38 * - In "small" code generating functions (eg emitMultiplyF64, emitQuotientI32, 39 * and surrounding functions; most functions fall into this class) where the 40 * meaning is obvious: 41 * 42 * Old school: 43 * - if there is a single source + destination register, it is called 'r' 44 * - if there is one source and a different destination, they are called 'rs' 45 * and 'rd' 46 * - if there is one source + destination register and another source register 47 * they are called 'r' and 'rs' 48 * - if there are two source registers and a destination register they are 49 * called 'rs0', 'rs1', and 'rd'. 50 * 51 * The new thing: 52 * - what is called 'r' in the old-school naming scheme is increasingly called 53 * 'rsd' in source+dest cases. 54 * 55 * - Generic temp registers are named /temp[0-9]?/ not /tmp[0-9]?/. 56 * 57 * - Registers can be named non-generically for their function ('rp' for the 58 * 'pointer' register and 'rv' for the 'value' register are typical) and those 59 * names may or may not have an 'r' prefix. 60 * 61 * - "Larger" code generating functions make their own rules. 62 */ 63 64 /* 65 * [SMDOC] WebAssembly baseline compiler -- Lazy Tier-Up mechanism 66 * 67 * For baseline functions, we compile in code to monitor the function's 68 * "hotness" and request tier-up once that hotness crosses a threshold. 69 * 70 * (1) Each function has an associated int32_t counter, 71 * FuncDefInstanceData::hotnessCounter. These are stored in an array in 72 * the Instance. Hence access to them is fast and thread-local. 73 * 74 * (2) On instantiation, the counters are set to some positive number 75 * (Instance::init, Instance::computeInitialHotnessCounter), which is a 76 * very crude estimate of the cost of Ion compilation of the function. 77 * 78 * (3) In baseline compilation, a function decrements its counter at every 79 * entry (BaseCompiler::beginFunction) and at the start of every loop 80 * iteration (BaseCompiler::emitLoop). The decrement code is created by 81 * BaseCompiler::addHotnessCheck. 82 * 83 * (4) The decrement is by some value in the range 1 .. 127, as computed from 84 * the function or loop-body size, by BlockSizeToDownwardsStep. 85 * 86 * (5) For loops, the body size is known only at the end of the loop, but the 87 * check is required at the start of the body. Hence the value is patched 88 * in at the end (BaseCompiler::emitEnd, case LabelKind::Loop). 89 * 90 * (6) BaseCompiler::addHotnessCheck creates the shortest possible 91 * decrement/check code, to minimise both time and code-space overhead. On 92 * Intel it is only two instructions. The counter has the value from (4) 93 * subtracted from it. If the result is negative, we jump to OOL code 94 * (class OutOfLineRequestTierUp) which requests tier up; control then 95 * continues immediately after the check. 96 * 97 * (7) The OOL tier-up request code calls the stub pointed to by 98 * Instance::requestTierUpStub_. This always points to the stub created by 99 * GenerateRequestTierUpStub. This saves all registers and calls onwards 100 * to WasmHandleRequestTierUp in C++-land. 101 * 102 * (8) WasmHandleRequestTierUp figures out which function in which Instance is 103 * requesting tier-up. It sets the function's counter (1) to the largest 104 * possible value, which is 2^31-1. It then calls onwards to 105 * Code::requestTierUp, which requests off-thread Ion compilation of the 106 * function, then immediately returns. 107 * 108 * (9) It is important that (8) sets the counter to 2^31-1 (as close to 109 * infinity as possible). This is because it may be arbitrarily long 110 * before the optimised code becomes available. In the meantime the 111 * baseline version of the function will continue to run. We do not want 112 * it to make frequent duplicate requests for tier-up. Although a request 113 * for tier-up is relatively cheap (a few hundred instructions), it is 114 * still way more expensive than the fast-case for a hotness check (2 insns 115 * on Intel), and performance of the baseline code will be badly affected 116 * if it makes many duplicate requests. 117 * 118 * (10) Of course it is impossible to *guarantee* that a baseline function will 119 * not make a duplicate request, because the Ion compilation of the 120 * function could take arbitrarily long, or even fail completely (eg OOM). 121 * Hence it is necessary for WasmCode::requestTierUp (8) to detect and 122 * ignore duplicate requests. 123 * 124 * (11) Each Instance of a Module runs in its own thread and has its own array 125 * of counters. This makes the counter updating thread-local and cheap. 126 * But it means that, if a Module has multiple threads (Instances), it 127 * could be that a function never gets hot enough to request tier up, 128 * because it is not hot enough in any single thread, even though the 129 * total hotness summed across all threads is enough to request tier up. 130 * Whether this inaccuracy is a problem in practice remains to be seen. 131 * 132 * (12) Code::requestTierUp (8) creates a PartialTier2CompileTask and queues it 133 * for execution. It does not do the compilation itself. 134 * 135 * (13) A PartialTier2CompileTask's runHelperThreadTask (running on a helper 136 * thread) calls CompilePartialTier2. This compiles the function with Ion 137 * and racily updates the tiering table entry for the function, which 138 * lives in Code::jumpTables_::tiering_. 139 * 140 * (14) Subsequent calls to the function's baseline entry points will then jump 141 * to the Ion version of the function. Hence lazy tier-up is achieved. 142 */ 143 144 #include "wasm/WasmBaselineCompile.h" 145 146 #include "wasm/WasmAnyRef.h" 147 #include "wasm/WasmBCClass.h" 148 #include "wasm/WasmBCDefs.h" 149 #include "wasm/WasmBCFrame.h" 150 #include "wasm/WasmBCRegDefs.h" 151 #include "wasm/WasmBCStk.h" 152 #include "wasm/WasmValType.h" 153 154 #include "jit/MacroAssembler-inl.h" 155 #include "wasm/WasmBCClass-inl.h" 156 #include "wasm/WasmBCCodegen-inl.h" 157 #include "wasm/WasmBCRegDefs-inl.h" 158 #include "wasm/WasmBCRegMgmt-inl.h" 159 #include "wasm/WasmBCStkMgmt-inl.h" 160 161 namespace js { 162 namespace wasm { 163 164 using namespace js::jit; 165 166 using mozilla::Maybe; 167 using mozilla::Nothing; 168 using mozilla::Some; 169 170 //////////////////////////////////////////////////////////// 171 // 172 // Out of line code management. 173 174 // The baseline compiler will use OOL code more sparingly than Ion since our 175 // code is not high performance and frills like code density and branch 176 // prediction friendliness will be less important. 177 class OutOfLineCode : public TempObject { 178 private: 179 NonAssertingLabel entry_; 180 NonAssertingLabel rejoin_; 181 StackHeight stackHeight_; 182 183 public: 184 OutOfLineCode() : stackHeight_(StackHeight::Invalid()) {} 185 186 Label* entry() { return &entry_; } 187 Label* rejoin() { return &rejoin_; } 188 189 void setStackHeight(StackHeight stackHeight) { 190 MOZ_ASSERT(!stackHeight_.isValid()); 191 stackHeight_ = stackHeight; 192 } 193 194 void bind(BaseStackFrame* fr, MacroAssembler* masm) { 195 MOZ_ASSERT(stackHeight_.isValid()); 196 masm->bind(&entry_); 197 fr->setStackHeight(stackHeight_); 198 } 199 200 // The generate() method must be careful about register use because it will be 201 // invoked when there is a register assignment in the BaseCompiler that does 202 // not correspond to the available registers when the generated OOL code is 203 // executed. The register allocator *must not* be called. 204 // 205 // The best strategy is for the creator of the OOL object to allocate all 206 // temps that the OOL code will need. 207 // 208 // Input, output, and temp registers are embedded in the OOL object and are 209 // known to the code generator. 210 // 211 // Scratch registers are available to use in OOL code. 212 // 213 // All other registers must be explicitly saved and restored by the OOL code 214 // before being used. 215 216 virtual void generate(MacroAssembler* masm) = 0; 217 }; 218 219 class OutOfLineAbortingTrap : public OutOfLineCode { 220 Trap trap_; 221 TrapSiteDesc desc_; 222 223 public: 224 OutOfLineAbortingTrap(Trap trap, const TrapSiteDesc& desc) 225 : trap_(trap), desc_(desc) {} 226 227 virtual void generate(MacroAssembler* masm) override { 228 masm->wasmTrap(trap_, desc_); 229 MOZ_ASSERT(!rejoin()->bound()); 230 } 231 }; 232 233 class OutOfLineResumableTrap : public OutOfLineCode { 234 Trap trap_; 235 TrapSiteDesc desc_; 236 wasm::StackMap* stackMap_; 237 wasm::StackMaps* stackMaps_; 238 239 public: 240 OutOfLineResumableTrap(Trap trap, const TrapSiteDesc& desc, 241 wasm::StackMap* stackMap, wasm::StackMaps* stackMaps) 242 : trap_(trap), desc_(desc), stackMap_(stackMap), stackMaps_(stackMaps) {} 243 244 virtual void generate(MacroAssembler* masm) override { 245 masm->wasmTrap(trap_, desc_); 246 if (stackMap_ && !stackMaps_->add(masm->currentOffset(), stackMap_)) { 247 masm->setOOM(); 248 } 249 masm->jump(rejoin()); 250 } 251 }; 252 253 OutOfLineCode* BaseCompiler::addOutOfLineCode(OutOfLineCode* ool) { 254 if (!ool || !outOfLine_.append(ool)) { 255 return nullptr; 256 } 257 ool->setStackHeight(fr.stackHeight()); 258 return ool; 259 } 260 261 bool BaseCompiler::generateOutOfLineCode() { 262 for (auto* ool : outOfLine_) { 263 if (!ool->entry()->used()) { 264 continue; 265 } 266 ool->bind(&fr, &masm); 267 ool->generate(&masm); 268 } 269 270 return !masm.oom(); 271 } 272 273 ////////////////////////////////////////////////////////////////////////////// 274 // 275 // Sundry code generation. 276 277 bool BaseCompiler::addInterruptCheck() { 278 #ifdef RABALDR_PIN_INSTANCE 279 Register tmp(InstanceReg); 280 #else 281 ScratchI32 tmp(*this); 282 fr.loadInstancePtr(tmp); 283 #endif 284 Label ok; 285 masm.branch32(Assembler::Equal, 286 Address(tmp, wasm::Instance::offsetOfInterrupt()), Imm32(0), 287 &ok); 288 trap(wasm::Trap::CheckInterrupt); 289 masm.bind(&ok); 290 return createStackMap("addInterruptCheck"); 291 } 292 293 void BaseCompiler::checkDivideByZero(RegI32 rhs) { 294 Label nonZero; 295 masm.branchTest32(Assembler::NonZero, rhs, rhs, &nonZero); 296 trap(Trap::IntegerDivideByZero); 297 masm.bind(&nonZero); 298 } 299 300 void BaseCompiler::checkDivideByZero(RegI64 r) { 301 Label nonZero; 302 ScratchI32 scratch(*this); 303 masm.branchTest64(Assembler::NonZero, r, r, scratch, &nonZero); 304 trap(Trap::IntegerDivideByZero); 305 masm.bind(&nonZero); 306 } 307 308 void BaseCompiler::checkDivideSignedOverflow(RegI32 rhs, RegI32 srcDest, 309 Label* done, bool zeroOnOverflow) { 310 Label notMin; 311 masm.branch32(Assembler::NotEqual, srcDest, Imm32(INT32_MIN), ¬Min); 312 if (zeroOnOverflow) { 313 masm.branch32(Assembler::NotEqual, rhs, Imm32(-1), ¬Min); 314 moveImm32(0, srcDest); 315 masm.jump(done); 316 } else { 317 masm.branch32(Assembler::NotEqual, rhs, Imm32(-1), ¬Min); 318 trap(Trap::IntegerOverflow); 319 } 320 masm.bind(¬Min); 321 } 322 323 void BaseCompiler::checkDivideSignedOverflow(RegI64 rhs, RegI64 srcDest, 324 Label* done, bool zeroOnOverflow) { 325 Label notmin; 326 masm.branch64(Assembler::NotEqual, srcDest, Imm64(INT64_MIN), ¬min); 327 masm.branch64(Assembler::NotEqual, rhs, Imm64(-1), ¬min); 328 if (zeroOnOverflow) { 329 masm.xor64(srcDest, srcDest); 330 masm.jump(done); 331 } else { 332 trap(Trap::IntegerOverflow); 333 } 334 masm.bind(¬min); 335 } 336 337 void BaseCompiler::jumpTable(const LabelVector& labels, Label* theTable) { 338 // Flush constant pools to ensure that the table is never interrupted by 339 // constant pool entries. 340 masm.flush(); 341 342 #if defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_ARM64) 343 // Prevent nop sequences to appear in the jump table. 344 AutoForbidNops afn(&masm); 345 #endif 346 masm.bind(theTable); 347 348 for (const auto& label : labels) { 349 CodeLabel cl; 350 masm.writeCodePointer(&cl); 351 cl.target()->bind(label.offset()); 352 masm.addCodeLabel(cl); 353 } 354 } 355 356 void BaseCompiler::tableSwitch(Label* theTable, RegI32 switchValue, 357 Label* dispatchCode) { 358 masm.bind(dispatchCode); 359 360 #if defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_X86) 361 ScratchI32 scratch(*this); 362 CodeLabel tableCl; 363 364 masm.mov(&tableCl, scratch); 365 366 tableCl.target()->bind(theTable->offset()); 367 masm.addCodeLabel(tableCl); 368 369 masm.jmp(Operand(scratch, switchValue, ScalePointer)); 370 #elif defined(JS_CODEGEN_ARM) 371 // Flush constant pools: offset must reflect the distance from the MOV 372 // to the start of the table; as the address of the MOV is given by the 373 // label, nothing must come between the bind() and the ma_mov(). 374 AutoForbidPoolsAndNops afp(&masm, 375 /* number of instructions in scope = */ 5); 376 377 ScratchI32 scratch(*this); 378 379 // Compute the offset from the ma_mov instruction to the jump table. 380 Label here; 381 masm.bind(&here); 382 uint32_t offset = here.offset() - theTable->offset(); 383 384 // Read PC+8 385 masm.ma_mov(pc, scratch); 386 387 // ARM scratch register is required by ma_sub. 388 ScratchRegisterScope arm_scratch(*this); 389 390 // Compute the absolute table base pointer into `scratch`, offset by 8 391 // to account for the fact that ma_mov read PC+8. 392 masm.ma_sub(Imm32(offset + 8), scratch, arm_scratch); 393 394 // Jump indirect via table element. 395 masm.ma_ldr(DTRAddr(scratch, DtrRegImmShift(switchValue, LSL, 2)), pc, Offset, 396 Assembler::Always); 397 #elif defined(JS_CODEGEN_MIPS64) || defined(JS_CODEGEN_LOONG64) || \ 398 defined(JS_CODEGEN_RISCV64) 399 ScratchI32 scratch(*this); 400 CodeLabel tableCl; 401 402 masm.ma_li(scratch, &tableCl); 403 404 tableCl.target()->bind(theTable->offset()); 405 masm.addCodeLabel(tableCl); 406 407 masm.branchToComputedAddress(BaseIndex(scratch, switchValue, ScalePointer)); 408 #elif defined(JS_CODEGEN_ARM64) 409 AutoForbidPoolsAndNops afp(&masm, 410 /* number of instructions in scope = */ 4); 411 412 ScratchI32 scratch(*this); 413 414 ARMRegister s(scratch, 64); 415 ARMRegister v(switchValue, 64); 416 masm.Adr(s, theTable); 417 masm.Add(s, s, Operand(v, vixl::LSL, 3)); 418 masm.Ldr(s, MemOperand(s, 0)); 419 masm.Br(s); 420 #else 421 MOZ_CRASH("BaseCompiler platform hook: tableSwitch"); 422 #endif 423 } 424 425 // Helpers for accessing the "baseline scratch" areas: all targets 426 void BaseCompiler::stashWord(RegPtr instancePtr, size_t index, RegPtr r) { 427 MOZ_ASSERT(r != instancePtr); 428 MOZ_ASSERT(index < Instance::N_BASELINE_SCRATCH_WORDS); 429 masm.storePtr(r, 430 Address(instancePtr, Instance::offsetOfBaselineScratchWords() + 431 index * sizeof(uintptr_t))); 432 } 433 434 void BaseCompiler::unstashWord(RegPtr instancePtr, size_t index, RegPtr r) { 435 MOZ_ASSERT(index < Instance::N_BASELINE_SCRATCH_WORDS); 436 masm.loadPtr(Address(instancePtr, Instance::offsetOfBaselineScratchWords() + 437 index * sizeof(uintptr_t)), 438 r); 439 } 440 441 // Helpers for accessing the "baseline scratch" areas: X86 only 442 #ifdef JS_CODEGEN_X86 443 void BaseCompiler::stashI64(RegPtr regForInstance, RegI64 r) { 444 static_assert(sizeof(uintptr_t) == 4); 445 MOZ_ASSERT(Instance::sizeOfBaselineScratchWords() >= 8); 446 MOZ_ASSERT(regForInstance != r.low && regForInstance != r.high); 447 # ifdef RABALDR_PIN_INSTANCE 448 # error "Pinned instance not expected" 449 # endif 450 fr.loadInstancePtr(regForInstance); 451 masm.store32( 452 r.low, Address(regForInstance, Instance::offsetOfBaselineScratchWords())); 453 masm.store32(r.high, Address(regForInstance, 454 Instance::offsetOfBaselineScratchWords() + 4)); 455 } 456 457 void BaseCompiler::unstashI64(RegPtr regForInstance, RegI64 r) { 458 static_assert(sizeof(uintptr_t) == 4); 459 MOZ_ASSERT(Instance::sizeOfBaselineScratchWords() >= 8); 460 # ifdef RABALDR_PIN_INSTANCE 461 # error "Pinned instance not expected" 462 # endif 463 fr.loadInstancePtr(regForInstance); 464 if (regForInstance == r.low) { 465 masm.load32( 466 Address(regForInstance, Instance::offsetOfBaselineScratchWords() + 4), 467 r.high); 468 masm.load32( 469 Address(regForInstance, Instance::offsetOfBaselineScratchWords()), 470 r.low); 471 } else { 472 masm.load32( 473 Address(regForInstance, Instance::offsetOfBaselineScratchWords()), 474 r.low); 475 masm.load32( 476 Address(regForInstance, Instance::offsetOfBaselineScratchWords() + 4), 477 r.high); 478 } 479 } 480 #endif 481 482 // Given the bytecode size of a block (a complete function body, or a loop 483 // body), return the required downwards step for the associated hotness 484 // counter. Returned value will be in 1 .. 127 inclusive. 485 static uint32_t BlockSizeToDownwardsStep(size_t blockBytecodeSize) { 486 MOZ_RELEASE_ASSERT(blockBytecodeSize <= size_t(MaxFunctionBytes)); 487 const uint32_t BYTECODES_PER_STEP = 20; // tunable parameter 488 size_t step = blockBytecodeSize / BYTECODES_PER_STEP; 489 step = std::max<uint32_t>(step, 1); 490 step = std::min<uint32_t>(step, 127); 491 return uint32_t(step); 492 } 493 494 ////////////////////////////////////////////////////////////////////////////// 495 // 496 // Function entry and exit 497 498 bool BaseCompiler::beginFunction() { 499 AutoCreatedBy acb(masm, "(wasm)BaseCompiler::beginFunction"); 500 501 JitSpew(JitSpew_Codegen, "# ========================================"); 502 JitSpew(JitSpew_Codegen, "# Emitting wasm baseline code"); 503 JitSpew(JitSpew_Codegen, 504 "# beginFunction: start of function prologue for index %d", 505 (int)func_.index); 506 507 // Make a start on the stackmap for this function. Inspect the args so 508 // as to determine which of them are both in-memory and pointer-typed, and 509 // add entries to machineStackTracker as appropriate. 510 511 ArgTypeVector args(funcType()); 512 size_t inboundStackArgBytes = StackArgAreaSizeUnaligned(args, ABIKind::Wasm); 513 MOZ_ASSERT(inboundStackArgBytes % sizeof(void*) == 0); 514 stackMapGenerator_.numStackArgBytes = inboundStackArgBytes; 515 516 MOZ_ASSERT(stackMapGenerator_.machineStackTracker.length() == 0); 517 if (!stackMapGenerator_.machineStackTracker.pushNonGCPointers( 518 stackMapGenerator_.numStackArgBytes / sizeof(void*))) { 519 return false; 520 } 521 522 // Identify GC-managed pointers passed on the stack. 523 for (ABIArgIter i(args, ABIKind::Wasm); !i.done(); i++) { 524 ABIArg argLoc = *i; 525 if (argLoc.kind() == ABIArg::Stack && 526 args[i.index()] == MIRType::WasmAnyRef) { 527 uint32_t offset = argLoc.offsetFromArgBase(); 528 MOZ_ASSERT(offset < inboundStackArgBytes); 529 MOZ_ASSERT(offset % sizeof(void*) == 0); 530 stackMapGenerator_.machineStackTracker.setGCPointer(offset / 531 sizeof(void*)); 532 } 533 } 534 535 perfSpewer_.startRecording(); 536 perfSpewer_.markStartOffset(masm.currentOffset()); 537 perfSpewer_.recordOffset(masm, "Prologue"); 538 GenerateFunctionPrologue( 539 masm, CallIndirectId::forFunc(codeMeta_, func_.index), 540 compilerEnv_.mode() != CompileMode::Once ? Some(func_.index) : Nothing(), 541 &offsets_); 542 543 // GenerateFunctionPrologue pushes exactly one wasm::Frame's worth of 544 // stuff, and none of the values are GC pointers. Hence: 545 if (!stackMapGenerator_.machineStackTracker.pushNonGCPointers( 546 sizeof(Frame) / sizeof(void*))) { 547 return false; 548 } 549 550 // Initialize DebugFrame fields before the stack overflow trap so that 551 // we have the invariant that all observable Frames in a debugEnabled 552 // Module have valid DebugFrames. 553 if (compilerEnv_.debugEnabled()) { 554 #ifdef JS_CODEGEN_ARM64 555 static_assert(DebugFrame::offsetOfFrame() % WasmStackAlignment == 0, 556 "aligned"); 557 #endif 558 masm.reserveStack(DebugFrame::offsetOfFrame()); 559 if (!stackMapGenerator_.machineStackTracker.pushNonGCPointers( 560 DebugFrame::offsetOfFrame() / sizeof(void*))) { 561 return false; 562 } 563 564 masm.store32(Imm32(func_.index), Address(masm.getStackPointer(), 565 DebugFrame::offsetOfFuncIndex())); 566 masm.store32(Imm32(0), 567 Address(masm.getStackPointer(), DebugFrame::offsetOfFlags())); 568 569 // No need to initialize cachedReturnJSValue_ or any ref-typed spilled 570 // register results, as they are traced if and only if a corresponding 571 // flag (hasCachedReturnJSValue or hasSpilledRefRegisterResult) is set. 572 } 573 574 // Generate a stack-overflow check and its associated stackmap. 575 576 ExitStubMapVector extras; 577 StackMap* functionEntryStackMap; 578 if (!stackMapGenerator_.generateStackmapEntriesForTrapExit(args, &extras) || 579 !stackMapGenerator_.createStackMap("stack check", extras, 580 HasDebugFrameWithLiveRefs::No, stk_, 581 &functionEntryStackMap)) { 582 return false; 583 } 584 585 OutOfLineCode* oolStackOverflowTrap = 586 addOutOfLineCode(new (alloc_) OutOfLineAbortingTrap( 587 Trap::StackOverflow, 588 TrapSiteDesc(BytecodeOffset(func_.lineOrBytecode)))); 589 if (!oolStackOverflowTrap) { 590 return false; 591 } 592 fr.checkStack(ABINonArgReg0, ABINonArgReg1, oolStackOverflowTrap->entry()); 593 594 OutOfLineCode* oolInterruptTrap = addOutOfLineCode( 595 new (alloc_) OutOfLineResumableTrap(Trap::CheckInterrupt, trapSiteDesc(), 596 functionEntryStackMap, stackMaps_)); 597 if (!oolInterruptTrap) { 598 return false; 599 } 600 masm.branch32(Assembler::NotEqual, 601 Address(InstanceReg, wasm::Instance::offsetOfInterrupt()), 602 Imm32(0), oolInterruptTrap->entry()); 603 masm.bind(oolInterruptTrap->rejoin()); 604 605 size_t reservedBytes = fr.fixedAllocSize() - masm.framePushed(); 606 MOZ_ASSERT(0 == (reservedBytes % sizeof(void*))); 607 608 masm.reserveStack(reservedBytes); 609 fr.onFixedStackAllocated(); 610 if (!stackMapGenerator_.machineStackTracker.pushNonGCPointers( 611 reservedBytes / sizeof(void*))) { 612 return false; 613 } 614 615 // Locals are stack allocated. Mark ref-typed ones in the stackmap 616 // accordingly. 617 for (const Local& l : localInfo_) { 618 // Locals that are stack arguments were already added to the stackmap 619 // before pushing the frame. 620 if (l.type == MIRType::WasmAnyRef && !l.isStackArgument()) { 621 uint32_t offs = fr.localOffsetFromSp(l); 622 MOZ_ASSERT(0 == (offs % sizeof(void*))); 623 stackMapGenerator_.machineStackTracker.setGCPointer(offs / sizeof(void*)); 624 } 625 } 626 627 // Copy arguments from registers to stack. 628 for (ABIArgIter i(args, ABIKind::Wasm); !i.done(); i++) { 629 if (args.isSyntheticStackResultPointerArg(i.index())) { 630 // If there are stack results and the pointer to stack results 631 // was passed in a register, store it to the stack. 632 if (i->argInRegister()) { 633 fr.storeIncomingStackResultAreaPtr(RegPtr(i->gpr())); 634 } 635 // If we're in a debug frame, copy the stack result pointer arg 636 // to a well-known place. 637 if (compilerEnv_.debugEnabled()) { 638 Register target = ABINonArgReturnReg0; 639 fr.loadIncomingStackResultAreaPtr(RegPtr(target)); 640 size_t debugFrameOffset = 641 masm.framePushed() - DebugFrame::offsetOfFrame(); 642 size_t debugStackResultsPointerOffset = 643 debugFrameOffset + DebugFrame::offsetOfStackResultsPointer(); 644 masm.storePtr(target, Address(masm.getStackPointer(), 645 debugStackResultsPointerOffset)); 646 } 647 continue; 648 } 649 if (!i->argInRegister()) { 650 continue; 651 } 652 Local& l = localInfo_[args.naturalIndex(i.index())]; 653 switch (i.mirType()) { 654 case MIRType::Int32: 655 fr.storeLocalI32(RegI32(i->gpr()), l); 656 break; 657 case MIRType::Int64: 658 fr.storeLocalI64(RegI64(i->gpr64()), l); 659 break; 660 case MIRType::WasmAnyRef: { 661 mozilla::DebugOnly<uint32_t> offs = fr.localOffsetFromSp(l); 662 MOZ_ASSERT(0 == (offs % sizeof(void*))); 663 fr.storeLocalRef(RegRef(i->gpr()), l); 664 // We should have just visited this local in the preceding loop. 665 MOZ_ASSERT(stackMapGenerator_.machineStackTracker.isGCPointer( 666 offs / sizeof(void*))); 667 break; 668 } 669 case MIRType::Double: 670 fr.storeLocalF64(RegF64(i->fpu()), l); 671 break; 672 case MIRType::Float32: 673 fr.storeLocalF32(RegF32(i->fpu()), l); 674 break; 675 #ifdef ENABLE_WASM_SIMD 676 case MIRType::Simd128: 677 fr.storeLocalV128(RegV128(i->fpu()), l); 678 break; 679 #endif 680 default: 681 MOZ_CRASH("Function argument type"); 682 } 683 } 684 685 fr.zeroLocals(&ra); 686 fr.storeInstancePtr(InstanceReg); 687 688 if (compilerEnv_.debugEnabled()) { 689 insertBreakablePoint(CallSiteKind::EnterFrame); 690 if (!createStackMap("debug: enter-frame breakpoint")) { 691 return false; 692 } 693 } 694 695 JitSpew(JitSpew_Codegen, 696 "# beginFunction: enter body with masm.framePushed = %u", 697 masm.framePushed()); 698 MOZ_ASSERT(stackMapGenerator_.framePushedAtEntryToBody.isNothing()); 699 stackMapGenerator_.framePushedAtEntryToBody.emplace(masm.framePushed()); 700 701 if (compilerEnv_.mode() == CompileMode::LazyTiering) { 702 size_t funcBytecodeSize = func_.end - func_.begin; 703 uint32_t step = BlockSizeToDownwardsStep(funcBytecodeSize); 704 705 // Create a patchable hotness check and patch it immediately (only because 706 // there's no way to directly create a non-patchable check directly). 707 Maybe<CodeOffset> ctrDecOffset = addHotnessCheck(); 708 if (ctrDecOffset.isNothing()) { 709 return false; 710 } 711 patchHotnessCheck(ctrDecOffset.value(), step); 712 } 713 714 return true; 715 } 716 717 bool BaseCompiler::endFunction() { 718 AutoCreatedBy acb(masm, "(wasm)BaseCompiler::endFunction"); 719 720 JitSpew(JitSpew_Codegen, "# endFunction: start of function epilogue"); 721 722 // Always branch to returnLabel_. 723 masm.breakpoint(); 724 725 // Patch the add in the prologue so that it checks against the correct 726 // frame size. Flush the constant pool in case it needs to be patched. 727 masm.flush(); 728 729 // Precondition for patching. 730 if (masm.oom()) { 731 return false; 732 } 733 734 fr.patchCheckStack(); 735 736 // We could skip generating the epilogue for functions which never return, 737 // but that would mess up invariants that all functions have a return address 738 // offset in CodeRange. It's also a rare thing, so not worth optimizing for. 739 deadCode_ = !returnLabel_.used(); 740 masm.bind(&returnLabel_); 741 742 ResultType resultType(ResultType::Vector(funcType().results())); 743 744 popStackReturnValues(resultType); 745 746 if (compilerEnv_.debugEnabled() && !deadCode_) { 747 // Store and reload the return value from DebugFrame::return so that 748 // it can be clobbered, and/or modified by the debug trap. 749 saveRegisterReturnValues(resultType); 750 insertBreakablePoint(CallSiteKind::Breakpoint); 751 if (!createStackMap("debug: return-point breakpoint", 752 HasDebugFrameWithLiveRefs::Maybe)) { 753 return false; 754 } 755 insertBreakablePoint(CallSiteKind::LeaveFrame); 756 if (!createStackMap("debug: leave-frame breakpoint", 757 HasDebugFrameWithLiveRefs::Maybe)) { 758 return false; 759 } 760 restoreRegisterReturnValues(resultType); 761 } 762 763 #ifndef RABALDR_PIN_INSTANCE 764 // To satisy instance extent invariant we need to reload InstanceReg because 765 // baseline can clobber it. 766 fr.loadInstancePtr(InstanceReg); 767 #endif 768 perfSpewer_.recordOffset(masm, "Epilogue"); 769 GenerateFunctionEpilogue(masm, fr.fixedAllocSize(), &offsets_); 770 771 #if defined(JS_ION_PERF) 772 // FIXME - profiling code missing. No bug for this. 773 774 // Note the end of the inline code and start of the OOL code. 775 // gen->perfSpewer().noteEndInlineCode(masm); 776 #endif 777 JitSpew(JitSpew_Codegen, "# endFunction: end of function epilogue"); 778 779 JitSpew(JitSpew_Codegen, "# endFunction: start of OOL code"); 780 perfSpewer_.recordOffset(masm, "OOLCode"); 781 if (!generateOutOfLineCode()) { 782 return false; 783 } 784 JitSpew(JitSpew_Codegen, "# endFunction: end of OOL code"); 785 786 if (compilerEnv_.debugEnabled()) { 787 JitSpew(JitSpew_Codegen, "# endFunction: start of per-function debug stub"); 788 insertPerFunctionDebugStub(); 789 JitSpew(JitSpew_Codegen, "# endFunction: end of per-function debug stub"); 790 } 791 792 offsets_.end = masm.currentOffset(); 793 794 if (!fr.checkStackHeight()) { 795 return decoder_.fail(decoder_.beginOffset(), "stack frame is too large"); 796 } 797 798 perfSpewer_.endRecording(); 799 800 JitSpew(JitSpew_Codegen, "# endFunction: end of OOL code for index %d", 801 (int)func_.index); 802 return !masm.oom(); 803 } 804 805 ////////////////////////////////////////////////////////////////////////////// 806 // 807 // Debugger API. 808 809 // [SMDOC] Wasm debug traps -- code details 810 // 811 // There are four pieces of code involved. 812 // 813 // (1) The "breakable point". This is placed at every location where we might 814 // want to transfer control to the debugger, most commonly before every 815 // bytecode. It must be as short and fast as possible. It checks 816 // Instance::debugStub_, which is either null or a pointer to (3). If 817 // non-null, a call to (2) is performed; when null, nothing happens. 818 // 819 // (2) The "per function debug stub". There is one per function. It consults 820 // a bit-vector attached to the Instance, to see whether breakpoints for 821 // the current function are enabled. If not, it returns (to (1), hence 822 // having no effect). Otherwise, it jumps (not calls) onwards to (3). 823 // 824 // (3) The "debug stub" -- not to be confused with the "per function debug 825 // stub". There is one per module. This saves all the registers and 826 // calls onwards to (4), which is in C++ land. When that call returns, 827 // (3) itself returns, which transfers control directly back to (after) 828 // (1). 829 // 830 // (4) In C++ land -- WasmHandleDebugTrap, corresponding to 831 // SymbolicAddress::HandleDebugTrap. This contains the detailed logic 832 // needed to handle the breakpoint. 833 834 void BaseCompiler::insertBreakablePoint(CallSiteKind kind) { 835 MOZ_ASSERT(!deadCode_); 836 837 // A sync() must happen before this. The debug stub does not save all live 838 // registers. 839 MOZ_ASSERT(!hasLiveRegsOnStk()); 840 841 #ifndef RABALDR_PIN_INSTANCE 842 fr.loadInstancePtr(InstanceReg); 843 #endif 844 845 // The breakpoint code must call the breakpoint handler installed on the 846 // instance if it is not null. There is one breakable point before 847 // every bytecode, and one at the beginning and at the end of the function. 848 // 849 // There are many constraints: 850 // 851 // - Code should be read-only; we do not want to patch 852 // - The breakpoint code should be as dense as possible, given the volume of 853 // breakable points 854 // - The handler-is-null case should be as fast as we can make it 855 // 856 // The scratch register is available here. 857 // 858 // An unconditional callout would be densest but is too slow. The best 859 // balance results from an inline test for null with a conditional call. The 860 // best code sequence is platform-dependent. 861 // 862 // The conditional call goes to a stub attached to the function that performs 863 // further filtering before calling the breakpoint handler. 864 #if defined(JS_CODEGEN_X64) 865 // REX 83 MODRM OFFS IB 866 static_assert(Instance::offsetOfDebugStub() < 128); 867 masm.cmpq(Imm32(0), 868 Operand(Address(InstanceReg, Instance::offsetOfDebugStub()))); 869 870 // 74 OFFS 871 Label L; 872 L.bind(masm.currentOffset() + 7); 873 masm.j(Assembler::Zero, &L); 874 875 // E8 OFFS OFFS OFFS OFFS 876 masm.call(&perFunctionDebugStub_); 877 masm.append(CallSiteDesc(iter_.lastOpcodeOffset(), kind), 878 CodeOffset(masm.currentOffset())); 879 880 // Branch destination 881 MOZ_ASSERT_IF(!masm.oom(), masm.currentOffset() == uint32_t(L.offset())); 882 #elif defined(JS_CODEGEN_X86) 883 // 83 MODRM OFFS IB 884 static_assert(Instance::offsetOfDebugStub() < 128); 885 masm.cmpl(Imm32(0), 886 Operand(Address(InstanceReg, Instance::offsetOfDebugStub()))); 887 888 // 74 OFFS 889 Label L; 890 L.bind(masm.currentOffset() + 7); 891 masm.j(Assembler::Zero, &L); 892 893 // E8 OFFS OFFS OFFS OFFS 894 masm.call(&perFunctionDebugStub_); 895 masm.append(CallSiteDesc(iter_.lastOpcodeOffset(), kind), 896 CodeOffset(masm.currentOffset())); 897 898 // Branch destination 899 MOZ_ASSERT_IF(!masm.oom(), masm.currentOffset() == uint32_t(L.offset())); 900 #elif defined(JS_CODEGEN_ARM64) 901 ScratchPtr scratch(*this); 902 ARMRegister tmp(scratch, 64); 903 Label L; 904 masm.Ldr(tmp, 905 MemOperand(Address(InstanceReg, Instance::offsetOfDebugStub()))); 906 masm.Cbz(tmp, &L); 907 masm.Bl(&perFunctionDebugStub_); 908 masm.append(CallSiteDesc(iter_.lastOpcodeOffset(), kind), 909 CodeOffset(masm.currentOffset())); 910 masm.bind(&L); 911 #elif defined(JS_CODEGEN_ARM) 912 ScratchPtr scratch(*this); 913 masm.loadPtr(Address(InstanceReg, Instance::offsetOfDebugStub()), scratch); 914 masm.ma_orr(scratch, scratch, SetCC); 915 masm.ma_bl(&perFunctionDebugStub_, Assembler::NonZero); 916 masm.append(CallSiteDesc(iter_.lastOpcodeOffset(), kind), 917 CodeOffset(masm.currentOffset())); 918 #elif defined(JS_CODEGEN_LOONG64) || defined(JS_CODEGEN_MIPS64) || \ 919 defined(JS_CODEGEN_RISCV64) 920 ScratchPtr scratch(*this); 921 Label L; 922 masm.loadPtr(Address(InstanceReg, Instance::offsetOfDebugStub()), scratch); 923 masm.branchPtr(Assembler::Equal, scratch, ImmWord(0), &L); 924 const CodeOffset retAddr = masm.call(&perFunctionDebugStub_); 925 masm.append(CallSiteDesc(iter_.lastOpcodeOffset(), kind), retAddr); 926 masm.bind(&L); 927 #else 928 MOZ_CRASH("BaseCompiler platform hook: insertBreakablePoint"); 929 #endif 930 } 931 932 void BaseCompiler::insertPerFunctionDebugStub() { 933 // The per-function debug stub performs out-of-line filtering before jumping 934 // to the per-module debug stub if necessary. The per-module debug stub 935 // returns directly to the breakable point. 936 // 937 // NOTE, the link register is live here on platforms that have LR. 938 // 939 // The scratch register is available here (as it was at the call site). 940 // 941 // It's useful for the per-function debug stub to be compact, as every 942 // function gets one. 943 944 Label L; 945 masm.bind(&perFunctionDebugStub_); 946 947 #if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64) 948 { 949 ScratchPtr scratch(*this); 950 951 // Get the per-instance table of filtering bits. 952 masm.loadPtr(Address(InstanceReg, Instance::offsetOfDebugFilter()), 953 scratch); 954 955 // Check the filter bit. There is one bit per function in the module. 956 // Table elements are 32-bit because the masm makes that convenient. 957 masm.branchTest32(Assembler::NonZero, Address(scratch, func_.index / 32), 958 Imm32(1 << (func_.index % 32)), &L); 959 960 // Fast path: return to the execution. 961 masm.ret(); 962 } 963 #elif defined(JS_CODEGEN_ARM64) 964 { 965 ScratchPtr scratch(*this); 966 967 // Logic as above, except abiret to jump to the LR directly 968 masm.loadPtr(Address(InstanceReg, Instance::offsetOfDebugFilter()), 969 scratch); 970 masm.branchTest32(Assembler::NonZero, Address(scratch, func_.index / 32), 971 Imm32(1 << (func_.index % 32)), &L); 972 masm.abiret(); 973 } 974 #elif defined(JS_CODEGEN_ARM) 975 { 976 // We must be careful not to use the SecondScratchRegister, which usually 977 // is LR, as LR is live here. This means avoiding masm abstractions such 978 // as branchTest32. 979 980 static_assert(ScratchRegister != lr); 981 static_assert(Instance::offsetOfDebugFilter() < 0x1000); 982 983 ScratchRegisterScope tmp1(masm); 984 ScratchI32 tmp2(*this); 985 masm.ma_ldr( 986 DTRAddr(InstanceReg, DtrOffImm(Instance::offsetOfDebugFilter())), tmp1); 987 masm.ma_mov(Imm32(func_.index / 32), tmp2); 988 masm.ma_ldr(DTRAddr(tmp1, DtrRegImmShift(tmp2, LSL, 0)), tmp2); 989 masm.ma_tst(tmp2, Imm32(1 << func_.index % 32), tmp1, Assembler::Always); 990 masm.ma_bx(lr, Assembler::Zero); 991 } 992 #elif defined(JS_CODEGEN_LOONG64) || defined(JS_CODEGEN_MIPS64) || \ 993 defined(JS_CODEGEN_RISCV64) 994 { 995 ScratchPtr scratch(*this); 996 997 // Logic same as ARM64. 998 masm.loadPtr(Address(InstanceReg, Instance::offsetOfDebugFilter()), 999 scratch); 1000 masm.branchTest32(Assembler::NonZero, Address(scratch, func_.index / 32), 1001 Imm32(1 << (func_.index % 32)), &L); 1002 masm.abiret(); 1003 } 1004 #else 1005 MOZ_CRASH("BaseCompiler platform hook: endFunction"); 1006 #endif 1007 1008 // Jump to the per-module debug stub, which calls onwards to C++ land. 1009 masm.bind(&L); 1010 masm.jump(Address(InstanceReg, Instance::offsetOfDebugStub())); 1011 } 1012 1013 void BaseCompiler::saveRegisterReturnValues(const ResultType& resultType) { 1014 MOZ_ASSERT(compilerEnv_.debugEnabled()); 1015 size_t debugFrameOffset = masm.framePushed() - DebugFrame::offsetOfFrame(); 1016 size_t registerResultIdx = 0; 1017 for (ABIResultIter i(resultType); !i.done(); i.next()) { 1018 const ABIResult result = i.cur(); 1019 if (!result.inRegister()) { 1020 #ifdef DEBUG 1021 for (i.next(); !i.done(); i.next()) { 1022 MOZ_ASSERT(!i.cur().inRegister()); 1023 } 1024 #endif 1025 break; 1026 } 1027 1028 size_t resultOffset = DebugFrame::offsetOfRegisterResult(registerResultIdx); 1029 Address dest(masm.getStackPointer(), debugFrameOffset + resultOffset); 1030 switch (result.type().kind()) { 1031 case ValType::I32: 1032 masm.store32(RegI32(result.gpr()), dest); 1033 break; 1034 case ValType::I64: 1035 masm.store64(RegI64(result.gpr64()), dest); 1036 break; 1037 case ValType::F64: 1038 masm.storeDouble(RegF64(result.fpr()), dest); 1039 break; 1040 case ValType::F32: 1041 masm.storeFloat32(RegF32(result.fpr()), dest); 1042 break; 1043 case ValType::Ref: { 1044 uint32_t flag = 1045 DebugFrame::hasSpilledRegisterRefResultBitMask(registerResultIdx); 1046 // Tell Instance::traceFrame that we have a pointer to trace. 1047 masm.or32(Imm32(flag), 1048 Address(masm.getStackPointer(), 1049 debugFrameOffset + DebugFrame::offsetOfFlags())); 1050 masm.storePtr(RegRef(result.gpr()), dest); 1051 break; 1052 } 1053 case ValType::V128: 1054 #ifdef ENABLE_WASM_SIMD 1055 masm.storeUnalignedSimd128(RegV128(result.fpr()), dest); 1056 break; 1057 #else 1058 MOZ_CRASH("No SIMD support"); 1059 #endif 1060 } 1061 registerResultIdx++; 1062 } 1063 } 1064 1065 void BaseCompiler::restoreRegisterReturnValues(const ResultType& resultType) { 1066 MOZ_ASSERT(compilerEnv_.debugEnabled()); 1067 size_t debugFrameOffset = masm.framePushed() - DebugFrame::offsetOfFrame(); 1068 size_t registerResultIdx = 0; 1069 for (ABIResultIter i(resultType); !i.done(); i.next()) { 1070 const ABIResult result = i.cur(); 1071 if (!result.inRegister()) { 1072 #ifdef DEBUG 1073 for (i.next(); !i.done(); i.next()) { 1074 MOZ_ASSERT(!i.cur().inRegister()); 1075 } 1076 #endif 1077 break; 1078 } 1079 size_t resultOffset = 1080 DebugFrame::offsetOfRegisterResult(registerResultIdx++); 1081 Address src(masm.getStackPointer(), debugFrameOffset + resultOffset); 1082 switch (result.type().kind()) { 1083 case ValType::I32: 1084 masm.load32(src, RegI32(result.gpr())); 1085 break; 1086 case ValType::I64: 1087 masm.load64(src, RegI64(result.gpr64())); 1088 break; 1089 case ValType::F64: 1090 masm.loadDouble(src, RegF64(result.fpr())); 1091 break; 1092 case ValType::F32: 1093 masm.loadFloat32(src, RegF32(result.fpr())); 1094 break; 1095 case ValType::Ref: 1096 masm.loadPtr(src, RegRef(result.gpr())); 1097 break; 1098 case ValType::V128: 1099 #ifdef ENABLE_WASM_SIMD 1100 masm.loadUnalignedSimd128(src, RegV128(result.fpr())); 1101 break; 1102 #else 1103 MOZ_CRASH("No SIMD support"); 1104 #endif 1105 } 1106 } 1107 } 1108 1109 ////////////////////////////////////////////////////////////////////////////// 1110 // 1111 // Support for lazy tiering 1112 1113 // The key thing here is, we generate a short piece of code which, most of the 1114 // time, has no effect, but just occasionally wants to call out to C++ land. 1115 // That's a similar requirement to the Debugger API support (see above) and so 1116 // we have a similar, but simpler, solution. Specifically, we use a single 1117 // stub routine for the whole module, whereas for debugging, there are 1118 // per-function stub routines as well as a whole-module stub routine involved. 1119 1120 class OutOfLineRequestTierUp : public OutOfLineCode { 1121 Register instance_; // points at the instance at entry; must remain unchanged 1122 Maybe<RegI32> scratch_; // only provided on arm32 1123 size_t lastOpcodeOffset_; // a bytecode offset 1124 1125 public: 1126 OutOfLineRequestTierUp(Register instance, Maybe<RegI32> scratch, 1127 size_t lastOpcodeOffset) 1128 : instance_(instance), 1129 scratch_(scratch), 1130 lastOpcodeOffset_(lastOpcodeOffset) {} 1131 virtual void generate(MacroAssembler* masm) override { 1132 // Generate: 1133 // 1134 // [optionally, if `instance_` != InstanceReg: swap(instance_, InstanceReg)] 1135 // call * $offsetOfRequestTierUpStub(InstanceReg) 1136 // [optionally, if `instance_` != InstanceReg: swap(instance_, InstanceReg)] 1137 // goto rejoin 1138 // 1139 // This is the unlikely path, where we call the (per-module) 1140 // request-tier-up stub. The stub wants the instance pointer to be in the 1141 // official InstanceReg at this point, but InstanceReg itself might hold 1142 // arbitrary other live data. Hence, if necessary, swap `instance_` and 1143 // InstanceReg before the call and swap them back after it. 1144 #ifndef RABALDR_PIN_INSTANCE 1145 if (Register(instance_) != InstanceReg) { 1146 # ifdef JS_CODEGEN_X86 1147 // On x86_32 this is easy. 1148 masm->xchgl(instance_, InstanceReg); 1149 # elif JS_CODEGEN_ARM 1150 masm->mov(instance_, 1151 scratch_.value()); // note, destination is second arg 1152 masm->mov(InstanceReg, instance_); 1153 masm->mov(scratch_.value(), InstanceReg); 1154 # else 1155 MOZ_CRASH("BaseCompiler::OutOfLineRequestTierUp #1"); 1156 # endif 1157 } 1158 #endif 1159 // Call the stub 1160 const CodeOffset retAddr = 1161 masm->call(Address(InstanceReg, Instance::offsetOfRequestTierUpStub())); 1162 masm->append(CallSiteDesc(lastOpcodeOffset_, CallSiteKind::RequestTierUp), 1163 retAddr); 1164 // And swap again, if we swapped above. 1165 #ifndef RABALDR_PIN_INSTANCE 1166 if (Register(instance_) != InstanceReg) { 1167 # ifdef JS_CODEGEN_X86 1168 masm->xchgl(instance_, InstanceReg); 1169 # elif JS_CODEGEN_ARM 1170 masm->mov(instance_, scratch_.value()); 1171 masm->mov(InstanceReg, instance_); 1172 masm->mov(scratch_.value(), InstanceReg); 1173 # else 1174 MOZ_CRASH("BaseCompiler::OutOfLineRequestTierUp #2"); 1175 # endif 1176 } 1177 #endif 1178 1179 masm->jump(rejoin()); 1180 } 1181 }; 1182 1183 Maybe<CodeOffset> BaseCompiler::addHotnessCheck() { 1184 // Here's an example of what we'll create. The path that almost always 1185 // happens, where the counter doesn't go negative, has just one branch. 1186 // 1187 // subl $to_be_filled_in_later, 0x170(%r14) 1188 // js oolCode // almost never taken 1189 // rejoin: 1190 // ---------------- 1191 // oolCode: // we get here when the counter is negative, viz, almost never 1192 // call *0x160(%r14) // RequestTierUpStub 1193 // jmp rejoin 1194 // 1195 // Note that the counter is updated regardless of whether or not it has gone 1196 // negative. That means that, at entry to RequestTierUpStub, we know the 1197 // counter must be negative, and not merely zero. 1198 // 1199 // Non-Intel targets will have to generate a load / subtract-and-set-flags / 1200 // store / jcond sequence. 1201 // 1202 // To ensure the shortest possible encoding, `to_be_filled_in_later` must be 1203 // a value in the range 1 .. 127 inclusive. This is good enough for 1204 // hotness-counting purposes. 1205 1206 AutoCreatedBy acb(masm, "BC::addHotnessCheck"); 1207 1208 // A sync() must happen before this. The request tier-up stub does not save 1209 // all live registers. 1210 MOZ_ASSERT(!hasLiveRegsOnStk()); 1211 1212 #ifdef RABALDR_PIN_INSTANCE 1213 Register instance(InstanceReg); 1214 #else 1215 // This seems to assume that any non-RABALDR_PIN_INSTANCE target is 32-bit 1216 ScratchI32 instance(*this); 1217 fr.loadInstancePtr(instance); 1218 #endif 1219 1220 Address addressOfCounter = Address( 1221 instance, wasm::Instance::offsetInData( 1222 codeMeta_.offsetOfFuncDefInstanceData(func_.index))); 1223 1224 #if JS_CODEGEN_ARM 1225 Maybe<RegI32> scratch = Some(needI32()); 1226 #else 1227 Maybe<RegI32> scratch = Nothing(); 1228 #endif 1229 1230 OutOfLineCode* ool = addOutOfLineCode(new (alloc_) OutOfLineRequestTierUp( 1231 instance, scratch, iter_.lastOpcodeOffset())); 1232 if (!ool) { 1233 return Nothing(); 1234 } 1235 1236 // Because of the Intel arch instruction formats, `patchPoint` points to the 1237 // byte immediately following the last byte of the instruction to patch. 1238 CodeOffset patchPoint = masm.sub32FromMemAndBranchIfNegativeWithPatch( 1239 addressOfCounter, ool->entry()); 1240 1241 masm.bind(ool->rejoin()); 1242 1243 if (scratch.isSome()) { 1244 freeI32(scratch.value()); 1245 } 1246 1247 // `patchPoint` might be invalid if the assembler OOMd at some point. 1248 return masm.oom() ? Nothing() : Some(patchPoint); 1249 } 1250 1251 void BaseCompiler::patchHotnessCheck(CodeOffset offset, uint32_t step) { 1252 // Zero makes the hotness check pointless. Above 127 is not representable in 1253 // the short-form Intel encoding. 1254 MOZ_RELEASE_ASSERT(step > 0 && step <= 127); 1255 MOZ_ASSERT(!masm.oom()); 1256 masm.patchSub32FromMemAndBranchIfNegative(offset, Imm32(step)); 1257 } 1258 1259 ////////////////////////////////////////////////////////////////////////////// 1260 // 1261 // Results and block parameters 1262 1263 void BaseCompiler::popStackReturnValues(const ResultType& resultType) { 1264 uint32_t bytes = ABIResultIter::MeasureStackBytes(resultType); 1265 if (bytes == 0) { 1266 return; 1267 } 1268 Register target = ABINonArgReturnReg0; 1269 Register temp = ABINonArgReturnReg1; 1270 fr.loadIncomingStackResultAreaPtr(RegPtr(target)); 1271 fr.popStackResultsToMemory(target, bytes, temp); 1272 } 1273 1274 // TODO / OPTIMIZE (Bug 1316818): At the moment we use the Wasm 1275 // inter-procedure ABI for block returns, which allocates ReturnReg as the 1276 // single block result register. It is possible other choices would lead to 1277 // better register allocation, as ReturnReg is often first in the register set 1278 // and will be heavily wanted by the register allocator that uses takeFirst(). 1279 // 1280 // Obvious options: 1281 // - pick a register at the back of the register set 1282 // - pick a random register per block (different blocks have 1283 // different join regs) 1284 1285 void BaseCompiler::popRegisterResults(ABIResultIter& iter) { 1286 // Pop register results. Note that in the single-value case, popping to a 1287 // register may cause a sync(); for multi-value we sync'd already. 1288 for (; !iter.done(); iter.next()) { 1289 const ABIResult& result = iter.cur(); 1290 if (!result.inRegister()) { 1291 // TODO / OPTIMIZE: We sync here to avoid solving the general parallel 1292 // move problem in popStackResults. However we could avoid syncing the 1293 // values that are going to registers anyway, if they are already in 1294 // registers. 1295 sync(); 1296 break; 1297 } 1298 switch (result.type().kind()) { 1299 case ValType::I32: 1300 popI32(RegI32(result.gpr())); 1301 break; 1302 case ValType::I64: 1303 popI64(RegI64(result.gpr64())); 1304 break; 1305 case ValType::F32: 1306 popF32(RegF32(result.fpr())); 1307 break; 1308 case ValType::F64: 1309 popF64(RegF64(result.fpr())); 1310 break; 1311 case ValType::Ref: 1312 popRef(RegRef(result.gpr())); 1313 break; 1314 case ValType::V128: 1315 #ifdef ENABLE_WASM_SIMD 1316 popV128(RegV128(result.fpr())); 1317 #else 1318 MOZ_CRASH("No SIMD support"); 1319 #endif 1320 } 1321 } 1322 } 1323 1324 void BaseCompiler::popStackResults(ABIResultIter& iter, StackHeight stackBase) { 1325 MOZ_ASSERT(!iter.done()); 1326 1327 // The iterator should be advanced beyond register results, and register 1328 // results should be popped already from the value stack. 1329 uint32_t alreadyPopped = iter.index(); 1330 1331 // At this point, only stack arguments are remaining. Iterate through them 1332 // to measure how much stack space they will take up. 1333 for (; !iter.done(); iter.next()) { 1334 MOZ_ASSERT(iter.cur().onStack()); 1335 } 1336 1337 // Calculate the space needed to store stack results, in bytes. 1338 uint32_t stackResultBytes = iter.stackBytesConsumedSoFar(); 1339 MOZ_ASSERT(stackResultBytes); 1340 1341 // Compute the stack height including the stack results. Note that it's 1342 // possible that this call expands the stack, for example if some of the 1343 // results are supplied by constants and so are not already on the machine 1344 // stack. 1345 uint32_t endHeight = fr.prepareStackResultArea(stackBase, stackResultBytes); 1346 1347 // Find a free GPR to use when shuffling stack values. If none is 1348 // available, push ReturnReg and restore it after we're done. 1349 bool saved = false; 1350 RegPtr temp = ra.needTempPtr(RegPtr(ReturnReg), &saved); 1351 1352 // The sequence of Stk values is in the same order on the machine stack as 1353 // the result locations, but there is a complication: constant values are 1354 // not actually pushed on the machine stack. (At this point registers and 1355 // locals have been spilled already.) So, moving the Stk values into place 1356 // isn't simply a shuffle-down or shuffle-up operation. There is a part of 1357 // the Stk sequence that shuffles toward the FP, a part that's already in 1358 // place, and a part that shuffles toward the SP. After shuffling, we have 1359 // to materialize the constants. 1360 1361 // Shuffle mem values toward the frame pointer, copying deepest values 1362 // first. Stop when we run out of results, get to a register result, or 1363 // find a Stk value that is closer to the FP than the result. 1364 for (iter.switchToPrev(); !iter.done(); iter.prev()) { 1365 const ABIResult& result = iter.cur(); 1366 if (!result.onStack()) { 1367 break; 1368 } 1369 MOZ_ASSERT(result.stackOffset() < stackResultBytes); 1370 uint32_t destHeight = endHeight - result.stackOffset(); 1371 uint32_t stkBase = stk_.length() - (iter.count() - alreadyPopped); 1372 Stk& v = stk_[stkBase + iter.index()]; 1373 if (v.isMem()) { 1374 uint32_t srcHeight = v.offs(); 1375 if (srcHeight <= destHeight) { 1376 break; 1377 } 1378 fr.shuffleStackResultsTowardFP(srcHeight, destHeight, result.size(), 1379 temp); 1380 } 1381 } 1382 1383 // Reset iterator and skip register results. 1384 for (iter.reset(); !iter.done(); iter.next()) { 1385 if (iter.cur().onStack()) { 1386 break; 1387 } 1388 } 1389 1390 // Revisit top stack values, shuffling mem values toward the stack pointer, 1391 // copying shallowest values first. 1392 for (; !iter.done(); iter.next()) { 1393 const ABIResult& result = iter.cur(); 1394 MOZ_ASSERT(result.onStack()); 1395 MOZ_ASSERT(result.stackOffset() < stackResultBytes); 1396 uint32_t destHeight = endHeight - result.stackOffset(); 1397 Stk& v = stk_[stk_.length() - (iter.index() - alreadyPopped) - 1]; 1398 if (v.isMem()) { 1399 uint32_t srcHeight = v.offs(); 1400 if (srcHeight >= destHeight) { 1401 break; 1402 } 1403 fr.shuffleStackResultsTowardSP(srcHeight, destHeight, result.size(), 1404 temp); 1405 } 1406 } 1407 1408 // Reset iterator and skip register results, which are already popped off 1409 // the value stack. 1410 for (iter.reset(); !iter.done(); iter.next()) { 1411 if (iter.cur().onStack()) { 1412 break; 1413 } 1414 } 1415 1416 // Materialize constants and pop the remaining items from the value stack. 1417 for (; !iter.done(); iter.next()) { 1418 const ABIResult& result = iter.cur(); 1419 uint32_t resultHeight = endHeight - result.stackOffset(); 1420 Stk& v = stk_.back(); 1421 switch (v.kind()) { 1422 case Stk::ConstI32: 1423 #if defined(JS_CODEGEN_MIPS64) || defined(JS_CODEGEN_LOONG64) || \ 1424 defined(JS_CODEGEN_RISCV64) 1425 fr.storeImmediatePtrToStack(v.i32val_, resultHeight, temp); 1426 #else 1427 fr.storeImmediatePtrToStack(uint32_t(v.i32val_), resultHeight, temp); 1428 #endif 1429 break; 1430 case Stk::ConstF32: 1431 fr.storeImmediateF32ToStack(v.f32val_, resultHeight, temp); 1432 break; 1433 case Stk::ConstI64: 1434 fr.storeImmediateI64ToStack(v.i64val_, resultHeight, temp); 1435 break; 1436 case Stk::ConstF64: 1437 fr.storeImmediateF64ToStack(v.f64val_, resultHeight, temp); 1438 break; 1439 #ifdef ENABLE_WASM_SIMD 1440 case Stk::ConstV128: 1441 fr.storeImmediateV128ToStack(v.v128val_, resultHeight, temp); 1442 break; 1443 #endif 1444 case Stk::ConstRef: 1445 fr.storeImmediatePtrToStack(v.refval_, resultHeight, temp); 1446 break; 1447 case Stk::MemRef: 1448 // Update bookkeeping as we pop the Stk entry. 1449 stackMapGenerator_.memRefsOnStk--; 1450 break; 1451 default: 1452 MOZ_ASSERT(v.isMem()); 1453 break; 1454 } 1455 stk_.popBack(); 1456 } 1457 1458 ra.freeTempPtr(temp, saved); 1459 1460 // This will pop the stack if needed. 1461 fr.finishStackResultArea(stackBase, stackResultBytes); 1462 } 1463 1464 void BaseCompiler::popBlockResults(ResultType type, StackHeight stackBase, 1465 ContinuationKind kind) { 1466 if (!type.empty()) { 1467 ABIResultIter iter(type); 1468 popRegisterResults(iter); 1469 if (!iter.done()) { 1470 popStackResults(iter, stackBase); 1471 // Because popStackResults might clobber the stack, it leaves the stack 1472 // pointer already in the right place for the continuation, whether the 1473 // continuation is a jump or fallthrough. 1474 return; 1475 } 1476 } 1477 // We get here if there are no stack results. For a fallthrough, the stack 1478 // is already at the right height. For a jump, we may need to pop the stack 1479 // pointer if the continuation's stack height is lower than the current 1480 // stack height. 1481 if (kind == ContinuationKind::Jump) { 1482 fr.popStackBeforeBranch(stackBase, type); 1483 } 1484 } 1485 1486 // This function is similar to popBlockResults, but additionally handles the 1487 // implicit exception pointer that is pushed to the value stack on entry to 1488 // a catch handler by dropping it appropriately. 1489 void BaseCompiler::popCatchResults(ResultType type, StackHeight stackBase) { 1490 if (!type.empty()) { 1491 ABIResultIter iter(type); 1492 popRegisterResults(iter); 1493 if (!iter.done()) { 1494 popStackResults(iter, stackBase); 1495 // Since popStackResults clobbers the stack, we only need to free the 1496 // exception off of the value stack. 1497 popValueStackBy(1); 1498 } else { 1499 // If there are no stack results, we have to adjust the stack by 1500 // dropping the exception reference that's now on the stack. 1501 dropValue(); 1502 } 1503 } else { 1504 dropValue(); 1505 } 1506 fr.popStackBeforeBranch(stackBase, type); 1507 } 1508 1509 Stk BaseCompiler::captureStackResult(const ABIResult& result, 1510 StackHeight resultsBase, 1511 uint32_t stackResultBytes) { 1512 MOZ_ASSERT(result.onStack()); 1513 uint32_t offs = fr.locateStackResult(result, resultsBase, stackResultBytes); 1514 return Stk::StackResult(result.type(), offs); 1515 } 1516 1517 // TODO: It may be fruitful to inline the fast path here, as it will be common. 1518 1519 bool BaseCompiler::pushResults(ResultType type, StackHeight resultsBase) { 1520 if (type.empty()) { 1521 return true; 1522 } 1523 1524 if (type.length() > 1) { 1525 // Reserve extra space on the stack for all the values we'll push. 1526 // Multi-value push is not accounted for by the pre-sizing of the stack in 1527 // the decoding loop. 1528 // 1529 // Also make sure we leave headroom for other pushes that will occur after 1530 // pushing results, just to be safe. 1531 if (!stk_.reserve(stk_.length() + type.length() + MaxPushesPerOpcode)) { 1532 return false; 1533 } 1534 } 1535 1536 // We need to push the results in reverse order, so first iterate through 1537 // all results to determine the locations of stack result types. 1538 ABIResultIter iter(type); 1539 while (!iter.done()) { 1540 iter.next(); 1541 } 1542 uint32_t stackResultBytes = iter.stackBytesConsumedSoFar(); 1543 for (iter.switchToPrev(); !iter.done(); iter.prev()) { 1544 const ABIResult& result = iter.cur(); 1545 if (!result.onStack()) { 1546 break; 1547 } 1548 Stk v = captureStackResult(result, resultsBase, stackResultBytes); 1549 push(v); 1550 if (v.kind() == Stk::MemRef) { 1551 stackMapGenerator_.memRefsOnStk++; 1552 } 1553 } 1554 1555 for (; !iter.done(); iter.prev()) { 1556 const ABIResult& result = iter.cur(); 1557 MOZ_ASSERT(result.inRegister()); 1558 switch (result.type().kind()) { 1559 case ValType::I32: 1560 pushI32(RegI32(result.gpr())); 1561 break; 1562 case ValType::I64: 1563 pushI64(RegI64(result.gpr64())); 1564 break; 1565 case ValType::V128: 1566 #ifdef ENABLE_WASM_SIMD 1567 pushV128(RegV128(result.fpr())); 1568 break; 1569 #else 1570 MOZ_CRASH("No SIMD support"); 1571 #endif 1572 case ValType::F32: 1573 pushF32(RegF32(result.fpr())); 1574 break; 1575 case ValType::F64: 1576 pushF64(RegF64(result.fpr())); 1577 break; 1578 case ValType::Ref: 1579 pushRef(RegRef(result.gpr())); 1580 break; 1581 } 1582 } 1583 1584 return true; 1585 } 1586 1587 bool BaseCompiler::pushBlockResults(ResultType type) { 1588 return pushResults(type, controlItem().stackHeight); 1589 } 1590 1591 // A combination of popBlockResults + pushBlockResults, used when entering a 1592 // block with a control-flow join (loops) or split (if) to shuffle the 1593 // fallthrough block parameters into the locations expected by the 1594 // continuation. 1595 bool BaseCompiler::topBlockParams(ResultType type) { 1596 // This function should only be called when entering a block with a 1597 // control-flow join at the entry, where there are no live temporaries in 1598 // the current block. 1599 StackHeight base = controlItem().stackHeight; 1600 MOZ_ASSERT(fr.stackResultsBase(stackConsumed(type.length())) == base); 1601 popBlockResults(type, base, ContinuationKind::Fallthrough); 1602 return pushBlockResults(type); 1603 } 1604 1605 // A combination of popBlockResults + pushBlockResults, used before branches 1606 // where we don't know the target (br_if / br_table). If and when the branch 1607 // is taken, the stack results will be shuffled down into place. For br_if 1608 // that has fallthrough, the parameters for the untaken branch flow through to 1609 // the continuation. 1610 bool BaseCompiler::topBranchParams(ResultType type, StackHeight* height) { 1611 if (type.empty()) { 1612 *height = fr.stackHeight(); 1613 return true; 1614 } 1615 // There may be temporary values that need spilling; delay computation of 1616 // the stack results base until after the popRegisterResults(), which spills 1617 // if needed. 1618 ABIResultIter iter(type); 1619 popRegisterResults(iter); 1620 StackHeight base = fr.stackResultsBase(stackConsumed(iter.remaining())); 1621 if (!iter.done()) { 1622 popStackResults(iter, base); 1623 } 1624 if (!pushResults(type, base)) { 1625 return false; 1626 } 1627 *height = base; 1628 return true; 1629 } 1630 1631 // Conditional branches with fallthrough are preceded by a topBranchParams, so 1632 // we know that there are no stack results that need to be materialized. In 1633 // that case, we can just shuffle the whole block down before popping the 1634 // stack. 1635 void BaseCompiler::shuffleStackResultsBeforeBranch(StackHeight srcHeight, 1636 StackHeight destHeight, 1637 ResultType type) { 1638 uint32_t stackResultBytes = 0; 1639 1640 if (ABIResultIter::HasStackResults(type)) { 1641 MOZ_ASSERT(stk_.length() >= type.length()); 1642 ABIResultIter iter(type); 1643 for (; !iter.done(); iter.next()) { 1644 #ifdef DEBUG 1645 const ABIResult& result = iter.cur(); 1646 const Stk& v = stk_[stk_.length() - iter.index() - 1]; 1647 MOZ_ASSERT(v.isMem() == result.onStack()); 1648 #endif 1649 } 1650 1651 stackResultBytes = iter.stackBytesConsumedSoFar(); 1652 MOZ_ASSERT(stackResultBytes > 0); 1653 1654 if (srcHeight != destHeight) { 1655 // Find a free GPR to use when shuffling stack values. If none 1656 // is available, push ReturnReg and restore it after we're done. 1657 bool saved = false; 1658 RegPtr temp = ra.needTempPtr(RegPtr(ReturnReg), &saved); 1659 fr.shuffleStackResultsTowardFP(srcHeight, destHeight, stackResultBytes, 1660 temp); 1661 ra.freeTempPtr(temp, saved); 1662 } 1663 } 1664 1665 fr.popStackBeforeBranch(destHeight, stackResultBytes); 1666 } 1667 1668 bool BaseCompiler::insertDebugCollapseFrame() { 1669 if (!compilerEnv_.debugEnabled() || deadCode_) { 1670 return true; 1671 } 1672 insertBreakablePoint(CallSiteKind::CollapseFrame); 1673 return createStackMap("debug: collapse-frame breakpoint", 1674 HasDebugFrameWithLiveRefs::Maybe); 1675 } 1676 1677 ////////////////////////////////////////////////////////////////////////////// 1678 // 1679 // Function calls. 1680 1681 void BaseCompiler::beginCall(FunctionCall& call) { 1682 // Use masm.framePushed() because the value we want here does not depend 1683 // on the height of the frame's stack area, but the actual size of the 1684 // allocated frame. 1685 call.frameAlignAdjustment = ComputeByteAlignment( 1686 masm.framePushed() + sizeof(Frame), JitStackAlignment); 1687 } 1688 1689 void BaseCompiler::endCall(FunctionCall& call, size_t stackSpace) { 1690 size_t adjustment = call.stackArgAreaSize + call.frameAlignAdjustment; 1691 fr.freeArgAreaAndPopBytes(adjustment, stackSpace); 1692 1693 MOZ_ASSERT(stackMapGenerator_.framePushedExcludingOutboundCallArgs.isSome()); 1694 stackMapGenerator_.framePushedExcludingOutboundCallArgs.reset(); 1695 1696 if (call.restoreState == RestoreState::All) { 1697 fr.loadInstancePtr(InstanceReg); 1698 masm.loadWasmPinnedRegsFromInstance(mozilla::Nothing()); 1699 masm.switchToWasmInstanceRealm(ABINonArgReturnReg0, ABINonArgReturnReg1); 1700 } else if (call.restoreState == RestoreState::PinnedRegs) { 1701 masm.loadWasmPinnedRegsFromInstance(mozilla::Nothing()); 1702 } 1703 } 1704 1705 void BaseCompiler::startCallArgs(size_t stackArgAreaSizeUnaligned, 1706 FunctionCall* call) { 1707 size_t stackArgAreaSizeAligned = 1708 AlignStackArgAreaSize(stackArgAreaSizeUnaligned); 1709 MOZ_ASSERT(stackArgAreaSizeUnaligned <= stackArgAreaSizeAligned); 1710 1711 // Record the masm.framePushed() value at this point, before we push args 1712 // for the call and any required alignment space. This defines the lower limit 1713 // of the stackmap that will be created for this call. 1714 MOZ_ASSERT( 1715 stackMapGenerator_.framePushedExcludingOutboundCallArgs.isNothing()); 1716 stackMapGenerator_.framePushedExcludingOutboundCallArgs.emplace( 1717 // However much we've pushed so far 1718 masm.framePushed() + 1719 // Extra space we'll push to get the frame aligned 1720 call->frameAlignAdjustment); 1721 1722 call->stackArgAreaSize = stackArgAreaSizeAligned; 1723 1724 size_t adjustment = call->stackArgAreaSize + call->frameAlignAdjustment; 1725 fr.allocArgArea(adjustment); 1726 } 1727 1728 ABIArg BaseCompiler::reservePointerArgument(FunctionCall* call) { 1729 return call->abi.next(MIRType::Pointer); 1730 } 1731 1732 // TODO / OPTIMIZE (Bug 1316821): Note passArg is used only in one place. 1733 // (Or it was, until Luke wandered through, but that can be fixed again.) 1734 // I'm not saying we should manually inline it, but we could hoist the 1735 // dispatch into the caller and have type-specific implementations of 1736 // passArg: passArgI32(), etc. Then those might be inlined, at least in PGO 1737 // builds. 1738 // 1739 // The bulk of the work here (60%) is in the next() call, though. 1740 // 1741 // Notably, since next() is so expensive, StackArgAreaSizeUnaligned() 1742 // becomes expensive too. 1743 // 1744 // Somehow there could be a trick here where the sequence of argument types 1745 // (read from the input stream) leads to a cached entry for 1746 // StackArgAreaSizeUnaligned() and for how to pass arguments... 1747 // 1748 // But at least we could reduce the cost of StackArgAreaSizeUnaligned() by 1749 // first reading the argument types into a (reusable) vector, then we have 1750 // the outgoing size at low cost, and then we can pass args based on the 1751 // info we read. 1752 1753 void BaseCompiler::passArg(ValType type, const Stk& arg, FunctionCall* call) { 1754 switch (type.kind()) { 1755 case ValType::I32: { 1756 ABIArg argLoc = call->abi.next(MIRType::Int32); 1757 if (argLoc.kind() == ABIArg::Stack) { 1758 ScratchI32 scratch(*this); 1759 loadI32(arg, scratch); 1760 masm.store32(scratch, Address(masm.getStackPointer(), 1761 argLoc.offsetFromArgBase())); 1762 } else { 1763 loadI32(arg, RegI32(argLoc.gpr())); 1764 } 1765 break; 1766 } 1767 case ValType::I64: { 1768 ABIArg argLoc = call->abi.next(MIRType::Int64); 1769 if (argLoc.kind() == ABIArg::Stack) { 1770 ScratchI32 scratch(*this); 1771 #ifdef JS_PUNBOX64 1772 loadI64(arg, fromI32(scratch)); 1773 masm.storePtr(scratch, Address(masm.getStackPointer(), 1774 argLoc.offsetFromArgBase())); 1775 #else 1776 loadI64Low(arg, scratch); 1777 masm.store32(scratch, LowWord(Address(masm.getStackPointer(), 1778 argLoc.offsetFromArgBase()))); 1779 loadI64High(arg, scratch); 1780 masm.store32(scratch, HighWord(Address(masm.getStackPointer(), 1781 argLoc.offsetFromArgBase()))); 1782 #endif 1783 } else { 1784 loadI64(arg, RegI64(argLoc.gpr64())); 1785 } 1786 break; 1787 } 1788 case ValType::V128: { 1789 #ifdef ENABLE_WASM_SIMD 1790 ABIArg argLoc = call->abi.next(MIRType::Simd128); 1791 switch (argLoc.kind()) { 1792 case ABIArg::Stack: { 1793 ScratchV128 scratch(*this); 1794 loadV128(arg, scratch); 1795 masm.storeUnalignedSimd128( 1796 (RegV128)scratch, 1797 Address(masm.getStackPointer(), argLoc.offsetFromArgBase())); 1798 break; 1799 } 1800 case ABIArg::GPR: { 1801 MOZ_CRASH("Unexpected parameter passing discipline"); 1802 } 1803 case ABIArg::FPU: { 1804 loadV128(arg, RegV128(argLoc.fpu())); 1805 break; 1806 } 1807 # if defined(JS_CODEGEN_REGISTER_PAIR) 1808 case ABIArg::GPR_PAIR: { 1809 MOZ_CRASH("Unexpected parameter passing discipline"); 1810 } 1811 # endif 1812 case ABIArg::Uninitialized: 1813 MOZ_CRASH("Uninitialized ABIArg kind"); 1814 } 1815 break; 1816 #else 1817 MOZ_CRASH("No SIMD support"); 1818 #endif 1819 } 1820 case ValType::F64: { 1821 ABIArg argLoc = call->abi.next(MIRType::Double); 1822 switch (argLoc.kind()) { 1823 case ABIArg::Stack: { 1824 ScratchF64 scratch(*this); 1825 loadF64(arg, scratch); 1826 masm.storeDouble(scratch, Address(masm.getStackPointer(), 1827 argLoc.offsetFromArgBase())); 1828 break; 1829 } 1830 #if defined(JS_CODEGEN_REGISTER_PAIR) 1831 case ABIArg::GPR_PAIR: { 1832 # if defined(JS_CODEGEN_ARM) 1833 ScratchF64 scratch(*this); 1834 loadF64(arg, scratch); 1835 masm.ma_vxfer(scratch, argLoc.evenGpr(), argLoc.oddGpr()); 1836 break; 1837 # else 1838 MOZ_CRASH("BaseCompiler platform hook: passArg F64 pair"); 1839 # endif 1840 } 1841 #endif 1842 case ABIArg::FPU: { 1843 loadF64(arg, RegF64(argLoc.fpu())); 1844 break; 1845 } 1846 case ABIArg::GPR: { 1847 MOZ_CRASH("Unexpected parameter passing discipline"); 1848 } 1849 case ABIArg::Uninitialized: 1850 MOZ_CRASH("Uninitialized ABIArg kind"); 1851 } 1852 break; 1853 } 1854 case ValType::F32: { 1855 ABIArg argLoc = call->abi.next(MIRType::Float32); 1856 switch (argLoc.kind()) { 1857 case ABIArg::Stack: { 1858 ScratchF32 scratch(*this); 1859 loadF32(arg, scratch); 1860 masm.storeFloat32(scratch, Address(masm.getStackPointer(), 1861 argLoc.offsetFromArgBase())); 1862 break; 1863 } 1864 case ABIArg::GPR: { 1865 ScratchF32 scratch(*this); 1866 loadF32(arg, scratch); 1867 masm.moveFloat32ToGPR(scratch, argLoc.gpr()); 1868 break; 1869 } 1870 case ABIArg::FPU: { 1871 loadF32(arg, RegF32(argLoc.fpu())); 1872 break; 1873 } 1874 #if defined(JS_CODEGEN_REGISTER_PAIR) 1875 case ABIArg::GPR_PAIR: { 1876 MOZ_CRASH("Unexpected parameter passing discipline"); 1877 } 1878 #endif 1879 case ABIArg::Uninitialized: 1880 MOZ_CRASH("Uninitialized ABIArg kind"); 1881 } 1882 break; 1883 } 1884 case ValType::Ref: { 1885 ABIArg argLoc = call->abi.next(MIRType::WasmAnyRef); 1886 if (argLoc.kind() == ABIArg::Stack) { 1887 ScratchRef scratch(*this); 1888 loadRef(arg, scratch); 1889 masm.storePtr(scratch, Address(masm.getStackPointer(), 1890 argLoc.offsetFromArgBase())); 1891 } else { 1892 loadRef(arg, RegRef(argLoc.gpr())); 1893 } 1894 break; 1895 } 1896 } 1897 } 1898 1899 template <typename T> 1900 bool BaseCompiler::emitCallArgs(const ValTypeVector& argTypes, T results, 1901 FunctionCall* baselineCall, 1902 CalleeOnStack calleeOnStack) { 1903 MOZ_ASSERT(!deadCode_); 1904 1905 ArgTypeVector args(argTypes, results.stackResults()); 1906 uint32_t naturalArgCount = argTypes.length(); 1907 uint32_t abiArgCount = args.lengthWithStackResults(); 1908 startCallArgs(StackArgAreaSizeUnaligned(args, baselineCall->abiKind), 1909 baselineCall); 1910 1911 // Args are deeper on the stack than the stack result area, if any. 1912 size_t argsDepth = results.onStackCount(); 1913 // They're deeper than the callee too, for callIndirect. 1914 if (calleeOnStack == CalleeOnStack::True) { 1915 argsDepth++; 1916 } 1917 1918 for (size_t i = 0; i < abiArgCount; ++i) { 1919 if (args.isNaturalArg(i)) { 1920 size_t naturalIndex = args.naturalIndex(i); 1921 size_t stackIndex = naturalArgCount - 1 - naturalIndex + argsDepth; 1922 passArg(argTypes[naturalIndex], peek(stackIndex), baselineCall); 1923 } else { 1924 // The synthetic stack result area pointer. 1925 ABIArg argLoc = baselineCall->abi.next(MIRType::Pointer); 1926 if (argLoc.kind() == ABIArg::Stack) { 1927 ScratchPtr scratch(*this); 1928 results.getStackResultArea(fr, scratch); 1929 masm.storePtr(scratch, Address(masm.getStackPointer(), 1930 argLoc.offsetFromArgBase())); 1931 } else { 1932 results.getStackResultArea(fr, RegPtr(argLoc.gpr())); 1933 } 1934 } 1935 } 1936 1937 #ifndef RABALDR_PIN_INSTANCE 1938 fr.loadInstancePtr(InstanceReg); 1939 #endif 1940 return true; 1941 } 1942 1943 bool BaseCompiler::pushStackResultsForWasmCall(const ResultType& type, 1944 RegPtr temp, 1945 StackResultsLoc* loc) { 1946 if (!ABIResultIter::HasStackResults(type)) { 1947 return true; 1948 } 1949 1950 // This method can increase stk_.length() by an unbounded amount, so we need 1951 // to perform an allocation here to accomodate the variable number of values. 1952 // There is enough headroom for any fixed number of values. The general case 1953 // is handled in emitBody. 1954 if (!stk_.reserve(stk_.length() + type.length())) { 1955 return false; 1956 } 1957 1958 // Measure stack results. 1959 ABIResultIter i(type); 1960 size_t count = 0; 1961 for (; !i.done(); i.next()) { 1962 if (i.cur().onStack()) { 1963 count++; 1964 } 1965 } 1966 uint32_t bytes = i.stackBytesConsumedSoFar(); 1967 1968 // Reserve space for the stack results. 1969 StackHeight resultsBase = fr.stackHeight(); 1970 uint32_t height = fr.prepareStackResultArea(resultsBase, bytes); 1971 1972 // Push Stk values onto the value stack, and zero out Ref values. 1973 for (i.switchToPrev(); !i.done(); i.prev()) { 1974 const ABIResult& result = i.cur(); 1975 if (result.onStack()) { 1976 Stk v = captureStackResult(result, resultsBase, bytes); 1977 push(v); 1978 if (v.kind() == Stk::MemRef) { 1979 stackMapGenerator_.memRefsOnStk++; 1980 fr.storeImmediatePtrToStack(intptr_t(0), v.offs(), temp); 1981 } 1982 } 1983 } 1984 1985 *loc = StackResultsLoc(bytes, count, height); 1986 1987 return true; 1988 } 1989 1990 // After a call, some results may be written to the stack result locations that 1991 // are pushed on the machine stack after any stack args. If there are stack 1992 // args and stack results, these results need to be shuffled down, as the args 1993 // are "consumed" by the call. 1994 void BaseCompiler::popStackResultsAfterWasmCall(const StackResultsLoc& results, 1995 uint32_t stackArgBytes) { 1996 if (results.bytes() != 0) { 1997 popValueStackBy(results.count()); 1998 if (stackArgBytes != 0) { 1999 uint32_t srcHeight = results.height(); 2000 MOZ_ASSERT(srcHeight >= stackArgBytes + results.bytes()); 2001 uint32_t destHeight = srcHeight - stackArgBytes; 2002 2003 fr.shuffleStackResultsTowardFP(srcHeight, destHeight, results.bytes(), 2004 ABINonArgReturnVolatileReg); 2005 } 2006 } 2007 } 2008 2009 void BaseCompiler::pushBuiltinCallResult(const FunctionCall& call, 2010 MIRType type) { 2011 switch (type) { 2012 case MIRType::Int32: { 2013 RegI32 rv = captureReturnedI32(); 2014 pushI32(rv); 2015 break; 2016 } 2017 case MIRType::Int64: { 2018 RegI64 rv = captureReturnedI64(); 2019 pushI64(rv); 2020 break; 2021 } 2022 case MIRType::Float32: { 2023 RegF32 rv = captureReturnedF32(call); 2024 pushF32(rv); 2025 break; 2026 } 2027 case MIRType::Double: { 2028 RegF64 rv = captureReturnedF64(call); 2029 pushF64(rv); 2030 break; 2031 } 2032 #ifdef ENABLE_WASM_SIMD 2033 case MIRType::Simd128: { 2034 RegV128 rv = captureReturnedV128(call); 2035 pushV128(rv); 2036 break; 2037 } 2038 #endif 2039 case MIRType::WasmAnyRef: { 2040 RegRef rv = captureReturnedRef(); 2041 pushRef(rv); 2042 break; 2043 } 2044 default: 2045 // In particular, passing |type| as MIRType::Void or MIRType::Pointer to 2046 // this function is an error. 2047 MOZ_CRASH("Function return type"); 2048 } 2049 } 2050 2051 bool BaseCompiler::pushWasmCallResults(const FunctionCall& call, 2052 ResultType type, 2053 const StackResultsLoc& loc) { 2054 // pushResults currently bypasses special case code in captureReturnedFxx() 2055 // that converts GPR results to FPR results for the system ABI when using 2056 // softFP. If we ever start using that combination for calls we need more 2057 // code. 2058 MOZ_ASSERT(call.abiKind == ABIKind::Wasm); 2059 return pushResults(type, fr.stackResultsBase(loc.bytes())); 2060 } 2061 2062 CodeOffset BaseCompiler::callDefinition(uint32_t funcIndex, 2063 const FunctionCall& call) { 2064 CallSiteDesc desc(bytecodeOffset(), CallSiteKind::Func); 2065 return masm.call(desc, funcIndex); 2066 } 2067 2068 CodeOffset BaseCompiler::callSymbolic(SymbolicAddress callee, 2069 const FunctionCall& call) { 2070 CallSiteDesc desc(bytecodeOffset(), CallSiteKind::Symbolic); 2071 return masm.call(desc, callee); 2072 } 2073 2074 // Precondition: sync() 2075 2076 static ReturnCallAdjustmentInfo BuildReturnCallAdjustmentInfo( 2077 const FuncType& callerType, const FuncType& calleeType) { 2078 return ReturnCallAdjustmentInfo( 2079 StackArgAreaSizeUnaligned(ArgTypeVector(calleeType), ABIKind::Wasm), 2080 StackArgAreaSizeUnaligned(ArgTypeVector(callerType), ABIKind::Wasm)); 2081 } 2082 2083 bool BaseCompiler::callIndirect(uint32_t funcTypeIndex, uint32_t tableIndex, 2084 const Stk& indexVal, const FunctionCall& call, 2085 bool tailCall, CodeOffset* fastCallOffset, 2086 CodeOffset* slowCallOffset) { 2087 CallIndirectId callIndirectId = 2088 CallIndirectId::forFuncType(codeMeta_, funcTypeIndex); 2089 MOZ_ASSERT(callIndirectId.kind() != CallIndirectIdKind::AsmJS); 2090 2091 const TableDesc& table = codeMeta_.tables[tableIndex]; 2092 CallSiteDesc desc(bytecodeOffset(), CallSiteKind::Indirect); 2093 CalleeDesc callee = 2094 CalleeDesc::wasmTable(codeMeta_, table, tableIndex, callIndirectId); 2095 OutOfLineCode* oob = addOutOfLineCode( 2096 new (alloc_) OutOfLineAbortingTrap(Trap::OutOfBounds, trapSiteDesc())); 2097 if (!oob) { 2098 return false; 2099 } 2100 2101 if (table.addressType() == AddressType::I64) { 2102 #ifdef JS_PUNBOX64 2103 RegI64 indexReg = RegI64(Register64(WasmTableCallIndexReg)); 2104 #else 2105 RegI64 indexReg = 2106 RegI64(Register64(WasmTableCallScratchReg0, WasmTableCallIndexReg)); 2107 #endif 2108 loadI64(indexVal, indexReg); 2109 masm.branch64( 2110 Assembler::Condition::BelowOrEqual, 2111 Address(InstanceReg, wasm::Instance::offsetInData( 2112 callee.tableLengthInstanceDataOffset())), 2113 indexReg, oob->entry()); 2114 // From this point forward, the callee index is known to fit in 32 bits and 2115 // therefore we only need WasmTableCallIndexReg. 2116 } else { 2117 loadI32(indexVal, RegI32(WasmTableCallIndexReg)); 2118 masm.branch32( 2119 Assembler::Condition::BelowOrEqual, 2120 Address(InstanceReg, wasm::Instance::offsetInData( 2121 callee.tableLengthInstanceDataOffset())), 2122 WasmTableCallIndexReg, oob->entry()); 2123 } 2124 2125 Label* nullCheckFailed = nullptr; 2126 #ifndef WASM_HAS_HEAPREG 2127 OutOfLineCode* nullref = addOutOfLineCode(new (alloc_) OutOfLineAbortingTrap( 2128 Trap::IndirectCallToNull, trapSiteDesc())); 2129 if (!nullref) { 2130 return false; 2131 } 2132 nullCheckFailed = nullref->entry(); 2133 #endif 2134 if (!tailCall) { 2135 masm.wasmCallIndirect(desc, callee, nullCheckFailed, fastCallOffset, 2136 slowCallOffset); 2137 } else { 2138 ReturnCallAdjustmentInfo retCallInfo = BuildReturnCallAdjustmentInfo( 2139 this->funcType(), (*codeMeta_.types)[funcTypeIndex].funcType()); 2140 masm.wasmReturnCallIndirect(desc, callee, nullCheckFailed, retCallInfo); 2141 } 2142 return true; 2143 } 2144 2145 class OutOfLineUpdateCallRefMetrics : public OutOfLineCode { 2146 public: 2147 virtual void generate(MacroAssembler* masm) override { 2148 // Call the stub pointed to by Instance::updateCallRefMetricsStub, then 2149 // rejoin. See "Register management" in BaseCompiler::updateCallRefMetrics 2150 // for details of register management. 2151 // 2152 // The monitored call may or may not be cross-instance. The stub will only 2153 // modify `regMetrics`, `regFuncRef`, `regScratch` and `regMetrics*` (that 2154 // is, the pointed-at CallRefMetrics) and cannot fail or trap. 2155 masm->call( 2156 Address(InstanceReg, Instance::offsetOfUpdateCallRefMetricsStub())); 2157 masm->jump(rejoin()); 2158 } 2159 }; 2160 2161 // Generate code that updates the `callRefIndex`th CallRefMetrics attached to 2162 // the current Instance, to reflect the fact that this call site is just about 2163 // to make a call to the funcref to which WasmCallRefReg currently points. 2164 bool BaseCompiler::updateCallRefMetrics(size_t callRefIndex) { 2165 AutoCreatedBy acb(masm, "BC::updateCallRefMetrics"); 2166 2167 // See declaration of CallRefMetrics for comments about assignments of 2168 // funcrefs to `CallRefMetrics::targets[]` fields. 2169 2170 // Register management: we will use three regs, `regMetrics`, `regFuncRef`, 2171 // `regScratch` as detailed below. All of them may be trashed. But 2172 // WasmCallRefReg needs to be unchanged across the update, so copy it into 2173 // `regFuncRef` and use that instead of the original. 2174 // 2175 // At entry here, at entry to the OOL code, and at entry to the stub it 2176 // calls, InstanceReg must be pointing at a valid Instance. 2177 const Register regMetrics = WasmCallRefCallScratchReg0; // CallRefMetrics* 2178 const Register regFuncRef = WasmCallRefCallScratchReg1; // FuncExtended* 2179 const Register regScratch = WasmCallRefCallScratchReg2; // scratch 2180 2181 OutOfLineCode* ool = 2182 addOutOfLineCode(new (alloc_) OutOfLineUpdateCallRefMetrics()); 2183 if (!ool) { 2184 return false; 2185 } 2186 2187 // We only need one Rejoin label, so use ool->rejoin() for that. 2188 2189 // if (target == nullptr) goto Rejoin 2190 masm.branchWasmAnyRefIsNull(/*isNull=*/true, WasmCallRefReg, ool->rejoin()); 2191 2192 // regFuncRef = target (make a copy of WasmCallRefReg) 2193 masm.mov(WasmCallRefReg, regFuncRef); 2194 2195 // regMetrics = thisInstance::callRefMetrics_ + <imm> 2196 // 2197 // Emit a patchable mov32 which will load the offset of the `CallRefMetrics` 2198 // stored inside the `Instance::callRefMetrics_` array 2199 const CodeOffset offsetOfCallRefOffset = masm.move32WithPatch(regScratch); 2200 masm.callRefMetricsPatches()[callRefIndex].setOffset( 2201 offsetOfCallRefOffset.offset()); 2202 // Get a pointer to the `CallRefMetrics` for this call_ref 2203 masm.loadPtr(Address(InstanceReg, wasm::Instance::offsetOfCallRefMetrics()), 2204 regMetrics); 2205 masm.addPtr(regScratch, regMetrics); 2206 2207 // At this point, regFuncRef = the FuncExtended*, regMetrics = the 2208 // CallRefMetrics* and we know regFuncRef is not null. 2209 // 2210 // if (target->instance != thisInstance) goto Out-Of-Line 2211 const size_t instanceSlotOffset = FunctionExtended::offsetOfExtendedSlot( 2212 FunctionExtended::WASM_INSTANCE_SLOT); 2213 masm.loadPtr(Address(regFuncRef, instanceSlotOffset), regScratch); 2214 masm.branchPtr(Assembler::NotEqual, InstanceReg, regScratch, ool->entry()); 2215 2216 // At this point, regFuncRef = the FuncExtended*, regMetrics = the 2217 // CallRefMetrics*, we know regFuncRef is not null and it's a same-instance 2218 // call. 2219 // 2220 // if (target != metrics->targets[0]) goto Out-Of-Line 2221 const size_t offsetOfTarget0 = CallRefMetrics::offsetOfTarget(0); 2222 masm.loadPtr(Address(regMetrics, offsetOfTarget0), regScratch); 2223 masm.branchPtr(Assembler::NotEqual, regScratch, regFuncRef, ool->entry()); 2224 2225 // At this point, regFuncRef = the FuncExtended*, regMetrics = the 2226 // CallRefMetrics*, we know regFuncRef is not null, it's a same-instance 2227 // call, and it is to the destination `regMetrics->targets[0]`. 2228 // 2229 // metrics->count0++ 2230 const size_t offsetOfCount0 = CallRefMetrics::offsetOfCount(0); 2231 masm.load32(Address(regMetrics, offsetOfCount0), regScratch); 2232 masm.add32(Imm32(1), regScratch); 2233 masm.store32(regScratch, Address(regMetrics, offsetOfCount0)); 2234 2235 masm.bind(ool->rejoin()); 2236 return true; 2237 } 2238 2239 bool BaseCompiler::callRef(const Stk& calleeRef, const FunctionCall& call, 2240 mozilla::Maybe<size_t> callRefIndex, 2241 CodeOffset* fastCallOffset, 2242 CodeOffset* slowCallOffset) { 2243 CallSiteDesc desc(bytecodeOffset(), CallSiteKind::FuncRef); 2244 CalleeDesc callee = CalleeDesc::wasmFuncRef(); 2245 2246 loadRef(calleeRef, RegRef(WasmCallRefReg)); 2247 if (compilerEnv_.mode() == CompileMode::LazyTiering) { 2248 if (!updateCallRefMetrics(*callRefIndex)) { 2249 return false; 2250 } 2251 } else { 2252 MOZ_ASSERT(callRefIndex.isNothing()); 2253 } 2254 2255 masm.wasmCallRef(desc, callee, fastCallOffset, slowCallOffset); 2256 return true; 2257 } 2258 2259 void BaseCompiler::returnCallRef(const Stk& calleeRef, const FunctionCall& call, 2260 const FuncType& funcType) { 2261 CallSiteDesc desc(bytecodeOffset(), CallSiteKind::FuncRef); 2262 CalleeDesc callee = CalleeDesc::wasmFuncRef(); 2263 2264 loadRef(calleeRef, RegRef(WasmCallRefReg)); 2265 ReturnCallAdjustmentInfo retCallInfo = 2266 BuildReturnCallAdjustmentInfo(this->funcType(), funcType); 2267 masm.wasmReturnCallRef(desc, callee, retCallInfo); 2268 } 2269 2270 // Precondition: sync() 2271 2272 CodeOffset BaseCompiler::callImport(unsigned instanceDataOffset, 2273 const FunctionCall& call) { 2274 CallSiteDesc desc(bytecodeOffset(), CallSiteKind::Import); 2275 CalleeDesc callee = CalleeDesc::import(instanceDataOffset); 2276 return masm.wasmCallImport(desc, callee); 2277 } 2278 2279 CodeOffset BaseCompiler::builtinCall(SymbolicAddress builtin, 2280 const FunctionCall& call) { 2281 return callSymbolic(builtin, call); 2282 } 2283 2284 CodeOffset BaseCompiler::builtinInstanceMethodCall( 2285 const SymbolicAddressSignature& builtin, const ABIArg& instanceArg, 2286 const FunctionCall& call) { 2287 #ifndef RABALDR_PIN_INSTANCE 2288 // Builtin method calls assume the instance register has been set. 2289 fr.loadInstancePtr(InstanceReg); 2290 #endif 2291 CallSiteDesc desc(bytecodeOffset(), CallSiteKind::Symbolic); 2292 return masm.wasmCallBuiltinInstanceMethod(desc, instanceArg, builtin.identity, 2293 builtin.failureMode, 2294 builtin.failureTrap); 2295 } 2296 2297 ////////////////////////////////////////////////////////////////////////////// 2298 // 2299 // Exception handling 2300 2301 // Abstracted helper for throwing, used for throw, rethrow, and rethrowing 2302 // at the end of a series of catch blocks (if none matched the exception). 2303 bool BaseCompiler::throwFrom(RegRef exn) { 2304 pushRef(exn); 2305 2306 // ThrowException invokes a trap, and the rest is dead code. 2307 return emitInstanceCall(SASigThrowException); 2308 } 2309 2310 void BaseCompiler::loadTag(RegPtr instance, uint32_t tagIndex, RegRef tagDst) { 2311 size_t offset = 2312 Instance::offsetInData(codeMeta_.offsetOfTagInstanceData(tagIndex)); 2313 masm.loadPtr(Address(instance, offset), tagDst); 2314 } 2315 2316 void BaseCompiler::consumePendingException(RegPtr instance, RegRef* exnDst, 2317 RegRef* tagDst) { 2318 RegPtr pendingAddr = RegPtr(PreBarrierReg); 2319 needPtr(pendingAddr); 2320 masm.computeEffectiveAddress( 2321 Address(instance, Instance::offsetOfPendingException()), pendingAddr); 2322 *exnDst = needRef(); 2323 masm.loadPtr(Address(pendingAddr, 0), *exnDst); 2324 emitBarrieredClear(pendingAddr); 2325 2326 *tagDst = needRef(); 2327 masm.computeEffectiveAddress( 2328 Address(instance, Instance::offsetOfPendingExceptionTag()), pendingAddr); 2329 masm.loadPtr(Address(pendingAddr, 0), *tagDst); 2330 emitBarrieredClear(pendingAddr); 2331 freePtr(pendingAddr); 2332 } 2333 2334 bool BaseCompiler::startTryNote(size_t* tryNoteIndex) { 2335 // Check the previous try note to ensure that we don't share an edge with 2336 // it that could lead to ambiguity. Insert a nop, if required. 2337 TryNoteVector& tryNotes = masm.tryNotes(); 2338 if (tryNotes.length() > 0) { 2339 const TryNote& previous = tryNotes.back(); 2340 uint32_t currentOffset = masm.currentOffset(); 2341 if (previous.tryBodyBegin() == currentOffset || 2342 previous.tryBodyEnd() == currentOffset) { 2343 masm.nop(); 2344 } 2345 } 2346 2347 // Mark the beginning of the try note 2348 wasm::TryNote tryNote = wasm::TryNote(); 2349 tryNote.setTryBodyBegin(masm.currentOffset()); 2350 return masm.append(tryNote, tryNoteIndex); 2351 } 2352 2353 void BaseCompiler::finishTryNote(size_t tryNoteIndex) { 2354 TryNoteVector& tryNotes = masm.tryNotes(); 2355 TryNote& tryNote = tryNotes[tryNoteIndex]; 2356 2357 // Disallow zero-length try notes by inserting a no-op 2358 if (tryNote.tryBodyBegin() == masm.currentOffset()) { 2359 masm.nop(); 2360 } 2361 2362 // Check the most recent finished try note to ensure that we don't share an 2363 // edge with it that could lead to ambiguity. Insert a nop, if required. 2364 // 2365 // Notice that finishTryNote is called in LIFO order -- using depth-first 2366 // search numbering to see if we are traversing back from a nested try to a 2367 // parent try, where we may need to ensure that the end offsets do not 2368 // coincide. 2369 // 2370 // In the case the tryNodeIndex >= mostRecentFinishedTryNoteIndex_, we have 2371 // finished a try that began after the most recent finished try, and so 2372 // startTryNote will take care of any nops. 2373 if (tryNoteIndex < mostRecentFinishedTryNoteIndex_) { 2374 const TryNote& previous = tryNotes[mostRecentFinishedTryNoteIndex_]; 2375 uint32_t currentOffset = masm.currentOffset(); 2376 if (previous.tryBodyEnd() == currentOffset) { 2377 masm.nop(); 2378 } 2379 } 2380 mostRecentFinishedTryNoteIndex_ = tryNoteIndex; 2381 2382 // Don't set the end of the try note if we've OOM'ed, as the above nop's may 2383 // not have been placed. This is okay as this compilation will be thrown 2384 // away. 2385 if (masm.oom()) { 2386 return; 2387 } 2388 2389 // Mark the end of the try note 2390 tryNote.setTryBodyEnd(masm.currentOffset()); 2391 } 2392 2393 //////////////////////////////////////////////////////////// 2394 // 2395 // Platform-specific popping and register targeting. 2396 2397 // The simple popping methods pop values into targeted registers; the caller 2398 // can free registers using standard functions. These are always called 2399 // popXForY where X says something about types and Y something about the 2400 // operation being targeted. 2401 2402 RegI32 BaseCompiler::needRotate64Temp() { 2403 #if defined(JS_CODEGEN_X86) 2404 return needI32(); 2405 #elif defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_ARM) || \ 2406 defined(JS_CODEGEN_ARM64) || defined(JS_CODEGEN_MIPS64) || \ 2407 defined(JS_CODEGEN_LOONG64) || defined(JS_CODEGEN_RISCV64) 2408 return RegI32::Invalid(); 2409 #else 2410 MOZ_CRASH("BaseCompiler platform hook: needRotate64Temp"); 2411 #endif 2412 } 2413 2414 void BaseCompiler::popAndAllocateForDivAndRemI32(RegI32* r0, RegI32* r1, 2415 RegI32* reserved) { 2416 #if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64) 2417 // r0 must be eax, and edx will be clobbered. 2418 need2xI32(specific_.eax, specific_.edx); 2419 *r1 = popI32(); 2420 *r0 = popI32ToSpecific(specific_.eax); 2421 *reserved = specific_.edx; 2422 #else 2423 pop2xI32(r0, r1); 2424 #endif 2425 } 2426 2427 static void QuotientI32(MacroAssembler& masm, RegI32 rs, RegI32 rsd, 2428 RegI32 reserved, IsUnsigned isUnsigned) { 2429 #if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64) 2430 masm.quotient32(rsd, rs, rsd, reserved, isUnsigned); 2431 #else 2432 masm.quotient32(rsd, rs, rsd, isUnsigned); 2433 #endif 2434 } 2435 2436 static void RemainderI32(MacroAssembler& masm, RegI32 rs, RegI32 rsd, 2437 RegI32 reserved, IsUnsigned isUnsigned) { 2438 #if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64) 2439 masm.remainder32(rsd, rs, rsd, reserved, isUnsigned); 2440 #else 2441 masm.remainder32(rsd, rs, rsd, isUnsigned); 2442 #endif 2443 } 2444 2445 void BaseCompiler::popAndAllocateForMulI64(RegI64* r0, RegI64* r1, 2446 RegI32* temp) { 2447 #if defined(JS_CODEGEN_X64) 2448 pop2xI64(r0, r1); 2449 #elif defined(JS_CODEGEN_X86) 2450 // lhsDest must be edx:eax and rhs must not be that. 2451 needI64(specific_.edx_eax); 2452 *r1 = popI64(); 2453 *r0 = popI64ToSpecific(specific_.edx_eax); 2454 *temp = needI32(); 2455 #elif defined(JS_CODEGEN_MIPS64) 2456 pop2xI64(r0, r1); 2457 #elif defined(JS_CODEGEN_ARM) 2458 pop2xI64(r0, r1); 2459 *temp = needI32(); 2460 #elif defined(JS_CODEGEN_ARM64) 2461 pop2xI64(r0, r1); 2462 #elif defined(JS_CODEGEN_LOONG64) 2463 pop2xI64(r0, r1); 2464 #elif defined(JS_CODEGEN_RISCV64) 2465 pop2xI64(r0, r1); 2466 #else 2467 MOZ_CRASH("BaseCompiler porting interface: popAndAllocateForMulI64"); 2468 #endif 2469 } 2470 2471 #ifndef RABALDR_INT_DIV_I64_CALLOUT 2472 2473 void BaseCompiler::popAndAllocateForDivAndRemI64(RegI64* r0, RegI64* r1, 2474 RegI64* reserved, 2475 IsRemainder isRemainder) { 2476 # if defined(JS_CODEGEN_X64) 2477 // r0 must be rax, and rdx will be clobbered. 2478 need2xI64(specific_.rax, specific_.rdx); 2479 *r1 = popI64(); 2480 *r0 = popI64ToSpecific(specific_.rax); 2481 *reserved = specific_.rdx; 2482 # elif defined(JS_CODEGEN_ARM64) 2483 pop2xI64(r0, r1); 2484 if (isRemainder) { 2485 *reserved = needI64(); 2486 } 2487 # else 2488 pop2xI64(r0, r1); 2489 # endif 2490 } 2491 2492 static void QuotientI64(MacroAssembler& masm, RegI64 rhs, RegI64 srcDest, 2493 RegI64 reserved, IsUnsigned isUnsigned) { 2494 # if defined(JS_CODEGEN_X64) 2495 // The caller must set up the following situation. 2496 MOZ_ASSERT(srcDest.reg == rax); 2497 MOZ_ASSERT(reserved.reg == rdx); 2498 if (isUnsigned) { 2499 masm.xorq(rdx, rdx); 2500 masm.udivq(rhs.reg); 2501 } else { 2502 masm.cqo(); 2503 masm.idivq(rhs.reg); 2504 } 2505 # elif !defined(JS_CODEGEN_NONE) && !defined(JS_CODEGEN_WASM32) 2506 masm.quotient64(srcDest.reg, rhs.reg, srcDest.reg, isUnsigned); 2507 # else 2508 MOZ_CRASH("BaseCompiler platform hook: quotientI64"); 2509 # endif 2510 } 2511 2512 static void RemainderI64(MacroAssembler& masm, RegI64 rhs, RegI64 srcDest, 2513 RegI64 reserved, IsUnsigned isUnsigned) { 2514 # if defined(JS_CODEGEN_X64) 2515 // The caller must set up the following situation. 2516 MOZ_ASSERT(srcDest.reg == rax); 2517 MOZ_ASSERT(reserved.reg == rdx); 2518 2519 if (isUnsigned) { 2520 masm.xorq(rdx, rdx); 2521 masm.udivq(rhs.reg); 2522 } else { 2523 masm.cqo(); 2524 masm.idivq(rhs.reg); 2525 } 2526 masm.movq(rdx, rax); 2527 # elif !defined(JS_CODEGEN_NONE) && !defined(JS_CODEGEN_WASM32) 2528 masm.remainder64(srcDest.reg, rhs.reg, srcDest.reg, isUnsigned); 2529 # else 2530 MOZ_CRASH("BaseCompiler platform hook: remainderI64"); 2531 # endif 2532 } 2533 2534 #endif // RABALDR_INT_DIV_I64_CALLOUT 2535 2536 RegI32 BaseCompiler::popI32RhsForShift() { 2537 #if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64) 2538 // r1 must be ecx for a variable shift, unless BMI2 is available. 2539 if (!Assembler::HasBMI2()) { 2540 return popI32(specific_.ecx); 2541 } 2542 #endif 2543 RegI32 r = popI32(); 2544 #if defined(JS_CODEGEN_ARM) 2545 masm.and32(Imm32(31), r); 2546 #endif 2547 return r; 2548 } 2549 2550 RegI32 BaseCompiler::popI32RhsForShiftI64() { 2551 #if defined(JS_CODEGEN_X86) 2552 // A limitation in the x86 masm requires ecx here 2553 return popI32(specific_.ecx); 2554 #elif defined(JS_CODEGEN_X64) 2555 if (!Assembler::HasBMI2()) { 2556 return popI32(specific_.ecx); 2557 } 2558 return popI32(); 2559 #else 2560 return popI32(); 2561 #endif 2562 } 2563 2564 RegI64 BaseCompiler::popI64RhsForShift() { 2565 #if defined(JS_CODEGEN_X86) 2566 // r1 must be ecx for a variable shift. 2567 needI32(specific_.ecx); 2568 return popI64ToSpecific(widenI32(specific_.ecx)); 2569 #else 2570 # if defined(JS_CODEGEN_X64) 2571 // r1 must be rcx for a variable shift, unless BMI2 is available. 2572 if (!Assembler::HasBMI2()) { 2573 needI64(specific_.rcx); 2574 return popI64ToSpecific(specific_.rcx); 2575 } 2576 # endif 2577 // No masking is necessary on 64-bit platforms, and on arm32 the masm 2578 // implementation masks. 2579 return popI64(); 2580 #endif 2581 } 2582 2583 RegI32 BaseCompiler::popI32RhsForRotate() { 2584 #if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64) 2585 // r1 must be ecx for a variable rotate. 2586 return popI32(specific_.ecx); 2587 #else 2588 return popI32(); 2589 #endif 2590 } 2591 2592 RegI64 BaseCompiler::popI64RhsForRotate() { 2593 #if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64) 2594 // r1 must be ecx for a variable rotate. 2595 needI32(specific_.ecx); 2596 return popI64ToSpecific(widenI32(specific_.ecx)); 2597 #else 2598 return popI64(); 2599 #endif 2600 } 2601 2602 void BaseCompiler::popI32ForSignExtendI64(RegI64* r0) { 2603 #if defined(JS_CODEGEN_X86) 2604 // r0 must be edx:eax for cdq 2605 need2xI32(specific_.edx, specific_.eax); 2606 *r0 = specific_.edx_eax; 2607 popI32ToSpecific(specific_.eax); 2608 #else 2609 *r0 = widenI32(popI32()); 2610 #endif 2611 } 2612 2613 void BaseCompiler::popI64ForSignExtendI64(RegI64* r0) { 2614 #if defined(JS_CODEGEN_X86) 2615 // r0 must be edx:eax for cdq 2616 need2xI32(specific_.edx, specific_.eax); 2617 // Low on top, high underneath 2618 *r0 = popI64ToSpecific(specific_.edx_eax); 2619 #else 2620 *r0 = popI64(); 2621 #endif 2622 } 2623 2624 class OutOfLineTruncateCheckF32OrF64ToI32 : public OutOfLineCode { 2625 AnyReg src; 2626 RegI32 dest; 2627 TruncFlags flags; 2628 TrapSiteDesc trapSiteDesc; 2629 2630 public: 2631 OutOfLineTruncateCheckF32OrF64ToI32(AnyReg src, RegI32 dest, TruncFlags flags, 2632 TrapSiteDesc trapSiteDesc) 2633 : src(src), dest(dest), flags(flags), trapSiteDesc(trapSiteDesc) {} 2634 2635 virtual void generate(MacroAssembler* masm) override { 2636 if (src.tag == AnyReg::F32) { 2637 masm->oolWasmTruncateCheckF32ToI32(src.f32(), dest, flags, trapSiteDesc, 2638 rejoin()); 2639 } else if (src.tag == AnyReg::F64) { 2640 masm->oolWasmTruncateCheckF64ToI32(src.f64(), dest, flags, trapSiteDesc, 2641 rejoin()); 2642 } else { 2643 MOZ_CRASH("unexpected type"); 2644 } 2645 } 2646 }; 2647 2648 bool BaseCompiler::truncateF32ToI32(RegF32 src, RegI32 dest, TruncFlags flags) { 2649 OutOfLineCode* ool = 2650 addOutOfLineCode(new (alloc_) OutOfLineTruncateCheckF32OrF64ToI32( 2651 AnyReg(src), dest, flags, trapSiteDesc())); 2652 if (!ool) { 2653 return false; 2654 } 2655 bool isSaturating = flags & TRUNC_SATURATING; 2656 if (flags & TRUNC_UNSIGNED) { 2657 masm.wasmTruncateFloat32ToUInt32(src, dest, isSaturating, ool->entry()); 2658 } else { 2659 masm.wasmTruncateFloat32ToInt32(src, dest, isSaturating, ool->entry()); 2660 } 2661 masm.bind(ool->rejoin()); 2662 return true; 2663 } 2664 2665 bool BaseCompiler::truncateF64ToI32(RegF64 src, RegI32 dest, TruncFlags flags) { 2666 OutOfLineCode* ool = 2667 addOutOfLineCode(new (alloc_) OutOfLineTruncateCheckF32OrF64ToI32( 2668 AnyReg(src), dest, flags, trapSiteDesc())); 2669 if (!ool) { 2670 return false; 2671 } 2672 bool isSaturating = flags & TRUNC_SATURATING; 2673 if (flags & TRUNC_UNSIGNED) { 2674 masm.wasmTruncateDoubleToUInt32(src, dest, isSaturating, ool->entry()); 2675 } else { 2676 masm.wasmTruncateDoubleToInt32(src, dest, isSaturating, ool->entry()); 2677 } 2678 masm.bind(ool->rejoin()); 2679 return true; 2680 } 2681 2682 class OutOfLineTruncateCheckF32OrF64ToI64 : public OutOfLineCode { 2683 AnyReg src; 2684 RegI64 dest; 2685 TruncFlags flags; 2686 TrapSiteDesc trapSiteDesc; 2687 2688 public: 2689 OutOfLineTruncateCheckF32OrF64ToI64(AnyReg src, RegI64 dest, TruncFlags flags, 2690 TrapSiteDesc trapSiteDesc) 2691 : src(src), dest(dest), flags(flags), trapSiteDesc(trapSiteDesc) {} 2692 2693 virtual void generate(MacroAssembler* masm) override { 2694 if (src.tag == AnyReg::F32) { 2695 masm->oolWasmTruncateCheckF32ToI64(src.f32(), dest, flags, trapSiteDesc, 2696 rejoin()); 2697 } else if (src.tag == AnyReg::F64) { 2698 masm->oolWasmTruncateCheckF64ToI64(src.f64(), dest, flags, trapSiteDesc, 2699 rejoin()); 2700 } else { 2701 MOZ_CRASH("unexpected type"); 2702 } 2703 } 2704 }; 2705 2706 #ifndef RABALDR_FLOAT_TO_I64_CALLOUT 2707 2708 RegF64 BaseCompiler::needTempForFloatingToI64(TruncFlags flags) { 2709 # if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64) 2710 if (flags & TRUNC_UNSIGNED) { 2711 return needF64(); 2712 } 2713 # endif 2714 return RegF64::Invalid(); 2715 } 2716 2717 bool BaseCompiler::truncateF32ToI64(RegF32 src, RegI64 dest, TruncFlags flags, 2718 RegF64 temp) { 2719 OutOfLineCode* ool = 2720 addOutOfLineCode(new (alloc_) OutOfLineTruncateCheckF32OrF64ToI64( 2721 AnyReg(src), dest, flags, trapSiteDesc())); 2722 if (!ool) { 2723 return false; 2724 } 2725 bool isSaturating = flags & TRUNC_SATURATING; 2726 if (flags & TRUNC_UNSIGNED) { 2727 masm.wasmTruncateFloat32ToUInt64(src, dest, isSaturating, ool->entry(), 2728 ool->rejoin(), temp); 2729 } else { 2730 masm.wasmTruncateFloat32ToInt64(src, dest, isSaturating, ool->entry(), 2731 ool->rejoin(), temp); 2732 } 2733 return true; 2734 } 2735 2736 bool BaseCompiler::truncateF64ToI64(RegF64 src, RegI64 dest, TruncFlags flags, 2737 RegF64 temp) { 2738 OutOfLineCode* ool = 2739 addOutOfLineCode(new (alloc_) OutOfLineTruncateCheckF32OrF64ToI64( 2740 AnyReg(src), dest, flags, trapSiteDesc())); 2741 if (!ool) { 2742 return false; 2743 } 2744 bool isSaturating = flags & TRUNC_SATURATING; 2745 if (flags & TRUNC_UNSIGNED) { 2746 masm.wasmTruncateDoubleToUInt64(src, dest, isSaturating, ool->entry(), 2747 ool->rejoin(), temp); 2748 } else { 2749 masm.wasmTruncateDoubleToInt64(src, dest, isSaturating, ool->entry(), 2750 ool->rejoin(), temp); 2751 } 2752 return true; 2753 } 2754 2755 #endif // RABALDR_FLOAT_TO_I64_CALLOUT 2756 2757 #ifndef RABALDR_I64_TO_FLOAT_CALLOUT 2758 2759 RegI32 BaseCompiler::needConvertI64ToFloatTemp(ValType to, bool isUnsigned) { 2760 bool needs = false; 2761 if (to == ValType::F64) { 2762 needs = isUnsigned && masm.convertUInt64ToDoubleNeedsTemp(); 2763 } else { 2764 # if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64) 2765 needs = true; 2766 # endif 2767 } 2768 return needs ? needI32() : RegI32::Invalid(); 2769 } 2770 2771 void BaseCompiler::convertI64ToF32(RegI64 src, bool isUnsigned, RegF32 dest, 2772 RegI32 temp) { 2773 if (isUnsigned) { 2774 masm.convertUInt64ToFloat32(src, dest, temp); 2775 } else { 2776 masm.convertInt64ToFloat32(src, dest); 2777 } 2778 } 2779 2780 void BaseCompiler::convertI64ToF64(RegI64 src, bool isUnsigned, RegF64 dest, 2781 RegI32 temp) { 2782 if (isUnsigned) { 2783 masm.convertUInt64ToDouble(src, dest, temp); 2784 } else { 2785 masm.convertInt64ToDouble(src, dest); 2786 } 2787 } 2788 2789 #endif // RABALDR_I64_TO_FLOAT_CALLOUT 2790 2791 ////////////////////////////////////////////////////////////////////// 2792 // 2793 // Global variable access. 2794 2795 Address BaseCompiler::addressOfGlobalVar(const GlobalDesc& global, RegPtr tmp) { 2796 uint32_t globalToInstanceOffset = Instance::offsetInData(global.offset()); 2797 #ifdef RABALDR_PIN_INSTANCE 2798 movePtr(RegPtr(InstanceReg), tmp); 2799 #else 2800 fr.loadInstancePtr(tmp); 2801 #endif 2802 if (global.isIndirect()) { 2803 masm.loadPtr(Address(tmp, globalToInstanceOffset), tmp); 2804 return Address(tmp, 0); 2805 } 2806 return Address(tmp, globalToInstanceOffset); 2807 } 2808 2809 ////////////////////////////////////////////////////////////////////// 2810 // 2811 // Table access. 2812 2813 Address BaseCompiler::addressOfTableField(uint32_t tableIndex, 2814 uint32_t fieldOffset, 2815 RegPtr instance) { 2816 uint32_t tableToInstanceOffset = wasm::Instance::offsetInData( 2817 codeMeta_.offsetOfTableInstanceData(tableIndex) + fieldOffset); 2818 return Address(instance, tableToInstanceOffset); 2819 } 2820 2821 void BaseCompiler::loadTableLength(uint32_t tableIndex, RegPtr instance, 2822 RegI32 length) { 2823 masm.load32(addressOfTableField( 2824 tableIndex, offsetof(TableInstanceData, length), instance), 2825 length); 2826 } 2827 2828 void BaseCompiler::loadTableElements(uint32_t tableIndex, RegPtr instance, 2829 RegPtr elements) { 2830 masm.loadPtr(addressOfTableField( 2831 tableIndex, offsetof(TableInstanceData, elements), instance), 2832 elements); 2833 } 2834 2835 ////////////////////////////////////////////////////////////////////////////// 2836 // 2837 // Basic emitters for simple operators. 2838 2839 static void AddI32(MacroAssembler& masm, RegI32 rs, RegI32 rsd) { 2840 masm.add32(rs, rsd); 2841 } 2842 2843 static void AddImmI32(MacroAssembler& masm, int32_t c, RegI32 rsd) { 2844 masm.add32(Imm32(c), rsd); 2845 } 2846 2847 static void SubI32(MacroAssembler& masm, RegI32 rs, RegI32 rsd) { 2848 masm.sub32(rs, rsd); 2849 } 2850 2851 static void SubImmI32(MacroAssembler& masm, int32_t c, RegI32 rsd) { 2852 masm.sub32(Imm32(c), rsd); 2853 } 2854 2855 static void MulI32(MacroAssembler& masm, RegI32 rs, RegI32 rsd) { 2856 masm.mul32(rs, rsd); 2857 } 2858 2859 static void OrI32(MacroAssembler& masm, RegI32 rs, RegI32 rsd) { 2860 masm.or32(rs, rsd); 2861 } 2862 2863 static void OrImmI32(MacroAssembler& masm, int32_t c, RegI32 rsd) { 2864 masm.or32(Imm32(c), rsd); 2865 } 2866 2867 static void AndI32(MacroAssembler& masm, RegI32 rs, RegI32 rsd) { 2868 masm.and32(rs, rsd); 2869 } 2870 2871 static void AndImmI32(MacroAssembler& masm, int32_t c, RegI32 rsd) { 2872 masm.and32(Imm32(c), rsd); 2873 } 2874 2875 static void XorI32(MacroAssembler& masm, RegI32 rs, RegI32 rsd) { 2876 masm.xor32(rs, rsd); 2877 } 2878 2879 static void XorImmI32(MacroAssembler& masm, int32_t c, RegI32 rsd) { 2880 masm.xor32(Imm32(c), rsd); 2881 } 2882 2883 static void ClzI32(MacroAssembler& masm, RegI32 rsd) { 2884 masm.clz32(rsd, rsd, IsKnownNotZero(false)); 2885 } 2886 2887 static void CtzI32(MacroAssembler& masm, RegI32 rsd) { 2888 masm.ctz32(rsd, rsd, IsKnownNotZero(false)); 2889 } 2890 2891 // Currently common to PopcntI32 and PopcntI64 2892 static RegI32 PopcntTemp(BaseCompiler& bc) { 2893 #if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64) 2894 return AssemblerX86Shared::HasPOPCNT() ? RegI32::Invalid() : bc.needI32(); 2895 #elif defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_ARM64) || \ 2896 defined(JS_CODEGEN_MIPS64) || defined(JS_CODEGEN_LOONG64) || \ 2897 defined(JS_CODEGEN_RISCV64) 2898 return bc.needI32(); 2899 #else 2900 MOZ_CRASH("BaseCompiler platform hook: PopcntTemp"); 2901 #endif 2902 } 2903 2904 static void PopcntI32(BaseCompiler& bc, RegI32 rsd, RegI32 temp) { 2905 bc.masm.popcnt32(rsd, rsd, temp); 2906 } 2907 2908 static void ShlI32(MacroAssembler& masm, RegI32 rs, RegI32 rsd) { 2909 masm.lshift32(rs, rsd); 2910 } 2911 2912 static void ShlImmI32(MacroAssembler& masm, int32_t c, RegI32 rsd) { 2913 masm.lshift32(Imm32(c & 31), rsd); 2914 } 2915 2916 static void ShrI32(MacroAssembler& masm, RegI32 rs, RegI32 rsd) { 2917 masm.rshift32Arithmetic(rs, rsd); 2918 } 2919 2920 static void ShrImmI32(MacroAssembler& masm, int32_t c, RegI32 rsd) { 2921 masm.rshift32Arithmetic(Imm32(c & 31), rsd); 2922 } 2923 2924 static void ShrUI32(MacroAssembler& masm, RegI32 rs, RegI32 rsd) { 2925 masm.rshift32(rs, rsd); 2926 } 2927 2928 static void ShrUImmI32(MacroAssembler& masm, int32_t c, RegI32 rsd) { 2929 masm.rshift32(Imm32(c & 31), rsd); 2930 } 2931 2932 static void RotlI32(MacroAssembler& masm, RegI32 rs, RegI32 rsd) { 2933 masm.rotateLeft(rs, rsd, rsd); 2934 } 2935 2936 static void RotlImmI32(MacroAssembler& masm, int32_t c, RegI32 rsd) { 2937 masm.rotateLeft(Imm32(c & 31), rsd, rsd); 2938 } 2939 2940 static void RotrI32(MacroAssembler& masm, RegI32 rs, RegI32 rsd) { 2941 masm.rotateRight(rs, rsd, rsd); 2942 } 2943 2944 static void RotrImmI32(MacroAssembler& masm, int32_t c, RegI32 rsd) { 2945 masm.rotateRight(Imm32(c & 31), rsd, rsd); 2946 } 2947 2948 static void EqzI32(MacroAssembler& masm, RegI32 rsd) { 2949 masm.cmp32Set(Assembler::Equal, rsd, Imm32(0), rsd); 2950 } 2951 2952 static void WrapI64ToI32(MacroAssembler& masm, RegI64 rs, RegI32 rd) { 2953 masm.move64To32(rs, rd); 2954 } 2955 2956 static void AddI64(MacroAssembler& masm, RegI64 rs, RegI64 rsd) { 2957 masm.add64(rs, rsd); 2958 } 2959 2960 static void AddImmI64(MacroAssembler& masm, int64_t c, RegI64 rsd) { 2961 masm.add64(Imm64(c), rsd); 2962 } 2963 2964 static void SubI64(MacroAssembler& masm, RegI64 rs, RegI64 rsd) { 2965 masm.sub64(rs, rsd); 2966 } 2967 2968 static void SubImmI64(MacroAssembler& masm, int64_t c, RegI64 rsd) { 2969 masm.sub64(Imm64(c), rsd); 2970 } 2971 2972 static void OrI64(MacroAssembler& masm, RegI64 rs, RegI64 rsd) { 2973 masm.or64(rs, rsd); 2974 } 2975 2976 static void OrImmI64(MacroAssembler& masm, int64_t c, RegI64 rsd) { 2977 masm.or64(Imm64(c), rsd); 2978 } 2979 2980 static void AndI64(MacroAssembler& masm, RegI64 rs, RegI64 rsd) { 2981 masm.and64(rs, rsd); 2982 } 2983 2984 static void AndImmI64(MacroAssembler& masm, int64_t c, RegI64 rsd) { 2985 masm.and64(Imm64(c), rsd); 2986 } 2987 2988 static void XorI64(MacroAssembler& masm, RegI64 rs, RegI64 rsd) { 2989 masm.xor64(rs, rsd); 2990 } 2991 2992 static void XorImmI64(MacroAssembler& masm, int64_t c, RegI64 rsd) { 2993 masm.xor64(Imm64(c), rsd); 2994 } 2995 2996 static void ClzI64(BaseCompiler& bc, RegI64 rsd) { bc.masm.clz64(rsd, rsd); } 2997 2998 static void CtzI64(BaseCompiler& bc, RegI64 rsd) { bc.masm.ctz64(rsd, rsd); } 2999 3000 static void PopcntI64(BaseCompiler& bc, RegI64 rsd, RegI32 temp) { 3001 bc.masm.popcnt64(rsd, rsd, temp); 3002 } 3003 3004 static void ShlI64(BaseCompiler& bc, RegI64 rs, RegI64 rsd) { 3005 bc.masm.lshift64(bc.lowPart(rs), rsd); 3006 } 3007 3008 static void ShlImmI64(MacroAssembler& masm, int64_t c, RegI64 rsd) { 3009 masm.lshift64(Imm32(c & 63), rsd); 3010 } 3011 3012 static void ShrI64(BaseCompiler& bc, RegI64 rs, RegI64 rsd) { 3013 bc.masm.rshift64Arithmetic(bc.lowPart(rs), rsd); 3014 } 3015 3016 static void ShrImmI64(MacroAssembler& masm, int64_t c, RegI64 rsd) { 3017 masm.rshift64Arithmetic(Imm32(c & 63), rsd); 3018 } 3019 3020 static void ShrUI64(BaseCompiler& bc, RegI64 rs, RegI64 rsd) { 3021 bc.masm.rshift64(bc.lowPart(rs), rsd); 3022 } 3023 3024 static void ShrUImmI64(MacroAssembler& masm, int64_t c, RegI64 rsd) { 3025 masm.rshift64(Imm32(c & 63), rsd); 3026 } 3027 3028 static void EqzI64(MacroAssembler& masm, RegI64 rs, RegI32 rd) { 3029 #ifdef JS_PUNBOX64 3030 masm.cmpPtrSet(Assembler::Equal, rs.reg, ImmWord(0), rd); 3031 #else 3032 MOZ_ASSERT(rs.low == rd); 3033 masm.or32(rs.high, rs.low); 3034 masm.cmp32Set(Assembler::Equal, rs.low, Imm32(0), rd); 3035 #endif 3036 } 3037 3038 static void AddF64(MacroAssembler& masm, RegF64 rs, RegF64 rsd) { 3039 masm.addDouble(rs, rsd); 3040 } 3041 3042 static void SubF64(MacroAssembler& masm, RegF64 rs, RegF64 rsd) { 3043 masm.subDouble(rs, rsd); 3044 } 3045 3046 static void MulF64(MacroAssembler& masm, RegF64 rs, RegF64 rsd) { 3047 masm.mulDouble(rs, rsd); 3048 } 3049 3050 static void DivF64(MacroAssembler& masm, RegF64 rs, RegF64 rsd) { 3051 masm.divDouble(rs, rsd); 3052 } 3053 3054 static void MinF64(BaseCompiler& bc, RegF64 rs, RegF64 rsd) { 3055 // Convert signaling NaN to quiet NaNs. 3056 // 3057 // TODO / OPTIMIZE (bug 1316824): see comment in MinF32. 3058 #ifdef RABALDR_SCRATCH_F64 3059 ScratchF64 zero(bc.ra); 3060 #else 3061 ScratchF64 zero(bc.masm); 3062 #endif 3063 bc.masm.loadConstantDouble(0, zero); 3064 bc.masm.subDouble(zero, rsd); 3065 bc.masm.subDouble(zero, rs); 3066 bc.masm.minDouble(rs, rsd, HandleNaNSpecially(true)); 3067 } 3068 3069 static void MaxF64(BaseCompiler& bc, RegF64 rs, RegF64 rsd) { 3070 // Convert signaling NaN to quiet NaNs. 3071 // 3072 // TODO / OPTIMIZE (bug 1316824): see comment in MinF32. 3073 #ifdef RABALDR_SCRATCH_F64 3074 ScratchF64 zero(bc.ra); 3075 #else 3076 ScratchF64 zero(bc.masm); 3077 #endif 3078 bc.masm.loadConstantDouble(0, zero); 3079 bc.masm.subDouble(zero, rsd); 3080 bc.masm.subDouble(zero, rs); 3081 bc.masm.maxDouble(rs, rsd, HandleNaNSpecially(true)); 3082 } 3083 3084 static void CopysignF64(MacroAssembler& masm, RegF64 rs, RegF64 rsd) { 3085 // No code generated for the no-op case. 3086 if (rs == rsd) { 3087 return; 3088 } 3089 masm.copySignDouble(rsd, rs, rsd); 3090 } 3091 3092 static void AbsF64(MacroAssembler& masm, RegF64 rsd) { 3093 masm.absDouble(rsd, rsd); 3094 } 3095 3096 static void NegateF64(MacroAssembler& masm, RegF64 rsd) { 3097 masm.negateDouble(rsd); 3098 } 3099 3100 static void SqrtF64(MacroAssembler& masm, RegF64 rsd) { 3101 masm.sqrtDouble(rsd, rsd); 3102 } 3103 3104 static void AddF32(MacroAssembler& masm, RegF32 rs, RegF32 rsd) { 3105 masm.addFloat32(rs, rsd); 3106 } 3107 3108 static void SubF32(MacroAssembler& masm, RegF32 rs, RegF32 rsd) { 3109 masm.subFloat32(rs, rsd); 3110 } 3111 3112 static void MulF32(MacroAssembler& masm, RegF32 rs, RegF32 rsd) { 3113 masm.mulFloat32(rs, rsd); 3114 } 3115 3116 static void DivF32(MacroAssembler& masm, RegF32 rs, RegF32 rsd) { 3117 masm.divFloat32(rs, rsd); 3118 } 3119 3120 static void MinF32(BaseCompiler& bc, RegF32 rs, RegF32 rsd) { 3121 // Convert signaling NaN to quiet NaNs. 3122 // 3123 // TODO / OPTIMIZE (bug 1316824): Don't do this if one of the operands 3124 // is known to be a constant. 3125 #ifdef RABALDR_SCRATCH_F32 3126 ScratchF32 zero(bc.ra); 3127 #else 3128 ScratchF32 zero(bc.masm); 3129 #endif 3130 bc.masm.loadConstantFloat32(0.f, zero); 3131 bc.masm.subFloat32(zero, rsd); 3132 bc.masm.subFloat32(zero, rs); 3133 bc.masm.minFloat32(rs, rsd, HandleNaNSpecially(true)); 3134 } 3135 3136 static void MaxF32(BaseCompiler& bc, RegF32 rs, RegF32 rsd) { 3137 // Convert signaling NaN to quiet NaNs. 3138 // 3139 // TODO / OPTIMIZE (bug 1316824): see comment in MinF32. 3140 #ifdef RABALDR_SCRATCH_F32 3141 ScratchF32 zero(bc.ra); 3142 #else 3143 ScratchF32 zero(bc.masm); 3144 #endif 3145 bc.masm.loadConstantFloat32(0.f, zero); 3146 bc.masm.subFloat32(zero, rsd); 3147 bc.masm.subFloat32(zero, rs); 3148 bc.masm.maxFloat32(rs, rsd, HandleNaNSpecially(true)); 3149 } 3150 3151 static void CopysignF32(MacroAssembler& masm, RegF32 rs, RegF32 rsd) { 3152 // No code generated for the no-op case. 3153 if (rs == rsd) { 3154 return; 3155 } 3156 masm.copySignFloat32(rsd, rs, rsd); 3157 } 3158 3159 static void AbsF32(MacroAssembler& masm, RegF32 rsd) { 3160 masm.absFloat32(rsd, rsd); 3161 } 3162 3163 static void NegateF32(MacroAssembler& masm, RegF32 rsd) { 3164 masm.negateFloat(rsd); 3165 } 3166 3167 static void SqrtF32(MacroAssembler& masm, RegF32 rsd) { 3168 masm.sqrtFloat32(rsd, rsd); 3169 } 3170 3171 #ifndef RABALDR_I64_TO_FLOAT_CALLOUT 3172 static void ConvertI64ToF32(MacroAssembler& masm, RegI64 rs, RegF32 rd) { 3173 masm.convertInt64ToFloat32(rs, rd); 3174 } 3175 3176 static void ConvertI64ToF64(MacroAssembler& masm, RegI64 rs, RegF64 rd) { 3177 masm.convertInt64ToDouble(rs, rd); 3178 } 3179 #endif 3180 3181 static void ReinterpretF32AsI32(MacroAssembler& masm, RegF32 rs, RegI32 rd) { 3182 masm.moveFloat32ToGPR(rs, rd); 3183 } 3184 3185 static void ReinterpretF64AsI64(MacroAssembler& masm, RegF64 rs, RegI64 rd) { 3186 masm.moveDoubleToGPR64(rs, rd); 3187 } 3188 3189 static void ConvertF64ToF32(MacroAssembler& masm, RegF64 rs, RegF32 rd) { 3190 masm.convertDoubleToFloat32(rs, rd); 3191 } 3192 3193 static void ConvertI32ToF32(MacroAssembler& masm, RegI32 rs, RegF32 rd) { 3194 masm.convertInt32ToFloat32(rs, rd); 3195 } 3196 3197 static void ConvertU32ToF32(MacroAssembler& masm, RegI32 rs, RegF32 rd) { 3198 masm.convertUInt32ToFloat32(rs, rd); 3199 } 3200 3201 static void ConvertF32ToF64(MacroAssembler& masm, RegF32 rs, RegF64 rd) { 3202 masm.convertFloat32ToDouble(rs, rd); 3203 } 3204 3205 static void ConvertI32ToF64(MacroAssembler& masm, RegI32 rs, RegF64 rd) { 3206 masm.convertInt32ToDouble(rs, rd); 3207 } 3208 3209 static void ConvertU32ToF64(MacroAssembler& masm, RegI32 rs, RegF64 rd) { 3210 masm.convertUInt32ToDouble(rs, rd); 3211 } 3212 3213 static void ReinterpretI32AsF32(MacroAssembler& masm, RegI32 rs, RegF32 rd) { 3214 masm.moveGPRToFloat32(rs, rd); 3215 } 3216 3217 static void ReinterpretI64AsF64(MacroAssembler& masm, RegI64 rs, RegF64 rd) { 3218 masm.moveGPR64ToDouble(rs, rd); 3219 } 3220 3221 static void ExtendI32_8(BaseCompiler& bc, RegI32 rsd) { 3222 #ifdef JS_CODEGEN_X86 3223 if (!bc.ra.isSingleByteI32(rsd)) { 3224 ScratchI8 scratch(bc.ra); 3225 bc.masm.move32(rsd, scratch); 3226 bc.masm.move8SignExtend(scratch, rsd); 3227 return; 3228 } 3229 #endif 3230 bc.masm.move8SignExtend(rsd, rsd); 3231 } 3232 3233 static void ExtendI32_16(MacroAssembler& masm, RegI32 rsd) { 3234 masm.move16SignExtend(rsd, rsd); 3235 } 3236 3237 void BaseCompiler::emitMultiplyI64() { 3238 RegI64 r, rs; 3239 RegI32 temp; 3240 popAndAllocateForMulI64(&r, &rs, &temp); 3241 masm.mul64(rs, r, temp); 3242 maybeFree(temp); 3243 freeI64(rs); 3244 pushI64(r); 3245 } 3246 3247 template <typename RegType, typename IntType> 3248 void BaseCompiler::quotientOrRemainder( 3249 RegType rs, RegType rsd, RegType reserved, IsUnsigned isUnsigned, 3250 ZeroOnOverflow zeroOnOverflow, bool isConst, IntType c, 3251 void (*operate)(MacroAssembler& masm, RegType rs, RegType rsd, 3252 RegType reserved, IsUnsigned isUnsigned)) { 3253 Label done; 3254 if (!isConst || c == 0) { 3255 checkDivideByZero(rs); 3256 } 3257 if (!isUnsigned && (!isConst || c == -1)) { 3258 checkDivideSignedOverflow(rs, rsd, &done, zeroOnOverflow); 3259 } 3260 operate(masm, rs, rsd, reserved, isUnsigned); 3261 masm.bind(&done); 3262 } 3263 3264 void BaseCompiler::emitQuotientI32() { 3265 int32_t c; 3266 uint_fast8_t power; 3267 if (popConstPositivePowerOfTwo(&c, &power, 0)) { 3268 if (power != 0) { 3269 RegI32 r = popI32(); 3270 Label positive; 3271 masm.branchTest32(Assembler::NotSigned, r, r, &positive); 3272 masm.add32(Imm32(c - 1), r); 3273 masm.bind(&positive); 3274 3275 masm.rshift32Arithmetic(Imm32(power & 31), r); 3276 pushI32(r); 3277 } 3278 } else { 3279 bool isConst = peekConst(&c); 3280 RegI32 r, rs, reserved; 3281 popAndAllocateForDivAndRemI32(&r, &rs, &reserved); 3282 quotientOrRemainder(rs, r, reserved, IsUnsigned(false), 3283 ZeroOnOverflow(false), isConst, c, QuotientI32); 3284 maybeFree(reserved); 3285 freeI32(rs); 3286 pushI32(r); 3287 } 3288 } 3289 3290 void BaseCompiler::emitQuotientU32() { 3291 int32_t c; 3292 uint_fast8_t power; 3293 if (popConstPositivePowerOfTwo(&c, &power, 0)) { 3294 if (power != 0) { 3295 RegI32 r = popI32(); 3296 masm.rshift32(Imm32(power & 31), r); 3297 pushI32(r); 3298 } 3299 } else { 3300 bool isConst = peekConst(&c); 3301 RegI32 r, rs, reserved; 3302 popAndAllocateForDivAndRemI32(&r, &rs, &reserved); 3303 quotientOrRemainder(rs, r, reserved, IsUnsigned(true), 3304 ZeroOnOverflow(false), isConst, c, QuotientI32); 3305 maybeFree(reserved); 3306 freeI32(rs); 3307 pushI32(r); 3308 } 3309 } 3310 3311 void BaseCompiler::emitRemainderI32() { 3312 int32_t c; 3313 uint_fast8_t power; 3314 if (popConstPositivePowerOfTwo(&c, &power, 1)) { 3315 RegI32 r = popI32(); 3316 RegI32 temp = needI32(); 3317 moveI32(r, temp); 3318 3319 Label positive; 3320 masm.branchTest32(Assembler::NotSigned, temp, temp, &positive); 3321 masm.add32(Imm32(c - 1), temp); 3322 masm.bind(&positive); 3323 3324 masm.rshift32Arithmetic(Imm32(power & 31), temp); 3325 masm.lshift32(Imm32(power & 31), temp); 3326 masm.sub32(temp, r); 3327 freeI32(temp); 3328 3329 pushI32(r); 3330 } else { 3331 bool isConst = peekConst(&c); 3332 RegI32 r, rs, reserved; 3333 popAndAllocateForDivAndRemI32(&r, &rs, &reserved); 3334 quotientOrRemainder(rs, r, reserved, IsUnsigned(false), 3335 ZeroOnOverflow(true), isConst, c, RemainderI32); 3336 maybeFree(reserved); 3337 freeI32(rs); 3338 pushI32(r); 3339 } 3340 } 3341 3342 void BaseCompiler::emitRemainderU32() { 3343 int32_t c; 3344 uint_fast8_t power; 3345 if (popConstPositivePowerOfTwo(&c, &power, 1)) { 3346 RegI32 r = popI32(); 3347 masm.and32(Imm32(c - 1), r); 3348 pushI32(r); 3349 } else { 3350 bool isConst = peekConst(&c); 3351 RegI32 r, rs, reserved; 3352 popAndAllocateForDivAndRemI32(&r, &rs, &reserved); 3353 quotientOrRemainder(rs, r, reserved, IsUnsigned(true), ZeroOnOverflow(true), 3354 isConst, c, RemainderI32); 3355 maybeFree(reserved); 3356 freeI32(rs); 3357 pushI32(r); 3358 } 3359 } 3360 3361 #ifndef RABALDR_INT_DIV_I64_CALLOUT 3362 void BaseCompiler::emitQuotientI64() { 3363 int64_t c; 3364 uint_fast8_t power; 3365 if (popConstPositivePowerOfTwo(&c, &power, 0)) { 3366 if (power != 0) { 3367 RegI64 r = popI64(); 3368 Label positive; 3369 masm.branchTest64(Assembler::NotSigned, r, r, &positive); 3370 masm.add64(Imm64(c - 1), r); 3371 masm.bind(&positive); 3372 3373 masm.rshift64Arithmetic(Imm32(power & 63), r); 3374 pushI64(r); 3375 } 3376 } else { 3377 bool isConst = peekConst(&c); 3378 RegI64 r, rs, reserved; 3379 popAndAllocateForDivAndRemI64(&r, &rs, &reserved, IsRemainder(false)); 3380 quotientOrRemainder(rs, r, reserved, IsUnsigned(false), 3381 ZeroOnOverflow(false), isConst, c, QuotientI64); 3382 maybeFree(reserved); 3383 freeI64(rs); 3384 pushI64(r); 3385 } 3386 } 3387 3388 void BaseCompiler::emitQuotientU64() { 3389 int64_t c; 3390 uint_fast8_t power; 3391 if (popConstPositivePowerOfTwo(&c, &power, 0)) { 3392 if (power != 0) { 3393 RegI64 r = popI64(); 3394 masm.rshift64(Imm32(power & 63), r); 3395 pushI64(r); 3396 } 3397 } else { 3398 bool isConst = peekConst(&c); 3399 RegI64 r, rs, reserved; 3400 popAndAllocateForDivAndRemI64(&r, &rs, &reserved, IsRemainder(false)); 3401 quotientOrRemainder(rs, r, reserved, IsUnsigned(true), 3402 ZeroOnOverflow(false), isConst, c, QuotientI64); 3403 maybeFree(reserved); 3404 freeI64(rs); 3405 pushI64(r); 3406 } 3407 } 3408 3409 void BaseCompiler::emitRemainderI64() { 3410 int64_t c; 3411 uint_fast8_t power; 3412 if (popConstPositivePowerOfTwo(&c, &power, 1)) { 3413 RegI64 r = popI64(); 3414 RegI64 temp = needI64(); 3415 moveI64(r, temp); 3416 3417 Label positive; 3418 masm.branchTest64(Assembler::NotSigned, temp, temp, &positive); 3419 masm.add64(Imm64(c - 1), temp); 3420 masm.bind(&positive); 3421 3422 masm.rshift64Arithmetic(Imm32(power & 63), temp); 3423 masm.lshift64(Imm32(power & 63), temp); 3424 masm.sub64(temp, r); 3425 freeI64(temp); 3426 3427 pushI64(r); 3428 } else { 3429 bool isConst = peekConst(&c); 3430 RegI64 r, rs, reserved; 3431 popAndAllocateForDivAndRemI64(&r, &rs, &reserved, IsRemainder(true)); 3432 quotientOrRemainder(rs, r, reserved, IsUnsigned(false), 3433 ZeroOnOverflow(true), isConst, c, RemainderI64); 3434 maybeFree(reserved); 3435 freeI64(rs); 3436 pushI64(r); 3437 } 3438 } 3439 3440 void BaseCompiler::emitRemainderU64() { 3441 int64_t c; 3442 uint_fast8_t power; 3443 if (popConstPositivePowerOfTwo(&c, &power, 1)) { 3444 RegI64 r = popI64(); 3445 masm.and64(Imm64(c - 1), r); 3446 pushI64(r); 3447 } else { 3448 bool isConst = peekConst(&c); 3449 RegI64 r, rs, reserved; 3450 popAndAllocateForDivAndRemI64(&r, &rs, &reserved, IsRemainder(true)); 3451 quotientOrRemainder(rs, r, reserved, IsUnsigned(true), ZeroOnOverflow(true), 3452 isConst, c, RemainderI64); 3453 maybeFree(reserved); 3454 freeI64(rs); 3455 pushI64(r); 3456 } 3457 } 3458 #endif // RABALDR_INT_DIV_I64_CALLOUT 3459 3460 void BaseCompiler::emitRotrI64() { 3461 int64_t c; 3462 if (popConst(&c)) { 3463 RegI64 r = popI64(); 3464 RegI32 temp = needRotate64Temp(); 3465 masm.rotateRight64(Imm32(c & 63), r, r, temp); 3466 maybeFree(temp); 3467 pushI64(r); 3468 } else { 3469 RegI64 rs = popI64RhsForRotate(); 3470 RegI64 r = popI64(); 3471 masm.rotateRight64(lowPart(rs), r, r, maybeHighPart(rs)); 3472 freeI64(rs); 3473 pushI64(r); 3474 } 3475 } 3476 3477 void BaseCompiler::emitRotlI64() { 3478 int64_t c; 3479 if (popConst(&c)) { 3480 RegI64 r = popI64(); 3481 RegI32 temp = needRotate64Temp(); 3482 masm.rotateLeft64(Imm32(c & 63), r, r, temp); 3483 maybeFree(temp); 3484 pushI64(r); 3485 } else { 3486 RegI64 rs = popI64RhsForRotate(); 3487 RegI64 r = popI64(); 3488 masm.rotateLeft64(lowPart(rs), r, r, maybeHighPart(rs)); 3489 freeI64(rs); 3490 pushI64(r); 3491 } 3492 } 3493 3494 void BaseCompiler::emitEqzI32() { 3495 if (sniffConditionalControlEqz(ValType::I32)) { 3496 return; 3497 } 3498 emitUnop(EqzI32); 3499 } 3500 3501 void BaseCompiler::emitEqzI64() { 3502 if (sniffConditionalControlEqz(ValType::I64)) { 3503 return; 3504 } 3505 emitUnop(EqzI64); 3506 } 3507 3508 template <TruncFlags flags> 3509 bool BaseCompiler::emitTruncateF32ToI32() { 3510 RegF32 rs = popF32(); 3511 RegI32 rd = needI32(); 3512 if (!truncateF32ToI32(rs, rd, flags)) { 3513 return false; 3514 } 3515 freeF32(rs); 3516 pushI32(rd); 3517 return true; 3518 } 3519 3520 template <TruncFlags flags> 3521 bool BaseCompiler::emitTruncateF64ToI32() { 3522 RegF64 rs = popF64(); 3523 RegI32 rd = needI32(); 3524 if (!truncateF64ToI32(rs, rd, flags)) { 3525 return false; 3526 } 3527 freeF64(rs); 3528 pushI32(rd); 3529 return true; 3530 } 3531 3532 #ifndef RABALDR_FLOAT_TO_I64_CALLOUT 3533 template <TruncFlags flags> 3534 bool BaseCompiler::emitTruncateF32ToI64() { 3535 RegF32 rs = popF32(); 3536 RegI64 rd = needI64(); 3537 RegF64 temp = needTempForFloatingToI64(flags); 3538 if (!truncateF32ToI64(rs, rd, flags, temp)) { 3539 return false; 3540 } 3541 maybeFree(temp); 3542 freeF32(rs); 3543 pushI64(rd); 3544 return true; 3545 } 3546 3547 template <TruncFlags flags> 3548 bool BaseCompiler::emitTruncateF64ToI64() { 3549 RegF64 rs = popF64(); 3550 RegI64 rd = needI64(); 3551 RegF64 temp = needTempForFloatingToI64(flags); 3552 if (!truncateF64ToI64(rs, rd, flags, temp)) { 3553 return false; 3554 } 3555 maybeFree(temp); 3556 freeF64(rs); 3557 pushI64(rd); 3558 return true; 3559 } 3560 #endif // RABALDR_FLOAT_TO_I64_CALLOUT 3561 3562 void BaseCompiler::emitExtendI64_8() { 3563 RegI64 r; 3564 popI64ForSignExtendI64(&r); 3565 masm.move8To64SignExtend(lowPart(r), r); 3566 pushI64(r); 3567 } 3568 3569 void BaseCompiler::emitExtendI64_16() { 3570 RegI64 r; 3571 popI64ForSignExtendI64(&r); 3572 masm.move16To64SignExtend(lowPart(r), r); 3573 pushI64(r); 3574 } 3575 3576 void BaseCompiler::emitExtendI64_32() { 3577 RegI64 r; 3578 popI64ForSignExtendI64(&r); 3579 masm.move32To64SignExtend(lowPart(r), r); 3580 pushI64(r); 3581 } 3582 3583 void BaseCompiler::emitExtendI32ToI64() { 3584 RegI64 r; 3585 popI32ForSignExtendI64(&r); 3586 masm.move32To64SignExtend(lowPart(r), r); 3587 pushI64(r); 3588 } 3589 3590 void BaseCompiler::emitExtendU32ToI64() { 3591 RegI32 rs = popI32(); 3592 RegI64 rd = widenI32(rs); 3593 masm.move32To64ZeroExtend(rs, rd); 3594 pushI64(rd); 3595 } 3596 3597 #ifndef RABALDR_I64_TO_FLOAT_CALLOUT 3598 void BaseCompiler::emitConvertU64ToF32() { 3599 RegI64 rs = popI64(); 3600 RegF32 rd = needF32(); 3601 RegI32 temp = needConvertI64ToFloatTemp(ValType::F32, IsUnsigned(true)); 3602 convertI64ToF32(rs, IsUnsigned(true), rd, temp); 3603 maybeFree(temp); 3604 freeI64(rs); 3605 pushF32(rd); 3606 } 3607 3608 void BaseCompiler::emitConvertU64ToF64() { 3609 RegI64 rs = popI64(); 3610 RegF64 rd = needF64(); 3611 RegI32 temp = needConvertI64ToFloatTemp(ValType::F64, IsUnsigned(true)); 3612 convertI64ToF64(rs, IsUnsigned(true), rd, temp); 3613 maybeFree(temp); 3614 freeI64(rs); 3615 pushF64(rd); 3616 } 3617 #endif // RABALDR_I64_TO_FLOAT_CALLOUT 3618 3619 //////////////////////////////////////////////////////////// 3620 // 3621 // Machinery for optimized conditional branches. 3622 // 3623 // To disable this optimization it is enough always to return false from 3624 // sniffConditionalControl{Cmp,Eqz}. 3625 3626 struct BranchState { 3627 union { 3628 struct { 3629 RegI32 lhs; 3630 RegI32 rhs; 3631 int32_t imm; 3632 bool rhsImm; 3633 } i32; 3634 struct { 3635 RegI64 lhs; 3636 RegI64 rhs; 3637 int64_t imm; 3638 bool rhsImm; 3639 } i64; 3640 struct { 3641 RegF32 lhs; 3642 RegF32 rhs; 3643 } f32; 3644 struct { 3645 RegF64 lhs; 3646 RegF64 rhs; 3647 } f64; 3648 }; 3649 3650 Label* const label; // The target of the branch, never NULL 3651 const StackHeight stackHeight; // The stack base above which to place 3652 // stack-spilled block results, if 3653 // hasBlockResults(). 3654 const bool invertBranch; // If true, invert the sense of the branch 3655 const ResultType resultType; // The result propagated along the edges 3656 3657 explicit BranchState(Label* label) 3658 : label(label), 3659 stackHeight(StackHeight::Invalid()), 3660 invertBranch(false), 3661 resultType(ResultType::Empty()) {} 3662 3663 BranchState(Label* label, bool invertBranch) 3664 : label(label), 3665 stackHeight(StackHeight::Invalid()), 3666 invertBranch(invertBranch), 3667 resultType(ResultType::Empty()) {} 3668 3669 BranchState(Label* label, StackHeight stackHeight, bool invertBranch, 3670 ResultType resultType) 3671 : label(label), 3672 stackHeight(stackHeight), 3673 invertBranch(invertBranch), 3674 resultType(resultType) {} 3675 3676 bool hasBlockResults() const { return stackHeight.isValid(); } 3677 }; 3678 3679 void BaseCompiler::setLatentCompare(Assembler::Condition compareOp, 3680 ValType operandType) { 3681 latentOp_ = LatentOp::Compare; 3682 latentType_ = operandType; 3683 latentIntCmp_ = compareOp; 3684 } 3685 3686 void BaseCompiler::setLatentCompare(Assembler::DoubleCondition compareOp, 3687 ValType operandType) { 3688 latentOp_ = LatentOp::Compare; 3689 latentType_ = operandType; 3690 latentDoubleCmp_ = compareOp; 3691 } 3692 3693 void BaseCompiler::setLatentEqz(ValType operandType) { 3694 latentOp_ = LatentOp::Eqz; 3695 latentType_ = operandType; 3696 } 3697 3698 bool BaseCompiler::hasLatentOp() const { return latentOp_ != LatentOp::None; } 3699 3700 void BaseCompiler::resetLatentOp() { latentOp_ = LatentOp::None; } 3701 3702 // Emit a conditional branch that optionally and optimally cleans up the CPU 3703 // stack before we branch. 3704 // 3705 // Cond is either Assembler::Condition or Assembler::DoubleCondition. 3706 // 3707 // Lhs is RegI32, RegI64, or RegF32, RegF64, or RegRef. 3708 // 3709 // Rhs is either the same as Lhs, or an immediate expression compatible with 3710 // Lhs "when applicable". 3711 3712 template <typename Cond, typename Lhs, typename Rhs> 3713 bool BaseCompiler::jumpConditionalWithResults(BranchState* b, Cond cond, 3714 Lhs lhs, Rhs rhs) { 3715 if (b->hasBlockResults()) { 3716 StackHeight resultsBase(0); 3717 if (!topBranchParams(b->resultType, &resultsBase)) { 3718 return false; 3719 } 3720 if (b->stackHeight != resultsBase) { 3721 Label notTaken; 3722 branchTo(b->invertBranch ? cond : Assembler::InvertCondition(cond), lhs, 3723 rhs, ¬Taken); 3724 3725 // Shuffle stack args. 3726 shuffleStackResultsBeforeBranch(resultsBase, b->stackHeight, 3727 b->resultType); 3728 masm.jump(b->label); 3729 masm.bind(¬Taken); 3730 return true; 3731 } 3732 } 3733 3734 branchTo(b->invertBranch ? Assembler::InvertCondition(cond) : cond, lhs, rhs, 3735 b->label); 3736 return true; 3737 } 3738 3739 bool BaseCompiler::jumpConditionalWithResults(BranchState* b, RegRef object, 3740 MaybeRefType sourceType, 3741 RefType destType, 3742 bool onSuccess) { 3743 // Temporarily take the result registers so that branchIfRefSubtype 3744 // doesn't use them. 3745 needIntegerResultRegisters(b->resultType); 3746 BranchIfRefSubtypeRegisters regs = 3747 allocRegistersForBranchIfRefSubtype(destType); 3748 freeIntegerResultRegisters(b->resultType); 3749 3750 if (b->hasBlockResults()) { 3751 StackHeight resultsBase(0); 3752 if (!topBranchParams(b->resultType, &resultsBase)) { 3753 return false; 3754 } 3755 if (b->stackHeight != resultsBase) { 3756 Label notTaken; 3757 3758 masm.branchWasmRefIsSubtype( 3759 object, sourceType, destType, ¬Taken, 3760 /*onSuccess=*/b->invertBranch ? onSuccess : !onSuccess, 3761 /*signalNullChecks=*/false, regs.superSTV, regs.scratch1, 3762 regs.scratch2); 3763 freeRegistersForBranchIfRefSubtype(regs); 3764 3765 // Shuffle stack args. 3766 shuffleStackResultsBeforeBranch(resultsBase, b->stackHeight, 3767 b->resultType); 3768 masm.jump(b->label); 3769 masm.bind(¬Taken); 3770 return true; 3771 } 3772 } 3773 3774 masm.branchWasmRefIsSubtype( 3775 object, sourceType, destType, b->label, 3776 /*onSuccess=*/b->invertBranch ? !onSuccess : onSuccess, 3777 /*signalNullChecks=*/false, regs.superSTV, regs.scratch1, regs.scratch2); 3778 freeRegistersForBranchIfRefSubtype(regs); 3779 return true; 3780 } 3781 3782 // sniffConditionalControl{Cmp,Eqz} may modify the latentWhatever_ state in 3783 // the BaseCompiler so that a subsequent conditional branch can be compiled 3784 // optimally. emitBranchSetup() and emitBranchPerform() will consume that 3785 // state. If the latter methods are not called because deadCode_ is true 3786 // then the compiler MUST instead call resetLatentOp() to reset the state. 3787 3788 template <typename Cond> 3789 bool BaseCompiler::sniffConditionalControlCmp(Cond compareOp, 3790 ValType operandType) { 3791 MOZ_ASSERT(latentOp_ == LatentOp::None, 3792 "Latent comparison state not properly reset"); 3793 3794 #ifdef JS_CODEGEN_X86 3795 // On x86, latent i64 binary comparisons use too many registers: the 3796 // reserved join register and the lhs and rhs operands require six, but we 3797 // only have five. 3798 if (operandType == ValType::I64) { 3799 return false; 3800 } 3801 #endif 3802 3803 // No optimization for pointer compares yet. 3804 if (operandType.isRefRepr()) { 3805 return false; 3806 } 3807 3808 OpBytes op{}; 3809 iter_.peekOp(&op); 3810 switch (op.b0) { 3811 case uint16_t(Op::BrIf): 3812 case uint16_t(Op::If): 3813 case uint16_t(Op::SelectNumeric): 3814 case uint16_t(Op::SelectTyped): 3815 setLatentCompare(compareOp, operandType); 3816 return true; 3817 default: 3818 return false; 3819 } 3820 } 3821 3822 bool BaseCompiler::sniffConditionalControlEqz(ValType operandType) { 3823 MOZ_ASSERT(latentOp_ == LatentOp::None, 3824 "Latent comparison state not properly reset"); 3825 3826 OpBytes op{}; 3827 iter_.peekOp(&op); 3828 switch (op.b0) { 3829 case uint16_t(Op::BrIf): 3830 case uint16_t(Op::SelectNumeric): 3831 case uint16_t(Op::SelectTyped): 3832 case uint16_t(Op::If): 3833 setLatentEqz(operandType); 3834 return true; 3835 default: 3836 return false; 3837 } 3838 } 3839 3840 void BaseCompiler::emitBranchSetup(BranchState* b) { 3841 // Avoid allocating operands to latentOp_ to result registers. 3842 if (b->hasBlockResults()) { 3843 needResultRegisters(b->resultType); 3844 } 3845 3846 // Set up fields so that emitBranchPerform() need not switch on latentOp_. 3847 switch (latentOp_) { 3848 case LatentOp::None: { 3849 latentIntCmp_ = Assembler::NotEqual; 3850 latentType_ = ValType::I32; 3851 b->i32.lhs = popI32(); 3852 b->i32.rhsImm = true; 3853 b->i32.imm = 0; 3854 break; 3855 } 3856 case LatentOp::Compare: { 3857 switch (latentType_.kind()) { 3858 case ValType::I32: { 3859 if (popConst(&b->i32.imm)) { 3860 b->i32.lhs = popI32(); 3861 b->i32.rhsImm = true; 3862 } else { 3863 pop2xI32(&b->i32.lhs, &b->i32.rhs); 3864 b->i32.rhsImm = false; 3865 } 3866 break; 3867 } 3868 case ValType::I64: { 3869 pop2xI64(&b->i64.lhs, &b->i64.rhs); 3870 b->i64.rhsImm = false; 3871 break; 3872 } 3873 case ValType::F32: { 3874 pop2xF32(&b->f32.lhs, &b->f32.rhs); 3875 break; 3876 } 3877 case ValType::F64: { 3878 pop2xF64(&b->f64.lhs, &b->f64.rhs); 3879 break; 3880 } 3881 default: { 3882 MOZ_CRASH("Unexpected type for LatentOp::Compare"); 3883 } 3884 } 3885 break; 3886 } 3887 case LatentOp::Eqz: { 3888 switch (latentType_.kind()) { 3889 case ValType::I32: { 3890 latentIntCmp_ = Assembler::Equal; 3891 b->i32.lhs = popI32(); 3892 b->i32.rhsImm = true; 3893 b->i32.imm = 0; 3894 break; 3895 } 3896 case ValType::I64: { 3897 latentIntCmp_ = Assembler::Equal; 3898 b->i64.lhs = popI64(); 3899 b->i64.rhsImm = true; 3900 b->i64.imm = 0; 3901 break; 3902 } 3903 default: { 3904 MOZ_CRASH("Unexpected type for LatentOp::Eqz"); 3905 } 3906 } 3907 break; 3908 } 3909 } 3910 3911 if (b->hasBlockResults()) { 3912 freeResultRegisters(b->resultType); 3913 } 3914 } 3915 3916 bool BaseCompiler::emitBranchPerform(BranchState* b) { 3917 switch (latentType_.kind()) { 3918 case ValType::I32: { 3919 if (b->i32.rhsImm) { 3920 if (!jumpConditionalWithResults(b, latentIntCmp_, b->i32.lhs, 3921 Imm32(b->i32.imm))) { 3922 return false; 3923 } 3924 } else { 3925 if (!jumpConditionalWithResults(b, latentIntCmp_, b->i32.lhs, 3926 b->i32.rhs)) { 3927 return false; 3928 } 3929 freeI32(b->i32.rhs); 3930 } 3931 freeI32(b->i32.lhs); 3932 break; 3933 } 3934 case ValType::I64: { 3935 if (b->i64.rhsImm) { 3936 if (!jumpConditionalWithResults(b, latentIntCmp_, b->i64.lhs, 3937 Imm64(b->i64.imm))) { 3938 return false; 3939 } 3940 } else { 3941 if (!jumpConditionalWithResults(b, latentIntCmp_, b->i64.lhs, 3942 b->i64.rhs)) { 3943 return false; 3944 } 3945 freeI64(b->i64.rhs); 3946 } 3947 freeI64(b->i64.lhs); 3948 break; 3949 } 3950 case ValType::F32: { 3951 if (!jumpConditionalWithResults(b, latentDoubleCmp_, b->f32.lhs, 3952 b->f32.rhs)) { 3953 return false; 3954 } 3955 freeF32(b->f32.lhs); 3956 freeF32(b->f32.rhs); 3957 break; 3958 } 3959 case ValType::F64: { 3960 if (!jumpConditionalWithResults(b, latentDoubleCmp_, b->f64.lhs, 3961 b->f64.rhs)) { 3962 return false; 3963 } 3964 freeF64(b->f64.lhs); 3965 freeF64(b->f64.rhs); 3966 break; 3967 } 3968 default: { 3969 MOZ_CRASH("Unexpected type for LatentOp::Compare"); 3970 } 3971 } 3972 resetLatentOp(); 3973 return true; 3974 } 3975 3976 // For blocks and loops and ifs: 3977 // 3978 // - Sync the value stack before going into the block in order to simplify exit 3979 // from the block: all exits from the block can assume that there are no 3980 // live registers except the one carrying the exit value. 3981 // - The block can accumulate a number of dead values on the stacks, so when 3982 // branching out of the block or falling out at the end be sure to 3983 // pop the appropriate stacks back to where they were on entry, while 3984 // preserving the exit value. 3985 // - A continue branch in a loop is much like an exit branch, but the branch 3986 // value must not be preserved. 3987 // - The exit value is always in a designated join register (type dependent). 3988 3989 bool BaseCompiler::emitBlock() { 3990 BlockType type; 3991 if (!iter_.readBlock(&type)) { 3992 return false; 3993 } 3994 3995 if (!deadCode_) { 3996 sync(); // Simplifies branching out from block 3997 } 3998 3999 initControl(controlItem(), type.params()); 4000 4001 return true; 4002 } 4003 4004 bool BaseCompiler::endBlock(ResultType type) { 4005 Control& block = controlItem(); 4006 4007 if (deadCode_) { 4008 // Block does not fall through; reset stack. 4009 fr.resetStackHeight(block.stackHeight, type); 4010 popValueStackTo(block.stackSize); 4011 } else { 4012 // If the block label is used, we have a control join, so we need to shuffle 4013 // fallthrough values into place. Otherwise if it's not a control join, we 4014 // can leave the value stack alone. 4015 MOZ_ASSERT(stk_.length() == block.stackSize + type.length()); 4016 if (block.label.used()) { 4017 popBlockResults(type, block.stackHeight, ContinuationKind::Fallthrough); 4018 } 4019 block.bceSafeOnExit &= bceSafe_; 4020 } 4021 4022 // Bind after cleanup: branches out will have popped the stack. 4023 if (block.label.used()) { 4024 masm.bind(&block.label); 4025 if (deadCode_) { 4026 captureResultRegisters(type); 4027 deadCode_ = false; 4028 } 4029 if (!pushBlockResults(type)) { 4030 return false; 4031 } 4032 } 4033 4034 bceSafe_ = block.bceSafeOnExit; 4035 4036 return true; 4037 } 4038 4039 bool BaseCompiler::emitLoop() { 4040 BlockType type; 4041 if (!iter_.readLoop(&type)) { 4042 return false; 4043 } 4044 4045 if (!deadCode_) { 4046 sync(); // Simplifies branching out from block 4047 } 4048 4049 initControl(controlItem(), type.params()); 4050 bceSafe_ = 0; 4051 4052 if (!deadCode_) { 4053 // Loop entry is a control join, so shuffle the entry parameters into the 4054 // well-known locations. 4055 if (!topBlockParams(type.params())) { 4056 return false; 4057 } 4058 masm.nopAlign(CodeAlignment); 4059 masm.bind(&controlItem(0).label); 4060 4061 // The interrupt check barfs if there are live registers. The hotness check 4062 // also can call the request tier-up stub, which assumes that no registers 4063 // are live. 4064 sync(); 4065 4066 if (!addInterruptCheck()) { 4067 return false; 4068 } 4069 4070 if (compilerEnv_.mode() == CompileMode::LazyTiering) { 4071 // Create an unpatched hotness check and stash enough information that we 4072 // can patch it with a value related to the loop's size when we get to 4073 // the corresponding `end` opcode. 4074 Maybe<CodeOffset> ctrDecOffset = addHotnessCheck(); 4075 if (ctrDecOffset.isNothing()) { 4076 return false; 4077 } 4078 controlItem().loopBytecodeStart = iter_.lastOpcodeOffset(); 4079 controlItem().offsetOfCtrDec = ctrDecOffset.value(); 4080 } 4081 } 4082 4083 return true; 4084 } 4085 4086 // The bodies of the "then" and "else" arms can be arbitrary sequences 4087 // of expressions, they push control and increment the nesting and can 4088 // even be targeted by jumps. A branch to the "if" block branches to 4089 // the exit of the if, ie, it's like "break". Consider: 4090 // 4091 // (func (result i32) 4092 // (if (i32.const 1) 4093 // (begin (br 1) (unreachable)) 4094 // (begin (unreachable))) 4095 // (i32.const 1)) 4096 // 4097 // The branch causes neither of the unreachable expressions to be 4098 // evaluated. 4099 4100 bool BaseCompiler::emitIf() { 4101 BlockType type; 4102 Nothing unused_cond; 4103 if (!iter_.readIf(&type, &unused_cond)) { 4104 return false; 4105 } 4106 4107 BranchState b(&controlItem().otherLabel, InvertBranch(true)); 4108 if (!deadCode_) { 4109 needResultRegisters(type.params()); 4110 emitBranchSetup(&b); 4111 freeResultRegisters(type.params()); 4112 sync(); 4113 } else { 4114 resetLatentOp(); 4115 } 4116 4117 initControl(controlItem(), type.params()); 4118 4119 if (!deadCode_) { 4120 // Because params can flow immediately to results in the case of an empty 4121 // "then" or "else" block, and the result of an if/then is a join in 4122 // general, we shuffle params eagerly to the result allocations. 4123 if (!topBlockParams(type.params())) { 4124 return false; 4125 } 4126 if (!emitBranchPerform(&b)) { 4127 return false; 4128 } 4129 } 4130 4131 return true; 4132 } 4133 4134 bool BaseCompiler::endIfThen(ResultType type) { 4135 Control& ifThen = controlItem(); 4136 4137 // The parameters to the "if" logically flow to both the "then" and "else" 4138 // blocks, but the "else" block is empty. Since we know that the "if" 4139 // type-checks, that means that the "else" parameters are the "else" results, 4140 // and that the "if"'s result type is the same as its parameter type. 4141 4142 if (deadCode_) { 4143 // "then" arm does not fall through; reset stack. 4144 fr.resetStackHeight(ifThen.stackHeight, type); 4145 popValueStackTo(ifThen.stackSize); 4146 if (!ifThen.deadOnArrival) { 4147 captureResultRegisters(type); 4148 } 4149 } else { 4150 MOZ_ASSERT(stk_.length() == ifThen.stackSize + type.length()); 4151 // Assume we have a control join, so place results in block result 4152 // allocations. 4153 popBlockResults(type, ifThen.stackHeight, ContinuationKind::Fallthrough); 4154 MOZ_ASSERT(!ifThen.deadOnArrival); 4155 } 4156 4157 if (ifThen.otherLabel.used()) { 4158 masm.bind(&ifThen.otherLabel); 4159 } 4160 4161 if (ifThen.label.used()) { 4162 masm.bind(&ifThen.label); 4163 } 4164 4165 if (!deadCode_) { 4166 ifThen.bceSafeOnExit &= bceSafe_; 4167 } 4168 4169 deadCode_ = ifThen.deadOnArrival; 4170 if (!deadCode_) { 4171 if (!pushBlockResults(type)) { 4172 return false; 4173 } 4174 } 4175 4176 bceSafe_ = ifThen.bceSafeOnExit & ifThen.bceSafeOnEntry; 4177 4178 return true; 4179 } 4180 4181 bool BaseCompiler::emitElse() { 4182 ResultType params, results; 4183 BaseNothingVector unused_thenValues{}; 4184 4185 if (!iter_.readElse(¶ms, &results, &unused_thenValues)) { 4186 return false; 4187 } 4188 4189 Control& ifThenElse = controlItem(0); 4190 4191 // See comment in endIfThenElse, below. 4192 4193 // Exit the "then" branch. 4194 4195 ifThenElse.deadThenBranch = deadCode_; 4196 4197 if (deadCode_) { 4198 fr.resetStackHeight(ifThenElse.stackHeight, results); 4199 popValueStackTo(ifThenElse.stackSize); 4200 } else { 4201 MOZ_ASSERT(stk_.length() == ifThenElse.stackSize + results.length()); 4202 popBlockResults(results, ifThenElse.stackHeight, ContinuationKind::Jump); 4203 freeResultRegisters(results); 4204 MOZ_ASSERT(!ifThenElse.deadOnArrival); 4205 } 4206 4207 if (!deadCode_) { 4208 masm.jump(&ifThenElse.label); 4209 } 4210 4211 if (ifThenElse.otherLabel.used()) { 4212 masm.bind(&ifThenElse.otherLabel); 4213 } 4214 4215 // Reset to the "else" branch. 4216 4217 if (!deadCode_) { 4218 ifThenElse.bceSafeOnExit &= bceSafe_; 4219 } 4220 4221 deadCode_ = ifThenElse.deadOnArrival; 4222 bceSafe_ = ifThenElse.bceSafeOnEntry; 4223 4224 fr.resetStackHeight(ifThenElse.stackHeight, params); 4225 4226 if (!deadCode_) { 4227 captureResultRegisters(params); 4228 if (!pushBlockResults(params)) { 4229 return false; 4230 } 4231 } 4232 4233 return true; 4234 } 4235 4236 bool BaseCompiler::endIfThenElse(ResultType type) { 4237 Control& ifThenElse = controlItem(); 4238 4239 // The expression type is not a reliable guide to what we'll find 4240 // on the stack, we could have (if E (i32.const 1) (unreachable)) 4241 // in which case the "else" arm is AnyType but the type of the 4242 // full expression is I32. So restore whatever's there, not what 4243 // we want to find there. The "then" arm has the same constraint. 4244 4245 if (deadCode_) { 4246 // "then" arm does not fall through; reset stack. 4247 fr.resetStackHeight(ifThenElse.stackHeight, type); 4248 popValueStackTo(ifThenElse.stackSize); 4249 } else { 4250 MOZ_ASSERT(stk_.length() == ifThenElse.stackSize + type.length()); 4251 // Assume we have a control join, so place results in block result 4252 // allocations. 4253 popBlockResults(type, ifThenElse.stackHeight, 4254 ContinuationKind::Fallthrough); 4255 ifThenElse.bceSafeOnExit &= bceSafe_; 4256 MOZ_ASSERT(!ifThenElse.deadOnArrival); 4257 } 4258 4259 if (ifThenElse.label.used()) { 4260 masm.bind(&ifThenElse.label); 4261 } 4262 4263 bool joinLive = 4264 !ifThenElse.deadOnArrival && 4265 (!ifThenElse.deadThenBranch || !deadCode_ || ifThenElse.label.bound()); 4266 4267 if (joinLive) { 4268 // No values were provided by the "then" path, but capture the values 4269 // provided by the "else" path. 4270 if (deadCode_) { 4271 captureResultRegisters(type); 4272 } 4273 deadCode_ = false; 4274 } 4275 4276 bceSafe_ = ifThenElse.bceSafeOnExit; 4277 4278 if (!deadCode_) { 4279 if (!pushBlockResults(type)) { 4280 return false; 4281 } 4282 } 4283 4284 return true; 4285 } 4286 4287 bool BaseCompiler::emitEnd() { 4288 LabelKind kind; 4289 ResultType type; 4290 BaseNothingVector unused_values{}; 4291 if (!iter_.readEnd(&kind, &type, &unused_values, &unused_values)) { 4292 return false; 4293 } 4294 4295 // Every label case is responsible to pop the control item at the appropriate 4296 // time for the label case 4297 switch (kind) { 4298 case LabelKind::Body: { 4299 if (!endBlock(type)) { 4300 return false; 4301 } 4302 doReturn(ContinuationKind::Fallthrough); 4303 iter_.popEnd(); 4304 MOZ_ASSERT(iter_.controlStackEmpty()); 4305 return iter_.endFunction(iter_.end()); 4306 } 4307 case LabelKind::Block: 4308 if (!endBlock(type)) { 4309 return false; 4310 } 4311 iter_.popEnd(); 4312 break; 4313 case LabelKind::Loop: { 4314 if (compilerEnv_.mode() == CompileMode::LazyTiering) { 4315 // These are set (or not set) together. 4316 MOZ_ASSERT((controlItem().loopBytecodeStart != UINTPTR_MAX) == 4317 (controlItem().offsetOfCtrDec.bound())); 4318 if (controlItem().loopBytecodeStart != UINTPTR_MAX) { 4319 // If the above condition is false, the loop was in dead code and so 4320 // there is no loop-head hotness check that needs to be patched. See 4321 // ::emitLoop. 4322 MOZ_ASSERT(controlItem().loopBytecodeStart <= 4323 iter_.lastOpcodeOffset()); 4324 size_t loopBytecodeSize = 4325 iter_.lastOpcodeOffset() - controlItem().loopBytecodeStart; 4326 uint32_t step = BlockSizeToDownwardsStep(loopBytecodeSize); 4327 // Don't try to patch the check if we've OOM'd, since the check might 4328 // not actually exist. 4329 if (masm.oom()) { 4330 return false; 4331 } 4332 patchHotnessCheck(controlItem().offsetOfCtrDec, step); 4333 } 4334 } 4335 // The end of a loop isn't a branch target, so we can just leave its 4336 // results on the expression stack to be consumed by the outer block. 4337 iter_.popEnd(); 4338 break; 4339 } 4340 case LabelKind::Then: 4341 if (!endIfThen(type)) { 4342 return false; 4343 } 4344 iter_.popEnd(); 4345 break; 4346 case LabelKind::Else: 4347 if (!endIfThenElse(type)) { 4348 return false; 4349 } 4350 iter_.popEnd(); 4351 break; 4352 case LabelKind::Try: 4353 case LabelKind::Catch: 4354 case LabelKind::CatchAll: 4355 if (!endTryCatch(type)) { 4356 return false; 4357 } 4358 iter_.popEnd(); 4359 break; 4360 case LabelKind::TryTable: 4361 if (!endTryTable(type)) { 4362 return false; 4363 } 4364 iter_.popEnd(); 4365 break; 4366 } 4367 4368 return true; 4369 } 4370 4371 bool BaseCompiler::emitBr() { 4372 uint32_t relativeDepth; 4373 ResultType type; 4374 BaseNothingVector unused_values{}; 4375 if (!iter_.readBr(&relativeDepth, &type, &unused_values)) { 4376 return false; 4377 } 4378 4379 if (deadCode_) { 4380 return true; 4381 } 4382 4383 Control& target = controlItem(relativeDepth); 4384 target.bceSafeOnExit &= bceSafe_; 4385 4386 // Save any values in the designated join registers, as if the target block 4387 // returned normally. 4388 4389 popBlockResults(type, target.stackHeight, ContinuationKind::Jump); 4390 masm.jump(&target.label); 4391 4392 // The registers holding the join values are free for the remainder of this 4393 // block. 4394 4395 freeResultRegisters(type); 4396 4397 deadCode_ = true; 4398 4399 return true; 4400 } 4401 4402 bool BaseCompiler::emitBrIf() { 4403 uint32_t relativeDepth; 4404 ResultType type; 4405 BaseNothingVector unused_values{}; 4406 Nothing unused_condition; 4407 if (!iter_.readBrIf(&relativeDepth, &type, &unused_values, 4408 &unused_condition)) { 4409 return false; 4410 } 4411 4412 if (deadCode_) { 4413 resetLatentOp(); 4414 return true; 4415 } 4416 4417 Control& target = controlItem(relativeDepth); 4418 target.bceSafeOnExit &= bceSafe_; 4419 4420 BranchState b(&target.label, target.stackHeight, InvertBranch(false), type); 4421 emitBranchSetup(&b); 4422 return emitBranchPerform(&b); 4423 } 4424 4425 bool BaseCompiler::emitBrOnNull() { 4426 MOZ_ASSERT(!hasLatentOp()); 4427 4428 uint32_t relativeDepth; 4429 ResultType type; 4430 BaseNothingVector unused_values{}; 4431 Nothing unused_condition; 4432 if (!iter_.readBrOnNull(&relativeDepth, &type, &unused_values, 4433 &unused_condition)) { 4434 return false; 4435 } 4436 4437 if (deadCode_) { 4438 return true; 4439 } 4440 4441 Control& target = controlItem(relativeDepth); 4442 target.bceSafeOnExit &= bceSafe_; 4443 4444 BranchState b(&target.label, target.stackHeight, InvertBranch(false), type); 4445 if (b.hasBlockResults()) { 4446 needResultRegisters(b.resultType); 4447 } 4448 RegRef ref = popRef(); 4449 if (b.hasBlockResults()) { 4450 freeResultRegisters(b.resultType); 4451 } 4452 if (!jumpConditionalWithResults(&b, Assembler::Equal, ref, 4453 ImmWord(AnyRef::NullRefValue))) { 4454 return false; 4455 } 4456 pushRef(ref); 4457 4458 return true; 4459 } 4460 4461 bool BaseCompiler::emitBrOnNonNull() { 4462 MOZ_ASSERT(!hasLatentOp()); 4463 4464 uint32_t relativeDepth; 4465 ResultType type; 4466 BaseNothingVector unused_values{}; 4467 Nothing unused_condition; 4468 if (!iter_.readBrOnNonNull(&relativeDepth, &type, &unused_values, 4469 &unused_condition)) { 4470 return false; 4471 } 4472 4473 if (deadCode_) { 4474 return true; 4475 } 4476 4477 Control& target = controlItem(relativeDepth); 4478 target.bceSafeOnExit &= bceSafe_; 4479 4480 BranchState b(&target.label, target.stackHeight, InvertBranch(false), type); 4481 MOZ_ASSERT(b.hasBlockResults(), "br_on_non_null has block results"); 4482 4483 // Don't allocate the result register used in the branch 4484 needIntegerResultRegisters(b.resultType); 4485 4486 // Get the ref from the top of the stack 4487 RegRef refCondition = popRef(); 4488 4489 // Create a copy of the ref for passing to the on_non_null label, 4490 // the original ref is used in the condition. 4491 RegRef ref = needRef(); 4492 moveRef(refCondition, ref); 4493 pushRef(ref); 4494 4495 freeIntegerResultRegisters(b.resultType); 4496 4497 if (!jumpConditionalWithResults(&b, Assembler::NotEqual, refCondition, 4498 ImmWord(AnyRef::NullRefValue))) { 4499 return false; 4500 } 4501 4502 freeRef(refCondition); 4503 4504 // Dropping null reference. 4505 dropValue(); 4506 4507 return true; 4508 } 4509 4510 bool BaseCompiler::emitBrTable() { 4511 Uint32Vector depths; 4512 uint32_t defaultDepth; 4513 ResultType branchParams; 4514 BaseNothingVector unused_values{}; 4515 Nothing unused_index; 4516 // N.B., `branchParams' gets set to the type of the default branch target. In 4517 // the presence of subtyping, it could be that the different branch targets 4518 // have different types. Here we rely on the assumption that the value 4519 // representations (e.g. Stk value types) of all branch target types are the 4520 // same, in the baseline compiler. Notably, this means that all Ref types 4521 // should be represented the same. 4522 if (!iter_.readBrTable(&depths, &defaultDepth, &branchParams, &unused_values, 4523 &unused_index)) { 4524 return false; 4525 } 4526 4527 if (deadCode_) { 4528 return true; 4529 } 4530 4531 // Don't use param registers for rc 4532 needIntegerResultRegisters(branchParams); 4533 4534 // Table switch value always on top. 4535 RegI32 rc = popI32(); 4536 4537 freeIntegerResultRegisters(branchParams); 4538 4539 StackHeight resultsBase(0); 4540 if (!topBranchParams(branchParams, &resultsBase)) { 4541 return false; 4542 } 4543 4544 Label dispatchCode; 4545 masm.branch32(Assembler::Below, rc, Imm32(depths.length()), &dispatchCode); 4546 4547 // This is the out-of-range stub. rc is dead here but we don't need it. 4548 4549 shuffleStackResultsBeforeBranch( 4550 resultsBase, controlItem(defaultDepth).stackHeight, branchParams); 4551 controlItem(defaultDepth).bceSafeOnExit &= bceSafe_; 4552 masm.jump(&controlItem(defaultDepth).label); 4553 4554 // Emit stubs. rc is dead in all of these but we don't need it. 4555 // 4556 // The labels in the vector are in the TempAllocator and will 4557 // be freed by and by. 4558 // 4559 // TODO / OPTIMIZE (Bug 1316804): Branch directly to the case code if we 4560 // can, don't emit an intermediate stub. 4561 4562 LabelVector stubs; 4563 if (!stubs.reserve(depths.length())) { 4564 return false; 4565 } 4566 4567 for (uint32_t depth : depths) { 4568 stubs.infallibleEmplaceBack(NonAssertingLabel()); 4569 masm.bind(&stubs.back()); 4570 shuffleStackResultsBeforeBranch(resultsBase, controlItem(depth).stackHeight, 4571 branchParams); 4572 controlItem(depth).bceSafeOnExit &= bceSafe_; 4573 masm.jump(&controlItem(depth).label); 4574 } 4575 4576 // Emit table. 4577 4578 Label theTable; 4579 jumpTable(stubs, &theTable); 4580 4581 // Emit indirect jump. rc is live here. 4582 4583 tableSwitch(&theTable, rc, &dispatchCode); 4584 4585 deadCode_ = true; 4586 4587 // Clean up. 4588 4589 freeI32(rc); 4590 popValueStackBy(branchParams.length()); 4591 4592 return true; 4593 } 4594 4595 bool BaseCompiler::emitTry() { 4596 BlockType type; 4597 if (!iter_.readTry(&type)) { 4598 return false; 4599 } 4600 4601 if (!deadCode_) { 4602 // Simplifies jumping out, but it is also necessary so that control 4603 // can re-enter the catch handler without restoring registers. 4604 sync(); 4605 } 4606 4607 initControl(controlItem(), type.params()); 4608 4609 if (!deadCode_) { 4610 // Be conservative for BCE due to complex control flow in try blocks. 4611 controlItem().bceSafeOnExit = 0; 4612 if (!startTryNote(&controlItem().tryNoteIndex)) { 4613 return false; 4614 } 4615 } 4616 4617 return true; 4618 } 4619 4620 bool BaseCompiler::emitTryTable() { 4621 BlockType type; 4622 TryTableCatchVector catches; 4623 if (!iter_.readTryTable(&type, &catches)) { 4624 return false; 4625 } 4626 4627 if (!deadCode_) { 4628 // Simplifies jumping out, but it is also necessary so that control 4629 // can re-enter the catch handler without restoring registers. 4630 sync(); 4631 } 4632 4633 initControl(controlItem(), type.params()); 4634 // Be conservative for BCE due to complex control flow in try blocks. 4635 controlItem().bceSafeOnExit = 0; 4636 4637 // Don't emit a landing pad if this whole try is dead code 4638 if (deadCode_) { 4639 return true; 4640 } 4641 4642 // Emit a landing pad that exceptions will jump into. Jump over it for now. 4643 Label skipLandingPad; 4644 masm.jump(&skipLandingPad); 4645 4646 StackHeight prePadHeight = fr.stackHeight(); 4647 uint32_t padOffset = masm.currentOffset(); 4648 uint32_t padStackHeight = masm.framePushed(); 4649 4650 // InstanceReg is live and contains this function's instance by the exception 4651 // handling resume method. We keep it alive for use in loading the tag for 4652 // each catch handler. 4653 RegPtr instance = RegPtr(InstanceReg); 4654 #ifndef RABALDR_PIN_INSTANCE 4655 needPtr(instance); 4656 #endif 4657 4658 // Load exception and tag from instance, clearing it in the process. 4659 RegRef exn; 4660 RegRef exnTag; 4661 consumePendingException(instance, &exn, &exnTag); 4662 4663 // Get a register to hold the tags for each catch 4664 RegRef catchTag = needRef(); 4665 4666 bool hadCatchAll = false; 4667 for (const TryTableCatch& tryTableCatch : catches) { 4668 ResultType labelParams = ResultType::Vector(tryTableCatch.labelType); 4669 4670 Control& target = controlItem(tryTableCatch.labelRelativeDepth); 4671 target.bceSafeOnExit = 0; 4672 4673 // Handle a catch_all by jumping to the target block 4674 if (tryTableCatch.tagIndex == CatchAllIndex) { 4675 // Capture the exnref if it has been requested, or else free it. 4676 if (tryTableCatch.captureExnRef) { 4677 pushRef(exn); 4678 } else { 4679 freeRef(exn); 4680 } 4681 // Free all of the other registers 4682 freeRef(exnTag); 4683 freeRef(catchTag); 4684 #ifndef RABALDR_PIN_INSTANCE 4685 freePtr(instance); 4686 #endif 4687 4688 // Pop the results needed for the target branch and perform the jump 4689 popBlockResults(labelParams, target.stackHeight, ContinuationKind::Jump); 4690 masm.jump(&target.label); 4691 freeResultRegisters(labelParams); 4692 4693 // Break from the loop and skip the implicit rethrow that's needed 4694 // if we didn't have a catch_all 4695 hadCatchAll = true; 4696 break; 4697 } 4698 4699 // This is a `catch $t`, load the tag type we're trying to match 4700 const TagType& tagType = *codeMeta_.tags[tryTableCatch.tagIndex].type; 4701 const TagOffsetVector& tagOffsets = tagType.argOffsets(); 4702 ResultType tagParams = tagType.resultType(); 4703 4704 // Load the tag for this catch and compare it against the exception's tag. 4705 // If they don't match, skip to the next catch handler. 4706 Label skipCatch; 4707 loadTag(instance, tryTableCatch.tagIndex, catchTag); 4708 masm.branchPtr(Assembler::NotEqual, exnTag, catchTag, &skipCatch); 4709 4710 // The tags and instance are dead after we've had a match, free them 4711 freeRef(exnTag); 4712 freeRef(catchTag); 4713 #ifndef RABALDR_PIN_INSTANCE 4714 freePtr(instance); 4715 #endif 4716 4717 // Allocate a register to hold the exception data pointer 4718 RegPtr data = needPtr(); 4719 4720 // Unpack the tag and jump to the block 4721 masm.loadPtr(Address(exn, (int32_t)WasmExceptionObject::offsetOfData()), 4722 data); 4723 // This method can increase stk_.length() by an unbounded amount, so we need 4724 // to perform an allocation here to accomodate the variable number of 4725 // values. There is enough headroom for the fixed number of values. The 4726 // general case is handled in emitBody. 4727 if (!stk_.reserve(stk_.length() + labelParams.length())) { 4728 return false; 4729 } 4730 4731 for (uint32_t i = 0; i < tagParams.length(); i++) { 4732 int32_t offset = tagOffsets[i]; 4733 switch (tagParams[i].kind()) { 4734 case ValType::I32: { 4735 RegI32 reg = needI32(); 4736 masm.load32(Address(data, offset), reg); 4737 pushI32(reg); 4738 break; 4739 } 4740 case ValType::I64: { 4741 RegI64 reg = needI64(); 4742 masm.load64(Address(data, offset), reg); 4743 pushI64(reg); 4744 break; 4745 } 4746 case ValType::F32: { 4747 RegF32 reg = needF32(); 4748 masm.loadFloat32(Address(data, offset), reg); 4749 pushF32(reg); 4750 break; 4751 } 4752 case ValType::F64: { 4753 RegF64 reg = needF64(); 4754 masm.loadDouble(Address(data, offset), reg); 4755 pushF64(reg); 4756 break; 4757 } 4758 case ValType::V128: { 4759 #ifdef ENABLE_WASM_SIMD 4760 RegV128 reg = needV128(); 4761 masm.loadUnalignedSimd128(Address(data, offset), reg); 4762 pushV128(reg); 4763 break; 4764 #else 4765 MOZ_CRASH("No SIMD support"); 4766 #endif 4767 } 4768 case ValType::Ref: { 4769 RegRef reg = needRef(); 4770 masm.loadPtr(Address(data, offset), reg); 4771 pushRef(reg); 4772 break; 4773 } 4774 } 4775 } 4776 4777 // The exception data pointer is no longer live after unpacking the 4778 // exception 4779 freePtr(data); 4780 4781 // Capture the exnref if it has been requested, or else free it. 4782 if (tryTableCatch.captureExnRef) { 4783 pushRef(exn); 4784 } else { 4785 freeRef(exn); 4786 } 4787 4788 // Pop the results needed for the target branch and perform the jump 4789 popBlockResults(labelParams, target.stackHeight, ContinuationKind::Jump); 4790 masm.jump(&target.label); 4791 freeResultRegisters(labelParams); 4792 4793 // Reset the stack height for the skip to the next catch handler 4794 fr.setStackHeight(prePadHeight); 4795 masm.bind(&skipCatch); 4796 4797 // Reset ownership of the registers for the next catch handler we emit 4798 needRef(exn); 4799 needRef(exnTag); 4800 needRef(catchTag); 4801 #ifndef RABALDR_PIN_INSTANCE 4802 needPtr(instance); 4803 #endif 4804 } 4805 4806 if (!hadCatchAll) { 4807 // Free all registers, except for the exception 4808 freeRef(exnTag); 4809 freeRef(catchTag); 4810 #ifndef RABALDR_PIN_INSTANCE 4811 freePtr(instance); 4812 #endif 4813 4814 // If none of the tag checks succeed and there is no catch_all, 4815 // then we rethrow the exception 4816 if (!throwFrom(exn)) { 4817 return false; 4818 } 4819 } else { 4820 // All registers should have been freed by the catch_all 4821 MOZ_ASSERT(isAvailableRef(exn)); 4822 MOZ_ASSERT(isAvailableRef(exnTag)); 4823 MOZ_ASSERT(isAvailableRef(catchTag)); 4824 #ifndef RABALDR_PIN_INSTANCE 4825 MOZ_ASSERT(isAvailablePtr(instance)); 4826 #endif 4827 } 4828 4829 // Reset stack height for skipLandingPad, and bind it 4830 fr.setStackHeight(prePadHeight); 4831 masm.bind(&skipLandingPad); 4832 4833 // Start the try note for this try block, after the landing pad 4834 if (!startTryNote(&controlItem().tryNoteIndex)) { 4835 return false; 4836 } 4837 4838 // Mark the try note to start at the landing pad we created above 4839 TryNoteVector& tryNotes = masm.tryNotes(); 4840 TryNote& tryNote = tryNotes[controlItem().tryNoteIndex]; 4841 tryNote.setLandingPad(padOffset, padStackHeight); 4842 4843 return true; 4844 } 4845 4846 void BaseCompiler::emitCatchSetup(LabelKind kind, Control& tryCatch, 4847 const ResultType& resultType) { 4848 // Catch ends the try or last catch, so we finish this like endIfThen. 4849 if (deadCode_) { 4850 fr.resetStackHeight(tryCatch.stackHeight, resultType); 4851 popValueStackTo(tryCatch.stackSize); 4852 } else { 4853 // If the previous block is a catch, we need to handle the extra exception 4854 // reference on the stack (for rethrow) and thus the stack size is 1 more. 4855 MOZ_ASSERT(stk_.length() == tryCatch.stackSize + resultType.length() + 4856 (kind == LabelKind::Try ? 0 : 1)); 4857 // Try jumps to the end of the try-catch block unless a throw is done. 4858 if (kind == LabelKind::Try) { 4859 popBlockResults(resultType, tryCatch.stackHeight, ContinuationKind::Jump); 4860 } else { 4861 popCatchResults(resultType, tryCatch.stackHeight); 4862 } 4863 MOZ_ASSERT(stk_.length() == tryCatch.stackSize); 4864 freeResultRegisters(resultType); 4865 MOZ_ASSERT(!tryCatch.deadOnArrival); 4866 } 4867 4868 // Reset to this "catch" branch. 4869 deadCode_ = tryCatch.deadOnArrival; 4870 4871 // We use the empty result type here because catch does *not* take the 4872 // try-catch block parameters. 4873 fr.resetStackHeight(tryCatch.stackHeight, ResultType::Empty()); 4874 4875 if (deadCode_) { 4876 return; 4877 } 4878 4879 bceSafe_ = 0; 4880 4881 // The end of the previous try/catch jumps to the join point. 4882 masm.jump(&tryCatch.label); 4883 4884 // Note end of try block for finding the catch block target. This needs 4885 // to happen after the stack is reset to the correct height. 4886 if (kind == LabelKind::Try) { 4887 finishTryNote(controlItem().tryNoteIndex); 4888 } 4889 } 4890 4891 bool BaseCompiler::emitCatch() { 4892 LabelKind kind; 4893 uint32_t tagIndex; 4894 ResultType paramType, resultType; 4895 BaseNothingVector unused_tryValues{}; 4896 4897 if (!iter_.readCatch(&kind, &tagIndex, ¶mType, &resultType, 4898 &unused_tryValues)) { 4899 return false; 4900 } 4901 4902 Control& tryCatch = controlItem(); 4903 4904 emitCatchSetup(kind, tryCatch, resultType); 4905 4906 if (deadCode_) { 4907 return true; 4908 } 4909 4910 // Construct info used for the exception landing pad. 4911 CatchInfo catchInfo(tagIndex); 4912 if (!tryCatch.catchInfos.emplaceBack(catchInfo)) { 4913 return false; 4914 } 4915 4916 masm.bind(&tryCatch.catchInfos.back().label); 4917 4918 // Extract the arguments in the exception package and push them. 4919 const SharedTagType& tagType = codeMeta_.tags[tagIndex].type; 4920 const ValTypeVector& params = tagType->argTypes(); 4921 const TagOffsetVector& offsets = tagType->argOffsets(); 4922 4923 // The landing pad uses the block return protocol to communicate the 4924 // exception object pointer to the catch block. 4925 ResultType exnResult = ResultType::Single(RefType::extern_()); 4926 captureResultRegisters(exnResult); 4927 if (!pushBlockResults(exnResult)) { 4928 return false; 4929 } 4930 RegRef exn = popRef(); 4931 RegPtr data = needPtr(); 4932 4933 masm.loadPtr(Address(exn, (int32_t)WasmExceptionObject::offsetOfData()), 4934 data); 4935 4936 // This method can increase stk_.length() by an unbounded amount, so we need 4937 // to perform an allocation here to accomodate the variable number of values. 4938 // There is enough headroom for the fixed number of values. The general case 4939 // is handled in emitBody. 4940 if (!stk_.reserve(stk_.length() + params.length() + 1)) { 4941 return false; 4942 } 4943 4944 // This reference is pushed onto the stack because a potential rethrow 4945 // may need to access it. It is always popped at the end of the block. 4946 pushRef(exn); 4947 4948 for (uint32_t i = 0; i < params.length(); i++) { 4949 int32_t offset = offsets[i]; 4950 switch (params[i].kind()) { 4951 case ValType::I32: { 4952 RegI32 reg = needI32(); 4953 masm.load32(Address(data, offset), reg); 4954 pushI32(reg); 4955 break; 4956 } 4957 case ValType::I64: { 4958 RegI64 reg = needI64(); 4959 masm.load64(Address(data, offset), reg); 4960 pushI64(reg); 4961 break; 4962 } 4963 case ValType::F32: { 4964 RegF32 reg = needF32(); 4965 masm.loadFloat32(Address(data, offset), reg); 4966 pushF32(reg); 4967 break; 4968 } 4969 case ValType::F64: { 4970 RegF64 reg = needF64(); 4971 masm.loadDouble(Address(data, offset), reg); 4972 pushF64(reg); 4973 break; 4974 } 4975 case ValType::V128: { 4976 #ifdef ENABLE_WASM_SIMD 4977 RegV128 reg = needV128(); 4978 masm.loadUnalignedSimd128(Address(data, offset), reg); 4979 pushV128(reg); 4980 break; 4981 #else 4982 MOZ_CRASH("No SIMD support"); 4983 #endif 4984 } 4985 case ValType::Ref: { 4986 RegRef reg = needRef(); 4987 masm.loadPtr(Address(data, offset), reg); 4988 pushRef(reg); 4989 break; 4990 } 4991 } 4992 } 4993 freePtr(data); 4994 4995 return true; 4996 } 4997 4998 bool BaseCompiler::emitCatchAll() { 4999 LabelKind kind; 5000 ResultType paramType, resultType; 5001 BaseNothingVector unused_tryValues{}; 5002 5003 if (!iter_.readCatchAll(&kind, ¶mType, &resultType, &unused_tryValues)) { 5004 return false; 5005 } 5006 5007 Control& tryCatch = controlItem(); 5008 5009 emitCatchSetup(kind, tryCatch, resultType); 5010 5011 if (deadCode_) { 5012 return true; 5013 } 5014 5015 CatchInfo catchInfo(CatchAllIndex); 5016 if (!tryCatch.catchInfos.emplaceBack(catchInfo)) { 5017 return false; 5018 } 5019 5020 masm.bind(&tryCatch.catchInfos.back().label); 5021 5022 // The landing pad uses the block return protocol to communicate the 5023 // exception object pointer to the catch block. 5024 ResultType exnResult = ResultType::Single(RefType::extern_()); 5025 captureResultRegisters(exnResult); 5026 // This reference is pushed onto the stack because a potential rethrow 5027 // may need to access it. It is always popped at the end of the block. 5028 return pushBlockResults(exnResult); 5029 } 5030 5031 bool BaseCompiler::emitDelegate() { 5032 uint32_t relativeDepth; 5033 ResultType resultType; 5034 BaseNothingVector unused_tryValues{}; 5035 5036 if (!iter_.readDelegate(&relativeDepth, &resultType, &unused_tryValues)) { 5037 return false; 5038 } 5039 5040 if (!endBlock(resultType)) { 5041 return false; 5042 } 5043 5044 if (controlItem().deadOnArrival) { 5045 return true; 5046 } 5047 5048 // Mark the end of the try body. This may insert a nop. 5049 finishTryNote(controlItem().tryNoteIndex); 5050 5051 // If the target block is a non-try block, skip over it and find the next 5052 // try block or the very last block (to re-throw out of the function). 5053 Control& lastBlock = controlOutermost(); 5054 while (controlKind(relativeDepth) != LabelKind::Try && 5055 controlKind(relativeDepth) != LabelKind::TryTable && 5056 &controlItem(relativeDepth) != &lastBlock) { 5057 relativeDepth++; 5058 } 5059 Control& target = controlItem(relativeDepth); 5060 TryNoteVector& tryNotes = masm.tryNotes(); 5061 TryNote& delegateTryNote = tryNotes[controlItem().tryNoteIndex]; 5062 5063 if (&target == &lastBlock) { 5064 // A delegate targeting the function body block means that any exception 5065 // in this try needs to be propagated to the caller function. We use the 5066 // delegate code offset of `0` as that will be in the prologue and cannot 5067 // have a try note. 5068 delegateTryNote.setDelegate(0); 5069 } else { 5070 // Delegate to one byte inside the beginning of the target try note, as 5071 // that's when matches hit. Try notes are guaranteed to not be empty either 5072 // and so this will not miss either. 5073 const TryNote& targetTryNote = tryNotes[target.tryNoteIndex]; 5074 delegateTryNote.setDelegate(targetTryNote.tryBodyBegin() + 1); 5075 } 5076 5077 return true; 5078 } 5079 5080 bool BaseCompiler::endTryCatch(ResultType type) { 5081 Control& tryCatch = controlItem(); 5082 LabelKind tryKind = controlKind(0); 5083 5084 if (deadCode_) { 5085 fr.resetStackHeight(tryCatch.stackHeight, type); 5086 popValueStackTo(tryCatch.stackSize); 5087 } else { 5088 // If the previous block is a catch, we must handle the extra exception 5089 // reference on the stack (for rethrow) and thus the stack size is 1 more. 5090 MOZ_ASSERT(stk_.length() == tryCatch.stackSize + type.length() + 5091 (tryKind == LabelKind::Try ? 0 : 1)); 5092 // Assume we have a control join, so place results in block result 5093 // allocations and also handle the implicit exception reference if needed. 5094 if (tryKind == LabelKind::Try) { 5095 popBlockResults(type, tryCatch.stackHeight, ContinuationKind::Jump); 5096 } else { 5097 popCatchResults(type, tryCatch.stackHeight); 5098 } 5099 MOZ_ASSERT(stk_.length() == tryCatch.stackSize); 5100 // Since we will emit a landing pad after this and jump over it to get to 5101 // the control join, we free these here and re-capture at the join. 5102 freeResultRegisters(type); 5103 masm.jump(&tryCatch.label); 5104 MOZ_ASSERT(!tryCatch.bceSafeOnExit); 5105 MOZ_ASSERT(!tryCatch.deadOnArrival); 5106 } 5107 5108 deadCode_ = tryCatch.deadOnArrival; 5109 5110 if (deadCode_) { 5111 return true; 5112 } 5113 5114 // Create landing pad for all catch handlers in this block. 5115 // When used for a catchless try block, this will generate a landing pad 5116 // with no handlers and only the fall-back rethrow. 5117 5118 // The stack height also needs to be set not for a block result, but for the 5119 // entry to the exception handlers. This is reset again below for the join. 5120 StackHeight prePadHeight = fr.stackHeight(); 5121 fr.setStackHeight(tryCatch.stackHeight); 5122 5123 // If we are in a catchless try block, then there were no catch blocks to 5124 // mark the end of the try note, so we need to end it here. 5125 if (tryKind == LabelKind::Try) { 5126 // Mark the end of the try body. This may insert a nop. 5127 finishTryNote(controlItem().tryNoteIndex); 5128 } 5129 5130 // The landing pad begins at this point 5131 TryNoteVector& tryNotes = masm.tryNotes(); 5132 TryNote& tryNote = tryNotes[controlItem().tryNoteIndex]; 5133 tryNote.setLandingPad(masm.currentOffset(), masm.framePushed()); 5134 5135 // Store the Instance that was left in InstanceReg by the exception 5136 // handling mechanism, that is this frame's Instance but with the exception 5137 // filled in Instance::pendingException. 5138 fr.storeInstancePtr(InstanceReg); 5139 5140 // Load exception pointer from Instance and make sure that it is 5141 // saved before the following call will clear it. 5142 RegRef exn; 5143 RegRef tag; 5144 consumePendingException(RegPtr(InstanceReg), &exn, &tag); 5145 5146 // Get a register to hold the tags for each catch 5147 RegRef catchTag = needRef(); 5148 5149 // Ensure that the exception is assigned to the block return register 5150 // before branching to a handler. 5151 pushRef(exn); 5152 ResultType exnResult = ResultType::Single(RefType::extern_()); 5153 popBlockResults(exnResult, tryCatch.stackHeight, ContinuationKind::Jump); 5154 freeResultRegisters(exnResult); 5155 5156 bool hasCatchAll = false; 5157 for (CatchInfo& info : tryCatch.catchInfos) { 5158 if (info.tagIndex != CatchAllIndex) { 5159 MOZ_ASSERT(!hasCatchAll); 5160 loadTag(RegPtr(InstanceReg), info.tagIndex, catchTag); 5161 masm.branchPtr(Assembler::Equal, tag, catchTag, &info.label); 5162 } else { 5163 masm.jump(&info.label); 5164 hasCatchAll = true; 5165 } 5166 } 5167 freeRef(catchTag); 5168 freeRef(tag); 5169 5170 // If none of the tag checks succeed and there is no catch_all, 5171 // then we rethrow the exception. 5172 if (!hasCatchAll) { 5173 captureResultRegisters(exnResult); 5174 if (!pushBlockResults(exnResult) || !throwFrom(popRef())) { 5175 return false; 5176 } 5177 } 5178 5179 // Reset stack height for join. 5180 fr.setStackHeight(prePadHeight); 5181 5182 // Create join point. 5183 if (tryCatch.label.used()) { 5184 masm.bind(&tryCatch.label); 5185 } 5186 5187 captureResultRegisters(type); 5188 deadCode_ = tryCatch.deadOnArrival; 5189 bceSafe_ = tryCatch.bceSafeOnExit; 5190 5191 return pushBlockResults(type); 5192 } 5193 5194 bool BaseCompiler::endTryTable(ResultType type) { 5195 if (!controlItem().deadOnArrival) { 5196 // Mark the end of the try body. This may insert a nop. 5197 finishTryNote(controlItem().tryNoteIndex); 5198 } 5199 return endBlock(type); 5200 } 5201 5202 bool BaseCompiler::emitThrow() { 5203 uint32_t tagIndex; 5204 BaseNothingVector unused_argValues{}; 5205 5206 if (!iter_.readThrow(&tagIndex, &unused_argValues)) { 5207 return false; 5208 } 5209 5210 if (deadCode_) { 5211 return true; 5212 } 5213 5214 const TagDesc& tagDesc = codeMeta_.tags[tagIndex]; 5215 const ResultType& params = tagDesc.type->resultType(); 5216 const TagOffsetVector& offsets = tagDesc.type->argOffsets(); 5217 5218 // Load the tag object 5219 #ifdef RABALDR_PIN_INSTANCE 5220 RegPtr instance(InstanceReg); 5221 #else 5222 RegPtr instance = needPtr(); 5223 fr.loadInstancePtr(instance); 5224 #endif 5225 RegRef tag = needRef(); 5226 loadTag(instance, tagIndex, tag); 5227 #ifndef RABALDR_PIN_INSTANCE 5228 freePtr(instance); 5229 #endif 5230 5231 // Create the new exception object that we will throw. 5232 pushRef(tag); 5233 if (!emitInstanceCall(SASigExceptionNew)) { 5234 return false; 5235 } 5236 5237 // Get registers for exn and data, excluding the prebarrier register 5238 needPtr(RegPtr(PreBarrierReg)); 5239 RegRef exn = popRef(); 5240 RegPtr data = needPtr(); 5241 freePtr(RegPtr(PreBarrierReg)); 5242 5243 masm.loadPtr(Address(exn, WasmExceptionObject::offsetOfData()), data); 5244 5245 for (int32_t i = params.length() - 1; i >= 0; i--) { 5246 uint32_t offset = offsets[i]; 5247 switch (params[i].kind()) { 5248 case ValType::I32: { 5249 RegI32 reg = popI32(); 5250 masm.store32(reg, Address(data, offset)); 5251 freeI32(reg); 5252 break; 5253 } 5254 case ValType::I64: { 5255 RegI64 reg = popI64(); 5256 masm.store64(reg, Address(data, offset)); 5257 freeI64(reg); 5258 break; 5259 } 5260 case ValType::F32: { 5261 RegF32 reg = popF32(); 5262 masm.storeFloat32(reg, Address(data, offset)); 5263 freeF32(reg); 5264 break; 5265 } 5266 case ValType::F64: { 5267 RegF64 reg = popF64(); 5268 masm.storeDouble(reg, Address(data, offset)); 5269 freeF64(reg); 5270 break; 5271 } 5272 case ValType::V128: { 5273 #ifdef ENABLE_WASM_SIMD 5274 RegV128 reg = popV128(); 5275 masm.storeUnalignedSimd128(reg, Address(data, offset)); 5276 freeV128(reg); 5277 break; 5278 #else 5279 MOZ_CRASH("No SIMD support"); 5280 #endif 5281 } 5282 case ValType::Ref: { 5283 RegPtr valueAddr(PreBarrierReg); 5284 needPtr(valueAddr); 5285 masm.computeEffectiveAddress(Address(data, offset), valueAddr); 5286 RegRef rv = popRef(); 5287 pushPtr(data); 5288 // emitBarrieredStore preserves exn, rv 5289 if (!emitBarrieredStore(Some(exn), valueAddr, rv, 5290 PreBarrierKind::Normal, 5291 PostBarrierKind::Imprecise)) { 5292 return false; 5293 } 5294 popPtr(data); 5295 freeRef(rv); 5296 break; 5297 } 5298 } 5299 } 5300 freePtr(data); 5301 5302 deadCode_ = true; 5303 5304 return throwFrom(exn); 5305 } 5306 5307 bool BaseCompiler::emitThrowRef() { 5308 Nothing unused{}; 5309 5310 if (!iter_.readThrowRef(&unused)) { 5311 return false; 5312 } 5313 5314 if (deadCode_) { 5315 return true; 5316 } 5317 5318 RegRef exn = popRef(); 5319 Label ok; 5320 masm.branchWasmAnyRefIsNull(false, exn, &ok); 5321 trap(Trap::NullPointerDereference); 5322 masm.bind(&ok); 5323 deadCode_ = true; 5324 return throwFrom(exn); 5325 } 5326 5327 bool BaseCompiler::emitRethrow() { 5328 uint32_t relativeDepth; 5329 if (!iter_.readRethrow(&relativeDepth)) { 5330 return false; 5331 } 5332 5333 if (deadCode_) { 5334 return true; 5335 } 5336 5337 Control& tryCatch = controlItem(relativeDepth); 5338 RegRef exn = needRef(); 5339 peekRefAt(tryCatch.stackSize, exn); 5340 5341 deadCode_ = true; 5342 5343 return throwFrom(exn); 5344 } 5345 5346 bool BaseCompiler::emitDrop() { 5347 if (!iter_.readDrop()) { 5348 return false; 5349 } 5350 5351 if (deadCode_) { 5352 return true; 5353 } 5354 5355 dropValue(); 5356 return true; 5357 } 5358 5359 void BaseCompiler::doReturn(ContinuationKind kind) { 5360 if (deadCode_) { 5361 return; 5362 } 5363 5364 StackHeight height = controlOutermost().stackHeight; 5365 ResultType type = ResultType::Vector(funcType().results()); 5366 popBlockResults(type, height, kind); 5367 masm.jump(&returnLabel_); 5368 freeResultRegisters(type); 5369 } 5370 5371 bool BaseCompiler::emitReturn() { 5372 BaseNothingVector unused_values{}; 5373 if (!iter_.readReturn(&unused_values)) { 5374 return false; 5375 } 5376 5377 if (deadCode_) { 5378 return true; 5379 } 5380 5381 doReturn(ContinuationKind::Jump); 5382 deadCode_ = true; 5383 5384 return true; 5385 } 5386 5387 // For now, always sync() at the beginning of the call to easily save live 5388 // values. 5389 // 5390 // TODO / OPTIMIZE (Bug 1316806): We may be able to avoid a full sync(), since 5391 // all we want is to save live registers that won't be saved by the callee or 5392 // that we need for outgoing args - we don't need to sync the locals. We can 5393 // just push the necessary registers, it'll be like a lightweight sync. 5394 // 5395 // Even some of the pushing may be unnecessary if the registers will be consumed 5396 // by the call, because then what we want is parallel assignment to the argument 5397 // registers or onto the stack for outgoing arguments. A sync() is just 5398 // simpler. 5399 5400 bool BaseCompiler::emitCall() { 5401 uint32_t funcIndex; 5402 BaseNothingVector args_{}; 5403 if (!iter_.readCall(&funcIndex, &args_)) { 5404 return false; 5405 } 5406 5407 if (deadCode_) { 5408 return true; 5409 } 5410 5411 bool import = codeMeta_.funcIsImport(funcIndex); 5412 5413 if (import) { 5414 BuiltinModuleFuncId knownFuncImport = codeMeta_.knownFuncImport(funcIndex); 5415 if (knownFuncImport != BuiltinModuleFuncId::None) { 5416 const BuiltinModuleFunc& builtinModuleFunc = 5417 BuiltinModuleFuncs::getFromId(knownFuncImport); 5418 if (builtinModuleFunc.usesMemory()) { 5419 // The final parameter of an builtinModuleFunc is implicitly the heap 5420 // base 5421 pushHeapBase(0); 5422 } 5423 5424 // Call the builtinModuleFunc 5425 return emitInstanceCall(*builtinModuleFunc.sig()); 5426 } 5427 } 5428 5429 sync(); 5430 5431 const FuncType& funcType = codeMeta_.getFuncType(funcIndex); 5432 5433 uint32_t numArgs = funcType.args().length(); 5434 size_t stackArgBytes = stackConsumed(numArgs); 5435 5436 ResultType resultType(ResultType::Vector(funcType.results())); 5437 StackResultsLoc results; 5438 if (!pushStackResultsForWasmCall(resultType, RegPtr(ABINonArgReg0), 5439 &results)) { 5440 return false; 5441 } 5442 5443 FunctionCall baselineCall(ABIKind::Wasm, 5444 import ? RestoreState::All : RestoreState::None); 5445 beginCall(baselineCall); 5446 5447 if (!emitCallArgs(funcType.args(), NormalCallResults(results), &baselineCall, 5448 CalleeOnStack::False)) { 5449 return false; 5450 } 5451 5452 CodeOffset raOffset; 5453 if (import) { 5454 raOffset = callImport(codeMeta_.offsetOfFuncImportInstanceData(funcIndex), 5455 baselineCall); 5456 } else { 5457 raOffset = callDefinition(funcIndex, baselineCall); 5458 } 5459 5460 if (!createStackMap("emitCall", raOffset)) { 5461 return false; 5462 } 5463 5464 popStackResultsAfterWasmCall(results, stackArgBytes); 5465 5466 endCall(baselineCall, stackArgBytes); 5467 5468 popValueStackBy(numArgs); 5469 5470 captureCallResultRegisters(resultType); 5471 return pushWasmCallResults(baselineCall, resultType, results); 5472 } 5473 5474 bool BaseCompiler::emitReturnCall() { 5475 uint32_t funcIndex; 5476 BaseNothingVector args_{}; 5477 if (!iter_.readReturnCall(&funcIndex, &args_)) { 5478 return false; 5479 } 5480 5481 if (deadCode_) { 5482 return true; 5483 } 5484 5485 sync(); 5486 if (!insertDebugCollapseFrame()) { 5487 return false; 5488 } 5489 5490 const FuncType& funcType = codeMeta_.getFuncType(funcIndex); 5491 bool import = codeMeta_.funcIsImport(funcIndex); 5492 5493 uint32_t numArgs = funcType.args().length(); 5494 5495 FunctionCall baselineCall(ABIKind::Wasm, 5496 import ? RestoreState::All : RestoreState::None); 5497 beginCall(baselineCall); 5498 5499 if (!emitCallArgs(funcType.args(), TailCallResults(funcType), &baselineCall, 5500 CalleeOnStack::False)) { 5501 return false; 5502 } 5503 5504 ReturnCallAdjustmentInfo retCallInfo = 5505 BuildReturnCallAdjustmentInfo(this->funcType(), funcType); 5506 5507 if (import) { 5508 CallSiteDesc desc(bytecodeOffset(), CallSiteKind::Import); 5509 CalleeDesc callee = 5510 CalleeDesc::import(codeMeta_.offsetOfFuncImportInstanceData(funcIndex)); 5511 masm.wasmReturnCallImport(desc, callee, retCallInfo); 5512 } else { 5513 CallSiteDesc desc(bytecodeOffset(), CallSiteKind::ReturnFunc); 5514 masm.wasmReturnCall(desc, funcIndex, retCallInfo); 5515 } 5516 5517 MOZ_ASSERT(stackMapGenerator_.framePushedExcludingOutboundCallArgs.isSome()); 5518 stackMapGenerator_.framePushedExcludingOutboundCallArgs.reset(); 5519 5520 popValueStackBy(numArgs); 5521 5522 deadCode_ = true; 5523 return true; 5524 } 5525 5526 bool BaseCompiler::emitCallIndirect() { 5527 uint32_t funcTypeIndex; 5528 uint32_t tableIndex; 5529 Nothing callee_; 5530 BaseNothingVector args_{}; 5531 if (!iter_.readCallIndirect(&funcTypeIndex, &tableIndex, &callee_, &args_)) { 5532 return false; 5533 } 5534 5535 if (deadCode_) { 5536 return true; 5537 } 5538 5539 // Stack: ... arg1 .. argn callee 5540 sync(); 5541 5542 const FuncType& funcType = (*codeMeta_.types)[funcTypeIndex].funcType(); 5543 5544 uint32_t numArgs = funcType.args().length() + 1; 5545 size_t stackArgBytes = stackConsumed(numArgs); 5546 5547 ResultType resultType(ResultType::Vector(funcType.results())); 5548 StackResultsLoc results; 5549 if (!pushStackResultsForWasmCall(resultType, RegPtr(ABINonArgReg0), 5550 &results)) { 5551 return false; 5552 } 5553 5554 // State and realm are restored as needed by by callIndirect (really by 5555 // MacroAssembler::wasmCallIndirect). 5556 FunctionCall baselineCall(ABIKind::Wasm, RestoreState::None); 5557 beginCall(baselineCall); 5558 5559 if (!emitCallArgs(funcType.args(), NormalCallResults(results), &baselineCall, 5560 CalleeOnStack::True)) { 5561 return false; 5562 } 5563 5564 const Stk& callee = peek(results.count()); 5565 CodeOffset fastCallOffset; 5566 CodeOffset slowCallOffset; 5567 if (!callIndirect(funcTypeIndex, tableIndex, callee, baselineCall, 5568 /*tailCall*/ false, &fastCallOffset, &slowCallOffset)) { 5569 return false; 5570 } 5571 if (!createStackMap("emitCallIndirect", fastCallOffset)) { 5572 return false; 5573 } 5574 if (!createStackMap("emitCallIndirect", slowCallOffset)) { 5575 return false; 5576 } 5577 5578 popStackResultsAfterWasmCall(results, stackArgBytes); 5579 5580 endCall(baselineCall, stackArgBytes); 5581 5582 popValueStackBy(numArgs); 5583 5584 captureCallResultRegisters(resultType); 5585 return pushWasmCallResults(baselineCall, resultType, results); 5586 } 5587 5588 bool BaseCompiler::emitReturnCallIndirect() { 5589 uint32_t funcTypeIndex; 5590 uint32_t tableIndex; 5591 Nothing callee_; 5592 BaseNothingVector args_{}; 5593 if (!iter_.readReturnCallIndirect(&funcTypeIndex, &tableIndex, &callee_, 5594 &args_)) { 5595 return false; 5596 } 5597 5598 if (deadCode_) { 5599 return true; 5600 } 5601 5602 // Stack: ... arg1 .. argn callee 5603 5604 sync(); 5605 if (!insertDebugCollapseFrame()) { 5606 return false; 5607 } 5608 5609 const FuncType& funcType = (*codeMeta_.types)[funcTypeIndex].funcType(); 5610 5611 uint32_t numArgs = funcType.args().length() + 1; 5612 5613 // State and realm are restored as needed by by callIndirect (really by 5614 // MacroAssembler::wasmCallIndirect). 5615 FunctionCall baselineCall(ABIKind::Wasm, RestoreState::None); 5616 beginCall(baselineCall); 5617 5618 if (!emitCallArgs(funcType.args(), TailCallResults(funcType), &baselineCall, 5619 CalleeOnStack::True)) { 5620 return false; 5621 } 5622 5623 const Stk& callee = peek(0); 5624 CodeOffset fastCallOffset; 5625 CodeOffset slowCallOffset; 5626 if (!callIndirect(funcTypeIndex, tableIndex, callee, baselineCall, 5627 /*tailCall*/ true, &fastCallOffset, &slowCallOffset)) { 5628 return false; 5629 } 5630 5631 MOZ_ASSERT(stackMapGenerator_.framePushedExcludingOutboundCallArgs.isSome()); 5632 stackMapGenerator_.framePushedExcludingOutboundCallArgs.reset(); 5633 5634 popValueStackBy(numArgs); 5635 5636 deadCode_ = true; 5637 return true; 5638 } 5639 5640 bool BaseCompiler::emitCallRef() { 5641 uint32_t funcTypeIndex; 5642 Nothing unused_callee; 5643 BaseNothingVector unused_args{}; 5644 if (!iter_.readCallRef(&funcTypeIndex, &unused_callee, &unused_args)) { 5645 return false; 5646 } 5647 5648 // Add a metrics entry to track this call_ref site. Do this even if we're in 5649 // 'dead code' to have easy consistency with ion, which consumes these. 5650 Maybe<size_t> callRefIndex; 5651 if (compilerEnv_.mode() == CompileMode::LazyTiering) { 5652 masm.append(wasm::CallRefMetricsPatch()); 5653 if (masm.oom()) { 5654 return false; 5655 } 5656 callRefIndex = Some(masm.callRefMetricsPatches().length() - 1); 5657 } 5658 5659 if (deadCode_) { 5660 return true; 5661 } 5662 5663 const FuncType& funcType = codeMeta_.types->type(funcTypeIndex).funcType(); 5664 5665 sync(); 5666 5667 // Stack: ... arg1 .. argn callee 5668 5669 uint32_t numArgs = funcType.args().length() + 1; 5670 size_t stackArgBytes = stackConsumed(numArgs); 5671 5672 ResultType resultType(ResultType::Vector(funcType.results())); 5673 StackResultsLoc results; 5674 if (!pushStackResultsForWasmCall(resultType, RegPtr(ABINonArgReg0), 5675 &results)) { 5676 return false; 5677 } 5678 5679 // State and realm are restored as needed by by callRef (really by 5680 // MacroAssembler::wasmCallRef). 5681 FunctionCall baselineCall(ABIKind::Wasm, RestoreState::None); 5682 beginCall(baselineCall); 5683 5684 if (!emitCallArgs(funcType.args(), NormalCallResults(results), &baselineCall, 5685 CalleeOnStack::True)) { 5686 return false; 5687 } 5688 5689 const Stk& callee = peek(results.count()); 5690 CodeOffset fastCallOffset; 5691 CodeOffset slowCallOffset; 5692 if (!callRef(callee, baselineCall, callRefIndex, &fastCallOffset, 5693 &slowCallOffset)) { 5694 return false; 5695 } 5696 if (!createStackMap("emitCallRef", fastCallOffset)) { 5697 return false; 5698 } 5699 if (!createStackMap("emitCallRef", slowCallOffset)) { 5700 return false; 5701 } 5702 5703 popStackResultsAfterWasmCall(results, stackArgBytes); 5704 5705 endCall(baselineCall, stackArgBytes); 5706 5707 popValueStackBy(numArgs); 5708 5709 captureCallResultRegisters(resultType); 5710 return pushWasmCallResults(baselineCall, resultType, results); 5711 } 5712 5713 bool BaseCompiler::emitReturnCallRef() { 5714 uint32_t funcTypeIndex; 5715 Nothing unused_callee; 5716 BaseNothingVector unused_args{}; 5717 if (!iter_.readReturnCallRef(&funcTypeIndex, &unused_callee, &unused_args)) { 5718 return false; 5719 } 5720 5721 if (deadCode_) { 5722 return true; 5723 } 5724 5725 const FuncType& funcType = codeMeta_.types->type(funcTypeIndex).funcType(); 5726 5727 sync(); 5728 5729 if (!insertDebugCollapseFrame()) { 5730 return false; 5731 } 5732 5733 // Stack: ... arg1 .. argn callee 5734 5735 uint32_t numArgs = funcType.args().length() + 1; 5736 5737 // State and realm are restored as needed by by callRef (really by 5738 // MacroAssembler::wasmCallRef). 5739 FunctionCall baselineCall(ABIKind::Wasm, RestoreState::None); 5740 beginCall(baselineCall); 5741 5742 if (!emitCallArgs(funcType.args(), TailCallResults(funcType), &baselineCall, 5743 CalleeOnStack::True)) { 5744 return false; 5745 } 5746 5747 const Stk& callee = peek(0); 5748 returnCallRef(callee, baselineCall, funcType); 5749 5750 MOZ_ASSERT(stackMapGenerator_.framePushedExcludingOutboundCallArgs.isSome()); 5751 stackMapGenerator_.framePushedExcludingOutboundCallArgs.reset(); 5752 5753 popValueStackBy(numArgs); 5754 5755 deadCode_ = true; 5756 return true; 5757 } 5758 5759 void BaseCompiler::emitRound(RoundingMode roundingMode, ValType operandType) { 5760 if (operandType == ValType::F32) { 5761 RegF32 f0 = popF32(); 5762 roundF32(roundingMode, f0); 5763 pushF32(f0); 5764 } else if (operandType == ValType::F64) { 5765 RegF64 f0 = popF64(); 5766 roundF64(roundingMode, f0); 5767 pushF64(f0); 5768 } else { 5769 MOZ_CRASH("unexpected type"); 5770 } 5771 } 5772 5773 bool BaseCompiler::emitUnaryMathBuiltinCall(SymbolicAddress callee, 5774 ValType operandType) { 5775 Nothing operand_; 5776 if (!iter_.readUnary(operandType, &operand_)) { 5777 return false; 5778 } 5779 5780 if (deadCode_) { 5781 return true; 5782 } 5783 5784 RoundingMode roundingMode; 5785 if (IsRoundingFunction(callee, &roundingMode) && 5786 supportsRoundInstruction(roundingMode)) { 5787 emitRound(roundingMode, operandType); 5788 return true; 5789 } 5790 5791 sync(); 5792 5793 ValTypeVector& signature = operandType == ValType::F32 ? SigF_ : SigD_; 5794 ValType retType = operandType; 5795 uint32_t numArgs = signature.length(); 5796 size_t stackSpace = stackConsumed(numArgs); 5797 5798 FunctionCall baselineCall(ABIForBuiltin(callee), RestoreState::None); 5799 beginCall(baselineCall); 5800 5801 if (!emitCallArgs(signature, NoCallResults(), &baselineCall, 5802 CalleeOnStack::False)) { 5803 return false; 5804 } 5805 5806 CodeOffset raOffset = builtinCall(callee, baselineCall); 5807 if (!createStackMap("emitUnaryMathBuiltin[..]", raOffset)) { 5808 return false; 5809 } 5810 5811 endCall(baselineCall, stackSpace); 5812 5813 popValueStackBy(numArgs); 5814 5815 pushBuiltinCallResult(baselineCall, retType.toMIRType()); 5816 5817 return true; 5818 } 5819 5820 #ifdef RABALDR_INT_DIV_I64_CALLOUT 5821 bool BaseCompiler::emitDivOrModI64BuiltinCall(SymbolicAddress callee, 5822 ValType operandType) { 5823 MOZ_ASSERT(operandType == ValType::I64); 5824 MOZ_ASSERT(!deadCode_); 5825 5826 sync(); 5827 5828 needI64(specific_.abiReturnRegI64); 5829 5830 RegI64 rhs = popI64(); 5831 RegI64 srcDest = popI64ToSpecific(specific_.abiReturnRegI64); 5832 5833 Label done; 5834 5835 checkDivideByZero(rhs); 5836 5837 if (callee == SymbolicAddress::DivI64) { 5838 checkDivideSignedOverflow(rhs, srcDest, &done, ZeroOnOverflow(false)); 5839 } else if (callee == SymbolicAddress::ModI64) { 5840 checkDivideSignedOverflow(rhs, srcDest, &done, ZeroOnOverflow(true)); 5841 } 5842 5843 masm.setupWasmABICall(callee); 5844 masm.passABIArg(srcDest.high); 5845 masm.passABIArg(srcDest.low); 5846 masm.passABIArg(rhs.high); 5847 masm.passABIArg(rhs.low); 5848 CodeOffset raOffset = masm.callWithABI( 5849 bytecodeOffset(), callee, mozilla::Some(fr.getInstancePtrOffset())); 5850 if (!createStackMap("emitDivOrModI64Bui[..]", raOffset)) { 5851 return false; 5852 } 5853 5854 masm.bind(&done); 5855 5856 freeI64(rhs); 5857 pushI64(srcDest); 5858 return true; 5859 } 5860 #endif // RABALDR_INT_DIV_I64_CALLOUT 5861 5862 #ifdef RABALDR_I64_TO_FLOAT_CALLOUT 5863 bool BaseCompiler::emitConvertInt64ToFloatingCallout(SymbolicAddress callee, 5864 ValType operandType, 5865 ValType resultType) { 5866 sync(); 5867 5868 RegI64 input = popI64(); 5869 5870 FunctionCall call(ABIKind::Wasm, RestoreState::None); 5871 5872 masm.setupWasmABICall(callee); 5873 # ifdef JS_PUNBOX64 5874 MOZ_CRASH("BaseCompiler platform hook: emitConvertInt64ToFloatingCallout"); 5875 # else 5876 masm.passABIArg(input.high); 5877 masm.passABIArg(input.low); 5878 # endif 5879 CodeOffset raOffset = masm.callWithABI( 5880 bytecodeOffset(), callee, mozilla::Some(fr.getInstancePtrOffset()), 5881 resultType == ValType::F32 ? ABIType::Float32 : ABIType::Float64); 5882 if (!createStackMap("emitConvertInt64To[..]", raOffset)) { 5883 return false; 5884 } 5885 5886 freeI64(input); 5887 5888 if (resultType == ValType::F32) { 5889 pushF32(captureReturnedF32(call)); 5890 } else { 5891 pushF64(captureReturnedF64(call)); 5892 } 5893 5894 return true; 5895 } 5896 #endif // RABALDR_I64_TO_FLOAT_CALLOUT 5897 5898 #ifdef RABALDR_FLOAT_TO_I64_CALLOUT 5899 // `Callee` always takes a double, so a float32 input must be converted. 5900 bool BaseCompiler::emitConvertFloatingToInt64Callout(SymbolicAddress callee, 5901 ValType operandType, 5902 ValType resultType) { 5903 RegF64 doubleInput; 5904 if (operandType == ValType::F32) { 5905 doubleInput = needF64(); 5906 RegF32 input = popF32(); 5907 masm.convertFloat32ToDouble(input, doubleInput); 5908 freeF32(input); 5909 } else { 5910 doubleInput = popF64(); 5911 } 5912 5913 // We may need the value after the call for the ool check. 5914 RegF64 otherReg = needF64(); 5915 moveF64(doubleInput, otherReg); 5916 pushF64(otherReg); 5917 5918 sync(); 5919 5920 FunctionCall call(ABIKind::Wasm, RestoreState::None); 5921 5922 masm.setupWasmABICall(callee); 5923 masm.passABIArg(doubleInput, ABIType::Float64); 5924 CodeOffset raOffset = masm.callWithABI( 5925 bytecodeOffset(), callee, mozilla::Some(fr.getInstancePtrOffset())); 5926 if (!createStackMap("emitConvertFloatin[..]", raOffset)) { 5927 return false; 5928 } 5929 5930 freeF64(doubleInput); 5931 5932 RegI64 rv = captureReturnedI64(); 5933 5934 RegF64 inputVal = popF64(); 5935 5936 TruncFlags flags = 0; 5937 if (callee == SymbolicAddress::TruncateDoubleToUint64) { 5938 flags |= TRUNC_UNSIGNED; 5939 } 5940 if (callee == SymbolicAddress::SaturatingTruncateDoubleToInt64 || 5941 callee == SymbolicAddress::SaturatingTruncateDoubleToUint64) { 5942 flags |= TRUNC_SATURATING; 5943 } 5944 5945 // If we're saturating, the callout will always produce the final result 5946 // value. Otherwise, the callout value will return 0x8000000000000000 5947 // and we need to produce traps. 5948 OutOfLineCode* ool = nullptr; 5949 if (!(flags & TRUNC_SATURATING)) { 5950 // The OOL check just succeeds or fails, it does not generate a value. 5951 ool = addOutOfLineCode(new (alloc_) OutOfLineTruncateCheckF32OrF64ToI64( 5952 AnyReg(inputVal), rv, flags, trapSiteDesc())); 5953 if (!ool) { 5954 return false; 5955 } 5956 5957 masm.branch64(Assembler::Equal, rv, Imm64(0x8000000000000000), 5958 ool->entry()); 5959 masm.bind(ool->rejoin()); 5960 } 5961 5962 pushI64(rv); 5963 freeF64(inputVal); 5964 5965 return true; 5966 } 5967 #endif // RABALDR_FLOAT_TO_I64_CALLOUT 5968 5969 bool BaseCompiler::emitGetLocal() { 5970 uint32_t slot; 5971 if (!iter_.readGetLocal(&slot)) { 5972 return false; 5973 } 5974 5975 if (deadCode_) { 5976 return true; 5977 } 5978 5979 // Local loads are pushed unresolved, ie, they may be deferred 5980 // until needed, until they may be affected by a store, or until a 5981 // sync. This is intended to reduce register pressure. 5982 5983 switch (locals_[slot].kind()) { 5984 case ValType::I32: 5985 pushLocalI32(slot); 5986 break; 5987 case ValType::I64: 5988 pushLocalI64(slot); 5989 break; 5990 case ValType::V128: 5991 #ifdef ENABLE_WASM_SIMD 5992 pushLocalV128(slot); 5993 break; 5994 #else 5995 MOZ_CRASH("No SIMD support"); 5996 #endif 5997 case ValType::F64: 5998 pushLocalF64(slot); 5999 break; 6000 case ValType::F32: 6001 pushLocalF32(slot); 6002 break; 6003 case ValType::Ref: 6004 pushLocalRef(slot); 6005 break; 6006 } 6007 6008 return true; 6009 } 6010 6011 template <bool isSetLocal> 6012 bool BaseCompiler::emitSetOrTeeLocal(uint32_t slot) { 6013 if (deadCode_) { 6014 return true; 6015 } 6016 6017 bceLocalIsUpdated(slot); 6018 switch (locals_[slot].kind()) { 6019 case ValType::I32: { 6020 RegI32 rv = popI32(); 6021 syncLocal(slot); 6022 fr.storeLocalI32(rv, localFromSlot(slot, MIRType::Int32)); 6023 if (isSetLocal) { 6024 freeI32(rv); 6025 } else { 6026 pushI32(rv); 6027 } 6028 break; 6029 } 6030 case ValType::I64: { 6031 RegI64 rv = popI64(); 6032 syncLocal(slot); 6033 fr.storeLocalI64(rv, localFromSlot(slot, MIRType::Int64)); 6034 if (isSetLocal) { 6035 freeI64(rv); 6036 } else { 6037 pushI64(rv); 6038 } 6039 break; 6040 } 6041 case ValType::F64: { 6042 RegF64 rv = popF64(); 6043 syncLocal(slot); 6044 fr.storeLocalF64(rv, localFromSlot(slot, MIRType::Double)); 6045 if (isSetLocal) { 6046 freeF64(rv); 6047 } else { 6048 pushF64(rv); 6049 } 6050 break; 6051 } 6052 case ValType::F32: { 6053 RegF32 rv = popF32(); 6054 syncLocal(slot); 6055 fr.storeLocalF32(rv, localFromSlot(slot, MIRType::Float32)); 6056 if (isSetLocal) { 6057 freeF32(rv); 6058 } else { 6059 pushF32(rv); 6060 } 6061 break; 6062 } 6063 case ValType::V128: { 6064 #ifdef ENABLE_WASM_SIMD 6065 RegV128 rv = popV128(); 6066 syncLocal(slot); 6067 fr.storeLocalV128(rv, localFromSlot(slot, MIRType::Simd128)); 6068 if (isSetLocal) { 6069 freeV128(rv); 6070 } else { 6071 pushV128(rv); 6072 } 6073 break; 6074 #else 6075 MOZ_CRASH("No SIMD support"); 6076 #endif 6077 } 6078 case ValType::Ref: { 6079 RegRef rv = popRef(); 6080 syncLocal(slot); 6081 fr.storeLocalRef(rv, localFromSlot(slot, MIRType::WasmAnyRef)); 6082 if (isSetLocal) { 6083 freeRef(rv); 6084 } else { 6085 pushRef(rv); 6086 } 6087 break; 6088 } 6089 } 6090 6091 return true; 6092 } 6093 6094 bool BaseCompiler::emitSetLocal() { 6095 uint32_t slot; 6096 Nothing unused_value; 6097 if (!iter_.readSetLocal(&slot, &unused_value)) { 6098 return false; 6099 } 6100 return emitSetOrTeeLocal<true>(slot); 6101 } 6102 6103 bool BaseCompiler::emitTeeLocal() { 6104 uint32_t slot; 6105 Nothing unused_value; 6106 if (!iter_.readTeeLocal(&slot, &unused_value)) { 6107 return false; 6108 } 6109 return emitSetOrTeeLocal<false>(slot); 6110 } 6111 6112 bool BaseCompiler::emitGetGlobal() { 6113 uint32_t id; 6114 if (!iter_.readGetGlobal(&id)) { 6115 return false; 6116 } 6117 6118 if (deadCode_) { 6119 return true; 6120 } 6121 6122 const GlobalDesc& global = codeMeta_.globals[id]; 6123 6124 if (global.isConstant()) { 6125 LitVal value = global.constantValue(); 6126 switch (value.type().kind()) { 6127 case ValType::I32: 6128 pushI32(value.i32()); 6129 break; 6130 case ValType::I64: 6131 pushI64(value.i64()); 6132 break; 6133 case ValType::F32: 6134 pushF32(value.f32()); 6135 break; 6136 case ValType::F64: 6137 pushF64(value.f64()); 6138 break; 6139 case ValType::Ref: 6140 pushRef(intptr_t(value.ref().forCompiledCode())); 6141 break; 6142 #ifdef ENABLE_WASM_SIMD 6143 case ValType::V128: 6144 pushV128(value.v128()); 6145 break; 6146 #endif 6147 default: 6148 MOZ_CRASH("Global constant type"); 6149 } 6150 return true; 6151 } 6152 6153 switch (global.type().kind()) { 6154 case ValType::I32: { 6155 RegI32 rv = needI32(); 6156 ScratchPtr tmp(*this); 6157 masm.load32(addressOfGlobalVar(global, tmp), rv); 6158 pushI32(rv); 6159 break; 6160 } 6161 case ValType::I64: { 6162 RegI64 rv = needI64(); 6163 ScratchPtr tmp(*this); 6164 masm.load64(addressOfGlobalVar(global, tmp), rv); 6165 pushI64(rv); 6166 break; 6167 } 6168 case ValType::F32: { 6169 RegF32 rv = needF32(); 6170 ScratchPtr tmp(*this); 6171 masm.loadFloat32(addressOfGlobalVar(global, tmp), rv); 6172 pushF32(rv); 6173 break; 6174 } 6175 case ValType::F64: { 6176 RegF64 rv = needF64(); 6177 ScratchPtr tmp(*this); 6178 masm.loadDouble(addressOfGlobalVar(global, tmp), rv); 6179 pushF64(rv); 6180 break; 6181 } 6182 case ValType::Ref: { 6183 RegRef rv = needRef(); 6184 ScratchPtr tmp(*this); 6185 masm.loadPtr(addressOfGlobalVar(global, tmp), rv); 6186 pushRef(rv); 6187 break; 6188 } 6189 #ifdef ENABLE_WASM_SIMD 6190 case ValType::V128: { 6191 RegV128 rv = needV128(); 6192 ScratchPtr tmp(*this); 6193 masm.loadUnalignedSimd128(addressOfGlobalVar(global, tmp), rv); 6194 pushV128(rv); 6195 break; 6196 } 6197 #endif 6198 default: 6199 MOZ_CRASH("Global variable type"); 6200 break; 6201 } 6202 return true; 6203 } 6204 6205 bool BaseCompiler::emitSetGlobal() { 6206 uint32_t id; 6207 Nothing unused_value; 6208 if (!iter_.readSetGlobal(&id, &unused_value)) { 6209 return false; 6210 } 6211 6212 if (deadCode_) { 6213 return true; 6214 } 6215 6216 const GlobalDesc& global = codeMeta_.globals[id]; 6217 6218 switch (global.type().kind()) { 6219 case ValType::I32: { 6220 RegI32 rv = popI32(); 6221 ScratchPtr tmp(*this); 6222 masm.store32(rv, addressOfGlobalVar(global, tmp)); 6223 freeI32(rv); 6224 break; 6225 } 6226 case ValType::I64: { 6227 RegI64 rv = popI64(); 6228 ScratchPtr tmp(*this); 6229 masm.store64(rv, addressOfGlobalVar(global, tmp)); 6230 freeI64(rv); 6231 break; 6232 } 6233 case ValType::F32: { 6234 RegF32 rv = popF32(); 6235 ScratchPtr tmp(*this); 6236 masm.storeFloat32(rv, addressOfGlobalVar(global, tmp)); 6237 freeF32(rv); 6238 break; 6239 } 6240 case ValType::F64: { 6241 RegF64 rv = popF64(); 6242 ScratchPtr tmp(*this); 6243 masm.storeDouble(rv, addressOfGlobalVar(global, tmp)); 6244 freeF64(rv); 6245 break; 6246 } 6247 case ValType::Ref: { 6248 RegPtr valueAddr(PreBarrierReg); 6249 needPtr(valueAddr); 6250 { 6251 ScratchPtr tmp(*this); 6252 masm.computeEffectiveAddress(addressOfGlobalVar(global, tmp), 6253 valueAddr); 6254 } 6255 RegRef rv = popRef(); 6256 // emitBarrieredStore preserves rv 6257 if (!emitBarrieredStore(Nothing(), valueAddr, rv, PreBarrierKind::Normal, 6258 PostBarrierKind::Imprecise)) { 6259 return false; 6260 } 6261 freeRef(rv); 6262 break; 6263 } 6264 #ifdef ENABLE_WASM_SIMD 6265 case ValType::V128: { 6266 RegV128 rv = popV128(); 6267 ScratchPtr tmp(*this); 6268 masm.storeUnalignedSimd128(rv, addressOfGlobalVar(global, tmp)); 6269 freeV128(rv); 6270 break; 6271 } 6272 #endif 6273 default: 6274 MOZ_CRASH("Global variable type"); 6275 break; 6276 } 6277 return true; 6278 } 6279 6280 bool BaseCompiler::emitLoad(ValType type, Scalar::Type viewType) { 6281 LinearMemoryAddress<Nothing> addr; 6282 if (!iter_.readLoad(type, Scalar::byteSize(viewType), &addr)) { 6283 return false; 6284 } 6285 if (deadCode_) { 6286 return true; 6287 } 6288 MemoryAccessDesc access(addr.memoryIndex, viewType, addr.align, addr.offset, 6289 trapSiteDesc(), hugeMemoryEnabled(addr.memoryIndex)); 6290 loadCommon(&access, AccessCheck(), type); 6291 return true; 6292 } 6293 6294 bool BaseCompiler::emitStore(ValType resultType, Scalar::Type viewType) { 6295 LinearMemoryAddress<Nothing> addr; 6296 Nothing unused_value; 6297 if (!iter_.readStore(resultType, Scalar::byteSize(viewType), &addr, 6298 &unused_value)) { 6299 return false; 6300 } 6301 if (deadCode_) { 6302 return true; 6303 } 6304 MemoryAccessDesc access(addr.memoryIndex, viewType, addr.align, addr.offset, 6305 trapSiteDesc(), hugeMemoryEnabled(addr.memoryIndex)); 6306 storeCommon(&access, AccessCheck(), resultType); 6307 return true; 6308 } 6309 6310 bool BaseCompiler::emitSelect(bool typed) { 6311 StackType type; 6312 Nothing unused_trueValue; 6313 Nothing unused_falseValue; 6314 Nothing unused_condition; 6315 if (!iter_.readSelect(typed, &type, &unused_trueValue, &unused_falseValue, 6316 &unused_condition)) { 6317 return false; 6318 } 6319 6320 if (deadCode_) { 6321 resetLatentOp(); 6322 return true; 6323 } 6324 6325 // I32 condition on top, then false, then true. 6326 6327 Label done; 6328 BranchState b(&done); 6329 emitBranchSetup(&b); 6330 6331 switch (type.valType().kind()) { 6332 case ValType::I32: { 6333 RegI32 r, rs; 6334 pop2xI32(&r, &rs); 6335 if (!emitBranchPerform(&b)) { 6336 return false; 6337 } 6338 moveI32(rs, r); 6339 masm.bind(&done); 6340 freeI32(rs); 6341 pushI32(r); 6342 break; 6343 } 6344 case ValType::I64: { 6345 #ifdef JS_CODEGEN_X86 6346 // There may be as many as four Int64 values in registers at a time: two 6347 // for the latent branch operands, and two for the true/false values we 6348 // normally pop before executing the branch. On x86 this is one value 6349 // too many, so we need to generate more complicated code here, and for 6350 // simplicity's sake we do so even if the branch operands are not Int64. 6351 // However, the resulting control flow diamond is complicated since the 6352 // arms of the diamond will have to stay synchronized with respect to 6353 // their evaluation stack and regalloc state. To simplify further, we 6354 // use a double branch and a temporary boolean value for now. 6355 RegI32 temp = needI32(); 6356 moveImm32(0, temp); 6357 if (!emitBranchPerform(&b)) { 6358 return false; 6359 } 6360 moveImm32(1, temp); 6361 masm.bind(&done); 6362 6363 Label trueValue; 6364 RegI64 r, rs; 6365 pop2xI64(&r, &rs); 6366 masm.branch32(Assembler::Equal, temp, Imm32(0), &trueValue); 6367 moveI64(rs, r); 6368 masm.bind(&trueValue); 6369 freeI32(temp); 6370 freeI64(rs); 6371 pushI64(r); 6372 #else 6373 RegI64 r, rs; 6374 pop2xI64(&r, &rs); 6375 if (!emitBranchPerform(&b)) { 6376 return false; 6377 } 6378 moveI64(rs, r); 6379 masm.bind(&done); 6380 freeI64(rs); 6381 pushI64(r); 6382 #endif 6383 break; 6384 } 6385 case ValType::F32: { 6386 RegF32 r, rs; 6387 pop2xF32(&r, &rs); 6388 if (!emitBranchPerform(&b)) { 6389 return false; 6390 } 6391 moveF32(rs, r); 6392 masm.bind(&done); 6393 freeF32(rs); 6394 pushF32(r); 6395 break; 6396 } 6397 case ValType::F64: { 6398 RegF64 r, rs; 6399 pop2xF64(&r, &rs); 6400 if (!emitBranchPerform(&b)) { 6401 return false; 6402 } 6403 moveF64(rs, r); 6404 masm.bind(&done); 6405 freeF64(rs); 6406 pushF64(r); 6407 break; 6408 } 6409 #ifdef ENABLE_WASM_SIMD 6410 case ValType::V128: { 6411 RegV128 r, rs; 6412 pop2xV128(&r, &rs); 6413 if (!emitBranchPerform(&b)) { 6414 return false; 6415 } 6416 moveV128(rs, r); 6417 masm.bind(&done); 6418 freeV128(rs); 6419 pushV128(r); 6420 break; 6421 } 6422 #endif 6423 case ValType::Ref: { 6424 RegRef r, rs; 6425 pop2xRef(&r, &rs); 6426 if (!emitBranchPerform(&b)) { 6427 return false; 6428 } 6429 moveRef(rs, r); 6430 masm.bind(&done); 6431 freeRef(rs); 6432 pushRef(r); 6433 break; 6434 } 6435 default: { 6436 MOZ_CRASH("select type"); 6437 } 6438 } 6439 6440 return true; 6441 } 6442 6443 void BaseCompiler::emitCompareI32(Assembler::Condition compareOp, 6444 ValType compareType) { 6445 MOZ_ASSERT(compareType == ValType::I32); 6446 6447 if (sniffConditionalControlCmp(compareOp, compareType)) { 6448 return; 6449 } 6450 6451 int32_t c; 6452 if (popConst(&c)) { 6453 RegI32 r = popI32(); 6454 masm.cmp32Set(compareOp, r, Imm32(c), r); 6455 pushI32(r); 6456 } else { 6457 RegI32 r, rs; 6458 pop2xI32(&r, &rs); 6459 masm.cmp32Set(compareOp, r, rs, r); 6460 freeI32(rs); 6461 pushI32(r); 6462 } 6463 } 6464 6465 void BaseCompiler::emitCompareI64(Assembler::Condition compareOp, 6466 ValType compareType) { 6467 MOZ_ASSERT(compareType == ValType::I64); 6468 6469 if (sniffConditionalControlCmp(compareOp, compareType)) { 6470 return; 6471 } 6472 6473 RegI64 rs0, rs1; 6474 pop2xI64(&rs0, &rs1); 6475 RegI32 rd(fromI64(rs0)); 6476 cmp64Set(compareOp, rs0, rs1, rd); 6477 freeI64(rs1); 6478 freeI64Except(rs0, rd); 6479 pushI32(rd); 6480 } 6481 6482 void BaseCompiler::emitCompareF32(Assembler::DoubleCondition compareOp, 6483 ValType compareType) { 6484 MOZ_ASSERT(compareType == ValType::F32); 6485 6486 if (sniffConditionalControlCmp(compareOp, compareType)) { 6487 return; 6488 } 6489 6490 Label across; 6491 RegF32 rs0, rs1; 6492 pop2xF32(&rs0, &rs1); 6493 RegI32 rd = needI32(); 6494 moveImm32(1, rd); 6495 masm.branchFloat(compareOp, rs0, rs1, &across); 6496 moveImm32(0, rd); 6497 masm.bind(&across); 6498 freeF32(rs0); 6499 freeF32(rs1); 6500 pushI32(rd); 6501 } 6502 6503 void BaseCompiler::emitCompareF64(Assembler::DoubleCondition compareOp, 6504 ValType compareType) { 6505 MOZ_ASSERT(compareType == ValType::F64); 6506 6507 if (sniffConditionalControlCmp(compareOp, compareType)) { 6508 return; 6509 } 6510 6511 Label across; 6512 RegF64 rs0, rs1; 6513 pop2xF64(&rs0, &rs1); 6514 RegI32 rd = needI32(); 6515 moveImm32(1, rd); 6516 masm.branchDouble(compareOp, rs0, rs1, &across); 6517 moveImm32(0, rd); 6518 masm.bind(&across); 6519 freeF64(rs0); 6520 freeF64(rs1); 6521 pushI32(rd); 6522 } 6523 6524 void BaseCompiler::emitCompareRef(Assembler::Condition compareOp, 6525 ValType compareType) { 6526 MOZ_ASSERT(!sniffConditionalControlCmp(compareOp, compareType)); 6527 6528 RegRef rs1, rs2; 6529 pop2xRef(&rs1, &rs2); 6530 RegI32 rd = needI32(); 6531 masm.cmpPtrSet(compareOp, rs1, rs2, rd); 6532 freeRef(rs1); 6533 freeRef(rs2); 6534 pushI32(rd); 6535 } 6536 6537 bool BaseCompiler::emitInstanceCall(const SymbolicAddressSignature& builtin) { 6538 // See declaration (WasmBCClass.h) for info on the relationship between the 6539 // compiler's value stack and the argument order for the to-be-called 6540 // function. 6541 const MIRType* argTypes = builtin.argTypes; 6542 MOZ_ASSERT(argTypes[0] == MIRType::Pointer); 6543 6544 sync(); 6545 6546 uint32_t numNonInstanceArgs = builtin.numArgs - 1 /* instance */; 6547 size_t stackSpace = stackConsumed(numNonInstanceArgs); 6548 6549 FunctionCall baselineCall(ABIForBuiltin(builtin.identity), 6550 RestoreState::PinnedRegs); 6551 beginCall(baselineCall); 6552 6553 ABIArg instanceArg = reservePointerArgument(&baselineCall); 6554 6555 startCallArgs(StackArgAreaSizeUnaligned(builtin, baselineCall.abiKind), 6556 &baselineCall); 6557 for (uint32_t i = 1; i < builtin.numArgs; i++) { 6558 ValType t; 6559 switch (argTypes[i]) { 6560 case MIRType::Int32: 6561 t = ValType::I32; 6562 break; 6563 case MIRType::Int64: 6564 t = ValType::I64; 6565 break; 6566 case MIRType::Float32: 6567 t = ValType::F32; 6568 break; 6569 case MIRType::WasmAnyRef: 6570 t = RefType::extern_(); 6571 break; 6572 case MIRType::Pointer: 6573 // Instance function args can now be uninterpreted pointers (eg, for 6574 // the cases PostBarrier and PostBarrierFilter) so we simply treat 6575 // them like the equivalently sized integer. 6576 t = ValType::fromMIRType(TargetWordMIRType()); 6577 break; 6578 default: 6579 MOZ_CRASH("Unexpected type"); 6580 } 6581 passArg(t, peek(numNonInstanceArgs - i), &baselineCall); 6582 } 6583 CodeOffset raOffset = 6584 builtinInstanceMethodCall(builtin, instanceArg, baselineCall); 6585 if (!createStackMap("emitInstanceCall", raOffset)) { 6586 return false; 6587 } 6588 6589 endCall(baselineCall, stackSpace); 6590 6591 popValueStackBy(numNonInstanceArgs); 6592 6593 // Note, many clients of emitInstanceCall currently assume that pushing the 6594 // result here does not destroy ReturnReg. 6595 // 6596 // Furthermore, clients assume that if builtin.retType != MIRType::None, the 6597 // callee will have returned a result and left it in ReturnReg for us to 6598 // find, and that that register will not be destroyed here (or above). 6599 6600 // For the return type only, MIRType::None is used to indicate that the 6601 // call doesn't return a result, that is, returns a C/C++ "void". 6602 6603 if (builtin.retType != MIRType::None) { 6604 pushBuiltinCallResult(baselineCall, builtin.retType); 6605 } 6606 return true; 6607 } 6608 6609 ////////////////////////////////////////////////////////////////////////////// 6610 // 6611 // Reference types. 6612 6613 bool BaseCompiler::emitRefFunc() { 6614 return emitInstanceCallOp<uint32_t>(SASigRefFunc, 6615 [this](uint32_t* funcIndex) -> bool { 6616 return iter_.readRefFunc(funcIndex); 6617 }); 6618 } 6619 6620 bool BaseCompiler::emitRefNull() { 6621 RefType type; 6622 if (!iter_.readRefNull(&type)) { 6623 return false; 6624 } 6625 6626 if (deadCode_) { 6627 return true; 6628 } 6629 6630 pushRef(AnyRef::NullRefValue); 6631 return true; 6632 } 6633 6634 bool BaseCompiler::emitRefIsNull() { 6635 Nothing nothing; 6636 if (!iter_.readRefIsNull(¬hing)) { 6637 return false; 6638 } 6639 6640 if (deadCode_) { 6641 return true; 6642 } 6643 6644 RegRef r = popRef(); 6645 RegI32 rd = narrowRef(r); 6646 6647 masm.cmpPtrSet(Assembler::Equal, r, ImmWord(AnyRef::NullRefValue), rd); 6648 pushI32(rd); 6649 return true; 6650 } 6651 6652 bool BaseCompiler::emitRefAsNonNull() { 6653 Nothing nothing; 6654 if (!iter_.readRefAsNonNull(¬hing)) { 6655 return false; 6656 } 6657 6658 if (deadCode_) { 6659 return true; 6660 } 6661 6662 RegRef rp = popRef(); 6663 Label ok; 6664 masm.branchWasmAnyRefIsNull(false, rp, &ok); 6665 trap(Trap::NullPointerDereference); 6666 masm.bind(&ok); 6667 pushRef(rp); 6668 6669 return true; 6670 } 6671 6672 ////////////////////////////////////////////////////////////////////////////// 6673 // 6674 // Atomic operations. 6675 6676 bool BaseCompiler::emitAtomicCmpXchg(ValType type, Scalar::Type viewType) { 6677 LinearMemoryAddress<Nothing> addr; 6678 Nothing unused{}; 6679 if (!iter_.readAtomicCmpXchg(&addr, type, Scalar::byteSize(viewType), &unused, 6680 &unused)) { 6681 return false; 6682 } 6683 if (deadCode_) { 6684 return true; 6685 } 6686 MemoryAccessDesc access(addr.memoryIndex, viewType, addr.align, addr.offset, 6687 trapSiteDesc(), hugeMemoryEnabled(addr.memoryIndex), 6688 Synchronization::Full()); 6689 atomicCmpXchg(&access, type); 6690 return true; 6691 } 6692 6693 bool BaseCompiler::emitAtomicLoad(ValType type, Scalar::Type viewType) { 6694 LinearMemoryAddress<Nothing> addr; 6695 if (!iter_.readAtomicLoad(&addr, type, Scalar::byteSize(viewType))) { 6696 return false; 6697 } 6698 if (deadCode_) { 6699 return true; 6700 } 6701 MemoryAccessDesc access(addr.memoryIndex, viewType, addr.align, addr.offset, 6702 trapSiteDesc(), hugeMemoryEnabled(addr.memoryIndex), 6703 Synchronization::Load()); 6704 atomicLoad(&access, type); 6705 return true; 6706 } 6707 6708 bool BaseCompiler::emitAtomicRMW(ValType type, Scalar::Type viewType, 6709 AtomicOp op) { 6710 LinearMemoryAddress<Nothing> addr; 6711 Nothing unused_value; 6712 if (!iter_.readAtomicRMW(&addr, type, Scalar::byteSize(viewType), 6713 &unused_value)) { 6714 return false; 6715 } 6716 if (deadCode_) { 6717 return true; 6718 } 6719 MemoryAccessDesc access(addr.memoryIndex, viewType, addr.align, addr.offset, 6720 trapSiteDesc(), hugeMemoryEnabled(addr.memoryIndex), 6721 Synchronization::Full()); 6722 atomicRMW(&access, type, op); 6723 return true; 6724 } 6725 6726 bool BaseCompiler::emitAtomicStore(ValType type, Scalar::Type viewType) { 6727 LinearMemoryAddress<Nothing> addr; 6728 Nothing unused_value; 6729 if (!iter_.readAtomicStore(&addr, type, Scalar::byteSize(viewType), 6730 &unused_value)) { 6731 return false; 6732 } 6733 if (deadCode_) { 6734 return true; 6735 } 6736 MemoryAccessDesc access(addr.memoryIndex, viewType, addr.align, addr.offset, 6737 trapSiteDesc(), hugeMemoryEnabled(addr.memoryIndex), 6738 Synchronization::Store()); 6739 atomicStore(&access, type); 6740 return true; 6741 } 6742 6743 bool BaseCompiler::emitAtomicXchg(ValType type, Scalar::Type viewType) { 6744 LinearMemoryAddress<Nothing> addr; 6745 Nothing unused_value; 6746 if (!iter_.readAtomicRMW(&addr, type, Scalar::byteSize(viewType), 6747 &unused_value)) { 6748 return false; 6749 } 6750 if (deadCode_) { 6751 return true; 6752 } 6753 MemoryAccessDesc access(addr.memoryIndex, viewType, addr.align, addr.offset, 6754 trapSiteDesc(), hugeMemoryEnabled(addr.memoryIndex), 6755 Synchronization::Full()); 6756 atomicXchg(&access, type); 6757 return true; 6758 } 6759 6760 bool BaseCompiler::emitWait(ValType type, uint32_t byteSize) { 6761 Nothing nothing; 6762 LinearMemoryAddress<Nothing> addr; 6763 if (!iter_.readWait(&addr, type, byteSize, ¬hing, ¬hing)) { 6764 return false; 6765 } 6766 if (deadCode_) { 6767 return true; 6768 } 6769 MemoryAccessDesc access( 6770 addr.memoryIndex, 6771 type.kind() == ValType::I32 ? Scalar::Int32 : Scalar::Int64, addr.align, 6772 addr.offset, trapSiteDesc(), hugeMemoryEnabled(addr.memoryIndex)); 6773 return atomicWait(type, &access); 6774 } 6775 6776 bool BaseCompiler::emitNotify() { 6777 Nothing nothing; 6778 LinearMemoryAddress<Nothing> addr; 6779 if (!iter_.readNotify(&addr, ¬hing)) { 6780 return false; 6781 } 6782 if (deadCode_) { 6783 return true; 6784 } 6785 MemoryAccessDesc access(addr.memoryIndex, Scalar::Int32, addr.align, 6786 addr.offset, trapSiteDesc(), 6787 hugeMemoryEnabled(addr.memoryIndex)); 6788 return atomicNotify(&access); 6789 } 6790 6791 bool BaseCompiler::emitFence() { 6792 if (!iter_.readFence()) { 6793 return false; 6794 } 6795 if (deadCode_) { 6796 return true; 6797 } 6798 masm.memoryBarrier(MemoryBarrier::Full()); 6799 return true; 6800 } 6801 6802 ////////////////////////////////////////////////////////////////////////////// 6803 // 6804 // Bulk memory operations. 6805 6806 bool BaseCompiler::emitMemoryGrow() { 6807 uint32_t memoryIndex; 6808 Nothing nothing; 6809 if (!iter_.readMemoryGrow(&memoryIndex, ¬hing)) { 6810 return false; 6811 } 6812 if (deadCode_) { 6813 return true; 6814 } 6815 6816 pushI32(memoryIndex); 6817 return emitInstanceCall(isMem32(memoryIndex) ? SASigMemoryGrowM32 6818 : SASigMemoryGrowM64); 6819 } 6820 6821 bool BaseCompiler::emitMemorySize() { 6822 uint32_t memoryIndex; 6823 if (!iter_.readMemorySize(&memoryIndex)) { 6824 return false; 6825 } 6826 if (deadCode_) { 6827 return true; 6828 } 6829 6830 pushI32(memoryIndex); 6831 return emitInstanceCall(isMem32(memoryIndex) ? SASigMemorySizeM32 6832 : SASigMemorySizeM64); 6833 } 6834 6835 bool BaseCompiler::emitMemCopy() { 6836 uint32_t dstMemIndex = 0; 6837 uint32_t srcMemIndex = 0; 6838 Nothing nothing; 6839 if (!iter_.readMemOrTableCopy(true, &dstMemIndex, ¬hing, &srcMemIndex, 6840 ¬hing, ¬hing)) { 6841 return false; 6842 } 6843 if (deadCode_) { 6844 return true; 6845 } 6846 6847 if (dstMemIndex == 0 && srcMemIndex == 0 && isMem32(dstMemIndex)) { 6848 int32_t signedLength; 6849 if (peekConst(&signedLength) && signedLength != 0 && 6850 uint32_t(signedLength) <= MaxInlineMemoryCopyLength) { 6851 memCopyInlineM32(); 6852 return true; 6853 } 6854 } 6855 6856 return memCopyCall(dstMemIndex, srcMemIndex); 6857 } 6858 6859 bool BaseCompiler::memCopyCall(uint32_t dstMemIndex, uint32_t srcMemIndex) { 6860 // Common and optimized path for when the src/dest memories are the same 6861 if (dstMemIndex == srcMemIndex) { 6862 bool mem32 = isMem32(dstMemIndex); 6863 pushHeapBase(dstMemIndex); 6864 return emitInstanceCall( 6865 usesSharedMemory(dstMemIndex) 6866 ? (mem32 ? SASigMemCopySharedM32 : SASigMemCopySharedM64) 6867 : (mem32 ? SASigMemCopyM32 : SASigMemCopyM64)); 6868 } 6869 6870 // Do the general-purpose fallback for copying between any combination of 6871 // memories. This works by moving everything to the lowest-common denominator. 6872 // i32 addresses are promoted to i64, and non-shared memories are treated as 6873 // shared. 6874 AddressType dstAddressType = codeMeta_.memories[dstMemIndex].addressType(); 6875 AddressType srcAddressType = codeMeta_.memories[srcMemIndex].addressType(); 6876 AddressType lenAddressType = MinAddressType(dstAddressType, srcAddressType); 6877 6878 // Pop the operands off of the stack and widen them 6879 RegI64 len = popAddressToInt64(lenAddressType); 6880 #ifdef JS_CODEGEN_X86 6881 { 6882 // Stash the length value to prevent running out of registers 6883 ScratchPtr scratch(*this); 6884 stashI64(scratch, len); 6885 freeI64(len); 6886 } 6887 #endif 6888 RegI64 srcIndex = popAddressToInt64(srcAddressType); 6889 RegI64 dstIndex = popAddressToInt64(dstAddressType); 6890 6891 pushI64(dstIndex); 6892 pushI64(srcIndex); 6893 #ifdef JS_CODEGEN_X86 6894 { 6895 // Unstash the length value and push it back on the stack 6896 ScratchPtr scratch(*this); 6897 len = needI64(); 6898 unstashI64(scratch, len); 6899 } 6900 #endif 6901 pushI64(len); 6902 pushI32(dstMemIndex); 6903 pushI32(srcMemIndex); 6904 return emitInstanceCall(SASigMemCopyAny); 6905 } 6906 6907 bool BaseCompiler::emitMemFill() { 6908 uint32_t memoryIndex; 6909 Nothing nothing; 6910 if (!iter_.readMemFill(&memoryIndex, ¬hing, ¬hing, ¬hing)) { 6911 return false; 6912 } 6913 if (deadCode_) { 6914 return true; 6915 } 6916 6917 if (memoryIndex == 0 && isMem32(memoryIndex)) { 6918 int32_t signedLength; 6919 int32_t signedValue; 6920 if (peek2xConst(&signedLength, &signedValue) && signedLength != 0 && 6921 uint32_t(signedLength) <= MaxInlineMemoryFillLength) { 6922 memFillInlineM32(); 6923 return true; 6924 } 6925 } 6926 return memFillCall(memoryIndex); 6927 } 6928 6929 bool BaseCompiler::memFillCall(uint32_t memoryIndex) { 6930 pushHeapBase(memoryIndex); 6931 return emitInstanceCall( 6932 usesSharedMemory(memoryIndex) 6933 ? (isMem32(memoryIndex) ? SASigMemFillSharedM32 6934 : SASigMemFillSharedM64) 6935 : (isMem32(memoryIndex) ? SASigMemFillM32 : SASigMemFillM64)); 6936 } 6937 6938 bool BaseCompiler::emitMemInit() { 6939 uint32_t segIndex; 6940 uint32_t memIndex; 6941 Nothing nothing; 6942 if (!iter_.readMemOrTableInit(/*isMem*/ true, &segIndex, &memIndex, ¬hing, 6943 ¬hing, ¬hing)) { 6944 return false; 6945 } 6946 if (deadCode_) { 6947 return true; 6948 } 6949 6950 pushI32(segIndex); 6951 pushI32(memIndex); 6952 return emitInstanceCall(isMem32(memIndex) ? SASigMemInitM32 6953 : SASigMemInitM64); 6954 } 6955 6956 ////////////////////////////////////////////////////////////////////////////// 6957 // 6958 // Bulk table operations. 6959 6960 bool BaseCompiler::emitTableCopy() { 6961 uint32_t dstTable = 0; 6962 uint32_t srcTable = 0; 6963 Nothing nothing; 6964 if (!iter_.readMemOrTableCopy(false, &dstTable, ¬hing, &srcTable, ¬hing, 6965 ¬hing)) { 6966 return false; 6967 } 6968 6969 if (deadCode_) { 6970 return true; 6971 } 6972 6973 AddressType dstAddressType = codeMeta_.tables[dstTable].addressType(); 6974 AddressType srcAddressType = codeMeta_.tables[srcTable].addressType(); 6975 AddressType lenAddressType = MinAddressType(dstAddressType, srcAddressType); 6976 6977 // Instance::tableCopy(dstOffset:u32, srcOffset:u32, len:u32, dstTable:u32, 6978 // srcTable:u32) 6979 RegI32 len = popTableAddressToClampedInt32(lenAddressType); 6980 RegI32 src = popTableAddressToClampedInt32(srcAddressType); 6981 replaceTableAddressWithClampedInt32(dstAddressType); 6982 pushI32(src); 6983 pushI32(len); 6984 pushI32(dstTable); 6985 pushI32(srcTable); 6986 return emitInstanceCall(SASigTableCopy); 6987 } 6988 6989 bool BaseCompiler::emitTableInit() { 6990 uint32_t segIndex = 0; 6991 uint32_t dstTable = 0; 6992 Nothing nothing; 6993 if (!iter_.readMemOrTableInit(false, &segIndex, &dstTable, ¬hing, ¬hing, 6994 ¬hing)) { 6995 return false; 6996 } 6997 6998 if (deadCode_) { 6999 return true; 7000 } 7001 7002 // Instance::tableInit(dst:u32, src:u32, len:u32, seg:u32, table:u32) 7003 RegI32 len = popI32(); 7004 RegI32 src = popI32(); 7005 replaceTableAddressWithClampedInt32(codeMeta_.tables[dstTable].addressType()); 7006 pushI32(src); 7007 pushI32(len); 7008 pushI32(segIndex); 7009 pushI32(dstTable); 7010 return emitInstanceCall(SASigTableInit); 7011 } 7012 7013 bool BaseCompiler::emitTableFill() { 7014 uint32_t tableIndex; 7015 Nothing nothing; 7016 if (!iter_.readTableFill(&tableIndex, ¬hing, ¬hing, ¬hing)) { 7017 return false; 7018 } 7019 if (deadCode_) { 7020 return true; 7021 } 7022 7023 AddressType addressType = codeMeta_.tables[tableIndex].addressType(); 7024 7025 // Instance::tableFill(start:u32, val:ref, len:u32, table:u32) -> void 7026 RegI32 len = popTableAddressToClampedInt32(addressType); 7027 AnyReg val = popAny(); 7028 replaceTableAddressWithClampedInt32(addressType); 7029 pushAny(val); 7030 pushI32(len); 7031 pushI32(tableIndex); 7032 return emitInstanceCall(SASigTableFill); 7033 } 7034 7035 bool BaseCompiler::emitMemDiscard() { 7036 uint32_t memoryIndex; 7037 Nothing nothing; 7038 if (!iter_.readMemDiscard(&memoryIndex, ¬hing, ¬hing)) { 7039 return false; 7040 } 7041 if (deadCode_) { 7042 return true; 7043 } 7044 7045 pushHeapBase(memoryIndex); 7046 return emitInstanceCall( 7047 usesSharedMemory(memoryIndex) 7048 ? (isMem32(memoryIndex) ? SASigMemDiscardSharedM32 7049 : SASigMemDiscardSharedM64) 7050 : (isMem32(memoryIndex) ? SASigMemDiscardM32 : SASigMemDiscardM64)); 7051 } 7052 7053 bool BaseCompiler::emitTableGet() { 7054 uint32_t tableIndex; 7055 Nothing nothing; 7056 if (!iter_.readTableGet(&tableIndex, ¬hing)) { 7057 return false; 7058 } 7059 if (deadCode_) { 7060 return true; 7061 } 7062 7063 replaceTableAddressWithClampedInt32( 7064 codeMeta_.tables[tableIndex].addressType()); 7065 if (codeMeta_.tables[tableIndex].elemType.tableRepr() == TableRepr::Ref) { 7066 return emitTableGetAnyRef(tableIndex); 7067 } 7068 pushI32(tableIndex); 7069 // Instance::tableGet(index:u32, table:u32) -> AnyRef 7070 return emitInstanceCall(SASigTableGet); 7071 } 7072 7073 bool BaseCompiler::emitTableGrow() { 7074 uint32_t tableIndex; 7075 Nothing nothing; 7076 if (!iter_.readTableGrow(&tableIndex, ¬hing, ¬hing)) { 7077 return false; 7078 } 7079 if (deadCode_) { 7080 return true; 7081 } 7082 7083 AddressType addressType = codeMeta_.tables[tableIndex].addressType(); 7084 7085 // Instance::tableGrow(initValue:anyref, delta:u32, table:u32) -> u32 7086 replaceTableAddressWithClampedInt32(addressType); 7087 pushI32(tableIndex); 7088 if (!emitInstanceCall(SASigTableGrow)) { 7089 return false; 7090 } 7091 7092 if (addressType == AddressType::I64) { 7093 RegI64 r; 7094 popI32ForSignExtendI64(&r); 7095 masm.move32To64SignExtend(lowPart(r), r); 7096 pushI64(r); 7097 } 7098 7099 return true; 7100 } 7101 7102 bool BaseCompiler::emitTableSet() { 7103 uint32_t tableIndex; 7104 Nothing nothing; 7105 if (!iter_.readTableSet(&tableIndex, ¬hing, ¬hing)) { 7106 return false; 7107 } 7108 if (deadCode_) { 7109 return true; 7110 } 7111 if (codeMeta_.tables[tableIndex].addressType() == AddressType::I64) { 7112 AnyReg value = popAny(); 7113 replaceTableAddressWithClampedInt32(AddressType::I64); 7114 pushAny(value); 7115 } 7116 if (codeMeta_.tables[tableIndex].elemType.tableRepr() == TableRepr::Ref) { 7117 return emitTableSetAnyRef(tableIndex); 7118 } 7119 pushI32(tableIndex); 7120 // Instance::tableSet(address:u32, value:ref, table:u32) -> void 7121 return emitInstanceCall(SASigTableSet); 7122 } 7123 7124 bool BaseCompiler::emitTableSize() { 7125 uint32_t tableIndex; 7126 if (!iter_.readTableSize(&tableIndex)) { 7127 return false; 7128 } 7129 if (deadCode_) { 7130 return true; 7131 } 7132 7133 RegPtr instance = needPtr(); 7134 RegI32 length = needI32(); 7135 7136 fr.loadInstancePtr(instance); 7137 loadTableLength(tableIndex, instance, length); 7138 7139 if (codeMeta_.tables[tableIndex].addressType() == AddressType::I64) { 7140 pushU32AsI64(length); 7141 } else { 7142 pushI32(length); 7143 } 7144 freePtr(instance); 7145 return true; 7146 } 7147 7148 void BaseCompiler::emitTableBoundsCheck(uint32_t tableIndex, RegI32 address, 7149 RegPtr instance) { 7150 Label ok; 7151 masm.wasmBoundsCheck32( 7152 Assembler::Condition::Below, address, 7153 addressOfTableField(tableIndex, offsetof(TableInstanceData, length), 7154 instance), 7155 &ok); 7156 trap(wasm::Trap::OutOfBounds); 7157 masm.bind(&ok); 7158 } 7159 7160 bool BaseCompiler::emitTableGetAnyRef(uint32_t tableIndex) { 7161 RegPtr instance = needPtr(); 7162 RegPtr elements = needPtr(); 7163 RegI32 address = popI32(); 7164 7165 fr.loadInstancePtr(instance); 7166 emitTableBoundsCheck(tableIndex, address, instance); 7167 loadTableElements(tableIndex, instance, elements); 7168 masm.loadPtr(BaseIndex(elements, address, ScalePointer), elements); 7169 7170 pushRef(RegRef(elements)); 7171 freeI32(address); 7172 freePtr(instance); 7173 7174 return true; 7175 } 7176 7177 bool BaseCompiler::emitTableSetAnyRef(uint32_t tableIndex) { 7178 // Create temporaries for valueAddr that is not in the prebarrier register 7179 // and can be consumed by the barrier operation 7180 RegPtr valueAddr = RegPtr(PreBarrierReg); 7181 needPtr(valueAddr); 7182 7183 RegPtr instance = needPtr(); 7184 RegPtr elements = needPtr(); 7185 RegRef value = popRef(); 7186 RegI32 address = popI32(); 7187 7188 // x86 is one register too short for this operation, shuffle `value` back 7189 // onto the stack until it is needed. 7190 #ifdef JS_CODEGEN_X86 7191 pushRef(value); 7192 #endif 7193 7194 fr.loadInstancePtr(instance); 7195 emitTableBoundsCheck(tableIndex, address, instance); 7196 loadTableElements(tableIndex, instance, elements); 7197 masm.computeEffectiveAddress(BaseIndex(elements, address, ScalePointer), 7198 valueAddr); 7199 7200 freeI32(address); 7201 freePtr(elements); 7202 freePtr(instance); 7203 7204 #ifdef JS_CODEGEN_X86 7205 value = popRef(); 7206 #endif 7207 7208 if (!emitBarrieredStore(Nothing(), valueAddr, value, PreBarrierKind::Normal, 7209 PostBarrierKind::Precise)) { 7210 return false; 7211 } 7212 freeRef(value); 7213 return true; 7214 } 7215 7216 ////////////////////////////////////////////////////////////////////////////// 7217 // 7218 // Data and element segment management. 7219 7220 bool BaseCompiler::emitDataOrElemDrop(bool isData) { 7221 return emitInstanceCallOp<uint32_t>( 7222 isData ? SASigDataDrop : SASigElemDrop, [&](uint32_t* segIndex) -> bool { 7223 return iter_.readDataOrElemDrop(isData, segIndex); 7224 }); 7225 } 7226 7227 ////////////////////////////////////////////////////////////////////////////// 7228 // 7229 // General object support. 7230 7231 void BaseCompiler::emitPreBarrier(RegPtr valueAddr) { 7232 Label skipBarrier; 7233 ScratchPtr scratch(*this); 7234 7235 #ifdef RABALDR_PIN_INSTANCE 7236 Register instance(InstanceReg); 7237 #else 7238 Register instance(scratch); 7239 fr.loadInstancePtr(instance); 7240 #endif 7241 7242 EmitWasmPreBarrierGuard(masm, instance, scratch, Address(valueAddr, 0), 7243 &skipBarrier, mozilla::Nothing()); 7244 7245 #ifndef RABALDR_PIN_INSTANCE 7246 fr.loadInstancePtr(instance); 7247 #endif 7248 #ifdef JS_CODEGEN_ARM64 7249 // The prebarrier stub assumes the PseudoStackPointer is set up. It is OK 7250 // to just move the sp to x28 here because x28 is not being used by the 7251 // baseline compiler and need not be saved or restored. 7252 MOZ_ASSERT(!GeneralRegisterSet::All().hasRegisterIndex(x28.asUnsized())); 7253 masm.Mov(x28, sp); 7254 #endif 7255 // The prebarrier call preserves all volatile registers 7256 EmitWasmPreBarrierCallImmediate(masm, instance, scratch, valueAddr, 7257 /*valueOffset=*/0); 7258 7259 masm.bind(&skipBarrier); 7260 } 7261 7262 bool BaseCompiler::emitPostBarrierWholeCell(RegRef object, RegRef value, 7263 RegPtr temp) { 7264 // We must force a sync before the guard so that locals are in a consistent 7265 // location for whether or not the post-barrier call is taken. 7266 sync(); 7267 7268 // Emit guards to skip the post-barrier call if it is not needed. 7269 Label skipBarrier; 7270 EmitWasmPostBarrierGuard(masm, mozilla::Some(object), temp, value, 7271 &skipBarrier); 7272 7273 #ifdef RABALDR_PIN_INSTANCE 7274 Register instance(InstanceReg); 7275 #else 7276 Register instance(temp); 7277 fr.loadInstancePtr(instance); 7278 #endif 7279 CheckWholeCellLastElementCache(masm, instance, object, temp, &skipBarrier); 7280 7281 movePtr(RegPtr(object), temp); 7282 7283 // Push `object` and `value` to preserve them across the call. 7284 pushRef(object); 7285 pushRef(value); 7286 7287 pushPtr(temp); 7288 if (!emitInstanceCall(SASigPostBarrierWholeCell)) { 7289 return false; 7290 } 7291 7292 // Restore `object` and `value`. 7293 popRef(value); 7294 popRef(object); 7295 7296 masm.bind(&skipBarrier); 7297 return true; 7298 } 7299 7300 bool BaseCompiler::emitPostBarrierEdgeImprecise(const Maybe<RegRef>& object, 7301 RegPtr valueAddr, 7302 RegRef value) { 7303 // We must force a sync before the guard so that locals are in a consistent 7304 // location for whether or not the post-barrier call is taken. 7305 sync(); 7306 7307 // Emit a guard to skip the post-barrier call if it is not needed. 7308 Label skipBarrier; 7309 RegPtr otherScratch = needPtr(); 7310 EmitWasmPostBarrierGuard(masm, object, otherScratch, value, &skipBarrier); 7311 freePtr(otherScratch); 7312 7313 // Push `object` and `value` to preserve them across the call. 7314 if (object) { 7315 pushRef(*object); 7316 } 7317 pushRef(value); 7318 7319 // The `valueAddr` is a raw pointer to the cell within some GC object or 7320 // instance area, and we are careful so that the GC will not run while the 7321 // post-barrier call is active, so push a uintptr_t value. 7322 pushPtr(valueAddr); 7323 if (!emitInstanceCall(SASigPostBarrierEdge)) { 7324 return false; 7325 } 7326 7327 // Restore `object` and `value`. 7328 popRef(value); 7329 if (object) { 7330 popRef(*object); 7331 } 7332 7333 masm.bind(&skipBarrier); 7334 return true; 7335 } 7336 7337 bool BaseCompiler::emitPostBarrierEdgePrecise(const Maybe<RegRef>& object, 7338 RegPtr valueAddr, 7339 RegRef prevValue, RegRef value) { 7340 // Currently this is only called to write into wasm tables. 7341 // 7342 // If this changes and we use this method to write into objects which might be 7343 // in the nursery then we need to check for that here and skip the barrier (we 7344 // only need to record pointers from the tenured heap into the nursery). 7345 MOZ_ASSERT(object.isNothing()); 7346 7347 // Push `object` and `value` to preserve them across the call. 7348 if (object) { 7349 pushRef(*object); 7350 } 7351 pushRef(value); 7352 7353 // Push the arguments and call the precise post-barrier 7354 pushPtr(valueAddr); 7355 pushRef(prevValue); 7356 if (!emitInstanceCall(SASigPostBarrierEdgePrecise)) { 7357 return false; 7358 } 7359 7360 // Restore `object` and `value`. 7361 popRef(value); 7362 if (object) { 7363 popRef(*object); 7364 } 7365 7366 return true; 7367 } 7368 7369 bool BaseCompiler::emitBarrieredStore(const Maybe<RegRef>& object, 7370 RegPtr valueAddr, RegRef value, 7371 PreBarrierKind preBarrierKind, 7372 PostBarrierKind postBarrierKind) { 7373 // The pre-barrier preserves all allocated registers. 7374 if (preBarrierKind == PreBarrierKind::Normal) { 7375 emitPreBarrier(valueAddr); 7376 } 7377 7378 // The precise post-barrier requires the previous value stored in the field, 7379 // in order to know if the previous store buffer entry needs to be removed. 7380 RegRef prevValue; 7381 if (postBarrierKind == PostBarrierKind::Precise) { 7382 prevValue = needRef(); 7383 masm.loadPtr(Address(valueAddr, 0), prevValue); 7384 } 7385 7386 // Store the value 7387 masm.storePtr(value, Address(valueAddr, 0)); 7388 7389 // The post-barrier preserves object and value. 7390 switch (postBarrierKind) { 7391 case PostBarrierKind::None: 7392 freePtr(valueAddr); 7393 return true; 7394 case PostBarrierKind::Imprecise: 7395 return emitPostBarrierEdgeImprecise(object, valueAddr, value); 7396 case PostBarrierKind::Precise: 7397 return emitPostBarrierEdgePrecise(object, valueAddr, prevValue, value); 7398 case PostBarrierKind::WholeCell: 7399 // valueAddr is reused as a temp register. 7400 return emitPostBarrierWholeCell(object.value(), value, valueAddr); 7401 default: 7402 MOZ_CRASH("unknown barrier kind"); 7403 } 7404 } 7405 7406 void BaseCompiler::emitBarrieredClear(RegPtr valueAddr) { 7407 // The pre-barrier preserves all allocated registers. 7408 emitPreBarrier(valueAddr); 7409 7410 // Store null 7411 masm.storePtr(ImmWord(AnyRef::NullRefValue), Address(valueAddr, 0)); 7412 7413 // No post-barrier is needed, as null does not require a store buffer entry 7414 } 7415 7416 ////////////////////////////////////////////////////////////////////////////// 7417 // 7418 // GC proposal. 7419 7420 RegPtr BaseCompiler::loadAllocSiteInstanceData(uint32_t allocSiteIndex) { 7421 RegPtr rp = needPtr(); 7422 RegPtr instance; 7423 #ifndef RABALDR_PIN_INSTANCE 7424 instance = rp; 7425 fr.loadInstancePtr(instance); 7426 #else 7427 // We can use the pinned instance register. 7428 instance = RegPtr(InstanceReg); 7429 #endif 7430 masm.loadPtr(Address(instance, Instance::offsetOfAllocSites()), rp); 7431 7432 ScratchI32 scratch(*this); 7433 CodeOffset offset = masm.move32WithPatch(scratch); 7434 masm.allocSitesPatches()[allocSiteIndex].setPatchOffset(offset.offset()); 7435 masm.addPtr(scratch, rp); 7436 return rp; 7437 } 7438 7439 bool BaseCompiler::readAllocSiteIndex(uint32_t* index) { 7440 *index = masm.allocSitesPatches().length(); 7441 masm.append(wasm::AllocSitePatch()); 7442 return !masm.oom(); 7443 } 7444 7445 RegPtr BaseCompiler::loadSuperTypeVector(uint32_t typeIndex) { 7446 RegPtr rp = needPtr(); 7447 RegPtr instance; 7448 #ifndef RABALDR_PIN_INSTANCE 7449 // We need to load the instance register, but can use the destination 7450 // register as a temporary. 7451 instance = rp; 7452 fr.loadInstancePtr(rp); 7453 #else 7454 // We can use the pinned instance register. 7455 instance = RegPtr(InstanceReg); 7456 #endif 7457 masm.loadPtr( 7458 Address(instance, Instance::offsetInData( 7459 codeMeta_.offsetOfSuperTypeVector(typeIndex))), 7460 rp); 7461 return rp; 7462 } 7463 7464 /* static */ 7465 void BaseCompiler::SignalNullCheck::emitNullCheck(BaseCompiler* bc, RegRef rp) { 7466 Label ok; 7467 MacroAssembler& masm = bc->masm; 7468 masm.branchTestPtr(Assembler::NonZero, rp, rp, &ok); 7469 bc->trap(Trap::NullPointerDereference); 7470 masm.bind(&ok); 7471 } 7472 7473 /* static */ 7474 void BaseCompiler::SignalNullCheck::emitTrapSite(BaseCompiler* bc, 7475 FaultingCodeOffset fco, 7476 TrapMachineInsn tmi) { 7477 MacroAssembler& masm = bc->masm; 7478 masm.append(wasm::Trap::NullPointerDereference, tmi, fco.get(), 7479 bc->trapSiteDesc()); 7480 } 7481 7482 template <typename NullCheckPolicy> 7483 RegPtr BaseCompiler::emitGcArrayGetData(RegRef rp) { 7484 // `rp` points at a WasmArrayObject. Return a reg holding the value of its 7485 // `data_` field. 7486 RegPtr rdata = needPtr(); 7487 FaultingCodeOffset fco = 7488 masm.loadPtr(Address(rp, WasmArrayObject::offsetOfData()), rdata); 7489 NullCheckPolicy::emitTrapSite(this, fco, TrapMachineInsnForLoadWord()); 7490 return rdata; 7491 } 7492 7493 template <typename NullCheckPolicy> 7494 RegI32 BaseCompiler::emitGcArrayGetNumElements(RegRef rp) { 7495 // `rp` points at a WasmArrayObject. Return a reg holding the value of its 7496 // `numElements_` field. 7497 STATIC_ASSERT_WASMARRAYELEMENTS_NUMELEMENTS_IS_U32; 7498 RegI32 numElements = needI32(); 7499 FaultingCodeOffset fco = masm.load32( 7500 Address(rp, WasmArrayObject::offsetOfNumElements()), numElements); 7501 NullCheckPolicy::emitTrapSite(this, fco, TrapMachineInsn::Load32); 7502 return numElements; 7503 } 7504 7505 void BaseCompiler::emitGcArrayBoundsCheck(RegI32 index, RegI32 numElements) { 7506 Label inBounds; 7507 masm.branch32(Assembler::Below, index, numElements, &inBounds); 7508 trap(Trap::OutOfBounds); 7509 masm.bind(&inBounds); 7510 } 7511 7512 template <typename T, typename NullCheckPolicy> 7513 void BaseCompiler::emitGcGet(StorageType type, FieldWideningOp wideningOp, 7514 const T& src) { 7515 switch (type.kind()) { 7516 case StorageType::I8: { 7517 MOZ_ASSERT(wideningOp != FieldWideningOp::None); 7518 RegI32 r = needI32(); 7519 FaultingCodeOffset fco; 7520 if (wideningOp == FieldWideningOp::Unsigned) { 7521 fco = masm.load8ZeroExtend(src, r); 7522 } else { 7523 fco = masm.load8SignExtend(src, r); 7524 } 7525 NullCheckPolicy::emitTrapSite(this, fco, TrapMachineInsn::Load8); 7526 pushI32(r); 7527 break; 7528 } 7529 case StorageType::I16: { 7530 MOZ_ASSERT(wideningOp != FieldWideningOp::None); 7531 RegI32 r = needI32(); 7532 FaultingCodeOffset fco; 7533 if (wideningOp == FieldWideningOp::Unsigned) { 7534 fco = masm.load16ZeroExtend(src, r); 7535 } else { 7536 fco = masm.load16SignExtend(src, r); 7537 } 7538 NullCheckPolicy::emitTrapSite(this, fco, TrapMachineInsn::Load16); 7539 pushI32(r); 7540 break; 7541 } 7542 case StorageType::I32: { 7543 MOZ_ASSERT(wideningOp == FieldWideningOp::None); 7544 RegI32 r = needI32(); 7545 FaultingCodeOffset fco = masm.load32(src, r); 7546 NullCheckPolicy::emitTrapSite(this, fco, TrapMachineInsn::Load32); 7547 pushI32(r); 7548 break; 7549 } 7550 case StorageType::I64: { 7551 MOZ_ASSERT(wideningOp == FieldWideningOp::None); 7552 RegI64 r = needI64(); 7553 #ifdef JS_64BIT 7554 FaultingCodeOffset fco = masm.load64(src, r); 7555 NullCheckPolicy::emitTrapSite(this, fco, TrapMachineInsn::Load64); 7556 #else 7557 FaultingCodeOffsetPair fcop = masm.load64(src, r); 7558 NullCheckPolicy::emitTrapSite(this, fcop.first, TrapMachineInsn::Load32); 7559 NullCheckPolicy::emitTrapSite(this, fcop.second, TrapMachineInsn::Load32); 7560 #endif 7561 pushI64(r); 7562 break; 7563 } 7564 case StorageType::F32: { 7565 MOZ_ASSERT(wideningOp == FieldWideningOp::None); 7566 RegF32 r = needF32(); 7567 FaultingCodeOffset fco = masm.loadFloat32(src, r); 7568 NullCheckPolicy::emitTrapSite(this, fco, TrapMachineInsn::Load32); 7569 pushF32(r); 7570 break; 7571 } 7572 case StorageType::F64: { 7573 MOZ_ASSERT(wideningOp == FieldWideningOp::None); 7574 RegF64 r = needF64(); 7575 FaultingCodeOffset fco = masm.loadDouble(src, r); 7576 NullCheckPolicy::emitTrapSite(this, fco, TrapMachineInsn::Load64); 7577 pushF64(r); 7578 break; 7579 } 7580 #ifdef ENABLE_WASM_SIMD 7581 case StorageType::V128: { 7582 MOZ_ASSERT(wideningOp == FieldWideningOp::None); 7583 RegV128 r = needV128(); 7584 FaultingCodeOffset fco = masm.loadUnalignedSimd128(src, r); 7585 NullCheckPolicy::emitTrapSite(this, fco, TrapMachineInsn::Load128); 7586 pushV128(r); 7587 break; 7588 } 7589 #endif 7590 case StorageType::Ref: { 7591 MOZ_ASSERT(wideningOp == FieldWideningOp::None); 7592 RegRef r = needRef(); 7593 FaultingCodeOffset fco = masm.loadPtr(src, r); 7594 NullCheckPolicy::emitTrapSite(this, fco, TrapMachineInsnForLoadWord()); 7595 pushRef(r); 7596 break; 7597 } 7598 default: { 7599 MOZ_CRASH("Unexpected field type"); 7600 } 7601 } 7602 } 7603 7604 template <typename T, typename NullCheckPolicy> 7605 void BaseCompiler::emitGcSetScalar(const T& dst, StorageType type, 7606 AnyReg value) { 7607 switch (type.kind()) { 7608 case StorageType::I8: { 7609 FaultingCodeOffset fco = masm.store8(value.i32(), dst); 7610 NullCheckPolicy::emitTrapSite(this, fco, TrapMachineInsn::Store8); 7611 break; 7612 } 7613 case StorageType::I16: { 7614 FaultingCodeOffset fco = masm.store16(value.i32(), dst); 7615 NullCheckPolicy::emitTrapSite(this, fco, TrapMachineInsn::Store16); 7616 break; 7617 } 7618 case StorageType::I32: { 7619 FaultingCodeOffset fco = masm.store32(value.i32(), dst); 7620 NullCheckPolicy::emitTrapSite(this, fco, TrapMachineInsn::Store32); 7621 break; 7622 } 7623 case StorageType::I64: { 7624 #ifdef JS_64BIT 7625 FaultingCodeOffset fco = masm.store64(value.i64(), dst); 7626 NullCheckPolicy::emitTrapSite(this, fco, TrapMachineInsn::Store64); 7627 #else 7628 FaultingCodeOffsetPair fcop = masm.store64(value.i64(), dst); 7629 NullCheckPolicy::emitTrapSite(this, fcop.first, TrapMachineInsn::Store32); 7630 NullCheckPolicy::emitTrapSite(this, fcop.second, 7631 TrapMachineInsn::Store32); 7632 #endif 7633 break; 7634 } 7635 case StorageType::F32: { 7636 FaultingCodeOffset fco = masm.storeFloat32(value.f32(), dst); 7637 NullCheckPolicy::emitTrapSite(this, fco, TrapMachineInsn::Store32); 7638 break; 7639 } 7640 case StorageType::F64: { 7641 FaultingCodeOffset fco = masm.storeDouble(value.f64(), dst); 7642 NullCheckPolicy::emitTrapSite(this, fco, TrapMachineInsn::Store64); 7643 break; 7644 } 7645 #ifdef ENABLE_WASM_SIMD 7646 case StorageType::V128: { 7647 FaultingCodeOffset fco = masm.storeUnalignedSimd128(value.v128(), dst); 7648 NullCheckPolicy::emitTrapSite(this, fco, TrapMachineInsn::Store128); 7649 break; 7650 } 7651 #endif 7652 default: { 7653 MOZ_CRASH("Unexpected field type"); 7654 } 7655 } 7656 } 7657 7658 template <typename NullCheckPolicy> 7659 bool BaseCompiler::emitGcStructSet(RegRef object, RegPtr areaBase, 7660 uint32_t areaOffset, StorageType type, 7661 AnyReg value, 7662 PreBarrierKind preBarrierKind) { 7663 // Easy path if the field is a scalar 7664 if (!type.isRefRepr()) { 7665 emitGcSetScalar<Address, NullCheckPolicy>(Address(areaBase, areaOffset), 7666 type, value); 7667 freeAny(value); 7668 return true; 7669 } 7670 7671 // Create temporary for the valueAddr that is not in the prebarrier register 7672 // and can be consumed by the barrier operation 7673 RegPtr valueAddr = RegPtr(PreBarrierReg); 7674 needPtr(valueAddr); 7675 masm.computeEffectiveAddress(Address(areaBase, areaOffset), valueAddr); 7676 7677 NullCheckPolicy::emitNullCheck(this, object); 7678 7679 // emitBarrieredStore preserves object and value 7680 if (!emitBarrieredStore(Some(object), valueAddr, value.ref(), preBarrierKind, 7681 PostBarrierKind::WholeCell)) { 7682 return false; 7683 } 7684 freeRef(value.ref()); 7685 7686 return true; 7687 } 7688 7689 bool BaseCompiler::emitGcArraySet(RegRef object, RegPtr data, RegI32 index, 7690 const ArrayType& arrayType, AnyReg value, 7691 PreBarrierKind preBarrierKind, 7692 PostBarrierKind postBarrierKind) { 7693 // Try to use a base index store instruction if the field type fits in a 7694 // shift immediate. If not we shift the index manually and then unshift 7695 // it after the store. We don't use an extra register for this because we 7696 // don't have any to spare on x86. 7697 uint32_t shift = arrayType.elementType().indexingShift(); 7698 Scale scale; 7699 bool shiftedIndex = false; 7700 if (IsShiftInScaleRange(shift)) { 7701 scale = ShiftToScale(shift); 7702 } else { 7703 masm.lshiftPtr(Imm32(shift), index); 7704 scale = TimesOne; 7705 shiftedIndex = true; 7706 } 7707 auto unshiftIndex = mozilla::MakeScopeExit([&] { 7708 if (shiftedIndex) { 7709 masm.rshiftPtr(Imm32(shift), index); 7710 } 7711 }); 7712 7713 // Easy path if the field is a scalar 7714 if (!arrayType.elementType().isRefRepr()) { 7715 emitGcSetScalar<BaseIndex, NoNullCheck>(BaseIndex(data, index, scale, 0), 7716 arrayType.elementType(), value); 7717 return true; 7718 } 7719 7720 // Create temporaries for valueAddr that is not in the prebarrier register 7721 // and can be consumed by the barrier operation 7722 RegPtr valueAddr = RegPtr(PreBarrierReg); 7723 needPtr(valueAddr); 7724 masm.computeEffectiveAddress(BaseIndex(data, index, scale, 0), valueAddr); 7725 7726 // Save state for after barriered write 7727 pushPtr(data); 7728 pushI32(index); 7729 7730 // emitBarrieredStore preserves object and value 7731 if (!emitBarrieredStore(Some(object), valueAddr, value.ref(), preBarrierKind, 7732 postBarrierKind)) { 7733 return false; 7734 } 7735 7736 // Restore state 7737 popI32(index); 7738 popPtr(data); 7739 7740 return true; 7741 } 7742 7743 template <bool ZeroFields> 7744 bool BaseCompiler::emitStructAlloc(uint32_t typeIndex, RegRef* object, 7745 bool* isOutlineStruct, RegPtr* outlineBase, 7746 uint32_t allocSiteIndex) { 7747 const TypeDef& typeDef = (*codeMeta_.types)[typeIndex]; 7748 const StructType& structType = typeDef.structType(); 7749 gc::AllocKind allocKind = structType.allocKind_; 7750 7751 *isOutlineStruct = structType.hasOOL(); 7752 7753 // Reserve this register early if we will need it so that it is not taken by 7754 // any register used in this function. 7755 needPtr(RegPtr(PreBarrierReg)); 7756 7757 *object = RegRef(); 7758 7759 // Allocate an uninitialized struct. This requires the type definition 7760 // for the struct to be pushed on the stack. This will trap on OOM. 7761 if (*isOutlineStruct) { 7762 pushI32(int32_t(typeIndex)); 7763 pushPtr(loadAllocSiteInstanceData(allocSiteIndex)); 7764 if (!emitInstanceCall(ZeroFields ? SASigStructNewOOL_true 7765 : SASigStructNewOOL_false)) { 7766 return false; 7767 } 7768 *object = popRef(); 7769 } else { 7770 // We eagerly sync the value stack to the machine stack here so as not to 7771 // confuse things with the conditional instance call below. 7772 sync(); 7773 7774 RegPtr instance; 7775 *object = RegRef(ReturnReg); 7776 needRef(*object); 7777 #ifndef RABALDR_PIN_INSTANCE 7778 // We reuse the result register for the instance. 7779 instance = needPtr(); 7780 fr.loadInstancePtr(instance); 7781 #else 7782 // We can use the pinned instance register. 7783 instance = RegPtr(InstanceReg); 7784 #endif 7785 7786 RegPtr allocSite = loadAllocSiteInstanceData(allocSiteIndex); 7787 7788 size_t offsetOfTypeDefData = wasm::Instance::offsetInData( 7789 codeMeta_.offsetOfTypeDefInstanceData(typeIndex)); 7790 7791 Label success; 7792 Label fail; 7793 { 7794 ScratchPtr scratch(*this); 7795 7796 masm.wasmNewStructObject(instance, *object, allocSite, scratch, 7797 offsetOfTypeDefData, &fail, allocKind, 7798 ZeroFields); 7799 } 7800 masm.jump(&success); 7801 7802 masm.bind(&fail); 7803 #ifndef RABALDR_PIN_INSTANCE 7804 freePtr(instance); 7805 #endif 7806 freeRef(*object); 7807 pushI32(int32_t(typeIndex)); 7808 pushPtr(allocSite); 7809 if (!emitInstanceCall(ZeroFields ? SASigStructNewIL_true 7810 : SASigStructNewIL_false)) { 7811 return false; 7812 } 7813 *object = popRef(); 7814 MOZ_ASSERT(*object == RegRef(ReturnReg)); 7815 7816 masm.bind(&success); 7817 } 7818 7819 *outlineBase = *isOutlineStruct ? needPtr() : RegPtr(); 7820 7821 // Free the barrier reg for later use 7822 freePtr(RegPtr(PreBarrierReg)); 7823 7824 return true; 7825 } 7826 7827 bool BaseCompiler::emitStructNew() { 7828 uint32_t typeIndex; 7829 BaseNothingVector args{}; 7830 if (!iter_.readStructNew(&typeIndex, &args)) { 7831 return false; 7832 } 7833 7834 uint32_t allocSiteIndex; 7835 if (!readAllocSiteIndex(&allocSiteIndex)) { 7836 return false; 7837 } 7838 7839 if (deadCode_) { 7840 return true; 7841 } 7842 7843 const TypeDef& typeDef = (*codeMeta_.types)[typeIndex]; 7844 const StructType& structType = typeDef.structType(); 7845 7846 RegRef object; 7847 RegPtr outlineBase; 7848 bool isOutlineStruct; 7849 if (!emitStructAlloc<false>(typeIndex, &object, &isOutlineStruct, 7850 &outlineBase, allocSiteIndex)) { 7851 return false; 7852 } 7853 7854 // Optimization opportunity: Iterate backward to pop arguments off the 7855 // stack. This will generate more instructions than we want, since we 7856 // really only need to pop the stack once at the end, not for every element, 7857 // but to do better we need a bit more machinery to load elements off the 7858 // stack into registers. 7859 7860 // Optimization opportunity: when the value being stored is a known 7861 // zero/null we need store nothing. This case may be somewhat common 7862 // because struct.new forces a value to be specified for every field. 7863 7864 // Optimization opportunity: this loop reestablishes the outline base pointer 7865 // every iteration, which really isn't very clever. It would be better to 7866 // establish it once before we start, then re-set it if/when we transition 7867 // from the out-of-line area back to the in-line area. That would however 7868 // require making ::emitGcStructSet preserve that register, which it 7869 // currently doesn't. 7870 7871 uint32_t fieldIndex = structType.fields_.length(); 7872 while (fieldIndex-- > 0) { 7873 const FieldType& field = structType.fields_[fieldIndex]; 7874 StorageType type = field.type; 7875 FieldAccessPath path = structType.fieldAccessPaths_[fieldIndex]; 7876 7877 // Reserve the barrier reg if we might need it for this store 7878 if (type.isRefRepr()) { 7879 needPtr(RegPtr(PreBarrierReg)); 7880 } 7881 AnyReg value = popAny(); 7882 // Free the barrier reg now that we've loaded the value 7883 if (type.isRefRepr()) { 7884 freePtr(RegPtr(PreBarrierReg)); 7885 } 7886 7887 if (path.hasOOL()) { 7888 // Load the outline data pointer. 7889 // The path has two components, of which the first (the IL component) is 7890 // the offset where the OOL pointer is stored. Hence `path.ilOffset()`. 7891 masm.loadPtr(Address(object, path.ilOffset()), outlineBase); 7892 7893 // Consumes value and outline data, object is preserved by this call. 7894 if (!emitGcStructSet<NoNullCheck>(object, outlineBase, path.oolOffset(), 7895 type, value, PreBarrierKind::None)) { 7896 return false; 7897 } 7898 } else { 7899 // Consumes value. object is unchanged by this call. 7900 if (!emitGcStructSet<NoNullCheck>(object, RegPtr(object), path.ilOffset(), 7901 type, value, PreBarrierKind::None)) { 7902 return false; 7903 } 7904 } 7905 } 7906 7907 if (isOutlineStruct) { 7908 freePtr(outlineBase); 7909 } 7910 pushRef(object); 7911 7912 return true; 7913 } 7914 7915 bool BaseCompiler::emitStructNewDefault() { 7916 uint32_t typeIndex; 7917 if (!iter_.readStructNewDefault(&typeIndex)) { 7918 return false; 7919 } 7920 7921 uint32_t allocSiteIndex; 7922 if (!readAllocSiteIndex(&allocSiteIndex)) { 7923 return false; 7924 } 7925 7926 if (deadCode_) { 7927 return true; 7928 } 7929 7930 RegRef object; 7931 bool isOutlineStruct; 7932 RegPtr outlineBase; 7933 if (!emitStructAlloc<true>(typeIndex, &object, &isOutlineStruct, &outlineBase, 7934 allocSiteIndex)) { 7935 return false; 7936 } 7937 7938 if (isOutlineStruct) { 7939 freePtr(outlineBase); 7940 } 7941 pushRef(object); 7942 7943 return true; 7944 } 7945 7946 bool BaseCompiler::emitStructGet(FieldWideningOp wideningOp) { 7947 uint32_t typeIndex; 7948 uint32_t fieldIndex; 7949 Nothing nothing; 7950 if (!iter_.readStructGet(&typeIndex, &fieldIndex, wideningOp, ¬hing)) { 7951 return false; 7952 } 7953 7954 if (deadCode_) { 7955 return true; 7956 } 7957 7958 const StructType& structType = (*codeMeta_.types)[typeIndex].structType(); 7959 const FieldType& structField = structType.fields_[fieldIndex]; 7960 StorageType fieldType = structField.type; 7961 FieldAccessPath path = structType.fieldAccessPaths_[fieldIndex]; 7962 7963 RegRef object = popRef(); 7964 if (path.hasOOL()) { 7965 // The path has two components, of which the first (the IL component) is 7966 // the offset where the OOL pointer is stored. Hence `path.ilOffset()`. 7967 RegPtr outlineBase = needPtr(); 7968 FaultingCodeOffset fco = 7969 masm.loadPtr(Address(object, path.ilOffset()), outlineBase); 7970 SignalNullCheck::emitTrapSite(this, fco, TrapMachineInsnForLoadWord()); 7971 // Load the value 7972 emitGcGet<Address, NoNullCheck>(fieldType, wideningOp, 7973 Address(outlineBase, path.oolOffset())); 7974 freePtr(outlineBase); 7975 } else { 7976 // Load the value 7977 emitGcGet<Address, SignalNullCheck>(fieldType, wideningOp, 7978 Address(object, path.ilOffset())); 7979 } 7980 freeRef(object); 7981 7982 return true; 7983 } 7984 7985 bool BaseCompiler::emitStructSet() { 7986 uint32_t typeIndex; 7987 uint32_t fieldIndex; 7988 Nothing nothing; 7989 if (!iter_.readStructSet(&typeIndex, &fieldIndex, ¬hing, ¬hing)) { 7990 return false; 7991 } 7992 7993 if (deadCode_) { 7994 return true; 7995 } 7996 7997 const StructType& structType = (*codeMeta_.types)[typeIndex].structType(); 7998 const FieldType& structField = structType.fields_[fieldIndex]; 7999 StorageType fieldType = structField.type; 8000 FieldAccessPath path = structType.fieldAccessPaths_[fieldIndex]; 8001 8002 // Reserve this register early if we will need it so that it is not taken by 8003 // any register used in this function. 8004 if (fieldType.isRefRepr()) { 8005 needPtr(RegPtr(PreBarrierReg)); 8006 } 8007 8008 // Set up other required registers 8009 RegPtr outlineBase = path.hasOOL() ? needPtr() : RegPtr(); 8010 AnyReg value = popAny(); 8011 RegRef object = popRef(); 8012 8013 // Free the barrier reg after we've allocated all registers 8014 if (fieldType.isRefRepr()) { 8015 freePtr(RegPtr(PreBarrierReg)); 8016 } 8017 8018 if (path.hasOOL()) { 8019 // Make `outlineBase` point at the first byte of the relevant area. 8020 // The path has two components, of which the first (the IL component) is 8021 // the offset where the OOL pointer is stored. Hence `path.ilOffset()`. 8022 FaultingCodeOffset fco = 8023 masm.loadPtr(Address(object, path.ilOffset()), outlineBase); 8024 SignalNullCheck::emitTrapSite(this, fco, TrapMachineInsnForLoadWord()); 8025 // Consumes `value`. `object` is unchanged by this call. 8026 if (!emitGcStructSet<NoNullCheck>(object, outlineBase, path.oolOffset(), 8027 fieldType, value, 8028 PreBarrierKind::Normal)) { 8029 return false; 8030 } 8031 } else { 8032 // Consumes `value`. `object` is unchanged by this call. 8033 if (!emitGcStructSet<SignalNullCheck>(object, RegPtr(object), 8034 path.ilOffset(), fieldType, value, 8035 PreBarrierKind::Normal)) { 8036 return false; 8037 } 8038 } 8039 8040 if (path.hasOOL()) { 8041 freePtr(outlineBase); 8042 } 8043 freeRef(object); 8044 8045 return true; 8046 } 8047 8048 template <bool ZeroFields> 8049 bool BaseCompiler::emitArrayAlloc(uint32_t typeIndex, RegRef object, 8050 RegI32 numElements, uint32_t elemSize, 8051 uint32_t allocSiteIndex) { 8052 // We eagerly sync the value stack to the machine stack here so as not to 8053 // confuse things with the conditional instance call below. 8054 sync(); 8055 8056 RegPtr instance; 8057 #ifndef RABALDR_PIN_INSTANCE 8058 // We reuse the object register for the instance. This is ok because object is 8059 // not live until instance is dead. 8060 instance = needPtr(); 8061 fr.loadInstancePtr(instance); 8062 #else 8063 // We can use the pinned instance register. 8064 instance = RegPtr(InstanceReg); 8065 #endif 8066 8067 RegPtr allocSite = loadAllocSiteInstanceData(allocSiteIndex); 8068 8069 size_t offsetOfTypeDefData = wasm::Instance::offsetInData( 8070 codeMeta_.offsetOfTypeDefInstanceData(typeIndex)); 8071 8072 Label success; 8073 Label fail; 8074 { 8075 ScratchPtr scratch(*this); 8076 masm.wasmNewArrayObject(instance, object, numElements, allocSite, scratch, 8077 offsetOfTypeDefData, &fail, elemSize, ZeroFields); 8078 } 8079 masm.jump(&success); 8080 8081 masm.bind(&fail); 8082 #ifndef RABALDR_PIN_INSTANCE 8083 freePtr(instance); 8084 #endif 8085 freeRef(object); 8086 pushI32(numElements); 8087 pushI32(int32_t(typeIndex)); 8088 pushPtr(allocSite); 8089 if (!emitInstanceCall(ZeroFields ? SASigArrayNew_true 8090 : SASigArrayNew_false)) { 8091 return false; 8092 } 8093 popRef(object); 8094 8095 masm.bind(&success); 8096 return true; 8097 } 8098 8099 template <bool ZeroFields> 8100 bool BaseCompiler::emitArrayAllocFixed(uint32_t typeIndex, RegRef object, 8101 uint32_t numElements, uint32_t elemSize, 8102 uint32_t allocSiteIndex) { 8103 SymbolicAddressSignature fun = 8104 ZeroFields ? SASigArrayNew_true : SASigArrayNew_false; 8105 8106 // The maximum number of elements for array.new_fixed enforced in validation 8107 // should always prevent overflow here. 8108 static_assert(MaxArrayNewFixedElements * sizeof(wasm::LitVal) < 8109 MaxArrayPayloadBytes); 8110 uint32_t storageBytes = 8111 WasmArrayObject::calcStorageBytesUnchecked(elemSize, numElements); 8112 if (storageBytes > WasmArrayObject_MaxInlineBytes) { 8113 freeRef(object); 8114 pushI32(numElements); 8115 pushI32(int32_t(typeIndex)); 8116 pushPtr(loadAllocSiteInstanceData(allocSiteIndex)); 8117 if (!emitInstanceCall(fun)) { 8118 return false; 8119 } 8120 popRef(object); 8121 8122 return true; 8123 } 8124 8125 // We eagerly sync the value stack to the machine stack here so as not to 8126 // confuse things with the conditional instance call below. 8127 sync(); 8128 8129 RegPtr instance; 8130 #ifndef RABALDR_PIN_INSTANCE 8131 // We reuse the object register for the instance. This is ok because object is 8132 // not live until instance is dead. 8133 instance = needPtr(); 8134 fr.loadInstancePtr(instance); 8135 #else 8136 // We can use the pinned instance register. 8137 instance = RegPtr(InstanceReg); 8138 #endif 8139 8140 RegPtr allocSite = loadAllocSiteInstanceData(allocSiteIndex); 8141 RegPtr temp1 = needPtr(); 8142 8143 size_t offsetOfTypeDefData = wasm::Instance::offsetInData( 8144 codeMeta_.offsetOfTypeDefInstanceData(typeIndex)); 8145 8146 Label success; 8147 Label fail; 8148 { 8149 ScratchPtr scratch(*this); 8150 masm.wasmNewArrayObjectFixed(instance, object, allocSite, temp1, scratch, 8151 offsetOfTypeDefData, &fail, numElements, 8152 storageBytes, ZeroFields); 8153 } 8154 freePtr(temp1); 8155 masm.jump(&success); 8156 8157 masm.bind(&fail); 8158 #ifndef RABALDR_PIN_INSTANCE 8159 freePtr(instance); 8160 #endif 8161 freeRef(object); 8162 pushI32(numElements); 8163 pushI32(int32_t(typeIndex)); 8164 pushPtr(allocSite); 8165 if (!emitInstanceCall(fun)) { 8166 return false; 8167 } 8168 popRef(object); 8169 8170 masm.bind(&success); 8171 8172 return true; 8173 } 8174 8175 bool BaseCompiler::emitArrayNew() { 8176 uint32_t typeIndex; 8177 Nothing nothing; 8178 if (!iter_.readArrayNew(&typeIndex, ¬hing, ¬hing)) { 8179 return false; 8180 } 8181 8182 uint32_t allocSiteIndex; 8183 if (!readAllocSiteIndex(&allocSiteIndex)) { 8184 return false; 8185 } 8186 8187 if (deadCode_) { 8188 return true; 8189 } 8190 8191 const ArrayType& arrayType = (*codeMeta_.types)[typeIndex].arrayType(); 8192 8193 // Reserve this register early if we will need it so that it is not taken by 8194 // any register used in this function. 8195 if (arrayType.elementType().isRefRepr()) { 8196 needPtr(RegPtr(PreBarrierReg)); 8197 } 8198 8199 RegRef object = needRef(); 8200 RegI32 numElements = popI32(); 8201 if (!emitArrayAlloc<false>(typeIndex, object, numElements, 8202 arrayType.elementType().size(), allocSiteIndex)) { 8203 return false; 8204 } 8205 8206 AnyReg value = popAny(); 8207 8208 // Acquire the data pointer from the object 8209 RegPtr rdata = emitGcArrayGetData<NoNullCheck>(object); 8210 8211 // Acquire the number of elements again 8212 numElements = emitGcArrayGetNumElements<NoNullCheck>(object); 8213 8214 // Free the barrier reg after we've allocated all registers 8215 if (arrayType.elementType().isRefRepr()) { 8216 freePtr(RegPtr(PreBarrierReg)); 8217 } 8218 8219 // Perform an initialization loop using `numElements` as the loop variable, 8220 // counting down to zero. 8221 Label done; 8222 Label loop; 8223 // Skip initialization if numElements = 0 8224 masm.branch32(Assembler::Equal, numElements, Imm32(0), &done); 8225 masm.bind(&loop); 8226 8227 // Move to the next element 8228 masm.sub32(Imm32(1), numElements); 8229 8230 // Assign value to array[numElements]. All registers are preserved 8231 if (!emitGcArraySet(object, rdata, numElements, arrayType, value, 8232 PreBarrierKind::None, PostBarrierKind::None)) { 8233 return false; 8234 } 8235 8236 // Loop back if there are still elements to initialize 8237 masm.branch32(Assembler::GreaterThan, numElements, Imm32(0), &loop); 8238 masm.bind(&done); 8239 8240 if (arrayType.elementType().isRefRepr()) { 8241 // Emit one whole-cell post barrier for the whole array, since there is just 8242 // one object and one value. Reuses rdata as a temp register. 8243 if (!emitPostBarrierWholeCell(object, value.ref(), rdata)) { 8244 return false; 8245 } 8246 } else { 8247 freePtr(rdata); 8248 } 8249 8250 freeI32(numElements); 8251 freeAny(value); 8252 pushRef(object); 8253 8254 return true; 8255 } 8256 8257 bool BaseCompiler::emitArrayNewFixed() { 8258 uint32_t typeIndex, numElements; 8259 BaseNothingVector nothings{}; 8260 if (!iter_.readArrayNewFixed(&typeIndex, &numElements, ¬hings)) { 8261 return false; 8262 } 8263 8264 uint32_t allocSiteIndex; 8265 if (!readAllocSiteIndex(&allocSiteIndex)) { 8266 return false; 8267 } 8268 8269 if (deadCode_) { 8270 return true; 8271 } 8272 8273 const ArrayType& arrayType = (*codeMeta_.types)[typeIndex].arrayType(); 8274 8275 // Reserve this register early if we will need it so that it is not taken by 8276 // any register used in this function. 8277 bool avoidPreBarrierReg = arrayType.elementType().isRefRepr(); 8278 if (avoidPreBarrierReg) { 8279 needPtr(RegPtr(PreBarrierReg)); 8280 } 8281 8282 RegRef object = needRef(); 8283 if (!emitArrayAllocFixed<false>(typeIndex, object, numElements, 8284 arrayType.elementType().size(), 8285 allocSiteIndex)) { 8286 return false; 8287 } 8288 8289 // Acquire the data pointer from the object 8290 RegPtr rdata = emitGcArrayGetData<NoNullCheck>(object); 8291 8292 // Free the barrier reg if we previously reserved it. 8293 if (avoidPreBarrierReg) { 8294 freePtr(RegPtr(PreBarrierReg)); 8295 } 8296 8297 // These together ensure that the max value of `index` in the loop below 8298 // remains comfortably below the 2^31 boundary. See comments on equivalent 8299 // assertions in EmitArrayNewFixed in WasmIonCompile.cpp for explanation. 8300 static_assert(16 /* sizeof v128 */ * MaxFunctionBytes <= 8301 MaxArrayPayloadBytes); 8302 MOZ_RELEASE_ASSERT(numElements <= MaxFunctionBytes); 8303 8304 // Generate straight-line initialization code. We could do better here if 8305 // there was a version of ::emitGcArraySet that took `index` as a `uint32_t` 8306 // rather than a general value-in-a-reg. 8307 for (uint32_t forwardIndex = 0; forwardIndex < numElements; forwardIndex++) { 8308 uint32_t reverseIndex = numElements - forwardIndex - 1; 8309 if (avoidPreBarrierReg) { 8310 needPtr(RegPtr(PreBarrierReg)); 8311 } 8312 AnyReg value = popAny(); 8313 pushI32(reverseIndex); 8314 RegI32 index = popI32(); 8315 if (avoidPreBarrierReg) { 8316 freePtr(RegPtr(PreBarrierReg)); 8317 } 8318 if (!emitGcArraySet(object, rdata, index, arrayType, value, 8319 PreBarrierKind::None, PostBarrierKind::WholeCell)) { 8320 return false; 8321 } 8322 freeI32(index); 8323 freeAny(value); 8324 } 8325 8326 freePtr(rdata); 8327 8328 pushRef(object); 8329 return true; 8330 } 8331 8332 bool BaseCompiler::emitArrayNewDefault() { 8333 uint32_t typeIndex; 8334 Nothing nothing; 8335 if (!iter_.readArrayNewDefault(&typeIndex, ¬hing)) { 8336 return false; 8337 } 8338 8339 uint32_t allocSiteIndex; 8340 if (!readAllocSiteIndex(&allocSiteIndex)) { 8341 return false; 8342 } 8343 8344 if (deadCode_) { 8345 return true; 8346 } 8347 8348 const ArrayType& arrayType = (*codeMeta_.types)[typeIndex].arrayType(); 8349 8350 RegRef object = needRef(); 8351 RegI32 numElements = popI32(); 8352 if (!emitArrayAlloc<true>(typeIndex, object, numElements, 8353 arrayType.elementType().size(), allocSiteIndex)) { 8354 return false; 8355 } 8356 8357 pushRef(object); 8358 return true; 8359 } 8360 8361 bool BaseCompiler::emitArrayNewData() { 8362 uint32_t typeIndex, segIndex; 8363 Nothing nothing; 8364 if (!iter_.readArrayNewData(&typeIndex, &segIndex, ¬hing, ¬hing)) { 8365 return false; 8366 } 8367 8368 uint32_t allocSiteIndex; 8369 if (!readAllocSiteIndex(&allocSiteIndex)) { 8370 return false; 8371 } 8372 8373 if (deadCode_) { 8374 return true; 8375 } 8376 8377 pushI32(int32_t(typeIndex)); 8378 pushPtr(loadAllocSiteInstanceData(allocSiteIndex)); 8379 pushI32(int32_t(segIndex)); 8380 8381 // The call removes 4 items from the stack: the segment byte offset and 8382 // number of elements (operands to array.new_data), and the type data and 8383 // seg index as pushed above. 8384 return emitInstanceCall(SASigArrayNewData); 8385 } 8386 8387 bool BaseCompiler::emitArrayNewElem() { 8388 uint32_t typeIndex, segIndex; 8389 Nothing nothing; 8390 if (!iter_.readArrayNewElem(&typeIndex, &segIndex, ¬hing, ¬hing)) { 8391 return false; 8392 } 8393 8394 uint32_t allocSiteIndex; 8395 if (!readAllocSiteIndex(&allocSiteIndex)) { 8396 return false; 8397 } 8398 8399 if (deadCode_) { 8400 return true; 8401 } 8402 8403 pushI32(int32_t(typeIndex)); 8404 pushPtr(loadAllocSiteInstanceData(allocSiteIndex)); 8405 pushI32(int32_t(segIndex)); 8406 8407 // The call removes 4 items from the stack: the segment element offset and 8408 // number of elements (operands to array.new_elem), and the type data and 8409 // seg index as pushed above. 8410 return emitInstanceCall(SASigArrayNewElem); 8411 } 8412 8413 bool BaseCompiler::emitArrayInitData() { 8414 uint32_t unusedTypeIndex, segIndex; 8415 Nothing nothing; 8416 if (!iter_.readArrayInitData(&unusedTypeIndex, &segIndex, ¬hing, ¬hing, 8417 ¬hing, ¬hing)) { 8418 return false; 8419 } 8420 8421 if (deadCode_) { 8422 return true; 8423 } 8424 8425 pushI32(int32_t(segIndex)); 8426 8427 // The call removes 5 items from the stack: the array, array index, segment 8428 // byte offset, and number of elements (operands to array.init_data), and the 8429 // seg index as pushed above. TypeDefInstanceData is not necessary for this 8430 // call because the array object has a reference to its type. 8431 return emitInstanceCall(SASigArrayInitData); 8432 } 8433 8434 bool BaseCompiler::emitArrayInitElem() { 8435 uint32_t typeIndex, segIndex; 8436 Nothing nothing; 8437 if (!iter_.readArrayInitElem(&typeIndex, &segIndex, ¬hing, ¬hing, 8438 ¬hing, ¬hing)) { 8439 return false; 8440 } 8441 8442 if (deadCode_) { 8443 return true; 8444 } 8445 8446 pushI32(int32_t(typeIndex)); 8447 pushI32(int32_t(segIndex)); 8448 8449 // The call removes 6 items from the stack: the array, array index, segment 8450 // offset, and number of elements (operands to array.init_elem), and the type 8451 // data and seg index as pushed above. 8452 return emitInstanceCall(SASigArrayInitElem); 8453 } 8454 8455 bool BaseCompiler::emitArrayGet(FieldWideningOp wideningOp) { 8456 uint32_t typeIndex; 8457 Nothing nothing; 8458 if (!iter_.readArrayGet(&typeIndex, wideningOp, ¬hing, ¬hing)) { 8459 return false; 8460 } 8461 8462 if (deadCode_) { 8463 return true; 8464 } 8465 8466 const ArrayType& arrayType = (*codeMeta_.types)[typeIndex].arrayType(); 8467 8468 RegI32 index = popI32(); 8469 RegRef rp = popRef(); 8470 8471 // Acquire the number of elements 8472 RegI32 numElements = emitGcArrayGetNumElements<SignalNullCheck>(rp); 8473 8474 // Bounds check the index 8475 emitGcArrayBoundsCheck(index, numElements); 8476 freeI32(numElements); 8477 8478 // Acquire the data pointer from the object 8479 RegPtr rdata = emitGcArrayGetData<NoNullCheck>(rp); 8480 8481 // Load the value 8482 uint32_t shift = arrayType.elementType().indexingShift(); 8483 if (IsShiftInScaleRange(shift)) { 8484 emitGcGet<BaseIndex, NoNullCheck>( 8485 arrayType.elementType(), wideningOp, 8486 BaseIndex(rdata, index, ShiftToScale(shift), 0)); 8487 } else { 8488 masm.lshiftPtr(Imm32(shift), index); 8489 emitGcGet<BaseIndex, NoNullCheck>(arrayType.elementType(), wideningOp, 8490 BaseIndex(rdata, index, TimesOne, 0)); 8491 } 8492 8493 freePtr(rdata); 8494 freeRef(rp); 8495 freeI32(index); 8496 8497 return true; 8498 } 8499 8500 bool BaseCompiler::emitArraySet() { 8501 uint32_t typeIndex; 8502 Nothing nothing; 8503 if (!iter_.readArraySet(&typeIndex, ¬hing, ¬hing, ¬hing)) { 8504 return false; 8505 } 8506 8507 if (deadCode_) { 8508 return true; 8509 } 8510 8511 const ArrayType& arrayType = (*codeMeta_.types)[typeIndex].arrayType(); 8512 8513 // Reserve this register early if we will need it so that it is not taken by 8514 // any register used in this function. 8515 if (arrayType.elementType().isRefRepr()) { 8516 needPtr(RegPtr(PreBarrierReg)); 8517 } 8518 8519 AnyReg value = popAny(); 8520 RegI32 index = popI32(); 8521 RegRef rp = popRef(); 8522 8523 // Acquire the number of elements 8524 RegI32 numElements = emitGcArrayGetNumElements<SignalNullCheck>(rp); 8525 8526 // Bounds check the index 8527 emitGcArrayBoundsCheck(index, numElements); 8528 freeI32(numElements); 8529 8530 // Acquire the data pointer from the object 8531 RegPtr rdata = emitGcArrayGetData<NoNullCheck>(rp); 8532 8533 // Free the barrier reg after we've allocated all registers 8534 if (arrayType.elementType().isRefRepr()) { 8535 freePtr(RegPtr(PreBarrierReg)); 8536 } 8537 8538 // All registers are preserved. This isn't strictly necessary, as we'll just 8539 // be freeing them all after this is done. But this is needed for repeated 8540 // assignments used in array.new/new_default. 8541 if (!emitGcArraySet(rp, rdata, index, arrayType, value, 8542 PreBarrierKind::Normal, PostBarrierKind::Imprecise)) { 8543 return false; 8544 } 8545 8546 freePtr(rdata); 8547 freeRef(rp); 8548 freeI32(index); 8549 freeAny(value); 8550 8551 return true; 8552 } 8553 8554 bool BaseCompiler::emitArrayLen() { 8555 Nothing nothing; 8556 if (!iter_.readArrayLen(¬hing)) { 8557 return false; 8558 } 8559 8560 if (deadCode_) { 8561 return true; 8562 } 8563 8564 RegRef rp = popRef(); 8565 8566 // Acquire the number of elements 8567 RegI32 numElements = emitGcArrayGetNumElements<SignalNullCheck>(rp); 8568 pushI32(numElements); 8569 8570 freeRef(rp); 8571 8572 return true; 8573 } 8574 8575 bool BaseCompiler::emitArrayCopy() { 8576 uint32_t dstArrayTypeIndex; 8577 uint32_t srcArrayTypeIndex; 8578 Nothing nothing; 8579 if (!iter_.readArrayCopy(&dstArrayTypeIndex, &srcArrayTypeIndex, ¬hing, 8580 ¬hing, ¬hing, ¬hing, ¬hing)) { 8581 return false; 8582 } 8583 8584 if (deadCode_) { 8585 return true; 8586 } 8587 8588 const ArrayType& dstArrayType = 8589 codeMeta_.types->type(dstArrayTypeIndex).arrayType(); 8590 StorageType dstElemType = dstArrayType.elementType(); 8591 int32_t elemSize = int32_t(dstElemType.size()); 8592 bool elemsAreRefTyped = dstElemType.isRefType(); 8593 8594 // The helper needs to know the element size. If copying ref values, the size 8595 // is negated to signal to the helper that it needs to do GC barriers and 8596 // such. 8597 pushI32(elemsAreRefTyped ? -elemSize : elemSize); 8598 8599 return emitInstanceCall(SASigArrayCopy); 8600 } 8601 8602 bool BaseCompiler::emitArrayFill() { 8603 AutoCreatedBy acb(masm, "(wasm)BaseCompiler::emitArrayFill"); 8604 uint32_t typeIndex; 8605 Nothing nothing; 8606 if (!iter_.readArrayFill(&typeIndex, ¬hing, ¬hing, ¬hing, 8607 ¬hing)) { 8608 return false; 8609 } 8610 8611 if (deadCode_) { 8612 return true; 8613 } 8614 8615 const TypeDef& typeDef = codeMeta_.types->type(typeIndex); 8616 const ArrayType& arrayType = typeDef.arrayType(); 8617 StorageType elementType = arrayType.elementType(); 8618 8619 // On x86 (32-bit), we are very short of registers, hence the code 8620 // generation scheme is less straightforward than it might otherwise be. 8621 // 8622 // Comment lines below of the form 8623 // 8624 // 3: foo bar xyzzy 8625 // 8626 // mean "at this point there are 3 integer regs in use (not including the 8627 // possible 2 needed for `value` in the worst case), and those 3 regs hold 8628 // the values for `foo`, `bar` and `xyzzy`. 8629 // 8630 // On x86, the worst case, we only have 3 int regs available (not including 8631 // `value`), thusly: 8632 // 8633 // - as a basis, we have: eax ebx ecx edx esi edi 8634 // 8635 // - ebx is "kind of" not available, since it is reserved as a wasm-baseline 8636 // scratch register (called `RabaldrScratchI32` on most targets, but 8637 // acquired below using `ScratchI32(..)`). 8638 // 8639 // - `value` isn't needed till the initialisation loop itself, hence it 8640 // would seem attractive to spill it across the range-check and setup code 8641 // that precedes the loop. However, it has variable type and regclass 8642 // (eg, it could be v128 or f32 or f64), which make spilling it somewhat 8643 // complex, so we bite the bullet and just allocate it at the start of the 8644 // routine. 8645 // 8646 // - Consequently we have the following two cases as worst-cases: 8647 // 8648 // * `value` is I64, so uses two registers on x86. 8649 // * `value` is reftyped, using one register, but also making 8650 // PreBarrierReg unavailable. 8651 // 8652 // - As a result of all of the above, we have only three registers, and 8653 // RabaldrScratchI32, to work with. And 4 spill-slot words; however .. 8654 // 8655 // - .. to spill/reload from a spill slot, we need a register to point at 8656 // the instance. That makes it even worse on x86 since there's no 8657 // reserved instance reg; hence we have to use one of our 3 for it. This 8658 // is indicated explicitly in the code below. 8659 // 8660 // There are many comment lines indicating the current disposition of the 3 8661 // regs. 8662 // 8663 // This generates somewhat clunky code for the setup and bounds test, but 8664 // the actual initialisation loop still has everything in registers, so 8665 // should run fast. 8666 // 8667 // The same scheme is used for all targets. Hence they all get clunky setup 8668 // code. Considering that this is a baseline compiler and also that the 8669 // loop itself is all-in-regs, this seems like a not-bad compromise compared 8670 // to having two different implementations for x86 vs everything else. 8671 8672 // Reserve this register early if we will need it so that it is not taken by 8673 // any register used in this function. 8674 if (elementType.isRefRepr()) { 8675 needPtr(RegPtr(PreBarrierReg)); 8676 } 8677 8678 // Set up a pointer to the Instance, so we can do manual spills/reloads 8679 #ifdef RABALDR_PIN_INSTANCE 8680 RegPtr instancePtr = RegPtr(InstanceReg); 8681 #else 8682 RegPtr instancePtr = needPtr(); 8683 fr.loadInstancePtr(instancePtr); 8684 #endif 8685 8686 // Pull operands off the instance stack and stash the non-Any ones 8687 RegI32 numElements = popI32(); 8688 stashWord(instancePtr, 0, RegPtr(numElements)); 8689 freeI32(numElements); 8690 numElements = RegI32::Invalid(); 8691 8692 AnyReg value = popAny(); 8693 8694 RegI32 index = popI32(); 8695 stashWord(instancePtr, 1, RegPtr(index)); 8696 freeI32(index); 8697 index = RegI32::Invalid(); 8698 8699 RegRef rp = popRef(); 8700 stashWord(instancePtr, 2, RegPtr(rp)); 8701 // 2: instancePtr rp 8702 8703 // Acquire the actual length of the array 8704 RegI32 arrayNumElements = emitGcArrayGetNumElements<SignalNullCheck>(rp); 8705 // 3: instancePtr rp arrayNumElements 8706 8707 // Drop rp 8708 freeRef(rp); 8709 rp = RegRef::Invalid(); 8710 // 2: instancePtr arrayNumElements 8711 8712 // Reload index 8713 index = needI32(); 8714 unstashWord(instancePtr, 1, RegPtr(index)); 8715 // 3: instancePtr arrayNumElements index 8716 8717 // Reload numElements into the reg that currently holds instancePtr 8718 #ifdef RABALDR_PIN_INSTANCE 8719 numElements = needI32(); 8720 #else 8721 numElements = RegI32(instancePtr); 8722 #endif 8723 unstashWord(instancePtr, 0, RegPtr(numElements)); 8724 instancePtr = RegPtr::Invalid(); 8725 // 3: arrayNumElements index numElements 8726 8727 // Do the bounds check. For this we will need to get hold of the 8728 // wasm-baseline's scratch register. 8729 { 8730 ScratchI32 scratch(*this); 8731 MOZ_ASSERT(RegI32(scratch) != arrayNumElements); 8732 MOZ_ASSERT(RegI32(scratch) != index); 8733 MOZ_ASSERT(RegI32(scratch) != numElements); 8734 masm.wasmBoundsCheckRange32(index, numElements, arrayNumElements, scratch, 8735 trapSiteDesc()); 8736 } 8737 // 3: arrayNumElements index numElements 8738 8739 // Drop arrayNumElements and numElements 8740 freeI32(arrayNumElements); 8741 arrayNumElements = RegI32::Invalid(); 8742 freeI32(numElements); 8743 numElements = RegI32::Invalid(); 8744 // 1: index 8745 8746 // Re-set-up the instance pointer; we had to ditch it earlier. 8747 #ifdef RABALDR_PIN_INSTANCE 8748 instancePtr = RegPtr(InstanceReg); 8749 #else 8750 instancePtr = needPtr(); 8751 fr.loadInstancePtr(instancePtr); 8752 #endif 8753 // 2: index instancePtr 8754 8755 // Reload rp 8756 rp = needRef(); 8757 unstashWord(instancePtr, 2, RegPtr(rp)); 8758 // 3: index instancePtr rp 8759 8760 // Drop instancePtr 8761 #ifdef RABALDR_PIN_INSTANCE 8762 instancePtr = RegPtr::Invalid(); 8763 #else 8764 freePtr(instancePtr); 8765 instancePtr = RegPtr::Invalid(); 8766 #endif 8767 // 2: index rp 8768 8769 // Acquire the data pointer from the object 8770 RegPtr rdata = emitGcArrayGetData<NoNullCheck>(rp); 8771 // 3: index rp rdata 8772 8773 // Currently `rdata` points at the start of the array data area. Move it 8774 // forwards by `index` units so as to make it point at the start of the area 8775 // to be filled. 8776 uint32_t shift = arrayType.elementType().indexingShift(); 8777 if (shift > 0) { 8778 masm.lshift32(Imm32(shift), index); 8779 // `index` is a 32 bit value, so we must zero-extend it to 64 bits before 8780 // adding it on to `rdata`. 8781 #ifdef JS_64BIT 8782 masm.move32To64ZeroExtend(index, widenI32(index)); 8783 #endif 8784 } 8785 masm.addPtr(index, rdata); 8786 // `index` is not used after this point. 8787 8788 // 3: index rp rdata 8789 // index is now (more or less) meaningless 8790 // rdata now points exactly at the start of the fill area 8791 // rp points at the array object 8792 8793 // Drop `index`; we no longer need it. 8794 freeI32(index); 8795 index = RegI32::Invalid(); 8796 // 2: rp rdata 8797 8798 // Re-re-set-up the instance pointer; we had to ditch it earlier. 8799 #ifdef RABALDR_PIN_INSTANCE 8800 instancePtr = RegPtr(InstanceReg); 8801 #else 8802 instancePtr = needPtr(); 8803 fr.loadInstancePtr(instancePtr); 8804 #endif 8805 // 3: rp rdata instancePtr 8806 8807 // And reload numElements. 8808 #ifdef RABALDR_PIN_INSTANCE 8809 numElements = needI32(); 8810 unstashWord(instancePtr, 0, RegPtr(numElements)); 8811 instancePtr = RegPtr::Invalid(); 8812 #else 8813 numElements = RegI32(instancePtr); 8814 unstashWord(instancePtr, 0, RegPtr(numElements)); 8815 instancePtr = RegPtr::Invalid(); 8816 #endif 8817 // 3: numElements rp rdata 8818 8819 // Free the barrier reg after we've allocated all registers 8820 if (elementType.isRefRepr()) { 8821 freePtr(RegPtr(PreBarrierReg)); 8822 } 8823 8824 // Perform an initialization loop using `numElements` as the loop variable, 8825 // starting at `numElements` and counting down to zero. 8826 Label done; 8827 Label loop; 8828 // Skip initialization if numElements = 0 8829 masm.branch32(Assembler::Equal, numElements, Imm32(0), &done); 8830 masm.bind(&loop); 8831 8832 // Move to the next element 8833 masm.sub32(Imm32(1), numElements); 8834 8835 // Assign value to rdata[numElements]. All registers are preserved. 8836 if (!emitGcArraySet(rp, rdata, numElements, arrayType, value, 8837 PreBarrierKind::None, PostBarrierKind::Imprecise)) { 8838 return false; 8839 } 8840 8841 // Loop back if there are still elements to initialize 8842 masm.branch32(Assembler::NotEqual, numElements, Imm32(0), &loop); 8843 masm.bind(&done); 8844 8845 freePtr(rdata); 8846 freeRef(rp); 8847 freeI32(numElements); 8848 freeAny(value); 8849 MOZ_ASSERT(index == RegPtr::Invalid()); 8850 MOZ_ASSERT(instancePtr == RegPtr::Invalid()); 8851 MOZ_ASSERT(arrayNumElements == RegI32::Invalid()); 8852 8853 return true; 8854 } 8855 8856 bool BaseCompiler::emitRefI31() { 8857 Nothing value; 8858 if (!iter_.readConversion(ValType::I32, 8859 ValType(RefType::i31().asNonNullable()), &value)) { 8860 return false; 8861 } 8862 8863 if (deadCode_) { 8864 return true; 8865 } 8866 8867 RegI32 intValue = popI32(); 8868 RegRef i31Value = needRef(); 8869 masm.truncate32ToWasmI31Ref(intValue, i31Value); 8870 freeI32(intValue); 8871 pushRef(i31Value); 8872 return true; 8873 } 8874 8875 bool BaseCompiler::emitI31Get(FieldWideningOp wideningOp) { 8876 MOZ_ASSERT(wideningOp != FieldWideningOp::None); 8877 8878 Nothing value; 8879 if (!iter_.readConversion(ValType(RefType::i31()), ValType::I32, &value)) { 8880 return false; 8881 } 8882 8883 if (deadCode_) { 8884 return true; 8885 } 8886 8887 RegRef i31Value = popRef(); 8888 RegI32 intValue = needI32(); 8889 8890 Label success; 8891 masm.branchWasmAnyRefIsNull(false, i31Value, &success); 8892 trap(Trap::NullPointerDereference); 8893 masm.bind(&success); 8894 8895 if (wideningOp == FieldWideningOp::Signed) { 8896 masm.convertWasmI31RefTo32Signed(i31Value, intValue); 8897 } else { 8898 masm.convertWasmI31RefTo32Unsigned(i31Value, intValue); 8899 } 8900 freeRef(i31Value); 8901 pushI32(intValue); 8902 return true; 8903 } 8904 8905 BranchIfRefSubtypeRegisters BaseCompiler::allocRegistersForBranchIfRefSubtype( 8906 RefType destType) { 8907 BranchWasmRefIsSubtypeRegisters needs = 8908 MacroAssembler::regsForBranchWasmRefIsSubtype(destType); 8909 return BranchIfRefSubtypeRegisters{ 8910 .superSTV = needs.needSuperSTV 8911 ? loadSuperTypeVector( 8912 codeMeta_.types->indexOf(*destType.typeDef())) 8913 : RegPtr::Invalid(), 8914 .scratch1 = needs.needScratch1 ? needI32() : RegI32::Invalid(), 8915 .scratch2 = needs.needScratch2 ? needI32() : RegI32::Invalid(), 8916 }; 8917 } 8918 8919 void BaseCompiler::freeRegistersForBranchIfRefSubtype( 8920 const BranchIfRefSubtypeRegisters& regs) { 8921 if (regs.superSTV.isValid()) { 8922 freePtr(regs.superSTV); 8923 } 8924 if (regs.scratch1.isValid()) { 8925 freeI32(regs.scratch1); 8926 } 8927 if (regs.scratch2.isValid()) { 8928 freeI32(regs.scratch2); 8929 } 8930 } 8931 8932 bool BaseCompiler::emitRefTest(bool nullable) { 8933 Nothing nothing; 8934 RefType sourceType; 8935 RefType destType; 8936 if (!iter_.readRefTest(nullable, &sourceType, &destType, ¬hing)) { 8937 return false; 8938 } 8939 8940 if (deadCode_) { 8941 return true; 8942 } 8943 8944 Label success; 8945 Label join; 8946 RegRef ref = popRef(); 8947 RegI32 result = needI32(); 8948 8949 BranchIfRefSubtypeRegisters regs = 8950 allocRegistersForBranchIfRefSubtype(destType); 8951 masm.branchWasmRefIsSubtype(ref, MaybeRefType(sourceType), destType, &success, 8952 /*onSuccess=*/true, /*signalNullChecks=*/false, 8953 regs.superSTV, regs.scratch1, regs.scratch2); 8954 freeRegistersForBranchIfRefSubtype(regs); 8955 8956 masm.xor32(result, result); 8957 masm.jump(&join); 8958 masm.bind(&success); 8959 masm.move32(Imm32(1), result); 8960 masm.bind(&join); 8961 8962 pushI32(result); 8963 freeRef(ref); 8964 8965 return true; 8966 } 8967 8968 bool BaseCompiler::emitRefCast(bool nullable) { 8969 Nothing nothing; 8970 RefType sourceType; 8971 RefType destType; 8972 if (!iter_.readRefCast(nullable, &sourceType, &destType, ¬hing)) { 8973 return false; 8974 } 8975 8976 if (deadCode_) { 8977 return true; 8978 } 8979 8980 RegRef ref = popRef(); 8981 8982 OutOfLineCode* ool = addOutOfLineCode( 8983 new (alloc_) OutOfLineAbortingTrap(Trap::BadCast, trapSiteDesc())); 8984 if (!ool) { 8985 return false; 8986 } 8987 8988 BranchIfRefSubtypeRegisters regs = 8989 allocRegistersForBranchIfRefSubtype(destType); 8990 FaultingCodeOffset fco = masm.branchWasmRefIsSubtype( 8991 ref, MaybeRefType(sourceType), destType, ool->entry(), 8992 /*onSuccess=*/false, /*signalNullChecks=*/true, regs.superSTV, 8993 regs.scratch1, regs.scratch2); 8994 if (fco.isValid()) { 8995 masm.append(wasm::Trap::BadCast, wasm::TrapMachineInsnForLoadWord(), 8996 fco.get(), trapSiteDesc()); 8997 } 8998 freeRegistersForBranchIfRefSubtype(regs); 8999 9000 pushRef(ref); 9001 9002 return true; 9003 } 9004 9005 bool BaseCompiler::emitBrOnCastCommon(bool onSuccess, 9006 uint32_t labelRelativeDepth, 9007 const ResultType& labelType, 9008 MaybeRefType sourceType, 9009 RefType destType) { 9010 Control& target = controlItem(labelRelativeDepth); 9011 target.bceSafeOnExit &= bceSafe_; 9012 9013 // 3. br_if $l : [T*, ref] -> [T*, ref] 9014 BranchState b(&target.label, target.stackHeight, InvertBranch(false), 9015 labelType); 9016 9017 // Don't allocate the result register used in the branch 9018 if (b.hasBlockResults()) { 9019 needIntegerResultRegisters(b.resultType); 9020 } 9021 9022 // Get the ref from the top of the stack 9023 RegRef refCondition = popRef(); 9024 9025 // Create a copy of the ref for passing to the on_cast label, 9026 // the original ref is used in the condition. 9027 RegRef ref = needRef(); 9028 moveRef(refCondition, ref); 9029 pushRef(ref); 9030 9031 if (b.hasBlockResults()) { 9032 freeIntegerResultRegisters(b.resultType); 9033 } 9034 9035 if (!jumpConditionalWithResults(&b, refCondition, sourceType, destType, 9036 onSuccess)) { 9037 return false; 9038 } 9039 freeRef(refCondition); 9040 9041 return true; 9042 } 9043 9044 bool BaseCompiler::emitBrOnCast(bool onSuccess) { 9045 MOZ_ASSERT(!hasLatentOp()); 9046 9047 uint32_t labelRelativeDepth; 9048 RefType sourceType; 9049 RefType destType; 9050 ResultType labelType; 9051 BaseNothingVector unused_values{}; 9052 if (!iter_.readBrOnCast(onSuccess, &labelRelativeDepth, &sourceType, 9053 &destType, &labelType, &unused_values)) { 9054 return false; 9055 } 9056 9057 if (deadCode_) { 9058 return true; 9059 } 9060 9061 return emitBrOnCastCommon(onSuccess, labelRelativeDepth, labelType, 9062 MaybeRefType(sourceType), destType); 9063 } 9064 9065 bool BaseCompiler::emitAnyConvertExtern() { 9066 // any.convert_extern is a no-op because anyref and extern share the same 9067 // representation 9068 Nothing nothing; 9069 return iter_.readRefConversion(RefType::extern_(), RefType::any(), ¬hing); 9070 } 9071 9072 bool BaseCompiler::emitExternConvertAny() { 9073 // extern.convert_any is a no-op because anyref and extern share the same 9074 // representation 9075 Nothing nothing; 9076 return iter_.readRefConversion(RefType::any(), RefType::extern_(), ¬hing); 9077 } 9078 9079 ////////////////////////////////////////////////////////////////////////////// 9080 // 9081 // SIMD and Relaxed SIMD. 9082 9083 #ifdef ENABLE_WASM_SIMD 9084 9085 // Emitter trampolines used by abstracted SIMD operations. Naming here follows 9086 // the SIMD spec pretty closely. 9087 9088 static void AndV128(MacroAssembler& masm, RegV128 rs, RegV128 rsd) { 9089 masm.bitwiseAndSimd128(rs, rsd); 9090 } 9091 9092 static void OrV128(MacroAssembler& masm, RegV128 rs, RegV128 rsd) { 9093 masm.bitwiseOrSimd128(rs, rsd); 9094 } 9095 9096 static void XorV128(MacroAssembler& masm, RegV128 rs, RegV128 rsd) { 9097 masm.bitwiseXorSimd128(rs, rsd); 9098 } 9099 9100 static void AddI8x16(MacroAssembler& masm, RegV128 rs, RegV128 rsd) { 9101 masm.addInt8x16(rsd, rs, rsd); 9102 } 9103 9104 static void AddI16x8(MacroAssembler& masm, RegV128 rs, RegV128 rsd) { 9105 masm.addInt16x8(rsd, rs, rsd); 9106 } 9107 9108 static void AddI32x4(MacroAssembler& masm, RegV128 rs, RegV128 rsd) { 9109 masm.addInt32x4(rsd, rs, rsd); 9110 } 9111 9112 static void AddF32x4(MacroAssembler& masm, RegV128 rs, RegV128 rsd) { 9113 masm.addFloat32x4(rsd, rs, rsd); 9114 } 9115 9116 static void AddI64x2(MacroAssembler& masm, RegV128 rs, RegV128 rsd) { 9117 masm.addInt64x2(rsd, rs, rsd); 9118 } 9119 9120 static void AddF64x2(MacroAssembler& masm, RegV128 rs, RegV128 rsd) { 9121 masm.addFloat64x2(rsd, rs, rsd); 9122 } 9123 9124 static void AddSatI8x16(MacroAssembler& masm, RegV128 rs, RegV128 rsd) { 9125 masm.addSatInt8x16(rsd, rs, rsd); 9126 } 9127 9128 static void AddSatUI8x16(MacroAssembler& masm, RegV128 rs, RegV128 rsd) { 9129 masm.unsignedAddSatInt8x16(rsd, rs, rsd); 9130 } 9131 9132 static void AddSatI16x8(MacroAssembler& masm, RegV128 rs, RegV128 rsd) { 9133 masm.addSatInt16x8(rsd, rs, rsd); 9134 } 9135 9136 static void AddSatUI16x8(MacroAssembler& masm, RegV128 rs, RegV128 rsd) { 9137 masm.unsignedAddSatInt16x8(rsd, rs, rsd); 9138 } 9139 9140 static void SubI8x16(MacroAssembler& masm, RegV128 rs, RegV128 rsd) { 9141 masm.subInt8x16(rsd, rs, rsd); 9142 } 9143 9144 static void SubI16x8(MacroAssembler& masm, RegV128 rs, RegV128 rsd) { 9145 masm.subInt16x8(rsd, rs, rsd); 9146 } 9147 9148 static void SubI32x4(MacroAssembler& masm, RegV128 rs, RegV128 rsd) { 9149 masm.subInt32x4(rsd, rs, rsd); 9150 } 9151 9152 static void SubF32x4(MacroAssembler& masm, RegV128 rs, RegV128 rsd) { 9153 masm.subFloat32x4(rsd, rs, rsd); 9154 } 9155 9156 static void SubI64x2(MacroAssembler& masm, RegV128 rs, RegV128 rsd) { 9157 masm.subInt64x2(rsd, rs, rsd); 9158 } 9159 9160 static void SubF64x2(MacroAssembler& masm, RegV128 rs, RegV128 rsd) { 9161 masm.subFloat64x2(rsd, rs, rsd); 9162 } 9163 9164 static void SubSatI8x16(MacroAssembler& masm, RegV128 rs, RegV128 rsd) { 9165 masm.subSatInt8x16(rsd, rs, rsd); 9166 } 9167 9168 static void SubSatUI8x16(MacroAssembler& masm, RegV128 rs, RegV128 rsd) { 9169 masm.unsignedSubSatInt8x16(rsd, rs, rsd); 9170 } 9171 9172 static void SubSatI16x8(MacroAssembler& masm, RegV128 rs, RegV128 rsd) { 9173 masm.subSatInt16x8(rsd, rs, rsd); 9174 } 9175 9176 static void SubSatUI16x8(MacroAssembler& masm, RegV128 rs, RegV128 rsd) { 9177 masm.unsignedSubSatInt16x8(rsd, rs, rsd); 9178 } 9179 9180 static void MulI16x8(MacroAssembler& masm, RegV128 rs, RegV128 rsd) { 9181 masm.mulInt16x8(rsd, rs, rsd); 9182 } 9183 9184 static void MulI32x4(MacroAssembler& masm, RegV128 rs, RegV128 rsd) { 9185 masm.mulInt32x4(rsd, rs, rsd); 9186 } 9187 9188 static void MulF32x4(MacroAssembler& masm, RegV128 rs, RegV128 rsd) { 9189 masm.mulFloat32x4(rsd, rs, rsd); 9190 } 9191 9192 # if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64) 9193 static void MulI64x2(MacroAssembler& masm, RegV128 rs, RegV128 rsd, 9194 RegV128 temp) { 9195 masm.mulInt64x2(rsd, rs, rsd, temp); 9196 } 9197 # elif defined(JS_CODEGEN_ARM64) 9198 static void MulI64x2(MacroAssembler& masm, RegV128 rs, RegV128 rsd, 9199 RegV128 temp1, RegV128 temp2) { 9200 masm.mulInt64x2(rsd, rs, rsd, temp1, temp2); 9201 } 9202 # endif 9203 9204 static void MulF64x2(MacroAssembler& masm, RegV128 rs, RegV128 rsd) { 9205 masm.mulFloat64x2(rsd, rs, rsd); 9206 } 9207 9208 static void DivF32x4(MacroAssembler& masm, RegV128 rs, RegV128 rsd) { 9209 masm.divFloat32x4(rsd, rs, rsd); 9210 } 9211 9212 static void DivF64x2(MacroAssembler& masm, RegV128 rs, RegV128 rsd) { 9213 masm.divFloat64x2(rsd, rs, rsd); 9214 } 9215 9216 # if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64) 9217 static void MinF32x4(MacroAssembler& masm, RegV128 rs, RegV128 rsd, 9218 RegV128 temp1, RegV128 temp2) { 9219 masm.minFloat32x4(rsd, rs, rsd, temp1, temp2); 9220 } 9221 9222 static void MinF64x2(MacroAssembler& masm, RegV128 rs, RegV128 rsd, 9223 RegV128 temp1, RegV128 temp2) { 9224 masm.minFloat64x2(rsd, rs, rsd, temp1, temp2); 9225 } 9226 9227 static void MaxF32x4(MacroAssembler& masm, RegV128 rs, RegV128 rsd, 9228 RegV128 temp1, RegV128 temp2) { 9229 masm.maxFloat32x4(rsd, rs, rsd, temp1, temp2); 9230 } 9231 9232 static void MaxF64x2(MacroAssembler& masm, RegV128 rs, RegV128 rsd, 9233 RegV128 temp1, RegV128 temp2) { 9234 masm.maxFloat64x2(rsd, rs, rsd, temp1, temp2); 9235 } 9236 9237 static void PMinF32x4(MacroAssembler& masm, RegV128 rsd, RegV128 rs, 9238 RhsDestOp) { 9239 masm.pseudoMinFloat32x4(rsd, rs); 9240 } 9241 9242 static void PMinF64x2(MacroAssembler& masm, RegV128 rsd, RegV128 rs, 9243 RhsDestOp) { 9244 masm.pseudoMinFloat64x2(rsd, rs); 9245 } 9246 9247 static void PMaxF32x4(MacroAssembler& masm, RegV128 rsd, RegV128 rs, 9248 RhsDestOp) { 9249 masm.pseudoMaxFloat32x4(rsd, rs); 9250 } 9251 9252 static void PMaxF64x2(MacroAssembler& masm, RegV128 rsd, RegV128 rs, 9253 RhsDestOp) { 9254 masm.pseudoMaxFloat64x2(rsd, rs); 9255 } 9256 # elif defined(JS_CODEGEN_ARM64) 9257 static void MinF32x4(MacroAssembler& masm, RegV128 rs, RegV128 rsd) { 9258 masm.minFloat32x4(rs, rsd); 9259 } 9260 9261 static void MinF64x2(MacroAssembler& masm, RegV128 rs, RegV128 rsd) { 9262 masm.minFloat64x2(rs, rsd); 9263 } 9264 9265 static void MaxF32x4(MacroAssembler& masm, RegV128 rs, RegV128 rsd) { 9266 masm.maxFloat32x4(rs, rsd); 9267 } 9268 9269 static void MaxF64x2(MacroAssembler& masm, RegV128 rs, RegV128 rsd) { 9270 masm.maxFloat64x2(rs, rsd); 9271 } 9272 9273 static void PMinF32x4(MacroAssembler& masm, RegV128 rs, RegV128 rsd) { 9274 masm.pseudoMinFloat32x4(rs, rsd); 9275 } 9276 9277 static void PMinF64x2(MacroAssembler& masm, RegV128 rs, RegV128 rsd) { 9278 masm.pseudoMinFloat64x2(rs, rsd); 9279 } 9280 9281 static void PMaxF32x4(MacroAssembler& masm, RegV128 rs, RegV128 rsd) { 9282 masm.pseudoMaxFloat32x4(rs, rsd); 9283 } 9284 9285 static void PMaxF64x2(MacroAssembler& masm, RegV128 rs, RegV128 rsd) { 9286 masm.pseudoMaxFloat64x2(rs, rsd); 9287 } 9288 # endif 9289 9290 static void DotI16x8(MacroAssembler& masm, RegV128 rs, RegV128 rsd) { 9291 masm.widenDotInt16x8(rsd, rs, rsd); 9292 } 9293 9294 static void ExtMulLowI8x16(MacroAssembler& masm, RegV128 rs, RegV128 rsd) { 9295 masm.extMulLowInt8x16(rsd, rs, rsd); 9296 } 9297 9298 static void ExtMulHighI8x16(MacroAssembler& masm, RegV128 rs, RegV128 rsd) { 9299 masm.extMulHighInt8x16(rsd, rs, rsd); 9300 } 9301 9302 static void ExtMulLowUI8x16(MacroAssembler& masm, RegV128 rs, RegV128 rsd) { 9303 masm.unsignedExtMulLowInt8x16(rsd, rs, rsd); 9304 } 9305 9306 static void ExtMulHighUI8x16(MacroAssembler& masm, RegV128 rs, RegV128 rsd) { 9307 masm.unsignedExtMulHighInt8x16(rsd, rs, rsd); 9308 } 9309 9310 static void ExtMulLowI16x8(MacroAssembler& masm, RegV128 rs, RegV128 rsd) { 9311 masm.extMulLowInt16x8(rsd, rs, rsd); 9312 } 9313 9314 static void ExtMulHighI16x8(MacroAssembler& masm, RegV128 rs, RegV128 rsd) { 9315 masm.extMulHighInt16x8(rsd, rs, rsd); 9316 } 9317 9318 static void ExtMulLowUI16x8(MacroAssembler& masm, RegV128 rs, RegV128 rsd) { 9319 masm.unsignedExtMulLowInt16x8(rsd, rs, rsd); 9320 } 9321 9322 static void ExtMulHighUI16x8(MacroAssembler& masm, RegV128 rs, RegV128 rsd) { 9323 masm.unsignedExtMulHighInt16x8(rsd, rs, rsd); 9324 } 9325 9326 static void ExtMulLowI32x4(MacroAssembler& masm, RegV128 rs, RegV128 rsd) { 9327 masm.extMulLowInt32x4(rsd, rs, rsd); 9328 } 9329 9330 static void ExtMulHighI32x4(MacroAssembler& masm, RegV128 rs, RegV128 rsd) { 9331 masm.extMulHighInt32x4(rsd, rs, rsd); 9332 } 9333 9334 static void ExtMulLowUI32x4(MacroAssembler& masm, RegV128 rs, RegV128 rsd) { 9335 masm.unsignedExtMulLowInt32x4(rsd, rs, rsd); 9336 } 9337 9338 static void ExtMulHighUI32x4(MacroAssembler& masm, RegV128 rs, RegV128 rsd) { 9339 masm.unsignedExtMulHighInt32x4(rsd, rs, rsd); 9340 } 9341 9342 static void Q15MulrSatS(MacroAssembler& masm, RegV128 rs, RegV128 rsd) { 9343 masm.q15MulrSatInt16x8(rsd, rs, rsd); 9344 } 9345 9346 static void CmpI8x16(MacroAssembler& masm, Assembler::Condition cond, 9347 RegV128 rs, RegV128 rsd) { 9348 masm.compareInt8x16(cond, rs, rsd); 9349 } 9350 9351 static void CmpI16x8(MacroAssembler& masm, Assembler::Condition cond, 9352 RegV128 rs, RegV128 rsd) { 9353 masm.compareInt16x8(cond, rs, rsd); 9354 } 9355 9356 static void CmpI32x4(MacroAssembler& masm, Assembler::Condition cond, 9357 RegV128 rs, RegV128 rsd) { 9358 masm.compareInt32x4(cond, rs, rsd); 9359 } 9360 9361 # if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64) 9362 static void CmpI64x2ForEquality(MacroAssembler& masm, Assembler::Condition cond, 9363 RegV128 rs, RegV128 rsd) { 9364 masm.compareForEqualityInt64x2(cond, rsd, rs, rsd); 9365 } 9366 9367 static void CmpI64x2ForOrdering(MacroAssembler& masm, Assembler::Condition cond, 9368 RegV128 rs, RegV128 rsd, RegV128 temp1, 9369 RegV128 temp2) { 9370 masm.compareForOrderingInt64x2(cond, rsd, rs, rsd, temp1, temp2); 9371 } 9372 # else 9373 static void CmpI64x2ForEquality(MacroAssembler& masm, Assembler::Condition cond, 9374 RegV128 rs, RegV128 rsd) { 9375 masm.compareInt64x2(cond, rs, rsd); 9376 } 9377 9378 static void CmpI64x2ForOrdering(MacroAssembler& masm, Assembler::Condition cond, 9379 RegV128 rs, RegV128 rsd) { 9380 masm.compareInt64x2(cond, rs, rsd); 9381 } 9382 # endif // JS_CODEGEN_X86 || JS_CODEGEN_X64 9383 9384 static void CmpUI8x16(MacroAssembler& masm, Assembler::Condition cond, 9385 RegV128 rs, RegV128 rsd) { 9386 masm.compareInt8x16(cond, rs, rsd); 9387 } 9388 9389 static void CmpUI16x8(MacroAssembler& masm, Assembler::Condition cond, 9390 RegV128 rs, RegV128 rsd) { 9391 masm.compareInt16x8(cond, rs, rsd); 9392 } 9393 9394 static void CmpUI32x4(MacroAssembler& masm, Assembler::Condition cond, 9395 RegV128 rs, RegV128 rsd) { 9396 masm.compareInt32x4(cond, rs, rsd); 9397 } 9398 9399 static void CmpF32x4(MacroAssembler& masm, Assembler::Condition cond, 9400 RegV128 rs, RegV128 rsd) { 9401 masm.compareFloat32x4(cond, rs, rsd); 9402 } 9403 9404 static void CmpF64x2(MacroAssembler& masm, Assembler::Condition cond, 9405 RegV128 rs, RegV128 rsd) { 9406 masm.compareFloat64x2(cond, rs, rsd); 9407 } 9408 9409 static void NegI8x16(MacroAssembler& masm, RegV128 rs, RegV128 rd) { 9410 masm.negInt8x16(rs, rd); 9411 } 9412 9413 static void NegI16x8(MacroAssembler& masm, RegV128 rs, RegV128 rd) { 9414 masm.negInt16x8(rs, rd); 9415 } 9416 9417 static void NegI32x4(MacroAssembler& masm, RegV128 rs, RegV128 rd) { 9418 masm.negInt32x4(rs, rd); 9419 } 9420 9421 static void NegI64x2(MacroAssembler& masm, RegV128 rs, RegV128 rd) { 9422 masm.negInt64x2(rs, rd); 9423 } 9424 9425 static void NegF32x4(MacroAssembler& masm, RegV128 rs, RegV128 rd) { 9426 masm.negFloat32x4(rs, rd); 9427 } 9428 9429 static void NegF64x2(MacroAssembler& masm, RegV128 rs, RegV128 rd) { 9430 masm.negFloat64x2(rs, rd); 9431 } 9432 9433 static void AbsF32x4(MacroAssembler& masm, RegV128 rs, RegV128 rd) { 9434 masm.absFloat32x4(rs, rd); 9435 } 9436 9437 static void AbsF64x2(MacroAssembler& masm, RegV128 rs, RegV128 rd) { 9438 masm.absFloat64x2(rs, rd); 9439 } 9440 9441 static void SqrtF32x4(MacroAssembler& masm, RegV128 rs, RegV128 rd) { 9442 masm.sqrtFloat32x4(rs, rd); 9443 } 9444 9445 static void SqrtF64x2(MacroAssembler& masm, RegV128 rs, RegV128 rd) { 9446 masm.sqrtFloat64x2(rs, rd); 9447 } 9448 9449 static void CeilF32x4(MacroAssembler& masm, RegV128 rs, RegV128 rd) { 9450 masm.ceilFloat32x4(rs, rd); 9451 } 9452 9453 static void FloorF32x4(MacroAssembler& masm, RegV128 rs, RegV128 rd) { 9454 masm.floorFloat32x4(rs, rd); 9455 } 9456 9457 static void TruncF32x4(MacroAssembler& masm, RegV128 rs, RegV128 rd) { 9458 masm.truncFloat32x4(rs, rd); 9459 } 9460 9461 static void NearestF32x4(MacroAssembler& masm, RegV128 rs, RegV128 rd) { 9462 masm.nearestFloat32x4(rs, rd); 9463 } 9464 9465 static void CeilF64x2(MacroAssembler& masm, RegV128 rs, RegV128 rd) { 9466 masm.ceilFloat64x2(rs, rd); 9467 } 9468 9469 static void FloorF64x2(MacroAssembler& masm, RegV128 rs, RegV128 rd) { 9470 masm.floorFloat64x2(rs, rd); 9471 } 9472 9473 static void TruncF64x2(MacroAssembler& masm, RegV128 rs, RegV128 rd) { 9474 masm.truncFloat64x2(rs, rd); 9475 } 9476 9477 static void NearestF64x2(MacroAssembler& masm, RegV128 rs, RegV128 rd) { 9478 masm.nearestFloat64x2(rs, rd); 9479 } 9480 9481 static void NotV128(MacroAssembler& masm, RegV128 rs, RegV128 rd) { 9482 masm.bitwiseNotSimd128(rs, rd); 9483 } 9484 9485 static void ExtAddPairwiseI8x16(MacroAssembler& masm, RegV128 rs, RegV128 rsd) { 9486 masm.extAddPairwiseInt8x16(rs, rsd); 9487 } 9488 9489 static void ExtAddPairwiseUI8x16(MacroAssembler& masm, RegV128 rs, 9490 RegV128 rsd) { 9491 masm.unsignedExtAddPairwiseInt8x16(rs, rsd); 9492 } 9493 9494 static void ExtAddPairwiseI16x8(MacroAssembler& masm, RegV128 rs, RegV128 rsd) { 9495 masm.extAddPairwiseInt16x8(rs, rsd); 9496 } 9497 9498 static void ExtAddPairwiseUI16x8(MacroAssembler& masm, RegV128 rs, 9499 RegV128 rsd) { 9500 masm.unsignedExtAddPairwiseInt16x8(rs, rsd); 9501 } 9502 9503 static void ShiftOpMask(MacroAssembler& masm, SimdOp op, RegI32 in, 9504 RegI32 out) { 9505 int32_t maskBits; 9506 9507 masm.mov(in, out); 9508 if (MacroAssembler::MustMaskShiftCountSimd128(op, &maskBits)) { 9509 masm.and32(Imm32(maskBits), out); 9510 } 9511 } 9512 9513 # if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64) 9514 static void ShiftLeftI8x16(MacroAssembler& masm, RegI32 rs, RegV128 rsd, 9515 RegI32 temp1, RegV128 temp2) { 9516 ShiftOpMask(masm, SimdOp::I8x16Shl, rs, temp1); 9517 masm.leftShiftInt8x16(temp1, rsd, temp2); 9518 } 9519 9520 static void ShiftLeftI16x8(MacroAssembler& masm, RegI32 rs, RegV128 rsd, 9521 RegI32 temp) { 9522 ShiftOpMask(masm, SimdOp::I16x8Shl, rs, temp); 9523 masm.leftShiftInt16x8(temp, rsd); 9524 } 9525 9526 static void ShiftLeftI32x4(MacroAssembler& masm, RegI32 rs, RegV128 rsd, 9527 RegI32 temp) { 9528 ShiftOpMask(masm, SimdOp::I32x4Shl, rs, temp); 9529 masm.leftShiftInt32x4(temp, rsd); 9530 } 9531 9532 static void ShiftLeftI64x2(MacroAssembler& masm, RegI32 rs, RegV128 rsd, 9533 RegI32 temp) { 9534 ShiftOpMask(masm, SimdOp::I64x2Shl, rs, temp); 9535 masm.leftShiftInt64x2(temp, rsd); 9536 } 9537 9538 static void ShiftRightI8x16(MacroAssembler& masm, RegI32 rs, RegV128 rsd, 9539 RegI32 temp1, RegV128 temp2) { 9540 ShiftOpMask(masm, SimdOp::I8x16ShrS, rs, temp1); 9541 masm.rightShiftInt8x16(temp1, rsd, temp2); 9542 } 9543 9544 static void ShiftRightUI8x16(MacroAssembler& masm, RegI32 rs, RegV128 rsd, 9545 RegI32 temp1, RegV128 temp2) { 9546 ShiftOpMask(masm, SimdOp::I8x16ShrU, rs, temp1); 9547 masm.unsignedRightShiftInt8x16(temp1, rsd, temp2); 9548 } 9549 9550 static void ShiftRightI16x8(MacroAssembler& masm, RegI32 rs, RegV128 rsd, 9551 RegI32 temp) { 9552 ShiftOpMask(masm, SimdOp::I16x8ShrS, rs, temp); 9553 masm.rightShiftInt16x8(temp, rsd); 9554 } 9555 9556 static void ShiftRightUI16x8(MacroAssembler& masm, RegI32 rs, RegV128 rsd, 9557 RegI32 temp) { 9558 ShiftOpMask(masm, SimdOp::I16x8ShrU, rs, temp); 9559 masm.unsignedRightShiftInt16x8(temp, rsd); 9560 } 9561 9562 static void ShiftRightI32x4(MacroAssembler& masm, RegI32 rs, RegV128 rsd, 9563 RegI32 temp) { 9564 ShiftOpMask(masm, SimdOp::I32x4ShrS, rs, temp); 9565 masm.rightShiftInt32x4(temp, rsd); 9566 } 9567 9568 static void ShiftRightUI32x4(MacroAssembler& masm, RegI32 rs, RegV128 rsd, 9569 RegI32 temp) { 9570 ShiftOpMask(masm, SimdOp::I32x4ShrU, rs, temp); 9571 masm.unsignedRightShiftInt32x4(temp, rsd); 9572 } 9573 9574 static void ShiftRightUI64x2(MacroAssembler& masm, RegI32 rs, RegV128 rsd, 9575 RegI32 temp) { 9576 ShiftOpMask(masm, SimdOp::I64x2ShrU, rs, temp); 9577 masm.unsignedRightShiftInt64x2(temp, rsd); 9578 } 9579 # elif defined(JS_CODEGEN_ARM64) 9580 static void ShiftLeftI8x16(MacroAssembler& masm, RegI32 rs, RegV128 rsd, 9581 RegI32 temp) { 9582 ShiftOpMask(masm, SimdOp::I8x16Shl, rs, temp); 9583 masm.leftShiftInt8x16(rsd, temp, rsd); 9584 } 9585 9586 static void ShiftLeftI16x8(MacroAssembler& masm, RegI32 rs, RegV128 rsd, 9587 RegI32 temp) { 9588 ShiftOpMask(masm, SimdOp::I16x8Shl, rs, temp); 9589 masm.leftShiftInt16x8(rsd, temp, rsd); 9590 } 9591 9592 static void ShiftLeftI32x4(MacroAssembler& masm, RegI32 rs, RegV128 rsd, 9593 RegI32 temp) { 9594 ShiftOpMask(masm, SimdOp::I32x4Shl, rs, temp); 9595 masm.leftShiftInt32x4(rsd, temp, rsd); 9596 } 9597 9598 static void ShiftLeftI64x2(MacroAssembler& masm, RegI32 rs, RegV128 rsd, 9599 RegI32 temp) { 9600 ShiftOpMask(masm, SimdOp::I64x2Shl, rs, temp); 9601 masm.leftShiftInt64x2(rsd, temp, rsd); 9602 } 9603 9604 static void ShiftRightI8x16(MacroAssembler& masm, RegI32 rs, RegV128 rsd, 9605 RegI32 temp) { 9606 ShiftOpMask(masm, SimdOp::I8x16ShrS, rs, temp); 9607 masm.rightShiftInt8x16(rsd, temp, rsd); 9608 } 9609 9610 static void ShiftRightUI8x16(MacroAssembler& masm, RegI32 rs, RegV128 rsd, 9611 RegI32 temp) { 9612 ShiftOpMask(masm, SimdOp::I8x16ShrU, rs, temp); 9613 masm.unsignedRightShiftInt8x16(rsd, temp, rsd); 9614 } 9615 9616 static void ShiftRightI16x8(MacroAssembler& masm, RegI32 rs, RegV128 rsd, 9617 RegI32 temp) { 9618 ShiftOpMask(masm, SimdOp::I16x8ShrS, rs, temp); 9619 masm.rightShiftInt16x8(rsd, temp, rsd); 9620 } 9621 9622 static void ShiftRightUI16x8(MacroAssembler& masm, RegI32 rs, RegV128 rsd, 9623 RegI32 temp) { 9624 ShiftOpMask(masm, SimdOp::I16x8ShrU, rs, temp); 9625 masm.unsignedRightShiftInt16x8(rsd, temp, rsd); 9626 } 9627 9628 static void ShiftRightI32x4(MacroAssembler& masm, RegI32 rs, RegV128 rsd, 9629 RegI32 temp) { 9630 ShiftOpMask(masm, SimdOp::I32x4ShrS, rs, temp); 9631 masm.rightShiftInt32x4(rsd, temp, rsd); 9632 } 9633 9634 static void ShiftRightUI32x4(MacroAssembler& masm, RegI32 rs, RegV128 rsd, 9635 RegI32 temp) { 9636 ShiftOpMask(masm, SimdOp::I32x4ShrU, rs, temp); 9637 masm.unsignedRightShiftInt32x4(rsd, temp, rsd); 9638 } 9639 9640 static void ShiftRightI64x2(MacroAssembler& masm, RegI32 rs, RegV128 rsd, 9641 RegI32 temp) { 9642 ShiftOpMask(masm, SimdOp::I64x2ShrS, rs, temp); 9643 masm.rightShiftInt64x2(rsd, temp, rsd); 9644 } 9645 9646 static void ShiftRightUI64x2(MacroAssembler& masm, RegI32 rs, RegV128 rsd, 9647 RegI32 temp) { 9648 ShiftOpMask(masm, SimdOp::I64x2ShrU, rs, temp); 9649 masm.unsignedRightShiftInt64x2(rsd, temp, rsd); 9650 } 9651 # endif 9652 9653 static void AverageUI8x16(MacroAssembler& masm, RegV128 rs, RegV128 rsd) { 9654 masm.unsignedAverageInt8x16(rsd, rs, rsd); 9655 } 9656 9657 static void AverageUI16x8(MacroAssembler& masm, RegV128 rs, RegV128 rsd) { 9658 masm.unsignedAverageInt16x8(rsd, rs, rsd); 9659 } 9660 9661 static void MinI8x16(MacroAssembler& masm, RegV128 rs, RegV128 rsd) { 9662 masm.minInt8x16(rsd, rs, rsd); 9663 } 9664 9665 static void MinUI8x16(MacroAssembler& masm, RegV128 rs, RegV128 rsd) { 9666 masm.unsignedMinInt8x16(rsd, rs, rsd); 9667 } 9668 9669 static void MaxI8x16(MacroAssembler& masm, RegV128 rs, RegV128 rsd) { 9670 masm.maxInt8x16(rsd, rs, rsd); 9671 } 9672 9673 static void MaxUI8x16(MacroAssembler& masm, RegV128 rs, RegV128 rsd) { 9674 masm.unsignedMaxInt8x16(rsd, rs, rsd); 9675 } 9676 9677 static void MinI16x8(MacroAssembler& masm, RegV128 rs, RegV128 rsd) { 9678 masm.minInt16x8(rsd, rs, rsd); 9679 } 9680 9681 static void MinUI16x8(MacroAssembler& masm, RegV128 rs, RegV128 rsd) { 9682 masm.unsignedMinInt16x8(rsd, rs, rsd); 9683 } 9684 9685 static void MaxI16x8(MacroAssembler& masm, RegV128 rs, RegV128 rsd) { 9686 masm.maxInt16x8(rsd, rs, rsd); 9687 } 9688 9689 static void MaxUI16x8(MacroAssembler& masm, RegV128 rs, RegV128 rsd) { 9690 masm.unsignedMaxInt16x8(rsd, rs, rsd); 9691 } 9692 9693 static void MinI32x4(MacroAssembler& masm, RegV128 rs, RegV128 rsd) { 9694 masm.minInt32x4(rsd, rs, rsd); 9695 } 9696 9697 static void MinUI32x4(MacroAssembler& masm, RegV128 rs, RegV128 rsd) { 9698 masm.unsignedMinInt32x4(rsd, rs, rsd); 9699 } 9700 9701 static void MaxI32x4(MacroAssembler& masm, RegV128 rs, RegV128 rsd) { 9702 masm.maxInt32x4(rsd, rs, rsd); 9703 } 9704 9705 static void MaxUI32x4(MacroAssembler& masm, RegV128 rs, RegV128 rsd) { 9706 masm.unsignedMaxInt32x4(rsd, rs, rsd); 9707 } 9708 9709 static void NarrowI16x8(MacroAssembler& masm, RegV128 rs, RegV128 rsd) { 9710 masm.narrowInt16x8(rsd, rs, rsd); 9711 } 9712 9713 static void NarrowUI16x8(MacroAssembler& masm, RegV128 rs, RegV128 rsd) { 9714 masm.unsignedNarrowInt16x8(rsd, rs, rsd); 9715 } 9716 9717 static void NarrowI32x4(MacroAssembler& masm, RegV128 rs, RegV128 rsd) { 9718 masm.narrowInt32x4(rsd, rs, rsd); 9719 } 9720 9721 static void NarrowUI32x4(MacroAssembler& masm, RegV128 rs, RegV128 rsd) { 9722 masm.unsignedNarrowInt32x4(rsd, rs, rsd); 9723 } 9724 9725 static void WidenLowI8x16(MacroAssembler& masm, RegV128 rs, RegV128 rd) { 9726 masm.widenLowInt8x16(rs, rd); 9727 } 9728 9729 static void WidenHighI8x16(MacroAssembler& masm, RegV128 rs, RegV128 rd) { 9730 masm.widenHighInt8x16(rs, rd); 9731 } 9732 9733 static void WidenLowUI8x16(MacroAssembler& masm, RegV128 rs, RegV128 rd) { 9734 masm.unsignedWidenLowInt8x16(rs, rd); 9735 } 9736 9737 static void WidenHighUI8x16(MacroAssembler& masm, RegV128 rs, RegV128 rd) { 9738 masm.unsignedWidenHighInt8x16(rs, rd); 9739 } 9740 9741 static void WidenLowI16x8(MacroAssembler& masm, RegV128 rs, RegV128 rd) { 9742 masm.widenLowInt16x8(rs, rd); 9743 } 9744 9745 static void WidenHighI16x8(MacroAssembler& masm, RegV128 rs, RegV128 rd) { 9746 masm.widenHighInt16x8(rs, rd); 9747 } 9748 9749 static void WidenLowUI16x8(MacroAssembler& masm, RegV128 rs, RegV128 rd) { 9750 masm.unsignedWidenLowInt16x8(rs, rd); 9751 } 9752 9753 static void WidenHighUI16x8(MacroAssembler& masm, RegV128 rs, RegV128 rd) { 9754 masm.unsignedWidenHighInt16x8(rs, rd); 9755 } 9756 9757 static void WidenLowI32x4(MacroAssembler& masm, RegV128 rs, RegV128 rd) { 9758 masm.widenLowInt32x4(rs, rd); 9759 } 9760 9761 static void WidenHighI32x4(MacroAssembler& masm, RegV128 rs, RegV128 rd) { 9762 masm.widenHighInt32x4(rs, rd); 9763 } 9764 9765 static void WidenLowUI32x4(MacroAssembler& masm, RegV128 rs, RegV128 rd) { 9766 masm.unsignedWidenLowInt32x4(rs, rd); 9767 } 9768 9769 static void WidenHighUI32x4(MacroAssembler& masm, RegV128 rs, RegV128 rd) { 9770 masm.unsignedWidenHighInt32x4(rs, rd); 9771 } 9772 9773 # if defined(JS_CODEGEN_ARM64) 9774 static void PopcntI8x16(MacroAssembler& masm, RegV128 rs, RegV128 rd) { 9775 masm.popcntInt8x16(rs, rd); 9776 } 9777 # else 9778 static void PopcntI8x16(MacroAssembler& masm, RegV128 rs, RegV128 rd, 9779 RegV128 temp) { 9780 masm.popcntInt8x16(rs, rd, temp); 9781 } 9782 # endif // JS_CODEGEN_ARM64 9783 9784 static void AbsI8x16(MacroAssembler& masm, RegV128 rs, RegV128 rd) { 9785 masm.absInt8x16(rs, rd); 9786 } 9787 9788 static void AbsI16x8(MacroAssembler& masm, RegV128 rs, RegV128 rd) { 9789 masm.absInt16x8(rs, rd); 9790 } 9791 9792 static void AbsI32x4(MacroAssembler& masm, RegV128 rs, RegV128 rd) { 9793 masm.absInt32x4(rs, rd); 9794 } 9795 9796 static void AbsI64x2(MacroAssembler& masm, RegV128 rs, RegV128 rd) { 9797 masm.absInt64x2(rs, rd); 9798 } 9799 9800 static void ExtractLaneI8x16(MacroAssembler& masm, uint32_t laneIndex, 9801 RegV128 rs, RegI32 rd) { 9802 masm.extractLaneInt8x16(laneIndex, rs, rd); 9803 } 9804 9805 static void ExtractLaneUI8x16(MacroAssembler& masm, uint32_t laneIndex, 9806 RegV128 rs, RegI32 rd) { 9807 masm.unsignedExtractLaneInt8x16(laneIndex, rs, rd); 9808 } 9809 9810 static void ExtractLaneI16x8(MacroAssembler& masm, uint32_t laneIndex, 9811 RegV128 rs, RegI32 rd) { 9812 masm.extractLaneInt16x8(laneIndex, rs, rd); 9813 } 9814 9815 static void ExtractLaneUI16x8(MacroAssembler& masm, uint32_t laneIndex, 9816 RegV128 rs, RegI32 rd) { 9817 masm.unsignedExtractLaneInt16x8(laneIndex, rs, rd); 9818 } 9819 9820 static void ExtractLaneI32x4(MacroAssembler& masm, uint32_t laneIndex, 9821 RegV128 rs, RegI32 rd) { 9822 masm.extractLaneInt32x4(laneIndex, rs, rd); 9823 } 9824 9825 static void ExtractLaneI64x2(MacroAssembler& masm, uint32_t laneIndex, 9826 RegV128 rs, RegI64 rd) { 9827 masm.extractLaneInt64x2(laneIndex, rs, rd); 9828 } 9829 9830 static void ExtractLaneF32x4(MacroAssembler& masm, uint32_t laneIndex, 9831 RegV128 rs, RegF32 rd) { 9832 masm.extractLaneFloat32x4(laneIndex, rs, rd); 9833 } 9834 9835 static void ExtractLaneF64x2(MacroAssembler& masm, uint32_t laneIndex, 9836 RegV128 rs, RegF64 rd) { 9837 masm.extractLaneFloat64x2(laneIndex, rs, rd); 9838 } 9839 9840 static void ReplaceLaneI8x16(MacroAssembler& masm, uint32_t laneIndex, 9841 RegI32 rs, RegV128 rsd) { 9842 masm.replaceLaneInt8x16(laneIndex, rs, rsd); 9843 } 9844 9845 static void ReplaceLaneI16x8(MacroAssembler& masm, uint32_t laneIndex, 9846 RegI32 rs, RegV128 rsd) { 9847 masm.replaceLaneInt16x8(laneIndex, rs, rsd); 9848 } 9849 9850 static void ReplaceLaneI32x4(MacroAssembler& masm, uint32_t laneIndex, 9851 RegI32 rs, RegV128 rsd) { 9852 masm.replaceLaneInt32x4(laneIndex, rs, rsd); 9853 } 9854 9855 static void ReplaceLaneI64x2(MacroAssembler& masm, uint32_t laneIndex, 9856 RegI64 rs, RegV128 rsd) { 9857 masm.replaceLaneInt64x2(laneIndex, rs, rsd); 9858 } 9859 9860 static void ReplaceLaneF32x4(MacroAssembler& masm, uint32_t laneIndex, 9861 RegF32 rs, RegV128 rsd) { 9862 masm.replaceLaneFloat32x4(laneIndex, rs, rsd); 9863 } 9864 9865 static void ReplaceLaneF64x2(MacroAssembler& masm, uint32_t laneIndex, 9866 RegF64 rs, RegV128 rsd) { 9867 masm.replaceLaneFloat64x2(laneIndex, rs, rsd); 9868 } 9869 9870 static void SplatI8x16(MacroAssembler& masm, RegI32 rs, RegV128 rd) { 9871 masm.splatX16(rs, rd); 9872 } 9873 9874 static void SplatI16x8(MacroAssembler& masm, RegI32 rs, RegV128 rd) { 9875 masm.splatX8(rs, rd); 9876 } 9877 9878 static void SplatI32x4(MacroAssembler& masm, RegI32 rs, RegV128 rd) { 9879 masm.splatX4(rs, rd); 9880 } 9881 9882 static void SplatI64x2(MacroAssembler& masm, RegI64 rs, RegV128 rd) { 9883 masm.splatX2(rs, rd); 9884 } 9885 9886 static void SplatF32x4(MacroAssembler& masm, RegF32 rs, RegV128 rd) { 9887 masm.splatX4(rs, rd); 9888 } 9889 9890 static void SplatF64x2(MacroAssembler& masm, RegF64 rs, RegV128 rd) { 9891 masm.splatX2(rs, rd); 9892 } 9893 9894 // This is the same op independent of lanes: it tests for any nonzero bit. 9895 static void AnyTrue(MacroAssembler& masm, RegV128 rs, RegI32 rd) { 9896 masm.anyTrueSimd128(rs, rd); 9897 } 9898 9899 static void AllTrueI8x16(MacroAssembler& masm, RegV128 rs, RegI32 rd) { 9900 masm.allTrueInt8x16(rs, rd); 9901 } 9902 9903 static void AllTrueI16x8(MacroAssembler& masm, RegV128 rs, RegI32 rd) { 9904 masm.allTrueInt16x8(rs, rd); 9905 } 9906 9907 static void AllTrueI32x4(MacroAssembler& masm, RegV128 rs, RegI32 rd) { 9908 masm.allTrueInt32x4(rs, rd); 9909 } 9910 9911 static void AllTrueI64x2(MacroAssembler& masm, RegV128 rs, RegI32 rd) { 9912 masm.allTrueInt64x2(rs, rd); 9913 } 9914 9915 # if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64) 9916 static void BitmaskI8x16(MacroAssembler& masm, RegV128 rs, RegI32 rd) { 9917 masm.bitmaskInt8x16(rs, rd); 9918 } 9919 9920 static void BitmaskI16x8(MacroAssembler& masm, RegV128 rs, RegI32 rd) { 9921 masm.bitmaskInt16x8(rs, rd); 9922 } 9923 9924 static void BitmaskI32x4(MacroAssembler& masm, RegV128 rs, RegI32 rd) { 9925 masm.bitmaskInt32x4(rs, rd); 9926 } 9927 9928 static void BitmaskI64x2(MacroAssembler& masm, RegV128 rs, RegI32 rd) { 9929 masm.bitmaskInt64x2(rs, rd); 9930 } 9931 # elif defined(JS_CODEGEN_ARM64) 9932 static void BitmaskI8x16(MacroAssembler& masm, RegV128 rs, RegI32 rd, 9933 RegV128 temp) { 9934 masm.bitmaskInt8x16(rs, rd, temp); 9935 } 9936 9937 static void BitmaskI16x8(MacroAssembler& masm, RegV128 rs, RegI32 rd, 9938 RegV128 temp) { 9939 masm.bitmaskInt16x8(rs, rd, temp); 9940 } 9941 9942 static void BitmaskI32x4(MacroAssembler& masm, RegV128 rs, RegI32 rd, 9943 RegV128 temp) { 9944 masm.bitmaskInt32x4(rs, rd, temp); 9945 } 9946 9947 static void BitmaskI64x2(MacroAssembler& masm, RegV128 rs, RegI32 rd, 9948 RegV128 temp) { 9949 masm.bitmaskInt64x2(rs, rd, temp); 9950 } 9951 # endif 9952 9953 static void Swizzle(MacroAssembler& masm, RegV128 rs, RegV128 rsd) { 9954 masm.swizzleInt8x16(rsd, rs, rsd); 9955 } 9956 9957 static void ConvertI32x4ToF32x4(MacroAssembler& masm, RegV128 rs, RegV128 rd) { 9958 masm.convertInt32x4ToFloat32x4(rs, rd); 9959 } 9960 9961 static void ConvertUI32x4ToF32x4(MacroAssembler& masm, RegV128 rs, RegV128 rd) { 9962 masm.unsignedConvertInt32x4ToFloat32x4(rs, rd); 9963 } 9964 9965 static void ConvertF32x4ToI32x4(MacroAssembler& masm, RegV128 rs, RegV128 rd) { 9966 masm.truncSatFloat32x4ToInt32x4(rs, rd); 9967 } 9968 9969 # if defined(JS_CODEGEN_ARM64) 9970 static void ConvertF32x4ToUI32x4(MacroAssembler& masm, RegV128 rs, RegV128 rd) { 9971 masm.unsignedTruncSatFloat32x4ToInt32x4(rs, rd); 9972 } 9973 # else 9974 static void ConvertF32x4ToUI32x4(MacroAssembler& masm, RegV128 rs, RegV128 rd, 9975 RegV128 temp) { 9976 masm.unsignedTruncSatFloat32x4ToInt32x4(rs, rd, temp); 9977 } 9978 # endif // JS_CODEGEN_ARM64 9979 9980 static void ConvertI32x4ToF64x2(MacroAssembler& masm, RegV128 rs, RegV128 rd) { 9981 masm.convertInt32x4ToFloat64x2(rs, rd); 9982 } 9983 9984 static void ConvertUI32x4ToF64x2(MacroAssembler& masm, RegV128 rs, RegV128 rd) { 9985 masm.unsignedConvertInt32x4ToFloat64x2(rs, rd); 9986 } 9987 9988 static void ConvertF64x2ToI32x4(MacroAssembler& masm, RegV128 rs, RegV128 rd, 9989 RegV128 temp) { 9990 masm.truncSatFloat64x2ToInt32x4(rs, rd, temp); 9991 } 9992 9993 static void ConvertF64x2ToUI32x4(MacroAssembler& masm, RegV128 rs, RegV128 rd, 9994 RegV128 temp) { 9995 masm.unsignedTruncSatFloat64x2ToInt32x4(rs, rd, temp); 9996 } 9997 9998 static void DemoteF64x2ToF32x4(MacroAssembler& masm, RegV128 rs, RegV128 rd) { 9999 masm.convertFloat64x2ToFloat32x4(rs, rd); 10000 } 10001 10002 static void PromoteF32x4ToF64x2(MacroAssembler& masm, RegV128 rs, RegV128 rd) { 10003 masm.convertFloat32x4ToFloat64x2(rs, rd); 10004 } 10005 10006 # if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64) 10007 static void BitselectV128(MacroAssembler& masm, RegV128 rhs, RegV128 control, 10008 RegV128 lhsDest, RegV128 temp) { 10009 // Ideally, we would have temp=control, and we can probably get away with 10010 // just doing that, but don't worry about it yet. 10011 masm.bitwiseSelectSimd128(control, lhsDest, rhs, lhsDest, temp); 10012 } 10013 # elif defined(JS_CODEGEN_ARM64) 10014 static void BitselectV128(MacroAssembler& masm, RegV128 rhs, RegV128 control, 10015 RegV128 lhsDest, RegV128 temp) { 10016 // The masm interface is not great for the baseline compiler here, but it's 10017 // optimal for Ion, so just work around it. 10018 masm.moveSimd128(control, temp); 10019 masm.bitwiseSelectSimd128(lhsDest, rhs, temp); 10020 masm.moveSimd128(temp, lhsDest); 10021 } 10022 # endif 10023 10024 # ifdef ENABLE_WASM_RELAXED_SIMD 10025 static void RelaxedMaddF32x4(MacroAssembler& masm, RegV128 rs1, RegV128 rs2, 10026 RegV128 rsd) { 10027 masm.fmaFloat32x4(rs1, rs2, rsd); 10028 } 10029 10030 static void RelaxedNmaddF32x4(MacroAssembler& masm, RegV128 rs1, RegV128 rs2, 10031 RegV128 rsd) { 10032 masm.fnmaFloat32x4(rs1, rs2, rsd); 10033 } 10034 10035 static void RelaxedMaddF64x2(MacroAssembler& masm, RegV128 rs1, RegV128 rs2, 10036 RegV128 rsd) { 10037 masm.fmaFloat64x2(rs1, rs2, rsd); 10038 } 10039 10040 static void RelaxedNmaddF64x2(MacroAssembler& masm, RegV128 rs1, RegV128 rs2, 10041 RegV128 rsd) { 10042 masm.fnmaFloat64x2(rs1, rs2, rsd); 10043 } 10044 10045 static void RelaxedSwizzle(MacroAssembler& masm, RegV128 rs, RegV128 rsd) { 10046 masm.swizzleInt8x16Relaxed(rsd, rs, rsd); 10047 } 10048 10049 static void RelaxedMinF32x4(MacroAssembler& masm, RegV128 rs, RegV128 rsd) { 10050 masm.minFloat32x4Relaxed(rs, rsd); 10051 } 10052 10053 static void RelaxedMaxF32x4(MacroAssembler& masm, RegV128 rs, RegV128 rsd) { 10054 masm.maxFloat32x4Relaxed(rs, rsd); 10055 } 10056 10057 static void RelaxedMinF64x2(MacroAssembler& masm, RegV128 rs, RegV128 rsd) { 10058 masm.minFloat64x2Relaxed(rs, rsd); 10059 } 10060 10061 static void RelaxedMaxF64x2(MacroAssembler& masm, RegV128 rs, RegV128 rsd) { 10062 masm.maxFloat64x2Relaxed(rs, rsd); 10063 } 10064 10065 static void RelaxedConvertF32x4ToI32x4(MacroAssembler& masm, RegV128 rs, 10066 RegV128 rd) { 10067 masm.truncFloat32x4ToInt32x4Relaxed(rs, rd); 10068 } 10069 10070 static void RelaxedConvertF32x4ToUI32x4(MacroAssembler& masm, RegV128 rs, 10071 RegV128 rd) { 10072 masm.unsignedTruncFloat32x4ToInt32x4Relaxed(rs, rd); 10073 } 10074 10075 static void RelaxedConvertF64x2ToI32x4(MacroAssembler& masm, RegV128 rs, 10076 RegV128 rd) { 10077 masm.truncFloat64x2ToInt32x4Relaxed(rs, rd); 10078 } 10079 10080 static void RelaxedConvertF64x2ToUI32x4(MacroAssembler& masm, RegV128 rs, 10081 RegV128 rd) { 10082 masm.unsignedTruncFloat64x2ToInt32x4Relaxed(rs, rd); 10083 } 10084 10085 static void RelaxedQ15MulrS(MacroAssembler& masm, RegV128 rs, RegV128 rsd) { 10086 masm.q15MulrInt16x8Relaxed(rsd, rs, rsd); 10087 } 10088 10089 static void DotI8x16I7x16S(MacroAssembler& masm, RegV128 rs, RegV128 rsd) { 10090 masm.dotInt8x16Int7x16(rsd, rs, rsd); 10091 } 10092 10093 void BaseCompiler::emitDotI8x16I7x16AddS() { 10094 RegV128 rsd = popV128(); 10095 RegV128 rs0, rs1; 10096 pop2xV128(&rs0, &rs1); 10097 # if defined(JS_CODEGEN_ARM64) 10098 RegV128 temp = needV128(); 10099 masm.dotInt8x16Int7x16ThenAdd(rs0, rs1, rsd, temp); 10100 freeV128(temp); 10101 # else 10102 masm.dotInt8x16Int7x16ThenAdd(rs0, rs1, rsd); 10103 # endif 10104 freeV128(rs1); 10105 freeV128(rs0); 10106 pushV128(rsd); 10107 } 10108 # endif // ENABLE_WASM_RELAXED_SIMD 10109 10110 void BaseCompiler::emitVectorAndNot() { 10111 // We want x & ~y but the available operation is ~x & y, so reverse the 10112 // operands. 10113 RegV128 r, rs; 10114 pop2xV128(&r, &rs); 10115 masm.bitwiseNotAndSimd128(r, rs); 10116 freeV128(r); 10117 pushV128(rs); 10118 } 10119 10120 bool BaseCompiler::emitLoadSplat(Scalar::Type viewType) { 10121 LinearMemoryAddress<Nothing> addr; 10122 if (!iter_.readLoadSplat(Scalar::byteSize(viewType), &addr)) { 10123 return false; 10124 } 10125 if (deadCode_) { 10126 return true; 10127 } 10128 MemoryAccessDesc access(addr.memoryIndex, viewType, addr.align, addr.offset, 10129 trapSiteDesc(), hugeMemoryEnabled(addr.memoryIndex)); 10130 loadSplat(&access); 10131 return true; 10132 } 10133 10134 bool BaseCompiler::emitLoadZero(Scalar::Type viewType) { 10135 // LoadZero has the structure of LoadSplat, so reuse the reader. 10136 LinearMemoryAddress<Nothing> addr; 10137 if (!iter_.readLoadSplat(Scalar::byteSize(viewType), &addr)) { 10138 return false; 10139 } 10140 if (deadCode_) { 10141 return true; 10142 } 10143 MemoryAccessDesc access(addr.memoryIndex, viewType, addr.align, addr.offset, 10144 trapSiteDesc(), hugeMemoryEnabled(addr.memoryIndex)); 10145 loadZero(&access); 10146 return true; 10147 } 10148 10149 bool BaseCompiler::emitLoadExtend(Scalar::Type viewType) { 10150 LinearMemoryAddress<Nothing> addr; 10151 if (!iter_.readLoadExtend(&addr)) { 10152 return false; 10153 } 10154 if (deadCode_) { 10155 return true; 10156 } 10157 MemoryAccessDesc access(addr.memoryIndex, Scalar::Int64, addr.align, 10158 addr.offset, trapSiteDesc(), 10159 hugeMemoryEnabled(addr.memoryIndex)); 10160 loadExtend(&access, viewType); 10161 return true; 10162 } 10163 10164 bool BaseCompiler::emitLoadLane(uint32_t laneSize) { 10165 Nothing nothing; 10166 LinearMemoryAddress<Nothing> addr; 10167 uint32_t laneIndex; 10168 if (!iter_.readLoadLane(laneSize, &addr, &laneIndex, ¬hing)) { 10169 return false; 10170 } 10171 if (deadCode_) { 10172 return true; 10173 } 10174 Scalar::Type viewType; 10175 switch (laneSize) { 10176 case 1: 10177 viewType = Scalar::Uint8; 10178 break; 10179 case 2: 10180 viewType = Scalar::Uint16; 10181 break; 10182 case 4: 10183 viewType = Scalar::Int32; 10184 break; 10185 case 8: 10186 viewType = Scalar::Int64; 10187 break; 10188 default: 10189 MOZ_CRASH("unsupported laneSize"); 10190 } 10191 MemoryAccessDesc access(addr.memoryIndex, viewType, addr.align, addr.offset, 10192 trapSiteDesc(), hugeMemoryEnabled(addr.memoryIndex)); 10193 loadLane(&access, laneIndex); 10194 return true; 10195 } 10196 10197 bool BaseCompiler::emitStoreLane(uint32_t laneSize) { 10198 Nothing nothing; 10199 LinearMemoryAddress<Nothing> addr; 10200 uint32_t laneIndex; 10201 if (!iter_.readStoreLane(laneSize, &addr, &laneIndex, ¬hing)) { 10202 return false; 10203 } 10204 if (deadCode_) { 10205 return true; 10206 } 10207 Scalar::Type viewType; 10208 switch (laneSize) { 10209 case 1: 10210 viewType = Scalar::Uint8; 10211 break; 10212 case 2: 10213 viewType = Scalar::Uint16; 10214 break; 10215 case 4: 10216 viewType = Scalar::Int32; 10217 break; 10218 case 8: 10219 viewType = Scalar::Int64; 10220 break; 10221 default: 10222 MOZ_CRASH("unsupported laneSize"); 10223 } 10224 MemoryAccessDesc access(addr.memoryIndex, viewType, addr.align, addr.offset, 10225 trapSiteDesc(), hugeMemoryEnabled(addr.memoryIndex)); 10226 storeLane(&access, laneIndex); 10227 return true; 10228 } 10229 10230 bool BaseCompiler::emitVectorShuffle() { 10231 Nothing unused_a, unused_b; 10232 V128 shuffleMask; 10233 10234 if (!iter_.readVectorShuffle(&unused_a, &unused_b, &shuffleMask)) { 10235 return false; 10236 } 10237 10238 if (deadCode_) { 10239 return true; 10240 } 10241 10242 RegV128 rd, rs; 10243 pop2xV128(&rd, &rs); 10244 10245 masm.shuffleInt8x16(shuffleMask.bytes, rs, rd); 10246 10247 freeV128(rs); 10248 pushV128(rd); 10249 10250 return true; 10251 } 10252 10253 # if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64) 10254 bool BaseCompiler::emitVectorShiftRightI64x2() { 10255 Nothing unused_a, unused_b; 10256 10257 if (!iter_.readVectorShift(&unused_a, &unused_b)) { 10258 return false; 10259 } 10260 10261 if (deadCode_) { 10262 return true; 10263 } 10264 10265 RegI32 count = popI32RhsForShiftI64(); 10266 RegV128 lhsDest = popV128(); 10267 RegI64 tmp = needI64(); 10268 masm.and32(Imm32(63), count); 10269 masm.extractLaneInt64x2(0, lhsDest, tmp); 10270 masm.rshift64Arithmetic(count, tmp); 10271 masm.replaceLaneInt64x2(0, tmp, lhsDest); 10272 masm.extractLaneInt64x2(1, lhsDest, tmp); 10273 masm.rshift64Arithmetic(count, tmp); 10274 masm.replaceLaneInt64x2(1, tmp, lhsDest); 10275 freeI64(tmp); 10276 freeI32(count); 10277 pushV128(lhsDest); 10278 10279 return true; 10280 } 10281 # endif 10282 10283 # ifdef ENABLE_WASM_RELAXED_SIMD 10284 bool BaseCompiler::emitVectorLaneSelect() { 10285 Nothing unused_a, unused_b, unused_c; 10286 10287 if (!iter_.readTernary(ValType::V128, &unused_a, &unused_b, &unused_c)) { 10288 return false; 10289 } 10290 10291 if (deadCode_) { 10292 return true; 10293 } 10294 10295 # if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64) 10296 RegV128 mask = popV128(RegV128(vmm0)); 10297 RegV128 rhsDest = popV128(); 10298 RegV128 lhs = popV128(); 10299 masm.laneSelectSimd128(mask, lhs, rhsDest, rhsDest); 10300 freeV128(lhs); 10301 freeV128(mask); 10302 pushV128(rhsDest); 10303 # elif defined(JS_CODEGEN_ARM64) 10304 RegV128 maskDest = popV128(); 10305 RegV128 rhs = popV128(); 10306 RegV128 lhs = popV128(); 10307 masm.laneSelectSimd128(maskDest, lhs, rhs, maskDest); 10308 freeV128(lhs); 10309 freeV128(rhs); 10310 pushV128(maskDest); 10311 # endif 10312 10313 return true; 10314 } 10315 # endif // ENABLE_WASM_RELAXED_SIMD 10316 #endif // ENABLE_WASM_SIMD 10317 10318 ////////////////////////////////////////////////////////////////////////////// 10319 // 10320 // "Builtin module funcs" - magically imported functions for internal use. 10321 10322 bool BaseCompiler::emitCallBuiltinModuleFunc() { 10323 const BuiltinModuleFunc* builtinModuleFunc; 10324 10325 BaseNothingVector params; 10326 if (!iter_.readCallBuiltinModuleFunc(&builtinModuleFunc, ¶ms)) { 10327 return false; 10328 } 10329 10330 if (deadCode_) { 10331 return true; 10332 } 10333 10334 if (builtinModuleFunc->usesMemory()) { 10335 // The final parameter of an builtinModuleFunc is implicitly the heap base 10336 pushHeapBase(0); 10337 } 10338 10339 // Call the builtinModuleFunc 10340 return emitInstanceCall(*builtinModuleFunc->sig()); 10341 } 10342 10343 ////////////////////////////////////////////////////////////////////////////// 10344 // 10345 // Function bodies - main opcode dispatch loop. 10346 10347 bool BaseCompiler::emitBody() { 10348 AutoCreatedBy acb(masm, "(wasm)BaseCompiler::emitBody"); 10349 10350 MOZ_ASSERT(stackMapGenerator_.framePushedAtEntryToBody.isSome()); 10351 10352 if (!iter_.startFunction(func_.index)) { 10353 return false; 10354 } 10355 10356 initControl(controlItem(), ResultType::Empty()); 10357 10358 #ifdef JS_ION_PERF 10359 bool spewerEnabled = perfSpewer_.needsToRecordInstruction(); 10360 #else 10361 bool spewerEnabled = false; 10362 #endif 10363 bool debugEnabled = compilerEnv_.debugEnabled(); 10364 // Fold the debug and profiler checks into a single bool. 10365 bool hasPerInstrCheck = spewerEnabled || debugEnabled; 10366 10367 for (;;) { 10368 Nothing unused_a, unused_b, unused_c; 10369 (void)unused_a; 10370 (void)unused_b; 10371 (void)unused_c; 10372 10373 #ifdef DEBUG 10374 performRegisterLeakCheck(); 10375 assertStackInvariants(); 10376 #endif 10377 10378 #define dispatchBinary0(doEmit, type) \ 10379 iter_.readBinary(type, &unused_a, &unused_b) && \ 10380 (deadCode_ || (doEmit(), true)) 10381 10382 #define dispatchBinary1(arg1, type) \ 10383 iter_.readBinary(type, &unused_a, &unused_b) && \ 10384 (deadCode_ || (emitBinop(arg1), true)) 10385 10386 #define dispatchBinary2(arg1, arg2, type) \ 10387 iter_.readBinary(type, &unused_a, &unused_b) && \ 10388 (deadCode_ || (emitBinop(arg1, arg2), true)) 10389 10390 #define dispatchBinary3(arg1, arg2, arg3, type) \ 10391 iter_.readBinary(type, &unused_a, &unused_b) && \ 10392 (deadCode_ || (emitBinop(arg1, arg2, arg3), true)) 10393 10394 #define dispatchUnary0(doEmit, type) \ 10395 iter_.readUnary(type, &unused_a) && (deadCode_ || (doEmit(), true)) 10396 10397 #define dispatchUnary1(arg1, type) \ 10398 iter_.readUnary(type, &unused_a) && (deadCode_ || (emitUnop(arg1), true)) 10399 10400 #define dispatchUnary2(arg1, arg2, type) \ 10401 iter_.readUnary(type, &unused_a) && \ 10402 (deadCode_ || (emitUnop(arg1, arg2), true)) 10403 10404 #define dispatchTernary0(doEmit, type) \ 10405 iter_.readTernary(type, &unused_a, &unused_b, &unused_c) && \ 10406 (deadCode_ || (doEmit(), true)) 10407 10408 #define dispatchTernary1(arg1, type) \ 10409 iter_.readTernary(type, &unused_a, &unused_b, &unused_c) && \ 10410 (deadCode_ || (emitTernary(arg1), true)) 10411 10412 #define dispatchTernary2(arg1, type) \ 10413 iter_.readTernary(type, &unused_a, &unused_b, &unused_c) && \ 10414 (deadCode_ || (emitTernaryResultLast(arg1), true)) 10415 10416 #define dispatchComparison0(doEmit, operandType, compareOp) \ 10417 iter_.readComparison(operandType, &unused_a, &unused_b) && \ 10418 (deadCode_ || (doEmit(compareOp, operandType), true)) 10419 10420 #define dispatchConversion0(doEmit, inType, outType) \ 10421 iter_.readConversion(inType, outType, &unused_a) && \ 10422 (deadCode_ || (doEmit(), true)) 10423 10424 #define dispatchConversion1(arg1, inType, outType) \ 10425 iter_.readConversion(inType, outType, &unused_a) && \ 10426 (deadCode_ || (emitUnop(arg1), true)) 10427 10428 #define dispatchConversionOOM(doEmit, inType, outType) \ 10429 iter_.readConversion(inType, outType, &unused_a) && (deadCode_ || doEmit()) 10430 10431 #define dispatchCalloutConversionOOM(doEmit, symbol, inType, outType) \ 10432 iter_.readConversion(inType, outType, &unused_a) && \ 10433 (deadCode_ || doEmit(symbol, inType, outType)) 10434 10435 #define dispatchIntDivCallout(doEmit, symbol, type) \ 10436 iter_.readBinary(type, &unused_a, &unused_b) && \ 10437 (deadCode_ || doEmit(symbol, type)) 10438 10439 #define dispatchVectorBinary(op) \ 10440 iter_.readBinary(ValType::V128, &unused_a, &unused_b) && \ 10441 (deadCode_ || (emitBinop(op), true)) 10442 10443 #define dispatchVectorUnary(op) \ 10444 iter_.readUnary(ValType::V128, &unused_a) && \ 10445 (deadCode_ || (emitUnop(op), true)) 10446 10447 #define dispatchVectorComparison(op, compareOp) \ 10448 iter_.readBinary(ValType::V128, &unused_a, &unused_b) && \ 10449 (deadCode_ || (emitBinop(compareOp, op), true)) 10450 10451 #define dispatchVectorVariableShift(op) \ 10452 iter_.readVectorShift(&unused_a, &unused_b) && \ 10453 (deadCode_ || (emitBinop(op), true)) 10454 10455 #define dispatchExtractLane(op, outType, laneLimit) \ 10456 iter_.readExtractLane(outType, laneLimit, &laneIndex, &unused_a) && \ 10457 (deadCode_ || (emitUnop(laneIndex, op), true)) 10458 10459 #define dispatchReplaceLane(op, inType, laneLimit) \ 10460 iter_.readReplaceLane(inType, laneLimit, &laneIndex, &unused_a, \ 10461 &unused_b) && \ 10462 (deadCode_ || (emitBinop(laneIndex, op), true)) 10463 10464 #define dispatchSplat(op, inType) \ 10465 iter_.readConversion(inType, ValType::V128, &unused_a) && \ 10466 (deadCode_ || (emitUnop(op), true)) 10467 10468 #define dispatchVectorReduction(op) \ 10469 iter_.readConversion(ValType::V128, ValType::I32, &unused_a) && \ 10470 (deadCode_ || (emitUnop(op), true)) 10471 10472 #ifdef DEBUG 10473 // Check that the number of ref-typed entries in the operand stack matches 10474 // reality. 10475 # define CHECK_POINTER_COUNT \ 10476 do { \ 10477 MOZ_ASSERT(countMemRefsOnStk() == stackMapGenerator_.memRefsOnStk); \ 10478 } while (0) 10479 #else 10480 # define CHECK_POINTER_COUNT \ 10481 do { \ 10482 } while (0) 10483 #endif 10484 10485 #define CHECK(E) \ 10486 if (!(E)) return false 10487 #define NEXT() \ 10488 { \ 10489 CHECK_POINTER_COUNT; \ 10490 continue; \ 10491 } 10492 #define CHECK_NEXT(E) \ 10493 if (!(E)) return false; \ 10494 { \ 10495 CHECK_POINTER_COUNT; \ 10496 continue; \ 10497 } 10498 10499 // Opcodes that push more than MaxPushesPerOpcode (anything with multiple 10500 // results) will perform additional reservation. 10501 CHECK(stk_.reserve(stk_.length() + MaxPushesPerOpcode)); 10502 10503 OpBytes op{}; 10504 CHECK(iter_.readOp(&op)); 10505 10506 if (MOZ_UNLIKELY(hasPerInstrCheck)) { 10507 // When compilerEnv_.debugEnabled(), some operators get a breakpoint site. 10508 if (debugEnabled && op.shouldHaveBreakpoint() && !deadCode_) { 10509 if (previousBreakablePoint_ != masm.currentOffset()) { 10510 // TODO sync only registers that can be clobbered by the exit 10511 // prologue/epilogue or disable these registers for use in 10512 // baseline compiler when compilerEnv_.debugEnabled() is set. 10513 // 10514 // This will require the debug stub to save/restore allocatable 10515 // registers. 10516 sync(); 10517 10518 insertBreakablePoint(CallSiteKind::Breakpoint); 10519 if (!createStackMap("debug: per-insn breakpoint")) { 10520 return false; 10521 } 10522 previousBreakablePoint_ = masm.currentOffset(); 10523 } 10524 } 10525 10526 #ifdef JS_ION_PERF 10527 if (spewerEnabled) { 10528 perfSpewer_.recordInstruction(masm, op); 10529 } 10530 #endif 10531 } 10532 10533 // Going below framePushedAtEntryToBody would imply that we've 10534 // popped off the machine stack, part of the frame created by 10535 // beginFunction(). 10536 MOZ_ASSERT(masm.framePushed() >= 10537 stackMapGenerator_.framePushedAtEntryToBody.value()); 10538 10539 // At this point we're definitely not generating code for a function call. 10540 MOZ_ASSERT( 10541 stackMapGenerator_.framePushedExcludingOutboundCallArgs.isNothing()); 10542 10543 switch (op.b0) { 10544 case uint16_t(Op::End): 10545 if (!emitEnd()) { 10546 return false; 10547 } 10548 if (iter_.controlStackEmpty()) { 10549 return true; 10550 } 10551 NEXT(); 10552 10553 // Control opcodes 10554 case uint16_t(Op::Nop): 10555 CHECK_NEXT(iter_.readNop()); 10556 case uint16_t(Op::Drop): 10557 CHECK_NEXT(emitDrop()); 10558 case uint16_t(Op::Block): 10559 CHECK_NEXT(emitBlock()); 10560 case uint16_t(Op::Loop): 10561 CHECK_NEXT(emitLoop()); 10562 case uint16_t(Op::If): 10563 CHECK_NEXT(emitIf()); 10564 case uint16_t(Op::Else): 10565 CHECK_NEXT(emitElse()); 10566 case uint16_t(Op::Try): 10567 CHECK_NEXT(emitTry()); 10568 case uint16_t(Op::Catch): 10569 CHECK_NEXT(emitCatch()); 10570 case uint16_t(Op::CatchAll): 10571 CHECK_NEXT(emitCatchAll()); 10572 case uint16_t(Op::Delegate): 10573 CHECK(emitDelegate()); 10574 iter_.popDelegate(); 10575 NEXT(); 10576 case uint16_t(Op::Throw): 10577 CHECK_NEXT(emitThrow()); 10578 case uint16_t(Op::Rethrow): 10579 CHECK_NEXT(emitRethrow()); 10580 case uint16_t(Op::ThrowRef): 10581 CHECK_NEXT(emitThrowRef()); 10582 case uint16_t(Op::TryTable): 10583 CHECK_NEXT(emitTryTable()); 10584 case uint16_t(Op::Br): 10585 CHECK_NEXT(emitBr()); 10586 case uint16_t(Op::BrIf): 10587 CHECK_NEXT(emitBrIf()); 10588 case uint16_t(Op::BrTable): 10589 CHECK_NEXT(emitBrTable()); 10590 case uint16_t(Op::Return): 10591 CHECK_NEXT(emitReturn()); 10592 case uint16_t(Op::Unreachable): 10593 CHECK(iter_.readUnreachable()); 10594 if (!deadCode_) { 10595 trap(Trap::Unreachable); 10596 deadCode_ = true; 10597 } 10598 NEXT(); 10599 10600 // Calls 10601 case uint16_t(Op::Call): 10602 CHECK_NEXT(emitCall()); 10603 case uint16_t(Op::CallIndirect): 10604 CHECK_NEXT(emitCallIndirect()); 10605 case uint16_t(Op::ReturnCall): 10606 CHECK_NEXT(emitReturnCall()); 10607 case uint16_t(Op::ReturnCallIndirect): 10608 CHECK_NEXT(emitReturnCallIndirect()); 10609 case uint16_t(Op::CallRef): 10610 CHECK_NEXT(emitCallRef()); 10611 case uint16_t(Op::ReturnCallRef): 10612 CHECK_NEXT(emitReturnCallRef()); 10613 10614 // Locals and globals 10615 case uint16_t(Op::LocalGet): 10616 CHECK_NEXT(emitGetLocal()); 10617 case uint16_t(Op::LocalSet): 10618 CHECK_NEXT(emitSetLocal()); 10619 case uint16_t(Op::LocalTee): 10620 CHECK_NEXT(emitTeeLocal()); 10621 case uint16_t(Op::GlobalGet): 10622 CHECK_NEXT(emitGetGlobal()); 10623 case uint16_t(Op::GlobalSet): 10624 CHECK_NEXT(emitSetGlobal()); 10625 case uint16_t(Op::TableGet): 10626 CHECK_NEXT(emitTableGet()); 10627 case uint16_t(Op::TableSet): 10628 CHECK_NEXT(emitTableSet()); 10629 10630 // Select 10631 case uint16_t(Op::SelectNumeric): 10632 CHECK_NEXT(emitSelect(/*typed*/ false)); 10633 case uint16_t(Op::SelectTyped): 10634 CHECK_NEXT(emitSelect(/*typed*/ true)); 10635 10636 // I32 10637 case uint16_t(Op::I32Const): { 10638 int32_t i32; 10639 CHECK(iter_.readI32Const(&i32)); 10640 if (!deadCode_) { 10641 pushI32(i32); 10642 } 10643 NEXT(); 10644 } 10645 case uint16_t(Op::I32Add): 10646 CHECK_NEXT(dispatchBinary2(AddI32, AddImmI32, ValType::I32)); 10647 case uint16_t(Op::I32Sub): 10648 CHECK_NEXT(dispatchBinary2(SubI32, SubImmI32, ValType::I32)); 10649 case uint16_t(Op::I32Mul): 10650 CHECK_NEXT(dispatchBinary1(MulI32, ValType::I32)); 10651 case uint16_t(Op::I32DivS): 10652 CHECK_NEXT(dispatchBinary0(emitQuotientI32, ValType::I32)); 10653 case uint16_t(Op::I32DivU): 10654 CHECK_NEXT(dispatchBinary0(emitQuotientU32, ValType::I32)); 10655 case uint16_t(Op::I32RemS): 10656 CHECK_NEXT(dispatchBinary0(emitRemainderI32, ValType::I32)); 10657 case uint16_t(Op::I32RemU): 10658 CHECK_NEXT(dispatchBinary0(emitRemainderU32, ValType::I32)); 10659 case uint16_t(Op::I32Eqz): 10660 CHECK_NEXT(dispatchConversion0(emitEqzI32, ValType::I32, ValType::I32)); 10661 case uint16_t(Op::I32TruncF32S): 10662 CHECK_NEXT(dispatchConversionOOM(emitTruncateF32ToI32<0>, ValType::F32, 10663 ValType::I32)); 10664 case uint16_t(Op::I32TruncF32U): 10665 CHECK_NEXT(dispatchConversionOOM(emitTruncateF32ToI32<TRUNC_UNSIGNED>, 10666 ValType::F32, ValType::I32)); 10667 case uint16_t(Op::I32TruncF64S): 10668 CHECK_NEXT(dispatchConversionOOM(emitTruncateF64ToI32<0>, ValType::F64, 10669 ValType::I32)); 10670 case uint16_t(Op::I32TruncF64U): 10671 CHECK_NEXT(dispatchConversionOOM(emitTruncateF64ToI32<TRUNC_UNSIGNED>, 10672 ValType::F64, ValType::I32)); 10673 case uint16_t(Op::I32WrapI64): 10674 CHECK_NEXT( 10675 dispatchConversion1(WrapI64ToI32, ValType::I64, ValType::I32)); 10676 case uint16_t(Op::I32ReinterpretF32): 10677 CHECK_NEXT(dispatchConversion1(ReinterpretF32AsI32, ValType::F32, 10678 ValType::I32)); 10679 case uint16_t(Op::I32Clz): 10680 CHECK_NEXT(dispatchUnary1(ClzI32, ValType::I32)); 10681 case uint16_t(Op::I32Ctz): 10682 CHECK_NEXT(dispatchUnary1(CtzI32, ValType::I32)); 10683 case uint16_t(Op::I32Popcnt): 10684 CHECK_NEXT(dispatchUnary2(PopcntI32, PopcntTemp, ValType::I32)); 10685 case uint16_t(Op::I32Or): 10686 CHECK_NEXT(dispatchBinary2(OrI32, OrImmI32, ValType::I32)); 10687 case uint16_t(Op::I32And): 10688 CHECK_NEXT(dispatchBinary2(AndI32, AndImmI32, ValType::I32)); 10689 case uint16_t(Op::I32Xor): 10690 CHECK_NEXT(dispatchBinary2(XorI32, XorImmI32, ValType::I32)); 10691 case uint16_t(Op::I32Shl): 10692 CHECK_NEXT(dispatchBinary3( 10693 ShlI32, ShlImmI32, &BaseCompiler::popI32RhsForShift, ValType::I32)); 10694 case uint16_t(Op::I32ShrS): 10695 CHECK_NEXT(dispatchBinary3( 10696 ShrI32, ShrImmI32, &BaseCompiler::popI32RhsForShift, ValType::I32)); 10697 case uint16_t(Op::I32ShrU): 10698 CHECK_NEXT(dispatchBinary3(ShrUI32, ShrUImmI32, 10699 &BaseCompiler::popI32RhsForShift, 10700 ValType::I32)); 10701 case uint16_t(Op::I32Load8S): 10702 CHECK_NEXT(emitLoad(ValType::I32, Scalar::Int8)); 10703 case uint16_t(Op::I32Load8U): 10704 CHECK_NEXT(emitLoad(ValType::I32, Scalar::Uint8)); 10705 case uint16_t(Op::I32Load16S): 10706 CHECK_NEXT(emitLoad(ValType::I32, Scalar::Int16)); 10707 case uint16_t(Op::I32Load16U): 10708 CHECK_NEXT(emitLoad(ValType::I32, Scalar::Uint16)); 10709 case uint16_t(Op::I32Load): 10710 CHECK_NEXT(emitLoad(ValType::I32, Scalar::Int32)); 10711 case uint16_t(Op::I32Store8): 10712 CHECK_NEXT(emitStore(ValType::I32, Scalar::Int8)); 10713 case uint16_t(Op::I32Store16): 10714 CHECK_NEXT(emitStore(ValType::I32, Scalar::Int16)); 10715 case uint16_t(Op::I32Store): 10716 CHECK_NEXT(emitStore(ValType::I32, Scalar::Int32)); 10717 case uint16_t(Op::I32Rotr): 10718 CHECK_NEXT(dispatchBinary3(RotrI32, RotrImmI32, 10719 &BaseCompiler::popI32RhsForRotate, 10720 ValType::I32)); 10721 case uint16_t(Op::I32Rotl): 10722 CHECK_NEXT(dispatchBinary3(RotlI32, RotlImmI32, 10723 &BaseCompiler::popI32RhsForRotate, 10724 ValType::I32)); 10725 10726 // I64 10727 case uint16_t(Op::I64Const): { 10728 int64_t i64; 10729 CHECK(iter_.readI64Const(&i64)); 10730 if (!deadCode_) { 10731 pushI64(i64); 10732 } 10733 NEXT(); 10734 } 10735 case uint16_t(Op::I64Add): 10736 CHECK_NEXT(dispatchBinary2(AddI64, AddImmI64, ValType::I64)); 10737 case uint16_t(Op::I64Sub): 10738 CHECK_NEXT(dispatchBinary2(SubI64, SubImmI64, ValType::I64)); 10739 case uint16_t(Op::I64Mul): 10740 CHECK_NEXT(dispatchBinary0(emitMultiplyI64, ValType::I64)); 10741 case uint16_t(Op::I64DivS): 10742 #ifdef RABALDR_INT_DIV_I64_CALLOUT 10743 CHECK_NEXT(dispatchIntDivCallout( 10744 emitDivOrModI64BuiltinCall, SymbolicAddress::DivI64, ValType::I64)); 10745 #else 10746 CHECK_NEXT(dispatchBinary0(emitQuotientI64, ValType::I64)); 10747 #endif 10748 case uint16_t(Op::I64DivU): 10749 #ifdef RABALDR_INT_DIV_I64_CALLOUT 10750 CHECK_NEXT(dispatchIntDivCallout(emitDivOrModI64BuiltinCall, 10751 SymbolicAddress::UDivI64, 10752 ValType::I64)); 10753 #else 10754 CHECK_NEXT(dispatchBinary0(emitQuotientU64, ValType::I64)); 10755 #endif 10756 case uint16_t(Op::I64RemS): 10757 #ifdef RABALDR_INT_DIV_I64_CALLOUT 10758 CHECK_NEXT(dispatchIntDivCallout( 10759 emitDivOrModI64BuiltinCall, SymbolicAddress::ModI64, ValType::I64)); 10760 #else 10761 CHECK_NEXT(dispatchBinary0(emitRemainderI64, ValType::I64)); 10762 #endif 10763 case uint16_t(Op::I64RemU): 10764 #ifdef RABALDR_INT_DIV_I64_CALLOUT 10765 CHECK_NEXT(dispatchIntDivCallout(emitDivOrModI64BuiltinCall, 10766 SymbolicAddress::UModI64, 10767 ValType::I64)); 10768 #else 10769 CHECK_NEXT(dispatchBinary0(emitRemainderU64, ValType::I64)); 10770 #endif 10771 case uint16_t(Op::I64TruncF32S): 10772 #ifdef RABALDR_FLOAT_TO_I64_CALLOUT 10773 CHECK_NEXT( 10774 dispatchCalloutConversionOOM(emitConvertFloatingToInt64Callout, 10775 SymbolicAddress::TruncateDoubleToInt64, 10776 ValType::F32, ValType::I64)); 10777 #else 10778 CHECK_NEXT(dispatchConversionOOM(emitTruncateF32ToI64<0>, ValType::F32, 10779 ValType::I64)); 10780 #endif 10781 case uint16_t(Op::I64TruncF32U): 10782 #ifdef RABALDR_FLOAT_TO_I64_CALLOUT 10783 CHECK_NEXT(dispatchCalloutConversionOOM( 10784 emitConvertFloatingToInt64Callout, 10785 SymbolicAddress::TruncateDoubleToUint64, ValType::F32, 10786 ValType::I64)); 10787 #else 10788 CHECK_NEXT(dispatchConversionOOM(emitTruncateF32ToI64<TRUNC_UNSIGNED>, 10789 ValType::F32, ValType::I64)); 10790 #endif 10791 case uint16_t(Op::I64TruncF64S): 10792 #ifdef RABALDR_FLOAT_TO_I64_CALLOUT 10793 CHECK_NEXT( 10794 dispatchCalloutConversionOOM(emitConvertFloatingToInt64Callout, 10795 SymbolicAddress::TruncateDoubleToInt64, 10796 ValType::F64, ValType::I64)); 10797 #else 10798 CHECK_NEXT(dispatchConversionOOM(emitTruncateF64ToI64<0>, ValType::F64, 10799 ValType::I64)); 10800 #endif 10801 case uint16_t(Op::I64TruncF64U): 10802 #ifdef RABALDR_FLOAT_TO_I64_CALLOUT 10803 CHECK_NEXT(dispatchCalloutConversionOOM( 10804 emitConvertFloatingToInt64Callout, 10805 SymbolicAddress::TruncateDoubleToUint64, ValType::F64, 10806 ValType::I64)); 10807 #else 10808 CHECK_NEXT(dispatchConversionOOM(emitTruncateF64ToI64<TRUNC_UNSIGNED>, 10809 ValType::F64, ValType::I64)); 10810 #endif 10811 case uint16_t(Op::I64ExtendI32S): 10812 CHECK_NEXT(dispatchConversion0(emitExtendI32ToI64, ValType::I32, 10813 ValType::I64)); 10814 case uint16_t(Op::I64ExtendI32U): 10815 CHECK_NEXT(dispatchConversion0(emitExtendU32ToI64, ValType::I32, 10816 ValType::I64)); 10817 case uint16_t(Op::I64ReinterpretF64): 10818 CHECK_NEXT(dispatchConversion1(ReinterpretF64AsI64, ValType::F64, 10819 ValType::I64)); 10820 case uint16_t(Op::I64Or): 10821 CHECK_NEXT(dispatchBinary2(OrI64, OrImmI64, ValType::I64)); 10822 case uint16_t(Op::I64And): 10823 CHECK_NEXT(dispatchBinary2(AndI64, AndImmI64, ValType::I64)); 10824 case uint16_t(Op::I64Xor): 10825 CHECK_NEXT(dispatchBinary2(XorI64, XorImmI64, ValType::I64)); 10826 case uint16_t(Op::I64Shl): 10827 CHECK_NEXT(dispatchBinary3( 10828 ShlI64, ShlImmI64, &BaseCompiler::popI64RhsForShift, ValType::I64)); 10829 case uint16_t(Op::I64ShrS): 10830 CHECK_NEXT(dispatchBinary3( 10831 ShrI64, ShrImmI64, &BaseCompiler::popI64RhsForShift, ValType::I64)); 10832 case uint16_t(Op::I64ShrU): 10833 CHECK_NEXT(dispatchBinary3(ShrUI64, ShrUImmI64, 10834 &BaseCompiler::popI64RhsForShift, 10835 ValType::I64)); 10836 case uint16_t(Op::I64Rotr): 10837 CHECK_NEXT(dispatchBinary0(emitRotrI64, ValType::I64)); 10838 case uint16_t(Op::I64Rotl): 10839 CHECK_NEXT(dispatchBinary0(emitRotlI64, ValType::I64)); 10840 case uint16_t(Op::I64Clz): 10841 CHECK_NEXT(dispatchUnary1(ClzI64, ValType::I64)); 10842 case uint16_t(Op::I64Ctz): 10843 CHECK_NEXT(dispatchUnary1(CtzI64, ValType::I64)); 10844 case uint16_t(Op::I64Popcnt): 10845 CHECK_NEXT(dispatchUnary2(PopcntI64, PopcntTemp, ValType::I64)); 10846 case uint16_t(Op::I64Eqz): 10847 CHECK_NEXT(dispatchConversion0(emitEqzI64, ValType::I64, ValType::I32)); 10848 case uint16_t(Op::I64Load8S): 10849 CHECK_NEXT(emitLoad(ValType::I64, Scalar::Int8)); 10850 case uint16_t(Op::I64Load16S): 10851 CHECK_NEXT(emitLoad(ValType::I64, Scalar::Int16)); 10852 case uint16_t(Op::I64Load32S): 10853 CHECK_NEXT(emitLoad(ValType::I64, Scalar::Int32)); 10854 case uint16_t(Op::I64Load8U): 10855 CHECK_NEXT(emitLoad(ValType::I64, Scalar::Uint8)); 10856 case uint16_t(Op::I64Load16U): 10857 CHECK_NEXT(emitLoad(ValType::I64, Scalar::Uint16)); 10858 case uint16_t(Op::I64Load32U): 10859 CHECK_NEXT(emitLoad(ValType::I64, Scalar::Uint32)); 10860 case uint16_t(Op::I64Load): 10861 CHECK_NEXT(emitLoad(ValType::I64, Scalar::Int64)); 10862 case uint16_t(Op::I64Store8): 10863 CHECK_NEXT(emitStore(ValType::I64, Scalar::Int8)); 10864 case uint16_t(Op::I64Store16): 10865 CHECK_NEXT(emitStore(ValType::I64, Scalar::Int16)); 10866 case uint16_t(Op::I64Store32): 10867 CHECK_NEXT(emitStore(ValType::I64, Scalar::Int32)); 10868 case uint16_t(Op::I64Store): 10869 CHECK_NEXT(emitStore(ValType::I64, Scalar::Int64)); 10870 10871 // F32 10872 case uint16_t(Op::F32Const): { 10873 float f32; 10874 CHECK(iter_.readF32Const(&f32)); 10875 if (!deadCode_) { 10876 pushF32(f32); 10877 } 10878 NEXT(); 10879 } 10880 case uint16_t(Op::F32Add): 10881 CHECK_NEXT(dispatchBinary1(AddF32, ValType::F32)) 10882 case uint16_t(Op::F32Sub): 10883 CHECK_NEXT(dispatchBinary1(SubF32, ValType::F32)); 10884 case uint16_t(Op::F32Mul): 10885 CHECK_NEXT(dispatchBinary1(MulF32, ValType::F32)); 10886 case uint16_t(Op::F32Div): 10887 CHECK_NEXT(dispatchBinary1(DivF32, ValType::F32)); 10888 case uint16_t(Op::F32Min): 10889 CHECK_NEXT(dispatchBinary1(MinF32, ValType::F32)); 10890 case uint16_t(Op::F32Max): 10891 CHECK_NEXT(dispatchBinary1(MaxF32, ValType::F32)); 10892 case uint16_t(Op::F32Neg): 10893 CHECK_NEXT(dispatchUnary1(NegateF32, ValType::F32)); 10894 case uint16_t(Op::F32Abs): 10895 CHECK_NEXT(dispatchUnary1(AbsF32, ValType::F32)); 10896 case uint16_t(Op::F32Sqrt): 10897 CHECK_NEXT(dispatchUnary1(SqrtF32, ValType::F32)); 10898 case uint16_t(Op::F32Ceil): 10899 CHECK_NEXT( 10900 emitUnaryMathBuiltinCall(SymbolicAddress::CeilF, ValType::F32)); 10901 case uint16_t(Op::F32Floor): 10902 CHECK_NEXT( 10903 emitUnaryMathBuiltinCall(SymbolicAddress::FloorF, ValType::F32)); 10904 case uint16_t(Op::F32DemoteF64): 10905 CHECK_NEXT( 10906 dispatchConversion1(ConvertF64ToF32, ValType::F64, ValType::F32)); 10907 case uint16_t(Op::F32ConvertI32S): 10908 CHECK_NEXT( 10909 dispatchConversion1(ConvertI32ToF32, ValType::I32, ValType::F32)); 10910 case uint16_t(Op::F32ConvertI32U): 10911 CHECK_NEXT( 10912 dispatchConversion1(ConvertU32ToF32, ValType::I32, ValType::F32)); 10913 case uint16_t(Op::F32ConvertI64S): 10914 #ifdef RABALDR_I64_TO_FLOAT_CALLOUT 10915 CHECK_NEXT(dispatchCalloutConversionOOM( 10916 emitConvertInt64ToFloatingCallout, SymbolicAddress::Int64ToFloat32, 10917 ValType::I64, ValType::F32)); 10918 #else 10919 CHECK_NEXT( 10920 dispatchConversion1(ConvertI64ToF32, ValType::I64, ValType::F32)); 10921 #endif 10922 case uint16_t(Op::F32ConvertI64U): 10923 #ifdef RABALDR_I64_TO_FLOAT_CALLOUT 10924 CHECK_NEXT(dispatchCalloutConversionOOM( 10925 emitConvertInt64ToFloatingCallout, SymbolicAddress::Uint64ToFloat32, 10926 ValType::I64, ValType::F32)); 10927 #else 10928 CHECK_NEXT(dispatchConversion0(emitConvertU64ToF32, ValType::I64, 10929 ValType::F32)); 10930 #endif 10931 case uint16_t(Op::F32ReinterpretI32): 10932 CHECK_NEXT(dispatchConversion1(ReinterpretI32AsF32, ValType::I32, 10933 ValType::F32)); 10934 case uint16_t(Op::F32Load): 10935 CHECK_NEXT(emitLoad(ValType::F32, Scalar::Float32)); 10936 case uint16_t(Op::F32Store): 10937 CHECK_NEXT(emitStore(ValType::F32, Scalar::Float32)); 10938 case uint16_t(Op::F32CopySign): 10939 CHECK_NEXT(dispatchBinary1(CopysignF32, ValType::F32)); 10940 case uint16_t(Op::F32Nearest): 10941 CHECK_NEXT(emitUnaryMathBuiltinCall(SymbolicAddress::NearbyIntF, 10942 ValType::F32)); 10943 case uint16_t(Op::F32Trunc): 10944 CHECK_NEXT( 10945 emitUnaryMathBuiltinCall(SymbolicAddress::TruncF, ValType::F32)); 10946 10947 // F64 10948 case uint16_t(Op::F64Const): { 10949 double f64; 10950 CHECK(iter_.readF64Const(&f64)); 10951 if (!deadCode_) { 10952 pushF64(f64); 10953 } 10954 NEXT(); 10955 } 10956 case uint16_t(Op::F64Add): 10957 CHECK_NEXT(dispatchBinary1(AddF64, ValType::F64)) 10958 case uint16_t(Op::F64Sub): 10959 CHECK_NEXT(dispatchBinary1(SubF64, ValType::F64)); 10960 case uint16_t(Op::F64Mul): 10961 CHECK_NEXT(dispatchBinary1(MulF64, ValType::F64)); 10962 case uint16_t(Op::F64Div): 10963 CHECK_NEXT(dispatchBinary1(DivF64, ValType::F64)); 10964 case uint16_t(Op::F64Min): 10965 CHECK_NEXT(dispatchBinary1(MinF64, ValType::F64)); 10966 case uint16_t(Op::F64Max): 10967 CHECK_NEXT(dispatchBinary1(MaxF64, ValType::F64)); 10968 case uint16_t(Op::F64Neg): 10969 CHECK_NEXT(dispatchUnary1(NegateF64, ValType::F64)); 10970 case uint16_t(Op::F64Abs): 10971 CHECK_NEXT(dispatchUnary1(AbsF64, ValType::F64)); 10972 case uint16_t(Op::F64Sqrt): 10973 CHECK_NEXT(dispatchUnary1(SqrtF64, ValType::F64)); 10974 case uint16_t(Op::F64Ceil): 10975 CHECK_NEXT( 10976 emitUnaryMathBuiltinCall(SymbolicAddress::CeilD, ValType::F64)); 10977 case uint16_t(Op::F64Floor): 10978 CHECK_NEXT( 10979 emitUnaryMathBuiltinCall(SymbolicAddress::FloorD, ValType::F64)); 10980 case uint16_t(Op::F64PromoteF32): 10981 CHECK_NEXT( 10982 dispatchConversion1(ConvertF32ToF64, ValType::F32, ValType::F64)); 10983 case uint16_t(Op::F64ConvertI32S): 10984 CHECK_NEXT( 10985 dispatchConversion1(ConvertI32ToF64, ValType::I32, ValType::F64)); 10986 case uint16_t(Op::F64ConvertI32U): 10987 CHECK_NEXT( 10988 dispatchConversion1(ConvertU32ToF64, ValType::I32, ValType::F64)); 10989 case uint16_t(Op::F64ConvertI64S): 10990 #ifdef RABALDR_I64_TO_FLOAT_CALLOUT 10991 CHECK_NEXT(dispatchCalloutConversionOOM( 10992 emitConvertInt64ToFloatingCallout, SymbolicAddress::Int64ToDouble, 10993 ValType::I64, ValType::F64)); 10994 #else 10995 CHECK_NEXT( 10996 dispatchConversion1(ConvertI64ToF64, ValType::I64, ValType::F64)); 10997 #endif 10998 case uint16_t(Op::F64ConvertI64U): 10999 #ifdef RABALDR_I64_TO_FLOAT_CALLOUT 11000 CHECK_NEXT(dispatchCalloutConversionOOM( 11001 emitConvertInt64ToFloatingCallout, SymbolicAddress::Uint64ToDouble, 11002 ValType::I64, ValType::F64)); 11003 #else 11004 CHECK_NEXT(dispatchConversion0(emitConvertU64ToF64, ValType::I64, 11005 ValType::F64)); 11006 #endif 11007 case uint16_t(Op::F64Load): 11008 CHECK_NEXT(emitLoad(ValType::F64, Scalar::Float64)); 11009 case uint16_t(Op::F64Store): 11010 CHECK_NEXT(emitStore(ValType::F64, Scalar::Float64)); 11011 case uint16_t(Op::F64ReinterpretI64): 11012 CHECK_NEXT(dispatchConversion1(ReinterpretI64AsF64, ValType::I64, 11013 ValType::F64)); 11014 case uint16_t(Op::F64CopySign): 11015 CHECK_NEXT(dispatchBinary1(CopysignF64, ValType::F64)); 11016 case uint16_t(Op::F64Nearest): 11017 CHECK_NEXT(emitUnaryMathBuiltinCall(SymbolicAddress::NearbyIntD, 11018 ValType::F64)); 11019 case uint16_t(Op::F64Trunc): 11020 CHECK_NEXT( 11021 emitUnaryMathBuiltinCall(SymbolicAddress::TruncD, ValType::F64)); 11022 11023 // Comparisons 11024 case uint16_t(Op::I32Eq): 11025 CHECK_NEXT(dispatchComparison0(emitCompareI32, ValType::I32, 11026 Assembler::Equal)); 11027 case uint16_t(Op::I32Ne): 11028 CHECK_NEXT(dispatchComparison0(emitCompareI32, ValType::I32, 11029 Assembler::NotEqual)); 11030 case uint16_t(Op::I32LtS): 11031 CHECK_NEXT(dispatchComparison0(emitCompareI32, ValType::I32, 11032 Assembler::LessThan)); 11033 case uint16_t(Op::I32LeS): 11034 CHECK_NEXT(dispatchComparison0(emitCompareI32, ValType::I32, 11035 Assembler::LessThanOrEqual)); 11036 case uint16_t(Op::I32GtS): 11037 CHECK_NEXT(dispatchComparison0(emitCompareI32, ValType::I32, 11038 Assembler::GreaterThan)); 11039 case uint16_t(Op::I32GeS): 11040 CHECK_NEXT(dispatchComparison0(emitCompareI32, ValType::I32, 11041 Assembler::GreaterThanOrEqual)); 11042 case uint16_t(Op::I32LtU): 11043 CHECK_NEXT(dispatchComparison0(emitCompareI32, ValType::I32, 11044 Assembler::Below)); 11045 case uint16_t(Op::I32LeU): 11046 CHECK_NEXT(dispatchComparison0(emitCompareI32, ValType::I32, 11047 Assembler::BelowOrEqual)); 11048 case uint16_t(Op::I32GtU): 11049 CHECK_NEXT(dispatchComparison0(emitCompareI32, ValType::I32, 11050 Assembler::Above)); 11051 case uint16_t(Op::I32GeU): 11052 CHECK_NEXT(dispatchComparison0(emitCompareI32, ValType::I32, 11053 Assembler::AboveOrEqual)); 11054 case uint16_t(Op::I64Eq): 11055 CHECK_NEXT(dispatchComparison0(emitCompareI64, ValType::I64, 11056 Assembler::Equal)); 11057 case uint16_t(Op::I64Ne): 11058 CHECK_NEXT(dispatchComparison0(emitCompareI64, ValType::I64, 11059 Assembler::NotEqual)); 11060 case uint16_t(Op::I64LtS): 11061 CHECK_NEXT(dispatchComparison0(emitCompareI64, ValType::I64, 11062 Assembler::LessThan)); 11063 case uint16_t(Op::I64LeS): 11064 CHECK_NEXT(dispatchComparison0(emitCompareI64, ValType::I64, 11065 Assembler::LessThanOrEqual)); 11066 case uint16_t(Op::I64GtS): 11067 CHECK_NEXT(dispatchComparison0(emitCompareI64, ValType::I64, 11068 Assembler::GreaterThan)); 11069 case uint16_t(Op::I64GeS): 11070 CHECK_NEXT(dispatchComparison0(emitCompareI64, ValType::I64, 11071 Assembler::GreaterThanOrEqual)); 11072 case uint16_t(Op::I64LtU): 11073 CHECK_NEXT(dispatchComparison0(emitCompareI64, ValType::I64, 11074 Assembler::Below)); 11075 case uint16_t(Op::I64LeU): 11076 CHECK_NEXT(dispatchComparison0(emitCompareI64, ValType::I64, 11077 Assembler::BelowOrEqual)); 11078 case uint16_t(Op::I64GtU): 11079 CHECK_NEXT(dispatchComparison0(emitCompareI64, ValType::I64, 11080 Assembler::Above)); 11081 case uint16_t(Op::I64GeU): 11082 CHECK_NEXT(dispatchComparison0(emitCompareI64, ValType::I64, 11083 Assembler::AboveOrEqual)); 11084 case uint16_t(Op::F32Eq): 11085 CHECK_NEXT(dispatchComparison0(emitCompareF32, ValType::F32, 11086 Assembler::DoubleEqual)); 11087 case uint16_t(Op::F32Ne): 11088 CHECK_NEXT(dispatchComparison0(emitCompareF32, ValType::F32, 11089 Assembler::DoubleNotEqualOrUnordered)); 11090 case uint16_t(Op::F32Lt): 11091 CHECK_NEXT(dispatchComparison0(emitCompareF32, ValType::F32, 11092 Assembler::DoubleLessThan)); 11093 case uint16_t(Op::F32Le): 11094 CHECK_NEXT(dispatchComparison0(emitCompareF32, ValType::F32, 11095 Assembler::DoubleLessThanOrEqual)); 11096 case uint16_t(Op::F32Gt): 11097 CHECK_NEXT(dispatchComparison0(emitCompareF32, ValType::F32, 11098 Assembler::DoubleGreaterThan)); 11099 case uint16_t(Op::F32Ge): 11100 CHECK_NEXT(dispatchComparison0(emitCompareF32, ValType::F32, 11101 Assembler::DoubleGreaterThanOrEqual)); 11102 case uint16_t(Op::F64Eq): 11103 CHECK_NEXT(dispatchComparison0(emitCompareF64, ValType::F64, 11104 Assembler::DoubleEqual)); 11105 case uint16_t(Op::F64Ne): 11106 CHECK_NEXT(dispatchComparison0(emitCompareF64, ValType::F64, 11107 Assembler::DoubleNotEqualOrUnordered)); 11108 case uint16_t(Op::F64Lt): 11109 CHECK_NEXT(dispatchComparison0(emitCompareF64, ValType::F64, 11110 Assembler::DoubleLessThan)); 11111 case uint16_t(Op::F64Le): 11112 CHECK_NEXT(dispatchComparison0(emitCompareF64, ValType::F64, 11113 Assembler::DoubleLessThanOrEqual)); 11114 case uint16_t(Op::F64Gt): 11115 CHECK_NEXT(dispatchComparison0(emitCompareF64, ValType::F64, 11116 Assembler::DoubleGreaterThan)); 11117 case uint16_t(Op::F64Ge): 11118 CHECK_NEXT(dispatchComparison0(emitCompareF64, ValType::F64, 11119 Assembler::DoubleGreaterThanOrEqual)); 11120 11121 // Sign extensions 11122 case uint16_t(Op::I32Extend8S): 11123 CHECK_NEXT( 11124 dispatchConversion1(ExtendI32_8, ValType::I32, ValType::I32)); 11125 case uint16_t(Op::I32Extend16S): 11126 CHECK_NEXT( 11127 dispatchConversion1(ExtendI32_16, ValType::I32, ValType::I32)); 11128 case uint16_t(Op::I64Extend8S): 11129 CHECK_NEXT( 11130 dispatchConversion0(emitExtendI64_8, ValType::I64, ValType::I64)); 11131 case uint16_t(Op::I64Extend16S): 11132 CHECK_NEXT( 11133 dispatchConversion0(emitExtendI64_16, ValType::I64, ValType::I64)); 11134 case uint16_t(Op::I64Extend32S): 11135 CHECK_NEXT( 11136 dispatchConversion0(emitExtendI64_32, ValType::I64, ValType::I64)); 11137 11138 // Memory Related 11139 case uint16_t(Op::MemoryGrow): 11140 CHECK_NEXT(emitMemoryGrow()); 11141 case uint16_t(Op::MemorySize): 11142 CHECK_NEXT(emitMemorySize()); 11143 11144 case uint16_t(Op::RefAsNonNull): 11145 CHECK_NEXT(emitRefAsNonNull()); 11146 case uint16_t(Op::BrOnNull): 11147 CHECK_NEXT(emitBrOnNull()); 11148 case uint16_t(Op::BrOnNonNull): 11149 CHECK_NEXT(emitBrOnNonNull()); 11150 case uint16_t(Op::RefEq): 11151 CHECK_NEXT(dispatchComparison0(emitCompareRef, RefType::eq(), 11152 Assembler::Equal)); 11153 case uint16_t(Op::RefFunc): 11154 CHECK_NEXT(emitRefFunc()); 11155 break; 11156 case uint16_t(Op::RefNull): 11157 CHECK_NEXT(emitRefNull()); 11158 break; 11159 case uint16_t(Op::RefIsNull): 11160 CHECK_NEXT(emitRefIsNull()); 11161 break; 11162 11163 // "GC" operations 11164 case uint16_t(Op::GcPrefix): { 11165 switch (op.b1) { 11166 case uint32_t(GcOp::StructNew): 11167 CHECK_NEXT(emitStructNew()); 11168 case uint32_t(GcOp::StructNewDefault): 11169 CHECK_NEXT(emitStructNewDefault()); 11170 case uint32_t(GcOp::StructGet): 11171 CHECK_NEXT(emitStructGet(FieldWideningOp::None)); 11172 case uint32_t(GcOp::StructGetS): 11173 CHECK_NEXT(emitStructGet(FieldWideningOp::Signed)); 11174 case uint32_t(GcOp::StructGetU): 11175 CHECK_NEXT(emitStructGet(FieldWideningOp::Unsigned)); 11176 case uint32_t(GcOp::StructSet): 11177 CHECK_NEXT(emitStructSet()); 11178 case uint32_t(GcOp::ArrayNew): 11179 CHECK_NEXT(emitArrayNew()); 11180 case uint32_t(GcOp::ArrayNewFixed): 11181 CHECK_NEXT(emitArrayNewFixed()); 11182 case uint32_t(GcOp::ArrayNewDefault): 11183 CHECK_NEXT(emitArrayNewDefault()); 11184 case uint32_t(GcOp::ArrayNewData): 11185 CHECK_NEXT(emitArrayNewData()); 11186 case uint32_t(GcOp::ArrayNewElem): 11187 CHECK_NEXT(emitArrayNewElem()); 11188 case uint32_t(GcOp::ArrayInitData): 11189 CHECK_NEXT(emitArrayInitData()); 11190 case uint32_t(GcOp::ArrayInitElem): 11191 CHECK_NEXT(emitArrayInitElem()); 11192 case uint32_t(GcOp::ArrayGet): 11193 CHECK_NEXT(emitArrayGet(FieldWideningOp::None)); 11194 case uint32_t(GcOp::ArrayGetS): 11195 CHECK_NEXT(emitArrayGet(FieldWideningOp::Signed)); 11196 case uint32_t(GcOp::ArrayGetU): 11197 CHECK_NEXT(emitArrayGet(FieldWideningOp::Unsigned)); 11198 case uint32_t(GcOp::ArraySet): 11199 CHECK_NEXT(emitArraySet()); 11200 case uint32_t(GcOp::ArrayLen): 11201 CHECK_NEXT(emitArrayLen()); 11202 case uint32_t(GcOp::ArrayCopy): 11203 CHECK_NEXT(emitArrayCopy()); 11204 case uint32_t(GcOp::ArrayFill): 11205 CHECK_NEXT(emitArrayFill()); 11206 case uint32_t(GcOp::RefI31): 11207 CHECK_NEXT(emitRefI31()); 11208 case uint32_t(GcOp::I31GetS): 11209 CHECK_NEXT(emitI31Get(FieldWideningOp::Signed)); 11210 case uint32_t(GcOp::I31GetU): 11211 CHECK_NEXT(emitI31Get(FieldWideningOp::Unsigned)); 11212 case uint32_t(GcOp::RefTest): 11213 CHECK_NEXT(emitRefTest(/*nullable=*/false)); 11214 case uint32_t(GcOp::RefTestNull): 11215 CHECK_NEXT(emitRefTest(/*nullable=*/true)); 11216 case uint32_t(GcOp::RefCast): 11217 CHECK_NEXT(emitRefCast(/*nullable=*/false)); 11218 case uint32_t(GcOp::RefCastNull): 11219 CHECK_NEXT(emitRefCast(/*nullable=*/true)); 11220 case uint32_t(GcOp::BrOnCast): 11221 CHECK_NEXT(emitBrOnCast(/*onSuccess=*/true)); 11222 case uint32_t(GcOp::BrOnCastFail): 11223 CHECK_NEXT(emitBrOnCast(/*onSuccess=*/false)); 11224 case uint16_t(GcOp::AnyConvertExtern): 11225 CHECK_NEXT(emitAnyConvertExtern()); 11226 case uint16_t(GcOp::ExternConvertAny): 11227 CHECK_NEXT(emitExternConvertAny()); 11228 default: 11229 break; 11230 } // switch (op.b1) 11231 return iter_.unrecognizedOpcode(&op); 11232 } 11233 11234 #ifdef ENABLE_WASM_SIMD 11235 // SIMD operations 11236 case uint16_t(Op::SimdPrefix): { 11237 uint32_t laneIndex; 11238 if (!codeMeta_.simdAvailable()) { 11239 return iter_.unrecognizedOpcode(&op); 11240 } 11241 switch (op.b1) { 11242 case uint32_t(SimdOp::I8x16ExtractLaneS): 11243 CHECK_NEXT(dispatchExtractLane(ExtractLaneI8x16, ValType::I32, 16)); 11244 case uint32_t(SimdOp::I8x16ExtractLaneU): 11245 CHECK_NEXT( 11246 dispatchExtractLane(ExtractLaneUI8x16, ValType::I32, 16)); 11247 case uint32_t(SimdOp::I16x8ExtractLaneS): 11248 CHECK_NEXT(dispatchExtractLane(ExtractLaneI16x8, ValType::I32, 8)); 11249 case uint32_t(SimdOp::I16x8ExtractLaneU): 11250 CHECK_NEXT(dispatchExtractLane(ExtractLaneUI16x8, ValType::I32, 8)); 11251 case uint32_t(SimdOp::I32x4ExtractLane): 11252 CHECK_NEXT(dispatchExtractLane(ExtractLaneI32x4, ValType::I32, 4)); 11253 case uint32_t(SimdOp::I64x2ExtractLane): 11254 CHECK_NEXT(dispatchExtractLane(ExtractLaneI64x2, ValType::I64, 2)); 11255 case uint32_t(SimdOp::F32x4ExtractLane): 11256 CHECK_NEXT(dispatchExtractLane(ExtractLaneF32x4, ValType::F32, 4)); 11257 case uint32_t(SimdOp::F64x2ExtractLane): 11258 CHECK_NEXT(dispatchExtractLane(ExtractLaneF64x2, ValType::F64, 2)); 11259 case uint32_t(SimdOp::I8x16Splat): 11260 CHECK_NEXT(dispatchSplat(SplatI8x16, ValType::I32)); 11261 case uint32_t(SimdOp::I16x8Splat): 11262 CHECK_NEXT(dispatchSplat(SplatI16x8, ValType::I32)); 11263 case uint32_t(SimdOp::I32x4Splat): 11264 CHECK_NEXT(dispatchSplat(SplatI32x4, ValType::I32)); 11265 case uint32_t(SimdOp::I64x2Splat): 11266 CHECK_NEXT(dispatchSplat(SplatI64x2, ValType::I64)); 11267 case uint32_t(SimdOp::F32x4Splat): 11268 CHECK_NEXT(dispatchSplat(SplatF32x4, ValType::F32)); 11269 case uint32_t(SimdOp::F64x2Splat): 11270 CHECK_NEXT(dispatchSplat(SplatF64x2, ValType::F64)); 11271 case uint32_t(SimdOp::V128AnyTrue): 11272 CHECK_NEXT(dispatchVectorReduction(AnyTrue)); 11273 case uint32_t(SimdOp::I8x16AllTrue): 11274 CHECK_NEXT(dispatchVectorReduction(AllTrueI8x16)); 11275 case uint32_t(SimdOp::I16x8AllTrue): 11276 CHECK_NEXT(dispatchVectorReduction(AllTrueI16x8)); 11277 case uint32_t(SimdOp::I32x4AllTrue): 11278 CHECK_NEXT(dispatchVectorReduction(AllTrueI32x4)); 11279 case uint32_t(SimdOp::I64x2AllTrue): 11280 CHECK_NEXT(dispatchVectorReduction(AllTrueI64x2)); 11281 case uint32_t(SimdOp::I8x16Bitmask): 11282 CHECK_NEXT(dispatchVectorReduction(BitmaskI8x16)); 11283 case uint32_t(SimdOp::I16x8Bitmask): 11284 CHECK_NEXT(dispatchVectorReduction(BitmaskI16x8)); 11285 case uint32_t(SimdOp::I32x4Bitmask): 11286 CHECK_NEXT(dispatchVectorReduction(BitmaskI32x4)); 11287 case uint32_t(SimdOp::I64x2Bitmask): 11288 CHECK_NEXT(dispatchVectorReduction(BitmaskI64x2)); 11289 case uint32_t(SimdOp::I8x16ReplaceLane): 11290 CHECK_NEXT(dispatchReplaceLane(ReplaceLaneI8x16, ValType::I32, 16)); 11291 case uint32_t(SimdOp::I16x8ReplaceLane): 11292 CHECK_NEXT(dispatchReplaceLane(ReplaceLaneI16x8, ValType::I32, 8)); 11293 case uint32_t(SimdOp::I32x4ReplaceLane): 11294 CHECK_NEXT(dispatchReplaceLane(ReplaceLaneI32x4, ValType::I32, 4)); 11295 case uint32_t(SimdOp::I64x2ReplaceLane): 11296 CHECK_NEXT(dispatchReplaceLane(ReplaceLaneI64x2, ValType::I64, 2)); 11297 case uint32_t(SimdOp::F32x4ReplaceLane): 11298 CHECK_NEXT(dispatchReplaceLane(ReplaceLaneF32x4, ValType::F32, 4)); 11299 case uint32_t(SimdOp::F64x2ReplaceLane): 11300 CHECK_NEXT(dispatchReplaceLane(ReplaceLaneF64x2, ValType::F64, 2)); 11301 case uint32_t(SimdOp::I8x16Eq): 11302 CHECK_NEXT(dispatchVectorComparison(CmpI8x16, Assembler::Equal)); 11303 case uint32_t(SimdOp::I8x16Ne): 11304 CHECK_NEXT(dispatchVectorComparison(CmpI8x16, Assembler::NotEqual)); 11305 case uint32_t(SimdOp::I8x16LtS): 11306 CHECK_NEXT(dispatchVectorComparison(CmpI8x16, Assembler::LessThan)); 11307 case uint32_t(SimdOp::I8x16LtU): 11308 CHECK_NEXT(dispatchVectorComparison(CmpUI8x16, Assembler::Below)); 11309 case uint32_t(SimdOp::I8x16GtS): 11310 CHECK_NEXT( 11311 dispatchVectorComparison(CmpI8x16, Assembler::GreaterThan)); 11312 case uint32_t(SimdOp::I8x16GtU): 11313 CHECK_NEXT(dispatchVectorComparison(CmpUI8x16, Assembler::Above)); 11314 case uint32_t(SimdOp::I8x16LeS): 11315 CHECK_NEXT( 11316 dispatchVectorComparison(CmpI8x16, Assembler::LessThanOrEqual)); 11317 case uint32_t(SimdOp::I8x16LeU): 11318 CHECK_NEXT( 11319 dispatchVectorComparison(CmpUI8x16, Assembler::BelowOrEqual)); 11320 case uint32_t(SimdOp::I8x16GeS): 11321 CHECK_NEXT(dispatchVectorComparison(CmpI8x16, 11322 Assembler::GreaterThanOrEqual)); 11323 case uint32_t(SimdOp::I8x16GeU): 11324 CHECK_NEXT( 11325 dispatchVectorComparison(CmpUI8x16, Assembler::AboveOrEqual)); 11326 case uint32_t(SimdOp::I16x8Eq): 11327 CHECK_NEXT(dispatchVectorComparison(CmpI16x8, Assembler::Equal)); 11328 case uint32_t(SimdOp::I16x8Ne): 11329 CHECK_NEXT(dispatchVectorComparison(CmpI16x8, Assembler::NotEqual)); 11330 case uint32_t(SimdOp::I16x8LtS): 11331 CHECK_NEXT(dispatchVectorComparison(CmpI16x8, Assembler::LessThan)); 11332 case uint32_t(SimdOp::I16x8LtU): 11333 CHECK_NEXT(dispatchVectorComparison(CmpUI16x8, Assembler::Below)); 11334 case uint32_t(SimdOp::I16x8GtS): 11335 CHECK_NEXT( 11336 dispatchVectorComparison(CmpI16x8, Assembler::GreaterThan)); 11337 case uint32_t(SimdOp::I16x8GtU): 11338 CHECK_NEXT(dispatchVectorComparison(CmpUI16x8, Assembler::Above)); 11339 case uint32_t(SimdOp::I16x8LeS): 11340 CHECK_NEXT( 11341 dispatchVectorComparison(CmpI16x8, Assembler::LessThanOrEqual)); 11342 case uint32_t(SimdOp::I16x8LeU): 11343 CHECK_NEXT( 11344 dispatchVectorComparison(CmpUI16x8, Assembler::BelowOrEqual)); 11345 case uint32_t(SimdOp::I16x8GeS): 11346 CHECK_NEXT(dispatchVectorComparison(CmpI16x8, 11347 Assembler::GreaterThanOrEqual)); 11348 case uint32_t(SimdOp::I16x8GeU): 11349 CHECK_NEXT( 11350 dispatchVectorComparison(CmpUI16x8, Assembler::AboveOrEqual)); 11351 case uint32_t(SimdOp::I32x4Eq): 11352 CHECK_NEXT(dispatchVectorComparison(CmpI32x4, Assembler::Equal)); 11353 case uint32_t(SimdOp::I32x4Ne): 11354 CHECK_NEXT(dispatchVectorComparison(CmpI32x4, Assembler::NotEqual)); 11355 case uint32_t(SimdOp::I32x4LtS): 11356 CHECK_NEXT(dispatchVectorComparison(CmpI32x4, Assembler::LessThan)); 11357 case uint32_t(SimdOp::I32x4LtU): 11358 CHECK_NEXT(dispatchVectorComparison(CmpUI32x4, Assembler::Below)); 11359 case uint32_t(SimdOp::I32x4GtS): 11360 CHECK_NEXT( 11361 dispatchVectorComparison(CmpI32x4, Assembler::GreaterThan)); 11362 case uint32_t(SimdOp::I32x4GtU): 11363 CHECK_NEXT(dispatchVectorComparison(CmpUI32x4, Assembler::Above)); 11364 case uint32_t(SimdOp::I32x4LeS): 11365 CHECK_NEXT( 11366 dispatchVectorComparison(CmpI32x4, Assembler::LessThanOrEqual)); 11367 case uint32_t(SimdOp::I32x4LeU): 11368 CHECK_NEXT( 11369 dispatchVectorComparison(CmpUI32x4, Assembler::BelowOrEqual)); 11370 case uint32_t(SimdOp::I32x4GeS): 11371 CHECK_NEXT(dispatchVectorComparison(CmpI32x4, 11372 Assembler::GreaterThanOrEqual)); 11373 case uint32_t(SimdOp::I32x4GeU): 11374 CHECK_NEXT( 11375 dispatchVectorComparison(CmpUI32x4, Assembler::AboveOrEqual)); 11376 case uint32_t(SimdOp::I64x2Eq): 11377 CHECK_NEXT(dispatchVectorComparison(CmpI64x2ForEquality, 11378 Assembler::Equal)); 11379 case uint32_t(SimdOp::I64x2Ne): 11380 CHECK_NEXT(dispatchVectorComparison(CmpI64x2ForEquality, 11381 Assembler::NotEqual)); 11382 case uint32_t(SimdOp::I64x2LtS): 11383 CHECK_NEXT(dispatchVectorComparison(CmpI64x2ForOrdering, 11384 Assembler::LessThan)); 11385 case uint32_t(SimdOp::I64x2GtS): 11386 CHECK_NEXT(dispatchVectorComparison(CmpI64x2ForOrdering, 11387 Assembler::GreaterThan)); 11388 case uint32_t(SimdOp::I64x2LeS): 11389 CHECK_NEXT(dispatchVectorComparison(CmpI64x2ForOrdering, 11390 Assembler::LessThanOrEqual)); 11391 case uint32_t(SimdOp::I64x2GeS): 11392 CHECK_NEXT(dispatchVectorComparison(CmpI64x2ForOrdering, 11393 Assembler::GreaterThanOrEqual)); 11394 case uint32_t(SimdOp::F32x4Eq): 11395 CHECK_NEXT(dispatchVectorComparison(CmpF32x4, Assembler::Equal)); 11396 case uint32_t(SimdOp::F32x4Ne): 11397 CHECK_NEXT(dispatchVectorComparison(CmpF32x4, Assembler::NotEqual)); 11398 case uint32_t(SimdOp::F32x4Lt): 11399 CHECK_NEXT(dispatchVectorComparison(CmpF32x4, Assembler::LessThan)); 11400 case uint32_t(SimdOp::F32x4Gt): 11401 CHECK_NEXT( 11402 dispatchVectorComparison(CmpF32x4, Assembler::GreaterThan)); 11403 case uint32_t(SimdOp::F32x4Le): 11404 CHECK_NEXT( 11405 dispatchVectorComparison(CmpF32x4, Assembler::LessThanOrEqual)); 11406 case uint32_t(SimdOp::F32x4Ge): 11407 CHECK_NEXT(dispatchVectorComparison(CmpF32x4, 11408 Assembler::GreaterThanOrEqual)); 11409 case uint32_t(SimdOp::F64x2Eq): 11410 CHECK_NEXT(dispatchVectorComparison(CmpF64x2, Assembler::Equal)); 11411 case uint32_t(SimdOp::F64x2Ne): 11412 CHECK_NEXT(dispatchVectorComparison(CmpF64x2, Assembler::NotEqual)); 11413 case uint32_t(SimdOp::F64x2Lt): 11414 CHECK_NEXT(dispatchVectorComparison(CmpF64x2, Assembler::LessThan)); 11415 case uint32_t(SimdOp::F64x2Gt): 11416 CHECK_NEXT( 11417 dispatchVectorComparison(CmpF64x2, Assembler::GreaterThan)); 11418 case uint32_t(SimdOp::F64x2Le): 11419 CHECK_NEXT( 11420 dispatchVectorComparison(CmpF64x2, Assembler::LessThanOrEqual)); 11421 case uint32_t(SimdOp::F64x2Ge): 11422 CHECK_NEXT(dispatchVectorComparison(CmpF64x2, 11423 Assembler::GreaterThanOrEqual)); 11424 case uint32_t(SimdOp::V128And): 11425 CHECK_NEXT(dispatchVectorBinary(AndV128)); 11426 case uint32_t(SimdOp::V128Or): 11427 CHECK_NEXT(dispatchVectorBinary(OrV128)); 11428 case uint32_t(SimdOp::V128Xor): 11429 CHECK_NEXT(dispatchVectorBinary(XorV128)); 11430 case uint32_t(SimdOp::V128AndNot): 11431 CHECK_NEXT(dispatchBinary0(emitVectorAndNot, ValType::V128)); 11432 case uint32_t(SimdOp::I8x16AvgrU): 11433 CHECK_NEXT(dispatchVectorBinary(AverageUI8x16)); 11434 case uint32_t(SimdOp::I16x8AvgrU): 11435 CHECK_NEXT(dispatchVectorBinary(AverageUI16x8)); 11436 case uint32_t(SimdOp::I8x16Add): 11437 CHECK_NEXT(dispatchVectorBinary(AddI8x16)); 11438 case uint32_t(SimdOp::I8x16AddSatS): 11439 CHECK_NEXT(dispatchVectorBinary(AddSatI8x16)); 11440 case uint32_t(SimdOp::I8x16AddSatU): 11441 CHECK_NEXT(dispatchVectorBinary(AddSatUI8x16)); 11442 case uint32_t(SimdOp::I8x16Sub): 11443 CHECK_NEXT(dispatchVectorBinary(SubI8x16)); 11444 case uint32_t(SimdOp::I8x16SubSatS): 11445 CHECK_NEXT(dispatchVectorBinary(SubSatI8x16)); 11446 case uint32_t(SimdOp::I8x16SubSatU): 11447 CHECK_NEXT(dispatchVectorBinary(SubSatUI8x16)); 11448 case uint32_t(SimdOp::I8x16MinS): 11449 CHECK_NEXT(dispatchVectorBinary(MinI8x16)); 11450 case uint32_t(SimdOp::I8x16MinU): 11451 CHECK_NEXT(dispatchVectorBinary(MinUI8x16)); 11452 case uint32_t(SimdOp::I8x16MaxS): 11453 CHECK_NEXT(dispatchVectorBinary(MaxI8x16)); 11454 case uint32_t(SimdOp::I8x16MaxU): 11455 CHECK_NEXT(dispatchVectorBinary(MaxUI8x16)); 11456 case uint32_t(SimdOp::I16x8Add): 11457 CHECK_NEXT(dispatchVectorBinary(AddI16x8)); 11458 case uint32_t(SimdOp::I16x8AddSatS): 11459 CHECK_NEXT(dispatchVectorBinary(AddSatI16x8)); 11460 case uint32_t(SimdOp::I16x8AddSatU): 11461 CHECK_NEXT(dispatchVectorBinary(AddSatUI16x8)); 11462 case uint32_t(SimdOp::I16x8Sub): 11463 CHECK_NEXT(dispatchVectorBinary(SubI16x8)); 11464 case uint32_t(SimdOp::I16x8SubSatS): 11465 CHECK_NEXT(dispatchVectorBinary(SubSatI16x8)); 11466 case uint32_t(SimdOp::I16x8SubSatU): 11467 CHECK_NEXT(dispatchVectorBinary(SubSatUI16x8)); 11468 case uint32_t(SimdOp::I16x8Mul): 11469 CHECK_NEXT(dispatchVectorBinary(MulI16x8)); 11470 case uint32_t(SimdOp::I16x8MinS): 11471 CHECK_NEXT(dispatchVectorBinary(MinI16x8)); 11472 case uint32_t(SimdOp::I16x8MinU): 11473 CHECK_NEXT(dispatchVectorBinary(MinUI16x8)); 11474 case uint32_t(SimdOp::I16x8MaxS): 11475 CHECK_NEXT(dispatchVectorBinary(MaxI16x8)); 11476 case uint32_t(SimdOp::I16x8MaxU): 11477 CHECK_NEXT(dispatchVectorBinary(MaxUI16x8)); 11478 case uint32_t(SimdOp::I32x4Add): 11479 CHECK_NEXT(dispatchVectorBinary(AddI32x4)); 11480 case uint32_t(SimdOp::I32x4Sub): 11481 CHECK_NEXT(dispatchVectorBinary(SubI32x4)); 11482 case uint32_t(SimdOp::I32x4Mul): 11483 CHECK_NEXT(dispatchVectorBinary(MulI32x4)); 11484 case uint32_t(SimdOp::I32x4MinS): 11485 CHECK_NEXT(dispatchVectorBinary(MinI32x4)); 11486 case uint32_t(SimdOp::I32x4MinU): 11487 CHECK_NEXT(dispatchVectorBinary(MinUI32x4)); 11488 case uint32_t(SimdOp::I32x4MaxS): 11489 CHECK_NEXT(dispatchVectorBinary(MaxI32x4)); 11490 case uint32_t(SimdOp::I32x4MaxU): 11491 CHECK_NEXT(dispatchVectorBinary(MaxUI32x4)); 11492 case uint32_t(SimdOp::I64x2Add): 11493 CHECK_NEXT(dispatchVectorBinary(AddI64x2)); 11494 case uint32_t(SimdOp::I64x2Sub): 11495 CHECK_NEXT(dispatchVectorBinary(SubI64x2)); 11496 case uint32_t(SimdOp::I64x2Mul): 11497 CHECK_NEXT(dispatchVectorBinary(MulI64x2)); 11498 case uint32_t(SimdOp::F32x4Add): 11499 CHECK_NEXT(dispatchVectorBinary(AddF32x4)); 11500 case uint32_t(SimdOp::F32x4Sub): 11501 CHECK_NEXT(dispatchVectorBinary(SubF32x4)); 11502 case uint32_t(SimdOp::F32x4Mul): 11503 CHECK_NEXT(dispatchVectorBinary(MulF32x4)); 11504 case uint32_t(SimdOp::F32x4Div): 11505 CHECK_NEXT(dispatchVectorBinary(DivF32x4)); 11506 case uint32_t(SimdOp::F32x4Min): 11507 CHECK_NEXT(dispatchVectorBinary(MinF32x4)); 11508 case uint32_t(SimdOp::F32x4Max): 11509 CHECK_NEXT(dispatchVectorBinary(MaxF32x4)); 11510 case uint32_t(SimdOp::F64x2Add): 11511 CHECK_NEXT(dispatchVectorBinary(AddF64x2)); 11512 case uint32_t(SimdOp::F64x2Sub): 11513 CHECK_NEXT(dispatchVectorBinary(SubF64x2)); 11514 case uint32_t(SimdOp::F64x2Mul): 11515 CHECK_NEXT(dispatchVectorBinary(MulF64x2)); 11516 case uint32_t(SimdOp::F64x2Div): 11517 CHECK_NEXT(dispatchVectorBinary(DivF64x2)); 11518 case uint32_t(SimdOp::F64x2Min): 11519 CHECK_NEXT(dispatchVectorBinary(MinF64x2)); 11520 case uint32_t(SimdOp::F64x2Max): 11521 CHECK_NEXT(dispatchVectorBinary(MaxF64x2)); 11522 case uint32_t(SimdOp::I8x16NarrowI16x8S): 11523 CHECK_NEXT(dispatchVectorBinary(NarrowI16x8)); 11524 case uint32_t(SimdOp::I8x16NarrowI16x8U): 11525 CHECK_NEXT(dispatchVectorBinary(NarrowUI16x8)); 11526 case uint32_t(SimdOp::I16x8NarrowI32x4S): 11527 CHECK_NEXT(dispatchVectorBinary(NarrowI32x4)); 11528 case uint32_t(SimdOp::I16x8NarrowI32x4U): 11529 CHECK_NEXT(dispatchVectorBinary(NarrowUI32x4)); 11530 case uint32_t(SimdOp::I8x16Swizzle): 11531 CHECK_NEXT(dispatchVectorBinary(Swizzle)); 11532 case uint32_t(SimdOp::F32x4PMax): 11533 CHECK_NEXT(dispatchVectorBinary(PMaxF32x4)); 11534 case uint32_t(SimdOp::F32x4PMin): 11535 CHECK_NEXT(dispatchVectorBinary(PMinF32x4)); 11536 case uint32_t(SimdOp::F64x2PMax): 11537 CHECK_NEXT(dispatchVectorBinary(PMaxF64x2)); 11538 case uint32_t(SimdOp::F64x2PMin): 11539 CHECK_NEXT(dispatchVectorBinary(PMinF64x2)); 11540 case uint32_t(SimdOp::I32x4DotI16x8S): 11541 CHECK_NEXT(dispatchVectorBinary(DotI16x8)); 11542 case uint32_t(SimdOp::I16x8ExtmulLowI8x16S): 11543 CHECK_NEXT(dispatchVectorBinary(ExtMulLowI8x16)); 11544 case uint32_t(SimdOp::I16x8ExtmulHighI8x16S): 11545 CHECK_NEXT(dispatchVectorBinary(ExtMulHighI8x16)); 11546 case uint32_t(SimdOp::I16x8ExtmulLowI8x16U): 11547 CHECK_NEXT(dispatchVectorBinary(ExtMulLowUI8x16)); 11548 case uint32_t(SimdOp::I16x8ExtmulHighI8x16U): 11549 CHECK_NEXT(dispatchVectorBinary(ExtMulHighUI8x16)); 11550 case uint32_t(SimdOp::I32x4ExtmulLowI16x8S): 11551 CHECK_NEXT(dispatchVectorBinary(ExtMulLowI16x8)); 11552 case uint32_t(SimdOp::I32x4ExtmulHighI16x8S): 11553 CHECK_NEXT(dispatchVectorBinary(ExtMulHighI16x8)); 11554 case uint32_t(SimdOp::I32x4ExtmulLowI16x8U): 11555 CHECK_NEXT(dispatchVectorBinary(ExtMulLowUI16x8)); 11556 case uint32_t(SimdOp::I32x4ExtmulHighI16x8U): 11557 CHECK_NEXT(dispatchVectorBinary(ExtMulHighUI16x8)); 11558 case uint32_t(SimdOp::I64x2ExtmulLowI32x4S): 11559 CHECK_NEXT(dispatchVectorBinary(ExtMulLowI32x4)); 11560 case uint32_t(SimdOp::I64x2ExtmulHighI32x4S): 11561 CHECK_NEXT(dispatchVectorBinary(ExtMulHighI32x4)); 11562 case uint32_t(SimdOp::I64x2ExtmulLowI32x4U): 11563 CHECK_NEXT(dispatchVectorBinary(ExtMulLowUI32x4)); 11564 case uint32_t(SimdOp::I64x2ExtmulHighI32x4U): 11565 CHECK_NEXT(dispatchVectorBinary(ExtMulHighUI32x4)); 11566 case uint32_t(SimdOp::I16x8Q15MulrSatS): 11567 CHECK_NEXT(dispatchVectorBinary(Q15MulrSatS)); 11568 case uint32_t(SimdOp::I8x16Neg): 11569 CHECK_NEXT(dispatchVectorUnary(NegI8x16)); 11570 case uint32_t(SimdOp::I16x8Neg): 11571 CHECK_NEXT(dispatchVectorUnary(NegI16x8)); 11572 case uint32_t(SimdOp::I16x8ExtendLowI8x16S): 11573 CHECK_NEXT(dispatchVectorUnary(WidenLowI8x16)); 11574 case uint32_t(SimdOp::I16x8ExtendHighI8x16S): 11575 CHECK_NEXT(dispatchVectorUnary(WidenHighI8x16)); 11576 case uint32_t(SimdOp::I16x8ExtendLowI8x16U): 11577 CHECK_NEXT(dispatchVectorUnary(WidenLowUI8x16)); 11578 case uint32_t(SimdOp::I16x8ExtendHighI8x16U): 11579 CHECK_NEXT(dispatchVectorUnary(WidenHighUI8x16)); 11580 case uint32_t(SimdOp::I32x4Neg): 11581 CHECK_NEXT(dispatchVectorUnary(NegI32x4)); 11582 case uint32_t(SimdOp::I32x4ExtendLowI16x8S): 11583 CHECK_NEXT(dispatchVectorUnary(WidenLowI16x8)); 11584 case uint32_t(SimdOp::I32x4ExtendHighI16x8S): 11585 CHECK_NEXT(dispatchVectorUnary(WidenHighI16x8)); 11586 case uint32_t(SimdOp::I32x4ExtendLowI16x8U): 11587 CHECK_NEXT(dispatchVectorUnary(WidenLowUI16x8)); 11588 case uint32_t(SimdOp::I32x4ExtendHighI16x8U): 11589 CHECK_NEXT(dispatchVectorUnary(WidenHighUI16x8)); 11590 case uint32_t(SimdOp::I32x4TruncSatF32x4S): 11591 CHECK_NEXT(dispatchVectorUnary(ConvertF32x4ToI32x4)); 11592 case uint32_t(SimdOp::I32x4TruncSatF32x4U): 11593 CHECK_NEXT(dispatchVectorUnary(ConvertF32x4ToUI32x4)); 11594 case uint32_t(SimdOp::I64x2Neg): 11595 CHECK_NEXT(dispatchVectorUnary(NegI64x2)); 11596 case uint32_t(SimdOp::I64x2ExtendLowI32x4S): 11597 CHECK_NEXT(dispatchVectorUnary(WidenLowI32x4)); 11598 case uint32_t(SimdOp::I64x2ExtendHighI32x4S): 11599 CHECK_NEXT(dispatchVectorUnary(WidenHighI32x4)); 11600 case uint32_t(SimdOp::I64x2ExtendLowI32x4U): 11601 CHECK_NEXT(dispatchVectorUnary(WidenLowUI32x4)); 11602 case uint32_t(SimdOp::I64x2ExtendHighI32x4U): 11603 CHECK_NEXT(dispatchVectorUnary(WidenHighUI32x4)); 11604 case uint32_t(SimdOp::F32x4Abs): 11605 CHECK_NEXT(dispatchVectorUnary(AbsF32x4)); 11606 case uint32_t(SimdOp::F32x4Neg): 11607 CHECK_NEXT(dispatchVectorUnary(NegF32x4)); 11608 case uint32_t(SimdOp::F32x4Sqrt): 11609 CHECK_NEXT(dispatchVectorUnary(SqrtF32x4)); 11610 case uint32_t(SimdOp::F32x4ConvertI32x4S): 11611 CHECK_NEXT(dispatchVectorUnary(ConvertI32x4ToF32x4)); 11612 case uint32_t(SimdOp::F32x4ConvertI32x4U): 11613 CHECK_NEXT(dispatchVectorUnary(ConvertUI32x4ToF32x4)); 11614 case uint32_t(SimdOp::F32x4DemoteF64x2Zero): 11615 CHECK_NEXT(dispatchVectorUnary(DemoteF64x2ToF32x4)); 11616 case uint32_t(SimdOp::F64x2PromoteLowF32x4): 11617 CHECK_NEXT(dispatchVectorUnary(PromoteF32x4ToF64x2)); 11618 case uint32_t(SimdOp::F64x2ConvertLowI32x4S): 11619 CHECK_NEXT(dispatchVectorUnary(ConvertI32x4ToF64x2)); 11620 case uint32_t(SimdOp::F64x2ConvertLowI32x4U): 11621 CHECK_NEXT(dispatchVectorUnary(ConvertUI32x4ToF64x2)); 11622 case uint32_t(SimdOp::I32x4TruncSatF64x2SZero): 11623 CHECK_NEXT(dispatchVectorUnary(ConvertF64x2ToI32x4)); 11624 case uint32_t(SimdOp::I32x4TruncSatF64x2UZero): 11625 CHECK_NEXT(dispatchVectorUnary(ConvertF64x2ToUI32x4)); 11626 case uint32_t(SimdOp::F64x2Abs): 11627 CHECK_NEXT(dispatchVectorUnary(AbsF64x2)); 11628 case uint32_t(SimdOp::F64x2Neg): 11629 CHECK_NEXT(dispatchVectorUnary(NegF64x2)); 11630 case uint32_t(SimdOp::F64x2Sqrt): 11631 CHECK_NEXT(dispatchVectorUnary(SqrtF64x2)); 11632 case uint32_t(SimdOp::V128Not): 11633 CHECK_NEXT(dispatchVectorUnary(NotV128)); 11634 case uint32_t(SimdOp::I8x16Popcnt): 11635 CHECK_NEXT(dispatchVectorUnary(PopcntI8x16)); 11636 case uint32_t(SimdOp::I8x16Abs): 11637 CHECK_NEXT(dispatchVectorUnary(AbsI8x16)); 11638 case uint32_t(SimdOp::I16x8Abs): 11639 CHECK_NEXT(dispatchVectorUnary(AbsI16x8)); 11640 case uint32_t(SimdOp::I32x4Abs): 11641 CHECK_NEXT(dispatchVectorUnary(AbsI32x4)); 11642 case uint32_t(SimdOp::I64x2Abs): 11643 CHECK_NEXT(dispatchVectorUnary(AbsI64x2)); 11644 case uint32_t(SimdOp::F32x4Ceil): 11645 CHECK_NEXT(dispatchVectorUnary(CeilF32x4)); 11646 case uint32_t(SimdOp::F32x4Floor): 11647 CHECK_NEXT(dispatchVectorUnary(FloorF32x4)); 11648 case uint32_t(SimdOp::F32x4Trunc): 11649 CHECK_NEXT(dispatchVectorUnary(TruncF32x4)); 11650 case uint32_t(SimdOp::F32x4Nearest): 11651 CHECK_NEXT(dispatchVectorUnary(NearestF32x4)); 11652 case uint32_t(SimdOp::F64x2Ceil): 11653 CHECK_NEXT(dispatchVectorUnary(CeilF64x2)); 11654 case uint32_t(SimdOp::F64x2Floor): 11655 CHECK_NEXT(dispatchVectorUnary(FloorF64x2)); 11656 case uint32_t(SimdOp::F64x2Trunc): 11657 CHECK_NEXT(dispatchVectorUnary(TruncF64x2)); 11658 case uint32_t(SimdOp::F64x2Nearest): 11659 CHECK_NEXT(dispatchVectorUnary(NearestF64x2)); 11660 case uint32_t(SimdOp::I16x8ExtaddPairwiseI8x16S): 11661 CHECK_NEXT(dispatchVectorUnary(ExtAddPairwiseI8x16)); 11662 case uint32_t(SimdOp::I16x8ExtaddPairwiseI8x16U): 11663 CHECK_NEXT(dispatchVectorUnary(ExtAddPairwiseUI8x16)); 11664 case uint32_t(SimdOp::I32x4ExtaddPairwiseI16x8S): 11665 CHECK_NEXT(dispatchVectorUnary(ExtAddPairwiseI16x8)); 11666 case uint32_t(SimdOp::I32x4ExtaddPairwiseI16x8U): 11667 CHECK_NEXT(dispatchVectorUnary(ExtAddPairwiseUI16x8)); 11668 case uint32_t(SimdOp::I8x16Shl): 11669 CHECK_NEXT(dispatchVectorVariableShift(ShiftLeftI8x16)); 11670 case uint32_t(SimdOp::I8x16ShrS): 11671 CHECK_NEXT(dispatchVectorVariableShift(ShiftRightI8x16)); 11672 case uint32_t(SimdOp::I8x16ShrU): 11673 CHECK_NEXT(dispatchVectorVariableShift(ShiftRightUI8x16)); 11674 case uint32_t(SimdOp::I16x8Shl): 11675 CHECK_NEXT(dispatchVectorVariableShift(ShiftLeftI16x8)); 11676 case uint32_t(SimdOp::I16x8ShrS): 11677 CHECK_NEXT(dispatchVectorVariableShift(ShiftRightI16x8)); 11678 case uint32_t(SimdOp::I16x8ShrU): 11679 CHECK_NEXT(dispatchVectorVariableShift(ShiftRightUI16x8)); 11680 case uint32_t(SimdOp::I32x4Shl): 11681 CHECK_NEXT(dispatchVectorVariableShift(ShiftLeftI32x4)); 11682 case uint32_t(SimdOp::I32x4ShrS): 11683 CHECK_NEXT(dispatchVectorVariableShift(ShiftRightI32x4)); 11684 case uint32_t(SimdOp::I32x4ShrU): 11685 CHECK_NEXT(dispatchVectorVariableShift(ShiftRightUI32x4)); 11686 case uint32_t(SimdOp::I64x2Shl): 11687 CHECK_NEXT(dispatchVectorVariableShift(ShiftLeftI64x2)); 11688 case uint32_t(SimdOp::I64x2ShrS): 11689 # if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64) 11690 CHECK_NEXT(emitVectorShiftRightI64x2()); 11691 # else 11692 CHECK_NEXT(dispatchVectorVariableShift(ShiftRightI64x2)); 11693 # endif 11694 case uint32_t(SimdOp::I64x2ShrU): 11695 CHECK_NEXT(dispatchVectorVariableShift(ShiftRightUI64x2)); 11696 case uint32_t(SimdOp::V128Bitselect): 11697 CHECK_NEXT(dispatchTernary1(BitselectV128, ValType::V128)); 11698 case uint32_t(SimdOp::I8x16Shuffle): 11699 CHECK_NEXT(emitVectorShuffle()); 11700 case uint32_t(SimdOp::V128Const): { 11701 V128 v128; 11702 CHECK(iter_.readV128Const(&v128)); 11703 if (!deadCode_) { 11704 pushV128(v128); 11705 } 11706 NEXT(); 11707 } 11708 case uint32_t(SimdOp::V128Load): 11709 CHECK_NEXT(emitLoad(ValType::V128, Scalar::Simd128)); 11710 case uint32_t(SimdOp::V128Load8Splat): 11711 CHECK_NEXT(emitLoadSplat(Scalar::Uint8)); 11712 case uint32_t(SimdOp::V128Load16Splat): 11713 CHECK_NEXT(emitLoadSplat(Scalar::Uint16)); 11714 case uint32_t(SimdOp::V128Load32Splat): 11715 CHECK_NEXT(emitLoadSplat(Scalar::Uint32)); 11716 case uint32_t(SimdOp::V128Load64Splat): 11717 CHECK_NEXT(emitLoadSplat(Scalar::Int64)); 11718 case uint32_t(SimdOp::V128Load8x8S): 11719 CHECK_NEXT(emitLoadExtend(Scalar::Int8)); 11720 case uint32_t(SimdOp::V128Load8x8U): 11721 CHECK_NEXT(emitLoadExtend(Scalar::Uint8)); 11722 case uint32_t(SimdOp::V128Load16x4S): 11723 CHECK_NEXT(emitLoadExtend(Scalar::Int16)); 11724 case uint32_t(SimdOp::V128Load16x4U): 11725 CHECK_NEXT(emitLoadExtend(Scalar::Uint16)); 11726 case uint32_t(SimdOp::V128Load32x2S): 11727 CHECK_NEXT(emitLoadExtend(Scalar::Int32)); 11728 case uint32_t(SimdOp::V128Load32x2U): 11729 CHECK_NEXT(emitLoadExtend(Scalar::Uint32)); 11730 case uint32_t(SimdOp::V128Load32Zero): 11731 CHECK_NEXT(emitLoadZero(Scalar::Float32)); 11732 case uint32_t(SimdOp::V128Load64Zero): 11733 CHECK_NEXT(emitLoadZero(Scalar::Float64)); 11734 case uint32_t(SimdOp::V128Store): 11735 CHECK_NEXT(emitStore(ValType::V128, Scalar::Simd128)); 11736 case uint32_t(SimdOp::V128Load8Lane): 11737 CHECK_NEXT(emitLoadLane(1)); 11738 case uint32_t(SimdOp::V128Load16Lane): 11739 CHECK_NEXT(emitLoadLane(2)); 11740 case uint32_t(SimdOp::V128Load32Lane): 11741 CHECK_NEXT(emitLoadLane(4)); 11742 case uint32_t(SimdOp::V128Load64Lane): 11743 CHECK_NEXT(emitLoadLane(8)); 11744 case uint32_t(SimdOp::V128Store8Lane): 11745 CHECK_NEXT(emitStoreLane(1)); 11746 case uint32_t(SimdOp::V128Store16Lane): 11747 CHECK_NEXT(emitStoreLane(2)); 11748 case uint32_t(SimdOp::V128Store32Lane): 11749 CHECK_NEXT(emitStoreLane(4)); 11750 case uint32_t(SimdOp::V128Store64Lane): 11751 CHECK_NEXT(emitStoreLane(8)); 11752 # ifdef ENABLE_WASM_RELAXED_SIMD 11753 case uint32_t(SimdOp::F32x4RelaxedMadd): 11754 if (!codeMeta_.v128RelaxedEnabled()) { 11755 return iter_.unrecognizedOpcode(&op); 11756 } 11757 CHECK_NEXT(dispatchTernary2(RelaxedMaddF32x4, ValType::V128)); 11758 case uint32_t(SimdOp::F32x4RelaxedNmadd): 11759 if (!codeMeta_.v128RelaxedEnabled()) { 11760 return iter_.unrecognizedOpcode(&op); 11761 } 11762 CHECK_NEXT(dispatchTernary2(RelaxedNmaddF32x4, ValType::V128)); 11763 case uint32_t(SimdOp::F64x2RelaxedMadd): 11764 if (!codeMeta_.v128RelaxedEnabled()) { 11765 return iter_.unrecognizedOpcode(&op); 11766 } 11767 CHECK_NEXT(dispatchTernary2(RelaxedMaddF64x2, ValType::V128)); 11768 case uint32_t(SimdOp::F64x2RelaxedNmadd): 11769 if (!codeMeta_.v128RelaxedEnabled()) { 11770 return iter_.unrecognizedOpcode(&op); 11771 } 11772 CHECK_NEXT(dispatchTernary2(RelaxedNmaddF64x2, ValType::V128)); 11773 break; 11774 case uint32_t(SimdOp::I8x16RelaxedLaneSelect): 11775 case uint32_t(SimdOp::I16x8RelaxedLaneSelect): 11776 case uint32_t(SimdOp::I32x4RelaxedLaneSelect): 11777 case uint32_t(SimdOp::I64x2RelaxedLaneSelect): 11778 if (!codeMeta_.v128RelaxedEnabled()) { 11779 return iter_.unrecognizedOpcode(&op); 11780 } 11781 CHECK_NEXT(emitVectorLaneSelect()); 11782 case uint32_t(SimdOp::F32x4RelaxedMin): 11783 if (!codeMeta_.v128RelaxedEnabled()) { 11784 return iter_.unrecognizedOpcode(&op); 11785 } 11786 CHECK_NEXT(dispatchVectorBinary(RelaxedMinF32x4)); 11787 case uint32_t(SimdOp::F32x4RelaxedMax): 11788 if (!codeMeta_.v128RelaxedEnabled()) { 11789 return iter_.unrecognizedOpcode(&op); 11790 } 11791 CHECK_NEXT(dispatchVectorBinary(RelaxedMaxF32x4)); 11792 case uint32_t(SimdOp::F64x2RelaxedMin): 11793 if (!codeMeta_.v128RelaxedEnabled()) { 11794 return iter_.unrecognizedOpcode(&op); 11795 } 11796 CHECK_NEXT(dispatchVectorBinary(RelaxedMinF64x2)); 11797 case uint32_t(SimdOp::F64x2RelaxedMax): 11798 if (!codeMeta_.v128RelaxedEnabled()) { 11799 return iter_.unrecognizedOpcode(&op); 11800 } 11801 CHECK_NEXT(dispatchVectorBinary(RelaxedMaxF64x2)); 11802 case uint32_t(SimdOp::I32x4RelaxedTruncF32x4S): 11803 if (!codeMeta_.v128RelaxedEnabled()) { 11804 return iter_.unrecognizedOpcode(&op); 11805 } 11806 CHECK_NEXT(dispatchVectorUnary(RelaxedConvertF32x4ToI32x4)); 11807 case uint32_t(SimdOp::I32x4RelaxedTruncF32x4U): 11808 if (!codeMeta_.v128RelaxedEnabled()) { 11809 return iter_.unrecognizedOpcode(&op); 11810 } 11811 CHECK_NEXT(dispatchVectorUnary(RelaxedConvertF32x4ToUI32x4)); 11812 case uint32_t(SimdOp::I32x4RelaxedTruncF64x2SZero): 11813 if (!codeMeta_.v128RelaxedEnabled()) { 11814 return iter_.unrecognizedOpcode(&op); 11815 } 11816 CHECK_NEXT(dispatchVectorUnary(RelaxedConvertF64x2ToI32x4)); 11817 case uint32_t(SimdOp::I32x4RelaxedTruncF64x2UZero): 11818 if (!codeMeta_.v128RelaxedEnabled()) { 11819 return iter_.unrecognizedOpcode(&op); 11820 } 11821 CHECK_NEXT(dispatchVectorUnary(RelaxedConvertF64x2ToUI32x4)); 11822 case uint32_t(SimdOp::I8x16RelaxedSwizzle): 11823 if (!codeMeta_.v128RelaxedEnabled()) { 11824 return iter_.unrecognizedOpcode(&op); 11825 } 11826 CHECK_NEXT(dispatchVectorBinary(RelaxedSwizzle)); 11827 case uint32_t(SimdOp::I16x8RelaxedQ15MulrS): 11828 if (!codeMeta_.v128RelaxedEnabled()) { 11829 return iter_.unrecognizedOpcode(&op); 11830 } 11831 CHECK_NEXT(dispatchVectorBinary(RelaxedQ15MulrS)); 11832 case uint32_t(SimdOp::I16x8RelaxedDotI8x16I7x16S): 11833 if (!codeMeta_.v128RelaxedEnabled()) { 11834 return iter_.unrecognizedOpcode(&op); 11835 } 11836 CHECK_NEXT(dispatchVectorBinary(DotI8x16I7x16S)); 11837 case uint32_t(SimdOp::I32x4RelaxedDotI8x16I7x16AddS): 11838 if (!codeMeta_.v128RelaxedEnabled()) { 11839 return iter_.unrecognizedOpcode(&op); 11840 } 11841 CHECK_NEXT(dispatchTernary0(emitDotI8x16I7x16AddS, ValType::V128)); 11842 # endif 11843 default: 11844 break; 11845 } // switch (op.b1) 11846 return iter_.unrecognizedOpcode(&op); 11847 } 11848 #endif // ENABLE_WASM_SIMD 11849 11850 // "Miscellaneous" operations 11851 case uint16_t(Op::MiscPrefix): { 11852 switch (op.b1) { 11853 case uint32_t(MiscOp::I32TruncSatF32S): 11854 CHECK_NEXT( 11855 dispatchConversionOOM(emitTruncateF32ToI32<TRUNC_SATURATING>, 11856 ValType::F32, ValType::I32)); 11857 case uint32_t(MiscOp::I32TruncSatF32U): 11858 CHECK_NEXT(dispatchConversionOOM( 11859 emitTruncateF32ToI32<TRUNC_UNSIGNED | TRUNC_SATURATING>, 11860 ValType::F32, ValType::I32)); 11861 case uint32_t(MiscOp::I32TruncSatF64S): 11862 CHECK_NEXT( 11863 dispatchConversionOOM(emitTruncateF64ToI32<TRUNC_SATURATING>, 11864 ValType::F64, ValType::I32)); 11865 case uint32_t(MiscOp::I32TruncSatF64U): 11866 CHECK_NEXT(dispatchConversionOOM( 11867 emitTruncateF64ToI32<TRUNC_UNSIGNED | TRUNC_SATURATING>, 11868 ValType::F64, ValType::I32)); 11869 case uint32_t(MiscOp::I64TruncSatF32S): 11870 #ifdef RABALDR_FLOAT_TO_I64_CALLOUT 11871 CHECK_NEXT(dispatchCalloutConversionOOM( 11872 emitConvertFloatingToInt64Callout, 11873 SymbolicAddress::SaturatingTruncateDoubleToInt64, ValType::F32, 11874 ValType::I64)); 11875 #else 11876 CHECK_NEXT( 11877 dispatchConversionOOM(emitTruncateF32ToI64<TRUNC_SATURATING>, 11878 ValType::F32, ValType::I64)); 11879 #endif 11880 case uint32_t(MiscOp::I64TruncSatF32U): 11881 #ifdef RABALDR_FLOAT_TO_I64_CALLOUT 11882 CHECK_NEXT(dispatchCalloutConversionOOM( 11883 emitConvertFloatingToInt64Callout, 11884 SymbolicAddress::SaturatingTruncateDoubleToUint64, ValType::F32, 11885 ValType::I64)); 11886 #else 11887 CHECK_NEXT(dispatchConversionOOM( 11888 emitTruncateF32ToI64<TRUNC_UNSIGNED | TRUNC_SATURATING>, 11889 ValType::F32, ValType::I64)); 11890 #endif 11891 case uint32_t(MiscOp::I64TruncSatF64S): 11892 #ifdef RABALDR_FLOAT_TO_I64_CALLOUT 11893 CHECK_NEXT(dispatchCalloutConversionOOM( 11894 emitConvertFloatingToInt64Callout, 11895 SymbolicAddress::SaturatingTruncateDoubleToInt64, ValType::F64, 11896 ValType::I64)); 11897 #else 11898 CHECK_NEXT( 11899 dispatchConversionOOM(emitTruncateF64ToI64<TRUNC_SATURATING>, 11900 ValType::F64, ValType::I64)); 11901 #endif 11902 case uint32_t(MiscOp::I64TruncSatF64U): 11903 #ifdef RABALDR_FLOAT_TO_I64_CALLOUT 11904 CHECK_NEXT(dispatchCalloutConversionOOM( 11905 emitConvertFloatingToInt64Callout, 11906 SymbolicAddress::SaturatingTruncateDoubleToUint64, ValType::F64, 11907 ValType::I64)); 11908 #else 11909 CHECK_NEXT(dispatchConversionOOM( 11910 emitTruncateF64ToI64<TRUNC_UNSIGNED | TRUNC_SATURATING>, 11911 ValType::F64, ValType::I64)); 11912 #endif 11913 case uint32_t(MiscOp::MemoryCopy): 11914 CHECK_NEXT(emitMemCopy()); 11915 case uint32_t(MiscOp::DataDrop): 11916 CHECK_NEXT(emitDataOrElemDrop(/*isData=*/true)); 11917 case uint32_t(MiscOp::MemoryFill): 11918 CHECK_NEXT(emitMemFill()); 11919 #ifdef ENABLE_WASM_MEMORY_CONTROL 11920 case uint32_t(MiscOp::MemoryDiscard): { 11921 if (!codeMeta_.memoryControlEnabled()) { 11922 return iter_.unrecognizedOpcode(&op); 11923 } 11924 CHECK_NEXT(emitMemDiscard()); 11925 } 11926 #endif 11927 case uint32_t(MiscOp::MemoryInit): 11928 CHECK_NEXT(emitMemInit()); 11929 case uint32_t(MiscOp::TableCopy): 11930 CHECK_NEXT(emitTableCopy()); 11931 case uint32_t(MiscOp::ElemDrop): 11932 CHECK_NEXT(emitDataOrElemDrop(/*isData=*/false)); 11933 case uint32_t(MiscOp::TableInit): 11934 CHECK_NEXT(emitTableInit()); 11935 case uint32_t(MiscOp::TableFill): 11936 CHECK_NEXT(emitTableFill()); 11937 case uint32_t(MiscOp::TableGrow): 11938 CHECK_NEXT(emitTableGrow()); 11939 case uint32_t(MiscOp::TableSize): 11940 CHECK_NEXT(emitTableSize()); 11941 default: 11942 break; 11943 } // switch (op.b1) 11944 return iter_.unrecognizedOpcode(&op); 11945 } 11946 11947 // Thread operations 11948 case uint16_t(Op::ThreadPrefix): { 11949 // Though thread ops can be used on nonshared memories, we make them 11950 // unavailable if shared memory has been disabled in the prefs, for 11951 // maximum predictability and safety and consistency with JS. 11952 if (codeMeta_.sharedMemoryEnabled() == Shareable::False) { 11953 return iter_.unrecognizedOpcode(&op); 11954 } 11955 switch (op.b1) { 11956 case uint32_t(ThreadOp::Notify): 11957 CHECK_NEXT(emitNotify()); 11958 11959 case uint32_t(ThreadOp::I32Wait): 11960 CHECK_NEXT(emitWait(ValType::I32, 4)); 11961 case uint32_t(ThreadOp::I64Wait): 11962 CHECK_NEXT(emitWait(ValType::I64, 8)); 11963 case uint32_t(ThreadOp::Fence): 11964 CHECK_NEXT(emitFence()); 11965 11966 case uint32_t(ThreadOp::I32AtomicLoad): 11967 CHECK_NEXT(emitAtomicLoad(ValType::I32, Scalar::Int32)); 11968 case uint32_t(ThreadOp::I64AtomicLoad): 11969 CHECK_NEXT(emitAtomicLoad(ValType::I64, Scalar::Int64)); 11970 case uint32_t(ThreadOp::I32AtomicLoad8U): 11971 CHECK_NEXT(emitAtomicLoad(ValType::I32, Scalar::Uint8)); 11972 case uint32_t(ThreadOp::I32AtomicLoad16U): 11973 CHECK_NEXT(emitAtomicLoad(ValType::I32, Scalar::Uint16)); 11974 case uint32_t(ThreadOp::I64AtomicLoad8U): 11975 CHECK_NEXT(emitAtomicLoad(ValType::I64, Scalar::Uint8)); 11976 case uint32_t(ThreadOp::I64AtomicLoad16U): 11977 CHECK_NEXT(emitAtomicLoad(ValType::I64, Scalar::Uint16)); 11978 case uint32_t(ThreadOp::I64AtomicLoad32U): 11979 CHECK_NEXT(emitAtomicLoad(ValType::I64, Scalar::Uint32)); 11980 11981 case uint32_t(ThreadOp::I32AtomicStore): 11982 CHECK_NEXT(emitAtomicStore(ValType::I32, Scalar::Int32)); 11983 case uint32_t(ThreadOp::I64AtomicStore): 11984 CHECK_NEXT(emitAtomicStore(ValType::I64, Scalar::Int64)); 11985 case uint32_t(ThreadOp::I32AtomicStore8U): 11986 CHECK_NEXT(emitAtomicStore(ValType::I32, Scalar::Uint8)); 11987 case uint32_t(ThreadOp::I32AtomicStore16U): 11988 CHECK_NEXT(emitAtomicStore(ValType::I32, Scalar::Uint16)); 11989 case uint32_t(ThreadOp::I64AtomicStore8U): 11990 CHECK_NEXT(emitAtomicStore(ValType::I64, Scalar::Uint8)); 11991 case uint32_t(ThreadOp::I64AtomicStore16U): 11992 CHECK_NEXT(emitAtomicStore(ValType::I64, Scalar::Uint16)); 11993 case uint32_t(ThreadOp::I64AtomicStore32U): 11994 CHECK_NEXT(emitAtomicStore(ValType::I64, Scalar::Uint32)); 11995 11996 case uint32_t(ThreadOp::I32AtomicAdd): 11997 CHECK_NEXT( 11998 emitAtomicRMW(ValType::I32, Scalar::Int32, AtomicOp::Add)); 11999 case uint32_t(ThreadOp::I64AtomicAdd): 12000 CHECK_NEXT( 12001 emitAtomicRMW(ValType::I64, Scalar::Int64, AtomicOp::Add)); 12002 case uint32_t(ThreadOp::I32AtomicAdd8U): 12003 CHECK_NEXT( 12004 emitAtomicRMW(ValType::I32, Scalar::Uint8, AtomicOp::Add)); 12005 case uint32_t(ThreadOp::I32AtomicAdd16U): 12006 CHECK_NEXT( 12007 emitAtomicRMW(ValType::I32, Scalar::Uint16, AtomicOp::Add)); 12008 case uint32_t(ThreadOp::I64AtomicAdd8U): 12009 CHECK_NEXT( 12010 emitAtomicRMW(ValType::I64, Scalar::Uint8, AtomicOp::Add)); 12011 case uint32_t(ThreadOp::I64AtomicAdd16U): 12012 CHECK_NEXT( 12013 emitAtomicRMW(ValType::I64, Scalar::Uint16, AtomicOp::Add)); 12014 case uint32_t(ThreadOp::I64AtomicAdd32U): 12015 CHECK_NEXT( 12016 emitAtomicRMW(ValType::I64, Scalar::Uint32, AtomicOp::Add)); 12017 12018 case uint32_t(ThreadOp::I32AtomicSub): 12019 CHECK_NEXT( 12020 emitAtomicRMW(ValType::I32, Scalar::Int32, AtomicOp::Sub)); 12021 case uint32_t(ThreadOp::I64AtomicSub): 12022 CHECK_NEXT( 12023 emitAtomicRMW(ValType::I64, Scalar::Int64, AtomicOp::Sub)); 12024 case uint32_t(ThreadOp::I32AtomicSub8U): 12025 CHECK_NEXT( 12026 emitAtomicRMW(ValType::I32, Scalar::Uint8, AtomicOp::Sub)); 12027 case uint32_t(ThreadOp::I32AtomicSub16U): 12028 CHECK_NEXT( 12029 emitAtomicRMW(ValType::I32, Scalar::Uint16, AtomicOp::Sub)); 12030 case uint32_t(ThreadOp::I64AtomicSub8U): 12031 CHECK_NEXT( 12032 emitAtomicRMW(ValType::I64, Scalar::Uint8, AtomicOp::Sub)); 12033 case uint32_t(ThreadOp::I64AtomicSub16U): 12034 CHECK_NEXT( 12035 emitAtomicRMW(ValType::I64, Scalar::Uint16, AtomicOp::Sub)); 12036 case uint32_t(ThreadOp::I64AtomicSub32U): 12037 CHECK_NEXT( 12038 emitAtomicRMW(ValType::I64, Scalar::Uint32, AtomicOp::Sub)); 12039 12040 case uint32_t(ThreadOp::I32AtomicAnd): 12041 CHECK_NEXT( 12042 emitAtomicRMW(ValType::I32, Scalar::Int32, AtomicOp::And)); 12043 case uint32_t(ThreadOp::I64AtomicAnd): 12044 CHECK_NEXT( 12045 emitAtomicRMW(ValType::I64, Scalar::Int64, AtomicOp::And)); 12046 case uint32_t(ThreadOp::I32AtomicAnd8U): 12047 CHECK_NEXT( 12048 emitAtomicRMW(ValType::I32, Scalar::Uint8, AtomicOp::And)); 12049 case uint32_t(ThreadOp::I32AtomicAnd16U): 12050 CHECK_NEXT( 12051 emitAtomicRMW(ValType::I32, Scalar::Uint16, AtomicOp::And)); 12052 case uint32_t(ThreadOp::I64AtomicAnd8U): 12053 CHECK_NEXT( 12054 emitAtomicRMW(ValType::I64, Scalar::Uint8, AtomicOp::And)); 12055 case uint32_t(ThreadOp::I64AtomicAnd16U): 12056 CHECK_NEXT( 12057 emitAtomicRMW(ValType::I64, Scalar::Uint16, AtomicOp::And)); 12058 case uint32_t(ThreadOp::I64AtomicAnd32U): 12059 CHECK_NEXT( 12060 emitAtomicRMW(ValType::I64, Scalar::Uint32, AtomicOp::And)); 12061 12062 case uint32_t(ThreadOp::I32AtomicOr): 12063 CHECK_NEXT( 12064 emitAtomicRMW(ValType::I32, Scalar::Int32, AtomicOp::Or)); 12065 case uint32_t(ThreadOp::I64AtomicOr): 12066 CHECK_NEXT( 12067 emitAtomicRMW(ValType::I64, Scalar::Int64, AtomicOp::Or)); 12068 case uint32_t(ThreadOp::I32AtomicOr8U): 12069 CHECK_NEXT( 12070 emitAtomicRMW(ValType::I32, Scalar::Uint8, AtomicOp::Or)); 12071 case uint32_t(ThreadOp::I32AtomicOr16U): 12072 CHECK_NEXT( 12073 emitAtomicRMW(ValType::I32, Scalar::Uint16, AtomicOp::Or)); 12074 case uint32_t(ThreadOp::I64AtomicOr8U): 12075 CHECK_NEXT( 12076 emitAtomicRMW(ValType::I64, Scalar::Uint8, AtomicOp::Or)); 12077 case uint32_t(ThreadOp::I64AtomicOr16U): 12078 CHECK_NEXT( 12079 emitAtomicRMW(ValType::I64, Scalar::Uint16, AtomicOp::Or)); 12080 case uint32_t(ThreadOp::I64AtomicOr32U): 12081 CHECK_NEXT( 12082 emitAtomicRMW(ValType::I64, Scalar::Uint32, AtomicOp::Or)); 12083 12084 case uint32_t(ThreadOp::I32AtomicXor): 12085 CHECK_NEXT( 12086 emitAtomicRMW(ValType::I32, Scalar::Int32, AtomicOp::Xor)); 12087 case uint32_t(ThreadOp::I64AtomicXor): 12088 CHECK_NEXT( 12089 emitAtomicRMW(ValType::I64, Scalar::Int64, AtomicOp::Xor)); 12090 case uint32_t(ThreadOp::I32AtomicXor8U): 12091 CHECK_NEXT( 12092 emitAtomicRMW(ValType::I32, Scalar::Uint8, AtomicOp::Xor)); 12093 case uint32_t(ThreadOp::I32AtomicXor16U): 12094 CHECK_NEXT( 12095 emitAtomicRMW(ValType::I32, Scalar::Uint16, AtomicOp::Xor)); 12096 case uint32_t(ThreadOp::I64AtomicXor8U): 12097 CHECK_NEXT( 12098 emitAtomicRMW(ValType::I64, Scalar::Uint8, AtomicOp::Xor)); 12099 case uint32_t(ThreadOp::I64AtomicXor16U): 12100 CHECK_NEXT( 12101 emitAtomicRMW(ValType::I64, Scalar::Uint16, AtomicOp::Xor)); 12102 case uint32_t(ThreadOp::I64AtomicXor32U): 12103 CHECK_NEXT( 12104 emitAtomicRMW(ValType::I64, Scalar::Uint32, AtomicOp::Xor)); 12105 12106 case uint32_t(ThreadOp::I32AtomicXchg): 12107 CHECK_NEXT(emitAtomicXchg(ValType::I32, Scalar::Int32)); 12108 case uint32_t(ThreadOp::I64AtomicXchg): 12109 CHECK_NEXT(emitAtomicXchg(ValType::I64, Scalar::Int64)); 12110 case uint32_t(ThreadOp::I32AtomicXchg8U): 12111 CHECK_NEXT(emitAtomicXchg(ValType::I32, Scalar::Uint8)); 12112 case uint32_t(ThreadOp::I32AtomicXchg16U): 12113 CHECK_NEXT(emitAtomicXchg(ValType::I32, Scalar::Uint16)); 12114 case uint32_t(ThreadOp::I64AtomicXchg8U): 12115 CHECK_NEXT(emitAtomicXchg(ValType::I64, Scalar::Uint8)); 12116 case uint32_t(ThreadOp::I64AtomicXchg16U): 12117 CHECK_NEXT(emitAtomicXchg(ValType::I64, Scalar::Uint16)); 12118 case uint32_t(ThreadOp::I64AtomicXchg32U): 12119 CHECK_NEXT(emitAtomicXchg(ValType::I64, Scalar::Uint32)); 12120 12121 case uint32_t(ThreadOp::I32AtomicCmpXchg): 12122 CHECK_NEXT(emitAtomicCmpXchg(ValType::I32, Scalar::Int32)); 12123 case uint32_t(ThreadOp::I64AtomicCmpXchg): 12124 CHECK_NEXT(emitAtomicCmpXchg(ValType::I64, Scalar::Int64)); 12125 case uint32_t(ThreadOp::I32AtomicCmpXchg8U): 12126 CHECK_NEXT(emitAtomicCmpXchg(ValType::I32, Scalar::Uint8)); 12127 case uint32_t(ThreadOp::I32AtomicCmpXchg16U): 12128 CHECK_NEXT(emitAtomicCmpXchg(ValType::I32, Scalar::Uint16)); 12129 case uint32_t(ThreadOp::I64AtomicCmpXchg8U): 12130 CHECK_NEXT(emitAtomicCmpXchg(ValType::I64, Scalar::Uint8)); 12131 case uint32_t(ThreadOp::I64AtomicCmpXchg16U): 12132 CHECK_NEXT(emitAtomicCmpXchg(ValType::I64, Scalar::Uint16)); 12133 case uint32_t(ThreadOp::I64AtomicCmpXchg32U): 12134 CHECK_NEXT(emitAtomicCmpXchg(ValType::I64, Scalar::Uint32)); 12135 12136 default: 12137 return iter_.unrecognizedOpcode(&op); 12138 } 12139 break; 12140 } 12141 12142 // asm.js and other private operations 12143 case uint16_t(Op::MozPrefix): { 12144 if (op.b1 != uint32_t(MozOp::CallBuiltinModuleFunc) || 12145 !codeMeta_.isBuiltinModule()) { 12146 return iter_.unrecognizedOpcode(&op); 12147 } 12148 // Call a private builtin module func 12149 CHECK_NEXT(emitCallBuiltinModuleFunc()); 12150 } 12151 12152 default: 12153 return iter_.unrecognizedOpcode(&op); 12154 } 12155 12156 #undef CHECK 12157 #undef NEXT 12158 #undef CHECK_NEXT 12159 #undef CHECK_POINTER_COUNT 12160 #undef dispatchBinary0 12161 #undef dispatchBinary1 12162 #undef dispatchBinary2 12163 #undef dispatchBinary3 12164 #undef dispatchUnary0 12165 #undef dispatchUnary1 12166 #undef dispatchUnary2 12167 #undef dispatchComparison0 12168 #undef dispatchConversion0 12169 #undef dispatchConversion1 12170 #undef dispatchConversionOOM 12171 #undef dispatchCalloutConversionOOM 12172 #undef dispatchIntDivCallout 12173 #undef dispatchVectorBinary 12174 #undef dispatchVectorUnary 12175 #undef dispatchVectorComparison 12176 #undef dispatchExtractLane 12177 #undef dispatchReplaceLane 12178 #undef dispatchSplat 12179 #undef dispatchVectorReduction 12180 12181 MOZ_CRASH("unreachable"); 12182 } 12183 12184 MOZ_CRASH("unreachable"); 12185 } 12186 12187 ////////////////////////////////////////////////////////////////////////////// 12188 // 12189 // Various helpers. 12190 12191 void BaseCompiler::assertResultRegistersAvailable(ResultType type) { 12192 #ifdef DEBUG 12193 for (ABIResultIter iter(type); !iter.done(); iter.next()) { 12194 ABIResult result = iter.cur(); 12195 if (!result.inRegister()) { 12196 return; 12197 } 12198 switch (result.type().kind()) { 12199 case ValType::I32: 12200 MOZ_ASSERT(isAvailableI32(RegI32(result.gpr()))); 12201 break; 12202 case ValType::I64: 12203 MOZ_ASSERT(isAvailableI64(RegI64(result.gpr64()))); 12204 break; 12205 case ValType::V128: 12206 # ifdef ENABLE_WASM_SIMD 12207 MOZ_ASSERT(isAvailableV128(RegV128(result.fpr()))); 12208 break; 12209 # else 12210 MOZ_CRASH("No SIMD support"); 12211 # endif 12212 case ValType::F32: 12213 MOZ_ASSERT(isAvailableF32(RegF32(result.fpr()))); 12214 break; 12215 case ValType::F64: 12216 MOZ_ASSERT(isAvailableF64(RegF64(result.fpr()))); 12217 break; 12218 case ValType::Ref: 12219 MOZ_ASSERT(isAvailableRef(RegRef(result.gpr()))); 12220 break; 12221 } 12222 } 12223 #endif 12224 } 12225 12226 void BaseCompiler::saveTempPtr(const RegPtr& r) { 12227 MOZ_ASSERT(!ra.isAvailablePtr(r)); 12228 fr.pushGPR(r); 12229 ra.freePtr(r); 12230 MOZ_ASSERT(ra.isAvailablePtr(r)); 12231 } 12232 12233 void BaseCompiler::restoreTempPtr(const RegPtr& r) { 12234 MOZ_ASSERT(ra.isAvailablePtr(r)); 12235 ra.needPtr(r); 12236 fr.popGPR(r); 12237 MOZ_ASSERT(!ra.isAvailablePtr(r)); 12238 } 12239 12240 #ifdef DEBUG 12241 void BaseCompiler::performRegisterLeakCheck() { 12242 BaseRegAlloc::LeakCheck check(ra); 12243 for (auto& item : stk_) { 12244 switch (item.kind_) { 12245 case Stk::RegisterI32: 12246 check.addKnownI32(item.i32reg()); 12247 break; 12248 case Stk::RegisterI64: 12249 check.addKnownI64(item.i64reg()); 12250 break; 12251 case Stk::RegisterF32: 12252 check.addKnownF32(item.f32reg()); 12253 break; 12254 case Stk::RegisterF64: 12255 check.addKnownF64(item.f64reg()); 12256 break; 12257 # ifdef ENABLE_WASM_SIMD 12258 case Stk::RegisterV128: 12259 check.addKnownV128(item.v128reg()); 12260 break; 12261 # endif 12262 case Stk::RegisterRef: 12263 check.addKnownRef(item.refReg()); 12264 break; 12265 default: 12266 break; 12267 } 12268 } 12269 } 12270 12271 void BaseCompiler::assertStackInvariants() const { 12272 if (deadCode_) { 12273 // Nonlocal control flow can pass values in stack locations in a way that 12274 // isn't accounted for by the value stack. In dead code, which occurs 12275 // after unconditional non-local control flow, there is no invariant to 12276 // assert. 12277 return; 12278 } 12279 size_t size = 0; 12280 for (const Stk& v : stk_) { 12281 switch (v.kind()) { 12282 case Stk::MemRef: 12283 size += BaseStackFrame::StackSizeOfPtr; 12284 break; 12285 case Stk::MemI32: 12286 size += BaseStackFrame::StackSizeOfPtr; 12287 break; 12288 case Stk::MemI64: 12289 size += BaseStackFrame::StackSizeOfInt64; 12290 break; 12291 case Stk::MemF64: 12292 size += BaseStackFrame::StackSizeOfDouble; 12293 break; 12294 case Stk::MemF32: 12295 size += BaseStackFrame::StackSizeOfFloat; 12296 break; 12297 # ifdef ENABLE_WASM_SIMD 12298 case Stk::MemV128: 12299 size += BaseStackFrame::StackSizeOfV128; 12300 break; 12301 # endif 12302 default: 12303 MOZ_ASSERT(!v.isMem()); 12304 break; 12305 } 12306 } 12307 MOZ_ASSERT(size == fr.dynamicHeight()); 12308 } 12309 12310 void BaseCompiler::showStack(const char* who) const { 12311 fprintf(stderr, "Stack at %s {{\n", who); 12312 size_t n = 0; 12313 for (const Stk& elem : stk_) { 12314 fprintf(stderr, " [%zu] ", n++); 12315 elem.showStackElem(); 12316 fprintf(stderr, "\n"); 12317 } 12318 fprintf(stderr, "}}\n"); 12319 } 12320 #endif 12321 12322 ////////////////////////////////////////////////////////////////////////////// 12323 // 12324 // Main compilation logic. 12325 12326 bool BaseCompiler::emitFunction() { 12327 AutoCreatedBy acb(masm, "(wasm)BaseCompiler::emitFunction"); 12328 12329 if (!beginFunction()) { 12330 return false; 12331 } 12332 12333 if (!emitBody()) { 12334 return false; 12335 } 12336 12337 return endFunction(); 12338 } 12339 12340 BaseCompiler::BaseCompiler(const CodeMetadata& codeMeta, 12341 const CompilerEnvironment& compilerEnv, 12342 const FuncCompileInput& func, 12343 const ValTypeVector& locals, 12344 const RegisterOffsets& trapExitLayout, 12345 size_t trapExitLayoutNumWords, Decoder& decoder, 12346 StkVector& stkSource, TempAllocator* alloc, 12347 MacroAssembler* masm, StackMaps* stackMaps) 12348 : // Environment 12349 codeMeta_(codeMeta), 12350 compilerEnv_(compilerEnv), 12351 func_(func), 12352 locals_(locals), 12353 previousBreakablePoint_(UINT32_MAX), 12354 stkSource_(stkSource), 12355 // Output-only data structures 12356 alloc_(alloc->fallible()), 12357 masm(*masm), 12358 // Compilation state 12359 decoder_(decoder), 12360 iter_(codeMeta, decoder, locals), 12361 fr(*masm), 12362 stackMaps_(stackMaps), 12363 stackMapGenerator_(stackMaps, trapExitLayout, trapExitLayoutNumWords, 12364 *masm), 12365 deadCode_(false), 12366 // Init value is selected to ensure proper logic in finishTryNote. 12367 mostRecentFinishedTryNoteIndex_(0), 12368 bceSafe_(0), 12369 latentOp_(LatentOp::None), 12370 latentType_(ValType::I32), 12371 latentIntCmp_(Assembler::Equal), 12372 latentDoubleCmp_(Assembler::DoubleEqual) { 12373 // Our caller, BaselineCompileFunctions, will lend us the vector contents to 12374 // use for the eval stack. To get hold of those contents, we'll temporarily 12375 // installing an empty one in its place. 12376 MOZ_ASSERT(stk_.empty()); 12377 stk_.swap(stkSource_); 12378 12379 // Assuming that previously processed wasm functions are well formed, the 12380 // eval stack should now be empty. But empty it anyway; any non-emptyness 12381 // at this point will cause chaos. 12382 stk_.clear(); 12383 } 12384 12385 BaseCompiler::~BaseCompiler() { 12386 stk_.swap(stkSource_); 12387 // We've returned the eval stack vector contents to our caller, 12388 // BaselineCompileFunctions. We expect the vector we get in return to be 12389 // empty since that's what we swapped for the stack vector in our 12390 // constructor. 12391 MOZ_ASSERT(stk_.empty()); 12392 } 12393 12394 bool BaseCompiler::init() { 12395 // We may lift this restriction in the future. 12396 for (uint32_t memoryIndex = 0; memoryIndex < codeMeta_.memories.length(); 12397 memoryIndex++) { 12398 MOZ_ASSERT_IF(isMem64(memoryIndex), 12399 !codeMeta_.hugeMemoryEnabled(memoryIndex)); 12400 } 12401 // asm.js is not supported in baseline 12402 MOZ_ASSERT(!codeMeta_.isAsmJS()); 12403 // Only asm.js modules have call site line numbers 12404 MOZ_ASSERT(func_.callSiteLineNums.empty()); 12405 12406 ra.init(this); 12407 12408 if (!SigD_.append(ValType::F64)) { 12409 return false; 12410 } 12411 if (!SigF_.append(ValType::F32)) { 12412 return false; 12413 } 12414 12415 ArgTypeVector args(funcType()); 12416 return fr.setupLocals(locals_, args, compilerEnv_.debugEnabled(), 12417 &localInfo_); 12418 } 12419 12420 FuncOffsets BaseCompiler::finish() { 12421 MOZ_ASSERT(iter_.done()); 12422 MOZ_ASSERT(stk_.empty()); 12423 MOZ_ASSERT(stackMapGenerator_.memRefsOnStk == 0); 12424 12425 masm.flushBuffer(); 12426 12427 return offsets_; 12428 } 12429 12430 } // namespace wasm 12431 } // namespace js 12432 12433 bool js::wasm::BaselinePlatformSupport() { 12434 #if defined(JS_CODEGEN_ARM) 12435 // Simplifying assumption: require SDIV and UDIV. 12436 // 12437 // I have no good data on ARM populations allowing me to say that 12438 // X% of devices in the market implement SDIV and UDIV. However, 12439 // they are definitely implemented on the Cortex-A7 and Cortex-A15 12440 // and on all ARMv8 systems. 12441 if (!ARMFlags::HasIDIV()) { 12442 return false; 12443 } 12444 #endif 12445 #if defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_X86) || \ 12446 defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_ARM64) || \ 12447 defined(JS_CODEGEN_MIPS64) || defined(JS_CODEGEN_LOONG64) || \ 12448 defined(JS_CODEGEN_RISCV64) 12449 return true; 12450 #else 12451 return false; 12452 #endif 12453 } 12454 12455 bool js::wasm::BaselineCompileFunctions(const CodeMetadata& codeMeta, 12456 const CompilerEnvironment& compilerEnv, 12457 LifoAlloc& lifo, 12458 const FuncCompileInputVector& inputs, 12459 CompiledCode* code, 12460 UniqueChars* error) { 12461 MOZ_ASSERT(compilerEnv.tier() == Tier::Baseline); 12462 MOZ_ASSERT(codeMeta.kind == ModuleKind::Wasm); 12463 12464 // The MacroAssembler will sometimes access the jitContext. 12465 12466 TempAllocator alloc(&lifo); 12467 JitContext jitContext; 12468 MOZ_ASSERT(IsCompilingWasm()); 12469 WasmMacroAssembler masm(alloc); 12470 12471 // Swap in already-allocated empty vectors to avoid malloc/free. 12472 MOZ_ASSERT(code->empty()); 12473 if (!code->swap(masm)) { 12474 return false; 12475 } 12476 12477 // Create a description of the stack layout created by GenerateTrapExit(). 12478 RegisterOffsets trapExitLayout; 12479 size_t trapExitLayoutNumWords; 12480 GenerateTrapExitRegisterOffsets(&trapExitLayout, &trapExitLayoutNumWords); 12481 12482 // The compiler's operand stack. We reuse it across all functions so as to 12483 // avoid malloc/free. Presize it to 128 elements in the hope of avoiding 12484 // reallocation later. 12485 StkVector stk; 12486 if (!stk.reserve(128)) { 12487 return false; 12488 } 12489 12490 for (const FuncCompileInput& func : inputs) { 12491 Decoder d(func.begin, func.end, func.lineOrBytecode, error); 12492 12493 // Build the local types vector. 12494 12495 ValTypeVector locals; 12496 if (!DecodeLocalEntriesWithParams(d, codeMeta, func.index, &locals)) { 12497 return false; 12498 } 12499 12500 size_t unwindInfoBefore = masm.codeRangeUnwindInfos().length(); 12501 size_t callRefMetricsBefore = masm.callRefMetricsPatches().length(); 12502 size_t allocSitesBefore = masm.allocSitesPatches().length(); 12503 12504 // One-pass baseline compilation. 12505 12506 BaseCompiler f(codeMeta, compilerEnv, func, locals, trapExitLayout, 12507 trapExitLayoutNumWords, d, stk, &alloc, &masm, 12508 &code->stackMaps); 12509 if (!f.init()) { 12510 return false; 12511 } 12512 if (!f.emitFunction()) { 12513 return false; 12514 } 12515 FuncOffsets offsets(f.finish()); 12516 bool hasUnwindInfo = 12517 unwindInfoBefore != masm.codeRangeUnwindInfos().length(); 12518 size_t callRefMetricsAfter = masm.callRefMetricsPatches().length(); 12519 size_t callRefMetricsLength = callRefMetricsAfter - callRefMetricsBefore; 12520 size_t allocSitesAfter = masm.allocSitesPatches().length(); 12521 size_t allocSitesLength = allocSitesAfter - allocSitesBefore; 12522 12523 // Record this function's code range 12524 if (!code->codeRanges.emplaceBack(func.index, offsets, hasUnwindInfo)) { 12525 return false; 12526 } 12527 12528 // Record this function's specific feature usage 12529 if (!code->funcs.emplaceBack( 12530 func.index, f.iter_.featureUsage(), 12531 CallRefMetricsRange(callRefMetricsBefore, callRefMetricsLength), 12532 AllocSitesRange(allocSitesBefore, allocSitesLength))) { 12533 return false; 12534 } 12535 12536 if (PerfEnabled()) { 12537 if (!code->funcBaselineSpewers.emplaceBack(func.index, 12538 std::move(f.perfSpewer_))) { 12539 return false; 12540 } 12541 } 12542 12543 // Accumulate observed feature usage 12544 code->featureUsage |= f.iter_.featureUsage(); 12545 } 12546 12547 masm.finish(); 12548 if (masm.oom()) { 12549 return false; 12550 } 12551 12552 return code->swap(masm); 12553 }