Bug 939614: IonMonkey: Split IonOptions in IonOptions and IonOptimizations, r=jandem
☠☠ backed out by ad9dc3482bd4 ☠ ☠
authorHannes Verschore <hv1989@gmail.com>
Thu, 12 Dec 2013 15:14:12 +0100
changeset 176239 fa2005c9ca0220a8c4c073b485a64fa2b25a9271
parent 176238 7d3c745a811f383c2a3ddf8c48126cbcd3740fa7
child 176240 2b87186ddb6df93d330d948e7a9bf5eef558ba2d
push id3343
push userffxbld
push dateMon, 17 Mar 2014 21:55:32 +0000
treeherdermozilla-beta@2f7d3415f79f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjandem
bugs939614
milestone29.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 939614: IonMonkey: Split IonOptions in IonOptions and IonOptimizations, r=jandem
js/src/jit/AsmJS.cpp
js/src/jit/BaselineCompiler.cpp
js/src/jit/CodeGenerator.cpp
js/src/jit/Ion.cpp
js/src/jit/Ion.h
js/src/jit/IonAnalysis.cpp
js/src/jit/IonBuilder.cpp
js/src/jit/IonBuilder.h
js/src/jit/IonCode.h
js/src/jit/IonOptimizationLevels.cpp
js/src/jit/IonOptimizationLevels.h
js/src/jit/JitOptions.cpp
js/src/jit/JitOptions.h
js/src/jit/Lowering.cpp
js/src/jit/MCallOptimize.cpp
js/src/jit/MIR.h
js/src/jit/MIRGenerator.h
js/src/jit/MIRGraph.cpp
js/src/jit/UnreachableCodeElimination.cpp
js/src/jit/shared/Lowering-shared-inl.h
js/src/jsapi.cpp
js/src/jsscript.h
js/src/jsworkers.cpp
js/src/moz.build
js/src/shell/js.cpp
--- a/js/src/jit/AsmJS.cpp
+++ b/js/src/jit/AsmJS.cpp
@@ -1931,17 +1931,19 @@ class FunctionCompiler
     {
         JS_ASSERT(locals_.count() == argTypes.length() + varInitializers_.length());
 
         alloc_  = lifo_.new_<TempAllocator>(&lifo_);
         ionContext_.construct(m_.cx(), alloc_);
 
         graph_  = lifo_.new_<MIRGraph>(alloc_);
         info_   = lifo_.new_<CompileInfo>(locals_.count(), SequentialExecution);
-        mirGen_ = lifo_.new_<MIRGenerator>(CompileCompartment::get(cx()->compartment()), alloc_, graph_, info_);
+        const OptimizationInfo *optimizationInfo = js_IonOptimizations.get(Optimization_AsmJS);
+        mirGen_ = lifo_.new_<MIRGenerator>(CompileCompartment::get(cx()->compartment()), alloc_,
+                                           graph_, info_, optimizationInfo);
 
         if (!newBlock(/* pred = */ nullptr, &curBlock_, fn_))
             return false;
 
         curBlock_->add(MAsmJSCheckOverRecursed::New(alloc(), &m_.stackOverflowLabel()));
 
         for (ABIArgTypeIter i = argTypes; !i.done(); i++) {
             MAsmJSParameter *ins = MAsmJSParameter::New(alloc(), *i, i.mirType());
--- a/js/src/jit/BaselineCompiler.cpp
+++ b/js/src/jit/BaselineCompiler.cpp
@@ -632,17 +632,17 @@ BaselineCompiler::emitUseCountIncrement(
     // count but don't attempt OSR (Ion only compiles the try block).
     if (analysis_.info(pc).loopEntryInCatchOrFinally) {
         JS_ASSERT(JSOp(*pc) == JSOP_LOOPENTRY);
         return true;
     }
 
     Label skipCall;
 
-    uint32_t minUses = UsesBeforeIonRecompile(script, pc);
+    uint32_t minUses = UsesBeforeIonCompile(script, pc);
     masm.branch32(Assembler::LessThan, countReg, Imm32(minUses), &skipCall);
 
     masm.branchPtr(Assembler::Equal,
                    Address(scriptReg, JSScript::offsetOfIonScript()),
                    ImmPtr(ION_COMPILING_SCRIPT), &skipCall);
 
     // Call IC.
     ICUseCount_Fallback::Compiler stubCompiler(cx);
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -17,16 +17,17 @@
 #include "builtin/Eval.h"
 #include "builtin/TypedObject.h"
 #ifdef JSGC_GENERATIONAL
 # include "gc/Nursery.h"
 #endif
 #include "jit/ExecutionModeInlines.h"
 #include "jit/IonCaches.h"
 #include "jit/IonLinker.h"
+#include "jit/IonOptimizationLevels.h"
 #include "jit/IonSpewer.h"
 #include "jit/MIRGenerator.h"
 #include "jit/MoveEmitter.h"
 #include "jit/ParallelFunctions.h"
 #include "jit/ParallelSafetyAnalysis.h"
 #include "jit/RangeAnalysis.h"
 #include "vm/ForkJoin.h"
 
@@ -5852,17 +5853,19 @@ CodeGenerator::generate()
     return !masm.oom();
 }
 
 bool
 CodeGenerator::link(JSContext *cx, types::CompilerConstraintList *constraints)
 {
     RootedScript script(cx, gen->info().script());
     ExecutionMode executionMode = gen->info().executionMode();
-    JS_ASSERT(!HasIonScript(script, executionMode));
+    OptimizationLevel optimizationLevel = gen->optimizationInfo().level();
+
+    JS_ASSERT_IF(HasIonScript(script, executionMode), executionMode == SequentialExecution);
 
     // Check to make sure we didn't have a mid-build invalidation. If so, we
     // will trickle to jit::Compile() and return Method_Skipped.
     types::RecompileInfo recompileInfo;
     if (!types::FinishCompilation(cx, script, executionMode, constraints, &recompileInfo))
         return true;
 
     uint32_t scriptFrameSize = frameClass_ == FrameSizeClass::None()
@@ -5880,17 +5883,17 @@ CodeGenerator::link(JSContext *cx, types
 
     IonScript *ionScript =
       IonScript::New(cx, recompileInfo,
                      graph.totalSlotCount(), scriptFrameSize, snapshots_.size(),
                      bailouts_.length(), graph.numConstants(),
                      safepointIndices_.length(), osiIndices_.length(),
                      cacheList_.length(), runtimeData_.length(),
                      safepoints_.size(), callTargets.length(),
-                     patchableBackedges_.length());
+                     patchableBackedges_.length(), optimizationLevel);
     if (!ionScript) {
         recompileInfo.compilerOutput(cx->compartment()->types)->invalidate();
         return false;
     }
 
     // Lock the runtime against operation callbacks during the link.
     // We don't want an operation callback to protect the code for the script
     // before it has been filled in, as we could segv before the runtime's
--- a/js/src/jit/Ion.cpp
+++ b/js/src/jit/Ion.cpp
@@ -22,16 +22,17 @@
 #include "jit/BaselineInspector.h"
 #include "jit/BaselineJIT.h"
 #include "jit/CodeGenerator.h"
 #include "jit/EdgeCaseAnalysis.h"
 #include "jit/EffectiveAddressAnalysis.h"
 #include "jit/ExecutionModeInlines.h"
 #include "jit/IonAnalysis.h"
 #include "jit/IonBuilder.h"
+#include "jit/IonOptimizationLevels.h"
 #include "jit/IonSpewer.h"
 #include "jit/JitCompartment.h"
 #include "jit/LICM.h"
 #include "jit/LinearScan.h"
 #include "jit/LIR.h"
 #include "jit/Lowering.h"
 #include "jit/ParallelSafetyAnalysis.h"
 #include "jit/PerfSpewer.h"
@@ -44,19 +45,16 @@
 #include "jscompartmentinlines.h"
 #include "jsgcinlines.h"
 #include "jsinferinlines.h"
 #include "jsobjinlines.h"
 
 using namespace js;
 using namespace js::jit;
 
-// Global variables.
-IonOptions jit::js_IonOptions;
-
 // Assert that IonCode is gc::Cell aligned.
 JS_STATIC_ASSERT(sizeof(IonCode) % gc::CellSize == 0);
 
 #ifdef JS_THREADSAFE
 static bool IonTLSInitialized = false;
 static unsigned IonTLSIndex;
 
 static inline IonContext *
@@ -756,17 +754,18 @@ IonScript::IonScript()
 {
 }
 
 IonScript *
 IonScript::New(JSContext *cx, types::RecompileInfo recompileInfo,
                uint32_t frameSlots, uint32_t frameSize, size_t snapshotsSize,
                size_t bailoutEntries, size_t constants, size_t safepointIndices,
                size_t osiIndices, size_t cacheEntries, size_t runtimeSize,
-               size_t safepointsSize, size_t callTargetEntries, size_t backedgeEntries)
+               size_t safepointsSize, size_t callTargetEntries, size_t backedgeEntries,
+               OptimizationLevel optimizationLevel)
 {
     static const int DataAlignment = sizeof(void *);
 
     if (snapshotsSize >= MAX_BUFFER_SIZE ||
         (bailoutEntries >= MAX_BUFFER_SIZE / sizeof(uint32_t)))
     {
         js_ReportOutOfMemory(cx);
         return nullptr;
@@ -843,16 +842,17 @@ IonScript::New(JSContext *cx, types::Rec
     script->backedgeList_ = offsetCursor;
     script->backedgeEntries_ = backedgeEntries;
     offsetCursor += paddedBackedgeSize;
 
     script->frameSlots_ = frameSlots;
     script->frameSize_ = frameSize;
 
     script->recompileInfo_ = recompileInfo;
+    script->optimizationLevel_ = optimizationLevel;
 
     return script;
 }
 
 void
 IonScript::trace(JSTracer *trc)
 {
     if (method_)
@@ -1228,17 +1228,19 @@ OptimizeMIR(MIRGenerator *mir)
     if (graph.entryBlock()->info().executionMode() == ParallelExecution) {
         ParallelSafetyAnalysis analysis(mir, graph);
         if (!analysis.analyze())
             return false;
     }
 
     // Alias analysis is required for LICM and GVN so that we don't move
     // loads across stores.
-    if (js_IonOptions.licm || js_IonOptions.gvn) {
+    if (mir->optimizationInfo().licmEnabled() ||
+        mir->optimizationInfo().gvnEnabled())
+    {
         AliasAnalysis analysis(mir, graph);
         if (!analysis.analyze())
             return false;
         IonSpewPass("Alias analysis");
         AssertExtendedGraphCoherency(graph);
 
         if (mir->shouldCancel("Alias analysis"))
             return false;
@@ -1248,56 +1250,56 @@ OptimizeMIR(MIRGenerator *mir)
         // alias analysis.
         if (!EliminateDeadResumePointOperands(mir, graph))
             return false;
 
         if (mir->shouldCancel("Eliminate dead resume point operands"))
             return false;
     }
 
-    if (js_IonOptions.gvn) {
-        ValueNumberer gvn(mir, graph, js_IonOptions.gvnIsOptimistic);
+    if (mir->optimizationInfo().gvnEnabled()) {
+        ValueNumberer gvn(mir, graph, mir->optimizationInfo().gvnKind() == GVN_Optimistic);
         if (!gvn.analyze())
             return false;
         IonSpewPass("GVN");
         AssertExtendedGraphCoherency(graph);
 
         if (mir->shouldCancel("GVN"))
             return false;
     }
 
-    if (js_IonOptions.uce) {
+    if (mir->optimizationInfo().uceEnabled()) {
         UnreachableCodeElimination uce(mir, graph);
         if (!uce.analyze())
             return false;
         IonSpewPass("UCE");
         AssertExtendedGraphCoherency(graph);
     }
 
     if (mir->shouldCancel("UCE"))
         return false;
 
-    if (js_IonOptions.licm) {
+    if (mir->optimizationInfo().licmEnabled()) {
         // LICM can hoist instructions from conditional branches and trigger
         // repeated bailouts. Disable it if this script is known to bailout
         // frequently.
         JSScript *script = mir->info().script();
         if (!script || !script->hadFrequentBailouts()) {
             LICM licm(mir, graph);
             if (!licm.analyze())
                 return false;
             IonSpewPass("LICM");
             AssertExtendedGraphCoherency(graph);
 
             if (mir->shouldCancel("LICM"))
                 return false;
         }
     }
 
-    if (js_IonOptions.rangeAnalysis) {
+    if (mir->optimizationInfo().rangeAnalysisEnabled()) {
         RangeAnalysis r(mir, graph);
         if (!r.addBetaNodes())
             return false;
         IonSpewPass("Beta");
         AssertExtendedGraphCoherency(graph);
 
         if (mir->shouldCancel("RA Beta"))
             return false;
@@ -1313,17 +1315,17 @@ OptimizeMIR(MIRGenerator *mir)
         if (!r.removeBetaNodes())
             return false;
         IonSpewPass("De-Beta");
         AssertExtendedGraphCoherency(graph);
 
         if (mir->shouldCancel("RA De-Beta"))
             return false;
 
-        if (js_IonOptions.uce) {
+        if (mir->optimizationInfo().uceEnabled()) {
             bool shouldRunUCE = false;
             if (!r.prepareForUCE(&shouldRunUCE))
                 return false;
             IonSpewPass("RA check UCE");
             AssertExtendedGraphCoherency(graph);
 
             if (mir->shouldCancel("RA check UCE"))
                 return false;
@@ -1345,17 +1347,17 @@ OptimizeMIR(MIRGenerator *mir)
             return false;
         IonSpewPass("Truncate Doubles");
         AssertExtendedGraphCoherency(graph);
 
         if (mir->shouldCancel("Truncate Doubles"))
             return false;
     }
 
-    if (js_IonOptions.eaa) {
+    if (mir->optimizationInfo().eaaEnabled()) {
         EffectiveAddressAnalysis eaa(graph);
         if (!eaa.analyze())
             return false;
         IonSpewPass("Effective Address Analysis");
         AssertExtendedGraphCoherency(graph);
 
         if (mir->shouldCancel("Effective Address Analysis"))
             return false;
@@ -1367,28 +1369,28 @@ OptimizeMIR(MIRGenerator *mir)
     AssertExtendedGraphCoherency(graph);
 
     if (mir->shouldCancel("DCE"))
         return false;
 
     // Passes after this point must not move instructions; these analyses
     // depend on knowing the final order in which instructions will execute.
 
-    if (js_IonOptions.edgeCaseAnalysis && !mir->compilingAsmJS()) {
+    if (mir->optimizationInfo().edgeCaseAnalysisEnabled()) {
         EdgeCaseAnalysis edgeCaseAnalysis(mir, graph);
         if (!edgeCaseAnalysis.analyzeLate())
             return false;
         IonSpewPass("Edge Case Analysis (Late)");
         AssertGraphCoherency(graph);
 
         if (mir->shouldCancel("Edge Case Analysis (Late)"))
             return false;
     }
 
-    if (!mir->compilingAsmJS()) {
+    if (mir->optimizationInfo().eliminateRedundantChecksEnabled()) {
         // Note: check elimination has to run after all other passes that move
         // instructions. Since check uses are replaced with the actual index,
         // code motion after this pass could incorrectly move a load or store
         // before its bounds check.
         if (!EliminateRedundantChecks(graph))
             return false;
         IonSpewPass("Bounds Check Elimination");
         AssertGraphCoherency(graph);
@@ -1411,17 +1413,17 @@ GenerateLIR(MIRGenerator *mir)
         return nullptr;
     IonSpewPass("Generate LIR");
 
     if (mir->shouldCancel("Generate LIR"))
         return nullptr;
 
     AllocationIntegrityState integrity(*lir);
 
-    switch (js_IonOptions.registerAllocator) {
+    switch (mir->optimizationInfo().registerAllocator()) {
       case RegisterAllocator_LSRA: {
 #ifdef DEBUG
         if (!integrity.record())
             return nullptr;
 #endif
 
         LinearScanAllocator regalloc(mir, &lirgen, *lir);
         if (!regalloc.go())
@@ -1623,24 +1625,27 @@ TrackPropertiesForSingletonScopes(JSCont
         if (scope->is<CallObject>() && scope->hasSingletonType())
             TrackAllProperties(cx, scope);
     }
 }
 
 static AbortReason
 IonCompile(JSContext *cx, JSScript *script,
            BaselineFrame *baselineFrame, jsbytecode *osrPc, bool constructing,
-           ExecutionMode executionMode, bool recompile)
+           ExecutionMode executionMode, bool recompile,
+           OptimizationLevel optimizationLevel)
 {
 #if JS_TRACE_LOGGING
     AutoTraceLog logger(TraceLogging::defaultLogger(),
                         TraceLogging::ION_COMPILE_START,
                         TraceLogging::ION_COMPILE_STOP,
                         script);
 #endif
+    JS_ASSERT(optimizationLevel > Optimization_DontCompile);
+
     TrackPropertiesForSingletonScopes(cx, script, baselineFrame);
 
     LifoAlloc *alloc = cx->new_<LifoAlloc>(BUILDER_LIFO_ALLOC_PRIMARY_CHUNK_SIZE);
     if (!alloc)
         return AbortReason_Alloc;
 
     ScopedJSDeletePtr<LifoAlloc> autoDelete(alloc);
 
@@ -1678,20 +1683,23 @@ IonCompile(JSContext *cx, JSScript *scri
 
     AutoFlushCache afc("IonCompile", cx->runtime()->jitRuntime());
 
     AutoTempAllocatorRooter root(cx, temp);
     types::CompilerConstraintList *constraints = types::NewCompilerConstraintList(*temp);
     if (!constraints)
         return AbortReason_Alloc;
 
+    const OptimizationInfo *optimizationInfo = js_IonOptimizations.get(optimizationLevel);
+
     IonBuilder *builder = alloc->new_<IonBuilder>((JSContext *) nullptr,
                                                   CompileCompartment::get(cx->compartment()),
                                                   temp, graph, constraints,
-                                                  &inspector, info, baselineFrameInspector);
+                                                  &inspector, info, optimizationInfo,
+                                                  baselineFrameInspector);
     if (!builder)
         return AbortReason_Alloc;
 
     JS_ASSERT(recompile == HasIonScript(builder->script(), executionMode));
     JS_ASSERT(CanIonCompile(builder->script(), executionMode));
 
     RootedScript builderScript(cx, builder->script());
     IonSpewNewFunction(graph, builderScript);
@@ -1862,23 +1870,36 @@ bool
 CanIonCompileScript(JSContext *cx, HandleScript script, bool osr)
 {
     if (!script->canIonCompile() || !CheckScript(cx, script, osr))
         return false;
 
     return CheckScriptSize(cx, script) == Method_Compiled;
 }
 
+static OptimizationLevel
+GetOptimizationLevel(HandleScript script, ExecutionMode executionMode)
+{
+    if (executionMode == ParallelExecution)
+        return Optimization_Normal;
+
+    JS_ASSERT(executionMode == SequentialExecution);
+
+    return js_IonOptimizations.levelForUseCount(script->getUseCount());
+}
+
 static MethodStatus
 Compile(JSContext *cx, HandleScript script, BaselineFrame *osrFrame, jsbytecode *osrPc,
         bool constructing, ExecutionMode executionMode)
 {
     JS_ASSERT(jit::IsIonEnabled(cx));
     JS_ASSERT(jit::IsBaselineEnabled(cx));
     JS_ASSERT_IF(osrPc != nullptr, (JSOp)*osrPc == JSOP_LOOPENTRY);
+    JS_ASSERT_IF(executionMode == ParallelExecution, !osrFrame && !osrPC);
+    JS_ASSERT_IF(executionMode == ParallelExecution, !HasIonScript(script, executionMode));
 
     if (!script->hasBaselineScript())
         return Method_Skipped;
 
     if (cx->compartment()->debugMode()) {
         IonSpew(IonSpew_Abort, "debugging");
         return Method_CantCompile;
     }
@@ -1890,33 +1911,54 @@ Compile(JSContext *cx, HandleScript scri
 
     MethodStatus status = CheckScriptSize(cx, script);
     if (status != Method_Compiled) {
         IonSpew(IonSpew_Abort, "Aborted compilation of %s:%d", script->filename(), script->lineno());
         return status;
     }
 
     bool recompile = false;
+    OptimizationLevel optimizationLevel = GetOptimizationLevel(script, executionMode);
+    if (optimizationLevel == Optimization_DontCompile)
+        return Method_Skipped;
 
     IonScript *scriptIon = GetIonScript(script, executionMode);
     if (scriptIon) {
         if (!scriptIon->method())
             return Method_CantCompile;
-        return Method_Compiled;
-    }
-
-    if (executionMode == SequentialExecution) {
-        // Use getUseCount instead of incUseCount to avoid bumping the
-        // use count twice.
-        if (script->getUseCount() < UsesBeforeIonRecompile(script, osrPc ? osrPc : script->code()))
-            return Method_Skipped;
+
+        MethodStatus failedState = Method_Compiled;
+
+        // If we keep failing to enter the script due to an OSR pc mismatch,
+        // recompile with the right pc.
+        if (osrPc && script->ionScript()->osrPc() != osrPc) {
+            uint32_t count = script->ionScript()->incrOsrPcMismatchCounter();
+            if (count <= js_IonOptions.osrPcMismatchesBeforeRecompile)
+                return Method_Skipped;
+
+            failedState = Method_Skipped;
+        }
+
+        // Don't recompile/overwrite higher optimized code,
+        // with a lower optimization level.
+        if (optimizationLevel <= scriptIon->optimizationLevel())
+            return failedState;
+
+        // Don't start compiling if already compiling
+        if (scriptIon->isRecompiling())
+            return failedState;
+
+        if (osrPc)
+            script->ionScript()->resetOsrPcMismatchCounter();
+
+        recompile = true;
     }
 
     AbortReason reason = IonCompile(cx, script, osrFrame, osrPc, constructing, executionMode,
-                                    recompile);
+                                    recompile, optimizationLevel);
     if (reason == AbortReason_Error)
         return Method_Error;
 
     if (reason == AbortReason_Disable)
         return Method_CantCompile;
 
     if (reason == AbortReason_Alloc) {
         js_ReportOutOfMemory(cx);
@@ -1956,39 +1998,29 @@ jit::CanEnterAtBranch(JSContext *cx, JSS
         return Method_Skipped;
 
     // Mark as forbidden if frame can't be handled.
     if (!CheckFrame(osrFrame)) {
         ForbidCompilation(cx, script);
         return Method_CantCompile;
     }
 
-    // Attempt compilation. Returns Method_Compiled if already compiled.
+    // Attempt compilation.
+    // - Returns Method_Compiled if the right ionscript is present
+    //   (Meaning it was present or a sequantial compile finished)
+    // - Returns Method_Skipped if pc doesn't match
+    //   (This means a background thread compilation with that pc could have started or not.)
     RootedScript rscript(cx, script);
     MethodStatus status = Compile(cx, rscript, osrFrame, pc, isConstructing, SequentialExecution);
     if (status != Method_Compiled) {
         if (status == Method_CantCompile)
             ForbidCompilation(cx, script);
         return status;
     }
 
-    if (script->ionScript()->osrPc() != pc) {
-        // If we keep failing to enter the script due to an OSR pc mismatch,
-        // invalidate the script to force a recompile.
-        uint32_t count = script->ionScript()->incrOsrPcMismatchCounter();
-
-        if (count > js_IonOptions.osrPcMismatchesBeforeRecompile) {
-            if (!Invalidate(cx, script, SequentialExecution, true))
-                return Method_Error;
-        }
-        return Method_Skipped;
-    }
-
-    script->ionScript()->resetOsrPcMismatchCounter();
-
     return Method_Compiled;
 }
 
 MethodStatus
 jit::CanEnter(JSContext *cx, RunState &state)
 {
     JS_ASSERT(jit::IsIonEnabled(cx));
 
@@ -2694,21 +2726,24 @@ jit::ForbidCompilation(JSContext *cx, JS
       default:
         MOZ_ASSUME_UNREACHABLE("No such execution mode");
     }
 
     MOZ_ASSUME_UNREACHABLE("No such execution mode");
 }
 
 uint32_t
-jit::UsesBeforeIonRecompile(JSScript *script, jsbytecode *pc)
+jit::UsesBeforeIonCompile(JSScript *script, jsbytecode *pc)
 {
     JS_ASSERT(pc == script->code() || JSOp(*pc) == JSOP_LOOPENTRY);
 
-    uint32_t minUses = js_IonOptions.usesBeforeCompile;
+    OptimizationLevel level = js_IonOptimizations.nextLevel(Optimization_DontCompile);
+    const OptimizationInfo *info = js_IonOptimizations.get(level);
+
+    uint32_t minUses = info->usesBeforeCompile();
 
     // If the script is too large to compile on the main thread, we can still
     // compile it off thread. In these cases, increase the use count threshold
     // to improve the compilation's type information and hopefully avoid later
     // recompilation.
 
     if (script->length() > MAX_MAIN_THREAD_SCRIPT_SIZE)
         minUses = minUses * (script->length() / (double) MAX_MAIN_THREAD_SCRIPT_SIZE);
--- a/js/src/jit/Ion.h
+++ b/js/src/jit/Ion.h
@@ -11,258 +11,23 @@
 
 #include "mozilla/MemoryReporting.h"
 
 #include "jscntxt.h"
 #include "jscompartment.h"
 
 #include "jit/CompileInfo.h"
 #include "jit/CompileWrappers.h"
+#include "jit/IonOptions.h"
 
 namespace js {
 namespace jit {
 
 class TempAllocator;
 
-// Possible register allocators which may be used.
-enum IonRegisterAllocator {
-    RegisterAllocator_LSRA,
-    RegisterAllocator_Backtracking,
-    RegisterAllocator_Stupid
-};
-
-struct IonOptions
-{
-    // Toggles whether global value numbering is used.
-    //
-    // Default: true
-    bool gvn;
-
-    // Toggles whether global value numbering is optimistic (true) or
-    // pessimistic (false).
-    //
-    // Default: true
-    bool gvnIsOptimistic;
-
-    // Toggles whether loop invariant code motion is performed.
-    //
-    // Default: true
-    bool licm;
-
-    // Toggles whether functions may be entered at loop headers.
-    //
-    // Default: true
-    bool osr;
-
-    // Toggles whether large scripts are rejected.
-    //
-    // Default: true
-    bool limitScriptSize;
-
-    // Describes which register allocator to use.
-    //
-    // Default: LSRA
-    IonRegisterAllocator registerAllocator;
-
-    // Toggles whether inlining is performed.
-    //
-    // Default: true
-    bool inlining;
-
-    // Toggles whether Edge Case Analysis is used.
-    //
-    // Default: true
-    bool edgeCaseAnalysis;
-
-    // Toggles whether Range Analysis is used.
-    //
-    // Default: true
-    bool rangeAnalysis;
-
-    // Whether to enable extra code to perform dynamic validation of
-    // RangeAnalysis results.
-    //
-    // Default: false
-    bool checkRangeAnalysis;
-
-    // Whether to protect the GC heap during Ion compilation and ensure that
-    // only threadsafe operations are performed on it.
-    //
-    // Default: false
-    bool checkThreadSafety;
-
-    // Whether to perform expensive graph-consistency DEBUG-only assertions.
-    // It can be useful to disable this to reduce DEBUG-compile time of large
-    // asm.js programs.
-    //
-    // Default: true
-    bool checkGraphConsistency;
-
-    // Toggles whether Unreachable Code Elimination is performed.
-    //
-    // Default: true
-    bool uce;
-
-    // Toggles whether Effective Address Analysis is performed.
-    //
-    // Default: true
-    bool eaa;
-
-#ifdef CHECK_OSIPOINT_REGISTERS
-    // Emit extra code to verify live regs at the start of a VM call
-    // are not modified before its OsiPoint.
-    //
-    // Default: false
-    bool checkOsiPointRegisters;
-#endif
-
-    // How many invocations or loop iterations are needed before functions
-    // are compiled with the baseline compiler.
-    //
-    // Default: 10
-    uint32_t baselineUsesBeforeCompile;
-
-    // How many invocations or loop iterations are needed before functions
-    // are compiled.
-    //
-    // Default: 1,000
-    uint32_t usesBeforeCompile;
-
-    // How many invocations or loop iterations are needed before calls
-    // are inlined, as a fraction of usesBeforeCompile.
-    //
-    // Default: .125
-    double usesBeforeInliningFactor;
-
-    // How many times we will try to enter a script via OSR before
-    // invalidating the script.
-    //
-    // Default: 6,000
-    uint32_t osrPcMismatchesBeforeRecompile;
-
-    // Number of bailouts without invalidation before we set
-    // JSScript::hadFrequentBailouts and invalidate.
-    //
-    // Default: 10
-    uint32_t frequentBailoutThreshold;
-
-    // Number of exception bailouts (resuming into catch/finally block) before
-    // we invalidate and forbid Ion compilation.
-    //
-    // Default: 10
-    uint32_t exceptionBailoutThreshold;
-
-    // Whether Ion should compile try-catch statements.
-    //
-    // Default: true
-    bool compileTryCatch;
-
-    // How many actual arguments are accepted on the C stack.
-    //
-    // Default: 4,096
-    uint32_t maxStackArgs;
-
-    // The maximum inlining depth.
-    //
-    // Default: 3
-    uint32_t maxInlineDepth;
-
-    // The maximum inlining depth for functions.
-    //
-    // Inlining small functions has almost no compiling overhead
-    // and removes the otherwise needed call overhead.
-    // The value is currently very low.
-    // Actually it is only needed to make sure we don't blow out the stack.
-    //
-    // Default: 10
-    uint32_t smallFunctionMaxInlineDepth;
-
-    // The bytecode length limit for small function.
-    //
-    // The default for this was arrived at empirically via benchmarking.
-    // We may want to tune it further after other optimizations have gone
-    // in.
-    //
-    // Default: 100
-    uint32_t smallFunctionMaxBytecodeLength;
-
-    // The maximum number of functions to polymorphically inline at a call site.
-    //
-    // Default: 4
-    uint32_t polyInlineMax;
-
-    // The maximum total bytecode size of an inline call site.
-    //
-    // Default: 1000
-    uint32_t inlineMaxTotalBytecodeLength;
-
-    // Whether functions are compiled immediately.
-    //
-    // Default: false
-    bool eagerCompilation;
-
-    // How many uses of a parallel kernel before we attempt compilation.
-    //
-    // Default: 1
-    uint32_t usesBeforeCompilePar;
-
-    // The maximum bytecode length the caller may have,
-    // before we stop inlining large functions in that caller.
-    //
-    // Default: 10000
-    uint32_t inliningMaxCallerBytecodeLength;
-
-    void setEagerCompilation() {
-        eagerCompilation = true;
-        usesBeforeCompile = 0;
-        baselineUsesBeforeCompile = 0;
-    }
-
-    MOZ_CONSTEXPR IonOptions()
-      : gvn(true),
-        gvnIsOptimistic(true),
-        licm(true),
-        osr(true),
-        limitScriptSize(true),
-        registerAllocator(RegisterAllocator_LSRA),
-        inlining(true),
-        edgeCaseAnalysis(true),
-        rangeAnalysis(true),
-        checkRangeAnalysis(false),
-        checkThreadSafety(false),
-        checkGraphConsistency(true),
-        uce(true),
-        eaa(true),
-#ifdef CHECK_OSIPOINT_REGISTERS
-        checkOsiPointRegisters(false),
-#endif
-        baselineUsesBeforeCompile(10),
-        usesBeforeCompile(1000),
-        usesBeforeInliningFactor(.125),
-        osrPcMismatchesBeforeRecompile(6000),
-        frequentBailoutThreshold(10),
-        exceptionBailoutThreshold(10),
-        compileTryCatch(true),
-        maxStackArgs(4096),
-        maxInlineDepth(3),
-        smallFunctionMaxInlineDepth(10),
-        smallFunctionMaxBytecodeLength(100),
-        polyInlineMax(4),
-        inlineMaxTotalBytecodeLength(1000),
-        eagerCompilation(false),
-        usesBeforeCompilePar(1),
-        inliningMaxCallerBytecodeLength(10000)
-    {
-    }
-
-    uint32_t usesBeforeInlining() {
-        return usesBeforeCompile * usesBeforeInliningFactor;
-    }
-};
-
 enum MethodStatus
 {
     Method_Error,
     Method_CantCompile,
     Method_Skipped,
     Method_Compiled
 };
 
@@ -303,18 +68,16 @@ class IonContext
     int getNextAssemblerId() {
         return assemblerCount_++;
     }
   private:
     IonContext *prev_;
     int assemblerCount_;
 };
 
-extern IonOptions js_IonOptions;
-
 // Initialize Ion statically for all JSRuntimes.
 bool InitializeIon();
 
 // Get and set the current Ion context.
 IonContext *GetIonContext();
 IonContext *MaybeGetIonContext();
 
 bool SetIonContext(IonContext *ctx);
@@ -411,17 +174,17 @@ IsIonInlinablePC(jsbytecode *pc) {
 inline bool
 TooManyArguments(unsigned nargs)
 {
     return (nargs >= SNAPSHOT_MAX_NARGS || nargs > js_IonOptions.maxStackArgs);
 }
 
 void ForbidCompilation(JSContext *cx, JSScript *script);
 void ForbidCompilation(JSContext *cx, JSScript *script, ExecutionMode mode);
-uint32_t UsesBeforeIonRecompile(JSScript *script, jsbytecode *pc);
+uint32_t UsesBeforeIonCompile(JSScript *script, jsbytecode *pc);
 
 void PurgeCaches(JSScript *script, JS::Zone *zone);
 size_t SizeOfIonData(JSScript *script, mozilla::MallocSizeOf mallocSizeOf);
 void DestroyIonScripts(FreeOp *fop, JSScript *script);
 void TraceIonScripts(JSTracer* trc, JSScript *script);
 
 void TriggerOperationCallbackForIonCode(JSRuntime *rt, JSRuntime::OperationCallbackTrigger trigger);
 
--- a/js/src/jit/IonAnalysis.cpp
+++ b/js/src/jit/IonAnalysis.cpp
@@ -7,16 +7,17 @@
 #include "jit/IonAnalysis.h"
 
 #include "jsanalyze.h"
 
 #include "jit/BaselineInspector.h"
 #include "jit/BaselineJIT.h"
 #include "jit/Ion.h"
 #include "jit/IonBuilder.h"
+#include "jit/IonOptimizationLevels.h"
 #include "jit/LIR.h"
 #include "jit/Lowering.h"
 #include "jit/MIRGraph.h"
 
 #include "jsinferinlines.h"
 #include "jsobjinlines.h"
 
 using namespace js;
@@ -2153,20 +2154,22 @@ jit::AnalyzeNewScriptProperties(JSContex
 
     MIRGraph graph(&temp);
     CompileInfo info(script, fun,
                      /* osrPc = */ nullptr, /* constructing = */ false,
                      DefinitePropertiesAnalysis);
 
     AutoTempAllocatorRooter root(cx, &temp);
 
+    const OptimizationInfo *optimizationInfo = js_IonOptimizations.get(Optimization_Normal);
+
     types::CompilerConstraintList *constraints = types::NewCompilerConstraintList(temp);
     BaselineInspector inspector(script);
     IonBuilder builder(cx, CompileCompartment::get(cx->compartment()), &temp, &graph, constraints,
-                       &inspector, &info, /* baselineFrame = */ nullptr);
+                       &inspector, &info, optimizationInfo, /* baselineFrame = */ nullptr);
 
     if (!builder.build()) {
         if (builder.abortReason() == AbortReason_Alloc)
             return false;
         return true;
     }
 
     if (!SplitCriticalEdges(graph))
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -13,16 +13,17 @@
 #include "builtin/Eval.h"
 #include "builtin/TypedObject.h"
 #include "builtin/TypeRepresentation.h"
 #include "frontend/SourceNotes.h"
 #include "jit/BaselineFrame.h"
 #include "jit/BaselineInspector.h"
 #include "jit/ExecutionModeInlines.h"
 #include "jit/Ion.h"
+#include "jit/IonOptimizationLevels.h"
 #include "jit/IonSpewer.h"
 #include "jit/Lowering.h"
 #include "jit/MIRGraph.h"
 
 #include "vm/ArgumentsObject.h"
 #include "vm/RegExpStatics.h"
 
 #include "jsinferinlines.h"
@@ -96,21 +97,23 @@ jit::NewBaselineFrameInspector(TempAlloc
             inspector->varTypes.infallibleAppend(types::Type::UndefinedType());
         else
             inspector->varTypes.infallibleAppend(types::GetValueType(frame->unaliasedVar(i)));
     }
 
     return inspector;
 }
 
-IonBuilder::IonBuilder(JSContext *analysisContext, CompileCompartment *comp, TempAllocator *temp, MIRGraph *graph,
-                       types::CompilerConstraintList *constraints,
-                       BaselineInspector *inspector, CompileInfo *info, BaselineFrameInspector *baselineFrame,
-                       size_t inliningDepth, uint32_t loopDepth)
-  : MIRGenerator(comp, temp, graph, info),
+IonBuilder::IonBuilder(JSContext *analysisContext, CompileCompartment *comp, TempAllocator *temp,
+                       MIRGraph *graph, types::CompilerConstraintList *constraints,
+                       BaselineInspector *inspector, CompileInfo *info,
+                       const OptimizationInfo *optimizationInfo,
+                       BaselineFrameInspector *baselineFrame, size_t inliningDepth,
+                       uint32_t loopDepth)
+  : MIRGenerator(comp, temp, graph, info, optimizationInfo),
     backgroundCodegen_(nullptr),
     analysisContext(analysisContext),
     baselineFrame_(baselineFrame),
     abortReason_(AbortReason_Disable),
     reprSetHash_(nullptr),
     constraints_(constraints),
     analysis_(*temp, info->script()),
     thisTypes(nullptr),
@@ -322,16 +325,19 @@ IonBuilder::DontInline(JSScript *targetS
     }
 
     return InliningDecision_DontInline;
 }
 
 IonBuilder::InliningDecision
 IonBuilder::canInlineTarget(JSFunction *target, CallInfo &callInfo)
 {
+    if (!optimizationInfo().inlineInterpreted())
+        return InliningDecision_DontInline;
+
     if (!target->isInterpreted())
         return DontInline(nullptr, "Non-interpreted target");
 
     // Allow constructing lazy scripts when performing the definite properties
     // analysis, as baseline has not been used to warm the caller up yet.
     if (target->isInterpreted() && info().executionMode() == DefinitePropertiesAnalysis) {
         if (!target->getOrCreateScript(analysisContext))
             return InliningDecision_Error;
@@ -3846,19 +3852,19 @@ IonBuilder::inlineScriptedCall(CallInfo 
                                                      this->info().executionMode());
     if (!info)
         return false;
 
     MIRGraphReturns returns(alloc());
     AutoAccumulateReturns aar(graph(), returns);
 
     // Build the graph.
-    IonBuilder inlineBuilder(analysisContext, compartment,
-                             &alloc(), &graph(), constraints(), &inspector, info, nullptr,
-                             inliningDepth_ + 1, loopDepth_);
+    IonBuilder inlineBuilder(analysisContext, compartment, &alloc(), &graph(), constraints(),
+                             &inspector, info, &optimizationInfo(), nullptr, inliningDepth_ + 1,
+                             loopDepth_);
     if (!inlineBuilder.buildInline(this, outerResumePoint, callInfo)) {
         if (analysisContext && analysisContext->isExceptionPending()) {
             IonSpew(IonSpew_Abort, "Inline builder raised exception.");
             abortReason_ = AbortReason_Error;
             return false;
         }
 
         // Inlining the callee failed. Mark the callee as uninlineable only if
@@ -3960,29 +3966,19 @@ IonBuilder::patchInlinedReturns(CallInfo
             return nullptr;
         phi->addInput(rdef);
     }
 
     bottom->addPhi(phi);
     return phi;
 }
 
-static bool
-IsSmallFunction(JSScript *script)
-{
-    return script->length() <= js_IonOptions.smallFunctionMaxBytecodeLength;
-}
-
 IonBuilder::InliningDecision
 IonBuilder::makeInliningDecision(JSFunction *target, CallInfo &callInfo)
 {
-    // Only inline when inlining is enabled.
-    if (!inliningEnabled())
-        return InliningDecision_DontInline;
-
     // When there is no target, inlining is impossible.
     if (target == nullptr)
         return InliningDecision_DontInline;
 
     // Native functions provide their own detection in inlineNativeCall().
     if (target->isNative())
         return InliningDecision_Inline;
 
@@ -3992,40 +3988,40 @@ IonBuilder::makeInliningDecision(JSFunct
         return decision;
 
     // Heuristics!
     JSScript *targetScript = target->nonLazyScript();
 
     // Skip heuristics if we have an explicit hint to inline.
     if (!targetScript->shouldInline()) {
         // Cap the inlining depth.
-        if (IsSmallFunction(targetScript)) {
-            if (inliningDepth_ >= js_IonOptions.smallFunctionMaxInlineDepth)
+        if (js_IonOptions.isSmallFunction(targetScript)) {
+            if (inliningDepth_ >= optimizationInfo().smallFunctionMaxInlineDepth())
                 return DontInline(targetScript, "Vetoed: exceeding allowed inline depth");
         } else {
-            if (inliningDepth_ >= js_IonOptions.maxInlineDepth)
+            if (inliningDepth_ >= optimizationInfo().maxInlineDepth())
                 return DontInline(targetScript, "Vetoed: exceeding allowed inline depth");
 
             if (targetScript->hasLoops())
                 return DontInline(targetScript, "Vetoed: big function that contains a loop");
 
             // Caller must not be excessively large.
-            if (script()->length() >= js_IonOptions.inliningMaxCallerBytecodeLength)
+            if (script()->length() >= optimizationInfo().inliningMaxCallerBytecodeLength())
                 return DontInline(targetScript, "Vetoed: caller excessively large");
         }
 
         // Callee must not be excessively large.
         // This heuristic also applies to the callsite as a whole.
-        if (targetScript->length() > js_IonOptions.inlineMaxTotalBytecodeLength)
+        if (targetScript->length() > optimizationInfo().inlineMaxTotalBytecodeLength())
             return DontInline(targetScript, "Vetoed: callee excessively large");
 
         // Callee must have been called a few times to have somewhat stable
         // type information, except for definite properties analysis,
         // as the caller has not run yet.
-        if (targetScript->getUseCount() < js_IonOptions.usesBeforeInlining() &&
+        if (targetScript->getUseCount() < optimizationInfo().usesBeforeInlining() &&
             info().executionMode() != DefinitePropertiesAnalysis)
         {
             return DontInline(targetScript, "Vetoed: callee is insufficiently hot.");
         }
     }
 
     // TI calls ObjectStateChange to trigger invalidation of the caller.
     types::TypeObjectKey *targetType = types::TypeObjectKey::get(target);
@@ -4058,17 +4054,17 @@ IonBuilder::selectInliningTargets(Object
           case InliningDecision_Inline:
             inlineable = true;
             break;
         }
 
         // Enforce a maximum inlined bytecode limit at the callsite.
         if (inlineable && target->isInterpreted()) {
             totalSize += target->nonLazyScript()->length();
-            if (totalSize > js_IonOptions.inlineMaxTotalBytecodeLength)
+            if (totalSize > optimizationInfo().inlineMaxTotalBytecodeLength())
                 inlineable = false;
         }
 
         choiceSet.append(inlineable);
         if (inlineable)
             *numInlineable += 1;
     }
 
@@ -4152,19 +4148,16 @@ IonBuilder::inlineSingleCall(CallInfo &c
         return InliningStatus_Error;
     return InliningStatus_Inlined;
 }
 
 IonBuilder::InliningStatus
 IonBuilder::inlineCallsite(ObjectVector &targets, ObjectVector &originals,
                            bool lambda, CallInfo &callInfo)
 {
-    if (!inliningEnabled())
-        return InliningStatus_NotInlined;
-
     if (targets.length() == 0)
         return InliningStatus_NotInlined;
 
     // Is the function provided by an MGetPropertyCache?
     // If so, the cache may be movable to a fallback path, with a dispatch
     // instruction guarding on the incoming TypeObject.
     MGetPropertyCache *propCache = getInlineableGetPropertyCache(callInfo);
 
@@ -5994,20 +5987,38 @@ ClassHasResolveHook(CompileCompartment *
         return FunctionHasResolveHook(comp->runtime()->names(), name);
 
     return true;
 }
 
 void
 IonBuilder::insertRecompileCheck()
 {
+    // PJS doesn't recompile and doesn't need recompile checks.
     if (info().executionMode() != SequentialExecution)
         return;
 
-    return;
+    // No need for recompile checks if this is the highest optimization level.
+    OptimizationLevel curLevel = optimizationInfo().level();
+    if (js_IonOptimizations.isLastLevel(curLevel))
+        return;
+
+    // Add recompile check.
+
+    // Get the topmost builder. The topmost script will get recompiled when
+    // usecount is high enough to justify a higher optimization level.
+    IonBuilder *topBuilder = this;
+    while (topBuilder->callerBuilder_)
+        topBuilder = topBuilder->callerBuilder_;
+
+    // Add recompile check to recompile when the usecount reaches the usecount
+    // of the next optimization level.
+    OptimizationLevel nextLevel = js_IonOptimizations.nextLevel(curLevel);
+    const OptimizationInfo *info = js_IonOptimizations.get(nextLevel);
+    current->add(MRecompileCheck::New(alloc(), topBuilder->script(), info->usesBeforeCompile()));
 }
 
 JSObject *
 IonBuilder::testSingletonProperty(JSObject *obj, PropertyName *name)
 {
     // We would like to completely no-op property/global accesses which can
     // produce only a particular JSObject. When indicating the access result is
     // definitely an object, type inference does not account for the
--- a/js/src/jit/IonBuilder.h
+++ b/js/src/jit/IonBuilder.h
@@ -8,16 +8,17 @@
 #define jit_IonBuilder_h
 
 #ifdef JS_ION
 
 // This file declares the data structures for building a MIRGraph from a
 // JSScript.
 
 #include "jit/BytecodeAnalysis.h"
+#include "jit/IonOptimizationLevels.h"
 #include "jit/MIR.h"
 #include "jit/MIRGenerator.h"
 #include "jit/MIRGraph.h"
 #include "jit/TypeRepresentationSet.h"
 
 namespace js {
 namespace jit {
 
@@ -206,39 +207,36 @@ class IonBuilder : public MIRGenerator
         static CFGState CondSwitch(IonBuilder *builder, jsbytecode *exitpc, jsbytecode *defaultTarget);
         static CFGState Label(jsbytecode *exitpc);
         static CFGState Try(jsbytecode *exitpc, MBasicBlock *successor);
     };
 
     static int CmpSuccessors(const void *a, const void *b);
 
   public:
-    IonBuilder(JSContext *analysisContext, CompileCompartment *comp, TempAllocator *temp, MIRGraph *graph,
-               types::CompilerConstraintList *constraints,
-               BaselineInspector *inspector, CompileInfo *info, BaselineFrameInspector *baselineFrame,
+    IonBuilder(JSContext *analysisContext, CompileCompartment *comp, TempAllocator *temp,
+               MIRGraph *graph, types::CompilerConstraintList *constraints,
+               BaselineInspector *inspector, CompileInfo *info,
+               const OptimizationInfo *optimizationInfo, BaselineFrameInspector *baselineFrame,
                size_t inliningDepth = 0, uint32_t loopDepth = 0);
 
     bool build();
     bool buildInline(IonBuilder *callerBuilder, MResumePoint *callerResumePoint,
                      CallInfo &callInfo);
 
   private:
     bool traverseBytecode();
     ControlStatus snoopControlFlow(JSOp op);
     bool processIterators();
     bool inspectOpcode(JSOp op);
     uint32_t readIndex(jsbytecode *pc);
     JSAtom *readAtom(jsbytecode *pc);
     bool abort(const char *message, ...);
     void spew(const char *message);
 
-    static bool inliningEnabled() {
-        return js_IonOptions.inlining;
-    }
-
     JSFunction *getSingleCallTarget(types::TemporaryTypeSet *calleeTypes);
     bool getPolyCallTargets(types::TemporaryTypeSet *calleeTypes, bool constructing,
                             ObjectVector &targets, uint32_t maxTargets, bool *gotLambda);
 
     void popCfgStack();
     DeferredEdge *filterDeadDeferredEdges(DeferredEdge *edge);
     bool processDeferredContinues(CFGState &state);
     ControlStatus processControlEnd();
--- a/js/src/jit/IonCode.h
+++ b/js/src/jit/IonCode.h
@@ -9,16 +9,17 @@
 
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/PodOperations.h"
 
 #include "jsinfer.h"
 #include "jstypes.h"
 
 #include "gc/Heap.h"
+#include "jit/IonOptimizationLevels.h"
 #include "jit/IonTypes.h"
 
 namespace JSC {
     class ExecutablePool;
 }
 
 namespace js {
 
@@ -256,16 +257,19 @@ struct IonScript
     uint32_t backedgeEntries_;
 
     // Number of references from invalidation records.
     size_t refcount_;
 
     // Identifier of the compilation which produced this code.
     types::RecompileInfo recompileInfo_;
 
+    // The optimization level this script was compiled in.
+    OptimizationLevel optimizationLevel_;
+
     // Number of times we tried to enter this script via OSR but failed due to
     // a LOOPENTRY pc other than osrPc_.
     uint32_t osrPcMismatchCounter_;
 
     // If non-null, the list of AsmJSModules
     // that contain an optimized call directly into this IonScript.
     Vector<DependentAsmJSModuleExit> *dependentAsmJSModules;
 
@@ -330,17 +334,18 @@ struct IonScript
     // Do not call directly, use IonScript::New. This is public for cx->new_.
     IonScript();
 
     static IonScript *New(JSContext *cx, types::RecompileInfo recompileInfo,
                           uint32_t frameLocals, uint32_t frameSize,
                           size_t snapshotsSize, size_t snapshotEntries,
                           size_t constants, size_t safepointIndexEntries, size_t osiIndexEntries,
                           size_t cacheEntries, size_t runtimeSize, size_t safepointsSize,
-                          size_t callTargetEntries, size_t backedgeEntries);
+                          size_t callTargetEntries, size_t backedgeEntries,
+                          OptimizationLevel optimizationLevel);
     static void Trace(JSTracer *trc, IonScript *script);
     static void Destroy(FreeOp *fop, IonScript *script);
 
     static inline size_t offsetOfMethod() {
         return offsetof(IonScript, method_);
     }
     static inline size_t offsetOfOsrEntryOffset() {
         return offsetof(IonScript, osrEntryOffset_);
@@ -521,16 +526,19 @@ struct IonScript
         JS_ASSERT(refcount_);
         refcount_--;
         if (!refcount_)
             Destroy(fop, this);
     }
     const types::RecompileInfo& recompileInfo() const {
         return recompileInfo_;
     }
+    OptimizationLevel optimizationLevel() const {
+        return optimizationLevel_;
+    }
     uint32_t incrOsrPcMismatchCounter() {
         return ++osrPcMismatchCounter_;
     }
     void resetOsrPcMismatchCounter() {
         osrPcMismatchCounter_ = 0;
     }
 
     void setRecompiling() {
new file mode 100644
--- /dev/null
+++ b/js/src/jit/IonOptimizationLevels.cpp
@@ -0,0 +1,110 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ * 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/IonOptimizationLevels.h"
+
+#include "jsanalyze.h"
+#include "jsscript.h"
+
+using namespace js;
+using namespace js::jit;
+
+namespace js {
+namespace jit {
+
+OptimizationInfos js_IonOptimizations;
+
+void
+OptimizationInfo::initNormalOptimizationInfo()
+{
+    level_ = Optimization_Normal;
+
+    eaa_ = true;
+    edgeCaseAnalysis_ = true;
+    eliminateRedundantChecks_ = true;
+    inlineInterpreted_ = true;
+    inlineNative_ = true;
+    gvn_ = true;
+    gvnKind_ = GVN_Optimistic;
+    licm_ = true;
+    uce_ = true;
+    rangeAnalysis_ = true;
+    registerAllocator_ = RegisterAllocator_LSRA;
+
+    inlineMaxTotalBytecodeLength_ = 1000;
+    inliningMaxCallerBytecodeLength_ = 10000;
+    maxInlineDepth_ = 3;
+    smallFunctionMaxInlineDepth_ = 10;
+    usesBeforeCompile_ = 1000;
+    usesBeforeInliningFactor_ = 0.125;
+}
+
+void
+OptimizationInfo::initAsmjsOptimizationInfo()
+{
+    // The AsmJS optimization level
+    // Disables some passes that don't work well with asmjs.
+
+    // Take normal option values for not specified values.
+    initNormalOptimizationInfo();
+
+    level_ = Optimization_AsmJS;
+    edgeCaseAnalysis_ = false;
+    eliminateRedundantChecks_ = false;
+}
+
+OptimizationInfos::OptimizationInfos()
+{
+    infos_[Optimization_Normal - 1].initNormalOptimizationInfo();
+    infos_[Optimization_AsmJS - 1].initAsmjsOptimizationInfo();
+
+#ifdef DEBUG
+    OptimizationLevel prev = nextLevel(Optimization_DontCompile);
+    while (!isLastLevel(prev)) {
+        OptimizationLevel next = nextLevel(prev);
+        JS_ASSERT(get(prev)->usesBeforeCompile() < get(next)->usesBeforeCompile());
+        prev = next;
+    }
+#endif
+}
+
+OptimizationLevel
+OptimizationInfos::nextLevel(OptimizationLevel level)
+{
+    JS_ASSERT(!isLastLevel(level));
+    switch (level) {
+      case Optimization_DontCompile:
+        return Optimization_Normal;
+      default:
+        MOZ_ASSUME_UNREACHABLE("Unknown optimization level.");
+    }
+}
+
+bool
+OptimizationInfos::isLastLevel(OptimizationLevel level)
+{
+    return level == Optimization_Normal;
+}
+
+OptimizationLevel
+OptimizationInfos::levelForUseCount(uint32_t useCount)
+{
+    OptimizationLevel prev = Optimization_DontCompile;
+
+    while (!isLastLevel(prev)) {
+        OptimizationLevel level = nextLevel(prev);
+        const OptimizationInfo *info = get(level);
+        if (useCount < info->usesBeforeCompile())
+            return prev;
+
+        prev = level;
+    }
+
+    return prev;
+}
+
+} // namespace jit
+} // namespace js
new file mode 100644
--- /dev/null
+++ b/js/src/jit/IonOptimizationLevels.h
@@ -0,0 +1,203 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ * 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/. */
+
+#ifndef jit_IonOptimizationLevels_h
+#define jit_IonOptimizationLevels_h
+
+#include "jsbytecode.h"
+#include "jstypes.h"
+
+#include "jit/IonOptions.h"
+#include "js/TypeDecls.h"
+
+namespace js {
+namespace jit {
+
+enum OptimizationLevel
+{
+    Optimization_DontCompile,
+    Optimization_Normal,
+    Optimization_AsmJS,
+    Optimization_Count
+};
+
+class OptimizationInfo
+{
+  public:
+    OptimizationLevel level_;
+
+    // Toggles whether Effective Address Analysis is performed.
+    bool eaa_;
+
+    // Toggles whether Edge Case Analysis is used.
+    bool edgeCaseAnalysis_;
+
+    // Toggles whether redundant checks get removed.
+    bool eliminateRedundantChecks_;
+
+    // Toggles whether interpreted scripts get inlined.
+    bool inlineInterpreted_;
+
+    // Toggles whether native scripts get inlined.
+    bool inlineNative_;
+
+    // Toggles whether global value numbering is used.
+    bool gvn_;
+
+    // Toggles whether global value numbering is optimistic or pessimistic.
+    IonGvnKind gvnKind_;
+
+    // Toggles whether loop invariant code motion is performed.
+    bool licm_;
+
+    // Toggles whether Unreachable Code Elimination is performed.
+    bool uce_;
+
+    // Toggles whether Range Analysis is used.
+    bool rangeAnalysis_;
+
+    // Describes which register allocator to use.
+    IonRegisterAllocator registerAllocator_;
+
+    // The maximum total bytecode size of an inline call site.
+    uint32_t inlineMaxTotalBytecodeLength_;
+
+    // The maximum bytecode length the caller may have,
+    // before we stop inlining large functions in that caller.
+    uint32_t inliningMaxCallerBytecodeLength_;
+
+    // The maximum inlining depth.
+    uint32_t maxInlineDepth_;
+
+    // The maximum inlining depth for functions.
+    //
+    // Inlining small functions has almost no compiling overhead
+    // and removes the otherwise needed call overhead.
+    // The value is currently very low.
+    // Actually it is only needed to make sure we don't blow out the stack.
+    uint32_t smallFunctionMaxInlineDepth_;
+
+    // How many invocations or loop iterations are needed before functions
+    // are compiled.
+    uint32_t usesBeforeCompile_;
+
+    // How many invocations or loop iterations are needed before calls
+    // are inlined, as a fraction of usesBeforeCompile.
+    double usesBeforeInliningFactor_;
+
+    OptimizationInfo()
+    { }
+
+    void initNormalOptimizationInfo();
+    void initAsmjsOptimizationInfo();
+
+    OptimizationLevel level() const {
+        return level_;
+    }
+
+    bool inlineInterpreted() const {
+        return inlineInterpreted_ && !js_IonOptions.disableInlining;
+    }
+
+    bool inlineNative() const {
+        return inlineNative_ && !js_IonOptions.disableInlining;
+    }
+
+    uint32_t usesBeforeCompile() const {
+        if (js_IonOptions.forceDefaultIonUsesBeforeCompile)
+            return js_IonOptions.forcedDefaultIonUsesBeforeCompile;
+        return usesBeforeCompile_;
+    }
+
+    bool gvnEnabled() const {
+        return gvn_ && !js_IonOptions.disableGvn;
+    }
+
+    bool licmEnabled() const {
+        return licm_ && !js_IonOptions.disableLicm;
+    }
+
+    bool uceEnabled() const {
+        return uce_ && !js_IonOptions.disableUce;
+    }
+
+    bool rangeAnalysisEnabled() const {
+        return rangeAnalysis_ && !js_IonOptions.disableRangeAnalysis;
+    }
+
+    bool eaaEnabled() const {
+        return eaa_ && !js_IonOptions.disableEaa;
+    }
+
+    bool edgeCaseAnalysisEnabled() const {
+        return edgeCaseAnalysis_ && !js_IonOptions.disableEdgeCaseAnalysis;
+    }
+
+    bool eliminateRedundantChecksEnabled() const {
+        return eliminateRedundantChecks_;
+    }
+
+    IonGvnKind gvnKind() const {
+        if (!js_IonOptions.forceGvnKind)
+            return gvnKind_;
+        return js_IonOptions.forcedGvnKind;
+    }
+
+    IonRegisterAllocator registerAllocator() const {
+        if (!js_IonOptions.forceRegisterAllocator)
+            return registerAllocator_;
+        return js_IonOptions.forcedRegisterAllocator;
+    }
+
+    uint32_t smallFunctionMaxInlineDepth() const {
+        return smallFunctionMaxInlineDepth_;
+    }
+
+    bool isSmallFunction(JSScript *script) const;
+
+    uint32_t maxInlineDepth() const {
+        return maxInlineDepth_;
+    }
+
+    uint32_t inlineMaxTotalBytecodeLength() const {
+        return inlineMaxTotalBytecodeLength_;
+    }
+
+    uint32_t inliningMaxCallerBytecodeLength() const {
+        return inlineMaxTotalBytecodeLength_;
+    }
+
+    uint32_t usesBeforeInlining() const {
+        return usesBeforeCompile() * usesBeforeInliningFactor_;
+    }
+};
+
+class OptimizationInfos
+{
+  private:
+    OptimizationInfo infos_[Optimization_Count - 1];
+
+  public:
+    OptimizationInfos();
+
+    const OptimizationInfo *get(OptimizationLevel level) {
+        JS_ASSERT(level < Optimization_Count);
+        JS_ASSERT(level != Optimization_DontCompile);
+
+        return &infos_[level - 1];
+    }
+
+    OptimizationLevel nextLevel(OptimizationLevel level);
+    bool isLastLevel(OptimizationLevel level);
+    OptimizationLevel levelForUseCount(uint32_t useCount);
+};
+
+extern OptimizationInfos js_IonOptimizations;
+
+} // namespace jit
+} // namespace js
+
+#endif /* jit_IonOptimizationLevels_h */
new file mode 100644
--- /dev/null
+++ b/js/src/jit/JitOptions.cpp
@@ -0,0 +1,160 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ * 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/IonOptions.h"
+
+using namespace js;
+using namespace js::jit;
+
+namespace js {
+namespace jit {
+
+IonOptions js_IonOptions;
+
+IonOptions::IonOptions()
+{
+    // Whether to perform expensive graph-consistency DEBUG-only assertions.
+    // It can be useful to disable this to reduce DEBUG-compile time of large
+    // asm.js programs.
+    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.
+    checkOsiPointRegisters = false;
+#endif
+
+    // Whether to enable extra code to perform dynamic validation of
+    // RangeAnalysis results.
+    checkRangeAnalysis = false;
+
+    // Whether to protect the GC heap during Ion compilation and ensure that
+    // only threadsafe operations are performed on it.
+    checkThreadSafety = false;
+
+    // Whether Ion should compile try-catch statements.
+    compileTryCatch = true;
+
+    // Toggle whether global value numbering is globally disabled.
+    disableGvn = false;
+
+    // Toggles whether loop invariant code motion is globally disabled.
+    disableLicm = false;
+
+    // Toggles whether inlining is globally disabled.
+    disableInlining = false;
+
+    // Toggles whether Edge Case Analysis is gobally disabled.
+    disableEdgeCaseAnalysis = false;
+
+    // Toggles whether Range Analysis is globally disabled.
+    disableRangeAnalysis = false;
+
+    // Toggles whether Unreachable Code Elimination is globally disabled.
+    disableUce = false;
+
+    // Toggles whether Effective Address Analysis is globally disabled.
+    disableEaa = false;
+
+    // Whether functions are compiled immediately.
+    eagerCompilation = false;
+
+    // Force how many invocation or loop iterations are needed before compiling
+    // a function with the highest ionmonkey optimization level.
+    // (i.e. OptimizationLevel_Normal)
+    forceDefaultIonUsesBeforeCompile = false;
+    forcedDefaultIonUsesBeforeCompile = 1000;
+
+    // Force the GVN kind to be optimistic or pessimistic instead of letting
+    // the optimization pass decide.
+    forceGvnKind = false;
+    forcedGvnKind = GVN_Optimistic;
+
+    // Force the used register allocator instead of letting the
+    // optimization pass decide.
+    forceRegisterAllocator = false;
+    forcedRegisterAllocator = RegisterAllocator_LSRA;
+
+    // Toggles whether large scripts are rejected.
+    limitScriptSize = true;
+
+    // Toggles whether functions may be entered at loop headers.
+    osr = true;
+
+    // How many invocations or loop iterations are needed before functions
+    // are compiled with the baseline compiler.
+    baselineUsesBeforeCompile = 10;
+
+    // Number of exception bailouts (resuming into catch/finally block) before
+    // we invalidate and forbid Ion compilation.
+    exceptionBailoutThreshold = 10;
+
+    // Number of bailouts without invalidation before we set
+    // JSScript::hadFrequentBailouts and invalidate.
+    frequentBailoutThreshold = 10;
+
+    // How many actual arguments are accepted on the C stack.
+    maxStackArgs = 4096;
+
+    // How many times we will try to enter a script via OSR before
+    // invalidating the script.
+    osrPcMismatchesBeforeRecompile = 6000;
+
+    // The bytecode length limit for small function.
+    //
+    // The default for this was arrived at empirically via benchmarking.
+    // We may want to tune it further after other optimizations have gone
+    // in.
+    smallFunctionMaxBytecodeLength_ = 100;
+
+    // How many uses of a parallel kernel before we attempt compilation.
+    usesBeforeCompilePar = 1;
+}
+
+bool
+IonOptions::isSmallFunction(JSScript *script) const
+{
+    return script->length() <= smallFunctionMaxBytecodeLength_;
+}
+
+void
+IonOptions::setEagerCompilation()
+{
+    eagerCompilation = true;
+    baselineUsesBeforeCompile = 0;
+    forceDefaultIonUsesBeforeCompile = true;
+    forcedDefaultIonUsesBeforeCompile = 0;
+}
+
+void
+IonOptions::setUsesBeforeCompile(uint32_t useCount)
+{
+    forceDefaultIonUsesBeforeCompile = true;
+    forcedDefaultIonUsesBeforeCompile = useCount;
+
+    // Undo eager compilation
+    if (eagerCompilation && useCount != 0) {
+        jit::IonOptions defaultValues;
+        eagerCompilation = false;
+        baselineUsesBeforeCompile = defaultValues.baselineUsesBeforeCompile;
+    }
+}
+
+void
+IonOptions::resetUsesBeforeCompile()
+{
+    forceDefaultIonUsesBeforeCompile = false;
+
+    // Undo eager compilation
+    if (eagerCompilation) {
+        jit::IonOptions defaultValues;
+        eagerCompilation = false;
+        baselineUsesBeforeCompile = defaultValues.baselineUsesBeforeCompile;
+    }
+}
+
+} // namespace jit
+} // namespace js
new file mode 100644
--- /dev/null
+++ b/js/src/jit/JitOptions.h
@@ -0,0 +1,76 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ * 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/. */
+
+#ifndef jit_IonOptions_h
+#define jit_IonOptions_h
+
+#include "jit/IonTypes.h"
+
+#ifdef JS_ION
+
+namespace js {
+namespace jit {
+
+// Possible register allocators which may be used.
+enum IonRegisterAllocator {
+    RegisterAllocator_LSRA,
+    RegisterAllocator_Backtracking,
+    RegisterAllocator_Stupid
+};
+
+enum IonGvnKind {
+    GVN_Optimistic,
+    GVN_Pessimistic
+};
+
+struct IonOptions
+{
+    bool checkGraphConsistency;
+#ifdef CHECK_OSIPOINT_REGISTERS
+    bool checkOsiPointRegisters;
+#endif
+    bool checkRangeAnalysis;
+    bool checkThreadSafety;
+    bool compileTryCatch;
+    bool disableGvn;
+    bool disableLicm;
+    bool disableInlining;
+    bool disableEdgeCaseAnalysis;
+    bool disableRangeAnalysis;
+    bool disableUce;
+    bool disableEaa;
+    bool eagerCompilation;
+    bool forceDefaultIonUsesBeforeCompile;
+    uint32_t forcedDefaultIonUsesBeforeCompile;
+    bool forceGvnKind;
+    IonGvnKind forcedGvnKind;
+    bool forceRegisterAllocator;
+    IonRegisterAllocator forcedRegisterAllocator;
+    bool limitScriptSize;
+    bool osr;
+    uint32_t baselineUsesBeforeCompile;
+    uint32_t exceptionBailoutThreshold;
+    uint32_t frequentBailoutThreshold;
+    uint32_t maxStackArgs;
+    uint32_t osrPcMismatchesBeforeRecompile;
+    uint32_t smallFunctionMaxBytecodeLength_;
+    uint32_t usesBeforeCompilePar;
+
+    IonOptions();
+    bool isSmallFunction(JSScript *script) const;
+    void setEagerCompilation();
+    void setUsesBeforeCompile(uint32_t useCount);
+    void resetUsesBeforeCompile();
+};
+
+extern IonOptions js_IonOptions;
+
+} // namespace jit
+} // namespace js
+
+#endif // JS_ION
+
+#endif /* jit_IonOptions_h */
--- a/js/src/jit/Lowering.cpp
+++ b/js/src/jit/Lowering.cpp
@@ -3545,17 +3545,17 @@ bool
 LIRGenerator::visitBlock(MBasicBlock *block)
 {
     current = block->lir();
     updateResumeState(block);
 
     if (!definePhis())
         return false;
 
-    if (js_IonOptions.registerAllocator == RegisterAllocator_LSRA) {
+    if (gen->optimizationInfo().registerAllocator() == RegisterAllocator_LSRA) {
         if (!add(new(alloc()) LLabel()))
             return false;
     }
 
     for (MInstructionIterator iter = block->begin(); *iter != block->lastIns(); iter++) {
         if (!visitInstruction(*iter))
             return false;
     }
--- a/js/src/jit/MCallOptimize.cpp
+++ b/js/src/jit/MCallOptimize.cpp
@@ -19,16 +19,19 @@
 #include "vm/StringObject-inl.h"
 
 namespace js {
 namespace jit {
 
 IonBuilder::InliningStatus
 IonBuilder::inlineNativeCall(CallInfo &callInfo, JSNative native)
 {
+    if (!optimizationInfo().inlineNative())
+        return InliningStatus_NotInlined;
+
     // Array natives.
     if (native == js_Array)
         return inlineArray(callInfo);
     if (native == js::array_pop)
         return inlineArrayPopShift(callInfo, MArrayPopShift::Pop);
     if (native == js::array_shift)
         return inlineArrayPopShift(callInfo, MArrayPopShift::Shift);
     if (native == js::array_push)
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -9199,23 +9199,26 @@ class MHaveSameClass
     TypePolicy *typePolicy() {
         return this;
     }
     AliasSet getAliasSet() const {
         return AliasSet::None();
     }
 };
 
+// Increase the usecount of the provided script upon execution and test if
+// the usecount surpasses the threshold. Upon hit it will recompile the
+// outermost script (i.e. not the inlined script).
 class MRecompileCheck : public MNullaryInstruction
 {
     JSScript *script_;
     uint32_t recompileThreshold_;
 
-    MRecompileCheck(JSScript *script_, uint32_t recompileThreshold)
-      : script_(script_),
+    MRecompileCheck(JSScript *script, uint32_t recompileThreshold)
+      : script_(script),
         recompileThreshold_(recompileThreshold)
     {
         setGuard();
     }
 
   public:
     INSTRUCTION_HEADER(RecompileCheck);
 
--- a/js/src/jit/MIRGenerator.h
+++ b/js/src/jit/MIRGenerator.h
@@ -24,37 +24,42 @@
 #include "jit/RegisterSets.h"
 
 namespace js {
 namespace jit {
 
 class MBasicBlock;
 class MIRGraph;
 class MStart;
+class OptimizationInfo;
 
 class MIRGenerator
 {
   public:
-    MIRGenerator(CompileCompartment *compartment, TempAllocator *alloc, MIRGraph *graph, CompileInfo *info);
+    MIRGenerator(CompileCompartment *compartment, TempAllocator *alloc, MIRGraph *graph,
+                 CompileInfo *info, const OptimizationInfo *optimizationInfo);
 
     TempAllocator &alloc() {
         return *alloc_;
     }
     MIRGraph &graph() {
         return *graph_;
     }
     bool ensureBallast() {
         return alloc().ensureBallast();
     }
     const JitRuntime *jitRuntime() const {
         return GetIonContext()->runtime->jitRuntime();
     }
     CompileInfo &info() {
         return *info_;
     }
+    const OptimizationInfo &optimizationInfo() const {
+        return *optimizationInfo_;
+    }
 
     template <typename T>
     T * allocate(size_t count = 1) {
         return reinterpret_cast<T *>(alloc().allocate(sizeof(T) * count));
     }
 
     // Set an error state and prints a message. Returns false so errors can be
     // propagated up.
@@ -122,16 +127,17 @@ class MIRGenerator
         return asmJSGlobalAccesses_;
     }
 
   public:
     CompileCompartment *compartment;
 
   protected:
     CompileInfo *info_;
+    const OptimizationInfo *optimizationInfo_;
     TempAllocator *alloc_;
     JSFunction *fun_;
     uint32_t nslots_;
     MIRGraph *graph_;
     bool error_;
     size_t cancelBuild_;
 
     uint32_t maxAsmJSStackArgBytes_;
--- a/js/src/jit/MIRGraph.cpp
+++ b/js/src/jit/MIRGraph.cpp
@@ -12,19 +12,21 @@
 #include "jit/IonSpewer.h"
 #include "jit/MIR.h"
 #include "jit/MIRGenerator.h"
 
 using namespace js;
 using namespace js::jit;
 
 MIRGenerator::MIRGenerator(CompileCompartment *compartment,
-                           TempAllocator *alloc, MIRGraph *graph, CompileInfo *info)
+                           TempAllocator *alloc, MIRGraph *graph, CompileInfo *info,
+                           const OptimizationInfo *optimizationInfo)
   : compartment(compartment),
     info_(info),
+    optimizationInfo_(optimizationInfo),
     alloc_(alloc),
     graph_(graph),
     error_(false),
     cancelBuild_(0),
     maxAsmJSStackArgBytes_(0),
     performsAsmJSCall_(false),
     asmJSHeapAccesses_(*alloc),
     asmJSGlobalAccesses_(*alloc),
--- a/js/src/jit/UnreachableCodeElimination.cpp
+++ b/js/src/jit/UnreachableCodeElimination.cpp
@@ -77,18 +77,18 @@ UnreachableCodeElimination::removeUnmark
     if (rerunAliasAnalysis_) {
         AliasAnalysis analysis(mir_, graph_);
         if (!analysis.analyze())
             return false;
     }
 
     // Pass 5: It's important for optimizations to re-run GVN (and in
     // turn alias analysis) after UCE if we eliminated branches.
-    if (rerunAliasAnalysis_ && js_IonOptions.gvn) {
-        ValueNumberer gvn(mir_, graph_, js_IonOptions.gvnIsOptimistic);
+    if (rerunAliasAnalysis_ && mir_->optimizationInfo().gvnEnabled()) {
+        ValueNumberer gvn(mir_, graph_, mir_->optimizationInfo().gvnKind() == GVN_Optimistic);
         if (!gvn.clear() || !gvn.analyze())
             return false;
         IonSpewPass("GVN-after-UCE");
         AssertExtendedGraphCoherency(graph_);
 
         if (mir_->shouldCancel("GVN-after-UCE"))
             return false;
     }
--- a/js/src/jit/shared/Lowering-shared-inl.h
+++ b/js/src/jit/shared/Lowering-shared-inl.h
@@ -71,17 +71,17 @@ LIRGeneratorShared::defineFixed(LInstruc
     LDefinition def(type, LDefinition::PRESET);
     def.setOutput(output);
 
     // Add an LNop to avoid regalloc problems if the next op uses this value
     // with a fixed or at-start policy.
     if (!define(lir, mir, def))
         return false;
 
-    if (js_IonOptions.registerAllocator == RegisterAllocator_LSRA) {
+    if (gen->optimizationInfo().registerAllocator() == RegisterAllocator_LSRA) {
         if (!add(new(alloc()) LNop))
             return false;
     }
 
     return true;
 }
 
 template <size_t Ops, size_t Temps> bool
@@ -158,17 +158,17 @@ LIRGeneratorShared::defineReturn(LInstru
         lir->setDef(0, LDefinition(vreg, type, LGeneralReg(ReturnReg)));
         break;
     }
 
     mir->setVirtualRegister(vreg);
     if (!add(lir))
         return false;
 
-    if (js_IonOptions.registerAllocator == RegisterAllocator_LSRA) {
+    if (gen->optimizationInfo().registerAllocator() == RegisterAllocator_LSRA) {
         if (!add(new(alloc()) LNop))
             return false;
     }
 
     return true;
 }
 
 // In LIR, we treat booleans and integers as the same low-level type (INTEGER).
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -5975,28 +5975,31 @@ JS_SetParallelIonCompilationEnabled(JSCo
     cx->runtime()->setParallelIonCompilationEnabled(enabled);
 #endif
 }
 
 JS_PUBLIC_API(void)
 JS_SetGlobalJitCompilerOption(JSContext *cx, JSJitCompilerOption opt, uint32_t value)
 {
 #ifdef JS_ION
-    jit::IonOptions defaultValues;
 
     switch (opt) {
       case JSJITCOMPILER_BASELINE_USECOUNT_TRIGGER:
-        if (value == uint32_t(-1))
+        if (value == uint32_t(-1)) {
+            jit::IonOptions defaultValues;
             value = defaultValues.baselineUsesBeforeCompile;
+        }
         jit::js_IonOptions.baselineUsesBeforeCompile = value;
         break;
       case JSJITCOMPILER_ION_USECOUNT_TRIGGER:
-        if (value == uint32_t(-1))
-            value = defaultValues.usesBeforeCompile;
-        jit::js_IonOptions.usesBeforeCompile = value;
+        if (value == uint32_t(-1)) {
+            jit::js_IonOptions.resetUsesBeforeCompile();
+            break;
+        }
+        jit::js_IonOptions.setUsesBeforeCompile(value);
         if (value == 0)
             jit::js_IonOptions.setEagerCompilation();
         break;
       case JSJITCOMPILER_ION_ENABLE:
         if (value == 1) {
             JS::ContextOptionsRef(cx).setIon(true);
             IonSpew(js::jit::IonSpew_Scripts, "Enable ion");
         } else if (value == 0) {
--- a/js/src/jsscript.h
+++ b/js/src/jsscript.h
@@ -585,18 +585,20 @@ class JSScript : public js::gc::Barriere
 
     uint32_t        natoms_;    /* length of atoms array */
 
     /* Range of characters in scriptSource which contains this script's source. */
     uint32_t        sourceStart_;
     uint32_t        sourceEnd_;
 
     uint32_t        useCount;   /* Number of times the script has been called
-                                 * or has had backedges taken. Reset if the
-                                 * script's JIT code is forcibly discarded. */
+                                 * or has had backedges taken. When running in
+                                 * ion, also increased for any inlined scripts.
+                                 * Reset if the script's JIT code is forcibly
+                                 * discarded. */
 
 #ifdef DEBUG
     // Unique identifier within the compartment for this script, used for
     // printing analysis information.
     uint32_t        id_;
     uint32_t        idpad;
 #endif
 
--- a/js/src/jsworkers.cpp
+++ b/js/src/jsworkers.cpp
@@ -741,17 +741,16 @@ WorkerThread::handleIonWorkload(WorkerTh
 {
     JS_ASSERT(state.isLocked());
     JS_ASSERT(state.canStartIonCompile());
     JS_ASSERT(idle());
 
     ionBuilder = state.ionWorklist.popCopy();
 
     DebugOnly<ExecutionMode> executionMode = ionBuilder->info().executionMode();
-    JS_ASSERT(jit::GetIonScript(ionBuilder->script(), executionMode) == ION_COMPILING_SCRIPT);
 
 #if JS_TRACE_LOGGING
     AutoTraceLog logger(TraceLogging::getLogger(TraceLogging::ION_BACKGROUND_COMPILER),
                         TraceLogging::ION_COMPILE_START,
                         TraceLogging::ION_COMPILE_STOP,
                         ionBuilder->script());
 #endif
 
--- a/js/src/moz.build
+++ b/js/src/moz.build
@@ -252,16 +252,18 @@ if CONFIG['ENABLE_ION']:
         'jit/EdgeCaseAnalysis.cpp',
         'jit/EffectiveAddressAnalysis.cpp',
         'jit/Ion.cpp',
         'jit/IonAnalysis.cpp',
         'jit/IonBuilder.cpp',
         'jit/IonCaches.cpp',
         'jit/IonFrames.cpp',
         'jit/IonMacroAssembler.cpp',
+        'jit/IonOptimizationLevels.cpp',
+        'jit/IonOptions.cpp',
         'jit/IonSpewer.cpp',
         'jit/JSONSpewer.cpp',
         'jit/LICM.cpp',
         'jit/LinearScan.cpp',
         'jit/LIR.cpp',
         'jit/LiveRangeAllocator.cpp',
         'jit/Lowering.cpp',
         'jit/MCallOptimize.cpp',
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -5435,64 +5435,67 @@ ProcessArgs(JSContext *cx, JSObject *obj
     }
 
     if (op->getBoolOption("no-baseline")) {
         enableBaseline = false;
         JS::ContextOptionsRef(cx).toggleBaseline();
     }
 
     if (const char *str = op->getStringOption("ion-gvn")) {
-        if (strcmp(str, "off") == 0)
-            jit::js_IonOptions.gvn = false;
-        else if (strcmp(str, "pessimistic") == 0)
-            jit::js_IonOptions.gvnIsOptimistic = false;
-        else if (strcmp(str, "optimistic") == 0)
-            jit::js_IonOptions.gvnIsOptimistic = true;
-        else
+        if (strcmp(str, "off") == 0) {
+            jit::js_IonOptions.disableGvn = true;
+        } else if (strcmp(str, "pessimistic") == 0) {
+            jit::js_IonOptions.forceGvnKind = true;
+            jit::js_IonOptions.forcedGvnKind = jit::GVN_Pessimistic;
+        } else if (strcmp(str, "optimistic") == 0) {
+            jit::js_IonOptions.forceGvnKind = true;
+            jit::js_IonOptions.forcedGvnKind = jit::GVN_Optimistic;
+        } else {
             return OptionFailure("ion-gvn", str);
+        }
     }
 
     if (const char *str = op->getStringOption("ion-licm")) {
         if (strcmp(str, "on") == 0)
-            jit::js_IonOptions.licm = true;
+            jit::js_IonOptions.disableLicm = false;
         else if (strcmp(str, "off") == 0)
-            jit::js_IonOptions.licm = false;
+            jit::js_IonOptions.disableLicm = true;
         else
             return OptionFailure("ion-licm", str);
     }
 
     if (const char *str = op->getStringOption("ion-edgecase-analysis")) {
         if (strcmp(str, "on") == 0)
-            jit::js_IonOptions.edgeCaseAnalysis = true;
+            jit::js_IonOptions.disableEdgeCaseAnalysis = false;
         else if (strcmp(str, "off") == 0)
-            jit::js_IonOptions.edgeCaseAnalysis = false;
+            jit::js_IonOptions.disableEdgeCaseAnalysis = true;
         else
             return OptionFailure("ion-edgecase-analysis", str);
     }
 
      if (const char *str = op->getStringOption("ion-range-analysis")) {
          if (strcmp(str, "on") == 0)
-             jit::js_IonOptions.rangeAnalysis = true;
+             jit::js_IonOptions.disableRangeAnalysis = false;
          else if (strcmp(str, "off") == 0)
-             jit::js_IonOptions.rangeAnalysis = false;
+             jit::js_IonOptions.disableRangeAnalysis = true;
          else
              return OptionFailure("ion-range-analysis", str);
      }
 
     if (op->getBoolOption("ion-check-range-analysis"))
         jit::js_IonOptions.checkRangeAnalysis = true;
 
     if (op->getBoolOption("ion-check-thread-safety"))
         jit::js_IonOptions.checkThreadSafety = true;
 
     if (const char *str = op->getStringOption("ion-inlining")) {
         if (strcmp(str, "on") == 0)
-            jit::js_IonOptions.inlining = true;
+            jit::js_IonOptions.disableInlining = false;
         else if (strcmp(str, "off") == 0)
-            jit::js_IonOptions.inlining = false;
+            jit::js_IonOptions.disableInlining = true;
         else
             return OptionFailure("ion-inlining", str);
     }
 
     if (const char *str = op->getStringOption("ion-osr")) {
         if (strcmp(str, "on") == 0)
             jit::js_IonOptions.osr = true;
         else if (strcmp(str, "off") == 0)
@@ -5507,34 +5510,38 @@ ProcessArgs(JSContext *cx, JSObject *obj
         else if (strcmp(str, "off") == 0)
             jit::js_IonOptions.limitScriptSize = false;
         else
             return OptionFailure("ion-limit-script-size", str);
     }
 
     int32_t useCount = op->getIntOption("ion-uses-before-compile");
     if (useCount >= 0)
-        jit::js_IonOptions.usesBeforeCompile = useCount;
+        jit::js_IonOptions.setUsesBeforeCompile(useCount);
 
     useCount = op->getIntOption("baseline-uses-before-compile");
     if (useCount >= 0)
         jit::js_IonOptions.baselineUsesBeforeCompile = useCount;
 
     if (op->getBoolOption("baseline-eager"))
         jit::js_IonOptions.baselineUsesBeforeCompile = 0;
 
     if (const char *str = op->getStringOption("ion-regalloc")) {
-        if (strcmp(str, "lsra") == 0)
-            jit::js_IonOptions.registerAllocator = jit::RegisterAllocator_LSRA;
-        else if (strcmp(str, "backtracking") == 0)
-            jit::js_IonOptions.registerAllocator = jit::RegisterAllocator_Backtracking;
-        else if (strcmp(str, "stupid") == 0)
-            jit::js_IonOptions.registerAllocator = jit::RegisterAllocator_Stupid;
-        else
+        if (strcmp(str, "lsra") == 0) {
+            jit::js_IonOptions.forceRegisterAllocator = true;
+            jit::js_IonOptions.forcedRegisterAllocator = jit::RegisterAllocator_LSRA;
+        } else if (strcmp(str, "backtracking") == 0) {
+            jit::js_IonOptions.forceRegisterAllocator = true;
+            jit::js_IonOptions.forcedRegisterAllocator = jit::RegisterAllocator_Backtracking;
+        } else if (strcmp(str, "stupid") == 0) {
+            jit::js_IonOptions.forceRegisterAllocator = true;
+            jit::js_IonOptions.forcedRegisterAllocator = jit::RegisterAllocator_Stupid;
+        } else {
             return OptionFailure("ion-regalloc", str);
+        }
     }
 
     if (op->getBoolOption("ion-eager"))
         jit::js_IonOptions.setEagerCompilation();
 
     if (op->getBoolOption("ion-compile-try-catch"))
         jit::js_IonOptions.compileTryCatch = true;