// ExportArg holds the unboxed operands to the wasm entry trampoline which can // be called through an ExportFuncPtr.
struct ExportArg {
uint64_t lo;
uint64_t hi;
};
using ExportFuncPtr = int32_t (*)(ExportArg*, Instance*);
// TypeDefInstanceData describes the runtime information associated with a // module's type definition. This is accessed directly from JIT code and the // Instance.
// The canonicalized pointer to this type definition. This is kept alive by // the type context associated with the instance. const wasm::TypeDef* typeDef;
// The supertype vector for this type definition. This is also kept alive // by the type context associated with the instance. const wasm::SuperTypeVector* superTypeVector;
// The next three fields are only meaningful for, and used by, structs and // arrays.
GCPtr<Shape*> shape; const JSClass* clasp;
// This union is only meaningful for structs and arrays, and should otherwise // be zeroed out. It exists so that allocators of structs and arrays don't // need to chase through `typeDef` to find this info. union { struct { // When `typeDef` refers to a struct type, these are copied unchanged // from fields of the same name in StructType.
uint32_t payloadOffsetIL;
uint32_t totalSizeIL;
uint32_t totalSizeOOL;
uint32_t oolPointerOffset; // Copied from StructType, and updated by GetFinalizedAllocKindForClass // (see comment on StructType::allocKind_).
gc::AllocKind allocKind;
} strukt; struct { // When `typeDef` refers to an array type, this caches the value of // `typeDef->arrayType().fieldType_.size()` (a size in bytes).
uint32_t elemSize;
} array;
} cached;
// FuncDefInstanceData maintains the per-instance hotness state for a locally // defined wasm function. This is a signed-int32 value that counts downwards // from an initially non-negative value. At the point where the value // transitions below zero (not *to* zero), we deem the owning function to // have become hot. Transitions from one negative value to any other (even // more) negative value are meaningless and should not happen. struct FuncDefInstanceData {
int32_t hotnessCounter;
};
// FuncExportInstanceData maintains the exported function JS wrapper for an // exported function. struct FuncExportInstanceData {
GCPtr<JSFunction*> func;
};
// FuncImportInstanceData describes the region of wasm global memory allocated // in the instance's thread-local storage for a function import. This is // accessed directly from JIT code and mutated by Instance as exits become // optimized and deoptimized.
struct FuncImportInstanceData { // The code to call at an import site: a wasm callee, a thunk into C++, or a // thunk into JIT code. void* code;
// The callee's Instance pointer, which must be loaded to InstanceReg // (along with any pinned registers) before calling 'code'.
Instance* instance;
// The callee function's realm.
JS::Realm* realm;
// A GC pointer which keeps the callee alive and is used to recover import // values for lazy table initialization.
GCPtr<JSObject*> callable;
static_assert(sizeof(GCPtr<JSObject*>) == sizeof(void*), "for JIT access");
// See "Wasm Function.prototype.call.bind optimization" in WasmInstance.cpp // for more information. bool isFunctionCallBind;
};
struct MemoryInstanceData { // Pointer to the memory object.
GCPtr<WasmMemoryObject*> memory;
// Pointer to the base of the memory.
uint8_t* base;
// Bounds check limit in bytes. This is 64 bits on 64-bit systems so as to // allow for heap lengths up to and beyond 4GB, and 32 bits on 32-bit systems, // where heaps are limited to 2GB. // // See "Linear memory addresses and bounds checking" in WasmMemory.cpp.
uintptr_t boundsCheckLimit;
// The default boundsCheckLimit is used for standard page sizes and also 8-bit // memory accesses on custom page sizes. These other limits are only used for // accesses on memories with custom page sizes. #ifdef ENABLE_WASM_CUSTOM_PAGE_SIZES
uintptr_t boundsCheckLimit16;
uintptr_t boundsCheckLimit32;
uintptr_t boundsCheckLimit64;
uintptr_t boundsCheckLimit128; #endif
// Whether this memory is shared or not. bool isShared;
// Total mapped size of the memory buffer, including guard pages. Stored // here to avoid touching GC objects from signal handlers.
size_t mappedSize;
};
// TableInstanceData describes the region of wasm global memory allocated in the // instance's thread-local storage which is accessed directly from JIT code // to bounds-check and index the table.
struct TableInstanceData { // Length of the table in number of elements (not bytes). Although the type is // uint64_t, the maximum value fits in 32 bits -- this value can safely be // loaded as either a 32-bit value or a 64-bit value.
uint64_t length;
// Pointer to the array of elements (which can have various representations). // For tables of anyref this is null. // For tables of functions, this is a pointer to the array of code pointers. void* elements;
};
// TagInstanceData describes the instance state associated with a tag.
// Table element for TableRepr::Func which carries both the code pointer and // a instance pointer (and thus anything reachable through the instance).
struct FunctionTableElem { // The code to call when calling this element. The table ABI is the system // ABI with the additional ABI requirements that: // - InstanceReg and any pinned registers have been loaded appropriately // - if this is a heterogeneous table that requires a signature check, // WasmTableCallSigReg holds the signature id. void* code;
// The pointer to the callee's instance's Instance. This must be loaded into // InstanceReg before calling 'code'.
Instance* instance;
};
// A collection of metrics for a `call_ref` instruction. This is tracked by // baseline when we are using lazy tiering to perform speculative inlining. // // See MacroAssembler::updateCallRefMetrics for how this is written into. // // Because it contains thread-local data and is written into without // synchronization, we cannot access this directly from our function compilers // and so we use CallRefHints for that (see WasmModuleTypes.h). struct CallRefMetrics { // We track up to NUM_SLOTS targets with associated count, plus a count for // "all other" targets, including cross-instance calls. This facilitates // knowing the total number of calls made by the instruction, which is // required in order to know whether the hottest tracked function is more // than some percentage (eg, 50%) of all calls. // // In order to keep search costs low (in the baseline code), we rely on the // fact that most call sites have distributions which are heavily skewed // towards one target. This struct is updated by the code generated in // GenerateUpdateCallRefMetricsStub. That tries to make // `targets[0]`/`counts[0]` be the hottest target, so that for most calls, // the monitoring code will only check `targets[0]` for a match. // // For NUM_SLOTS <= 2, GenerateUpdateCallRefMetricsStub's incremental-sort // heuristic maintains the `counts` array in strictly non-decreasing order. // For NUM_SLOTS > 2, in the worst case we will have counts[N+1] at most // (NUM_SLOTS - 2) larger than counts[N]. In practice the incremental // sorting heuristic is very effective and so counts are in decreasing order, // as we desire. // // Once NUM_SLOTS targets are being tracked, all new targets will be lumped // together in the `countOther` bucket. This can lead to the unfortunate // case of having NUM_SLOTS different cold targets show up first, after which // follows a different target that is hot, but cannot be inlined because it // goes in the `countOther` bucket, so its identity is unknown. This is // unlikely but could happen. The only known fix is to increase NUM_SLOTS. // // The `targets` values may be nullptr only to indicate that the slot is not // in use. No legitimate target can be nullptr. Given that the state is // updated by generated code and that code isn't entirely simple, we place // emphasis on checking invariants carefully. // // Stores of funcrefs in `targets[]`: These CallRefMetrics structs logically // belong to the Instance data, and do not require any GC barriers for two // reasons: // // 1. The pre-write barrier protects against an unmarked object being stored // into a marked object during an incremental GC. However this funcref is // from the Instance we're storing it into (see above) and so if the // instance has already been traced, this function will already have been // traced (all exported functions are kept alive by an instance cache). // // 2. The post-write barrier tracks edges from tenured objects to nursery // objects. However wasm exported functions are not nursery allocated and // so no new edge can be created. // // Overflows in `counts[]` and `countOther`: increments of these values are // not checked for overflow and so could wrap around from 2^32-1 to zero. // We ignore but tolerate this, because: // // 1. This is extremely unlikely to happen in practice, since the function // containing the call site is almost certain to get tiered up long before // any of these counters gets anywhere near the limit. // // 2. Performing saturating increments is possible, but has a minimum extra // cost of two instructions, and given (1.) it is pointless. // // This does however require that interpretation of the `counts[]` and // `countOther` values needs to be aware that zeroes could mean 2^32 or any // multiple of it. Hence a zero in `counts[]` does not necessarily mean that // the corresponding `target[]` was never called, nor is it the case that a // `countsOther` of zero means no "other" targets were observed.
static constexpr size_t NUM_SLOTS = 3;
static_assert(NUM_SLOTS >= 1); // 1 slot + others is the minimal config
// An array of pairs of (target, count) ..
GCPtr<JSFunction*> targets[NUM_SLOTS];
uint32_t counts[NUM_SLOTS]; // .. and a count for all other targets.
uint32_t countOther;
CallRefMetrics() { for (size_t i = 0; i < NUM_SLOTS; i++) {
targets[i] = nullptr;
counts[i] = 0;
}
countOther = 0;
MOZ_ASSERT(checkInvariants());
}
[[nodiscard]] bool checkInvariants() const { // If targets[N] is null, then this slot is not in use and so counts[N] // must be zero. Per comments above about overflow, the implication in the // other direction does not hold.
size_t i; for (i = 0; i < NUM_SLOTS; i++) { if (targets[i] == nullptr && counts[i] != 0) { returnfalse;
}
} // The targets/counts slots must be filled in in sequence. for (i = 0; i < NUM_SLOTS; i++) { if (targets[i] == nullptr) { break;
}
}
size_t numUsed = i; for (/*keepgoing*/; i < NUM_SLOTS; i++) { if (targets[i] != nullptr) { returnfalse;
}
} // For the slots in use, the target values must be different for (i = 0; i < numUsed; i++) { for (size_t j = i + 1; j < numUsed; j++) { if (targets[j] == targets[i]) { returnfalse;
}
}
} // Note we don't say anything about `countOther`. This gets incremented in // the cases when we (1) see a new target when all slots are already in // use, or (2) have a cross-instance call. The effect of (2) is that // `countOther` can be non-zero regardless of how many slots are in use. returntrue;
}
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.