WasmMetadata.h (19686B)
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 * This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #ifndef wasm_WasmMetadata_h 8 #define wasm_WasmMetadata_h 9 10 #include "wasm/WasmBinaryTypes.h" 11 #include "wasm/WasmHeuristics.h" 12 #include "wasm/WasmInstanceData.h" // various of *InstanceData 13 #include "wasm/WasmModuleTypes.h" 14 #include "wasm/WasmProcess.h" // IsHugeMemoryEnabled 15 16 namespace js { 17 namespace wasm { 18 19 using BuiltinModuleFuncIdVector = 20 Vector<BuiltinModuleFuncId, 0, SystemAllocPolicy>; 21 22 // ==== Printing of names 23 // 24 // The Developer-Facing Display Conventions section of the WebAssembly Web 25 // API spec defines two cases for displaying a wasm function name: 26 // 1. the function name stands alone 27 // 2. the function name precedes the location 28 29 enum class NameContext { Standalone, BeforeLocation }; 30 31 // wasm::CodeMetadata contains metadata whose lifetime ends at the same time 32 // that the lifetime of wasm::Code ends. This encompasses a wide variety of 33 // uses. In practice that means metadata needed for any and all aspects of 34 // compilation or execution of wasm code. Hence this metadata conceptually 35 // belongs to, and is kept alive by, wasm::Code. Note also that wasm::Code is 36 // in turn kept alive by wasm::Instance(s), hence this metadata will be kept 37 // alive as long as any instance for it exists. 38 39 using ModuleHash = uint8_t[8]; 40 41 struct CodeMetadata : public ShareableBase<CodeMetadata> { 42 // NOTE: if you add, remove, rename or reorder fields here, be sure to 43 // update CodeCodeMetadata() to keep it in sync. 44 45 // Constant parameters for the entire compilation. These are not marked 46 // `const` only because it breaks constructor delegation in 47 // CodeMetadata::CodeMetadata, which is a shame. 48 ModuleKind kind; 49 50 // The compile arguments that were used for this module. 51 SharedCompileArgs compileArgs; 52 53 // The number of imported functions in the module. 54 uint32_t numFuncImports; 55 // A vector of the builtin func id (or 'none') for all imported functions. 56 // This may be empty for internally constructed modules which don't care 57 // about this information. 58 BuiltinModuleFuncIdVector knownFuncImports; 59 // Treat imported wasm functions as if they were JS functions. This is used 60 // when compiling the module for new WebAssembly.Function. 61 bool funcImportsAreJS; 62 // The number of imported globals in the module. 63 uint32_t numGlobalImports; 64 65 // Info about all types in the module. 66 MutableTypeContext types; 67 // Info about all functions in the module. 68 FuncDescVector funcs; 69 // Info about all tables in the module. 70 TableDescVector tables; 71 // Info about all memories in the module. 72 MemoryDescVector memories; 73 // Info about all tags in the module. 74 TagDescVector tags; 75 // Info about all globals in the module. 76 GlobalDescVector globals; 77 78 // The start function for the module, if any 79 mozilla::Maybe<uint32_t> startFuncIndex; 80 81 // Info about elem segments needed only for validation and compilation. 82 // Should have the same length as ModuleMetadata::elemSegments, and each 83 // entry here should be identical the corresponding .elemType field in 84 // ModuleMetadata::elemSegments. 85 RefTypeVector elemSegmentTypes; 86 87 // The number of data segments this module will have. Pre-declared before the 88 // code section so that we can validate instructions that reference data 89 // segments. 90 mozilla::Maybe<uint32_t> dataCount; 91 92 // A sorted vector of the index of every function that is exported from this 93 // module. An index into this vector is a 'exported function index' and can 94 // be used to lookup exported functions on an instance. 95 Uint32Vector exportedFuncIndices; 96 97 // asm.js tables are homogenous and only store functions of the same type. 98 // This maps from a function type to the table index to use for an indirect 99 // call. 100 Uint32Vector asmJSSigToTableIndex; 101 102 // Branch hints to apply to functions 103 BranchHintCollection branchHints; 104 105 // Name section information 106 mozilla::Maybe<NameSection> nameSection; 107 108 // Bytecode ranges for custom sections. 109 CustomSectionRangeVector customSectionRanges; 110 111 // Bytecode range for the code section. 112 MaybeBytecodeRange codeSectionRange; 113 114 // ==== Instance layout fields 115 // 116 // The start offset of the FuncDefInstanceData[] section of the instance 117 // data. There is one entry for every function definition. 118 uint32_t funcDefsOffsetStart; 119 // The start offset of the FuncImportInstanceData[] section of the instance 120 // data. There is one entry for every imported function. 121 uint32_t funcImportsOffsetStart; 122 // The start offset of the FuncExportInstanceData[] section of the instance 123 // data. There is one entry for every exported function. 124 uint32_t funcExportsOffsetStart; 125 // The start offset of the TypeDefInstanceData[] section of the instance 126 // data. There is one entry for every type. 127 uint32_t typeDefsOffsetStart; 128 // The start offset of the MemoryInstanceData[] section of the instance data. 129 // There is one entry for every memory. 130 uint32_t memoriesOffsetStart; 131 // The start offset of the TableInstanceData[] section of the instance data. 132 // There is one entry for every table. 133 uint32_t tablesOffsetStart; 134 // The start offset of the tag section of the instance data. There is one 135 // entry for every tag. 136 uint32_t tagsOffsetStart; 137 // The total size of the instance data. 138 uint32_t instanceDataLength; 139 140 explicit CodeMetadata(const CompileArgs* compileArgs = nullptr, 141 ModuleKind kind = ModuleKind::Wasm) 142 : kind(kind), 143 compileArgs(compileArgs), 144 numFuncImports(0), 145 funcImportsAreJS(false), 146 numGlobalImports(0), 147 funcDefsOffsetStart(UINT32_MAX), 148 funcImportsOffsetStart(UINT32_MAX), 149 funcExportsOffsetStart(UINT32_MAX), 150 typeDefsOffsetStart(UINT32_MAX), 151 memoriesOffsetStart(UINT32_MAX), 152 tablesOffsetStart(UINT32_MAX), 153 tagsOffsetStart(UINT32_MAX), 154 instanceDataLength(UINT32_MAX) {} 155 156 [[nodiscard]] bool init() { 157 MOZ_ASSERT(!types); 158 types = js_new<TypeContext>(); 159 return types; 160 } 161 162 // Generates any new metadata necessary to compile this module. This must be 163 // called after the 'module environment' (everything before the code section) 164 // has been decoded. 165 [[nodiscard]] bool prepareForCompile(CompileMode mode); 166 bool isPreparedForCompile() const { return instanceDataLength != UINT32_MAX; } 167 168 bool isAsmJS() const { return kind == ModuleKind::AsmJS; } 169 // A builtin module is a host constructed wasm module that exports host 170 // functionality, using special opcodes. Otherwise, it has the same rules 171 // as wasm modules and so it does not get a new ModuleKind. 172 bool isBuiltinModule() const { return features().isBuiltinModule; } 173 174 #define WASM_FEATURE(NAME, SHORT_NAME, ...) \ 175 bool SHORT_NAME##Enabled() const { return features().SHORT_NAME; } 176 JS_FOR_WASM_FEATURES(WASM_FEATURE) 177 #undef WASM_FEATURE 178 Shareable sharedMemoryEnabled() const { return features().sharedMemory; } 179 bool simdAvailable() const { return features().simd; } 180 181 bool hugeMemoryEnabled(uint32_t memoryIndex) const { 182 return !isAsmJS() && memoryIndex < memories.length() && 183 IsHugeMemoryEnabled(memories[memoryIndex].addressType(), 184 memories[memoryIndex].pageSize()); 185 } 186 bool usesSharedMemory(uint32_t memoryIndex) const { 187 return memoryIndex < memories.length() && memories[memoryIndex].isShared(); 188 } 189 190 const FeatureArgs& features() const { return compileArgs->features; } 191 const ScriptedCaller& scriptedCaller() const { 192 return compileArgs->scriptedCaller; 193 } 194 const UniqueChars& sourceMapURL() const { return compileArgs->sourceMapURL; } 195 196 size_t numTypes() const { return types->length(); } 197 size_t numFuncs() const { return funcs.length(); } 198 size_t numFuncDefs() const { return funcs.length() - numFuncImports; } 199 size_t numTables() const { return tables.length(); } 200 size_t numMemories() const { return memories.length(); } 201 202 bool funcIsImport(uint32_t funcIndex) const { 203 return funcIndex < numFuncImports; 204 } 205 const TypeDef& getFuncTypeDef(uint32_t funcIndex) const { 206 return types->type(funcs[funcIndex].typeIndex); 207 } 208 const FuncType& getFuncType(uint32_t funcIndex) const { 209 return getFuncTypeDef(funcIndex).funcType(); 210 } 211 212 BuiltinModuleFuncId knownFuncImport(uint32_t funcIndex) const { 213 MOZ_ASSERT(funcIndex < numFuncImports); 214 if (knownFuncImports.empty()) { 215 return BuiltinModuleFuncId::None; 216 } 217 return knownFuncImports[funcIndex]; 218 } 219 220 // Find the exported function index for a function index 221 uint32_t findFuncExportIndex(uint32_t funcIndex) const; 222 223 // The number of functions that are exported in this module 224 uint32_t numExportedFuncs() const { return exportedFuncIndices.length(); } 225 226 size_t codeSectionSize() const { 227 if (codeSectionRange) { 228 return codeSectionRange->size(); 229 } 230 return 0; 231 } 232 233 // This gets names for wasm only. 234 // For asm.js, see CodeMetadataForAsmJS::getFuncNameForAsmJS. 235 bool getFuncNameForWasm(NameContext ctx, uint32_t funcIndex, 236 const ShareableBytes* nameSectionPayload, 237 UTF8Bytes* name) const; 238 239 uint32_t offsetOfFuncDefInstanceData(uint32_t funcIndex) const { 240 MOZ_ASSERT(funcIndex >= numFuncImports && funcIndex < numFuncs()); 241 return funcDefsOffsetStart + 242 (funcIndex - numFuncImports) * sizeof(FuncDefInstanceData); 243 } 244 245 uint32_t offsetOfFuncImportInstanceData(uint32_t funcIndex) const { 246 MOZ_ASSERT(funcIndex < numFuncImports); 247 return funcImportsOffsetStart + funcIndex * sizeof(FuncImportInstanceData); 248 } 249 250 uint32_t offsetOfFuncExportInstanceData(uint32_t funcExportIndex) const { 251 MOZ_ASSERT(funcExportIndex < exportedFuncIndices.length()); 252 return funcExportsOffsetStart + 253 funcExportIndex * sizeof(FuncExportInstanceData); 254 } 255 256 uint32_t offsetOfTypeDefInstanceData(uint32_t typeIndex) const { 257 MOZ_ASSERT(typeIndex < types->length()); 258 return typeDefsOffsetStart + typeIndex * sizeof(TypeDefInstanceData); 259 } 260 261 uint32_t offsetOfTypeDef(uint32_t typeIndex) const { 262 return offsetOfTypeDefInstanceData(typeIndex) + 263 offsetof(TypeDefInstanceData, typeDef); 264 } 265 uint32_t offsetOfSuperTypeVector(uint32_t typeIndex) const { 266 return offsetOfTypeDefInstanceData(typeIndex) + 267 offsetof(TypeDefInstanceData, superTypeVector); 268 } 269 270 uint32_t offsetOfMemoryInstanceData(uint32_t memoryIndex) const { 271 MOZ_ASSERT(memoryIndex < memories.length()); 272 return memoriesOffsetStart + memoryIndex * sizeof(MemoryInstanceData); 273 } 274 uint32_t offsetOfTableInstanceData(uint32_t tableIndex) const { 275 MOZ_ASSERT(tableIndex < tables.length()); 276 return tablesOffsetStart + tableIndex * sizeof(TableInstanceData); 277 } 278 279 uint32_t offsetOfTagInstanceData(uint32_t tagIndex) const { 280 MOZ_ASSERT(tagIndex < tags.length()); 281 return tagsOffsetStart + tagIndex * sizeof(TagInstanceData); 282 } 283 284 size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const; 285 286 private: 287 // Allocate space for `bytes`@`align` in the instance, updating 288 // `instanceDataLength` and returning the offset in in `assignedOffset`. 289 [[nodiscard]] bool allocateInstanceDataBytes(uint32_t bytes, uint32_t align, 290 uint32_t* assignedOffset); 291 // The same for an array of allocations. 292 [[nodiscard]] bool allocateInstanceDataBytesN(uint32_t bytes, uint32_t align, 293 uint32_t count, 294 uint32_t* assignedOffset); 295 }; 296 297 using MutableCodeMetadata = RefPtr<CodeMetadata>; 298 using SharedCodeMetadata = RefPtr<const CodeMetadata>; 299 300 using InliningBudget = ExclusiveData<int64_t>; 301 302 // wasm::CodeTailMetadata contains all metadata needed by wasm::Code that is 303 // only after the whole module has been downloaded. It is sometimes available 304 // for Ion compilation, and never available for baseline compilation. 305 struct CodeTailMetadata : public ShareableBase<CodeTailMetadata> { 306 // Default initializer only used for serialization. 307 CodeTailMetadata(); 308 309 // Initialize the metadata with a given wasm::CodeMetadata. 310 explicit CodeTailMetadata(const CodeMetadata& codeMeta); 311 312 // The code metadata for this module. 313 SharedCodeMetadata codeMeta; 314 315 // The bytes for the code section. 316 SharedBytes codeSectionBytecode; 317 318 // Whether this module was compiled with debugging support. 319 bool debugEnabled; 320 321 // A SHA-1 hash of the module bytecode for use in display urls. Only 322 // available if we're debugging. 323 ModuleHash debugHash; 324 325 // The full bytecode for this module. Only available for debuggable modules. 326 BytecodeBuffer debugBytecode; 327 328 // Shared and mutable inlining budget for this module. 329 mutable InliningBudget inliningBudget; 330 331 // The ranges of every function defined in this module. 332 BytecodeRangeVector funcDefRanges; 333 334 // The feature usage for every function defined in this module. 335 FeatureUsageVector funcDefFeatureUsages; 336 337 // Tracks the range of CallRefMetrics created for each function definition in 338 // this module. 339 CallRefMetricsRangeVector funcDefCallRefs; 340 341 // Tracks the range of AllocSites created for each function definition in 342 // this module. If we are compiling with a 'once' compilation and using just 343 // ion, this will be empty and ion will instead use per-typedef alloc sites. 344 AllocSitesRangeVector funcDefAllocSites; 345 346 // An array of hints to use when compiling a call_ref. 347 // 348 // This is written into when an instance requests a function to be tiered up, 349 // and read from our function compilers. 350 MutableCallRefHints callRefHints; 351 352 // nameSectionPayload points at the name section's CustomSection::payload so 353 // that the Names (which are use payload-relative offsets) can be used 354 // independently of the Module without duplicating the name section. 355 SharedBytes nameSectionPayload; 356 357 // The number of call ref metrics in Instance::callRefs_ 358 uint32_t numCallRefMetrics; 359 360 // The number of AllocSites in Instance::allocSites_. 361 uint32_t numAllocSites; 362 363 // Given a bytecode offset inside a function definition, find the function 364 // index. 365 uint32_t findFuncIndex(uint32_t bytecodeOffset) const; 366 uint32_t funcBytecodeOffset(uint32_t funcIndex) const { 367 if (funcIndex < codeMeta->numFuncImports) { 368 return 0; 369 } 370 uint32_t funcDefIndex = funcIndex - codeMeta->numFuncImports; 371 return funcDefRanges[funcDefIndex].start; 372 } 373 const BytecodeRange& funcDefRange(uint32_t funcIndex) const { 374 MOZ_ASSERT(funcIndex >= codeMeta->numFuncImports); 375 uint32_t funcDefIndex = funcIndex - codeMeta->numFuncImports; 376 return funcDefRanges[funcDefIndex]; 377 } 378 BytecodeSpan funcDefBody(uint32_t funcIndex) const { 379 return funcDefRange(funcIndex) 380 .relativeTo(*codeMeta->codeSectionRange) 381 .toSpan(*codeSectionBytecode); 382 } 383 FeatureUsage funcDefFeatureUsage(uint32_t funcIndex) const { 384 MOZ_ASSERT(funcIndex >= codeMeta->numFuncImports); 385 uint32_t funcDefIndex = funcIndex - codeMeta->numFuncImports; 386 return funcDefFeatureUsages[funcDefIndex]; 387 } 388 389 CallRefMetricsRange getFuncDefCallRefs(uint32_t funcIndex) const { 390 MOZ_ASSERT(funcIndex >= codeMeta->numFuncImports); 391 uint32_t funcDefIndex = funcIndex - codeMeta->numFuncImports; 392 return funcDefCallRefs[funcDefIndex]; 393 } 394 395 AllocSitesRange getFuncDefAllocSites(uint32_t funcIndex) const { 396 MOZ_ASSERT(funcIndex >= codeMeta->numFuncImports); 397 uint32_t funcDefIndex = funcIndex - codeMeta->numFuncImports; 398 return funcDefAllocSites[funcDefIndex]; 399 } 400 401 bool hasFuncDefAllocSites() const { return !funcDefAllocSites.empty(); } 402 403 CallRefHint getCallRefHint(uint32_t callRefIndex) const { 404 if (!callRefHints) { 405 return CallRefHint(); 406 } 407 return CallRefHint::fromRepr(callRefHints[callRefIndex]); 408 } 409 void setCallRefHint(uint32_t callRefIndex, CallRefHint hint) const { 410 callRefHints[callRefIndex] = hint.toRepr(); 411 } 412 }; 413 414 using MutableCodeTailMetadata = RefPtr<CodeTailMetadata>; 415 using SharedCodeTailMetadata = RefPtr<const CodeTailMetadata>; 416 417 // wasm::ModuleMetadata contains metadata whose lifetime ends at the same time 418 // that the lifetime of wasm::Module ends. In practice that means metadata 419 // that is needed only for creating wasm::Instances. Hence this metadata 420 // conceptually belongs to, and is held alive by, wasm::Module. 421 422 struct ModuleMetadata : public ShareableBase<ModuleMetadata> { 423 // NOTE: if you add, remove, rename or reorder fields here, be sure to 424 // update CodeModuleMetadata() to keep it in sync. 425 426 // The subset of module metadata that is shared between a module and 427 // instance (i.e. wasm::Code), and is available for all compilation. It 428 // does not contain any data that is only available after the whole module 429 // has been downloaded. 430 MutableCodeMetadata codeMeta; 431 432 // The subset of module metadata that is shared between a module and 433 // instance (i.e. wasm::Code), and is only available after the whole module 434 // has been downloaded. 435 MutableCodeTailMetadata codeTailMeta; 436 437 // Module fields decoded from the module environment (or initialized while 438 // validating an asm.js module) and immutable during compilation: 439 ImportVector imports; 440 ExportVector exports; 441 442 // Info about elem segments needed for instantiation. Should have the same 443 // length as CodeMetadata::elemSegmentTypes. 444 ModuleElemSegmentVector elemSegments; 445 446 // Info about data segments needed for instantiation. These wind up having 447 // the same length. Initially both are empty. `dataSegmentRanges` is 448 // filled in during validation, and `dataSegments` remains empty. Later, at 449 // module-generation time, `dataSegments` is filled in, by copying the 450 // underlying data blocks, and so the two vectors have the same length after 451 // that. 452 DataSegmentRangeVector dataSegmentRanges; 453 DataSegmentVector dataSegments; 454 455 CustomSectionVector customSections; 456 457 // Which features were observed when compiling this module. 458 FeatureUsage featureUsage = FeatureUsage::None; 459 460 explicit ModuleMetadata() = default; 461 462 [[nodiscard]] bool init(const CompileArgs& compileArgs, 463 ModuleKind kind = ModuleKind::Wasm) { 464 codeMeta = js_new<CodeMetadata>(&compileArgs, kind); 465 return !!codeMeta && codeMeta->init(); 466 } 467 468 bool addDefinedFunc(ValTypeVector&& params, ValTypeVector&& results, 469 bool declareForRef = false, 470 mozilla::Maybe<CacheableName>&& optionalExportedName = 471 mozilla::Nothing()); 472 bool addImportedFunc(ValTypeVector&& params, ValTypeVector&& results, 473 CacheableName&& importModName, 474 CacheableName&& importFieldName); 475 476 // Generates any new metadata necessary to compile this module. This must be 477 // called after the 'module environment' (everything before the code section) 478 // has been decoded. 479 [[nodiscard]] bool prepareForCompile(CompileMode mode) { 480 return codeMeta->prepareForCompile(mode); 481 } 482 bool isPreparedForCompile() const { return codeMeta->isPreparedForCompile(); } 483 484 size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const; 485 }; 486 487 using MutableModuleMetadata = RefPtr<ModuleMetadata>; 488 using SharedModuleMetadata = RefPtr<const ModuleMetadata>; 489 490 } // namespace wasm 491 } // namespace js 492 493 #endif /* wasm_WasmMetadata_h */