tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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 */