tor-browser

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

WasmMetadata.cpp (11466B)


      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 #include "wasm/WasmMetadata.h"
      8 
      9 #include "mozilla/BinarySearch.h"
     10 #include "mozilla/CheckedInt.h"
     11 
     12 #include "jsnum.h"  // Int32ToCStringBuf
     13 
     14 #include "vm/Logging.h"
     15 
     16 using mozilla::CheckedInt;
     17 
     18 using namespace js;
     19 using namespace js::wasm;
     20 
     21 // CodeMetadata helpers -- computing the Instance layout.
     22 
     23 bool CodeMetadata::allocateInstanceDataBytes(uint32_t bytes, uint32_t align,
     24                                             uint32_t* assignedOffset) {
     25  // Assert that this offset hasn't already been computed.
     26  MOZ_ASSERT(*assignedOffset == UINT32_MAX);
     27 
     28  CheckedInt<uint32_t> newInstanceDataLength(instanceDataLength);
     29 
     30  // Adjust the current global data length so that it's aligned to `align`
     31  newInstanceDataLength +=
     32      ComputeByteAlignment(newInstanceDataLength.value(), align);
     33  if (!newInstanceDataLength.isValid()) {
     34    return false;
     35  }
     36 
     37  // The allocated data is given by the aligned length
     38  *assignedOffset = newInstanceDataLength.value();
     39 
     40  // Advance the length for `bytes` being allocated
     41  newInstanceDataLength += bytes;
     42  if (!newInstanceDataLength.isValid()) {
     43    return false;
     44  }
     45 
     46  // This is the highest offset into Instance::globalArea that will not
     47  // overflow a signed 32-bit integer.
     48  const uint32_t maxInstanceDataOffset =
     49      uint32_t(INT32_MAX) - uint32_t(Instance::offsetOfData());
     50 
     51  // Check that the highest offset into this allocated space would not overflow
     52  // a signed 32-bit integer.
     53  if (newInstanceDataLength.value() > maxInstanceDataOffset + 1) {
     54    return false;
     55  }
     56 
     57  instanceDataLength = newInstanceDataLength.value();
     58  return true;
     59 }
     60 
     61 bool CodeMetadata::allocateInstanceDataBytesN(uint32_t bytes, uint32_t align,
     62                                              uint32_t count,
     63                                              uint32_t* assignedOffset) {
     64  // The size of each allocation should be a multiple of alignment so that a
     65  // contiguous array of allocations will be aligned
     66  MOZ_ASSERT(bytes % align == 0);
     67 
     68  // Compute the total bytes being allocated
     69  CheckedInt<uint32_t> totalBytes = bytes;
     70  totalBytes *= count;
     71  if (!totalBytes.isValid()) {
     72    return false;
     73  }
     74 
     75  // Allocate the bytes
     76  return allocateInstanceDataBytes(totalBytes.value(), align, assignedOffset);
     77 }
     78 
     79 bool CodeMetadata::prepareForCompile(CompileMode mode) {
     80  MOZ_ASSERT(!isPreparedForCompile());
     81 
     82  // Find every function that is exported from this module and give it an
     83  // implicit index
     84  uint32_t exportedFuncCount = 0;
     85  for (uint32_t funcIndex = 0; funcIndex < funcs.length(); funcIndex++) {
     86    const FuncDesc& func = funcs[funcIndex];
     87    if (func.isExported()) {
     88      exportedFuncCount++;
     89    }
     90  }
     91 
     92  if (!exportedFuncIndices.reserve(exportedFuncCount)) {
     93    return false;
     94  }
     95  for (uint32_t funcIndex = 0; funcIndex < funcs.length(); funcIndex++) {
     96    const FuncDesc& func = funcs[funcIndex];
     97    if (!func.isExported()) {
     98      continue;
     99    }
    100    exportedFuncIndices.infallibleEmplaceBack(funcIndex);
    101  }
    102 
    103  // Allocate the layout for instance data
    104  instanceDataLength = 0;
    105 
    106  // Allocate space for function counters, if we have them
    107  if (mode == CompileMode::LazyTiering) {
    108    if (!allocateInstanceDataBytesN(sizeof(FuncDefInstanceData),
    109                                    alignof(FuncDefInstanceData), numFuncDefs(),
    110                                    &funcDefsOffsetStart)) {
    111      return false;
    112    }
    113  }
    114 
    115  // Allocate space for type definitions
    116  if (!allocateInstanceDataBytesN(sizeof(TypeDefInstanceData),
    117                                  alignof(TypeDefInstanceData), types->length(),
    118                                  &typeDefsOffsetStart)) {
    119    return false;
    120  }
    121 
    122  // Allocate space for every function import
    123  if (!allocateInstanceDataBytesN(sizeof(FuncImportInstanceData),
    124                                  alignof(FuncImportInstanceData),
    125                                  numFuncImports, &funcImportsOffsetStart)) {
    126    return false;
    127  }
    128 
    129  // Allocate space for every function export
    130  if (!allocateInstanceDataBytesN(
    131          sizeof(FuncExportInstanceData), alignof(FuncExportInstanceData),
    132          numExportedFuncs(), &funcExportsOffsetStart)) {
    133    return false;
    134  }
    135 
    136  // Allocate space for every memory
    137  if (!allocateInstanceDataBytesN(sizeof(MemoryInstanceData),
    138                                  alignof(MemoryInstanceData),
    139                                  memories.length(), &memoriesOffsetStart)) {
    140    return false;
    141  }
    142 
    143  // Allocate space for every table
    144  if (!allocateInstanceDataBytesN(sizeof(TableInstanceData),
    145                                  alignof(TableInstanceData), tables.length(),
    146                                  &tablesOffsetStart)) {
    147    return false;
    148  }
    149 
    150  // Allocate space for every tag
    151  if (!allocateInstanceDataBytesN(sizeof(TagInstanceData),
    152                                  alignof(TagInstanceData), tags.length(),
    153                                  &tagsOffsetStart)) {
    154    return false;
    155  }
    156 
    157  // Allocate space for every global that requires it
    158  for (GlobalDesc& global : globals) {
    159    if (global.isConstant()) {
    160      continue;
    161    }
    162 
    163    uint32_t width = global.isIndirect() ? sizeof(void*) : global.type().size();
    164 
    165    uint32_t assignedOffset = UINT32_MAX;
    166    if (!allocateInstanceDataBytes(width, width, &assignedOffset)) {
    167      return false;
    168    }
    169 
    170    global.setOffset(assignedOffset);
    171  }
    172 
    173  return true;
    174 }
    175 
    176 uint32_t CodeMetadata::findFuncExportIndex(uint32_t funcIndex) const {
    177  MOZ_ASSERT(funcs[funcIndex].isExported());
    178 
    179  size_t match;
    180  if (!mozilla::BinarySearch(exportedFuncIndices, 0,
    181                             exportedFuncIndices.length(), funcIndex, &match)) {
    182    MOZ_CRASH("missing function export");
    183  }
    184  return (uint32_t)match;
    185 }
    186 
    187 uint32_t CodeTailMetadata::findFuncIndex(uint32_t bytecodeOffset) const {
    188  size_t funcDefIndex;
    189  if (!mozilla::BinarySearchIf(
    190          funcDefRanges, 0, funcDefRanges.length(),
    191          [bytecodeOffset](const BytecodeRange& range) {
    192            return range.compareOffset(bytecodeOffset);
    193          },
    194          &funcDefIndex)) {
    195    MOZ_CRASH("missing function definition");
    196  }
    197  return codeMeta->numFuncImports + funcDefIndex;
    198 }
    199 
    200 // CodeMetadata helpers -- getting function names.
    201 
    202 static bool AppendName(const Bytes& namePayload, const Name& name,
    203                       UTF8Bytes* bytes) {
    204  MOZ_RELEASE_ASSERT(name.offsetInNamePayload <= namePayload.length());
    205  MOZ_RELEASE_ASSERT(name.length <=
    206                     namePayload.length() - name.offsetInNamePayload);
    207  return bytes->append(
    208      (const char*)namePayload.begin() + name.offsetInNamePayload, name.length);
    209 }
    210 
    211 static bool AppendFunctionIndexName(uint32_t funcIndex, UTF8Bytes* bytes) {
    212  const char beforeFuncIndex[] = "wasm-function[";
    213  const char afterFuncIndex[] = "]";
    214 
    215  Int32ToCStringBuf cbuf;
    216  size_t funcIndexStrLen;
    217  const char* funcIndexStr =
    218      Uint32ToCString(&cbuf, funcIndex, &funcIndexStrLen);
    219  MOZ_ASSERT(funcIndexStr);
    220 
    221  return bytes->append(beforeFuncIndex, strlen(beforeFuncIndex)) &&
    222         bytes->append(funcIndexStr, funcIndexStrLen) &&
    223         bytes->append(afterFuncIndex, strlen(afterFuncIndex));
    224 }
    225 
    226 bool CodeMetadata::getFuncNameForWasm(NameContext ctx, uint32_t funcIndex,
    227                                      const ShareableBytes* nameSectionPayload,
    228                                      UTF8Bytes* name) const {
    229  if (nameSection && nameSection->moduleName.length != 0) {
    230    if (!AppendName(nameSectionPayload->vector, nameSection->moduleName,
    231                    name)) {
    232      return false;
    233    }
    234    if (!name->append('.')) {
    235      return false;
    236    }
    237  }
    238 
    239  if (nameSection && funcIndex < nameSection->funcNames.length() &&
    240      nameSection->funcNames[funcIndex].length != 0) {
    241    return AppendName(nameSectionPayload->vector,
    242                      nameSection->funcNames[funcIndex], name);
    243  }
    244 
    245  if (ctx == NameContext::BeforeLocation) {
    246    return true;
    247  }
    248 
    249  return AppendFunctionIndexName(funcIndex, name);
    250 }
    251 
    252 // CodeMetadata helpers -- memory accounting.
    253 
    254 size_t CodeMetadata::sizeOfExcludingThis(
    255    mozilla::MallocSizeOf mallocSizeOf) const {
    256  return memories.sizeOfExcludingThis(mallocSizeOf) +
    257         types->sizeOfExcludingThis(mallocSizeOf) +
    258         globals.sizeOfExcludingThis(mallocSizeOf) +
    259         tags.sizeOfExcludingThis(mallocSizeOf) +
    260         tables.sizeOfExcludingThis(mallocSizeOf) +
    261         SizeOfMaybeExcludingThis(nameSection, mallocSizeOf) +
    262         funcs.sizeOfExcludingThis(mallocSizeOf) +
    263         elemSegmentTypes.sizeOfExcludingThis(mallocSizeOf) +
    264         asmJSSigToTableIndex.sizeOfExcludingThis(mallocSizeOf) +
    265         customSectionRanges.sizeOfExcludingThis(mallocSizeOf);
    266 }
    267 
    268 CodeTailMetadata::CodeTailMetadata()
    269    : codeMeta(nullptr),
    270      debugEnabled(false),
    271      debugHash(),
    272      inliningBudget(mutexid::WasmInliningBudget, 0),
    273      callRefHints(nullptr),
    274      numCallRefMetrics(UINT32_MAX),
    275      numAllocSites(UINT32_MAX) {}
    276 
    277 CodeTailMetadata::CodeTailMetadata(const CodeMetadata& codeMeta)
    278    : js::wasm::CodeTailMetadata() {
    279  this->codeMeta = &codeMeta;
    280  inliningBudget.lock().get() =
    281      InliningHeuristics::moduleInliningBudget(codeMeta.codeSectionSize());
    282 }
    283 
    284 // ModuleMetadata helpers -- memory accounting.
    285 
    286 size_t ModuleMetadata::sizeOfExcludingThis(
    287    mozilla::MallocSizeOf mallocSizeOf) const {
    288  return imports.sizeOfExcludingThis(mallocSizeOf) +
    289         exports.sizeOfExcludingThis(mallocSizeOf) +
    290         elemSegments.sizeOfExcludingThis(mallocSizeOf) +
    291         dataSegmentRanges.sizeOfExcludingThis(mallocSizeOf) +
    292         dataSegments.sizeOfExcludingThis(mallocSizeOf) +
    293         customSections.sizeOfExcludingThis(mallocSizeOf);
    294 }
    295 
    296 bool ModuleMetadata::addDefinedFunc(
    297    ValTypeVector&& params, ValTypeVector&& results, bool declareForRef,
    298    mozilla::Maybe<CacheableName>&& optionalExportedName) {
    299  uint32_t typeIndex = codeMeta->types->length();
    300  FuncType funcType(std::move(params), std::move(results));
    301  if (!codeMeta->types->addType(std::move(funcType))) {
    302    return false;
    303  }
    304 
    305  FuncDesc funcDesc = FuncDesc(typeIndex);
    306  uint32_t funcIndex = codeMeta->funcs.length();
    307  if (!codeMeta->funcs.append(funcDesc)) {
    308    return false;
    309  }
    310  if (declareForRef) {
    311    codeMeta->funcs[funcIndex].declareFuncExported(true, true);
    312  }
    313  if (optionalExportedName.isSome()) {
    314    if (!exports.emplaceBack(std::move(optionalExportedName.ref()), funcIndex,
    315                             DefinitionKind::Function)) {
    316      return false;
    317    }
    318  }
    319  return true;
    320 }
    321 
    322 bool ModuleMetadata::addImportedFunc(ValTypeVector&& params,
    323                                     ValTypeVector&& results,
    324                                     CacheableName&& importModName,
    325                                     CacheableName&& importFieldName) {
    326  MOZ_ASSERT(codeMeta->numFuncImports == codeMeta->funcs.length());
    327  if (!addDefinedFunc(std::move(params), std::move(results), false,
    328                      mozilla::Nothing())) {
    329    return false;
    330  }
    331  codeMeta->numFuncImports++;
    332  return imports.emplaceBack(std::move(importModName),
    333                             std::move(importFieldName),
    334                             DefinitionKind::Function);
    335 }