WasmInstanceData.h (13906B)
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 2021 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 #ifndef wasm_instance_data_h 20 #define wasm_instance_data_h 21 22 #include <stdint.h> 23 24 #include "NamespaceImports.h" 25 26 #include "gc/Pretenuring.h" 27 #include "js/Utility.h" 28 #include "vm/JSFunction.h" 29 #include "wasm/WasmInstance.h" 30 #include "wasm/WasmMemory.h" 31 #include "wasm/WasmTypeDecls.h" 32 33 namespace js { 34 namespace wasm { 35 36 // ExportArg holds the unboxed operands to the wasm entry trampoline which can 37 // be called through an ExportFuncPtr. 38 39 struct ExportArg { 40 uint64_t lo; 41 uint64_t hi; 42 }; 43 44 using ExportFuncPtr = int32_t (*)(ExportArg*, Instance*); 45 46 // TypeDefInstanceData describes the runtime information associated with a 47 // module's type definition. This is accessed directly from JIT code and the 48 // Instance. 49 50 struct TypeDefInstanceData { 51 TypeDefInstanceData() 52 : typeDef(nullptr), 53 superTypeVector(nullptr), 54 shape(nullptr), 55 clasp(nullptr) { 56 memset(&cached, 0, sizeof(cached)); 57 cached.strukt.allocKind = gc::AllocKind::INVALID; 58 } 59 60 // The canonicalized pointer to this type definition. This is kept alive by 61 // the type context associated with the instance. 62 const wasm::TypeDef* typeDef; 63 64 // The supertype vector for this type definition. This is also kept alive 65 // by the type context associated with the instance. 66 const wasm::SuperTypeVector* superTypeVector; 67 68 // The next three fields are only meaningful for, and used by, structs and 69 // arrays. 70 GCPtr<Shape*> shape; 71 const JSClass* clasp; 72 73 // This union is only meaningful for structs and arrays, and should otherwise 74 // be zeroed out. It exists so that allocators of structs and arrays don't 75 // need to chase through `typeDef` to find this info. 76 union { 77 struct { 78 // When `typeDef` refers to a struct type, these are copied unchanged 79 // from fields of the same name in StructType. 80 uint32_t payloadOffsetIL; 81 uint32_t totalSizeIL; 82 uint32_t totalSizeOOL; 83 uint32_t oolPointerOffset; 84 // Copied from StructType, and updated by GetFinalizedAllocKindForClass 85 // (see comment on StructType::allocKind_). 86 gc::AllocKind allocKind; 87 } strukt; 88 struct { 89 // When `typeDef` refers to an array type, this caches the value of 90 // `typeDef->arrayType().fieldType_.size()` (a size in bytes). 91 uint32_t elemSize; 92 } array; 93 } cached; 94 95 static constexpr size_t offsetOfShape() { 96 return offsetof(TypeDefInstanceData, shape); 97 } 98 static constexpr size_t offsetOfSuperTypeVector() { 99 return offsetof(TypeDefInstanceData, superTypeVector); 100 } 101 static constexpr size_t offsetOfArrayElemSize() { 102 return offsetof(TypeDefInstanceData, cached.array.elemSize); 103 } 104 }; 105 106 // FuncDefInstanceData maintains the per-instance hotness state for a locally 107 // defined wasm function. This is a signed-int32 value that counts downwards 108 // from an initially non-negative value. At the point where the value 109 // transitions below zero (not *to* zero), we deem the owning function to 110 // have become hot. Transitions from one negative value to any other (even 111 // more) negative value are meaningless and should not happen. 112 struct FuncDefInstanceData { 113 int32_t hotnessCounter; 114 }; 115 116 // FuncExportInstanceData maintains the exported function JS wrapper for an 117 // exported function. 118 struct FuncExportInstanceData { 119 GCPtr<JSFunction*> func; 120 }; 121 122 // FuncImportInstanceData describes the region of wasm global memory allocated 123 // in the instance's thread-local storage for a function import. This is 124 // accessed directly from JIT code and mutated by Instance as exits become 125 // optimized and deoptimized. 126 127 struct FuncImportInstanceData { 128 // The code to call at an import site: a wasm callee, a thunk into C++, or a 129 // thunk into JIT code. 130 void* code; 131 132 // The callee's Instance pointer, which must be loaded to InstanceReg 133 // (along with any pinned registers) before calling 'code'. 134 Instance* instance; 135 136 // The callee function's realm. 137 JS::Realm* realm; 138 139 // A GC pointer which keeps the callee alive and is used to recover import 140 // values for lazy table initialization. 141 GCPtr<JSObject*> callable; 142 static_assert(sizeof(GCPtr<JSObject*>) == sizeof(void*), "for JIT access"); 143 144 // See "Wasm Function.prototype.call.bind optimization" in WasmInstance.cpp 145 // for more information. 146 bool isFunctionCallBind; 147 }; 148 149 struct MemoryInstanceData { 150 // Pointer to the memory object. 151 GCPtr<WasmMemoryObject*> memory; 152 153 // Pointer to the base of the memory. 154 uint8_t* base; 155 156 // Bounds check limit in bytes. This is 64 bits on 64-bit systems so as to 157 // allow for heap lengths up to and beyond 4GB, and 32 bits on 32-bit systems, 158 // where heaps are limited to 2GB. 159 // 160 // See "Linear memory addresses and bounds checking" in WasmMemory.cpp. 161 uintptr_t boundsCheckLimit; 162 163 // The default boundsCheckLimit is used for standard page sizes and also 8-bit 164 // memory accesses on custom page sizes. These other limits are only used for 165 // accesses on memories with custom page sizes. 166 #ifdef ENABLE_WASM_CUSTOM_PAGE_SIZES 167 uintptr_t boundsCheckLimit16; 168 uintptr_t boundsCheckLimit32; 169 uintptr_t boundsCheckLimit64; 170 uintptr_t boundsCheckLimit128; 171 #endif 172 173 // Whether this memory is shared or not. 174 bool isShared; 175 }; 176 177 // TableInstanceData describes the region of wasm global memory allocated in the 178 // instance's thread-local storage which is accessed directly from JIT code 179 // to bounds-check and index the table. 180 181 struct TableInstanceData { 182 // Length of the table in number of elements (not bytes). Although the type is 183 // uint64_t, the maximum value fits in 32 bits -- this value can safely be 184 // loaded as either a 32-bit value or a 64-bit value. 185 uint64_t length; 186 187 // Pointer to the array of elements (which can have various representations). 188 // For tables of anyref this is null. 189 // For tables of functions, this is a pointer to the array of code pointers. 190 void* elements; 191 }; 192 193 // TagInstanceData describes the instance state associated with a tag. 194 195 struct TagInstanceData { 196 GCPtr<WasmTagObject*> object; 197 }; 198 199 // Table element for TableRepr::Func which carries both the code pointer and 200 // a instance pointer (and thus anything reachable through the instance). 201 202 struct FunctionTableElem { 203 // The code to call when calling this element. The table ABI is the system 204 // ABI with the additional ABI requirements that: 205 // - InstanceReg and any pinned registers have been loaded appropriately 206 // - if this is a heterogeneous table that requires a signature check, 207 // WasmTableCallSigReg holds the signature id. 208 void* code; 209 210 // The pointer to the callee's instance's Instance. This must be loaded into 211 // InstanceReg before calling 'code'. 212 Instance* instance; 213 }; 214 215 // A collection of metrics for a `call_ref` instruction. This is tracked by 216 // baseline when we are using lazy tiering to perform speculative inlining. 217 // 218 // See MacroAssembler::updateCallRefMetrics for how this is written into. 219 // 220 // Because it contains thread-local data and is written into without 221 // synchronization, we cannot access this directly from our function compilers 222 // and so we use CallRefHints for that (see WasmModuleTypes.h). 223 struct CallRefMetrics { 224 // We track up to NUM_SLOTS targets with associated count, plus a count for 225 // "all other" targets, including cross-instance calls. This facilitates 226 // knowing the total number of calls made by the instruction, which is 227 // required in order to know whether the hottest tracked function is more 228 // than some percentage (eg, 50%) of all calls. 229 // 230 // In order to keep search costs low (in the baseline code), we rely on the 231 // fact that most call sites have distributions which are heavily skewed 232 // towards one target. This struct is updated by the code generated in 233 // GenerateUpdateCallRefMetricsStub. That tries to make 234 // `targets[0]`/`counts[0]` be the hottest target, so that for most calls, 235 // the monitoring code will only check `targets[0]` for a match. 236 // 237 // For NUM_SLOTS <= 2, GenerateUpdateCallRefMetricsStub's incremental-sort 238 // heuristic maintains the `counts` array in strictly non-decreasing order. 239 // For NUM_SLOTS > 2, in the worst case we will have counts[N+1] at most 240 // (NUM_SLOTS - 2) larger than counts[N]. In practice the incremental 241 // sorting heuristic is very effective and so counts are in decreasing order, 242 // as we desire. 243 // 244 // Once NUM_SLOTS targets are being tracked, all new targets will be lumped 245 // together in the `countOther` bucket. This can lead to the unfortunate 246 // case of having NUM_SLOTS different cold targets show up first, after which 247 // follows a different target that is hot, but cannot be inlined because it 248 // goes in the `countOther` bucket, so its identity is unknown. This is 249 // unlikely but could happen. The only known fix is to increase NUM_SLOTS. 250 // 251 // The `targets` values may be nullptr only to indicate that the slot is not 252 // in use. No legitimate target can be nullptr. Given that the state is 253 // updated by generated code and that code isn't entirely simple, we place 254 // emphasis on checking invariants carefully. 255 // 256 // Stores of funcrefs in `targets[]`: These CallRefMetrics structs logically 257 // belong to the Instance data, and do not require any GC barriers for two 258 // reasons: 259 // 260 // 1. The pre-write barrier protects against an unmarked object being stored 261 // into a marked object during an incremental GC. However this funcref is 262 // from the Instance we're storing it into (see above) and so if the 263 // instance has already been traced, this function will already have been 264 // traced (all exported functions are kept alive by an instance cache). 265 // 266 // 2. The post-write barrier tracks edges from tenured objects to nursery 267 // objects. However wasm exported functions are not nursery allocated and 268 // so no new edge can be created. 269 // 270 // Overflows in `counts[]` and `countOther`: increments of these values are 271 // not checked for overflow and so could wrap around from 2^32-1 to zero. 272 // We ignore but tolerate this, because: 273 // 274 // 1. This is extremely unlikely to happen in practice, since the function 275 // containing the call site is almost certain to get tiered up long before 276 // any of these counters gets anywhere near the limit. 277 // 278 // 2. Performing saturating increments is possible, but has a minimum extra 279 // cost of two instructions, and given (1.) it is pointless. 280 // 281 // This does however require that interpretation of the `counts[]` and 282 // `countOther` values needs to be aware that zeroes could mean 2^32 or any 283 // multiple of it. Hence a zero in `counts[]` does not necessarily mean that 284 // the corresponding `target[]` was never called, nor is it the case that a 285 // `countsOther` of zero means no "other" targets were observed. 286 287 static constexpr size_t NUM_SLOTS = 3; 288 static_assert(NUM_SLOTS >= 1); // 1 slot + others is the minimal config 289 290 // An array of pairs of (target, count) .. 291 GCPtr<JSFunction*> targets[NUM_SLOTS]; 292 uint32_t counts[NUM_SLOTS]; 293 // .. and a count for all other targets. 294 uint32_t countOther; 295 296 // Generated code assumes this 297 static_assert(sizeof(GCPtr<JSFunction*>) == sizeof(void*)); 298 static_assert(sizeof(uint32_t) == 4); 299 300 CallRefMetrics() { 301 for (size_t i = 0; i < NUM_SLOTS; i++) { 302 targets[i] = nullptr; 303 counts[i] = 0; 304 } 305 countOther = 0; 306 MOZ_ASSERT(checkInvariants()); 307 } 308 309 [[nodiscard]] bool checkInvariants() const { 310 // If targets[N] is null, then this slot is not in use and so counts[N] 311 // must be zero. Per comments above about overflow, the implication in the 312 // other direction does not hold. 313 size_t i; 314 for (i = 0; i < NUM_SLOTS; i++) { 315 if (targets[i] == nullptr && counts[i] != 0) { 316 return false; 317 } 318 } 319 // The targets/counts slots must be filled in in sequence. 320 for (i = 0; i < NUM_SLOTS; i++) { 321 if (targets[i] == nullptr) { 322 break; 323 } 324 } 325 size_t numUsed = i; 326 for (/*keepgoing*/; i < NUM_SLOTS; i++) { 327 if (targets[i] != nullptr) { 328 return false; 329 } 330 } 331 // For the slots in use, the target values must be different 332 for (i = 0; i < numUsed; i++) { 333 for (size_t j = i + 1; j < numUsed; j++) { 334 if (targets[j] == targets[i]) { 335 return false; 336 } 337 } 338 } 339 // Note we don't say anything about `countOther`. This gets incremented in 340 // the cases when we (1) see a new target when all slots are already in 341 // use, or (2) have a cross-instance call. The effect of (2) is that 342 // `countOther` can be non-zero regardless of how many slots are in use. 343 return true; 344 } 345 346 static size_t offsetOfTarget(size_t n) { 347 MOZ_ASSERT(n < NUM_SLOTS); 348 return offsetof(CallRefMetrics, targets) + n * sizeof(GCPtr<JSFunction*>); 349 } 350 static size_t offsetOfCount(size_t n) { 351 MOZ_ASSERT(n < NUM_SLOTS); 352 return offsetof(CallRefMetrics, counts) + n * sizeof(uint32_t); 353 } 354 static size_t offsetOfCountOther() { 355 return offsetof(CallRefMetrics, countOther); 356 } 357 }; 358 359 } // namespace wasm 360 } // namespace js 361 362 #endif // wasm_instance_data_h