ScriptedCaller ScriptedCaller::selfHosted(JSContext* cx) {
AutoEnterOOMUnsafeRegion oomUnsafe; // The self_hosted_ atom is used by the saved stack code to distinguish self // hosted frames from normal user frames.
UniqueChars selfHosted =
StringToNewUTF8CharsZ(cx, *cx->names().self_hosted_.get()); if (!selfHosted) {
oomUnsafe.crash("ScriptedCaller::selfHosted");
} return ScriptedCaller(std::move(selfHosted), ScriptedCallerKind::SelfHosted, 0);
}
features.simd = jit::JitSupportsWasmSimd();
features.isBuiltinModule = options.isBuiltinModule; if (features.isBuiltinModule) { // Builtin modules can use stack switching if it's available. JS-PI needs // this.
features.stackSwitching = wasm::IonPlatformSupport(); // No builtin modules are available to use within a builtin module. We // theoretically could allow a builtin module to import another builtin // module, but we'd need to find a way to prevent cycles. For now just // disable this.
MOZ_ASSERT(!options.jsStringBuiltins);
MOZ_ASSERT(!options.jsStringConstants);
MOZ_ASSERT(!options.mozIntGemm);
} else { // Enable builtin modules that have been selected by the user.
features.builtinModules.jsString = options.jsStringBuiltins;
features.builtinModules.jsStringConstants = options.jsStringConstants;
features.builtinModules.jsStringConstantsNamespace =
options.jsStringConstantsNamespace;
features.builtinModules.intGemm =
MozIntGemmAvailable(cx) && options.mozIntGemm;
}
// If the user requested to disable ion and we're able to, fallback to // baseline. if (baseline && options.disableOptimizingCompiler) {
ion = false;
}
// Debug information such as source view or debug traps will require // additional memory and permanently stay in baseline code, so we try to // only enable it when a developer actually cares: when the debugger tab // is open. bool debug = cx->realm() && cx->realm()->debuggerObservesWasm();
// The <Compiler>Available() predicates should ensure no failure here, but // when we're fuzzing we allow inconsistent switches and the check may thus // fail. Let it go to a run-time error instead of crashing. if (debug && ion) {
*error = CompileArgsError::NoCompiler; return nullptr;
}
if (forceTiering && !(baseline && ion)) { // This can happen only in testing, and in this case we don't have a // proper way to signal the error, so just silently override the default, // instead of adding a skip-if directive to every test using debug/gc.
forceTiering = false;
}
target->scriptedCaller = std::move(scriptedCaller); // AsmJS is deprecated and doesn't have mechanisms for experimental features, // so we don't need to initialize the FeatureArgs. It also only targets the // Ion backend and does not need WASM debug support since it is de-optimized // to JS in that case.
target->ionEnabled = true;
target->debugEnabled = false;
// Validation will not need compilers, just mark them disabled
target->baselineEnabled = false;
target->ionEnabled = false;
target->debugEnabled = false;
target->forceTiering = false;
switch (error) { case CompileArgsError::NoCompiler: {
JS_ReportErrorASCII(cx, "no WebAssembly compiler available"); break;
} case CompileArgsError::OutOfMemory: { // Most callers are required to return 'false' without reporting an OOM, // so we make reporting it optional here. if (reportOOM) {
ReportOutOfMemory(cx);
} break;
}
} return nullptr;
}
BytecodeRange envRange;
BytecodeRange tailRange; if (codeRange.end <= length) {
envRange = BytecodeRange(0, codeRange.start);
tailRange = BytecodeRange(codeRange.end, length - codeRange.end);
} else {
MOZ_RELEASE_ASSERT(codeRange.start <= length); // If the specified code range is larger than the buffer, clamp it to the // the buffer size. This buffer will be rejected later.
envRange = BytecodeRange(0, codeRange.start);
codeRange = BytecodeRange(codeRange.start, length - codeRange.start);
MOZ_RELEASE_ASSERT(codeRange.end == length);
tailRange = BytecodeRange(length, 0);
}
// Classify the current system as one of a set of recognizable classes. This // really needs to get our tier-1 systems right. // // TODO: We don't yet have a good measure of how fast a system is. We // distinguish between mobile and desktop because these are very different kinds // of systems, but we could further distinguish between low / medium / high end // within those major classes. If we do so, then constants below would be // provided for each (class, architecture, system-tier) combination, not just // (class, architecture) as now. // // CPU clock speed is not by itself a good predictor of system performance, as // there are high-performance systems with slow clocks (recent Intel) and // low-performance systems with fast clocks (older AMD). We can also use // physical memory, core configuration, OS details, CPU class and family, and // CPU manufacturer to disambiguate.
// Code sizes in machine code bytes per bytecode byte, again empirical except // where marked. // // The Ion estimate for ARM64 is the measured Baseline value scaled by a // plausible factor for optimized code.
staticdouble OptimizedBytesPerBytecode(SystemClass cls) { switch (cls) { case SystemClass::DesktopX86: case SystemClass::MobileX86: case SystemClass::DesktopUnknown32: return x86IonBytesPerBytecode; case SystemClass::DesktopX64: case SystemClass::DesktopUnknown64: return x64IonBytesPerBytecode; case SystemClass::MobileArm32: case SystemClass::MobileUnknown32: return arm32IonBytesPerBytecode; case SystemClass::MobileArm64: case SystemClass::MobileUnknown64: return arm64IonBytesPerBytecode; default:
MOZ_CRASH();
}
}
staticdouble BaselineBytesPerBytecode(SystemClass cls) { switch (cls) { case SystemClass::DesktopX86: case SystemClass::MobileX86: case SystemClass::DesktopUnknown32: return x86BaselineBytesPerBytecode; case SystemClass::DesktopX64: case SystemClass::DesktopUnknown64: return x64BaselineBytesPerBytecode; case SystemClass::MobileArm32: case SystemClass::MobileUnknown32: return arm32BaselineBytesPerBytecode; case SystemClass::MobileArm64: case SystemClass::MobileUnknown64: return arm64BaselineBytesPerBytecode; default:
MOZ_CRASH();
}
}
// If parallel Ion compilation is going to take longer than this, we should // tier.
staticconstdouble tierCutoffMs = 10;
// Compilation rate values are empirical except when noted, the reference // systems are: // // Late-2013 MacBook Pro (2.6GHz 4 x hyperthreaded Haswell, Mac OS X) // Late-2015 Nexus 5X (1.4GHz 4 x Cortex-A53 + 1.8GHz 2 x Cortex-A57, Android) // Ca-2016 SoftIron Overdrive 1000 (1.7GHz 4 x Cortex-A57, Fedora) // // The rates are always per core. // // The estimate for ARM64 is the Baseline compilation rate on the SoftIron // (because we have no Ion yet), divided by 5 to estimate Ion compile rate and // then divided by 2 to make it more reasonable for consumer ARM64 systems.
// Tiering cutoff values: if code section sizes are below these values (when // divided by the effective number of cores) we do not tier, because we guess // that parallel Ion compilation will be fast enough.
staticdouble CodesizeCutoff(SystemClass cls) { switch (cls) { case SystemClass::DesktopX86: case SystemClass::DesktopUnknown32: return x86DesktopTierCutoff; case SystemClass::DesktopX64: case SystemClass::DesktopUnknown64: return x64DesktopTierCutoff; case SystemClass::MobileX86: return x86MobileTierCutoff; case SystemClass::MobileArm32: case SystemClass::MobileUnknown32: return arm32MobileTierCutoff; case SystemClass::MobileArm64: case SystemClass::MobileUnknown64: return arm64MobileTierCutoff; default:
MOZ_CRASH();
}
}
// As the number of cores grows the effectiveness of each core dwindles (on the // systems we care about for SpiderMonkey). // // The data are empirical, computed from the observed compilation time of the // Tanks demo code on a variable number of cores. // // The heuristic may fail on NUMA systems where the core count is high but the // performance increase is nil or negative once the program moves beyond one // socket. However, few browser users have such systems.
#ifndef JS_64BIT // Don't tier if tiering will fill code memory to more to more than this // fraction.
staticconstdouble spaceCutoffPct = 0.9; #endif
// Figure out whether we should use tiered compilation or not. staticbool TieringBeneficial(bool lazyTiering, uint32_t codeSize) { // Lazy tiering is assumed to always be beneficial when it is enabled. if (lazyTiering) { returntrue;
}
// It's mostly sensible not to background compile when there's only one // hardware thread as we want foreground computation to have access to that. // However, if wasm background compilation helper threads can be given lower // priority then background compilation on single-core systems still makes // some kind of sense. That said, this is a non-issue: as of September 2017 // 1-core was down to 3.5% of our population and falling.
if (cpuCount == 1) { returnfalse;
}
// Compute the max number of threads available to do actual background // compilation work.
if ((codeSize / effectiveCores) < cutoffSize) { returnfalse;
}
// Do not implement a size cutoff for 64-bit systems since the code size // budget for 64 bit is so large that it will hardly ever be an issue. // (Also the cutoff percentage might be different on 64-bit.)
#ifndef JS_64BIT // If the amount of executable code for baseline compilation jeopardizes the // availability of executable memory for ion code then do not tier, for now. // // TODO: For now we consider this module in isolation. We should really // worry about what else is going on in this process and might be filling up // the code memory. It's like we need some kind of code memory reservation // system or JIT compilation for large modules.
// Ensure that we have the non-compiler requirements to tier safely. staticbool PlatformCanTier(bool lazyTiering) { // Note: ensure this function stays in sync with `WasmLazyTieringEnabled()`. // Tiering needs background threads if we're using eager tiering or we're // using lazy tiering without the synchronous flag. bool synchronousTiering =
lazyTiering && JS::Prefs::wasm_lazy_tiering_synchronous();
// We use lazy tiering if the pref is enabled and we're not doing // serialization-testing. bool testSerialization = args_->features.testSerialization; bool lazyTiering = JS::Prefs::wasm_lazy_tiering() && !testSerialization;
template <class DecoderT, class ModuleGeneratorT> staticbool DecodeFunctionBody(DecoderT& d, ModuleGeneratorT& mg,
uint32_t funcIndex) {
uint32_t bodySize; if (!d.readVarU32(&bodySize)) { return d.fail("expected number of function body bytes");
}
if (bodySize > MaxFunctionBytes) { return d.fail("function body too big");
}
const size_t offsetInModule = d.currentOffset();
// Skip over the function body; it will be validated by the compilation // thread. const uint8_t* bodyBegin; if (!d.readBytes(bodySize, &bodyBegin)) { return d.fail("function body length too big");
}
// If our bytecode has a code section, then we must switch decoders for // these section. if (bytecodeSource.hasCodeSection()) { // DecodeModuleEnvironment will stop and return true if there is an unknown // section before the code section. We must check this and return an error. if (!moduleMeta->codeMeta->codeSectionRange) {
envDecoder.fail("unknown section before code section"); return nullptr;
}
// Our pre-parse that split the module should ensure that after we've // parsed the environment there are no bytes left.
MOZ_RELEASE_ASSERT(envDecoder.done());
Decoder codeDecoder(bytecodeSource.codeSpan(),
bytecodeSource.codeRange().start, error, warnings); if (!DecodeCodeSection(*moduleMeta->codeMeta, codeDecoder, mg)) { return nullptr;
} // Our pre-parse that split the module should ensure that after we've // parsed the code section there are no bytes left.
MOZ_RELEASE_ASSERT(codeDecoder.done());
Decoder tailDecoder(bytecodeSource.tailSpan(),
bytecodeSource.tailRange().start, error, warnings); if (!DecodeModuleTail(tailDecoder, moduleMeta->codeMeta, moduleMeta)) { return nullptr;
} // Decoding the module tail should consume all remaining bytes.
MOZ_RELEASE_ASSERT(tailDecoder.done());
} else { // We still must call this method even without a code section because it // does validation that ensure we aren't missing function definitions. if (!DecodeCodeSection(*moduleMeta->codeMeta, envDecoder, mg)) { return nullptr;
}
if (!DecodeModuleTail(envDecoder, moduleMeta->codeMeta, moduleMeta)) { return nullptr;
}
// Decoding the module tail should consume all remaining bytes.
MOZ_RELEASE_ASSERT(envDecoder.done());
}
const CodeMetadata& codeMeta = code.codeMeta();
ModuleGenerator mg(codeMeta, compilerEnv, CompileState::LazyTier2, cancelled,
error, warnings); if (!mg.initializePartialTier(code, funcIndex)) { // The module is already validated, so this can only be an OOM.
MOZ_ASSERT(!*error); returnfalse;
}
// The following sequence will compile/finish this function, on this thread. // `error` (as stashed in `mg`) may get set to, for example, "stack frame too // large", or to "", denoting OOM. return mg.compileFuncDef(funcIndex, funcRange.start, funcBytecode.data(),
funcBytecode.data() + funcBytecode.size()) &&
mg.finishFuncDefs() && mg.finishPartialTier2();
}
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.