WasmBCClass.h (76178B)
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 // This is an INTERNAL header for Wasm baseline compiler: the compiler object 20 // and its supporting types. 21 22 #ifndef wasm_wasm_baseline_object_h 23 #define wasm_wasm_baseline_object_h 24 25 #include "jit/PerfSpewer.h" 26 #include "wasm/WasmBCDefs.h" 27 #include "wasm/WasmBCFrame.h" 28 #include "wasm/WasmBCRegDefs.h" 29 #include "wasm/WasmBCStk.h" 30 #include "wasm/WasmConstants.h" 31 32 namespace js { 33 namespace wasm { 34 35 // Container for a piece of out-of-line code, the slow path that supports an 36 // operation. 37 class OutOfLineCode; 38 39 // Part of the inter-bytecode state for the boolean-evaluation-for-control 40 // optimization. 41 struct BranchState; 42 43 // Representation of wasm local variables. 44 using Local = BaseStackFrame::Local; 45 46 // Bitset used for simple bounds check elimination. Capping this at 64 locals 47 // makes sense; even 32 locals would probably be OK in practice. 48 // 49 // For more information about BCE, see the block comment in WasmBCMemory.cpp. 50 using BCESet = uint64_t; 51 52 // Information stored in the control node for generating exception handling 53 // landing pads. 54 struct CatchInfo { 55 uint32_t tagIndex; // Index for the associated exception. 56 NonAssertingLabel label; // The entry label for the handler. 57 58 explicit CatchInfo(uint32_t tagIndex_) : tagIndex(tagIndex_) {} 59 }; 60 61 using CatchInfoVector = Vector<CatchInfo, 1, SystemAllocPolicy>; 62 63 // Control node, representing labels and stack heights at join points. 64 struct Control { 65 NonAssertingLabel label; // The "exit" label 66 NonAssertingLabel otherLabel; // Used for the "else" branch of if-then-else 67 // and to allow delegate to jump to catches. 68 StackHeight stackHeight; // From BaseStackFrame 69 uint32_t stackSize; // Value stack height 70 BCESet bceSafeOnEntry; // Bounds check info flowing into the item 71 BCESet bceSafeOnExit; // Bounds check info flowing out of the item 72 bool deadOnArrival; // deadCode_ was set on entry to the region 73 bool deadThenBranch; // deadCode_ was set on exit from "then" 74 size_t tryNoteIndex; // For tracking try branch code ranges. 75 CatchInfoVector catchInfos; // Used for try-catch handlers. 76 size_t loopBytecodeStart; // For LT: bytecode offset of start of a loop. 77 CodeOffset offsetOfCtrDec; // For LT: masm offset of loop's counter decr. 78 79 Control() 80 : stackHeight(StackHeight::Invalid()), 81 stackSize(UINT32_MAX), 82 bceSafeOnEntry(0), 83 bceSafeOnExit(~BCESet(0)), 84 deadOnArrival(false), 85 deadThenBranch(false), 86 tryNoteIndex(0), 87 loopBytecodeStart(UINTPTR_MAX), 88 offsetOfCtrDec(CodeOffset()) {} 89 90 Control(Control&&) = default; 91 Control(const Control&) = delete; 92 }; 93 94 // A vector of Nothing values, used for reading opcodes. 95 class BaseNothingVector { 96 mozilla::Nothing unused_; 97 98 public: 99 bool reserve(size_t size) { return true; } 100 bool resize(size_t length) { return true; } 101 mozilla::Nothing& operator[](size_t) { return unused_; } 102 mozilla::Nothing& back() { return unused_; } 103 size_t length() const { return 0; } 104 bool append(mozilla::Nothing& nothing) { return true; } 105 void infallibleAppend(mozilla::Nothing& nothing) {} 106 }; 107 108 // The baseline compiler tracks values on a stack of its own -- it needs to scan 109 // that stack for spilling -- and thus has no need for the values maintained by 110 // the iterator. 111 struct BaseCompilePolicy { 112 using Value = mozilla::Nothing; 113 using ValueVector = BaseNothingVector; 114 115 // The baseline compiler uses the iterator's control stack, attaching 116 // its own control information. 117 using ControlItem = Control; 118 }; 119 120 using BaseOpIter = OpIter<BaseCompilePolicy>; 121 122 // Latent operation for boolean-evaluation-for-control optimization. 123 enum class LatentOp { None, Compare, Eqz }; 124 125 // Encapsulate the checking needed for a memory access. 126 struct AccessCheck { 127 AccessCheck() 128 : omitBoundsCheck(false), 129 omitAlignmentCheck(false), 130 onlyPointerAlignment(false) {} 131 132 // If `omitAlignmentCheck` is true then we need check neither the 133 // pointer nor the offset. Otherwise, if `onlyPointerAlignment` is true 134 // then we need check only the pointer. Otherwise, check the sum of 135 // pointer and offset. 136 137 bool omitBoundsCheck; 138 bool omitAlignmentCheck; 139 bool onlyPointerAlignment; 140 }; 141 142 // Encapsulate all the information about a function call. 143 struct FunctionCall { 144 FunctionCall(ABIKind abiKind, RestoreState restoreState) 145 : abi(abiKind), 146 restoreState(restoreState), 147 abiKind(abiKind), 148 #ifdef JS_CODEGEN_ARM 149 hardFP(true), 150 #endif 151 frameAlignAdjustment(0), 152 stackArgAreaSize(0) { 153 // The system ABI preserves the instance register (as it's in a 154 // non-volatile register) and realm. We just need to reload the HeapReg in 155 // case the memory has been moved. 156 MOZ_ASSERT_IF(abiKind == ABIKind::System, 157 restoreState == RestoreState::None || 158 restoreState == RestoreState::PinnedRegs); 159 if (abiKind == ABIKind::System) { 160 // Builtin calls use the system hardFP setting on ARM32. 161 #if defined(JS_CODEGEN_ARM) 162 hardFP = ARMFlags::UseHardFpABI(); 163 abi.setUseHardFp(hardFP); 164 #endif 165 } else { 166 #if defined(JS_CODEGEN_ARM) 167 MOZ_ASSERT(hardFP, "The WASM ABI passes FP arguments in registers"); 168 #endif 169 } 170 } 171 172 ABIArgGenerator abi; 173 RestoreState restoreState; 174 ABIKind abiKind; 175 #ifdef JS_CODEGEN_ARM 176 bool hardFP; 177 #endif 178 size_t frameAlignAdjustment; 179 size_t stackArgAreaSize; 180 }; 181 182 enum class PreBarrierKind { 183 // No pre-write barrier is required because the previous value is undefined. 184 None, 185 // Perform a pre-write barrier to mark the previous value if an incremental 186 // GC is underway. 187 Normal, 188 }; 189 190 enum class PostBarrierKind { 191 // No post barrier. 192 None, 193 // Add a store buffer entry if the new value requires it, but do not attempt 194 // to remove a pre-existing entry. 195 Imprecise, 196 // Remove an existing store buffer entry if the new value does not require 197 // one. This is required to preserve invariants with HeapPtr when used for 198 // movable storage. 199 Precise, 200 // Add a store buffer entry for the entire cell (e.g. the entire struct or 201 // array who now has a field pointing into the nursery). 202 WholeCell, 203 }; 204 205 struct BranchIfRefSubtypeRegisters { 206 RegPtr superSTV; 207 RegI32 scratch1; 208 RegI32 scratch2; 209 }; 210 211 ////////////////////////////////////////////////////////////////////////////// 212 // 213 // Wasm baseline compiler proper. 214 // 215 // This is a struct and not a class because there is no real benefit to hiding 216 // anything, and because many static functions that are wrappers for masm 217 // methods need to reach into it and would otherwise have to be declared as 218 // friends. 219 // 220 // (Members generally have a '_' suffix but some don't because they are 221 // referenced everywhere and it would be tedious to spell that out.) 222 223 struct BaseCompiler final { 224 /////////////////////////////////////////////////////////////////////////// 225 // 226 // Private types 227 228 using LabelVector = Vector<NonAssertingLabel, 8, SystemAllocPolicy>; 229 230 /////////////////////////////////////////////////////////////////////////// 231 // 232 // Read-only and write-once members. 233 234 // Static compilation environment. 235 const CodeMetadata& codeMeta_; 236 const CompilerEnvironment& compilerEnv_; 237 const FuncCompileInput& func_; 238 const ValTypeVector& locals_; 239 240 // Information about the locations of locals, this is set up during 241 // initialization and read-only after that. 242 BaseStackFrame::LocalVector localInfo_; 243 244 // On specific platforms we sometimes need to use specific registers. 245 const SpecificRegs specific_; 246 247 // SigD and SigF are single-entry parameter lists for f64 and f32, these are 248 // created during initialization. 249 ValTypeVector SigD_; 250 ValTypeVector SigF_; 251 252 // Where to go to to return, bound as compilation ends. 253 NonAssertingLabel returnLabel_; 254 255 // Prologue and epilogue offsets, initialized during prologue and epilogue 256 // generation and only used by the caller. 257 FuncOffsets offsets_; 258 259 // We call this address from the breakable point when the (per-module) debug 260 // stub pointer in the Instance is not null. 261 NonAssertingLabel perFunctionDebugStub_; 262 uint32_t previousBreakablePoint_; 263 264 // BaselineCompileFunctions() "lends" us the StkVector to use in this 265 // BaseCompiler object, and that is installed in |stk_| in our constructor. 266 // This is so as to avoid having to malloc/free the vector's contents at 267 // each creation/destruction of a BaseCompiler object. It does however mean 268 // that we need to hold on to a reference to BaselineCompileFunctions()'s 269 // vector, so we can swap (give) its contents back when this BaseCompiler 270 // object is destroyed. This significantly reduces the heap turnover of the 271 // baseline compiler. See bug 1532592. 272 StkVector& stkSource_; 273 274 /////////////////////////////////////////////////////////////////////////// 275 // 276 // Output-only data structures. 277 278 // Bump allocator for temporary memory, used for the value stack and 279 // out-of-line code blobs. Bump-allocated memory is not freed until the end 280 // of the compilation. 281 TempAllocator::Fallible alloc_; 282 283 // Machine code emitter. 284 MacroAssembler& masm; 285 286 // Perf spewer for annotated JIT code while profiling. 287 WasmBaselinePerfSpewer perfSpewer_; 288 289 /////////////////////////////////////////////////////////////////////////// 290 // 291 // Compilation state. 292 293 // Decoder for this function, used for misc error reporting. 294 Decoder& decoder_; 295 296 // Opcode reader. 297 BaseOpIter iter_; 298 299 // Register allocator. 300 BaseRegAlloc ra; 301 302 // Stack frame abstraction. 303 BaseStackFrame fr; 304 305 // Latent out of line support code for some operations, code for these will be 306 // emitted at the end of compilation. 307 Vector<OutOfLineCode*, 8, SystemAllocPolicy> outOfLine_; 308 309 // The stack maps for this compilation. 310 StackMaps* stackMaps_; 311 312 // Stack map state. This keeps track of live pointer slots and allows precise 313 // stack maps to be generated at safe points. 314 StackMapGenerator stackMapGenerator_; 315 316 // Wasm value stack. This maps values on the wasm stack to values in the 317 // running code and their locations. 318 // 319 // The value stack facilitates on-the-fly register allocation and the use of 320 // immediates in instructions. It tracks latent constants, latent references 321 // to locals, register contents, and values that have been flushed to the CPU 322 // stack. 323 // 324 // The stack can be flushed to the CPU stack using sync(). 325 // 326 // The stack is a StkVector rather than a StkVector& since constantly 327 // dereferencing a StkVector& has been shown to add 0.5% or more to the 328 // compiler's dynamic instruction count. 329 StkVector stk_; 330 331 // Flag indicating that the compiler is currently in a dead code region. 332 bool deadCode_; 333 334 // Store previously finished note to know if we need to insert a nop in 335 // finishTryNote. 336 size_t mostRecentFinishedTryNoteIndex_; 337 338 /////////////////////////////////////////////////////////////////////////// 339 // 340 // State for bounds check elimination. 341 342 // Locals that have been bounds checked and not updated since 343 BCESet bceSafe_; 344 345 /////////////////////////////////////////////////////////////////////////// 346 // 347 // State for boolean-evaluation-for-control. 348 349 // Latent operation for branch (seen next) 350 LatentOp latentOp_; 351 352 // Operand type, if latentOp_ is true 353 ValType latentType_; 354 355 // Comparison operator, if latentOp_ == Compare, int types 356 Assembler::Condition latentIntCmp_; 357 358 // Comparison operator, if latentOp_ == Compare, float types 359 Assembler::DoubleCondition latentDoubleCmp_; 360 361 /////////////////////////////////////////////////////////////////////////// 362 // 363 // Main compilation API. 364 // 365 // A client will create a compiler object, and then call init(), 366 // emitFunction(), and finish() in that order. 367 368 BaseCompiler(const CodeMetadata& codeMetadata, 369 const CompilerEnvironment& compilerEnv, 370 const FuncCompileInput& func, const ValTypeVector& locals, 371 const RegisterOffsets& trapExitLayout, 372 size_t trapExitLayoutNumWords, Decoder& decoder, 373 StkVector& stkSource, TempAllocator* alloc, MacroAssembler* masm, 374 StackMaps* stackMaps); 375 ~BaseCompiler(); 376 377 [[nodiscard]] bool init(); 378 [[nodiscard]] bool emitFunction(); 379 [[nodiscard]] FuncOffsets finish(); 380 381 ////////////////////////////////////////////////////////////////////////////// 382 // 383 // Sundry accessor abstractions and convenience predicates. 384 // 385 // WasmBaselineObject-inl.h. 386 387 inline const FuncType& funcType() const; 388 inline bool usesMemory() const; 389 inline bool usesSharedMemory(uint32_t memoryIndex) const; 390 inline bool isMem32(uint32_t memoryIndex) const; 391 inline bool isMem64(uint32_t memoryIndex) const; 392 inline bool hugeMemoryEnabled(uint32_t memoryIndex) const; 393 inline uint32_t instanceOffsetOfMemoryBase(uint32_t memoryIndex) const; 394 inline uint32_t instanceOffsetOfBoundsCheckLimit(uint32_t memoryIndex, 395 unsigned byteSize) const; 396 397 // The casts are used by some of the ScratchRegister implementations. 398 operator MacroAssembler&() const { return masm; } 399 operator BaseRegAlloc&() { return ra; } 400 401 ////////////////////////////////////////////////////////////////////////////// 402 // 403 // Locals. 404 // 405 // WasmBaselineObject-inl.h. 406 407 // Assert that the local at the given index has the given type, and return a 408 // reference to the Local. 409 inline const Local& localFromSlot(uint32_t slot, MIRType type); 410 411 ////////////////////////////////////////////////////////////////////////////// 412 // 413 // Out of line code management. 414 415 [[nodiscard]] OutOfLineCode* addOutOfLineCode(OutOfLineCode* ool); 416 [[nodiscard]] bool generateOutOfLineCode(); 417 418 ///////////////////////////////////////////////////////////////////////////// 419 // 420 // Layering in the compiler (briefly). 421 // 422 // At the lowest layers are abstractions for registers (managed by the 423 // BaseRegAlloc and the wrappers below) and the stack frame (managed by the 424 // BaseStackFrame). 425 // 426 // The registers and frame are in turn used by the value abstraction, which is 427 // implemented by the Stk type and backed by the value stack. Values may be 428 // stored in registers, in the frame, or may be latent constants, and the 429 // value stack handles storage mostly transparently in its push and pop 430 // routines. 431 // 432 // In turn, the pop routines bring values into registers so that we can 433 // compute on them, and the push routines move values to the stack (where they 434 // may still reside in registers until the registers are needed or the value 435 // must be in memory). 436 // 437 // Routines for managing parameters and results (for blocks or calls) may also 438 // manipulate the stack directly. 439 // 440 // At the top are the code generators: methods that use the poppers and 441 // pushers and other utilities to move values into place, and that emit code 442 // to compute on those values or change control flow. 443 444 ///////////////////////////////////////////////////////////////////////////// 445 // 446 // Register management. These are simply strongly-typed wrappers that 447 // delegate to the register allocator. 448 449 inline bool isAvailableI32(RegI32 r); 450 inline bool isAvailableI64(RegI64 r); 451 inline bool isAvailableRef(RegRef r); 452 inline bool isAvailablePtr(RegPtr r); 453 inline bool isAvailableF32(RegF32 r); 454 inline bool isAvailableF64(RegF64 r); 455 #ifdef ENABLE_WASM_SIMD 456 inline bool isAvailableV128(RegV128 r); 457 #endif 458 459 // Allocate any register 460 [[nodiscard]] inline RegI32 needI32(); 461 [[nodiscard]] inline RegI64 needI64(); 462 [[nodiscard]] inline RegRef needRef(); 463 [[nodiscard]] inline RegPtr needPtr(); 464 [[nodiscard]] inline RegF32 needF32(); 465 [[nodiscard]] inline RegF64 needF64(); 466 #ifdef ENABLE_WASM_SIMD 467 [[nodiscard]] inline RegV128 needV128(); 468 #endif 469 470 // Allocate a specific register 471 inline void needI32(RegI32 specific); 472 inline void needI64(RegI64 specific); 473 inline void needRef(RegRef specific); 474 inline void needPtr(RegPtr specific); 475 inline void needF32(RegF32 specific); 476 inline void needF64(RegF64 specific); 477 #ifdef ENABLE_WASM_SIMD 478 inline void needV128(RegV128 specific); 479 #endif 480 481 template <typename RegType> 482 inline RegType need(); 483 484 // Just a shorthand. 485 inline void need2xI32(RegI32 r0, RegI32 r1); 486 inline void need2xI64(RegI64 r0, RegI64 r1); 487 488 // Get a register but do not sync the stack to free one up. This will crash 489 // if no register is available. 490 inline void needI32NoSync(RegI32 r); 491 492 #if defined(JS_CODEGEN_ARM) 493 // Allocate a specific register pair (even-odd register numbers). 494 [[nodiscard]] inline RegI64 needI64Pair(); 495 #endif 496 497 inline void freeAny(AnyReg r); 498 inline void freeI32(RegI32 r); 499 inline void freeI64(RegI64 r); 500 inline void freeRef(RegRef r); 501 inline void freePtr(RegPtr r); 502 inline void freeF32(RegF32 r); 503 inline void freeF64(RegF64 r); 504 #ifdef ENABLE_WASM_SIMD 505 inline void freeV128(RegV128 r); 506 #endif 507 508 template <typename RegType> 509 inline void free(RegType r); 510 511 // Free r if it is not invalid. 512 inline void maybeFree(RegI32 r); 513 inline void maybeFree(RegI64 r); 514 inline void maybeFree(RegF32 r); 515 inline void maybeFree(RegF64 r); 516 inline void maybeFree(RegRef r); 517 inline void maybeFree(RegPtr r); 518 #ifdef ENABLE_WASM_SIMD 519 inline void maybeFree(RegV128 r); 520 #endif 521 522 // On 64-bit systems, `except` must equal r and this is a no-op. On 32-bit 523 // systems, `except` must equal the high or low part of a pair and the other 524 // part of the pair is freed. 525 inline void freeI64Except(RegI64 r, RegI32 except); 526 527 // Return the 32-bit low part of the 64-bit register, do not free anything. 528 inline RegI32 fromI64(RegI64 r); 529 530 // If r is valid, return fromI64(r), otherwise an invalid RegI32. 531 inline RegI32 maybeFromI64(RegI64 r); 532 533 #ifdef JS_PUNBOX64 534 // On 64-bit systems, reinterpret r as 64-bit. 535 inline RegI64 fromI32(RegI32 r); 536 #endif 537 538 // Widen r to 64 bits; this may allocate another register to form a pair. 539 // Note this does not generate code for sign/zero extension. 540 inline RegI64 widenI32(RegI32 r); 541 542 // Narrow r to 32 bits; this may free part of a pair. Note this does not 543 // generate code to canonicalize the value on 64-bit systems. 544 inline RegI32 narrowI64(RegI64 r); 545 inline RegI32 narrowRef(RegRef r); 546 547 // Return the 32-bit low part of r. 548 inline RegI32 lowPart(RegI64 r); 549 550 // On 64-bit systems, return an invalid register. On 32-bit systems, return 551 // the low part of a pair. 552 inline RegI32 maybeHighPart(RegI64 r); 553 554 ////////////////////////////////////////////////////////////////////////////// 555 // 556 // Values and value stack: Low-level methods for moving Stk values of specific 557 // kinds to registers. 558 559 inline void loadConstI32(const Stk& src, RegI32 dest); 560 inline void loadMemI32(const Stk& src, RegI32 dest); 561 inline void loadLocalI32(const Stk& src, RegI32 dest); 562 inline void loadRegisterI32(const Stk& src, RegI32 dest); 563 inline void loadConstI64(const Stk& src, RegI64 dest); 564 inline void loadMemI64(const Stk& src, RegI64 dest); 565 inline void loadLocalI64(const Stk& src, RegI64 dest); 566 inline void loadRegisterI64(const Stk& src, RegI64 dest); 567 inline void loadConstRef(const Stk& src, RegRef dest); 568 inline void loadMemRef(const Stk& src, RegRef dest); 569 inline void loadLocalRef(const Stk& src, RegRef dest); 570 inline void loadRegisterRef(const Stk& src, RegRef dest); 571 inline void loadConstF64(const Stk& src, RegF64 dest); 572 inline void loadMemF64(const Stk& src, RegF64 dest); 573 inline void loadLocalF64(const Stk& src, RegF64 dest); 574 inline void loadRegisterF64(const Stk& src, RegF64 dest); 575 inline void loadConstF32(const Stk& src, RegF32 dest); 576 inline void loadMemF32(const Stk& src, RegF32 dest); 577 inline void loadLocalF32(const Stk& src, RegF32 dest); 578 inline void loadRegisterF32(const Stk& src, RegF32 dest); 579 #ifdef ENABLE_WASM_SIMD 580 inline void loadConstV128(const Stk& src, RegV128 dest); 581 inline void loadMemV128(const Stk& src, RegV128 dest); 582 inline void loadLocalV128(const Stk& src, RegV128 dest); 583 inline void loadRegisterV128(const Stk& src, RegV128 dest); 584 #endif 585 586 ////////////////////////////////////////////////////////////////////////// 587 // 588 // Values and value stack: Mid-level routines for moving Stk values of any 589 // kind to registers. 590 591 inline void loadI32(const Stk& src, RegI32 dest); 592 inline void loadI64(const Stk& src, RegI64 dest); 593 #if !defined(JS_PUNBOX64) 594 inline void loadI64Low(const Stk& src, RegI32 dest); 595 inline void loadI64High(const Stk& src, RegI32 dest); 596 #endif 597 inline void loadF64(const Stk& src, RegF64 dest); 598 inline void loadF32(const Stk& src, RegF32 dest); 599 #ifdef ENABLE_WASM_SIMD 600 inline void loadV128(const Stk& src, RegV128 dest); 601 #endif 602 inline void loadRef(const Stk& src, RegRef dest); 603 604 ////////////////////////////////////////////////////////////////////// 605 // 606 // Value stack: stack management. 607 608 // Flush all local and register value stack elements to memory. 609 inline void sync(); 610 611 // Save a register on the value stack temporarily. 612 void saveTempPtr(const RegPtr& r); 613 614 // Restore a temporarily saved register from the value stack. 615 void restoreTempPtr(const RegPtr& r); 616 617 // This is an optimization used to avoid calling sync for setLocal: if the 618 // local does not exist unresolved on the value stack then we can skip the 619 // sync. 620 inline bool hasLocal(uint32_t slot); 621 622 // Sync the local if necessary. (This currently syncs everything if a sync is 623 // needed at all.) 624 inline void syncLocal(uint32_t slot); 625 626 // Return the amount of execution stack consumed by the top numval 627 // values on the value stack. 628 inline size_t stackConsumed(size_t numval); 629 630 // Drop one value off the stack, possibly also moving the physical stack 631 // pointer. 632 inline void dropValue(); 633 634 #ifdef DEBUG 635 // Check that we're not leaking registers by comparing the 636 // state of the stack + available registers with the set of 637 // all available registers. 638 639 // Call this between opcodes. 640 void performRegisterLeakCheck(); 641 642 // This can be called at any point, really, but typically just after 643 // performRegisterLeakCheck(). 644 void assertStackInvariants() const; 645 646 // Count the number of memory references on the value stack. 647 inline size_t countMemRefsOnStk(); 648 649 // Check if there are any live registers on the value stack. 650 inline bool hasLiveRegsOnStk(); 651 652 // Print the stack to stderr. 653 void showStack(const char* who) const; 654 #endif 655 656 ////////////////////////////////////////////////////////////////////// 657 // 658 // Value stack: pushers of values. 659 660 // Push a register onto the value stack. 661 inline void pushAny(AnyReg r); 662 inline void pushI32(RegI32 r); 663 inline void pushI64(RegI64 r); 664 inline void pushRef(RegRef r); 665 inline void pushPtr(RegPtr r); 666 inline void pushF64(RegF64 r); 667 inline void pushF32(RegF32 r); 668 #ifdef ENABLE_WASM_SIMD 669 inline void pushV128(RegV128 r); 670 #endif 671 672 // Template variation of the foregoing, for use by templated emitters. 673 template <typename RegType> 674 inline void push(RegType item); 675 676 // Push a constant value onto the stack. pushI32 can also take uint32_t, and 677 // pushI64 can take uint64_t; the semantics are the same. Appropriate sign 678 // extension for a 32-bit value on a 64-bit architecture happens when the 679 // value is popped, see the definition of moveImm32. 680 inline void pushI32(int32_t v); 681 inline void pushI64(int64_t v); 682 inline void pushRef(intptr_t v); 683 inline void pushPtr(intptr_t v); 684 inline void pushF64(double v); 685 inline void pushF32(float v); 686 #ifdef ENABLE_WASM_SIMD 687 inline void pushV128(V128 v); 688 #endif 689 inline void pushConstRef(intptr_t v); 690 691 // Push the local slot onto the stack. The slot will not be read here; it 692 // will be read when it is consumed, or when a side effect to the slot forces 693 // its value to be saved. 694 inline void pushLocalI32(uint32_t slot); 695 inline void pushLocalI64(uint32_t slot); 696 inline void pushLocalRef(uint32_t slot); 697 inline void pushLocalF64(uint32_t slot); 698 inline void pushLocalF32(uint32_t slot); 699 #ifdef ENABLE_WASM_SIMD 700 inline void pushLocalV128(uint32_t slot); 701 #endif 702 703 // Push an U32 as an I64, zero-extending it in the process 704 inline void pushU32AsI64(RegI32 rs); 705 706 ////////////////////////////////////////////////////////////////////// 707 // 708 // Value stack: poppers and peekers of values. 709 710 // Pop some value off the stack. 711 inline AnyReg popAny(); 712 inline AnyReg popAny(AnyReg specific); 713 714 // Call only from other popI32() variants. v must be the stack top. May pop 715 // the CPU stack. 716 inline void popI32(const Stk& v, RegI32 dest); 717 718 [[nodiscard]] inline RegI32 popI32(); 719 inline RegI32 popI32(RegI32 specific); 720 721 #ifdef ENABLE_WASM_SIMD 722 // Call only from other popV128() variants. v must be the stack top. May pop 723 // the CPU stack. 724 inline void popV128(const Stk& v, RegV128 dest); 725 726 [[nodiscard]] inline RegV128 popV128(); 727 inline RegV128 popV128(RegV128 specific); 728 #endif 729 730 // Call only from other popI64() variants. v must be the stack top. May pop 731 // the CPU stack. 732 inline void popI64(const Stk& v, RegI64 dest); 733 734 [[nodiscard]] inline RegI64 popI64(); 735 inline RegI64 popI64(RegI64 specific); 736 737 // Call only from other popRef() variants. v must be the stack top. May pop 738 // the CPU stack. 739 inline void popRef(const Stk& v, RegRef dest); 740 741 inline RegRef popRef(RegRef specific); 742 [[nodiscard]] inline RegRef popRef(); 743 744 // Call only from other popPtr() variants. v must be the stack top. May pop 745 // the CPU stack. 746 inline void popPtr(const Stk& v, RegPtr dest); 747 748 inline RegPtr popPtr(RegPtr specific); 749 [[nodiscard]] inline RegPtr popPtr(); 750 751 // Call only from other popF64() variants. v must be the stack top. May pop 752 // the CPU stack. 753 inline void popF64(const Stk& v, RegF64 dest); 754 755 [[nodiscard]] inline RegF64 popF64(); 756 inline RegF64 popF64(RegF64 specific); 757 758 // Call only from other popF32() variants. v must be the stack top. May pop 759 // the CPU stack. 760 inline void popF32(const Stk& v, RegF32 dest); 761 762 [[nodiscard]] inline RegF32 popF32(); 763 inline RegF32 popF32(RegF32 specific); 764 765 // Templated variation of the foregoing, for use by templated emitters. 766 template <typename RegType> 767 inline RegType pop(); 768 769 // Constant poppers will return true and pop the value if the stack top is a 770 // constant of the appropriate type; otherwise pop nothing and return false. 771 [[nodiscard]] inline bool hasConst() const; 772 [[nodiscard]] inline bool popConst(int32_t* c); 773 [[nodiscard]] inline bool popConst(int64_t* c); 774 [[nodiscard]] inline bool peekConst(int32_t* c); 775 [[nodiscard]] inline bool peekConst(int64_t* c); 776 [[nodiscard]] inline bool peek2xConst(int32_t* c0, int32_t* c1); 777 [[nodiscard]] inline bool popConstPositivePowerOfTwo(int32_t* c, 778 uint_fast8_t* power, 779 int32_t cutoff); 780 [[nodiscard]] inline bool popConstPositivePowerOfTwo(int64_t* c, 781 uint_fast8_t* power, 782 int64_t cutoff); 783 784 // Shorthand: Pop r1, then r0. 785 inline void pop2xI32(RegI32* r0, RegI32* r1); 786 inline void pop2xI64(RegI64* r0, RegI64* r1); 787 inline void pop2xF32(RegF32* r0, RegF32* r1); 788 inline void pop2xF64(RegF64* r0, RegF64* r1); 789 #ifdef ENABLE_WASM_SIMD 790 inline void pop2xV128(RegV128* r0, RegV128* r1); 791 #endif 792 inline void pop2xRef(RegRef* r0, RegRef* r1); 793 794 // Pop to a specific register 795 inline RegI32 popI32ToSpecific(RegI32 specific); 796 inline RegI64 popI64ToSpecific(RegI64 specific); 797 798 #ifdef JS_CODEGEN_ARM 799 // Pop an I64 as a valid register pair. 800 inline RegI64 popI64Pair(); 801 #endif 802 803 // Pop an I64 but narrow it and return the narrowed part. 804 inline RegI32 popI64ToI32(); 805 inline RegI32 popI64ToSpecificI32(RegI32 specific); 806 807 // Pop an I32 or I64 as an I64. The value is zero extended out to 64-bits. 808 inline RegI64 popAddressToInt64(AddressType addressType); 809 810 // Pop an I32 or I64 as an I32. The value is clamped to UINT32_MAX to ensure 811 // that it trips bounds checks. 812 inline RegI32 popTableAddressToClampedInt32(AddressType addressType); 813 814 // A combined push/pop that replaces an I32 or I64 on the stack with a clamped 815 // I32, which will trip bounds checks if out of I32 range. 816 inline void replaceTableAddressWithClampedInt32(AddressType addressType); 817 818 // Pop the stack until it has the desired size, but do not move the physical 819 // stack pointer. 820 inline void popValueStackTo(uint32_t stackSize); 821 822 // Pop the given number of elements off the value stack, but do not move 823 // the physical stack pointer. 824 inline void popValueStackBy(uint32_t items); 825 826 // Peek into the stack at relativeDepth from the top. 827 inline Stk& peek(uint32_t relativeDepth); 828 829 // Peek the reference value at the specified depth and load it into a 830 // register. 831 inline void peekRefAt(uint32_t depth, RegRef dest); 832 833 // Peek at the value on the top of the stack and return true if it is a Local 834 // of any type. 835 [[nodiscard]] inline bool peekLocal(uint32_t* local); 836 837 //////////////////////////////////////////////////////////////////////////// 838 // 839 // Block parameters and results. 840 // 841 // Blocks may have multiple parameters and multiple results. Blocks can also 842 // be the target of branches: the entry for loops, and the exit for 843 // non-loops. 844 // 845 // Passing multiple values to a non-branch target (i.e., the entry of a 846 // "block") falls out naturally: any items on the value stack can flow 847 // directly from one block to another. 848 // 849 // However, for branch targets, we need to allocate well-known locations for 850 // the branch values. The approach taken in the baseline compiler is to 851 // allocate registers to the top N values (currently N=1), and then stack 852 // locations for the rest. 853 // 854 855 // Types of result registers that interest us for result-manipulating 856 // functions. 857 enum class ResultRegKind { 858 // General and floating result registers. 859 All, 860 861 // General result registers only. 862 OnlyGPRs 863 }; 864 865 // This is a flag ultimately intended for popBlockResults() that specifies how 866 // the CPU stack should be handled after the result values have been 867 // processed. 868 enum class ContinuationKind { 869 // Adjust the stack for a fallthrough: do nothing. 870 Fallthrough, 871 872 // Adjust the stack for a jump: make the stack conform to the 873 // expected stack at the target 874 Jump 875 }; 876 877 // TODO: It's definitely disputable whether the result register management is 878 // hot enough to warrant inlining at the outermost level. 879 880 inline void needResultRegisters(ResultType type, ResultRegKind which); 881 #ifdef JS_64BIT 882 inline void widenInt32ResultRegisters(ResultType type); 883 #endif 884 inline void freeResultRegisters(ResultType type, ResultRegKind which); 885 inline void needIntegerResultRegisters(ResultType type); 886 inline void freeIntegerResultRegisters(ResultType type); 887 inline void needResultRegisters(ResultType type); 888 inline void freeResultRegisters(ResultType type); 889 void assertResultRegistersAvailable(ResultType type); 890 inline void captureResultRegisters(ResultType type); 891 inline void captureCallResultRegisters(ResultType type); 892 893 void popRegisterResults(ABIResultIter& iter); 894 void popStackResults(ABIResultIter& iter, StackHeight stackBase); 895 896 void popBlockResults(ResultType type, StackHeight stackBase, 897 ContinuationKind kind); 898 899 // This function is similar to popBlockResults, but additionally handles the 900 // implicit exception pointer that is pushed to the value stack on entry to 901 // a catch handler by dropping it appropriately. 902 void popCatchResults(ResultType type, StackHeight stackBase); 903 904 Stk captureStackResult(const ABIResult& result, StackHeight resultsBase, 905 uint32_t stackResultBytes); 906 907 [[nodiscard]] bool pushResults(ResultType type, StackHeight resultsBase); 908 [[nodiscard]] bool pushBlockResults(ResultType type); 909 910 // A combination of popBlockResults + pushBlockResults, used when entering a 911 // block with a control-flow join (loops) or split (if) to shuffle the 912 // fallthrough block parameters into the locations expected by the 913 // continuation. 914 // 915 // This function should only be called when entering a block with a 916 // control-flow join at the entry, where there are no live temporaries in 917 // the current block. 918 [[nodiscard]] bool topBlockParams(ResultType type); 919 920 // A combination of popBlockResults + pushBlockResults, used before branches 921 // where we don't know the target (br_if / br_table). If and when the branch 922 // is taken, the stack results will be shuffled down into place. For br_if 923 // that has fallthrough, the parameters for the untaken branch flow through to 924 // the continuation. 925 [[nodiscard]] bool topBranchParams(ResultType type, StackHeight* height); 926 927 // Conditional branches with fallthrough are preceded by a topBranchParams, so 928 // we know that there are no stack results that need to be materialized. In 929 // that case, we can just shuffle the whole block down before popping the 930 // stack. 931 void shuffleStackResultsBeforeBranch(StackHeight srcHeight, 932 StackHeight destHeight, ResultType type); 933 934 // If in debug mode, adds LeaveFrame breakpoint. 935 bool insertDebugCollapseFrame(); 936 937 ////////////////////////////////////////////////////////////////////// 938 // 939 // Stack maps 940 941 // Various methods for creating a stackmap. Stackmaps are indexed by the 942 // lowest address of the instruction immediately *after* the instruction of 943 // interest. In practice that means either: the return point of a call, the 944 // instruction immediately after a trap instruction (the "resume" 945 // instruction), or the instruction immediately following a no-op (when 946 // debugging is enabled). 947 948 // Create a vanilla stackmap. 949 [[nodiscard]] bool createStackMap(const char* who); 950 951 // Create a stackmap as vanilla, but for a custom assembler offset. 952 [[nodiscard]] bool createStackMap(const char* who, 953 CodeOffset assemblerOffset); 954 955 // Create a stack map as vanilla, and note the presence of a ref-typed 956 // DebugFrame on the stack. 957 [[nodiscard]] bool createStackMap( 958 const char* who, HasDebugFrameWithLiveRefs debugFrameWithLiveRefs); 959 960 //////////////////////////////////////////////////////////// 961 // 962 // Control stack 963 964 inline void initControl(Control& item, ResultType params); 965 inline Control& controlItem(); 966 inline Control& controlItem(uint32_t relativeDepth); 967 inline Control& controlOutermost(); 968 inline LabelKind controlKind(uint32_t relativeDepth); 969 970 //////////////////////////////////////////////////////////// 971 // 972 // Debugger API 973 974 // Insert a breakpoint almost anywhere. This will create a call, with all the 975 // overhead that entails. 976 void insertBreakablePoint(CallSiteKind kind); 977 978 // Insert code at the end of a function for breakpoint filtering. 979 void insertPerFunctionDebugStub(); 980 981 // Debugger API used at the return point: shuffle register return values off 982 // to memory for the debugger to see; and get them back again. 983 void saveRegisterReturnValues(const ResultType& resultType); 984 void restoreRegisterReturnValues(const ResultType& resultType); 985 986 ////////////////////////////////////////////////////////////////////// 987 // 988 // Function prologue and epilogue. 989 990 // Set up and tear down frame, execute prologue and epilogue. 991 [[nodiscard]] bool beginFunction(); 992 [[nodiscard]] bool endFunction(); 993 994 // Move return values to memory before returning, as appropriate 995 void popStackReturnValues(const ResultType& resultType); 996 997 ////////////////////////////////////////////////////////////////////// 998 // 999 // Calls. 1000 1001 void beginCall(FunctionCall& call); 1002 void endCall(FunctionCall& call, size_t stackSpace); 1003 1004 void startCallArgs(size_t stackArgAreaSizeUnaligned, FunctionCall* call); 1005 ABIArg reservePointerArgument(FunctionCall* call); 1006 void passArg(ValType type, const Stk& arg, FunctionCall* call); 1007 1008 // A flag passed to emitCallArgs, describing how the value stack is laid out. 1009 enum class CalleeOnStack { 1010 // After the arguments to the call, there is a callee pushed onto value 1011 // stack. This is only the case for callIndirect. To get the arguments to 1012 // the call, emitCallArgs has to reach one element deeper into the value 1013 // stack, to skip the callee. 1014 True, 1015 1016 // No callee on the stack. 1017 False 1018 }; 1019 // The typename T for emitCallArgs can be one of the following: 1020 // NormalCallResults, TailCallResults, or NoCallResults. 1021 template <typename T> 1022 [[nodiscard]] bool emitCallArgs(const ValTypeVector& argTypes, T results, 1023 FunctionCall* baselineCall, 1024 CalleeOnStack calleeOnStack); 1025 1026 [[nodiscard]] bool pushStackResultsForWasmCall(const ResultType& type, 1027 RegPtr temp, 1028 StackResultsLoc* loc); 1029 void popStackResultsAfterWasmCall(const StackResultsLoc& results, 1030 uint32_t stackArgBytes); 1031 1032 void pushBuiltinCallResult(const FunctionCall& call, MIRType type); 1033 [[nodiscard]] bool pushWasmCallResults(const FunctionCall& call, 1034 ResultType type, 1035 const StackResultsLoc& loc); 1036 1037 // Precondition for the call*() methods: sync() 1038 CodeOffset callDefinition(uint32_t funcIndex, const FunctionCall& call); 1039 CodeOffset callSymbolic(SymbolicAddress callee, const FunctionCall& call); 1040 bool callIndirect(uint32_t funcTypeIndex, uint32_t tableIndex, 1041 const Stk& indexVal, const FunctionCall& call, 1042 bool tailCall, CodeOffset* fastCallOffset, 1043 CodeOffset* slowCallOffset); 1044 CodeOffset callImport(unsigned instanceDataOffset, const FunctionCall& call); 1045 bool updateCallRefMetrics(size_t callRefIndex); 1046 bool callRef(const Stk& calleeRef, const FunctionCall& call, 1047 mozilla::Maybe<size_t> callRefIndex, CodeOffset* fastCallOffset, 1048 CodeOffset* slowCallOffset); 1049 void returnCallRef(const Stk& calleeRef, const FunctionCall& call, 1050 const FuncType& funcType); 1051 CodeOffset builtinCall(SymbolicAddress builtin, const FunctionCall& call); 1052 CodeOffset builtinInstanceMethodCall(const SymbolicAddressSignature& builtin, 1053 const ABIArg& instanceArg, 1054 const FunctionCall& call); 1055 1056 // Helpers to pick up the returned value from the return register. 1057 inline RegI32 captureReturnedI32(); 1058 inline RegI64 captureReturnedI64(); 1059 inline RegF32 captureReturnedF32(const FunctionCall& call); 1060 inline RegF64 captureReturnedF64(const FunctionCall& call); 1061 #ifdef ENABLE_WASM_SIMD 1062 inline RegV128 captureReturnedV128(const FunctionCall& call); 1063 #endif 1064 inline RegRef captureReturnedRef(); 1065 1066 ////////////////////////////////////////////////////////////////////// 1067 // 1068 // Register-to-register moves. These emit nothing if src == dest. 1069 1070 inline void moveI32(RegI32 src, RegI32 dest); 1071 inline void moveI64(RegI64 src, RegI64 dest); 1072 inline void moveRef(RegRef src, RegRef dest); 1073 inline void movePtr(RegPtr src, RegPtr dest); 1074 inline void moveF64(RegF64 src, RegF64 dest); 1075 inline void moveF32(RegF32 src, RegF32 dest); 1076 #ifdef ENABLE_WASM_SIMD 1077 inline void moveV128(RegV128 src, RegV128 dest); 1078 #endif 1079 1080 template <typename RegType> 1081 inline void move(RegType src, RegType dest); 1082 1083 ////////////////////////////////////////////////////////////////////// 1084 // 1085 // Immediate-to-register moves. 1086 // 1087 // The compiler depends on moveImm32() clearing the high bits of a 64-bit 1088 // register on 64-bit systems except MIPS64 And LoongArch64 where high bits 1089 // are sign extended from lower bits, see doc block "64-bit GPRs carrying 1090 // 32-bit values" in MacroAssembler.h. 1091 1092 inline void moveImm32(int32_t v, RegI32 dest); 1093 inline void moveImm64(int64_t v, RegI64 dest); 1094 inline void moveImmRef(intptr_t v, RegRef dest); 1095 1096 ////////////////////////////////////////////////////////////////////// 1097 // 1098 // Sundry low-level code generators. 1099 1100 // Decrement the per-instance function hotness counter by `step` and request 1101 // optimized compilation for this function if the updated counter is negative 1102 // when regarded as an int32_t. The amount to decrement is to be filled in 1103 // later by ::patchHotnessCheck. 1104 [[nodiscard]] Maybe<CodeOffset> addHotnessCheck(); 1105 1106 // Patch in the counter decrement for a hotness check, using the offset 1107 // previously obtained from ::addHotnessCheck. We require 1 <= `step` <= 127. 1108 void patchHotnessCheck(CodeOffset offset, uint32_t step); 1109 1110 // Check the interrupt flag, trap if it is set. 1111 [[nodiscard]] bool addInterruptCheck(); 1112 1113 // Check that the value is not zero, trap if it is. 1114 void checkDivideByZero(RegI32 rhs); 1115 void checkDivideByZero(RegI64 r); 1116 1117 // Check that a signed division will not overflow, trap or flush-to-zero if it 1118 // will according to `zeroOnOverflow`. 1119 void checkDivideSignedOverflow(RegI32 rhs, RegI32 srcDest, Label* done, 1120 bool zeroOnOverflow); 1121 void checkDivideSignedOverflow(RegI64 rhs, RegI64 srcDest, Label* done, 1122 bool zeroOnOverflow); 1123 1124 // Emit a jump table to be used by tableSwitch() 1125 void jumpTable(const LabelVector& labels, Label* theTable); 1126 1127 // Emit a table switch, `theTable` is the jump table. 1128 void tableSwitch(Label* theTable, RegI32 switchValue, Label* dispatchCode); 1129 1130 // Compare i64 and set an i32 boolean result according to the condition. 1131 inline void cmp64Set(Assembler::Condition cond, RegI64 lhs, RegI64 rhs, 1132 RegI32 dest); 1133 1134 // Round floating to integer. 1135 [[nodiscard]] inline bool supportsRoundInstruction(RoundingMode mode); 1136 inline void roundF32(RoundingMode roundingMode, RegF32 f0); 1137 inline void roundF64(RoundingMode roundingMode, RegF64 f0); 1138 1139 // These are just wrappers around assembler functions, but without 1140 // type-specific names, and using our register abstractions for better type 1141 // discipline. 1142 inline void branchTo(Assembler::DoubleCondition c, RegF64 lhs, RegF64 rhs, 1143 Label* l); 1144 inline void branchTo(Assembler::DoubleCondition c, RegF32 lhs, RegF32 rhs, 1145 Label* l); 1146 inline void branchTo(Assembler::Condition c, RegI32 lhs, RegI32 rhs, 1147 Label* l); 1148 inline void branchTo(Assembler::Condition c, RegI32 lhs, Imm32 rhs, Label* l); 1149 inline void branchTo(Assembler::Condition c, RegI64 lhs, RegI64 rhs, 1150 Label* l); 1151 inline void branchTo(Assembler::Condition c, RegI64 lhs, Imm64 rhs, Label* l); 1152 inline void branchTo(Assembler::Condition c, RegRef lhs, ImmWord rhs, 1153 Label* l); 1154 1155 // Helpers for accessing Instance::baselineScratchWords_. Note that Word 1156 // and I64 versions of these routines access the same area and it is up to 1157 // the caller to use it in some way which makes sense. 1158 1159 // Store/load `r`, a machine word, to/from the `index`th scratch storage 1160 // slot in the current Instance. `instancePtr` must point at the current 1161 // Instance; it will not be modified. For ::stashWord, `r` must not be the 1162 // same as `instancePtr`. 1163 void stashWord(RegPtr instancePtr, size_t index, RegPtr r); 1164 void unstashWord(RegPtr instancePtr, size_t index, RegPtr r); 1165 1166 #ifdef JS_CODEGEN_X86 1167 // Store r in instance scratch storage after first loading the instance from 1168 // the frame into the regForInstance. regForInstance must be neither of the 1169 // registers in r. 1170 void stashI64(RegPtr regForInstance, RegI64 r); 1171 1172 // Load r from the instance scratch storage after first loading the instance 1173 // from the frame into the regForInstance. regForInstance can be one of the 1174 // registers in r. 1175 void unstashI64(RegPtr regForInstance, RegI64 r); 1176 #endif 1177 1178 ////////////////////////////////////////////////////////////////////// 1179 // 1180 // Code generators for actual operations. 1181 1182 template <typename RegType, typename IntType> 1183 void quotientOrRemainder(RegType rs, RegType rsd, RegType reserved, 1184 IsUnsigned isUnsigned, ZeroOnOverflow zeroOnOverflow, 1185 bool isConst, IntType c, 1186 void (*operate)(MacroAssembler&, RegType, RegType, 1187 RegType, IsUnsigned)); 1188 1189 [[nodiscard]] bool truncateF32ToI32(RegF32 src, RegI32 dest, 1190 TruncFlags flags); 1191 [[nodiscard]] bool truncateF64ToI32(RegF64 src, RegI32 dest, 1192 TruncFlags flags); 1193 1194 #ifndef RABALDR_FLOAT_TO_I64_CALLOUT 1195 [[nodiscard]] RegF64 needTempForFloatingToI64(TruncFlags flags); 1196 [[nodiscard]] bool truncateF32ToI64(RegF32 src, RegI64 dest, TruncFlags flags, 1197 RegF64 temp); 1198 [[nodiscard]] bool truncateF64ToI64(RegF64 src, RegI64 dest, TruncFlags flags, 1199 RegF64 temp); 1200 #endif // RABALDR_FLOAT_TO_I64_CALLOUT 1201 1202 #ifndef RABALDR_I64_TO_FLOAT_CALLOUT 1203 [[nodiscard]] RegI32 needConvertI64ToFloatTemp(ValType to, bool isUnsigned); 1204 void convertI64ToF32(RegI64 src, bool isUnsigned, RegF32 dest, RegI32 temp); 1205 void convertI64ToF64(RegI64 src, bool isUnsigned, RegF64 dest, RegI32 temp); 1206 #endif // RABALDR_I64_TO_FLOAT_CALLOUT 1207 1208 ////////////////////////////////////////////////////////////////////// 1209 // 1210 // Global variable access. 1211 1212 Address addressOfGlobalVar(const GlobalDesc& global, RegPtr tmp); 1213 1214 ////////////////////////////////////////////////////////////////////// 1215 // 1216 // Table access. 1217 1218 Address addressOfTableField(uint32_t tableIndex, uint32_t fieldOffset, 1219 RegPtr instance); 1220 void loadTableLength(uint32_t tableIndex, RegPtr instance, RegI32 length); 1221 void loadTableElements(uint32_t tableIndex, RegPtr instance, RegPtr elements); 1222 1223 ////////////////////////////////////////////////////////////////////// 1224 // 1225 // Heap access. 1226 1227 void bceCheckLocal(MemoryAccessDesc* access, AccessCheck* check, 1228 uint32_t local); 1229 void bceLocalIsUpdated(uint32_t local); 1230 1231 // Fold offsets into ptr and bounds check as necessary. The instance will be 1232 // valid in cases where it's needed. 1233 template <typename RegAddressType> 1234 void prepareMemoryAccess(MemoryAccessDesc* access, AccessCheck* check, 1235 RegPtr instance, RegAddressType ptr); 1236 1237 void branchAddNoOverflow(uint64_t offset, RegI32 ptr, Label* ok); 1238 void branchTestLowZero(RegI32 ptr, Imm32 mask, Label* ok); 1239 void boundsCheck4GBOrLargerAccess(uint32_t memoryIndex, unsigned byteSize, 1240 RegPtr instance, RegI32 ptr, Label* ok); 1241 void boundsCheckBelow4GBAccess(uint32_t memoryIndex, unsigned byteSize, 1242 RegPtr instance, RegI32 ptr, Label* ok); 1243 1244 void branchAddNoOverflow(uint64_t offset, RegI64 ptr, Label* ok); 1245 void branchTestLowZero(RegI64 ptr, Imm32 mask, Label* ok); 1246 void boundsCheck4GBOrLargerAccess(uint32_t memoryIndex, unsigned byteSize, 1247 RegPtr instance, RegI64 ptr, Label* ok); 1248 void boundsCheckBelow4GBAccess(uint32_t memoryIndex, unsigned byteSize, 1249 RegPtr instance, RegI64 ptr, Label* ok); 1250 1251 // Some consumers depend on the returned Address not incorporating instance, 1252 // as instance may be the scratch register. 1253 template <typename RegAddressType> 1254 Address prepareAtomicMemoryAccess(MemoryAccessDesc* access, 1255 AccessCheck* check, RegPtr instance, 1256 RegAddressType ptr); 1257 1258 template <typename RegAddressType> 1259 void computeEffectiveAddress(MemoryAccessDesc* access); 1260 1261 [[nodiscard]] bool needInstanceForAccess(const MemoryAccessDesc* access, 1262 const AccessCheck& check); 1263 1264 // ptr and dest may be the same iff dest is I32. 1265 // This may destroy ptr even if ptr and dest are not the same. 1266 void executeLoad(MemoryAccessDesc* access, AccessCheck* check, 1267 RegPtr instance, RegPtr memoryBase, RegI32 ptr, AnyReg dest, 1268 RegI32 temp); 1269 void load(MemoryAccessDesc* access, AccessCheck* check, RegPtr instance, 1270 RegPtr memoryBase, RegI32 ptr, AnyReg dest, RegI32 temp); 1271 void load(MemoryAccessDesc* access, AccessCheck* check, RegPtr instance, 1272 RegPtr memoryBase, RegI64 ptr, AnyReg dest, RegI64 temp); 1273 1274 template <typename RegType> 1275 void doLoadCommon(MemoryAccessDesc* access, AccessCheck check, ValType type); 1276 1277 void loadCommon(MemoryAccessDesc* access, AccessCheck check, ValType type); 1278 1279 // ptr and src must not be the same register. 1280 // This may destroy ptr and src. 1281 void executeStore(MemoryAccessDesc* access, AccessCheck* check, 1282 RegPtr instance, RegPtr memoryBase, RegI32 ptr, AnyReg src, 1283 RegI32 temp); 1284 void store(MemoryAccessDesc* access, AccessCheck* check, RegPtr instance, 1285 RegPtr memoryBase, RegI32 ptr, AnyReg src, RegI32 temp); 1286 void store(MemoryAccessDesc* access, AccessCheck* check, RegPtr instance, 1287 RegPtr memoryBase, RegI64 ptr, AnyReg src, RegI64 temp); 1288 1289 template <typename RegType> 1290 void doStoreCommon(MemoryAccessDesc* access, AccessCheck check, 1291 ValType resultType); 1292 1293 void storeCommon(MemoryAccessDesc* access, AccessCheck check, 1294 ValType resultType); 1295 1296 void atomicLoad(MemoryAccessDesc* access, ValType type); 1297 #if !defined(JS_64BIT) 1298 template <typename RegAddressType> 1299 void atomicLoad64(MemoryAccessDesc* desc); 1300 #endif 1301 1302 void atomicStore(MemoryAccessDesc* access, ValType type); 1303 1304 void atomicRMW(MemoryAccessDesc* access, ValType type, AtomicOp op); 1305 template <typename RegAddressType> 1306 void atomicRMW32(MemoryAccessDesc* access, ValType type, AtomicOp op); 1307 template <typename RegAddressType> 1308 void atomicRMW64(MemoryAccessDesc* access, ValType type, AtomicOp op); 1309 1310 void atomicXchg(MemoryAccessDesc* access, ValType type); 1311 template <typename RegAddressType> 1312 void atomicXchg64(MemoryAccessDesc* access, WantResult wantResult); 1313 template <typename RegAddressType> 1314 void atomicXchg32(MemoryAccessDesc* access, ValType type); 1315 1316 void atomicCmpXchg(MemoryAccessDesc* access, ValType type); 1317 template <typename RegAddressType> 1318 void atomicCmpXchg32(MemoryAccessDesc* access, ValType type); 1319 template <typename RegAddressType> 1320 void atomicCmpXchg64(MemoryAccessDesc* access, ValType type); 1321 1322 template <typename RegType> 1323 RegType popConstMemoryAccess(MemoryAccessDesc* access, AccessCheck* check); 1324 template <typename RegType> 1325 RegType popMemoryAccess(MemoryAccessDesc* access, AccessCheck* check); 1326 1327 void pushHeapBase(uint32_t memoryIndex); 1328 1329 //////////////////////////////////////////////////////////////////////////// 1330 // 1331 // Platform-specific popping and register targeting. 1332 1333 // The simple popping methods pop values into targeted registers; the caller 1334 // can free registers using standard functions. These are always called 1335 // popXForY where X says something about types and Y something about the 1336 // operation being targeted. 1337 1338 RegI32 needRotate64Temp(); 1339 void popAndAllocateForDivAndRemI32(RegI32* r0, RegI32* r1, RegI32* reserved); 1340 void popAndAllocateForMulI64(RegI64* r0, RegI64* r1, RegI32* temp); 1341 #ifndef RABALDR_INT_DIV_I64_CALLOUT 1342 void popAndAllocateForDivAndRemI64(RegI64* r0, RegI64* r1, RegI64* reserved, 1343 IsRemainder isRemainder); 1344 #endif 1345 RegI32 popI32RhsForShift(); 1346 RegI32 popI32RhsForShiftI64(); 1347 RegI64 popI64RhsForShift(); 1348 RegI32 popI32RhsForRotate(); 1349 RegI64 popI64RhsForRotate(); 1350 void popI32ForSignExtendI64(RegI64* r0); 1351 void popI64ForSignExtendI64(RegI64* r0); 1352 1353 //////////////////////////////////////////////////////////// 1354 // 1355 // Sundry helpers. 1356 1357 // Retrieve the current bytecodeOffset. 1358 inline BytecodeOffset bytecodeOffset() const; 1359 1360 // Get a trap site description for a trap that would occur in the current 1361 // opcode. 1362 inline TrapSiteDesc trapSiteDesc() const; 1363 1364 // Generate a trap instruction for the current bytecodeOffset. 1365 inline void trap(Trap t) const; 1366 1367 // Abstracted helper for throwing, used for throw, rethrow, and rethrowing 1368 // at the end of a series of catch blocks (if none matched the exception). 1369 [[nodiscard]] bool throwFrom(RegRef exn); 1370 1371 // Load the specified tag object from the Instance. 1372 void loadTag(RegPtr instance, uint32_t tagIndex, RegRef tagDst); 1373 1374 // Load the pending exception state from the Instance and then reset it. 1375 void consumePendingException(RegPtr instance, RegRef* exnDst, RegRef* tagDst); 1376 1377 [[nodiscard]] bool startTryNote(size_t* tryNoteIndex); 1378 void finishTryNote(size_t tryNoteIndex); 1379 1380 //////////////////////////////////////////////////////////// 1381 // 1382 // Barriers support. 1383 1384 // This emits a GC pre-write barrier. The pre-barrier is needed when we 1385 // replace a member field with a new value, and the previous field value 1386 // might have no other referents, and incremental GC is ongoing. The field 1387 // might belong to an object or be a stack slot or a register or a heap 1388 // allocated value. 1389 // 1390 // let obj = { field: previousValue }; 1391 // obj.field = newValue; // previousValue must be marked with a pre-barrier. 1392 // 1393 // The `valueAddr` is the address of the location that we are about to 1394 // update. This function preserves that register. 1395 void emitPreBarrier(RegPtr valueAddr); 1396 1397 // These emit GC post-write barriers. The post-barrier is needed when we 1398 // replace a member field with a new value, the new value is in the nursery, 1399 // and the containing object is a tenured object. The field (or the entire 1400 // containing object) must then be added to the store buffer so that the 1401 // nursery can be correctly collected. The field might belong to an object or 1402 // be a stack slot or a register or a heap allocated value. 1403 // 1404 // For the difference between 'precise' and 'imprecise', look at the 1405 // documentation on PostBarrierKind. 1406 1407 // Emits a post-write barrier that creates a whole-cell store buffer entry. 1408 // See above for details. 1409 // 1410 // - `object` is a pointer to the object that contains the field. This 1411 // register is preserved by this function. 1412 // - `value` is the value that was stored in the field. This register is 1413 // preserved by this function. 1414 // - `temp` is consumed by this function. 1415 [[nodiscard]] bool emitPostBarrierWholeCell(RegRef object, RegRef value, 1416 RegPtr temp); 1417 1418 // Emits a post-write barrier of type WasmAnyRefEdge, imprecisely. See above 1419 // for details. 1420 // 1421 // - `object` is a pointer to the object that contains the field. It is used, 1422 // if present, to skip adding a store buffer entry when the containing 1423 // object is in the nursery. This register is preserved by this function. 1424 // - `valueAddr` is the address of the location that we are writing to. This 1425 // register is consumed by this function. 1426 // - `value` is the value that was stored in the field. This register is 1427 // preserved by this function. 1428 [[nodiscard]] bool emitPostBarrierEdgeImprecise( 1429 const mozilla::Maybe<RegRef>& object, RegPtr valueAddr, RegRef value); 1430 1431 // Emits a post-write barrier of type WasmAnyRefEdge, precisely. See above for 1432 // details. 1433 // 1434 // - `object` is a pointer to the object that contains the field. It is used, 1435 // if present, to skip adding a store buffer entry when the containing 1436 // object is in the nursery. This register is preserved by this function. 1437 // - `valueAddr` is the address of the location that we are writing to. This 1438 // register is consumed by this function. 1439 // - `prevValue` is the value that existed in the field before `value` was 1440 // stored. This register is consumed by this function. 1441 // - `value` is the value that was stored in the field. This register is 1442 // preserved by this function. 1443 [[nodiscard]] bool emitPostBarrierEdgePrecise( 1444 const mozilla::Maybe<RegRef>& object, RegPtr valueAddr, RegRef prevValue, 1445 RegRef value); 1446 1447 // Emits a store to a JS object pointer at the address `valueAddr`, which is 1448 // inside the GC cell `object`. 1449 // 1450 // Preserves `object` and `value`. Consumes `valueAddr`. 1451 [[nodiscard]] bool emitBarrieredStore(const mozilla::Maybe<RegRef>& object, 1452 RegPtr valueAddr, RegRef value, 1453 PreBarrierKind preBarrierKind, 1454 PostBarrierKind postBarrierKind); 1455 1456 // Emits a store of nullptr to a JS object pointer at the address valueAddr. 1457 // Preserves `valueAddr`. 1458 void emitBarrieredClear(RegPtr valueAddr); 1459 1460 //////////////////////////////////////////////////////////// 1461 // 1462 // Machinery for optimized conditional branches. See comments in the 1463 // implementation. 1464 1465 void setLatentCompare(Assembler::Condition compareOp, ValType operandType); 1466 void setLatentCompare(Assembler::DoubleCondition compareOp, 1467 ValType operandType); 1468 void setLatentEqz(ValType operandType); 1469 bool hasLatentOp() const; 1470 void resetLatentOp(); 1471 // Jump to the given branch, passing results, if the condition, `cond` 1472 // matches between `lhs` and `rhs. 1473 template <typename Cond, typename Lhs, typename Rhs> 1474 [[nodiscard]] bool jumpConditionalWithResults(BranchState* b, Cond cond, 1475 Lhs lhs, Rhs rhs); 1476 // Jump to the given branch, passing results, if the WasmGcObject, `object`, 1477 // is a subtype of `destType`. 1478 [[nodiscard]] bool jumpConditionalWithResults(BranchState* b, RegRef object, 1479 MaybeRefType sourceType, 1480 RefType destType, 1481 bool onSuccess); 1482 template <typename Cond> 1483 [[nodiscard]] bool sniffConditionalControlCmp(Cond compareOp, 1484 ValType operandType); 1485 [[nodiscard]] bool sniffConditionalControlEqz(ValType operandType); 1486 void emitBranchSetup(BranchState* b); 1487 [[nodiscard]] bool emitBranchPerform(BranchState* b); 1488 1489 ////////////////////////////////////////////////////////////////////// 1490 1491 [[nodiscard]] bool emitBody(); 1492 [[nodiscard]] bool emitBlock(); 1493 [[nodiscard]] bool emitLoop(); 1494 [[nodiscard]] bool emitIf(); 1495 [[nodiscard]] bool emitElse(); 1496 // Used for common setup for catch and catch_all. 1497 void emitCatchSetup(LabelKind kind, Control& tryCatch, 1498 const ResultType& resultType); 1499 1500 [[nodiscard]] bool emitTry(); 1501 [[nodiscard]] bool emitTryTable(); 1502 [[nodiscard]] bool emitCatch(); 1503 [[nodiscard]] bool emitCatchAll(); 1504 [[nodiscard]] bool emitDelegate(); 1505 [[nodiscard]] bool emitThrow(); 1506 [[nodiscard]] bool emitThrowRef(); 1507 [[nodiscard]] bool emitRethrow(); 1508 [[nodiscard]] bool emitEnd(); 1509 [[nodiscard]] bool emitBr(); 1510 [[nodiscard]] bool emitBrIf(); 1511 [[nodiscard]] bool emitBrTable(); 1512 [[nodiscard]] bool emitDrop(); 1513 [[nodiscard]] bool emitReturn(); 1514 [[nodiscard]] bool emitCall(); 1515 [[nodiscard]] bool emitReturnCall(); 1516 [[nodiscard]] bool emitCallIndirect(); 1517 [[nodiscard]] bool emitReturnCallIndirect(); 1518 [[nodiscard]] bool emitUnaryMathBuiltinCall(SymbolicAddress callee, 1519 ValType operandType); 1520 [[nodiscard]] bool emitGetLocal(); 1521 [[nodiscard]] bool emitSetLocal(); 1522 [[nodiscard]] bool emitTeeLocal(); 1523 [[nodiscard]] bool emitGetGlobal(); 1524 [[nodiscard]] bool emitSetGlobal(); 1525 [[nodiscard]] RegPtr maybeLoadMemoryBaseForAccess( 1526 RegPtr instance, const MemoryAccessDesc* access); 1527 [[nodiscard]] RegPtr maybeLoadInstanceForAccess( 1528 const MemoryAccessDesc* access, const AccessCheck& check); 1529 [[nodiscard]] RegPtr maybeLoadInstanceForAccess( 1530 const MemoryAccessDesc* access, const AccessCheck& check, 1531 RegPtr specific); 1532 [[nodiscard]] bool emitLoad(ValType type, Scalar::Type viewType); 1533 [[nodiscard]] bool emitStore(ValType resultType, Scalar::Type viewType); 1534 [[nodiscard]] bool emitSelect(bool typed); 1535 1536 template <bool isSetLocal> 1537 [[nodiscard]] bool emitSetOrTeeLocal(uint32_t slot); 1538 1539 [[nodiscard]] bool endBlock(ResultType type); 1540 [[nodiscard]] bool endIfThen(ResultType type); 1541 [[nodiscard]] bool endIfThenElse(ResultType type); 1542 [[nodiscard]] bool endTryCatch(ResultType type); 1543 [[nodiscard]] bool endTryTable(ResultType type); 1544 1545 void doReturn(ContinuationKind kind); 1546 1547 void emitCompareI32(Assembler::Condition compareOp, ValType compareType); 1548 void emitCompareI64(Assembler::Condition compareOp, ValType compareType); 1549 void emitCompareF32(Assembler::DoubleCondition compareOp, 1550 ValType compareType); 1551 void emitCompareF64(Assembler::DoubleCondition compareOp, 1552 ValType compareType); 1553 void emitCompareRef(Assembler::Condition compareOp, ValType compareType); 1554 1555 template <typename CompilerType> 1556 inline CompilerType& selectCompiler(); 1557 1558 template <typename SourceType, typename DestType> 1559 inline void emitUnop(void (*op)(MacroAssembler& masm, SourceType rs, 1560 DestType rd)); 1561 1562 template <typename SourceType, typename DestType, typename TempType> 1563 inline void emitUnop(void (*op)(MacroAssembler& masm, SourceType rs, 1564 DestType rd, TempType temp)); 1565 1566 template <typename SourceType, typename DestType, typename ImmType> 1567 inline void emitUnop(ImmType immediate, void (*op)(MacroAssembler&, ImmType, 1568 SourceType, DestType)); 1569 1570 template <typename CompilerType, typename RegType> 1571 inline void emitUnop(void (*op)(CompilerType& compiler, RegType rsd)); 1572 1573 template <typename RegType, typename TempType> 1574 inline void emitUnop(void (*op)(BaseCompiler& bc, RegType rsd, TempType rt), 1575 TempType (*getSpecializedTemp)(BaseCompiler& bc)); 1576 1577 template <typename CompilerType, typename RhsType, typename LhsDestType> 1578 inline void emitBinop(void (*op)(CompilerType& masm, RhsType src, 1579 LhsDestType srcDest)); 1580 1581 template <typename RhsDestType, typename LhsType> 1582 inline void emitBinop(void (*op)(MacroAssembler& masm, RhsDestType src, 1583 LhsType srcDest, RhsDestOp)); 1584 1585 template <typename RhsType, typename LhsDestType, typename TempType> 1586 inline void emitBinop(void (*)(MacroAssembler& masm, RhsType rs, 1587 LhsDestType rsd, TempType temp)); 1588 1589 template <typename RhsType, typename LhsDestType, typename TempType1, 1590 typename TempType2> 1591 inline void emitBinop(void (*)(MacroAssembler& masm, RhsType rs, 1592 LhsDestType rsd, TempType1 temp1, 1593 TempType2 temp2)); 1594 1595 template <typename RhsType, typename LhsDestType, typename ImmType> 1596 inline void emitBinop(ImmType immediate, void (*op)(MacroAssembler&, ImmType, 1597 RhsType, LhsDestType)); 1598 1599 template <typename RhsType, typename LhsDestType, typename ImmType, 1600 typename TempType1, typename TempType2> 1601 inline void emitBinop(ImmType immediate, 1602 void (*op)(MacroAssembler&, ImmType, RhsType, 1603 LhsDestType, TempType1 temp1, 1604 TempType2 temp2)); 1605 1606 template <typename CompilerType1, typename CompilerType2, typename RegType, 1607 typename ImmType> 1608 inline void emitBinop(void (*op)(CompilerType1& compiler1, RegType rs, 1609 RegType rd), 1610 void (*opConst)(CompilerType2& compiler2, ImmType c, 1611 RegType rd), 1612 RegType (BaseCompiler::*rhsPopper)() = nullptr); 1613 1614 template <typename CompilerType, typename ValType> 1615 inline void emitTernary(void (*op)(CompilerType&, ValType src0, ValType src1, 1616 ValType srcDest)); 1617 1618 template <typename CompilerType, typename ValType> 1619 inline void emitTernary(void (*op)(CompilerType&, ValType src0, ValType src1, 1620 ValType srcDest, ValType temp)); 1621 1622 template <typename CompilerType, typename ValType> 1623 inline void emitTernaryResultLast(void (*op)(CompilerType&, ValType src0, 1624 ValType src1, ValType srcDest)); 1625 1626 template <typename R> 1627 [[nodiscard]] inline bool emitInstanceCallOp( 1628 const SymbolicAddressSignature& fn, R reader); 1629 1630 template <typename A1, typename R> 1631 [[nodiscard]] inline bool emitInstanceCallOp( 1632 const SymbolicAddressSignature& fn, R reader); 1633 1634 template <typename A1, typename A2, typename R> 1635 [[nodiscard]] inline bool emitInstanceCallOp( 1636 const SymbolicAddressSignature& fn, R reader); 1637 1638 void emitMultiplyI64(); 1639 void emitQuotientI32(); 1640 void emitQuotientU32(); 1641 void emitRemainderI32(); 1642 void emitRemainderU32(); 1643 #ifdef RABALDR_INT_DIV_I64_CALLOUT 1644 [[nodiscard]] bool emitDivOrModI64BuiltinCall(SymbolicAddress callee, 1645 ValType operandType); 1646 #else 1647 void emitQuotientI64(); 1648 void emitQuotientU64(); 1649 void emitRemainderI64(); 1650 void emitRemainderU64(); 1651 #endif 1652 void emitRotrI64(); 1653 void emitRotlI64(); 1654 void emitEqzI32(); 1655 void emitEqzI64(); 1656 template <TruncFlags flags> 1657 [[nodiscard]] bool emitTruncateF32ToI32(); 1658 template <TruncFlags flags> 1659 [[nodiscard]] bool emitTruncateF64ToI32(); 1660 #ifdef RABALDR_FLOAT_TO_I64_CALLOUT 1661 [[nodiscard]] bool emitConvertFloatingToInt64Callout(SymbolicAddress callee, 1662 ValType operandType, 1663 ValType resultType); 1664 #else 1665 template <TruncFlags flags> 1666 [[nodiscard]] bool emitTruncateF32ToI64(); 1667 template <TruncFlags flags> 1668 [[nodiscard]] bool emitTruncateF64ToI64(); 1669 #endif 1670 void emitExtendI64_8(); 1671 void emitExtendI64_16(); 1672 void emitExtendI64_32(); 1673 void emitExtendI32ToI64(); 1674 void emitExtendU32ToI64(); 1675 #ifdef RABALDR_I64_TO_FLOAT_CALLOUT 1676 [[nodiscard]] bool emitConvertInt64ToFloatingCallout(SymbolicAddress callee, 1677 ValType operandType, 1678 ValType resultType); 1679 #else 1680 void emitConvertU64ToF32(); 1681 void emitConvertU64ToF64(); 1682 #endif 1683 void emitRound(RoundingMode roundingMode, ValType operandType); 1684 1685 // Generate a call to the instance function denoted by `builtin`, passing as 1686 // args the top elements of the compiler's value stack and optionally an 1687 // Instance* too. The relationship between the top of stack and arg 1688 // ordering is as follows. If the value stack looks like this: 1689 // 1690 // A <- least recently pushed 1691 // B 1692 // C <- most recently pushed 1693 // 1694 // then the called function is expected to have signature [if an Instance* 1695 // is also to be passed]: 1696 // 1697 // static Instance::foo(Instance*, A, B, C) 1698 // 1699 // and the SymbolicAddressSignature::argTypes array will be 1700 // 1701 // {_PTR, _A, _B, _C, _END} // _PTR is for the Instance* 1702 // 1703 // (see WasmBuiltins.cpp). In short, the most recently pushed value is the 1704 // rightmost argument to the function. 1705 [[nodiscard]] bool emitInstanceCall(const SymbolicAddressSignature& builtin); 1706 1707 [[nodiscard]] bool emitMemoryGrow(); 1708 [[nodiscard]] bool emitMemorySize(); 1709 1710 [[nodiscard]] bool emitRefFunc(); 1711 [[nodiscard]] bool emitRefNull(); 1712 [[nodiscard]] bool emitRefIsNull(); 1713 [[nodiscard]] bool emitRefAsNonNull(); 1714 [[nodiscard]] bool emitBrOnNull(); 1715 [[nodiscard]] bool emitBrOnNonNull(); 1716 [[nodiscard]] bool emitCallRef(); 1717 [[nodiscard]] bool emitReturnCallRef(); 1718 1719 [[nodiscard]] bool emitAtomicCmpXchg(ValType type, Scalar::Type viewType); 1720 [[nodiscard]] bool emitAtomicLoad(ValType type, Scalar::Type viewType); 1721 [[nodiscard]] bool emitAtomicRMW(ValType type, Scalar::Type viewType, 1722 AtomicOp op); 1723 [[nodiscard]] bool emitAtomicStore(ValType type, Scalar::Type viewType); 1724 [[nodiscard]] bool emitWait(ValType type, uint32_t byteSize); 1725 [[nodiscard]] bool atomicWait(ValType type, MemoryAccessDesc* access); 1726 [[nodiscard]] bool emitNotify(); 1727 [[nodiscard]] bool atomicNotify(MemoryAccessDesc* access); 1728 [[nodiscard]] bool emitFence(); 1729 [[nodiscard]] bool emitAtomicXchg(ValType type, Scalar::Type viewType); 1730 [[nodiscard]] bool emitMemInit(); 1731 [[nodiscard]] bool emitMemCopy(); 1732 [[nodiscard]] bool memCopyCall(uint32_t dstMemIndex, uint32_t srcMemIndex); 1733 void memCopyInlineM32(); 1734 [[nodiscard]] bool emitTableCopy(); 1735 [[nodiscard]] bool emitDataOrElemDrop(bool isData); 1736 [[nodiscard]] bool emitMemFill(); 1737 [[nodiscard]] bool memFillCall(uint32_t memoryIndex); 1738 void memFillInlineM32(); 1739 [[nodiscard]] bool emitTableInit(); 1740 [[nodiscard]] bool emitTableFill(); 1741 [[nodiscard]] bool emitMemDiscard(); 1742 [[nodiscard]] bool emitTableGet(); 1743 [[nodiscard]] bool emitTableGrow(); 1744 [[nodiscard]] bool emitTableSet(); 1745 [[nodiscard]] bool emitTableSize(); 1746 1747 void emitTableBoundsCheck(uint32_t tableIndex, RegI32 address, 1748 RegPtr instance); 1749 [[nodiscard]] bool emitTableGetAnyRef(uint32_t tableIndex); 1750 [[nodiscard]] bool emitTableSetAnyRef(uint32_t tableIndex); 1751 1752 [[nodiscard]] bool emitStructNew(); 1753 [[nodiscard]] bool emitStructNewDefault(); 1754 [[nodiscard]] bool emitStructGet(FieldWideningOp wideningOp); 1755 [[nodiscard]] bool emitStructSet(); 1756 [[nodiscard]] bool emitArrayNew(); 1757 [[nodiscard]] bool emitArrayNewFixed(); 1758 [[nodiscard]] bool emitArrayNewDefault(); 1759 [[nodiscard]] bool emitArrayNewData(); 1760 [[nodiscard]] bool emitArrayNewElem(); 1761 [[nodiscard]] bool emitArrayInitData(); 1762 [[nodiscard]] bool emitArrayInitElem(); 1763 [[nodiscard]] bool emitArrayGet(FieldWideningOp wideningOp); 1764 [[nodiscard]] bool emitArraySet(); 1765 [[nodiscard]] bool emitArrayLen(); 1766 [[nodiscard]] bool emitArrayCopy(); 1767 [[nodiscard]] bool emitArrayFill(); 1768 [[nodiscard]] bool emitRefI31(); 1769 [[nodiscard]] bool emitI31Get(FieldWideningOp wideningOp); 1770 [[nodiscard]] bool emitRefTest(bool nullable); 1771 [[nodiscard]] bool emitRefCast(bool nullable); 1772 [[nodiscard]] bool emitBrOnCastCommon(bool onSuccess, 1773 uint32_t labelRelativeDepth, 1774 const ResultType& labelType, 1775 MaybeRefType sourceType, 1776 RefType destType); 1777 [[nodiscard]] bool emitBrOnCast(bool onSuccess); 1778 [[nodiscard]] bool emitAnyConvertExtern(); 1779 [[nodiscard]] bool emitExternConvertAny(); 1780 1781 // Utility classes/methods to add trap information related to 1782 // null pointer dereferences/accesses. 1783 struct NoNullCheck { 1784 static void emitNullCheck(BaseCompiler* bc, RegRef rp) {} 1785 static void emitTrapSite(BaseCompiler* bc, FaultingCodeOffset fco, 1786 TrapMachineInsn tmi) {} 1787 }; 1788 struct SignalNullCheck { 1789 static void emitNullCheck(BaseCompiler* bc, RegRef rp); 1790 static void emitTrapSite(BaseCompiler* bc, FaultingCodeOffset fco, 1791 TrapMachineInsn tmi); 1792 }; 1793 1794 // Load a pointer to the AllocSite for current bytecode offset 1795 RegPtr loadAllocSiteInstanceData(uint32_t allocSiteIndex); 1796 1797 // Gets alloc site allociated with current instruction 1798 [[nodiscard]] bool readAllocSiteIndex(uint32_t* index); 1799 1800 // Load a pointer to the SuperTypeVector for a given type index 1801 RegPtr loadSuperTypeVector(uint32_t typeIndex); 1802 1803 // Emits allocation code for a GC struct. The struct may have an out-of-line 1804 // data area; if so, `isOutlineStruct` will be true and `outlineBase` will be 1805 // allocated and must be freed. 1806 template <bool ZeroFields> 1807 bool emitStructAlloc(uint32_t typeIndex, RegRef* object, 1808 bool* isOutlineStruct, RegPtr* outlineBase, 1809 uint32_t allocSiteIndex); 1810 // Emits allocation code for a dynamically-sized GC array. 1811 template <bool ZeroFields> 1812 bool emitArrayAlloc(uint32_t typeIndex, RegRef object, RegI32 numElements, 1813 uint32_t elemSize, uint32_t allocSiteIndex); 1814 // Emits allocation code for a fixed-size GC array. 1815 template <bool ZeroFields> 1816 bool emitArrayAllocFixed(uint32_t typeIndex, RegRef object, 1817 uint32_t numElements, uint32_t elemSize, 1818 uint32_t allocSiteIndex); 1819 1820 template <typename NullCheckPolicy> 1821 RegPtr emitGcArrayGetData(RegRef rp); 1822 template <typename NullCheckPolicy> 1823 RegI32 emitGcArrayGetNumElements(RegRef rp); 1824 void emitGcArrayBoundsCheck(RegI32 index, RegI32 numElements); 1825 template <typename T, typename NullCheckPolicy> 1826 void emitGcGet(StorageType type, FieldWideningOp wideningOp, const T& src); 1827 template <typename T, typename NullCheckPolicy> 1828 void emitGcSetScalar(const T& dst, StorageType type, AnyReg value); 1829 1830 BranchIfRefSubtypeRegisters allocRegistersForBranchIfRefSubtype( 1831 RefType destType); 1832 void freeRegistersForBranchIfRefSubtype( 1833 const BranchIfRefSubtypeRegisters& regs); 1834 1835 // Write `value` to wasm struct `object`, at `areaBase + areaOffset`. The 1836 // caller must decide on the in- vs out-of-lineness before the call and set 1837 // the latter two accordingly; this routine does not take that into account. 1838 // The value in `object` is unmodified, but `areaBase` and `value` may get 1839 // trashed. 1840 template <typename NullCheckPolicy> 1841 [[nodiscard]] bool emitGcStructSet(RegRef object, RegPtr areaBase, 1842 uint32_t areaOffset, StorageType type, 1843 AnyReg value, 1844 PreBarrierKind preBarrierKind); 1845 1846 [[nodiscard]] bool emitGcArraySet(RegRef object, RegPtr data, RegI32 index, 1847 const ArrayType& array, AnyReg value, 1848 PreBarrierKind preBarrierKind, 1849 PostBarrierKind postBarrierKind); 1850 1851 #ifdef ENABLE_WASM_SIMD 1852 void emitVectorAndNot(); 1853 # ifdef ENABLE_WASM_RELAXED_SIMD 1854 void emitDotI8x16I7x16AddS(); 1855 # endif 1856 1857 void loadSplat(MemoryAccessDesc* access); 1858 void loadZero(MemoryAccessDesc* access); 1859 void loadExtend(MemoryAccessDesc* access, Scalar::Type viewType); 1860 void loadLane(MemoryAccessDesc* access, uint32_t laneIndex); 1861 void storeLane(MemoryAccessDesc* access, uint32_t laneIndex); 1862 1863 [[nodiscard]] bool emitLoadSplat(Scalar::Type viewType); 1864 [[nodiscard]] bool emitLoadZero(Scalar::Type viewType); 1865 [[nodiscard]] bool emitLoadExtend(Scalar::Type viewType); 1866 [[nodiscard]] bool emitLoadLane(uint32_t laneSize); 1867 [[nodiscard]] bool emitStoreLane(uint32_t laneSize); 1868 [[nodiscard]] bool emitVectorShuffle(); 1869 [[nodiscard]] bool emitVectorLaneSelect(); 1870 # if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64) 1871 [[nodiscard]] bool emitVectorShiftRightI64x2(); 1872 # endif 1873 #endif 1874 [[nodiscard]] bool emitCallBuiltinModuleFunc(); 1875 }; 1876 1877 } // namespace wasm 1878 } // namespace js 1879 1880 #endif // wasm_wasm_baseline_object_h