/* -*- 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 "jit/JitOptions.h" #include #include #include "vm/JSScript.h" using namespace js; using namespace js::jit; using mozilla::Maybe; namespace js { namespace jit { DefaultJitOptions JitOptions; static void Warn(const char* env, const char* value) { fprintf(stderr, "Warning: I didn't understand %s=\"%s\"\n", env, value); } static Maybe ParseInt(const char* str) { char* endp; int retval = strtol(str, &endp, 0); if (*endp == '\0') { return mozilla::Some(retval); } return mozilla::Nothing(); } template T overrideDefault(const char* param, T dflt) { char* str = getenv(param); if (!str) { return dflt; } if constexpr (std::is_same_v) { if (strcmp(str, "true") == 0 || strcmp(str, "yes") == 0) { return true; } if (strcmp(str, "false") == 0 || strcmp(str, "no") == 0) { return false; } Warn(param, str); } else { Maybe value = ParseInt(str); if (value.isSome()) { return value.ref(); } Warn(param, str); } return dflt; } #define SET_DEFAULT(var, dflt) var = overrideDefault("JIT_OPTION_" #var, dflt) DefaultJitOptions::DefaultJitOptions() { // Whether to perform expensive graph-consistency DEBUG-only assertions. // It can be useful to disable this to reduce DEBUG-compile time of large // wasm programs. SET_DEFAULT(checkGraphConsistency, true); #ifdef CHECK_OSIPOINT_REGISTERS // Emit extra code to verify live regs at the start of a VM call // are not modified before its OsiPoint. SET_DEFAULT(checkOsiPointRegisters, false); #endif // Whether to enable extra code to perform dynamic validation of // RangeAnalysis results. SET_DEFAULT(checkRangeAnalysis, false); // Toggles whether Alignment Mask Analysis is globally disabled. SET_DEFAULT(disableAma, false); // Toggles whether Effective Address Analysis is globally disabled. SET_DEFAULT(disableEaa, false); // Toggles whether Edge Case Analysis is gobally disabled. SET_DEFAULT(disableEdgeCaseAnalysis, false); // Toggle whether global value numbering is globally disabled. SET_DEFAULT(disableGvn, false); // Toggles whether inlining is globally disabled. SET_DEFAULT(disableInlining, false); // Toggles whether loop invariant code motion is globally disabled. SET_DEFAULT(disableLicm, false); // Toggle whether branch pruning is globally disabled. SET_DEFAULT(disablePruning, false); // Toggles whether instruction reordering is globally disabled. SET_DEFAULT(disableInstructionReordering, false); // Toggles whether Range Analysis is globally disabled. SET_DEFAULT(disableRangeAnalysis, false); // Toggles wheter Recover instructions is globally disabled. SET_DEFAULT(disableRecoverIns, false); // Toggle whether eager scalar replacement is globally disabled. SET_DEFAULT(disableScalarReplacement, false); // Toggles whether CacheIR stubs are used. SET_DEFAULT(disableCacheIR, false); // Toggles whether sink code motion is globally disabled. SET_DEFAULT(disableSink, true); // Toggles whether redundant shape guard elimination is globally disabled. SET_DEFAULT(disableRedundantShapeGuards, false); // Toggles whether we verify that we don't recompile with the same CacheIR. SET_DEFAULT(disableBailoutLoopCheck, false); // Whether the Baseline Interpreter is enabled. SET_DEFAULT(baselineInterpreter, true); // Whether the Baseline JIT is enabled. SET_DEFAULT(baselineJit, true); // Whether the IonMonkey JIT is enabled. SET_DEFAULT(ion, true); // Whether the IonMonkey and Baseline JITs are enabled for Trusted Principals. // (Ignored if ion or baselineJit is set to true.) SET_DEFAULT(jitForTrustedPrincipals, false); // Whether the RegExp JIT is enabled. SET_DEFAULT(nativeRegExp, true); // Whether Warp should use ICs instead of transpiling Baseline CacheIR. SET_DEFAULT(forceInlineCaches, false); // Whether all ICs should be initialized as megamorphic ICs. SET_DEFAULT(forceMegamorphicICs, false); // Toggles whether large scripts are rejected. SET_DEFAULT(limitScriptSize, true); // Toggles whether functions may be entered at loop headers. SET_DEFAULT(osr, true); // Whether the JIT backend (used by JITs, Wasm, Baseline Interpreter) has been // disabled for this process. See JS::DisableJitBackend. SET_DEFAULT(disableJitBackend, false); // Whether to enable extra code to perform dynamic validations. SET_DEFAULT(runExtraChecks, false); // How many invocations or loop iterations are needed before functions // enter the Baseline Interpreter. SET_DEFAULT(baselineInterpreterWarmUpThreshold, 10); // How many invocations or loop iterations are needed before functions // are compiled with the baseline compiler. // Duplicated in all.js - ensure both match. SET_DEFAULT(baselineJitWarmUpThreshold, 100); // How many invocations or loop iterations are needed before functions // are considered for trial inlining. SET_DEFAULT(trialInliningWarmUpThreshold, 500); // The initial warm-up count for ICScripts created by trial inlining. // // Note: the difference between trialInliningInitialWarmUpCount and // trialInliningWarmUpThreshold must be: // // * Small enough to allow inlining multiple levels deep before the outer // script reaches its normalIonWarmUpThreshold. // // * Greater than inliningEntryThreshold or no scripts can be inlined. SET_DEFAULT(trialInliningInitialWarmUpCount, 250); // How many invocations or loop iterations are needed before functions // are compiled with the Ion compiler at OptimizationLevel::Normal. // Duplicated in all.js - ensure both match. SET_DEFAULT(normalIonWarmUpThreshold, 1500); // How many invocations are needed before regexps are compiled to // native code. SET_DEFAULT(regexpWarmUpThreshold, 10); // Number of exception bailouts (resuming into catch/finally block) before // we invalidate and forbid Ion compilation. SET_DEFAULT(exceptionBailoutThreshold, 10); // Number of bailouts without invalidation before we set // JSScript::hadFrequentBailouts and invalidate. // Duplicated in all.js - ensure both match. SET_DEFAULT(frequentBailoutThreshold, 10); // Whether to run all debug checks in debug builds. // Disabling might make it more enjoyable to run JS in debug builds. SET_DEFAULT(fullDebugChecks, true); // How many actual arguments are accepted on the C stack. SET_DEFAULT(maxStackArgs, 4096); // How many times we will try to enter a script via OSR before // invalidating the script. SET_DEFAULT(osrPcMismatchesBeforeRecompile, 6000); // The bytecode length limit for small function. SET_DEFAULT(smallFunctionMaxBytecodeLength, 130); // The minimum entry count for an IC stub before it can be trial-inlined. SET_DEFAULT(inliningEntryThreshold, 100); // An artificial testing limit for the maximum supported offset of // pc-relative jump and call instructions. SET_DEFAULT(jumpThreshold, UINT32_MAX); // Branch pruning heuristic is based on a scoring system, which is look at // different metrics and provide a score. The score is computed as a // projection where each factor defines the weight of each metric. Then this // score is compared against a threshold to prevent a branch from being // removed. SET_DEFAULT(branchPruningHitCountFactor, 1); SET_DEFAULT(branchPruningInstFactor, 10); SET_DEFAULT(branchPruningBlockSpanFactor, 100); SET_DEFAULT(branchPruningEffectfulInstFactor, 3500); SET_DEFAULT(branchPruningThreshold, 4000); // Limits on bytecode length and number of locals/arguments for Ion // compilation. There are different (lower) limits for when off-thread Ion // compilation isn't available. SET_DEFAULT(ionMaxScriptSize, 100 * 1000); SET_DEFAULT(ionMaxScriptSizeMainThread, 2 * 1000); SET_DEFAULT(ionMaxLocalsAndArgs, 10 * 1000); SET_DEFAULT(ionMaxLocalsAndArgsMainThread, 256); // Force the used register allocator instead of letting the optimization // pass decide. const char* forcedRegisterAllocatorEnv = "JIT_OPTION_forcedRegisterAllocator"; if (const char* env = getenv(forcedRegisterAllocatorEnv)) { forcedRegisterAllocator = LookupRegisterAllocator(env); if (!forcedRegisterAllocator.isSome()) { Warn(forcedRegisterAllocatorEnv, env); } } #if defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64) || \ defined(JS_CODEGEN_LOONG64) SET_DEFAULT(spectreIndexMasking, false); SET_DEFAULT(spectreObjectMitigations, false); SET_DEFAULT(spectreStringMitigations, false); SET_DEFAULT(spectreValueMasking, false); SET_DEFAULT(spectreJitToCxxCalls, false); #else SET_DEFAULT(spectreIndexMasking, true); SET_DEFAULT(spectreObjectMitigations, true); SET_DEFAULT(spectreStringMitigations, true); SET_DEFAULT(spectreValueMasking, true); SET_DEFAULT(spectreJitToCxxCalls, true); #endif // This is set to its actual value in InitializeJit. SET_DEFAULT(supportsUnalignedAccesses, false); // To access local (non-argument) slots, it's more efficient to use the frame // pointer (FP) instead of the stack pointer (SP) as base register on x86 and // x64 (because instructions are one byte shorter, for example). // // However, because this requires a negative offset from FP, on ARM64 it can // be more efficient to use SP-relative addresses for larger stack frames // because the range for load/store immediate offsets is [-256, 4095] and // offsets outside this range will require an extra instruction. // // We default to FP-relative addresses on x86/x64 and SP-relative on other // platforms, but to improve fuzzing we allow changing this in the shell: // // setJitCompilerOption("base-reg-for-locals", N); // 0 for SP, 1 for FP #if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64) baseRegForLocals = BaseRegForAddress::FP; #else baseRegForLocals = BaseRegForAddress::SP; #endif // Toggles the optimization whereby offsets are folded into loads and not // included in the bounds check. SET_DEFAULT(wasmFoldOffsets, true); // Controls whether two-tiered compilation should be requested when // compiling a new wasm module, independently of other heuristics, and // should be delayed to test both baseline and ion paths in compiled code, // as well as the transition from one tier to the other. SET_DEFAULT(wasmDelayTier2, false); // Until which wasm bytecode size should we accumulate functions, in order // to compile efficiently on helper threads. Baseline code compiles much // faster than Ion code so use scaled thresholds (see also bug 1320374). SET_DEFAULT(wasmBatchBaselineThreshold, 10000); SET_DEFAULT(wasmBatchIonThreshold, 1100); // Controls how much assertion checking code is emitted SET_DEFAULT(lessDebugCode, false); // Whether the MegamorphicCache is enabled. SET_DEFAULT(enableWatchtowerMegamorphic, true); SET_DEFAULT(enableWasmJitExit, true); SET_DEFAULT(enableWasmJitEntry, true); SET_DEFAULT(enableWasmIonFastCalls, true); #ifdef WASM_CODEGEN_DEBUG SET_DEFAULT(enableWasmImportCallSpew, false); SET_DEFAULT(enableWasmFuncCallSpew, false); #endif // This is used to control whether regexps tier up from interpreted to // compiled. We control this with --no-native-regexp and // --regexp-warmup-threshold. SET_DEFAULT(regexp_tier_up, true); // Dumps a representation of parsed regexps to stderr SET_DEFAULT(trace_regexp_parser, false); // Dumps the calls made to the regexp assembler to stderr SET_DEFAULT(trace_regexp_assembler, false); // Dumps the bytecodes interpreted by the regexp engine to stderr SET_DEFAULT(trace_regexp_bytecodes, false); // Dumps the changes made by the regexp peephole optimizer to stderr SET_DEFAULT(trace_regexp_peephole_optimization, false); // ***** Irregexp shim flags ***** // V8 uses this for differential fuzzing to handle stack overflows. // We address the same problem in StackLimitCheck::HasOverflowed. SET_DEFAULT(correctness_fuzzer_suppressions, false); // Instead of using a flag for this, we provide an implementation of // CanReadUnaligned in SMRegExpMacroAssembler. SET_DEFAULT(enable_regexp_unaligned_accesses, false); // This is used to guard an old prototype implementation of possessive // quantifiers, which never got past the point of adding parser support. SET_DEFAULT(regexp_possessive_quantifier, false); // These affect the default level of optimization. We can still turn // optimization off on a case-by-case basis in CompilePattern - for // example, if a regexp is too long - so we might as well turn these // flags on unconditionally. SET_DEFAULT(regexp_optimization, true); #if MOZ_BIG_ENDIAN() // peephole optimization not supported on big endian SET_DEFAULT(regexp_peephole_optimization, false); #else SET_DEFAULT(regexp_peephole_optimization, true); #endif } bool DefaultJitOptions::isSmallFunction(JSScript* script) const { return script->length() <= smallFunctionMaxBytecodeLength; } void DefaultJitOptions::enableGvn(bool enable) { disableGvn = !enable; } void DefaultJitOptions::setEagerBaselineCompilation() { baselineInterpreterWarmUpThreshold = 0; baselineJitWarmUpThreshold = 0; regexpWarmUpThreshold = 0; } void DefaultJitOptions::setEagerIonCompilation() { setEagerBaselineCompilation(); normalIonWarmUpThreshold = 0; } void DefaultJitOptions::setFastWarmUp() { baselineInterpreterWarmUpThreshold = 4; baselineJitWarmUpThreshold = 10; trialInliningWarmUpThreshold = 14; trialInliningInitialWarmUpCount = 12; normalIonWarmUpThreshold = 30; inliningEntryThreshold = 2; smallFunctionMaxBytecodeLength = 2000; } void DefaultJitOptions::setNormalIonWarmUpThreshold(uint32_t warmUpThreshold) { normalIonWarmUpThreshold = warmUpThreshold; } void DefaultJitOptions::resetNormalIonWarmUpThreshold() { jit::DefaultJitOptions defaultValues; setNormalIonWarmUpThreshold(defaultValues.normalIonWarmUpThreshold); } } // namespace jit } // namespace js