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 160471 fa2005c9ca0220a8c4c073b485a64fa2b25a9271
parent 160470 7d3c745a811f383c2a3ddf8c48126cbcd3740fa7
child 160472 2b87186ddb6df93d330d948e7a9bf5eef558ba2d
push id270
push userpvanderbeken@mozilla.com
push dateThu, 06 Mar 2014 09:24:21 +0000
reviewersjandem
bugs939614
milestone29.0a1
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;