tor-browser

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

WasmCompile.cpp (44161B)


      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 2015 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 #include "wasm/WasmCompile.h"
     20 
     21 #include <algorithm>
     22 #include <cstdint>
     23 
     24 #include "js/Conversions.h"
     25 #include "js/Equality.h"
     26 #include "js/ForOfIterator.h"
     27 #include "js/PropertyAndElement.h"
     28 
     29 #ifndef __wasi__
     30 #  include "jit/ProcessExecutableMemory.h"
     31 #endif
     32 
     33 #include "jit/FlushICache.h"
     34 #include "jit/JitOptions.h"
     35 #include "util/Text.h"
     36 #include "vm/HelperThreads.h"
     37 #include "vm/JSAtomState.h"
     38 #include "vm/Realm.h"
     39 #include "wasm/WasmBaselineCompile.h"
     40 #include "wasm/WasmFeatures.h"
     41 #include "wasm/WasmGenerator.h"
     42 #include "wasm/WasmIonCompile.h"
     43 #include "wasm/WasmOpIter.h"
     44 #include "wasm/WasmProcess.h"
     45 #include "wasm/WasmSignalHandlers.h"
     46 #include "wasm/WasmValidate.h"
     47 
     48 using namespace js;
     49 using namespace js::jit;
     50 using namespace js::wasm;
     51 
     52 using mozilla::Atomic;
     53 
     54 uint32_t wasm::ObservedCPUFeatures() {
     55  enum Arch : uint32_t {
     56    X86 = 0x1,
     57    X64 = 0x2,
     58    ARM = 0x3,
     59    MIPS = 0x4,
     60    MIPS64 = 0x5,
     61    ARM64 = 0x6,
     62    LOONG64 = 0x7,
     63    RISCV64 = 0x8,
     64 
     65    LAST = RISCV64,
     66    ARCH_BITS = 4
     67  };
     68 
     69  static_assert(LAST < (1 << ARCH_BITS));
     70 
     71 #if defined(JS_CODEGEN_X86)
     72  MOZ_ASSERT(uint32_t(jit::CPUInfo::GetFingerprint()) <=
     73             (UINT32_MAX >> ARCH_BITS));
     74  return X86 | (uint32_t(jit::CPUInfo::GetFingerprint()) << ARCH_BITS);
     75 #elif defined(JS_CODEGEN_X64)
     76  MOZ_ASSERT(uint32_t(jit::CPUInfo::GetFingerprint()) <=
     77             (UINT32_MAX >> ARCH_BITS));
     78  return X64 | (uint32_t(jit::CPUInfo::GetFingerprint()) << ARCH_BITS);
     79 #elif defined(JS_CODEGEN_ARM)
     80  MOZ_ASSERT(jit::GetARMFlags() <= (UINT32_MAX >> ARCH_BITS));
     81  return ARM | (jit::GetARMFlags() << ARCH_BITS);
     82 #elif defined(JS_CODEGEN_ARM64)
     83  MOZ_ASSERT(jit::GetARM64Flags() <= (UINT32_MAX >> ARCH_BITS));
     84  return ARM64 | (jit::GetARM64Flags() << ARCH_BITS);
     85 #elif defined(JS_CODEGEN_MIPS64)
     86  MOZ_ASSERT(jit::GetMIPSFlags() <= (UINT32_MAX >> ARCH_BITS));
     87  return MIPS64 | (jit::GetMIPSFlags() << ARCH_BITS);
     88 #elif defined(JS_CODEGEN_LOONG64)
     89  MOZ_ASSERT(jit::GetLOONG64Flags() <= (UINT32_MAX >> ARCH_BITS));
     90  return LOONG64 | (jit::GetLOONG64Flags() << ARCH_BITS);
     91 #elif defined(JS_CODEGEN_RISCV64)
     92  MOZ_ASSERT(jit::GetRISCV64Flags() <= (UINT32_MAX >> ARCH_BITS));
     93  return RISCV64 | (jit::GetRISCV64Flags() << ARCH_BITS);
     94 #elif defined(JS_CODEGEN_NONE) || defined(JS_CODEGEN_WASM32)
     95  return 0;
     96 #else
     97 #  error "unknown architecture"
     98 #endif
     99 }
    100 
    101 bool FeatureOptions::init(JSContext* cx, HandleValue val) {
    102  if (val.isNullOrUndefined()) {
    103    return true;
    104  }
    105 
    106  if (!val.isObject()) {
    107    JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
    108                             JSMSG_WASM_BAD_COMPILE_OPTIONS);
    109    return false;
    110  }
    111  RootedObject obj(cx, &val.toObject());
    112 
    113  if (IsPrivilegedContext(cx)) {
    114    RootedValue disableOptimizingCompiler(cx);
    115    if (!JS_GetProperty(cx, obj, "disableOptimizingCompiler",
    116                        &disableOptimizingCompiler)) {
    117      return false;
    118    }
    119 
    120    this->disableOptimizingCompiler = JS::ToBoolean(disableOptimizingCompiler);
    121 
    122    RootedValue mozIntGemm(cx);
    123    if (!JS_GetProperty(cx, obj, "mozIntGemm", &mozIntGemm)) {
    124      return false;
    125    }
    126 
    127    this->mozIntGemm = JS::ToBoolean(mozIntGemm);
    128  } else {
    129    MOZ_ASSERT(!this->disableOptimizingCompiler);
    130  }
    131 
    132  // Check the 'importedStringConstants' option
    133  RootedValue importedStringConstants(cx);
    134  if (!JS_GetProperty(cx, obj, "importedStringConstants",
    135                      &importedStringConstants)) {
    136    return false;
    137  }
    138 
    139  if (importedStringConstants.isNullOrUndefined()) {
    140    this->jsStringConstants = false;
    141  } else {
    142    this->jsStringConstants = true;
    143 
    144    RootedString importedStringConstantsString(
    145        cx, JS::ToString(cx, importedStringConstants));
    146    if (!importedStringConstantsString) {
    147      return false;
    148    }
    149 
    150    UniqueChars jsStringConstantsNamespace =
    151        StringToNewUTF8CharsZ(cx, *importedStringConstantsString);
    152    if (!jsStringConstantsNamespace) {
    153      return false;
    154    }
    155 
    156    this->jsStringConstantsNamespace =
    157        cx->new_<ShareableChars>(std::move(jsStringConstantsNamespace));
    158    if (!this->jsStringConstantsNamespace) {
    159      return false;
    160    }
    161  }
    162 
    163  // Get the `builtins` iterable
    164  RootedValue builtins(cx);
    165  if (!JS_GetProperty(cx, obj, "builtins", &builtins)) {
    166    return false;
    167  }
    168 
    169  if (!builtins.isUndefined()) {
    170    JS::ForOfIterator iterator(cx);
    171 
    172    if (!iterator.init(builtins, JS::ForOfIterator::ThrowOnNonIterable)) {
    173      return false;
    174    }
    175 
    176    RootedValue jsStringModule(cx, StringValue(cx->names().jsStringModule));
    177    RootedValue nextBuiltin(cx);
    178    while (true) {
    179      bool done;
    180      if (!iterator.next(&nextBuiltin, &done)) {
    181        return false;
    182      }
    183      if (done) {
    184        break;
    185      }
    186 
    187      bool jsStringBuiltins;
    188      if (!JS::LooselyEqual(cx, nextBuiltin, jsStringModule,
    189                            &jsStringBuiltins)) {
    190        return false;
    191      }
    192 
    193      // We ignore unknown builtins
    194      if (!jsStringBuiltins) {
    195        continue;
    196      }
    197 
    198      // You cannot request the same builtin twice
    199      if (this->jsStringBuiltins && jsStringBuiltins) {
    200        JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
    201                                 JSMSG_WASM_DUPLICATE_BUILTIN);
    202        return false;
    203      }
    204      this->jsStringBuiltins = jsStringBuiltins;
    205    }
    206  }
    207 
    208  return true;
    209 }
    210 
    211 FeatureArgs FeatureArgs::build(JSContext* cx, const FeatureOptions& options) {
    212  FeatureArgs features;
    213 
    214 #define WASM_FEATURE(NAME, LOWER_NAME, ...) \
    215  features.LOWER_NAME = wasm::NAME##Available(cx);
    216  JS_FOR_WASM_FEATURES(WASM_FEATURE);
    217 #undef WASM_FEATURE
    218 
    219  features.sharedMemory =
    220      wasm::ThreadsAvailable(cx) ? Shareable::True : Shareable::False;
    221 
    222  features.simd = jit::JitSupportsWasmSimd();
    223  features.isBuiltinModule = options.isBuiltinModule;
    224  features.builtinModules.jsString = options.jsStringBuiltins;
    225  features.builtinModules.jsStringConstants = options.jsStringConstants;
    226  features.builtinModules.jsStringConstantsNamespace =
    227      options.jsStringConstantsNamespace;
    228  features.builtinModules.intGemm =
    229      MozIntGemmAvailable(cx) && options.mozIntGemm;
    230 
    231  return features;
    232 }
    233 
    234 SharedCompileArgs CompileArgs::build(JSContext* cx,
    235                                     ScriptedCaller&& scriptedCaller,
    236                                     const FeatureOptions& options,
    237                                     CompileArgsError* error) {
    238  bool baseline = BaselineAvailable(cx);
    239  bool ion = IonAvailable(cx);
    240 
    241  // If the user requested to disable ion and we're able to, fallback to
    242  // baseline.
    243  if (baseline && options.disableOptimizingCompiler) {
    244    ion = false;
    245  }
    246 
    247  // Debug information such as source view or debug traps will require
    248  // additional memory and permanently stay in baseline code, so we try to
    249  // only enable it when a developer actually cares: when the debugger tab
    250  // is open.
    251  bool debug = cx->realm() && cx->realm()->debuggerObservesWasm();
    252 
    253  bool forceTiering =
    254      cx->options().testWasmAwaitTier2() || JitOptions.wasmDelayTier2;
    255 
    256  // The <Compiler>Available() predicates should ensure no failure here, but
    257  // when we're fuzzing we allow inconsistent switches and the check may thus
    258  // fail.  Let it go to a run-time error instead of crashing.
    259  if (debug && ion) {
    260    *error = CompileArgsError::NoCompiler;
    261    return nullptr;
    262  }
    263 
    264  if (forceTiering && !(baseline && ion)) {
    265    // This can happen only in testing, and in this case we don't have a
    266    // proper way to signal the error, so just silently override the default,
    267    // instead of adding a skip-if directive to every test using debug/gc.
    268    forceTiering = false;
    269  }
    270 
    271  if (!(baseline || ion)) {
    272    *error = CompileArgsError::NoCompiler;
    273    return nullptr;
    274  }
    275 
    276  CompileArgs* target = cx->new_<CompileArgs>();
    277  if (!target) {
    278    *error = CompileArgsError::OutOfMemory;
    279    return nullptr;
    280  }
    281 
    282  target->scriptedCaller = std::move(scriptedCaller);
    283  target->baselineEnabled = baseline;
    284  target->ionEnabled = ion;
    285  target->debugEnabled = debug;
    286  target->forceTiering = forceTiering;
    287  target->features = FeatureArgs::build(cx, options);
    288 
    289  return target;
    290 }
    291 
    292 void wasm::SetUseCountersForFeatureUsage(JSContext* cx, JSObject* object,
    293                                         FeatureUsage usage) {
    294  if (usage & FeatureUsage::LegacyExceptions) {
    295    cx->runtime()->setUseCounter(object, JSUseCounter::WASM_LEGACY_EXCEPTIONS);
    296  }
    297 }
    298 
    299 SharedCompileArgs CompileArgs::buildForAsmJS(ScriptedCaller&& scriptedCaller) {
    300  CompileArgs* target = js_new<CompileArgs>();
    301  if (!target) {
    302    return nullptr;
    303  }
    304 
    305  target->scriptedCaller = std::move(scriptedCaller);
    306  // AsmJS is deprecated and doesn't have mechanisms for experimental features,
    307  // so we don't need to initialize the FeatureArgs. It also only targets the
    308  // Ion backend and does not need WASM debug support since it is de-optimized
    309  // to JS in that case.
    310  target->ionEnabled = true;
    311  target->debugEnabled = false;
    312 
    313  return target;
    314 }
    315 
    316 SharedCompileArgs CompileArgs::buildForValidation(const FeatureArgs& args) {
    317  CompileArgs* target = js_new<CompileArgs>();
    318  if (!target) {
    319    return nullptr;
    320  }
    321 
    322  // Validation will not need compilers, just mark them disabled
    323  target->baselineEnabled = false;
    324  target->ionEnabled = false;
    325  target->debugEnabled = false;
    326  target->forceTiering = false;
    327 
    328  // Set the features
    329  target->features = args;
    330 
    331  return target;
    332 }
    333 
    334 SharedCompileArgs CompileArgs::buildAndReport(JSContext* cx,
    335                                              ScriptedCaller&& scriptedCaller,
    336                                              const FeatureOptions& options,
    337                                              bool reportOOM) {
    338  CompileArgsError error;
    339  SharedCompileArgs args =
    340      CompileArgs::build(cx, std::move(scriptedCaller), options, &error);
    341  if (args) {
    342    Log(cx, "available wasm compilers: tier1=%s tier2=%s",
    343        args->baselineEnabled ? "baseline" : "none",
    344        args->ionEnabled ? "ion" : "none");
    345    return args;
    346  }
    347 
    348  switch (error) {
    349    case CompileArgsError::NoCompiler: {
    350      JS_ReportErrorASCII(cx, "no WebAssembly compiler available");
    351      break;
    352    }
    353    case CompileArgsError::OutOfMemory: {
    354      // Most callers are required to return 'false' without reporting an OOM,
    355      // so we make reporting it optional here.
    356      if (reportOOM) {
    357        ReportOutOfMemory(cx);
    358      }
    359      break;
    360    }
    361  }
    362  return nullptr;
    363 }
    364 
    365 BytecodeSource::BytecodeSource(const uint8_t* begin, size_t length) {
    366  BytecodeRange envRange;
    367  BytecodeRange codeRange;
    368  BytecodeRange tailRange;
    369  if (StartsCodeSection(begin, begin + length, &codeRange)) {
    370    if (codeRange.end <= length) {
    371      envRange = BytecodeRange(0, codeRange.start);
    372      tailRange = BytecodeRange(codeRange.end, length - codeRange.end);
    373    } else {
    374      MOZ_RELEASE_ASSERT(codeRange.start <= length);
    375      // If the specified code range is larger than the buffer, clamp it to the
    376      // the buffer size. This buffer will be rejected later.
    377      envRange = BytecodeRange(0, codeRange.start);
    378      codeRange = BytecodeRange(codeRange.start, length - codeRange.start);
    379      MOZ_RELEASE_ASSERT(codeRange.end == length);
    380      tailRange = BytecodeRange(length, 0);
    381    }
    382  } else {
    383    envRange = BytecodeRange(0, length);
    384    codeRange = BytecodeRange(length, 0);
    385    tailRange = BytecodeRange(length, 0);
    386  }
    387 
    388  BytecodeSpan module(begin, length);
    389  env_ = envRange.toSpan(module);
    390  code_ = codeRange.toSpan(module);
    391  tail_ = tailRange.toSpan(module);
    392 }
    393 
    394 BytecodeBuffer::BytecodeBuffer(const ShareableBytes* env,
    395                               const ShareableBytes* code,
    396                               const ShareableBytes* tail)
    397    : env_(env),
    398      code_(code),
    399      tail_(tail),
    400      source_(env_ ? env_->span() : BytecodeSpan(),
    401              code_ ? code_->span() : BytecodeSpan(),
    402              tail_ ? tail_->span() : BytecodeSpan()) {}
    403 
    404 bool BytecodeBuffer::fromSource(const BytecodeSource& bytecodeSource,
    405                                BytecodeBuffer* bytecodeBuffer) {
    406  SharedBytes env;
    407  if (!bytecodeSource.envRange().isEmpty()) {
    408    env = ShareableBytes::fromSpan(bytecodeSource.envSpan());
    409    if (!env) {
    410      return false;
    411    }
    412  }
    413 
    414  SharedBytes code;
    415  if (bytecodeSource.hasCodeSection() &&
    416      !bytecodeSource.codeRange().isEmpty()) {
    417    code = ShareableBytes::fromSpan(bytecodeSource.codeSpan());
    418    if (!code) {
    419      return false;
    420    }
    421  }
    422 
    423  SharedBytes tail;
    424  if (bytecodeSource.hasCodeSection() &&
    425      !bytecodeSource.tailRange().isEmpty()) {
    426    tail = ShareableBytes::fromSpan(bytecodeSource.tailSpan());
    427    if (!tail) {
    428      return false;
    429    }
    430  }
    431 
    432  *bytecodeBuffer = BytecodeBuffer(env, code, tail);
    433  return true;
    434 }
    435 
    436 /*
    437 * [SMDOC] Tiered wasm compilation.
    438 *
    439 * "Tiered compilation" refers to the mechanism where we first compile the code
    440 * with a fast non-optimizing compiler so that we can start running the code
    441 * quickly, while in the background recompiling the code with the slower
    442 * optimizing compiler.  Code created by baseline is called "tier-1"; code
    443 * created by the optimizing compiler is called "tier-2".  When the tier-2 code
    444 * is ready, we "tier up" the code by creating paths from tier-1 code into their
    445 * tier-2 counterparts; this patching is performed as the program is running.
    446 *
    447 * ## Selecting the compilation mode
    448 *
    449 * When wasm bytecode arrives, we choose the compilation strategy based on
    450 * switches and on aspects of the code and the hardware.  If switches allow
    451 * tiered compilation to happen (the normal case), the following logic applies.
    452 *
    453 * If the code is sufficiently large that tiered compilation would be beneficial
    454 * but not so large that it might blow our compiled code budget and make
    455 * compilation fail, we choose tiered compilation.  Otherwise we go straight to
    456 * optimized code.
    457 *
    458 * The expected benefit of tiering is computed by TieringBeneficial(), below,
    459 * based on various estimated parameters of the hardware: ratios of object code
    460 * to byte code, speed of the system, number of cores.
    461 *
    462 * ## Mechanics of tiering up; patching
    463 *
    464 * Every time control enters a tier-1 function, the function prologue loads its
    465 * tiering pointer from the tiering jump table (see JumpTable in WasmCode.h) and
    466 * jumps to it.
    467 *
    468 * Initially, an entry in the tiering table points to the instruction inside the
    469 * tier-1 function that follows the jump instruction (hence the jump is an
    470 * expensive nop).  When the tier-2 compiler is finished, the table is patched
    471 * racily to point into the tier-2 function at the correct prologue location
    472 * (see loop near the end of Module::finishTier2()).  As tier-2 compilation is
    473 * performed at most once per Module, there is at most one such racy overwrite
    474 * per table element during the lifetime of the Module.
    475 *
    476 * The effect of the patching is to cause the tier-1 function to jump to its
    477 * tier-2 counterpart whenever the tier-1 function is called subsequently.  That
    478 * is, tier-1 code performs standard frame setup on behalf of whatever code it
    479 * jumps to, and the target code (tier-1 or tier-2) allocates its own frame in
    480 * whatever way it wants.
    481 *
    482 * The racy writing means that it is often nondeterministic whether tier-1 or
    483 * tier-2 code is reached by any call during the tiering-up process; if F calls
    484 * A and B in that order, it may reach tier-2 code for A and tier-1 code for B.
    485 * If F is running concurrently on threads T1 and T2, T1 and T2 may see code
    486 * from different tiers for either function.
    487 *
    488 * Note, tiering up also requires upgrading the jit-entry stubs so that they
    489 * reference tier-2 code.  The mechanics of this upgrading are described at
    490 * WasmInstanceObject::getExportedFunction().
    491 *
    492 * ## Current limitations of tiering
    493 *
    494 * Tiering is not always seamless.  Partly, it is possible for a program to get
    495 * stuck in tier-1 code.  Partly, a function that has tiered up continues to
    496 * force execution to go via tier-1 code to reach tier-2 code, paying for an
    497 * additional jump and a slightly less optimized prologue than tier-2 code could
    498 * have had on its own.
    499 *
    500 * Known tiering limitiations:
    501 *
    502 * - We can tier up only at function boundaries.  If a tier-1 function has a
    503 *   long-running loop it will not tier up until it returns to its caller.  If
    504 *   this loop never exits (a runloop in a worker, for example) then the
    505 *   function will never tier up.
    506 *
    507 *   To do better, we need OSR.
    508 *
    509 * - Wasm Table entries are never patched during tier-up.  A Table of funcref
    510 *   holds not a JSFunction pointer, but a (code*,instance*) pair of pointers.
    511 * When a table.set operation is performed, the JSFunction value is decomposed
    512 * and its code and instance pointers are stored in the table; subsequently,
    513 * when a table.get operation is performed, the JSFunction value is
    514 * reconstituted from its code pointer using fairly elaborate machinery.  (The
    515 * mechanics are the same also for the reflected JS operations on a
    516 * WebAssembly.Table.  For everything, see WasmTable.{cpp,h}.)  The code pointer
    517 * in the Table will always be the code pointer belonging to the best tier that
    518 * was active at the time when that function was stored in that Table slot; in
    519 * many cases, it will be tier-1 code.  As a consequence, a call through a table
    520 * will first enter tier-1 code and then jump to tier-2 code.
    521 *
    522 *   To do better, we must update all the tables in the system when an instance
    523 *   tiers up.  This is expected to be very hard.
    524 *
    525 * - Imported Wasm functions are never patched during tier-up.  Imports are held
    526 *   in FuncImportInstanceData values in the instance, and for a wasm
    527 *   callee, what's stored is the raw code pointer into the best tier of the
    528 *   callee that was active at the time the import was resolved.  That could be
    529 *   baseline code, and if it is, the situation is as for Table entries: a call
    530 *   to an import will always go via that import's tier-1 code, which will tier
    531 * up with an indirect jump.
    532 *
    533 *   To do better, we must update all the import tables in the system that
    534 *   import functions from instances whose modules have tiered up.  This is
    535 *   expected to be hard.
    536 */
    537 
    538 // Classify the current system as one of a set of recognizable classes.  This
    539 // really needs to get our tier-1 systems right.
    540 //
    541 // TODO: We don't yet have a good measure of how fast a system is.  We
    542 // distinguish between mobile and desktop because these are very different kinds
    543 // of systems, but we could further distinguish between low / medium / high end
    544 // within those major classes.  If we do so, then constants below would be
    545 // provided for each (class, architecture, system-tier) combination, not just
    546 // (class, architecture) as now.
    547 //
    548 // CPU clock speed is not by itself a good predictor of system performance, as
    549 // there are high-performance systems with slow clocks (recent Intel) and
    550 // low-performance systems with fast clocks (older AMD).  We can also use
    551 // physical memory, core configuration, OS details, CPU class and family, and
    552 // CPU manufacturer to disambiguate.
    553 
    554 enum class SystemClass {
    555  DesktopX86,
    556  DesktopX64,
    557  DesktopUnknown32,
    558  DesktopUnknown64,
    559  MobileX86,
    560  MobileArm32,
    561  MobileArm64,
    562  MobileUnknown32,
    563  MobileUnknown64
    564 };
    565 
    566 static SystemClass ClassifySystem() {
    567  bool isDesktop;
    568 
    569 #if defined(ANDROID) || defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_ARM64)
    570  isDesktop = false;
    571 #else
    572  isDesktop = true;
    573 #endif
    574 
    575  if (isDesktop) {
    576 #if defined(JS_CODEGEN_X64)
    577    return SystemClass::DesktopX64;
    578 #elif defined(JS_CODEGEN_X86)
    579    return SystemClass::DesktopX86;
    580 #elif defined(JS_64BIT)
    581    return SystemClass::DesktopUnknown64;
    582 #else
    583    return SystemClass::DesktopUnknown32;
    584 #endif
    585  } else {
    586 #if defined(JS_CODEGEN_X86)
    587    return SystemClass::MobileX86;
    588 #elif defined(JS_CODEGEN_ARM)
    589    return SystemClass::MobileArm32;
    590 #elif defined(JS_CODEGEN_ARM64)
    591    return SystemClass::MobileArm64;
    592 #elif defined(JS_64BIT)
    593    return SystemClass::MobileUnknown64;
    594 #else
    595    return SystemClass::MobileUnknown32;
    596 #endif
    597  }
    598 }
    599 
    600 // Code sizes in machine code bytes per bytecode byte, again empirical except
    601 // where marked.
    602 //
    603 // The Ion estimate for ARM64 is the measured Baseline value scaled by a
    604 // plausible factor for optimized code.
    605 
    606 static const double x64Tox86Inflation = 1.25;
    607 
    608 static const double x64IonBytesPerBytecode = 2.45;
    609 static const double x86IonBytesPerBytecode =
    610    x64IonBytesPerBytecode * x64Tox86Inflation;
    611 static const double arm32IonBytesPerBytecode = 3.3;
    612 static const double arm64IonBytesPerBytecode = 3.0 / 1.4;  // Estimate
    613 
    614 static const double x64BaselineBytesPerBytecode = x64IonBytesPerBytecode * 1.43;
    615 static const double x86BaselineBytesPerBytecode =
    616    x64BaselineBytesPerBytecode * x64Tox86Inflation;
    617 static const double arm32BaselineBytesPerBytecode =
    618    arm32IonBytesPerBytecode * 1.39;
    619 static const double arm64BaselineBytesPerBytecode = 3.0;
    620 
    621 static double OptimizedBytesPerBytecode(SystemClass cls) {
    622  switch (cls) {
    623    case SystemClass::DesktopX86:
    624    case SystemClass::MobileX86:
    625    case SystemClass::DesktopUnknown32:
    626      return x86IonBytesPerBytecode;
    627    case SystemClass::DesktopX64:
    628    case SystemClass::DesktopUnknown64:
    629      return x64IonBytesPerBytecode;
    630    case SystemClass::MobileArm32:
    631    case SystemClass::MobileUnknown32:
    632      return arm32IonBytesPerBytecode;
    633    case SystemClass::MobileArm64:
    634    case SystemClass::MobileUnknown64:
    635      return arm64IonBytesPerBytecode;
    636    default:
    637      MOZ_CRASH();
    638  }
    639 }
    640 
    641 static double BaselineBytesPerBytecode(SystemClass cls) {
    642  switch (cls) {
    643    case SystemClass::DesktopX86:
    644    case SystemClass::MobileX86:
    645    case SystemClass::DesktopUnknown32:
    646      return x86BaselineBytesPerBytecode;
    647    case SystemClass::DesktopX64:
    648    case SystemClass::DesktopUnknown64:
    649      return x64BaselineBytesPerBytecode;
    650    case SystemClass::MobileArm32:
    651    case SystemClass::MobileUnknown32:
    652      return arm32BaselineBytesPerBytecode;
    653    case SystemClass::MobileArm64:
    654    case SystemClass::MobileUnknown64:
    655      return arm64BaselineBytesPerBytecode;
    656    default:
    657      MOZ_CRASH();
    658  }
    659 }
    660 
    661 double wasm::EstimateCompiledCodeSize(Tier tier, size_t bytecodeSize) {
    662  SystemClass cls = ClassifySystem();
    663  switch (tier) {
    664    case Tier::Baseline:
    665      return double(bytecodeSize) * BaselineBytesPerBytecode(cls);
    666    case Tier::Optimized:
    667      return double(bytecodeSize) * OptimizedBytesPerBytecode(cls);
    668  }
    669  MOZ_CRASH("bad tier");
    670 }
    671 
    672 // If parallel Ion compilation is going to take longer than this, we should
    673 // tier.
    674 
    675 static const double tierCutoffMs = 10;
    676 
    677 // Compilation rate values are empirical except when noted, the reference
    678 // systems are:
    679 //
    680 // Late-2013 MacBook Pro (2.6GHz 4 x hyperthreaded Haswell, Mac OS X)
    681 // Late-2015 Nexus 5X (1.4GHz 4 x Cortex-A53 + 1.8GHz 2 x Cortex-A57, Android)
    682 // Ca-2016 SoftIron Overdrive 1000 (1.7GHz 4 x Cortex-A57, Fedora)
    683 //
    684 // The rates are always per core.
    685 //
    686 // The estimate for ARM64 is the Baseline compilation rate on the SoftIron
    687 // (because we have no Ion yet), divided by 5 to estimate Ion compile rate and
    688 // then divided by 2 to make it more reasonable for consumer ARM64 systems.
    689 
    690 static const double x64IonBytecodesPerMs = 2100;
    691 static const double x86IonBytecodesPerMs = 1500;
    692 static const double arm32IonBytecodesPerMs = 450;
    693 static const double arm64IonBytecodesPerMs = 750;  // Estimate
    694 
    695 // Tiering cutoff values: if code section sizes are below these values (when
    696 // divided by the effective number of cores) we do not tier, because we guess
    697 // that parallel Ion compilation will be fast enough.
    698 
    699 static const double x64DesktopTierCutoff = x64IonBytecodesPerMs * tierCutoffMs;
    700 static const double x86DesktopTierCutoff = x86IonBytecodesPerMs * tierCutoffMs;
    701 static const double x86MobileTierCutoff = x86DesktopTierCutoff / 2;  // Guess
    702 static const double arm32MobileTierCutoff =
    703    arm32IonBytecodesPerMs * tierCutoffMs;
    704 static const double arm64MobileTierCutoff =
    705    arm64IonBytecodesPerMs * tierCutoffMs;
    706 
    707 static double CodesizeCutoff(SystemClass cls) {
    708  switch (cls) {
    709    case SystemClass::DesktopX86:
    710    case SystemClass::DesktopUnknown32:
    711      return x86DesktopTierCutoff;
    712    case SystemClass::DesktopX64:
    713    case SystemClass::DesktopUnknown64:
    714      return x64DesktopTierCutoff;
    715    case SystemClass::MobileX86:
    716      return x86MobileTierCutoff;
    717    case SystemClass::MobileArm32:
    718    case SystemClass::MobileUnknown32:
    719      return arm32MobileTierCutoff;
    720    case SystemClass::MobileArm64:
    721    case SystemClass::MobileUnknown64:
    722      return arm64MobileTierCutoff;
    723    default:
    724      MOZ_CRASH();
    725  }
    726 }
    727 
    728 // As the number of cores grows the effectiveness of each core dwindles (on the
    729 // systems we care about for SpiderMonkey).
    730 //
    731 // The data are empirical, computed from the observed compilation time of the
    732 // Tanks demo code on a variable number of cores.
    733 //
    734 // The heuristic may fail on NUMA systems where the core count is high but the
    735 // performance increase is nil or negative once the program moves beyond one
    736 // socket.  However, few browser users have such systems.
    737 
    738 static double EffectiveCores(uint32_t cores) {
    739  if (cores <= 3) {
    740    return pow(cores, 0.9);
    741  }
    742  return pow(cores, 0.75);
    743 }
    744 
    745 #ifndef JS_64BIT
    746 // Don't tier if tiering will fill code memory to more to more than this
    747 // fraction.
    748 
    749 static const double spaceCutoffPct = 0.9;
    750 #endif
    751 
    752 // Figure out whether we should use tiered compilation or not.
    753 static bool TieringBeneficial(bool lazyTiering, uint32_t codeSize) {
    754  // Lazy tiering is assumed to always be beneficial when it is enabled.
    755  if (lazyTiering) {
    756    return true;
    757  }
    758 
    759  uint32_t cpuCount = GetHelperThreadCPUCount();
    760  MOZ_ASSERT(cpuCount > 0);
    761 
    762  // It's mostly sensible not to background compile when there's only one
    763  // hardware thread as we want foreground computation to have access to that.
    764  // However, if wasm background compilation helper threads can be given lower
    765  // priority then background compilation on single-core systems still makes
    766  // some kind of sense.  That said, this is a non-issue: as of September 2017
    767  // 1-core was down to 3.5% of our population and falling.
    768 
    769  if (cpuCount == 1) {
    770    return false;
    771  }
    772 
    773  // Compute the max number of threads available to do actual background
    774  // compilation work.
    775 
    776  uint32_t workers = GetMaxWasmCompilationThreads();
    777 
    778  // The number of cores we will use is bounded both by the CPU count and the
    779  // worker count, since the worker count already takes this into account.
    780 
    781  uint32_t cores = workers;
    782 
    783  SystemClass cls = ClassifySystem();
    784 
    785  // Ion compilation on available cores must take long enough to be worth the
    786  // bother.
    787 
    788  double cutoffSize = CodesizeCutoff(cls);
    789  double effectiveCores = EffectiveCores(cores);
    790 
    791  if ((codeSize / effectiveCores) < cutoffSize) {
    792    return false;
    793  }
    794 
    795  // Do not implement a size cutoff for 64-bit systems since the code size
    796  // budget for 64 bit is so large that it will hardly ever be an issue.
    797  // (Also the cutoff percentage might be different on 64-bit.)
    798 
    799 #ifndef JS_64BIT
    800  // If the amount of executable code for baseline compilation jeopardizes the
    801  // availability of executable memory for ion code then do not tier, for now.
    802  //
    803  // TODO: For now we consider this module in isolation.  We should really
    804  // worry about what else is going on in this process and might be filling up
    805  // the code memory.  It's like we need some kind of code memory reservation
    806  // system or JIT compilation for large modules.
    807 
    808  double ionRatio = OptimizedBytesPerBytecode(cls);
    809  double baselineRatio = BaselineBytesPerBytecode(cls);
    810  double needMemory = codeSize * (ionRatio + baselineRatio);
    811  double availMemory = LikelyAvailableExecutableMemory();
    812  double cutoff = spaceCutoffPct * MaxCodeBytesPerProcess;
    813 
    814  // If the sum of baseline and ion code makes us exceeds some set percentage
    815  // of the executable memory then disable tiering.
    816 
    817  if ((MaxCodeBytesPerProcess - availMemory) + needMemory > cutoff) {
    818    return false;
    819  }
    820 #endif
    821 
    822  return true;
    823 }
    824 
    825 // Ensure that we have the non-compiler requirements to tier safely.
    826 static bool PlatformCanTier(bool lazyTiering) {
    827  // Note: ensure this function stays in sync with `WasmLazyTieringEnabled()`.
    828  // Tiering needs background threads if we're using eager tiering or we're
    829  // using lazy tiering without the synchronous flag.
    830  bool synchronousTiering =
    831      lazyTiering && JS::Prefs::wasm_lazy_tiering_synchronous();
    832 
    833  return (synchronousTiering || CanUseExtraThreads()) &&
    834         jit::CanFlushExecutionContextForAllThreads();
    835 }
    836 
    837 CompilerEnvironment::CompilerEnvironment(const CompileArgs& args)
    838    : state_(InitialWithArgs), args_(&args) {}
    839 
    840 CompilerEnvironment::CompilerEnvironment(CompileMode mode, Tier tier,
    841                                         DebugEnabled debugEnabled)
    842    : state_(InitialWithModeTierDebug),
    843      mode_(mode),
    844      tier_(tier),
    845      debug_(debugEnabled) {}
    846 
    847 void CompilerEnvironment::computeParameters() {
    848  MOZ_ASSERT(state_ == InitialWithModeTierDebug);
    849 
    850  state_ = Computed;
    851 }
    852 
    853 void CompilerEnvironment::computeParameters(const ModuleMetadata& moduleMeta) {
    854  MOZ_ASSERT(!isComputed());
    855 
    856  if (state_ == InitialWithModeTierDebug) {
    857    computeParameters();
    858    return;
    859  }
    860 
    861  bool baselineEnabled = args_->baselineEnabled;
    862  bool ionEnabled = args_->ionEnabled;
    863  bool debugEnabled = args_->debugEnabled;
    864  bool forceTiering = args_->forceTiering;
    865 
    866  bool hasSecondTier = ionEnabled;
    867  MOZ_ASSERT_IF(debugEnabled, baselineEnabled);
    868  MOZ_ASSERT_IF(forceTiering, baselineEnabled && hasSecondTier);
    869 
    870  // Various constraints in various places should prevent failure here.
    871  MOZ_RELEASE_ASSERT(baselineEnabled || ionEnabled);
    872 
    873  bool isGcModule = moduleMeta.codeMeta->types->hasGcType();
    874  uint32_t codeSectionSize = moduleMeta.codeMeta->codeSectionSize();
    875 
    876  // We use lazy tiering if the 'for-all' pref is enabled, or the 'gc-only'
    877  // pref is enabled and we're compiling a GC module.  However, forcing
    878  // serialization-testing disables lazy tiering.
    879  bool testSerialization = args_->features.testSerialization;
    880  bool lazyTiering = (JS::Prefs::wasm_lazy_tiering() ||
    881                      (JS::Prefs::wasm_lazy_tiering_for_gc() && isGcModule)) &&
    882                     !testSerialization;
    883 
    884  if (baselineEnabled && hasSecondTier &&
    885      (TieringBeneficial(lazyTiering, codeSectionSize) || forceTiering) &&
    886      PlatformCanTier(lazyTiering)) {
    887    mode_ = lazyTiering ? CompileMode::LazyTiering : CompileMode::EagerTiering;
    888    tier_ = Tier::Baseline;
    889  } else {
    890    mode_ = CompileMode::Once;
    891    tier_ = hasSecondTier ? Tier::Optimized : Tier::Baseline;
    892  }
    893 
    894  debug_ = debugEnabled ? DebugEnabled::True : DebugEnabled::False;
    895 
    896  state_ = Computed;
    897 }
    898 
    899 template <class DecoderT, class ModuleGeneratorT>
    900 static bool DecodeFunctionBody(DecoderT& d, ModuleGeneratorT& mg,
    901                               uint32_t funcIndex) {
    902  uint32_t bodySize;
    903  if (!d.readVarU32(&bodySize)) {
    904    return d.fail("expected number of function body bytes");
    905  }
    906 
    907  if (bodySize > MaxFunctionBytes) {
    908    return d.fail("function body too big");
    909  }
    910 
    911  const size_t offsetInModule = d.currentOffset();
    912 
    913  // Skip over the function body; it will be validated by the compilation
    914  // thread.
    915  const uint8_t* bodyBegin;
    916  if (!d.readBytes(bodySize, &bodyBegin)) {
    917    return d.fail("function body length too big");
    918  }
    919 
    920  return mg.compileFuncDef(funcIndex, offsetInModule, bodyBegin,
    921                           bodyBegin + bodySize);
    922 }
    923 
    924 template <class DecoderT, class ModuleGeneratorT>
    925 static bool DecodeCodeSection(const CodeMetadata& codeMeta, DecoderT& d,
    926                              ModuleGeneratorT& mg) {
    927  if (!codeMeta.codeSectionRange) {
    928    if (codeMeta.numFuncDefs() != 0) {
    929      return d.fail("expected code section");
    930    }
    931 
    932    return mg.finishFuncDefs();
    933  }
    934 
    935  uint32_t numFuncDefs;
    936  if (!d.readVarU32(&numFuncDefs)) {
    937    return d.fail("expected function body count");
    938  }
    939 
    940  if (numFuncDefs != codeMeta.numFuncDefs()) {
    941    return d.fail(
    942        "function body count does not match function signature count");
    943  }
    944 
    945  for (uint32_t funcDefIndex = 0; funcDefIndex < numFuncDefs; funcDefIndex++) {
    946    if (!DecodeFunctionBody(d, mg, codeMeta.numFuncImports + funcDefIndex)) {
    947      return false;
    948    }
    949  }
    950 
    951  if (!d.finishSection(*codeMeta.codeSectionRange, "code")) {
    952    return false;
    953  }
    954 
    955  return mg.finishFuncDefs();
    956 }
    957 
    958 SharedModule wasm::CompileBuffer(const CompileArgs& args,
    959                                 const BytecodeBufferOrSource& bytecode,
    960                                 UniqueChars* error,
    961                                 UniqueCharsVector* warnings,
    962                                 JS::OptimizedEncodingListener* listener) {
    963  MutableModuleMetadata moduleMeta = js_new<ModuleMetadata>();
    964  if (!moduleMeta || !moduleMeta->init(args)) {
    965    return nullptr;
    966  }
    967 
    968  const BytecodeSource& bytecodeSource = bytecode.source();
    969  Decoder envDecoder(bytecodeSource.envSpan(), bytecodeSource.envRange().start,
    970                     error, warnings);
    971  if (!DecodeModuleEnvironment(envDecoder, moduleMeta->codeMeta.get(),
    972                               moduleMeta)) {
    973    return nullptr;
    974  }
    975 
    976  CompilerEnvironment compilerEnv(args);
    977  compilerEnv.computeParameters(*moduleMeta);
    978  if (!moduleMeta->prepareForCompile(compilerEnv.mode())) {
    979    return nullptr;
    980  }
    981 
    982  ModuleGenerator mg(*moduleMeta->codeMeta, compilerEnv,
    983                     compilerEnv.initialState(), nullptr, error, warnings);
    984  if (!mg.initializeCompleteTier()) {
    985    return nullptr;
    986  }
    987 
    988  // If our bytecode has a code section, then we must switch decoders for
    989  // these section.
    990  if (bytecodeSource.hasCodeSection()) {
    991    // DecodeModuleEnvironment will stop and return true if there is an unknown
    992    // section before the code section. We must check this and return an error.
    993    if (!moduleMeta->codeMeta->codeSectionRange) {
    994      envDecoder.fail("unknown section before code section");
    995      return nullptr;
    996    }
    997 
    998    // Our pre-parse that split the module should ensure that after we've
    999    // parsed the environment there are no bytes left.
   1000    MOZ_RELEASE_ASSERT(envDecoder.done());
   1001 
   1002    Decoder codeDecoder(bytecodeSource.codeSpan(),
   1003                        bytecodeSource.codeRange().start, error, warnings);
   1004    if (!DecodeCodeSection(*moduleMeta->codeMeta, codeDecoder, mg)) {
   1005      return nullptr;
   1006    }
   1007    // Our pre-parse that split the module should ensure that after we've
   1008    // parsed the code section there are no bytes left.
   1009    MOZ_RELEASE_ASSERT(codeDecoder.done());
   1010 
   1011    Decoder tailDecoder(bytecodeSource.tailSpan(),
   1012                        bytecodeSource.tailRange().start, error, warnings);
   1013    if (!DecodeModuleTail(tailDecoder, moduleMeta->codeMeta, moduleMeta)) {
   1014      return nullptr;
   1015    }
   1016    // Decoding the module tail should consume all remaining bytes.
   1017    MOZ_RELEASE_ASSERT(tailDecoder.done());
   1018  } else {
   1019    // We still must call this method even without a code section because it
   1020    // does validation that ensure we aren't missing function definitions.
   1021    if (!DecodeCodeSection(*moduleMeta->codeMeta, envDecoder, mg)) {
   1022      return nullptr;
   1023    }
   1024 
   1025    if (!DecodeModuleTail(envDecoder, moduleMeta->codeMeta, moduleMeta)) {
   1026      return nullptr;
   1027    }
   1028 
   1029    // Decoding the module tail should consume all remaining bytes.
   1030    MOZ_RELEASE_ASSERT(envDecoder.done());
   1031  }
   1032 
   1033  return mg.finishModule(bytecode, *moduleMeta, listener);
   1034 }
   1035 
   1036 bool wasm::CompileCompleteTier2(const ShareableBytes* codeSection,
   1037                                const Module& module, UniqueChars* error,
   1038                                UniqueCharsVector* warnings,
   1039                                Atomic<bool>* cancelled) {
   1040  CompilerEnvironment compilerEnv(CompileMode::EagerTiering, Tier::Optimized,
   1041                                  DebugEnabled::False);
   1042  compilerEnv.computeParameters();
   1043 
   1044  const CodeMetadata& codeMeta = module.codeMeta();
   1045  ModuleGenerator mg(codeMeta, compilerEnv, CompileState::EagerTier2, cancelled,
   1046                     error, warnings);
   1047  if (!mg.initializeCompleteTier()) {
   1048    return false;
   1049  }
   1050 
   1051  if (codeMeta.codeSectionRange) {
   1052    BytecodeSpan codeSpan(codeSection->begin(), codeSection->end());
   1053    Decoder d(codeSpan, codeMeta.codeSectionRange->start, error);
   1054    if (!DecodeCodeSection(module.codeMeta(), d, mg)) {
   1055      return false;
   1056    }
   1057  } else {
   1058    MOZ_ASSERT(!codeSection);
   1059    MOZ_ASSERT(codeMeta.numFuncDefs() == 0);
   1060    if (!mg.finishFuncDefs()) {
   1061      return false;
   1062    }
   1063  }
   1064 
   1065  return mg.finishTier2(module);
   1066 }
   1067 
   1068 bool wasm::CompilePartialTier2(const Code& code, uint32_t funcIndex,
   1069                               UniqueChars* error, UniqueCharsVector* warnings,
   1070                               mozilla::Atomic<bool>* cancelled) {
   1071  CompilerEnvironment compilerEnv(CompileMode::LazyTiering, Tier::Optimized,
   1072                                  DebugEnabled::False);
   1073  compilerEnv.computeParameters();
   1074 
   1075  const CodeMetadata& codeMeta = code.codeMeta();
   1076  ModuleGenerator mg(codeMeta, compilerEnv, CompileState::LazyTier2, cancelled,
   1077                     error, warnings);
   1078  if (!mg.initializePartialTier(code, funcIndex)) {
   1079    // The module is already validated, so this can only be an OOM.
   1080    MOZ_ASSERT(!*error);
   1081    return false;
   1082  }
   1083 
   1084  const BytecodeRange& funcRange = code.codeTailMeta().funcDefRange(funcIndex);
   1085  BytecodeSpan funcBytecode = code.codeTailMeta().funcDefBody(funcIndex);
   1086 
   1087  // The following sequence will compile/finish this function, on this thread.
   1088  // `error` (as stashed in `mg`) may get set to, for example, "stack frame too
   1089  // large", or to "", denoting OOM.
   1090  return mg.compileFuncDef(funcIndex, funcRange.start, funcBytecode.data(),
   1091                           funcBytecode.data() + funcBytecode.size()) &&
   1092         mg.finishFuncDefs() && mg.finishPartialTier2();
   1093 }
   1094 
   1095 class StreamingDecoder {
   1096  Decoder d_;
   1097  const ExclusiveBytesPtr& codeBytesEnd_;
   1098  const Atomic<bool>& cancelled_;
   1099 
   1100 public:
   1101  StreamingDecoder(const CodeMetadata& codeMeta, const Bytes& begin,
   1102                   const ExclusiveBytesPtr& codeBytesEnd,
   1103                   const Atomic<bool>& cancelled, UniqueChars* error,
   1104                   UniqueCharsVector* warnings)
   1105      : d_(begin, codeMeta.codeSectionRange->start, error, warnings),
   1106        codeBytesEnd_(codeBytesEnd),
   1107        cancelled_(cancelled) {}
   1108 
   1109  bool fail(const char* msg) { return d_.fail(msg); }
   1110 
   1111  bool done() const { return d_.done(); }
   1112 
   1113  size_t currentOffset() const { return d_.currentOffset(); }
   1114 
   1115  bool waitForBytes(size_t numBytes) {
   1116    numBytes = std::min(numBytes, d_.bytesRemain());
   1117    const uint8_t* requiredEnd = d_.currentPosition() + numBytes;
   1118    auto codeBytesEnd = codeBytesEnd_.lock();
   1119    while (codeBytesEnd < requiredEnd) {
   1120      if (cancelled_) {
   1121        return false;
   1122      }
   1123      codeBytesEnd.wait();
   1124    }
   1125    return true;
   1126  }
   1127 
   1128  bool readVarU32(uint32_t* u32) {
   1129    return waitForBytes(MaxVarU32DecodedBytes) && d_.readVarU32(u32);
   1130  }
   1131 
   1132  bool readBytes(size_t size, const uint8_t** begin) {
   1133    return waitForBytes(size) && d_.readBytes(size, begin);
   1134  }
   1135 
   1136  bool finishSection(const BytecodeRange& range, const char* name) {
   1137    return d_.finishSection(range, name);
   1138  }
   1139 };
   1140 
   1141 SharedModule wasm::CompileStreaming(
   1142    const CompileArgs& args, const ShareableBytes& envBytes,
   1143    const ShareableBytes& codeBytes, const ExclusiveBytesPtr& codeBytesEnd,
   1144    const ExclusiveStreamEndData& exclusiveStreamEnd,
   1145    const Atomic<bool>& cancelled, UniqueChars* error,
   1146    UniqueCharsVector* warnings) {
   1147  CompilerEnvironment compilerEnv(args);
   1148  MutableModuleMetadata moduleMeta = js_new<ModuleMetadata>();
   1149  if (!moduleMeta || !moduleMeta->init(args)) {
   1150    return nullptr;
   1151  }
   1152  CodeMetadata& codeMeta = *moduleMeta->codeMeta;
   1153 
   1154  {
   1155    Decoder d(envBytes.vector, 0, error, warnings);
   1156 
   1157    if (!DecodeModuleEnvironment(d, &codeMeta, moduleMeta)) {
   1158      return nullptr;
   1159    }
   1160    compilerEnv.computeParameters(*moduleMeta);
   1161 
   1162    if (!codeMeta.codeSectionRange) {
   1163      d.fail("unknown section before code section");
   1164      return nullptr;
   1165    }
   1166 
   1167    MOZ_RELEASE_ASSERT(codeMeta.codeSectionRange->size() == codeBytes.length());
   1168    MOZ_RELEASE_ASSERT(d.done());
   1169  }
   1170 
   1171  if (!moduleMeta->prepareForCompile(compilerEnv.mode())) {
   1172    return nullptr;
   1173  }
   1174 
   1175  ModuleGenerator mg(codeMeta, compilerEnv, compilerEnv.initialState(),
   1176                     &cancelled, error, warnings);
   1177  if (!mg.initializeCompleteTier()) {
   1178    return nullptr;
   1179  }
   1180 
   1181  {
   1182    StreamingDecoder d(codeMeta, codeBytes.vector, codeBytesEnd, cancelled,
   1183                       error, warnings);
   1184 
   1185    if (!DecodeCodeSection(codeMeta, d, mg)) {
   1186      return nullptr;
   1187    }
   1188 
   1189    MOZ_RELEASE_ASSERT(d.done());
   1190  }
   1191 
   1192  {
   1193    auto streamEnd = exclusiveStreamEnd.lock();
   1194    while (!streamEnd->reached) {
   1195      if (cancelled) {
   1196        return nullptr;
   1197      }
   1198      streamEnd.wait();
   1199    }
   1200  }
   1201 
   1202  const StreamEndData streamEnd = exclusiveStreamEnd.lock();
   1203  const ShareableBytes& tailBytes = *streamEnd.tailBytes;
   1204 
   1205  {
   1206    Decoder d(tailBytes.vector, codeMeta.codeSectionRange->end, error,
   1207              warnings);
   1208 
   1209    if (!DecodeModuleTail(d, &codeMeta, moduleMeta)) {
   1210      return nullptr;
   1211    }
   1212 
   1213    MOZ_RELEASE_ASSERT(d.done());
   1214  }
   1215 
   1216  BytecodeBuffer bytecodeBuffer(&envBytes, &codeBytes, &tailBytes);
   1217  return mg.finishModule(BytecodeBufferOrSource(bytecodeBuffer), *moduleMeta,
   1218                         streamEnd.completeTier2Listener);
   1219 }
   1220 
   1221 class DumpIonModuleGenerator {
   1222 private:
   1223  const CompilerEnvironment& compilerEnv_;
   1224  CodeMetadata& codeMeta_;
   1225  uint32_t targetFuncIndex_;
   1226  GenericPrinter& out_;
   1227  UniqueChars* error_;
   1228 
   1229 public:
   1230  DumpIonModuleGenerator(const CompilerEnvironment& compilerEnv,
   1231                         CodeMetadata& codeMeta, uint32_t targetFuncIndex,
   1232                         GenericPrinter& out, UniqueChars* error)
   1233      : compilerEnv_(compilerEnv),
   1234        codeMeta_(codeMeta),
   1235        targetFuncIndex_(targetFuncIndex),
   1236        out_(out),
   1237        error_(error) {}
   1238 
   1239  bool finishFuncDefs() { return true; }
   1240  bool compileFuncDef(uint32_t funcIndex, uint32_t lineOrBytecode,
   1241                      const uint8_t* begin, const uint8_t* end) {
   1242    if (funcIndex != targetFuncIndex_) {
   1243      return true;
   1244    }
   1245 
   1246    FuncCompileInput input(funcIndex, lineOrBytecode, begin, end,
   1247                           Uint32Vector());
   1248    return IonDumpFunction(compilerEnv_, codeMeta_, input, out_, error_);
   1249  }
   1250 };
   1251 
   1252 bool wasm::DumpIonFunctionInModule(const ShareableBytes& bytecode,
   1253                                   uint32_t targetFuncIndex,
   1254                                   GenericPrinter& out, UniqueChars* error) {
   1255  SharedCompileArgs compileArgs =
   1256      CompileArgs::buildForValidation(FeatureArgs::allEnabled());
   1257  if (!compileArgs) {
   1258    return false;
   1259  }
   1260  CompilerEnvironment compilerEnv(CompileMode::Once, Tier::Optimized,
   1261                                  DebugEnabled::False);
   1262  compilerEnv.computeParameters();
   1263 
   1264  UniqueCharsVector warnings;
   1265  Decoder d(bytecode.span(), 0, error, &warnings);
   1266  MutableModuleMetadata moduleMeta = js_new<ModuleMetadata>();
   1267  if (!moduleMeta || !moduleMeta->init(*compileArgs)) {
   1268    return false;
   1269  }
   1270 
   1271  if (!DecodeModuleEnvironment(d, moduleMeta->codeMeta, moduleMeta)) {
   1272    return false;
   1273  }
   1274 
   1275  DumpIonModuleGenerator mg(compilerEnv, *moduleMeta->codeMeta, targetFuncIndex,
   1276                            out, error);
   1277  return moduleMeta->prepareForCompile(CompileMode::Once) &&
   1278         DecodeCodeSection(*moduleMeta->codeMeta, d, mg);
   1279 }