// Container for a piece of out-of-line code, the slow path that supports an // operation. class OutOfLineCode;
// Part of the inter-bytecode state for the boolean-evaluation-for-control // optimization. struct BranchState;
// Representation of wasm local variables. using Local = BaseStackFrame::Local;
// Bitset used for simple bounds check elimination. Capping this at 64 locals // makes sense; even 32 locals would probably be OK in practice. // // For more information about BCE, see the block comment in WasmBCMemory.cpp. using BCESet = uint64_t;
// Information stored in the control node for generating exception handling // landing pads. struct CatchInfo {
uint32_t tagIndex; // Index for the associated exception.
NonAssertingLabel label; // The entry label for the handler.
using CatchInfoVector = Vector<CatchInfo, 1, SystemAllocPolicy>;
// Control node, representing labels and stack heights at join points. struct Control {
NonAssertingLabel label; // The "exit" label
NonAssertingLabel otherLabel; // Used for the "else" branch of if-then-else // and to allow delegate to jump to catches.
StackHeight stackHeight; // From BaseStackFrame
uint32_t stackSize; // Value stack height
BCESet bceSafeOnEntry; // Bounds check info flowing into the item
BCESet bceSafeOnExit; // Bounds check info flowing out of the item bool deadOnArrival; // deadCode_ was set on entry to the region bool deadThenBranch; // deadCode_ was set on exit from "then"
size_t tryNoteIndex; // For tracking try branch code ranges.
CatchInfoVector catchInfos; // Used for try-catch handlers.
size_t loopBytecodeStart; // For LT: bytecode offset of start of a loop.
CodeOffset offsetOfCtrDec; // For LT: masm offset of loop's counter decr.
// The baseline compiler tracks values on a stack of its own -- it needs to scan // that stack for spilling -- and thus has no need for the values maintained by // the iterator. struct BaseCompilePolicy { using Value = mozilla::Nothing; using ValueVector = BaseNothingVector;
// The baseline compiler uses the iterator's control stack, attaching // its own control information. using ControlItem = Control;
};
// Encapsulate the checking needed for a memory access. struct AccessCheck {
AccessCheck()
: omitBoundsCheck(false),
omitAlignmentCheck(false),
onlyPointerAlignment(false) {}
// If `omitAlignmentCheck` is true then we need check neither the // pointer nor the offset. Otherwise, if `onlyPointerAlignment` is true // then we need check only the pointer. Otherwise, check the sum of // pointer and offset.
// Encapsulate all the information about a function call. struct FunctionCall {
FunctionCall(ABIKind abiKind, RestoreState restoreState)
: abi(abiKind),
restoreState(restoreState),
abiKind(abiKind), #ifdef JS_CODEGEN_ARM
hardFP(true), #endif
frameAlignAdjustment(0),
stackArgAreaSize(0) { // The system ABI preserves the instance register (as it's in a // non-volatile register) and realm. We just need to reload the HeapReg in // case the memory has been moved.
MOZ_ASSERT_IF(abiKind == ABIKind::System,
restoreState == RestoreState::None ||
restoreState == RestoreState::PinnedRegs); if (abiKind == ABIKind::System) { // Builtin calls use the system hardFP setting on ARM32. #ifdefined(JS_CODEGEN_ARM)
hardFP = ARMFlags::UseHardFpABI();
abi.setUseHardFp(hardFP); #endif
} else { #ifdefined(JS_CODEGEN_ARM)
MOZ_ASSERT(hardFP, "The WASM ABI passes FP arguments in registers"); #endif
}
}
enumclass PreBarrierKind { // No pre-write barrier is required because the previous value is undefined.
None, // Perform a pre-write barrier to mark the previous value if an incremental // GC is underway.
Normal,
};
enumclass PostBarrierKind { // No post barrier.
None, // Add a store buffer entry if the new value requires it, but do not attempt // to remove a pre-existing entry.
Imprecise, // Remove an existing store buffer entry if the new value does not require // one. This is required to preserve invariants with HeapPtr when used for // movable storage.
Precise, // Add a store buffer entry for the entire cell (e.g. the entire struct or // array who now has a field pointing into the nursery).
WholeCell,
};
////////////////////////////////////////////////////////////////////////////// // // Wasm baseline compiler proper. // // This is a struct and not a class because there is no real benefit to hiding // anything, and because many static functions that are wrappers for masm // methods need to reach into it and would otherwise have to be declared as // friends. // // (Members generally have a '_' suffix but some don't because they are // referenced everywhere and it would be tedious to spell that out.)
struct BaseCompiler final { /////////////////////////////////////////////////////////////////////////// // // Private types
using LabelVector = Vector<NonAssertingLabel, 8, SystemAllocPolicy>;
/////////////////////////////////////////////////////////////////////////// // // Read-only and write-once members.
// Information about the locations of locals, this is set up during // initialization and read-only after that.
BaseStackFrame::LocalVector localInfo_;
// On specific platforms we sometimes need to use specific registers. const SpecificRegs specific_;
// SigD and SigF are single-entry parameter lists for f64 and f32, these are // created during initialization.
ValTypeVector SigD_;
ValTypeVector SigF_;
// Where to go to to return, bound as compilation ends.
NonAssertingLabel returnLabel_;
// Prologue and epilogue offsets, initialized during prologue and epilogue // generation and only used by the caller.
FuncOffsets offsets_;
// We call this address from the breakable point when the (per-module) debug // stub pointer in the Instance is not null.
NonAssertingLabel perFunctionDebugStub_;
uint32_t previousBreakablePoint_;
// BaselineCompileFunctions() "lends" us the StkVector to use in this // BaseCompiler object, and that is installed in |stk_| in our constructor. // This is so as to avoid having to malloc/free the vector's contents at // each creation/destruction of a BaseCompiler object. It does however mean // that we need to hold on to a reference to BaselineCompileFunctions()'s // vector, so we can swap (give) its contents back when this BaseCompiler // object is destroyed. This significantly reduces the heap turnover of the // baseline compiler. See bug 1532592.
StkVector& stkSource_;
/////////////////////////////////////////////////////////////////////////// // // Output-only data structures.
// Bump allocator for temporary memory, used for the value stack and // out-of-line code blobs. Bump-allocated memory is not freed until the end // of the compilation.
TempAllocator::Fallible alloc_;
// Machine code emitter.
MacroAssembler& masm;
// Perf spewer for annotated JIT code while profiling.
WasmBaselinePerfSpewer perfSpewer_;
// Decoder for this function, used for misc error reporting.
Decoder& decoder_;
// Opcode reader.
BaseOpIter iter_;
// Register allocator.
BaseRegAlloc ra;
// Stack frame abstraction.
BaseStackFrame fr;
// Latent out of line support code for some operations, code for these will be // emitted at the end of compilation.
Vector<OutOfLineCode*, 8, SystemAllocPolicy> outOfLine_;
// The stack maps for this compilation.
StackMaps* stackMaps_;
// Stack map state. This keeps track of live pointer slots and allows precise // stack maps to be generated at safe points.
StackMapGenerator stackMapGenerator_;
// Wasm value stack. This maps values on the wasm stack to values in the // running code and their locations. // // The value stack facilitates on-the-fly register allocation and the use of // immediates in instructions. It tracks latent constants, latent references // to locals, register contents, and values that have been flushed to the CPU // stack. // // The stack can be flushed to the CPU stack using sync(). // // The stack is a StkVector rather than a StkVector& since constantly // dereferencing a StkVector& has been shown to add 0.5% or more to the // compiler's dynamic instruction count.
StkVector stk_;
// Flag indicating that the compiler is currently in a dead code region. bool deadCode_;
// Store previously finished note to know if we need to insert a nop in // finishTryNote.
size_t mostRecentFinishedTryNoteIndex_;
/////////////////////////////////////////////////////////////////////////// // // State for bounds check elimination.
// Locals that have been bounds checked and not updated since
BCESet bceSafe_;
/////////////////////////////////////////////////////////////////////////// // // State for boolean-evaluation-for-control.
// Latent operation for branch (seen next)
LatentOp latentOp_;
// Operand type, if latentOp_ is true
ValType latentType_;
// Comparison operator, if latentOp_ == Compare, int types
Assembler::Condition latentIntCmp_;
/////////////////////////////////////////////////////////////////////////// // // Main compilation API. // // A client will create a compiler object, and then call init(), // emitFunction(), and finish() in that order.
// The casts are used by some of the ScratchRegister implementations. operator MacroAssembler&() const { return masm; } operator BaseRegAlloc&() { return ra; }
// Assert that the local at the given index has the given type, and return a // reference to the Local. inlineconst Local& localFromSlot(uint32_t slot, MIRType type);
////////////////////////////////////////////////////////////////////////////// // // Out of line code management.
///////////////////////////////////////////////////////////////////////////// // // Layering in the compiler (briefly). // // At the lowest layers are abstractions for registers (managed by the // BaseRegAlloc and the wrappers below) and the stack frame (managed by the // BaseStackFrame). // // The registers and frame are in turn used by the value abstraction, which is // implemented by the Stk type and backed by the value stack. Values may be // stored in registers, in the frame, or may be latent constants, and the // value stack handles storage mostly transparently in its push and pop // routines. // // In turn, the pop routines bring values into registers so that we can // compute on them, and the push routines move values to the stack (where they // may still reside in registers until the registers are needed or the value // must be in memory). // // Routines for managing parameters and results (for blocks or calls) may also // manipulate the stack directly. // // At the top are the code generators: methods that use the poppers and // pushers and other utilities to move values into place, and that emit code // to compute on those values or change control flow.
///////////////////////////////////////////////////////////////////////////// // // Register management. These are simply strongly-typed wrappers that // delegate to the register allocator.
// Free r if it is not invalid. inlinevoid maybeFree(RegI32 r); inlinevoid maybeFree(RegI64 r); inlinevoid maybeFree(RegF32 r); inlinevoid maybeFree(RegF64 r); inlinevoid maybeFree(RegRef r); inlinevoid maybeFree(RegPtr r); #ifdef ENABLE_WASM_SIMD inlinevoid maybeFree(RegV128 r); #endif
// On 64-bit systems, `except` must equal r and this is a no-op. On 32-bit // systems, `except` must equal the high or low part of a pair and the other // part of the pair is freed. inlinevoid freeI64Except(RegI64 r, RegI32 except);
// Return the 32-bit low part of the 64-bit register, do not free anything. inline RegI32 fromI64(RegI64 r);
// If r is valid, return fromI64(r), otherwise an invalid RegI32. inline RegI32 maybeFromI64(RegI64 r);
#ifdef JS_PUNBOX64 // On 64-bit systems, reinterpret r as 64-bit. inline RegI64 fromI32(RegI32 r); #endif
// Widen r to 64 bits; this may allocate another register to form a pair. // Note this does not generate code for sign/zero extension. inline RegI64 widenI32(RegI32 r);
// Narrow r to 32 bits; this may free part of a pair. Note this does not // generate code to canonicalize the value on 64-bit systems. inline RegI32 narrowI64(RegI64 r); inline RegI32 narrowRef(RegRef r);
// Return the 32-bit low part of r. inline RegI32 lowPart(RegI64 r);
// On 64-bit systems, return an invalid register. On 32-bit systems, return // the low part of a pair. inline RegI32 maybeHighPart(RegI64 r);
////////////////////////////////////////////////////////////////////////////// // // Values and value stack: Low-level methods for moving Stk values of specific // kinds to registers.
////////////////////////////////////////////////////////////////////////// // // Values and value stack: Mid-level routines for moving Stk values of any // kind to registers.
////////////////////////////////////////////////////////////////////// // // Value stack: stack management.
// Flush all local and register value stack elements to memory. inlinevoid sync();
// Save a register on the value stack temporarily. void saveTempPtr(const RegPtr& r);
// Restore a temporarily saved register from the value stack. void restoreTempPtr(const RegPtr& r);
// This is an optimization used to avoid calling sync for setLocal: if the // local does not exist unresolved on the value stack then we can skip the // sync. inlinebool hasLocal(uint32_t slot);
// Sync the local if necessary. (This currently syncs everything if a sync is // needed at all.) inlinevoid syncLocal(uint32_t slot);
// Return the amount of execution stack consumed by the top numval // values on the value stack. inline size_t stackConsumed(size_t numval);
// Drop one value off the stack, possibly also moving the physical stack // pointer. inlinevoid dropValue();
#ifdef DEBUG // Check that we're not leaking registers by comparing the // state of the stack + available registers with the set of // all available registers.
// Call this between opcodes. void performRegisterLeakCheck();
// This can be called at any point, really, but typically just after // performRegisterLeakCheck(). void assertStackInvariants() const;
// Count the number of memory references on the value stack. inline size_t countMemRefsOnStk();
// Check if there are any live registers on the value stack. inlinebool hasLiveRegsOnStk();
// Print the stack to stderr. void showStack(constchar* who) const; #endif
////////////////////////////////////////////////////////////////////// // // Value stack: pushers of values.
// Template variation of the foregoing, for use by templated emitters. template <typename RegType> inlinevoid push(RegType item);
// Push a constant value onto the stack. pushI32 can also take uint32_t, and // pushI64 can take uint64_t; the semantics are the same. Appropriate sign // extension for a 32-bit value on a 64-bit architecture happens when the // value is popped, see the definition of moveImm32. inlinevoid pushI32(int32_t v); inlinevoid pushI64(int64_t v); inlinevoid pushRef(intptr_t v); inlinevoid pushPtr(intptr_t v); inlinevoid pushF64(double v); inlinevoid pushF32(float v); #ifdef ENABLE_WASM_SIMD inlinevoid pushV128(V128 v); #endif inlinevoid pushConstRef(intptr_t v);
// Push the local slot onto the stack. The slot will not be read here; it // will be read when it is consumed, or when a side effect to the slot forces // its value to be saved. inlinevoid pushLocalI32(uint32_t slot); inlinevoid pushLocalI64(uint32_t slot); inlinevoid pushLocalRef(uint32_t slot); inlinevoid pushLocalF64(uint32_t slot); inlinevoid pushLocalF32(uint32_t slot); #ifdef ENABLE_WASM_SIMD inlinevoid pushLocalV128(uint32_t slot); #endif
// Push an U32 as an I64, zero-extending it in the process inlinevoid pushU32AsI64(RegI32 rs);
////////////////////////////////////////////////////////////////////// // // Value stack: poppers and peekers of values.
// Pop some value off the stack. inline AnyReg popAny(); inline AnyReg popAny(AnyReg specific);
// Call only from other popI32() variants. v must be the stack top. May pop // the CPU stack. inlinevoid popI32(const Stk& v, RegI32 dest);
#ifdef ENABLE_WASM_SIMD // Call only from other popV128() variants. v must be the stack top. May pop // the CPU stack. inlinevoid popV128(const Stk& v, RegV128 dest);
// Templated variation of the foregoing, for use by templated emitters. template <typename RegType> inline RegType pop();
// Constant poppers will return true and pop the value if the stack top is a // constant of the appropriate type; otherwise pop nothing and return false.
[[nodiscard]] inlinebool hasConst() const;
[[nodiscard]] inlinebool popConst(int32_t* c);
[[nodiscard]] inlinebool popConst(int64_t* c);
[[nodiscard]] inlinebool peekConst(int32_t* c);
[[nodiscard]] inlinebool peekConst(int64_t* c);
[[nodiscard]] inlinebool peek2xConst(int32_t* c0, int32_t* c1);
[[nodiscard]] inlinebool popConstPositivePowerOfTwo(int32_t* c,
uint_fast8_t* power,
int32_t cutoff);
[[nodiscard]] inlinebool popConstPositivePowerOfTwo(int64_t* c,
uint_fast8_t* power,
int64_t cutoff);
// Pop to a specific register inline RegI32 popI32ToSpecific(RegI32 specific); inline RegI64 popI64ToSpecific(RegI64 specific);
#ifdef JS_CODEGEN_ARM // Pop an I64 as a valid register pair. inline RegI64 popI64Pair(); #endif
// Pop an I64 but narrow it and return the narrowed part. inline RegI32 popI64ToI32(); inline RegI32 popI64ToSpecificI32(RegI32 specific);
// Pop an I32 or I64 as an I64. The value is zero extended out to 64-bits. inline RegI64 popAddressToInt64(AddressType addressType);
// Pop an I32 or I64 as an I32. The value is clamped to UINT32_MAX to ensure // that it trips bounds checks. inline RegI32 popTableAddressToClampedInt32(AddressType addressType);
// A combined push/pop that replaces an I32 or I64 on the stack with a clamped // I32, which will trip bounds checks if out of I32 range. inlinevoid replaceTableAddressWithClampedInt32(AddressType addressType);
// Pop the stack until it has the desired size, but do not move the physical // stack pointer. inlinevoid popValueStackTo(uint32_t stackSize);
// Pop the given number of elements off the value stack, but do not move // the physical stack pointer. inlinevoid popValueStackBy(uint32_t items);
// Peek into the stack at relativeDepth from the top. inline Stk& peek(uint32_t relativeDepth);
// Peek the reference value at the specified depth and load it into a // register. inlinevoid peekRefAt(uint32_t depth, RegRef dest);
// Peek at the value on the top of the stack and return true if it is a Local // of any type.
[[nodiscard]] inlinebool peekLocal(uint32_t* local);
//////////////////////////////////////////////////////////////////////////// // // Block parameters and results. // // Blocks may have multiple parameters and multiple results. Blocks can also // be the target of branches: the entry for loops, and the exit for // non-loops. // // Passing multiple values to a non-branch target (i.e., the entry of a // "block") falls out naturally: any items on the value stack can flow // directly from one block to another. // // However, for branch targets, we need to allocate well-known locations for // the branch values. The approach taken in the baseline compiler is to // allocate registers to the top N values (currently N=1), and then stack // locations for the rest. //
// Types of result registers that interest us for result-manipulating // functions. enumclass ResultRegKind { // General and floating result registers.
All,
// General result registers only.
OnlyGPRs
};
// This is a flag ultimately intended for popBlockResults() that specifies how // the CPU stack should be handled after the result values have been // processed. enumclass ContinuationKind { // Adjust the stack for a fallthrough: do nothing.
Fallthrough,
// Adjust the stack for a jump: make the stack conform to the // expected stack at the target
Jump
};
// TODO: It's definitely disputable whether the result register management is // hot enough to warrant inlining at the outermost level.
// This function is similar to popBlockResults, but additionally handles the // implicit exception pointer that is pushed to the value stack on entry to // a catch handler by dropping it appropriately. void popCatchResults(ResultType type, StackHeight stackBase);
// A combination of popBlockResults + pushBlockResults, used when entering a // block with a control-flow join (loops) or split (if) to shuffle the // fallthrough block parameters into the locations expected by the // continuation. // // This function should only be called when entering a block with a // control-flow join at the entry, where there are no live temporaries in // the current block.
[[nodiscard]] bool topBlockParams(ResultType type);
// A combination of popBlockResults + pushBlockResults, used before branches // where we don't know the target (br_if / br_table). If and when the branch // is taken, the stack results will be shuffled down into place. For br_if // that has fallthrough, the parameters for the untaken branch flow through to // the continuation.
[[nodiscard]] bool topBranchParams(ResultType type, StackHeight* height);
// Conditional branches with fallthrough are preceded by a topBranchParams, so // we know that there are no stack results that need to be materialized. In // that case, we can just shuffle the whole block down before popping the // stack. void shuffleStackResultsBeforeBranch(StackHeight srcHeight,
StackHeight destHeight, ResultType type);
// If in debug mode, adds LeaveFrame breakpoint. bool insertDebugCollapseFrame();
// Various methods for creating a stackmap. Stackmaps are indexed by the // lowest address of the instruction immediately *after* the instruction of // interest. In practice that means either: the return point of a call, the // instruction immediately after a trap instruction (the "resume" // instruction), or the instruction immediately following a no-op (when // debugging is enabled).
// Create a vanilla stackmap.
[[nodiscard]] bool createStackMap(constchar* who);
// Create a stackmap as vanilla, but for a custom assembler offset.
[[nodiscard]] bool createStackMap(constchar* who,
CodeOffset assemblerOffset);
// Create a stack map as vanilla, and note the presence of a ref-typed // DebugFrame on the stack.
[[nodiscard]] bool createStackMap( constchar* who, HasDebugFrameWithLiveRefs debugFrameWithLiveRefs);
// Creates a stack map for an aborting trap instruction that will be emitted // OOL.
[[nodiscard]] bool createAbortingOutOfLineTrapStackMap(StackMap** result);
//////////////////////////////////////////////////////////// // // Control stack
//////////////////////////////////////////////////////////// // // Debugger API
// Insert a breakpoint almost anywhere. This will create a call, with all the // overhead that entails. void insertBreakablePoint(CallSiteKind kind);
// Insert code at the end of a function for breakpoint filtering. void insertPerFunctionDebugStub();
// Debugger API used at the return point: shuffle register return values off // to memory for the debugger to see; and get them back again. void saveRegisterReturnValues(const ResultType& resultType); void restoreRegisterReturnValues(const ResultType& resultType);
////////////////////////////////////////////////////////////////////// // // Function prologue and epilogue.
// Set up and tear down frame, execute prologue and epilogue.
[[nodiscard]] bool beginFunction();
[[nodiscard]] bool endFunction();
// Move return values to memory before returning, as appropriate void popStackReturnValues(const ResultType& resultType);
// A flag passed to emitCallArgs, describing how the value stack is laid out. enumclass CalleeOnStack { // After the arguments to the call, there is a callee pushed onto value // stack. This is only the case for callIndirect. To get the arguments to // the call, emitCallArgs has to reach one element deeper into the value // stack, to skip the callee. True,
// No callee on the stack. False
}; // The typename T for emitCallArgs can be one of the following: // NormalCallResults, TailCallResults, or NoCallResults. template <typename T>
[[nodiscard]] bool emitCallArgs(const ValTypeVector& argTypes, T results,
FunctionCall* baselineCall,
CalleeOnStack calleeOnStack);
////////////////////////////////////////////////////////////////////// // // Immediate-to-register moves. // // The compiler depends on moveImm32() clearing the high bits of a 64-bit // register on 64-bit systems except MIPS64 And LoongArch64 where high bits // are sign extended from lower bits, see doc block "64-bit GPRs carrying // 32-bit values" in MacroAssembler.h.
// Decrement the per-instance function hotness counter by `step` and request // optimized compilation for this function if the updated counter is negative // when regarded as an int32_t. The amount to decrement is to be filled in // later by ::patchHotnessCheck.
[[nodiscard]] Maybe<CodeOffset> addHotnessCheck();
// Patch in the counter decrement for a hotness check, using the offset // previously obtained from ::addHotnessCheck. We require 1 <= `step` <= 127. void patchHotnessCheck(CodeOffset offset, uint32_t step);
// Check the interrupt flag, trap if it is set.
[[nodiscard]] bool addInterruptCheck();
// Check that the value is not zero, trap if it is. void checkDivideByZero(RegI32 rhs); void checkDivideByZero(RegI64 r);
// Check that a signed division will not overflow, trap or flush-to-zero if it // will according to `zeroOnOverflow`. void checkDivideSignedOverflow(RegI32 rhs, RegI32 srcDest, Label* done, bool zeroOnOverflow); void checkDivideSignedOverflow(RegI64 rhs, RegI64 srcDest, Label* done, bool zeroOnOverflow);
// Emit a jump table to be used by tableSwitch() void jumpTable(const LabelVector& labels, Label* theTable);
// Emit a table switch, `theTable` is the jump table. void tableSwitch(Label* theTable, RegI32 switchValue, Label* dispatchCode);
// Compare i64 and set an i32 boolean result according to the condition. inlinevoid cmp64Set(Assembler::Condition cond, RegI64 lhs, RegI64 rhs,
RegI32 dest);
// These are just wrappers around assembler functions, but without // type-specific names, and using our register abstractions for better type // discipline. inlinevoid branchTo(Assembler::DoubleCondition c, RegF64 lhs, RegF64 rhs,
Label* l); inlinevoid branchTo(Assembler::DoubleCondition c, RegF32 lhs, RegF32 rhs,
Label* l); inlinevoid branchTo(Assembler::Condition c, RegI32 lhs, RegI32 rhs,
Label* l); inlinevoid branchTo(Assembler::Condition c, RegI32 lhs, Imm32 rhs, Label* l); inlinevoid branchTo(Assembler::Condition c, RegI64 lhs, RegI64 rhs,
Label* l); inlinevoid branchTo(Assembler::Condition c, RegI64 lhs, Imm64 rhs, Label* l); inlinevoid branchTo(Assembler::Condition c, RegRef lhs, ImmWord rhs,
Label* l);
// Helpers for accessing Instance::baselineScratchWords_. Note that Word // and I64 versions of these routines access the same area and it is up to // the caller to use it in some way which makes sense.
// Store/load `r`, a machine word, to/from the `index`th scratch storage // slot in the current Instance. `instancePtr` must point at the current // Instance; it will not be modified. For ::stashWord, `r` must not be the // same as `instancePtr`. void stashWord(RegPtr instancePtr, size_t index, RegPtr r); void unstashWord(RegPtr instancePtr, size_t index, RegPtr r);
#ifdef JS_CODEGEN_X86 // Store r in instance scratch storage after first loading the instance from // the frame into the regForInstance. regForInstance must be neither of the // registers in r. void stashI64(RegPtr regForInstance, RegI64 r);
// Load r from the instance scratch storage after first loading the instance // from the frame into the regForInstance. regForInstance can be one of the // registers in r. void unstashI64(RegPtr regForInstance, RegI64 r); #endif
////////////////////////////////////////////////////////////////////// // // Code generators for actual operations.
// Fold offsets into ptr and bounds check as necessary. The instance will be // valid in cases where it's needed. template <typename RegAddressType> void prepareMemoryAccess(MemoryAccessDesc* access, AccessCheck* check,
RegPtr instance, RegAddressType ptr);
// Some consumers depend on the returned Address not incorporating instance, // as instance may be the scratch register. template <typename RegAddressType>
Address prepareAtomicMemoryAccess(MemoryAccessDesc* access,
AccessCheck* check, RegPtr instance,
RegAddressType ptr);
// ptr and dest may be the same iff dest is I32. // This may destroy ptr even if ptr and dest are not the same. void executeLoad(MemoryAccessDesc* access, AccessCheck* check,
RegPtr instance, RegPtr memoryBase, RegI32 ptr, AnyReg dest,
RegI32 temp); void load(MemoryAccessDesc* access, AccessCheck* check, RegPtr instance,
RegPtr memoryBase, RegI32 ptr, AnyReg dest, RegI32 temp); void load(MemoryAccessDesc* access, AccessCheck* check, RegPtr instance,
RegPtr memoryBase, RegI64 ptr, AnyReg dest, RegI64 temp);
//////////////////////////////////////////////////////////////////////////// // // Platform-specific popping and register targeting.
// The simple popping methods pop values into targeted registers; the caller // can free registers using standard functions. These are always called // popXForY where X says something about types and Y something about the // operation being targeted.
// Retrieve the current bytecodeOffset. inline BytecodeOffset bytecodeOffset() const;
// Get a trap site description for a trap that would occur in the current // opcode. inline TrapSiteDesc trapSiteDesc() const;
// Generate a trap instruction for the current bytecodeOffset. inlinevoid trap(Trap t);
// Generate a trap instruction for given location and stack map. inlinevoid trap(Trap t, const TrapSiteDesc& trapSite, StackMap* stackMap);
// Abstracted helper for throwing, used for throw, rethrow, and rethrowing // at the end of a series of catch blocks (if none matched the exception).
[[nodiscard]] bool throwFrom(RegRef exn);
// Load the specified tag object from the Instance. void loadTag(RegPtr instance, uint32_t tagIndex, RegRef tagDst);
// Load the pending exception state from the Instance and then reset it. void consumePendingException(RegPtr instance, RegRef* exnDst, RegRef* tagDst);
// This emits a GC pre-write barrier. The pre-barrier is needed when we // replace a member field with a new value, and the previous field value // might have no other referents, and incremental GC is ongoing. The field // might belong to an object or be a stack slot or a register or a heap // allocated value. // // let obj = { field: previousValue }; // obj.field = newValue; // previousValue must be marked with a pre-barrier. // // The `valueAddr` is the address of the location that we are about to // update. This function preserves that register. void emitPreBarrier(RegPtr valueAddr);
// These emit GC post-write barriers. The post-barrier is needed when we // replace a member field with a new value, the new value is in the nursery, // and the containing object is a tenured object. The field (or the entire // containing object) must then be added to the store buffer so that the // nursery can be correctly collected. The field might belong to an object or // be a stack slot or a register or a heap allocated value. // // For the difference between 'precise' and 'imprecise', look at the // documentation on PostBarrierKind.
// Emits a post-write barrier that creates a whole-cell store buffer entry. // See above for details. // // - `object` is a pointer to the object that contains the field. This // register is preserved by this function. // - `value` is the value that was stored in the field. This register is // preserved by this function. // - `temp` is consumed by this function.
[[nodiscard]] bool emitPostBarrierWholeCell(RegRef object, RegRef value,
RegPtr temp);
// Emits a post-write barrier of type WasmAnyRefEdge, imprecisely. See above // for details. // // - `object` is a pointer to the object that contains the field. It is used, // if present, to skip adding a store buffer entry when the containing // object is in the nursery. This register is preserved by this function. // - `valueAddr` is the address of the location that we are writing to. This // register is consumed by this function. // - `value` is the value that was stored in the field. This register is // preserved by this function.
[[nodiscard]] bool emitPostBarrierEdgeImprecise( const mozilla::Maybe<RegRef>& object, RegPtr valueAddr, RegRef value);
// Emits a post-write barrier of type WasmAnyRefEdge, precisely. See above for // details. // // - `object` is a pointer to the object that contains the field. It is used, // if present, to skip adding a store buffer entry when the containing // object is in the nursery. This register is preserved by this function. // - `valueAddr` is the address of the location that we are writing to. This // register is consumed by this function. // - `prevValue` is the value that existed in the field before `value` was // stored. This register is consumed by this function. // - `value` is the value that was stored in the field. This register is // preserved by this function.
[[nodiscard]] bool emitPostBarrierEdgePrecise( const mozilla::Maybe<RegRef>& object, RegPtr valueAddr, RegRef prevValue,
RegRef value);
// Emits a store to a JS object pointer at the address `valueAddr`, which is // inside the GC cell `object`. // // Preserves `object` and `value`. Consumes `valueAddr`.
[[nodiscard]] bool emitBarrieredStore(const mozilla::Maybe<RegRef>& object,
RegPtr valueAddr, RegRef value,
PreBarrierKind preBarrierKind,
PostBarrierKind postBarrierKind);
// Emits a store of nullptr to a JS object pointer at the address valueAddr. // Preserves `valueAddr`. void emitBarrieredClear(RegPtr valueAddr);
//////////////////////////////////////////////////////////// // // Machinery for optimized conditional branches. See comments in the // implementation.
void setLatentCompare(Assembler::Condition compareOp, ValType operandType); void setLatentCompare(Assembler::DoubleCondition compareOp,
ValType operandType); void setLatentEqz(ValType operandType); bool hasLatentOp() const; void resetLatentOp(); // Jump to the given branch, passing results, if the condition, `cond` // matches between `lhs` and `rhs. template <typename Cond, typename Lhs, typename Rhs>
[[nodiscard]] bool jumpConditionalWithResults(BranchState* b, Cond cond,
Lhs lhs, Rhs rhs); // Jump to the given branch, passing results, if the WasmGcObject, `object`, // is a subtype of `destType`.
[[nodiscard]] bool jumpConditionalWithResults(BranchState* b, RegRef object,
MaybeRefType sourceType,
RefType destType, bool onSuccess); template <typename Cond>
[[nodiscard]] bool sniffConditionalControlCmp(Cond compareOp,
ValType operandType);
[[nodiscard]] bool sniffConditionalControlEqz(ValType operandType); void emitBranchSetup(BranchState* b);
[[nodiscard]] bool emitBranchPerform(BranchState* b);
// Generate a call to the instance function denoted by `builtin`, passing as // args the top elements of the compiler's value stack and optionally an // Instance* too. The relationship between the top of stack and arg // ordering is as follows. If the value stack looks like this: // // A <- least recently pushed // B // C <- most recently pushed // // then the called function is expected to have signature [if an Instance* // is also to be passed]: // // static Instance::foo(Instance*, A, B, C) // // and the SymbolicAddressSignature::argTypes array will be // // {_PTR, _A, _B, _C, _END} // _PTR is for the Instance* // // (see WasmBuiltins.cpp). In short, the most recently pushed value is the // rightmost argument to the function.
[[nodiscard]] bool emitInstanceCall(const SymbolicAddressSignature& builtin);
// Load a pointer to the AllocSite for current bytecode offset
RegPtr loadAllocSiteInstanceData(uint32_t allocSiteIndex);
// Gets alloc site allociated with current instruction
[[nodiscard]] bool readAllocSiteIndex(uint32_t* index);
// Load a pointer to the SuperTypeVector for a given type index
RegPtr loadSuperTypeVector(uint32_t typeIndex);
// Emits allocation code for a GC struct. The struct may have an out-of-line // data area; if so, `isOutlineStruct` will be true and `outlineBase` will be // allocated and must be freed. template <bool ZeroFields> bool emitStructAlloc(uint32_t typeIndex, RegRef* object, bool* isOutlineStruct, RegPtr* outlineBase,
uint32_t allocSiteIndex); // Emits allocation code for a dynamically-sized GC array. template <bool ZeroFields> bool emitArrayAlloc(uint32_t typeIndex, RegRef object, RegI32 numElements,
uint32_t elemSize, uint32_t allocSiteIndex); // Emits allocation code for a fixed-size GC array. template <bool ZeroFields> bool emitArrayAllocFixed(uint32_t typeIndex, RegRef object,
uint32_t numElements, uint32_t elemSize,
uint32_t allocSiteIndex);
// Write `value` to wasm struct `object`, at `areaBase + areaOffset`. The // caller must decide on the in- vs out-of-lineness before the call and set // the latter two accordingly; this routine does not take that into account. // The value in `object` is unmodified, but `areaBase` and `value` may get // trashed. template <typename NullCheckPolicy>
[[nodiscard]] bool emitGcStructSet(RegRef object, RegPtr areaBase,
uint32_t areaOffset, StorageType type,
AnyReg value,
PreBarrierKind preBarrierKind);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.