Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/js/src/wasm/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 14 kB image not shown  

Quelle  WasmMetadata.cpp   Sprache: C

 
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
 * vim: set ts=8 sts=2 et sw=2 tw=80:
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */


#include "wasm/WasmMetadata.h"

#include "mozilla/BinarySearch.h"
#include "mozilla/CheckedInt.h"

#include "jsnum.h"  // Int32ToCStringBuf

#include "vm/Logging.h"

using mozilla::CheckedInt;

using namespace js;
using namespace js::wasm;

// CodeMetadata helpers -- computing the Instance layout.

bool CodeMetadata::allocateInstanceDataBytes(uint32_t bytes, uint32_t align,
                                             uint32_t* assignedOffset) {
  // Assert that this offset hasn't already been computed.
  MOZ_ASSERT(*assignedOffset == UINT32_MAX);

  CheckedInt<uint32_t> newInstanceDataLength(instanceDataLength);

  // Adjust the current global data length so that it's aligned to `align`
  newInstanceDataLength +=
      ComputeByteAlignment(newInstanceDataLength.value(), align);
  if (!newInstanceDataLength.isValid()) {
    return false;
  }

  // The allocated data is given by the aligned length
  *assignedOffset = newInstanceDataLength.value();

  // Advance the length for `bytes` being allocated
  newInstanceDataLength += bytes;
  if (!newInstanceDataLength.isValid()) {
    return false;
  }

  // This is the highest offset into Instance::globalArea that will not
  // overflow a signed 32-bit integer.
  const uint32_t maxInstanceDataOffset =
      uint32_t(INT32_MAX) - uint32_t(Instance::offsetOfData());

  // Check that the highest offset into this allocated space would not overflow
  // a signed 32-bit integer.
  if (newInstanceDataLength.value() > maxInstanceDataOffset + 1) {
    return false;
  }

  instanceDataLength = newInstanceDataLength.value();
  return true;
}

bool CodeMetadata::allocateInstanceDataBytesN(uint32_t bytes, uint32_t align,
                                              uint32_t count,
                                              uint32_t* assignedOffset) {
  // The size of each allocation should be a multiple of alignment so that a
  // contiguous array of allocations will be aligned
  MOZ_ASSERT(bytes % align == 0);

  // Compute the total bytes being allocated
  CheckedInt<uint32_t> totalBytes = bytes;
  totalBytes *= count;
  if (!totalBytes.isValid()) {
    return false;
  }

  // Allocate the bytes
  return allocateInstanceDataBytes(totalBytes.value(), align, assignedOffset);
}

bool CodeMetadata::prepareForCompile(CompileMode mode) {
  MOZ_ASSERT(!isPreparedForCompile());

  // Find every function that is exported from this module and give it an
  // implicit index
  uint32_t exportedFuncCount = 0;
  for (uint32_t funcIndex = 0; funcIndex < funcs.length(); funcIndex++) {
    const FuncDesc& func = funcs[funcIndex];
    if (func.isExported()) {
      exportedFuncCount++;
    }
  }

  if (!exportedFuncIndices.reserve(exportedFuncCount)) {
    return false;
  }
  for (uint32_t funcIndex = 0; funcIndex < funcs.length(); funcIndex++) {
    const FuncDesc& func = funcs[funcIndex];
    if (!func.isExported()) {
      continue;
    }
    exportedFuncIndices.infallibleEmplaceBack(funcIndex);
  }

  // Allocate the layout for instance data
  instanceDataLength = 0;

  // Allocate space for function counters, if we have them
  if (mode == CompileMode::LazyTiering) {
    if (!allocateInstanceDataBytesN(sizeof(FuncDefInstanceData),
                                    alignof(FuncDefInstanceData), numFuncDefs(),
                                    &funcDefsOffsetStart)) {
      return false;
    }
  }

  // Allocate space for type definitions
  if (!allocateInstanceDataBytesN(sizeof(TypeDefInstanceData),
                                  alignof(TypeDefInstanceData), types->length(),
                                  &typeDefsOffsetStart)) {
    return false;
  }

  // Allocate space for every function import
  if (!allocateInstanceDataBytesN(sizeof(FuncImportInstanceData),
                                  alignof(FuncImportInstanceData),
                                  numFuncImports, &funcImportsOffsetStart)) {
    return false;
  }

  // Allocate space for every function export
  if (!allocateInstanceDataBytesN(
          sizeof(FuncExportInstanceData), alignof(FuncExportInstanceData),
          numExportedFuncs(), &funcExportsOffsetStart)) {
    return false;
  }

  // Allocate space for every memory
  if (!allocateInstanceDataBytesN(sizeof(MemoryInstanceData),
                                  alignof(MemoryInstanceData),
                                  memories.length(), &memoriesOffsetStart)) {
    return false;
  }

  // Allocate space for every table
  if (!allocateInstanceDataBytesN(sizeof(TableInstanceData),
                                  alignof(TableInstanceData), tables.length(),
                                  &tablesOffsetStart)) {
    return false;
  }

  // Allocate space for every tag
  if (!allocateInstanceDataBytesN(sizeof(TagInstanceData),
                                  alignof(TagInstanceData), tags.length(),
                                  &tagsOffsetStart)) {
    return false;
  }

  // Allocate space for every global that requires it
  for (GlobalDesc& global : globals) {
    if (global.isConstant()) {
      continue;
    }

    uint32_t width = global.isIndirect() ? sizeof(void*) : global.type().size();

    uint32_t assignedOffset = UINT32_MAX;
    if (!allocateInstanceDataBytes(width, width, &assignedOffset)) {
      return false;
    }

    global.setOffset(assignedOffset);
  }

  return true;
}

uint32_t CodeMetadata::findFuncIndex(uint32_t bytecodeOffset) const {
  size_t funcDefIndex;
  if (!mozilla::BinarySearchIf(
          funcDefRanges, 0, funcDefRanges.length(),
          [bytecodeOffset](const BytecodeRange& range) {
            return range.compareOffset(bytecodeOffset);
          },
          &funcDefIndex)) {
    MOZ_CRASH("missing function definition");
  }
  return numFuncImports + funcDefIndex;
}

uint32_t CodeMetadata::findFuncExportIndex(uint32_t funcIndex) const {
  MOZ_ASSERT(funcs[funcIndex].isExported());

  size_t match;
  if (!mozilla::BinarySearch(exportedFuncIndices, 0,
                             exportedFuncIndices.length(), funcIndex, &match)) {
    MOZ_CRASH("missing function export");
  }
  return (uint32_t)match;
}

// CodeMetadata helpers -- getting function names.

static bool AppendName(const Bytes& namePayload, const Name& name,
                       UTF8Bytes* bytes) {
  MOZ_RELEASE_ASSERT(name.offsetInNamePayload <= namePayload.length());
  MOZ_RELEASE_ASSERT(name.length <=
                     namePayload.length() - name.offsetInNamePayload);
  return bytes->append(
      (const char*)namePayload.begin() + name.offsetInNamePayload, name.length);
}

static bool AppendFunctionIndexName(uint32_t funcIndex, UTF8Bytes* bytes) {
  const char beforeFuncIndex[] = "wasm-function[";
  const char afterFuncIndex[] = "]";

  Int32ToCStringBuf cbuf;
  size_t funcIndexStrLen;
  const char* funcIndexStr =
      Uint32ToCString(&cbuf, funcIndex, &funcIndexStrLen);
  MOZ_ASSERT(funcIndexStr);

  return bytes->append(beforeFuncIndex, strlen(beforeFuncIndex)) &&
         bytes->append(funcIndexStr, funcIndexStrLen) &&
         bytes->append(afterFuncIndex, strlen(afterFuncIndex));
}

bool CodeMetadata::getFuncNameForWasm(NameContext ctx, uint32_t funcIndex,
                                      UTF8Bytes* name) const {
  if (moduleName && moduleName->length != 0) {
    if (!AppendName(namePayload->vector, *moduleName, name)) {
      return false;
    }
    if (!name->append('.')) {
      return false;
    }
  }

  if (funcIndex < funcNames.length() && funcNames[funcIndex].length != 0) {
    return AppendName(namePayload->vector, funcNames[funcIndex], name);
  }

  if (ctx == NameContext::BeforeLocation) {
    return true;
  }

  return AppendFunctionIndexName(funcIndex, name);
}

// CodeMetadata helpers -- memory accounting.

size_t CodeMetadata::sizeOfExcludingThis(
    mozilla::MallocSizeOf mallocSizeOf) const {
  return memories.sizeOfExcludingThis(mallocSizeOf) +
         types->sizeOfExcludingThis(mallocSizeOf) +
         globals.sizeOfExcludingThis(mallocSizeOf) +
         tags.sizeOfExcludingThis(mallocSizeOf) +
         tables.sizeOfExcludingThis(mallocSizeOf) +
         namePayload->sizeOfExcludingThis(mallocSizeOf) +
         funcNames.sizeOfExcludingThis(mallocSizeOf) +
         funcs.sizeOfExcludingThis(mallocSizeOf) +
         elemSegmentTypes.sizeOfExcludingThis(mallocSizeOf) +
         asmJSSigToTableIndex.sizeOfExcludingThis(mallocSizeOf) +
         customSectionRanges.sizeOfExcludingThis(mallocSizeOf);
}

// CodeMetadata helpers -- statistics collection.

void CodeMetadata::dumpStats() const {
  // To see the statistics printed here:
  // * configure with --enable-jitspew or --enable-debug
  // * run with MOZ_LOG=wasmPerf:3
  // * this works for both JS builds and full browser builds
#ifdef JS_JITSPEW
  // Get the stats lock, pull a copy of the stats and drop the lock, so as to
  // avoid possible lock-ordering problems relative to JS_LOG.
  CodeMetadata::ProtectedOptimizationStats statsCopy;
  {
    auto guard = stats.readLock();
    statsCopy = guard.get();
  }
  JS_LOG(wasmPerf, Info, "CM=..%06lx CodeMetadata::~CodeMetadata() <<<<",
         0xFFFFFF & (unsigned long)uintptr_t(this));
  JS_LOG(wasmPerf, Info, " ------ Heuristic Settings ------");
  JS_LOG(wasmPerf, Info, " w_lazy_tiering_level (1..9) = %u",
         LazyTieringHeuristics::rawLevel());
  JS_LOG(wasmPerf, Info, " w_inlining_level (1..9) = %u",
         InliningHeuristics::rawLevel());
  JS_LOG(wasmPerf, Info, " w_direct_inlining = %s",
         InliningHeuristics::rawDirectAllowed() ? "true" : "false");
  JS_LOG(wasmPerf, Info, " w_call_ref_inlining = %s",
         InliningHeuristics::rawCallRefAllowed() ? "true" : "false");
  JS_LOG(wasmPerf, Info, " w_call_ref_inlining_percent (10..100) = %u",
         InliningHeuristics::rawCallRefPercent());
  JS_LOG(wasmPerf, Info, " ------ Complete Tier ------");
  JS_LOG(wasmPerf, Info, " %7zu functions in module",
         statsCopy.completeNumFuncs);
  JS_LOG(wasmPerf, Info, " %7zu bytecode bytes in module",
         statsCopy.completeBCSize);
  uint32_t nMetrics = numCallRefMetrics == UINT32_MAX ? 0 : numCallRefMetrics;
  JS_LOG(wasmPerf, Info, " %7u CallRefMetrics in module (%zu bytes)",
         nMetrics, size_t(nMetrics) * sizeof(CallRefMetrics));
  JS_LOG(wasmPerf, Info, " ------ Partial Tier ------");
  JS_LOG(wasmPerf, Info, " %7zu functions tiered up",
         statsCopy.partialNumFuncs);
  JS_LOG(wasmPerf, Info, " %7zu bytecode bytes tiered up",
         statsCopy.partialBCSize);
  JS_LOG(wasmPerf, Info, " %7zu direct-calls inlined",
         statsCopy.partialNumFuncsInlinedDirect);
  JS_LOG(wasmPerf, Info, " %7zu call_ref-calls inlined",
         statsCopy.partialNumFuncsInlinedCallRef);
  JS_LOG(wasmPerf, Info, " %7zu direct-call bytecodes inlined",
         statsCopy.partialBCInlinedSizeDirect);
  JS_LOG(wasmPerf, Info, " %7zu call_ref-call bytecodes inlined",
         statsCopy.partialBCInlinedSizeCallRef);
  JS_LOG(wasmPerf, Info, " %7zu functions overran inlining budget",
         statsCopy.partialInlineBudgetOverruns);
  JS_LOG(wasmPerf, Info, " %7zu bytes mmap'd for p-t code storage",
         statsCopy.partialCodeBytesMapped);
  JS_LOG(wasmPerf, Info, " %7zu bytes actually used for p-t code storage",
         statsCopy.partialCodeBytesUsed);

  // This value will be 0.0 if inlining did not cause any code expansion.  A
  // value of 1.0 means inlining doubled the total amount of bytecode, 2.0
  // means tripled it, etc.
  float inliningExpansion = float(statsCopy.partialBCInlinedSizeDirect +
                                  statsCopy.partialBCInlinedSizeCallRef) /
                            float(statsCopy.partialBCSize);

  // This is always between 0.0 and 1.0.
  float codeSpaceUseRatio = float(statsCopy.partialCodeBytesUsed) /
                            float(statsCopy.partialCodeBytesMapped);

  JS_LOG(wasmPerf, Info, " ------ Derived Values ------");
  JS_LOG(wasmPerf, Info,
         " %5.1f%% p-t bytecode expansion caused by inlining",
         inliningExpansion * 100.0);
  JS_LOG(wasmPerf, Info, " %4.1f%% of partial tier mapped code space used",
         codeSpaceUseRatio * 100.0);
  JS_LOG(wasmPerf, Info, " ------");
  JS_LOG(wasmPerf, Info, ">>>>");
#endif
}

// ModuleMetadata helpers -- memory accounting.

size_t ModuleMetadata::sizeOfExcludingThis(
    mozilla::MallocSizeOf mallocSizeOf) const {
  return imports.sizeOfExcludingThis(mallocSizeOf) +
         exports.sizeOfExcludingThis(mallocSizeOf) +
         elemSegments.sizeOfExcludingThis(mallocSizeOf) +
         dataSegmentRanges.sizeOfExcludingThis(mallocSizeOf) +
         dataSegments.sizeOfExcludingThis(mallocSizeOf) +
         customSections.sizeOfExcludingThis(mallocSizeOf);
}

bool ModuleMetadata::addDefinedFunc(
    ValTypeVector&& params, ValTypeVector&& results, bool declareForRef,
    mozilla::Maybe<CacheableName>&& optionalExportedName) {
  uint32_t typeIndex = codeMeta->types->length();
  FuncType funcType(std::move(params), std::move(results));
  if (!codeMeta->types->addType(std::move(funcType))) {
    return false;
  }

  FuncDesc funcDesc = FuncDesc(typeIndex);
  uint32_t funcIndex = codeMeta->funcs.length();
  if (!codeMeta->funcs.append(funcDesc)) {
    return false;
  }
  if (declareForRef) {
    codeMeta->funcs[funcIndex].declareFuncExported(truetrue);
  }
  if (optionalExportedName.isSome()) {
    if (!exports.emplaceBack(std::move(optionalExportedName.ref()), funcIndex,
                             DefinitionKind::Function)) {
      return false;
    }
  }
  return true;
}

bool ModuleMetadata::addImportedFunc(ValTypeVector&& params,
                                     ValTypeVector&& results,
                                     CacheableName&& importModName,
                                     CacheableName&& importFieldName) {
  MOZ_ASSERT(codeMeta->numFuncImports == codeMeta->funcs.length());
  if (!addDefinedFunc(std::move(params), std::move(results), false,
                      mozilla::Nothing())) {
    return false;
  }
  codeMeta->numFuncImports++;
  return imports.emplaceBack(std::move(importModName),
                             std::move(importFieldName),
                             DefinitionKind::Function);
}

Messung V0.5
C=93 H=98 G=95

¤ Dauer der Verarbeitung: 0.13 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

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.