Bug 906788 - Construct TypeObject newScript information using MIR, r=jandem.
authorBrian Hackett <bhackett1024@gmail.com>
Mon, 02 Sep 2013 10:05:27 -0700
changeset 145238 abb25a18b5a50fbaba8b1e488b41806e8f6b7fd0
parent 145237 f66ac3e480caffa9a112a4b9b5294250ac0f0d1e
child 145239 24ffdfbf55d8d8abf8257a4ad9acdb6f0232f5e9
push id25203
push useremorley@mozilla.com
push dateTue, 03 Sep 2013 11:11:23 +0000
treeherdermozilla-central@541ba36e8b9d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjandem
bugs906788
milestone26.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 906788 - Construct TypeObject newScript information using MIR, r=jandem.
js/src/jit/CodeGenerator.cpp
js/src/jit/CompileInfo.h
js/src/jit/ExecutionModeInlines.h
js/src/jit/Ion.cpp
js/src/jit/IonAnalysis.cpp
js/src/jit/IonAnalysis.h
js/src/jit/IonBuilder.cpp
js/src/jit/MCallOptimize.cpp
js/src/jsinfer.cpp
js/src/jsinfer.h
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -1781,16 +1781,19 @@ CodeGenerator::visitCallGeneric(LCallGen
         if (!emitCallInvokeFunction(call, calleereg, call->numActualArgs(), unusedStack))
             return false;
         break;
 
       case ParallelExecution:
         if (!emitCallToUncompiledScriptPar(call, calleereg))
             return false;
         break;
+
+      default:
+        MOZ_ASSUME_UNREACHABLE("No such execution mode");
     }
 
     masm.bind(&end);
 
     // If the return value of the constructing function is Primitive,
     // replace the return value with the Object from CreateThis.
     if (call->mir()->isConstructing()) {
         Label notPrimitive;
@@ -1906,16 +1909,19 @@ CodeGenerator::visitCallKnown(LCallKnown
         if (!emitCallInvokeFunction(call, calleereg, call->numActualArgs(), unusedStack))
             return false;
         break;
 
       case ParallelExecution:
         if (!emitCallToUncompiledScriptPar(call, calleereg))
             return false;
         break;
+
+      default:
+        MOZ_ASSUME_UNREACHABLE("No such execution mode");
     }
 
     masm.bind(&end);
 
     if (!checkForAbortPar(call))
         return false;
 
     // If the return value of the constructing function is Primitive,
@@ -4795,17 +4801,17 @@ CodeGenerator::visitOutOfLineStoreElemen
         pushArg(object);
         if (!callVM(SetObjectElementInfo, ins))
             return false;
 
         restoreLive(ins);
         masm.jump(ool->rejoin());
         return true;
 
-      case ParallelExecution:
+      case ParallelExecution: {
         //////////////////////////////////////////////////////////////
         // If the problem is that we do not have sufficient capacity,
         // try to reallocate the elements array and then branch back
         // to perform the actual write.  Note that we do not want to
         // force the reg alloc to assign any particular register, so
         // we make space on the stack and pass the arguments that way.
         // (Also, outside of the VM call mechanism, it's very hard to
         // pass in a Value to a C function!).
@@ -4850,16 +4856,20 @@ CodeGenerator::visitOutOfLineStoreElemen
         // sparse array, and since we don't want to think about that
         // case right now, we just bail out.
         masm.bind(&indexNotInitLen);
         OutOfLineAbortPar *bail1 = oolAbortPar(ParallelBailoutUnsupportedSparseArray, ins);
         if (!bail1)
             return false;
         masm.jump(bail1->entry());
         return true;
+      }
+
+      default:
+        MOZ_ASSUME_UNREACHABLE("No such execution mode");
     }
 
     JS_ASSERT(false);
     return false;
 }
 
 typedef bool (*ArrayPopShiftFn)(JSContext *, HandleObject, MutableHandleValue);
 static const VMFunction ArrayPopDenseInfo = FunctionInfo<ArrayPopShiftFn>(jit::ArrayPopDense);
--- a/js/src/jit/CompileInfo.h
+++ b/js/src/jit/CompileInfo.h
@@ -25,21 +25,25 @@ StartArgSlot(JSScript *script, JSFunctio
 inline unsigned
 CountArgSlots(JSScript *script, JSFunction *fun)
 {
     return StartArgSlot(script, fun) + (fun ? fun->nargs + 1 : 0);
 }
 
 enum ExecutionMode {
     // Normal JavaScript execution
-    SequentialExecution = 0,
+    SequentialExecution,
 
     // JavaScript code to be executed in parallel worker threads,
     // e.g. by ParallelArray
-    ParallelExecution
+    ParallelExecution,
+
+    // MIR analysis performed when invoking 'new' on a script, to determine
+    // definite properties
+    DefinitePropertiesAnalysis
 };
 
 // Not as part of the enum so we don't get warnings about unhandled enum
 // values.
 static const unsigned NumExecutionModes = ParallelExecution + 1;
 
 // Contains information about the compilation source for IR being generated.
 class CompileInfo
--- a/js/src/jit/ExecutionModeInlines.h
+++ b/js/src/jit/ExecutionModeInlines.h
@@ -17,56 +17,62 @@ namespace js {
 namespace jit {
 
 static inline bool
 HasIonScript(JSScript *script, ExecutionMode cmode)
 {
     switch (cmode) {
       case SequentialExecution: return script->hasIonScript();
       case ParallelExecution: return script->hasParallelIonScript();
+      default:;
     }
     MOZ_ASSUME_UNREACHABLE("No such execution mode");
 }
 
 static inline IonScript *
 GetIonScript(JSScript *script, ExecutionMode cmode)
 {
     switch (cmode) {
       case SequentialExecution: return script->maybeIonScript();
       case ParallelExecution: return script->maybeParallelIonScript();
+      default:;
     }
     MOZ_ASSUME_UNREACHABLE("No such execution mode");
 }
 
 static inline void
 SetIonScript(JSScript *script, ExecutionMode cmode, IonScript *ionScript)
 {
     switch (cmode) {
       case SequentialExecution: script->setIonScript(ionScript); return;
       case ParallelExecution: script->setParallelIonScript(ionScript); return;
+      default:;
     }
     MOZ_ASSUME_UNREACHABLE("No such execution mode");
 }
 
 static inline size_t
 OffsetOfIonInJSScript(ExecutionMode cmode)
 {
     switch (cmode) {
       case SequentialExecution: return JSScript::offsetOfIonScript();
       case ParallelExecution: return JSScript::offsetOfParallelIonScript();
+      default:;
     }
     MOZ_ASSUME_UNREACHABLE("No such execution mode");
 }
 
 static inline bool
 CanIonCompile(JSScript *script, ExecutionMode cmode)
 {
     switch (cmode) {
       case SequentialExecution: return script->canIonCompile();
       case ParallelExecution: return script->canParallelIonCompile();
+      case DefinitePropertiesAnalysis: return true;
+      default:;
     }
     MOZ_ASSUME_UNREACHABLE("No such execution mode");
     return false;
 }
 
 static inline bool
 CanIonCompile(JSFunction *fun, ExecutionMode cmode)
 {
@@ -74,36 +80,39 @@ CanIonCompile(JSFunction *fun, Execution
 }
 
 static inline bool
 CompilingOffThread(JSScript *script, ExecutionMode cmode)
 {
     switch (cmode) {
       case SequentialExecution: return script->isIonCompilingOffThread();
       case ParallelExecution: return script->isParallelIonCompilingOffThread();
+      default:;
     }
     MOZ_ASSUME_UNREACHABLE("No such execution mode");
 }
 
 static inline bool
 CompilingOffThread(HandleScript script, ExecutionMode cmode)
 {
     switch (cmode) {
       case SequentialExecution: return script->isIonCompilingOffThread();
       case ParallelExecution: return script->isParallelIonCompilingOffThread();
+      default:;
     }
     MOZ_ASSUME_UNREACHABLE("No such execution mode");
 }
 
 static inline types::CompilerOutput::Kind
 CompilerOutputKind(ExecutionMode cmode)
 {
     switch (cmode) {
       case SequentialExecution: return types::CompilerOutput::Ion;
       case ParallelExecution: return types::CompilerOutput::ParallelIon;
+      default:;
     }
     MOZ_ASSUME_UNREACHABLE("No such execution mode");
 }
 
 } // namespace jit
 } // namespace js
 
 #endif  // JS_ION
--- a/js/src/jit/Ion.cpp
+++ b/js/src/jit/Ion.cpp
@@ -2446,16 +2446,18 @@ jit::Invalidate(JSContext *cx, JSScript 
         if (!scripts.append(script->ionScript()->recompileInfo()))
             return false;
         break;
       case ParallelExecution:
         JS_ASSERT(script->hasParallelIonScript());
         if (!scripts.append(script->parallelIonScript()->recompileInfo()))
             return false;
         break;
+      default:
+        MOZ_ASSUME_UNREACHABLE("No such execution mode");
     }
 
     Invalidate(cx, scripts, resetUses);
     return true;
 }
 
 bool
 jit::Invalidate(JSContext *cx, JSScript *script, bool resetUses)
@@ -2537,16 +2539,19 @@ jit::ForbidCompilation(JSContext *cx, JS
       case ParallelExecution:
         if (script->hasParallelIonScript()) {
             if (!Invalidate(cx, script, mode, false))
                 return;
         }
 
         script->setParallelIonScript(ION_DISABLED_SCRIPT);
         return;
+
+      default:
+        MOZ_ASSUME_UNREACHABLE("No such execution mode");
     }
 
     MOZ_ASSUME_UNREACHABLE("No such execution mode");
 }
 
 uint32_t
 jit::UsesBeforeIonRecompile(JSScript *script, jsbytecode *pc)
 {
--- a/js/src/jit/IonAnalysis.cpp
+++ b/js/src/jit/IonAnalysis.cpp
@@ -3,24 +3,30 @@
  * 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/IonAnalysis.h"
 
 #include "jsanalyze.h"
 
+#include "jit/BaselineInspector.h"
 #include "jit/Ion.h"
 #include "jit/IonBuilder.h"
 #include "jit/LIR.h"
 #include "jit/MIRGraph.h"
 
+#include "jsinferinlines.h"
+#include "jsscriptinlines.h"
+
 using namespace js;
 using namespace js::jit;
 
+using mozilla::DebugOnly;
+
 // A critical edge is an edge which is neither its successor's only predecessor
 // nor its predecessor's only successor. Critical edges must be split to
 // prevent copy-insertion and code motion from affecting other edges.
 bool
 jit::SplitCriticalEdges(MIRGraph &graph)
 {
     for (MBasicBlockIterator block(graph.begin()); block != graph.end(); block++) {
         if (block->numSuccessors() < 2)
@@ -1600,8 +1606,268 @@ LinearSum::print(Sprinter &sp) const
             sp.printf("%d*#%d", scale, id);
         }
     }
     if (constant_ > 0)
         sp.printf("+%d", constant_);
     else if (constant_ < 0)
         sp.printf("%d", constant_);
 }
+
+static bool
+AnalyzePoppedThis(JSContext *cx, types::TypeObject *type,
+                  MDefinition *thisValue, MInstruction *ins, bool definitelyExecuted,
+                  HandleObject baseobj,
+                  Vector<types::TypeNewScript::Initializer> *initializerList,
+                  Vector<jsid> *accessedProperties,
+                  bool *phandled)
+{
+    // Determine the effect that a use of the |this| value when calling |new|
+    // on a script has on the properties definitely held by the new object.
+
+    if (ins->isCallSetProperty()) {
+        MCallSetProperty *setprop = ins->toCallSetProperty();
+
+        if (setprop->obj() != thisValue)
+            return true;
+
+        // Don't use GetAtomId here, we need to watch for SETPROP on
+        // integer properties and bail out. We can't mark the aggregate
+        // JSID_VOID type property as being in a definite slot.
+        RootedId id(cx, NameToId(setprop->name()));
+        if (types::IdToTypeId(id) != id ||
+            id == NameToId(cx->names().classPrototype) ||
+            id == NameToId(cx->names().proto) ||
+            id == NameToId(cx->names().constructor))
+        {
+            return true;
+        }
+
+        // Ignore assignments to properties that were already written to.
+        if (baseobj->nativeLookup(cx, id)) {
+            *phandled = true;
+            return true;
+        }
+
+        // Don't add definite properties for properties that were already
+        // read in the constructor.
+        for (size_t i = 0; i < accessedProperties->length(); i++) {
+            if ((*accessedProperties)[i] == id)
+                return true;
+        }
+
+        if (baseobj->slotSpan() >= (types::TYPE_FLAG_DEFINITE_MASK >> types::TYPE_FLAG_DEFINITE_SHIFT)) {
+            // Maximum number of definite properties added.
+            return true;
+        }
+
+        // Assignments to new properties must always execute.
+        if (!definitelyExecuted)
+            return true;
+
+        if (!types::AddClearDefiniteGetterSetterForPrototypeChain(cx, type, id)) {
+            // The prototype chain already contains a getter/setter for this
+            // property, or type information is too imprecise.
+            return true;
+        }
+
+        DebugOnly<unsigned> slotSpan = baseobj->slotSpan();
+        RootedValue value(cx, UndefinedValue());
+        if (!DefineNativeProperty(cx, baseobj, id, value, NULL, NULL,
+                                  JSPROP_ENUMERATE, 0, 0, DNP_SKIP_TYPE))
+        {
+            return false;
+        }
+        JS_ASSERT(baseobj->slotSpan() != slotSpan);
+        JS_ASSERT(!baseobj->inDictionaryMode());
+
+        Vector<MResumePoint *> callerResumePoints(cx);
+        MBasicBlock *block = ins->block();
+        for (MResumePoint *rp = block->callerResumePoint();
+             rp;
+             block = rp->block(), rp = block->callerResumePoint())
+        {
+            JSScript *script = rp->block()->info().script();
+            types::AddClearDefiniteFunctionUsesInScript(cx, type, script, block->info().script());
+            if (!callerResumePoints.append(rp))
+                return false;
+        }
+
+        for (int i = callerResumePoints.length() - 1; i >= 0; i--) {
+            MResumePoint *rp = callerResumePoints[i];
+            JSScript *script = rp->block()->info().script();
+            types::TypeNewScript::Initializer entry(types::TypeNewScript::Initializer::SETPROP_FRAME,
+                                                    rp->pc() - script->code);
+            if (!initializerList->append(entry))
+                return false;
+        }
+
+        JSScript *script = ins->block()->info().script();
+        types::TypeNewScript::Initializer entry(types::TypeNewScript::Initializer::SETPROP,
+                                                setprop->resumePoint()->pc() - script->code);
+        if (!initializerList->append(entry))
+            return false;
+
+        *phandled = true;
+        return true;
+    }
+
+    if (ins->isCallGetProperty()) {
+        MCallGetProperty *get = ins->toCallGetProperty();
+
+        /*
+         * Properties can be read from the 'this' object if the following hold:
+         *
+         * - The read is not on a getter along the prototype chain, which
+         *   could cause 'this' to escape.
+         *
+         * - The accessed property is either already a definite property or
+         *   is not later added as one. Since the definite properties are
+         *   added to the object at the point of its creation, reading a
+         *   definite property before it is assigned could incorrectly hit.
+         */
+        RootedId id(cx, NameToId(get->name()));
+        if (types::IdToTypeId(id) != id)
+            return true;
+        if (!baseobj->nativeLookup(cx, id) && !accessedProperties->append(id.get()))
+            return false;
+
+        if (!types::AddClearDefiniteGetterSetterForPrototypeChain(cx, type, id)) {
+            // The |this| value can escape if any property reads it does go
+            // through a getter.
+            return true;
+        }
+
+        *phandled = true;
+        return true;
+    }
+
+    if (ins->isPostWriteBarrier()) {
+        *phandled = true;
+        return true;
+    }
+
+    return true;
+}
+
+static int
+CmpInstructions(const void *a, const void *b)
+{
+    return (*static_cast<MInstruction * const *>(a))->id() -
+           (*static_cast<MInstruction * const *>(b))->id();
+}
+
+bool
+jit::AnalyzeNewScriptProperties(JSContext *cx, JSFunction *fun,
+                                types::TypeObject *type, HandleObject baseobj,
+                                Vector<types::TypeNewScript::Initializer> *initializerList)
+{
+    // When invoking 'new' on the specified script, try to find some properties
+    // which will definitely be added to the created object before it has a
+    // chance to escape and be accessed elsewhere.
+
+    if (!fun->nonLazyScript()->compileAndGo)
+        return true;
+
+    if (!fun->nonLazyScript()->ensureHasTypes(cx))
+        return false;
+
+    types::TypeScript::SetThis(cx, fun->nonLazyScript(), types::Type::ObjectType(type));
+
+    Vector<jsid> accessedProperties(cx);
+
+    LifoAlloc alloc(JSCompartment::ANALYSIS_LIFO_ALLOC_PRIMARY_CHUNK_SIZE);
+
+    TempAllocator temp(&alloc);
+    IonContext ictx(cx, &temp);
+
+    types::AutoEnterAnalysis enter(cx);
+
+    if (!fun->nonLazyScript()->ensureRanAnalysis(cx))
+        return false;
+
+    MIRGraph graph(&temp);
+    CompileInfo info(fun->nonLazyScript(), fun,
+                     /* osrPc = */ NULL, /* constructing = */ false,
+                     DefinitePropertiesAnalysis);
+
+    AutoTempAllocatorRooter root(cx, &temp);
+
+    BaselineInspector inspector(cx, fun->nonLazyScript());
+    IonBuilder builder(cx, &temp, &graph, &inspector, &info, /* baselineFrame = */ NULL);
+
+    if (!builder.build()) {
+        if (builder.abortReason() == AbortReason_Alloc)
+            return false;
+        return true;
+    }
+
+    if (!SplitCriticalEdges(graph))
+        return false;
+
+    if (!RenumberBlocks(graph))
+        return false;
+
+    if (!BuildDominatorTree(graph))
+        return false;
+
+    if (!EliminatePhis(&builder, graph, AggressiveObservability))
+        return false;
+
+    MDefinition *thisValue = graph.begin()->getSlot(info.thisSlot());
+
+    // Get a list of instructions using the |this| value in the order they
+    // appear in the graph.
+    Vector<MInstruction *> instructions(cx);
+
+    Vector<MDefinition *> useWorklist(cx);
+    for (MUseDefIterator uses(thisValue); uses; uses++) {
+        MDefinition *use = uses.def();
+
+        // Don't track |this| through assignments to phis.
+        if (!use->isInstruction())
+            return true;
+
+        if (!instructions.append(use->toInstruction()))
+            return false;
+    }
+
+    // Sort the instructions to visit in increasing order.
+    qsort(instructions.begin(), instructions.length(),
+          sizeof(MInstruction *), CmpInstructions);
+
+    // Find all exit blocks in the graph.
+    Vector<MBasicBlock *> exitBlocks(cx);
+    for (MBasicBlockIterator block(graph.begin()); block != graph.end(); block++) {
+        if (!block->numSuccessors() && !exitBlocks.append(*block))
+            return false;
+    }
+
+    for (size_t i = 0; i < instructions.length(); i++) {
+        MInstruction *ins = instructions[i];
+
+        // Track whether the use of |this| is in unconditional code, i.e.
+        // the block dominates all graph exits.
+        bool definitelyExecuted = true;
+        for (size_t i = 0; i < exitBlocks.length(); i++) {
+            for (MBasicBlock *exit = exitBlocks[i];
+                 exit != ins->block();
+                 exit = exit->immediateDominator())
+            {
+                if (exit == exit->immediateDominator()) {
+                    definitelyExecuted = false;
+                    break;
+                }
+            }
+        }
+
+        bool handled = false;
+        if (!AnalyzePoppedThis(cx, type, thisValue, ins, definitelyExecuted,
+                               baseobj, initializerList, &accessedProperties, &handled))
+        {
+            return false;
+        }
+        if (!handled)
+            return true;
+    }
+
+    return true;
+}
--- a/js/src/jit/IonAnalysis.h
+++ b/js/src/jit/IonAnalysis.h
@@ -119,12 +119,17 @@ class LinearSum
 
     void print(Sprinter &sp) const;
 
   private:
     Vector<LinearTerm, 2, IonAllocPolicy> terms_;
     int32_t constant_;
 };
 
+bool
+AnalyzeNewScriptProperties(JSContext *cx, JSFunction *fun,
+                           types::TypeObject *type, HandleObject baseobj,
+                           Vector<types::TypeNewScript::Initializer> *initializerList);
+
 } // namespace jit
 } // namespace js
 
 #endif /* jit_IonAnalysis_h */
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -3924,26 +3924,31 @@ IonBuilder::makeInliningDecision(JSFunct
     // Callee must not be excessively large.
     // This heuristic also applies to the callsite as a whole.
     if (targetScript->length > js_IonOptions.inlineMaxTotalBytecodeLength) {
         IonSpew(IonSpew_Inlining, "%s:%d - Vetoed: callee excessively large.",
                                   targetScript->filename(), targetScript->lineno);
         return false;
     }
 
-    // Caller must be... somewhat hot.
+    // Caller must be... somewhat hot. Ignore use counts when inlining for
+    // the definite properties analysis, as the caller has not run yet.
     uint32_t callerUses = script()->getUseCount();
-    if (callerUses < js_IonOptions.usesBeforeInlining()) {
+    if (callerUses < js_IonOptions.usesBeforeInlining() &&
+        info().executionMode() != DefinitePropertiesAnalysis)
+    {
         IonSpew(IonSpew_Inlining, "%s:%d - Vetoed: caller is insufficiently hot.",
                                   targetScript->filename(), targetScript->lineno);
         return false;
     }
 
     // Callee must be hot relative to the caller.
-    if (targetScript->getUseCount() * js_IonOptions.inlineUseCountRatio < callerUses) {
+    if (targetScript->getUseCount() * js_IonOptions.inlineUseCountRatio < callerUses &&
+        info().executionMode() != DefinitePropertiesAnalysis)
+    {
         IonSpew(IonSpew_Inlining, "%s:%d - Vetoed: callee is not hot.",
                                   targetScript->filename(), targetScript->lineno);
         return false;
     }
 
     JS_ASSERT(!target->hasLazyType());
     types::TypeObject *targetType = target->getType(cx);
     JS_ASSERT(targetType && !targetType->unknownProperties());
@@ -4798,17 +4803,17 @@ IonBuilder::jsop_funapplyarguments(uint3
     int funcDepth = -((int)argc + 1);
 
     // Extract call target.
     types::StackTypeSet *funTypes = current->peek(funcDepth)->resultTypeSet();
     RootedFunction target(cx, getSingleCallTarget(funTypes));
 
     // When this script isn't inlined, use MApplyArgs,
     // to copy the arguments from the stack and call the function
-    if (inliningDepth_ == 0) {
+    if (inliningDepth_ == 0 && info().executionMode() != DefinitePropertiesAnalysis) {
 
         // Vp
         MPassArg *passVp = current->pop()->toPassArg();
         passVp->getArgument()->setFoldedUnchecked();
         passVp->replaceAllUsesWith(passVp->getArgument());
         passVp->block()->discard(passVp);
 
         // This
@@ -4836,30 +4841,34 @@ IonBuilder::jsop_funapplyarguments(uint3
             return false;
 
         types::StackTypeSet *types = types::TypeScript::BytecodeTypes(script(), pc);
         return pushTypeBarrier(apply, types, true);
     }
 
     // When inlining we have the arguments the function gets called with
     // and can optimize even more, by just calling the functions with the args.
-    JS_ASSERT(inliningDepth_ > 0);
+    // We also try this path when doing the definite properties analysis, as we
+    // can inline the apply() target and don't care about the actual arguments
+    // that were passed in.
 
     CallInfo callInfo(cx, false);
 
     // Vp
     MPassArg *passVp = current->pop()->toPassArg();
     passVp->getArgument()->setFoldedUnchecked();
     passVp->replaceAllUsesWith(passVp->getArgument());
     passVp->block()->discard(passVp);
 
     // Arguments
     Vector<MDefinition *> args(cx);
-    if (!args.append(inlineCallInfo_->argv().begin(), inlineCallInfo_->argv().end()))
-        return false;
+    if (inliningDepth_) {
+        if (!args.append(inlineCallInfo_->argv().begin(), inlineCallInfo_->argv().end()))
+            return false;
+    }
     callInfo.setArgs(&args);
 
     // This
     MPassArg *passThis = current->pop()->toPassArg();
     MDefinition *argThis = passThis->getArgument();
     passThis->replaceAllUsesWith(argThis);
     passThis->block()->discard(passThis);
     callInfo.setThis(argThis);
@@ -5565,16 +5574,17 @@ IonBuilder::jsop_initprop(HandleProperty
     {
         needsBarrier = false;
     }
 
     // In parallel execution, we never require write barriers.  See
     // forkjoin.cpp for more information.
     switch (info().executionMode()) {
       case SequentialExecution:
+      case DefinitePropertiesAnalysis:
         break;
       case ParallelExecution:
         needsBarrier = false;
         break;
     }
 
     if (templateObject->isFixedSlot(shape->slot())) {
         MStoreFixedSlot *store = MStoreFixedSlot::New(obj, shape->slot(), value);
@@ -8014,21 +8024,32 @@ IonBuilder::jsop_getprop(HandlePropertyN
     bool emitted = false;
 
     types::StackTypeSet *types = types::TypeScript::BytecodeTypes(script(), pc);
 
     // Try to optimize arguments.length.
     if (!getPropTryArgumentsLength(&emitted) || emitted)
         return emitted;
 
+    bool barrier = PropertyReadNeedsTypeBarrier(cx, current->peek(-1), name, types);
+
     // Try to hardcode known constants.
     if (!getPropTryConstant(&emitted, id, types) || emitted)
         return emitted;
 
-    bool barrier = PropertyReadNeedsTypeBarrier(cx, current->peek(-1), name, types);
+    // Except when loading constants above, always use a call if we are doing
+    // the definite properties analysis and not actually emitting code, to
+    // simplify later analysis.
+    if (info().executionMode() == DefinitePropertiesAnalysis) {
+        MDefinition *obj = current->pop();
+        MCallGetProperty *call = MCallGetProperty::New(obj, name);
+        current->add(call);
+        current->push(call);
+        return resumeAfter(call);
+    }
 
     // Try to emit loads from definite slots.
     if (!getPropTryDefiniteSlot(&emitted, name, barrier, types) || emitted)
         return emitted;
 
     // Try to inline a common property getter, or make a call.
     if (!getPropTryCommonGetter(&emitted, id, barrier, types) || emitted)
         return emitted;
@@ -8365,16 +8386,25 @@ bool
 IonBuilder::jsop_setprop(HandlePropertyName name)
 {
     MDefinition *value = current->pop();
     MDefinition *obj = current->pop();
 
     RootedId id(cx, NameToId(name));
     bool emitted = false;
 
+    // Always use a call if we are doing the definite properties analysis and
+    // not actually emitting code, to simplify later analysis.
+    if (info().executionMode() == DefinitePropertiesAnalysis) {
+        MInstruction *ins = MCallSetProperty::New(obj, value, name, script()->strict);
+        current->add(ins);
+        current->push(value);
+        return resumeAfter(ins);
+    }
+
     // Add post barrier if needed.
     if (NeedsPostBarrier(info(), value))
         current->add(MPostWriteBarrier::New(obj, value));
 
     // Try to inline a common property setter, or make a call.
     if (!setPropTryCommonSetter(&emitted, obj, name, id, value) || emitted)
         return emitted;
 
@@ -8754,16 +8784,25 @@ IonBuilder::jsop_this()
     {
         // This is safe, because if the entry type of |this| is an object, it
         // will necessarily be an object throughout the entire function. OSR
         // can introduce a phi, but this phi will be specialized.
         current->pushSlot(info().thisSlot());
         return true;
     }
 
+    // If we are doing a definite properties analysis, we don't yet know the
+    // |this| type as its type object is being created right now. Instead of
+    // bailing out just push the |this| slot, as this code won't actually
+    // execute and it does not matter whether |this| is primitive.
+    if (info().executionMode() == DefinitePropertiesAnalysis) {
+        current->pushSlot(info().thisSlot());
+        return true;
+    }
+
     return abort("JSOP_THIS hard case not yet handled");
 }
 
 bool
 IonBuilder::jsop_typeof()
 {
     MDefinition *input = current->pop();
     MTypeOf *ins = MTypeOf::New(input, input->type());
--- a/js/src/jit/MCallOptimize.cpp
+++ b/js/src/jit/MCallOptimize.cpp
@@ -1097,25 +1097,28 @@ IonBuilder::inlineForceSequentialOrInPar
 
     ExecutionMode executionMode = info().executionMode();
     switch (executionMode) {
       case SequentialExecution:
         // In sequential mode, leave as is, because we'd have to
         // access the "in warmup" flag of the runtime.
         return InliningStatus_NotInlined;
 
-      case ParallelExecution:
+      case ParallelExecution: {
         // During Parallel Exec, we always force sequential, so
         // replace with true.  This permits UCE to eliminate the
         // entire path as dead, which is important.
         callInfo.unwrapArgs();
         MConstant *ins = MConstant::New(BooleanValue(true));
         current->add(ins);
         current->push(ins);
         return InliningStatus_Inlined;
+      }
+
+      default:;
     }
 
     MOZ_ASSUME_UNREACHABLE("Invalid execution mode");
 }
 
 IonBuilder::InliningStatus
 IonBuilder::inlineNewParallelArray(CallInfo &callInfo)
 {
@@ -1280,16 +1283,17 @@ IonBuilder::inlineNewDenseArray(CallInfo
     // For now, in seq. mode we just call the C function.  In
     // par. mode we use inlined MIR.
     ExecutionMode executionMode = info().executionMode();
     switch (executionMode) {
       case SequentialExecution:
         return inlineNewDenseArrayForSequentialExecution(callInfo);
       case ParallelExecution:
         return inlineNewDenseArrayForParallelExecution(callInfo);
+      default:;
     }
 
     MOZ_ASSUME_UNREACHABLE("unknown ExecutionMode");
 }
 
 IonBuilder::InliningStatus
 IonBuilder::inlineNewDenseArrayForSequentialExecution(CallInfo &callInfo)
 {
--- a/js/src/jsinfer.cpp
+++ b/js/src/jsinfer.cpp
@@ -24,16 +24,17 @@
 #include "jsstr.h"
 #include "jsworkers.h"
 #include "prmjtime.h"
 
 #include "gc/Marking.h"
 #ifdef JS_ION
 #include "jit/BaselineJIT.h"
 #include "jit/Ion.h"
+#include "jit/IonAnalysis.h"
 #include "jit/IonCompartment.h"
 #endif
 #include "js/MemoryMetrics.h"
 #include "vm/Shape.h"
 
 #include "jsanalyzeinlines.h"
 #include "jsatominlines.h"
 #include "jsgcinlines.h"
@@ -1968,16 +1969,17 @@ HeapTypeSet::isOwnProperty(JSContext *cx
 {
     /*
      * Everywhere compiled code depends on definite properties associated with
      * a type object's newScript, we need to make sure there are constraints
      * in place which will mark those properties as configured should the
      * definite properties be invalidated.
      */
     if (object->flags & OBJECT_FLAG_NEW_SCRIPT_REGENERATE) {
+        object->flags &= ~OBJECT_FLAG_NEW_SCRIPT_REGENERATE;
         if (object->hasNewScript()) {
             Rooted<TypeObject*> typeObj(cx, object);
             RootedFunction fun(cx, object->newScript()->fun);
             CheckNewScriptProperties(cx, typeObj, fun);
         } else {
             JS_ASSERT(object->flags & OBJECT_FLAG_ADDENDUM_CLEARED);
             object->flags &= ~OBJECT_FLAG_NEW_SCRIPT_REGENERATE;
         }
@@ -3821,76 +3823,79 @@ TypeObject::clearNewScriptAddendum(Exclu
      * We can't really detect the possibility of this statically, but the new
      * script keeps track of where each property is initialized so we can walk
      * the stack and fix up any such objects.
      */
     if (cx->isJSContext()) {
         Vector<uint32_t, 32> pcOffsets(cx);
         for (ScriptFrameIter iter(cx->asJSContext()); !iter.done(); ++iter) {
             pcOffsets.append(uint32_t(iter.pc() - iter.script()->code));
-            if (iter.isConstructing() &&
-                iter.callee() == newScript()->fun &&
-                iter.thisv().isObject() &&
-                !iter.thisv().toObject().hasLazyType() &&
-                iter.thisv().toObject().type() == this)
+            if (!iter.isConstructing() ||
+                iter.callee() != newScript()->fun ||
+                !iter.thisv().isObject() ||
+                iter.thisv().toObject().hasLazyType() ||
+                iter.thisv().toObject().type() != this)
             {
-                RootedObject obj(cx, &iter.thisv().toObject());
-
-                /* Whether all identified 'new' properties have been initialized. */
-                bool finished = false;
-
-                /* If not finished, number of properties that have been added. */
-                uint32_t numProperties = 0;
-
-                /*
-                 * If non-zero, we are scanning initializers in a call which has
-                 * already finished.
-                 */
-                size_t depth = 0;
-                size_t callDepth = pcOffsets.length() - 1;
-                uint32_t offset = pcOffsets[callDepth];
-
-                for (TypeNewScript::Initializer *init = newScript()->initializerList;; init++) {
-                    if (init->kind == TypeNewScript::Initializer::SETPROP) {
-                        if (!depth && init->offset > offset) {
-                            /* Advanced past all properties which have been initialized. */
-                            break;
-                        }
-                        numProperties++;
-                    } else if (init->kind == TypeNewScript::Initializer::FRAME_PUSH) {
-                        if (depth) {
-                            depth++;
-                        } else if (init->offset > offset) {
-                            /* Advanced past all properties which have been initialized. */
-                            break;
-                        } else if (init->offset == offset) {
-                            if (!callDepth)
-                                break;
-                            offset = pcOffsets[--callDepth];
-                        } else {
-                            /* This call has already finished. */
-                            depth = 1;
-                        }
-                    } else if (init->kind == TypeNewScript::Initializer::FRAME_POP) {
-                        if (depth) {
-                            depth--;
-                        } else {
-                            /* This call has not finished yet. */
-                            break;
-                        }
-                    } else {
-                        JS_ASSERT(init->kind == TypeNewScript::Initializer::DONE);
-                        finished = true;
+                continue;
+            }
+
+            // Found a matching frame.
+            RootedObject obj(cx, &iter.thisv().toObject());
+
+            // Whether all identified 'new' properties have been initialized.
+            bool finished = false;
+
+            // If not finished, number of properties that have been added.
+            uint32_t numProperties = 0;
+
+            // Whether the current SETPROP is within an inner frame which has
+            // finished entirely.
+            bool pastProperty = false;
+
+            // Index in pcOffsets of the outermost frame.
+            int callDepth = pcOffsets.length() - 1;
+
+            // Index in pcOffsets of the frame currently being checked for a SETPROP.
+            int setpropDepth = callDepth;
+
+            for (TypeNewScript::Initializer *init = newScript()->initializerList;; init++) {
+                if (init->kind == TypeNewScript::Initializer::SETPROP) {
+                    if (!pastProperty && pcOffsets[setpropDepth] < init->offset) {
+                        // Have not yet reached this setprop.
                         break;
                     }
+                    // This setprop has executed, reset state for the next one.
+                    numProperties++;
+                    pastProperty = false;
+                    setpropDepth = callDepth;
+                } else if (init->kind == TypeNewScript::Initializer::SETPROP_FRAME) {
+                    if (!pastProperty) {
+                        if (pcOffsets[setpropDepth] < init->offset) {
+                            // Have not yet reached this inner call.
+                            break;
+                        } else if (pcOffsets[setpropDepth] > init->offset) {
+                            // Have advanced past this inner call.
+                            pastProperty = true;
+                        } else if (setpropDepth == 0) {
+                            // Have reached this call but not yet in it.
+                            break;
+                        } else {
+                            // Somewhere inside this inner call.
+                            setpropDepth--;
+                        }
+                    }
+                } else {
+                    JS_ASSERT(init->kind == TypeNewScript::Initializer::DONE);
+                    finished = true;
+                    break;
                 }
-
-                if (!finished)
-                    obj->rollbackProperties(cx, numProperties);
             }
+
+            if (!finished)
+                obj->rollbackProperties(cx, numProperties);
         }
     } else {
         // Threads with an ExclusiveContext are not allowed to run scripts.
         JS_ASSERT(!cx->perThreadData->activation());
     }
 }
 
 void
@@ -4796,18 +4801,16 @@ ScriptAnalysis::analyzeTypes(JSContext *
             state.forTypes->addType(cx, Type::UnknownType());
         }
         result = result->next;
     }
 
     TypeScript::AddFreezeConstraints(cx, script_);
 }
 
-namespace {
-
 /*
  * Persistent constraint clearing out newScript and definite properties from
  * an object should a property on another object get a getter or setter.
  */
 class TypeConstraintClearDefiniteGetterSetter : public TypeConstraint
 {
   public:
     TypeObject *object;
@@ -4830,42 +4833,38 @@ class TypeConstraintClearDefiniteGetterS
          */
         if (!(object->flags & OBJECT_FLAG_ADDENDUM_CLEARED) && source->ownProperty(true))
             object->clearAddendum(cx);
     }
 
     void newType(JSContext *cx, TypeSet *source, Type type) {}
 };
 
-} /* anonymous namespace */
-
-static bool
-AddClearDefiniteGetterSetterForPrototypeChain(JSContext *cx, TypeObject *type, jsid id)
+bool
+types::AddClearDefiniteGetterSetterForPrototypeChain(JSContext *cx, TypeObject *type, jsid id)
 {
     /*
      * Ensure that if the properties named here could have a getter, setter or
      * a permanent property in any transitive prototype, the definite
-     * properties get cleared from the shape.
+     * properties get cleared from the type.
      */
     RootedObject parent(cx, type->proto);
     while (parent) {
         TypeObject *parentObject = parent->getType(cx);
         if (!parentObject || parentObject->unknownProperties())
             return false;
         HeapTypeSet *parentTypes = parentObject->getProperty(cx, id, false);
         if (!parentTypes || parentTypes->ownProperty(true))
             return false;
         parentTypes->add(cx, cx->typeLifoAlloc().new_<TypeConstraintClearDefiniteGetterSetter>(type));
         parent = parent->getProto();
     }
     return true;
 }
 
-namespace {
-
 /*
  * Constraint which clears definite properties on an object should a type set
  * contain any types other than a single object.
  */
 class TypeConstraintClearDefiniteSingle : public TypeConstraint
 {
   public:
     TypeObject *object;
@@ -4880,412 +4879,124 @@ class TypeConstraintClearDefiniteSingle 
         if (object->flags & OBJECT_FLAG_ADDENDUM_CLEARED)
             return;
 
         if (source->baseFlags() || source->getObjectCount() > 1)
             object->clearAddendum(cx);
     }
 };
 
-struct NewScriptPropertiesState
-{
-    RootedFunction thisFunction;
-    RootedObject baseobj;
-    Vector<TypeNewScript::Initializer> initializerList;
-    Vector<jsid> accessedProperties;
-
-    NewScriptPropertiesState(JSContext *cx)
-      : thisFunction(cx), baseobj(cx), initializerList(cx), accessedProperties(cx)
-    {}
-};
-
-} /* anonymous namespace */
-
-static bool
-AnalyzePoppedThis(JSContext *cx, SSAUseChain *use,
-                  TypeObject *type, JSFunction *fun, NewScriptPropertiesState &state);
-
-static bool
-AnalyzeNewScriptProperties(JSContext *cx, TypeObject *type, HandleFunction fun,
-                           NewScriptPropertiesState &state)
-{
-    /*
-     * When invoking 'new' on the specified script, try to find some properties
-     * which will definitely be added to the created object before it has a
-     * chance to escape and be accessed elsewhere.
-     *
-     * Returns true if the entire script was analyzed (pbaseobj has been
-     * preserved), false if we had to bail out part way through (pbaseobj may
-     * have been cleared).
-     */
-
-    if (state.initializerList.length() > 50) {
-        /*
-         * Bail out on really long initializer lists (far longer than maximum
-         * number of properties we can track), we may be recursing.
-         */
-        return false;
-    }
-
-    RootedScript script(cx, fun->getOrCreateScript(cx));
-    if (!script)
-        return false;
-
-    if (!script->ensureRanAnalysis(cx) || !script->ensureRanInference(cx)) {
-        state.baseobj = NULL;
-        cx->compartment()->types.setPendingNukeTypes(cx);
-        return false;
-    }
-
-    ScriptAnalysis *analysis = script->analysis();
-
-    /*
-     * Offset of the last bytecode which popped 'this' and which we have
-     * processed. To support compound inline assignments to properties like
-     * 'this.f = (this.g = ...)'  where multiple 'this' values are pushed
-     * and popped en masse, we keep a stack of 'this' values that have yet to
-     * be processed. If a 'this' is pushed before the previous 'this' value
-     * was popped, we defer processing it until we see a 'this' that is popped
-     * after the previous 'this' was popped, i.e. the end of the compound
-     * inline assignment, or we encounter a return from the script.
-     */
-    Vector<SSAUseChain *> pendingPoppedThis(cx);
-
-    unsigned nextOffset = 0;
-    while (nextOffset < script->length) {
-        unsigned offset = nextOffset;
-        jsbytecode *pc = script->code + offset;
-
-        JSOp op = JSOp(*pc);
-
-        nextOffset += GetBytecodeLength(pc);
-
-        Bytecode *code = analysis->maybeCode(pc);
-        if (!code)
-            continue;
-
-        /*
-         * If offset >= the offset at the top of the pending stack, we either
-         * encountered the end of a compound inline assignment or a 'this' was
-         * immediately popped and used. In either case, handle the uses
-         * consumed before the current offset.
-         */
-        while (!pendingPoppedThis.empty() && offset >= pendingPoppedThis.back()->offset) {
-            SSAUseChain *use = pendingPoppedThis.popCopy();
-            if (!AnalyzePoppedThis(cx, use, type, fun, state))
-                return false;
-        }
-
-        /*
-         * End analysis after the first return statement from the script,
-         * returning success if the return is unconditional.
-         */
-        if (op == JSOP_RETURN || op == JSOP_STOP || op == JSOP_RETRVAL)
-            return code->unconditional;
-
-        /* 'this' can escape through a call to eval. */
-        if (op == JSOP_EVAL)
-            return false;
-
-        /*
-         * We are only interested in places where 'this' is popped. The new
-         * 'this' value cannot escape and be accessed except through such uses.
-         */
-        if (op != JSOP_THIS)
-            continue;
-
-        SSAValue thisv = SSAValue::PushedValue(offset, 0);
-        SSAUseChain *uses = analysis->useChain(thisv);
-
-        JS_ASSERT(uses);
-        if (uses->next || !uses->popped) {
-            /* 'this' value popped in more than one place. */
-            return false;
-        }
-
-        /* Only handle 'this' values popped in unconditional code. */
-        Bytecode *poppedCode = analysis->maybeCode(uses->offset);
-        if (!poppedCode || !poppedCode->unconditional)
-            return false;
-
-        if (!pendingPoppedThis.append(uses))
-            return false;
-    }
-
-    /* Will have hit a STOP or similar, unless the script always throws. */
-    return true;
-}
-
-static bool
-AnalyzePoppedThis(JSContext *cx, SSAUseChain *use,
-                  TypeObject *type, JSFunction *fun, NewScriptPropertiesState &state)
-{
-    RootedScript script(cx, fun->nonLazyScript());
-    ScriptAnalysis *analysis = script->analysis();
-
-    jsbytecode *pc = script->code + use->offset;
-    JSOp op = JSOp(*pc);
-
-    if (op == JSOP_SETPROP && use->u.which == 1) {
-        /*
-         * Don't use GetAtomId here, we need to watch for SETPROP on
-         * integer properties and bail out. We can't mark the aggregate
-         * JSID_VOID type property as being in a definite slot.
-         */
-        RootedId id(cx, NameToId(script->getName(GET_UINT32_INDEX(pc))));
-        if (IdToTypeId(id) != id)
-            return false;
-        if (id_prototype(cx) == id || id___proto__(cx) == id || id_constructor(cx) == id)
-            return false;
-
-        /*
-         * Don't add definite properties for properties that were already
-         * read in the constructor.
-         */
-        for (size_t i = 0; i < state.accessedProperties.length(); i++) {
-            if (state.accessedProperties[i] == id)
-                return false;
+void
+types::AddClearDefiniteFunctionUsesInScript(JSContext *cx, TypeObject *type,
+                                            JSScript *script, JSScript *calleeScript)
+{
+    // Look for any uses of the specified calleeScript in type sets for
+    // |script|, and add constraints to ensure that if the type sets' contents
+    // change then the definite properties are cleared from the type.
+    // This ensures that the inlining performed when the definite properties
+    // analysis was done is stable.
+
+    TypeObjectKey *calleeKey = Type::ObjectType(calleeScript->function()).objectKey();
+
+    unsigned count = TypeScript::NumTypeSets(script);
+    TypeSet *typeArray = script->types->typeArray();
+
+    for (unsigned i = 0; i < count; i++) {
+        TypeSet *types = &typeArray[i];
+        if (types->getObjectCount() == 1) {
+            if (calleeKey != types->getObject(0)) {
+                // Also check if the object is the Function.call or
+                // Function.apply native. IonBuilder uses the presence of these
+                // functions during inlining.
+                JSObject *singleton = types->getSingleObject(0);
+                if (!singleton || !singleton->is<JSFunction>())
+                    continue;
+                JSFunction *fun = &singleton->as<JSFunction>();
+                if (!fun->isNative())
+                    continue;
+                if (fun->native() != js_fun_call && fun->native() != js_fun_apply)
+                    continue;
+            }
+            // This is a type set that might have been used when inlining
+            // |calleeScript| into |script|.
+            types->add(cx,
+                cx->analysisLifoAlloc().new_<TypeConstraintClearDefiniteSingle>(type));
         }
-
-        if (!AddClearDefiniteGetterSetterForPrototypeChain(cx, type, id))
-            return false;
-
-        unsigned slotSpan = state.baseobj->slotSpan();
-        RootedValue value(cx, UndefinedValue());
-        if (!DefineNativeProperty(cx, state.baseobj, id, value, NULL, NULL,
-                                  JSPROP_ENUMERATE, 0, 0, DNP_SKIP_TYPE))
-        {
-            cx->compartment()->types.setPendingNukeTypes(cx);
-            state.baseobj = NULL;
-            return false;
-        }
-
-        if (state.baseobj->inDictionaryMode()) {
-            state.baseobj = NULL;
-            return false;
-        }
-
-        if (state.baseobj->slotSpan() == slotSpan) {
-            /* Set a duplicate property. */
-            return false;
-        }
-
-        TypeNewScript::Initializer setprop(TypeNewScript::Initializer::SETPROP, use->offset);
-        if (!state.initializerList.append(setprop)) {
-            cx->compartment()->types.setPendingNukeTypes(cx);
-            state.baseobj = NULL;
-            return false;
-        }
-
-        if (state.baseobj->slotSpan() >= (TYPE_FLAG_DEFINITE_MASK >> TYPE_FLAG_DEFINITE_SHIFT)) {
-            /* Maximum number of definite properties added. */
-            return false;
-        }
-
-        return true;
-    }
-
-    if (op == JSOP_GETPROP && use->u.which == 0) {
-        /*
-         * Properties can be read from the 'this' object if the following hold:
-         *
-         * - The read is not on a getter along the prototype chain, which
-         *   could cause 'this' to escape.
-         *
-         * - The accessed property is either already a definite property or
-         *   is not later added as one. Since the definite properties are
-         *   added to the object at the point of its creation, reading a
-         *   definite property before it is assigned could incorrectly hit.
-         */
-        RootedId id(cx, NameToId(script->getName(GET_UINT32_INDEX(pc))));
-        if (IdToTypeId(id) != id)
-            return false;
-        if (!state.baseobj->nativeLookup(cx, id) && !state.accessedProperties.append(id.get())) {
-            cx->compartment()->types.setPendingNukeTypes(cx);
-            state.baseobj = NULL;
-            return false;
-        }
-
-        if (!AddClearDefiniteGetterSetterForPrototypeChain(cx, type, id))
-            return false;
-
-        /*
-         * Populate the read with any value from the type's proto, if
-         * this is being used in a function call and we need to analyze the
-         * callee's behavior.
-         */
-        Shape *shape = (type->proto && type->proto->isNative())
-                       ? type->proto->nativeLookup(cx, id)
-                       : NULL;
-        if (shape && shape->hasSlot()) {
-            Value protov = type->proto->getSlot(shape->slot());
-            TypeSet *types = TypeScript::BytecodeTypes(script, pc);
-            types->addType(cx, GetValueType(protov));
-        }
-
-        return true;
-    }
-
-    if ((op == JSOP_FUNCALL || op == JSOP_FUNAPPLY) && use->u.which == GET_ARGC(pc) - 1) {
-        /*
-         * Passed as the first parameter to Function.call. Follow control
-         * into the callee, and add any definite properties it assigns to
-         * the object as well. :TODO: This is narrow pattern matching on
-         * the inheritance patterns seen in the v8-deltablue benchmark, and
-         * needs robustness against other ways initialization can cross
-         * script boundaries.
-         *
-         * Add constraints ensuring we are calling Function.call on a
-         * particular script, removing definite properties from the result
-         */
-
-        /* Callee/this must have been pushed by a CALLPROP. */
-        SSAValue calleev = analysis->poppedValue(pc, GET_ARGC(pc) + 1);
-        if (calleev.kind() != SSAValue::PUSHED)
-            return false;
-        jsbytecode *calleepc = script->code + calleev.pushedOffset();
-        if (JSOp(*calleepc) != JSOP_CALLPROP)
-            return false;
-
-        /*
-         * This code may not have run yet, break any type barriers involved
-         * in performing the call (for the greater good!).
-         */
-        if (cx->compartment()->types.compiledInfo.outputIndex == RecompileInfo::NoCompilerRunning) {
-            analysis->breakTypeBarriersSSA(cx, analysis->poppedValue(calleepc, 0));
-            analysis->breakTypeBarriers(cx, calleepc - script->code, true);
-        }
-
-        StackTypeSet *funcallTypes = analysis->poppedTypes(pc, GET_ARGC(pc) + 1);
-        StackTypeSet *scriptTypes = analysis->poppedTypes(pc, GET_ARGC(pc));
-
-        /* Need to definitely be calling Function.call/apply on a specific script. */
-        RootedFunction function(cx);
-        {
-            JSObject *funcallObj = funcallTypes->getSingleton();
-            JSObject *scriptObj = scriptTypes->getSingleton();
-            if (!funcallObj || !funcallObj->is<JSFunction>() ||
-                funcallObj->as<JSFunction>().isInterpreted() ||
-                !scriptObj || !scriptObj->is<JSFunction>() ||
-                !scriptObj->as<JSFunction>().isInterpreted())
-            {
-                return false;
-            }
-            Native native = funcallObj->as<JSFunction>().native();
-            if (native != js_fun_call && native != js_fun_apply)
-                return false;
-            function = &scriptObj->as<JSFunction>();
-        }
-
-        /*
-         * Generate constraints to clear definite properties from the type
-         * should the Function.call or callee itself change in the future.
-         */
-        funcallTypes->add(cx,
-            cx->analysisLifoAlloc().new_<TypeConstraintClearDefiniteSingle>(type));
-        scriptTypes->add(cx,
-            cx->analysisLifoAlloc().new_<TypeConstraintClearDefiniteSingle>(type));
-
-        TypeNewScript::Initializer pushframe(TypeNewScript::Initializer::FRAME_PUSH, use->offset);
-        if (!state.initializerList.append(pushframe)) {
-            cx->compartment()->types.setPendingNukeTypes(cx);
-            state.baseobj = NULL;
-            return false;
-        }
-
-        if (!AnalyzeNewScriptProperties(cx, type, function, state))
-            return false;
-
-        TypeNewScript::Initializer popframe(TypeNewScript::Initializer::FRAME_POP, 0);
-        if (!state.initializerList.append(popframe)) {
-            cx->compartment()->types.setPendingNukeTypes(cx);
-            state.baseobj = NULL;
-            return false;
-        }
-
-        /*
-         * The callee never lets the 'this' value escape, continue looking
-         * for definite properties in the remainder of this script.
-         */
-        return true;
-    }
-
-    /* Unhandled use of 'this'. */
-    return false;
+    }
 }
 
 /*
  * Either make the newScript information for type when it is constructed
  * by the specified script, or regenerate the constraints for an existing
  * newScript on the type after they were cleared by a GC.
  */
 static void
 CheckNewScriptProperties(JSContext *cx, HandleTypeObject type, HandleFunction fun)
 {
+#ifdef JS_ION
     if (type->unknownProperties())
         return;
 
-    NewScriptPropertiesState state(cx);
-    state.thisFunction = fun;
-
     /* Strawman object to add properties to and watch for duplicates. */
-    state.baseobj = NewBuiltinClassInstance(cx, &JSObject::class_, gc::FINALIZE_OBJECT16);
-    if (!state.baseobj) {
-        if (type->addendum)
-            type->clearAddendum(cx);
+    RootedObject baseobj(cx, NewBuiltinClassInstance(cx, &JSObject::class_, gc::FINALIZE_OBJECT16));
+    if (!baseobj)
+        return;
+
+    Vector<TypeNewScript::Initializer> initializerList(cx);
+
+    if (!jit::AnalyzeNewScriptProperties(cx, fun, type, baseobj, &initializerList)) {
         cx->compartment()->types.setPendingNukeTypes(cx);
         return;
     }
 
-    AnalyzeNewScriptProperties(cx, type, fun, state);
-    if (!state.baseobj ||
-        state.baseobj->slotSpan() == 0 ||
+    if (baseobj->slotSpan() == 0 ||
         !!(type->flags & OBJECT_FLAG_ADDENDUM_CLEARED))
     {
         if (type->addendum)
             type->clearAddendum(cx);
         return;
     }
 
     /*
      * If the type already has a new script, we are just regenerating the type
      * constraints and don't need to make another TypeNewScript. Make sure that
      * the properties added to baseobj match the type's definite properties.
      */
     if (type->hasNewScript()) {
-        if (!type->matchDefiniteProperties(state.baseobj))
+        if (!type->matchDefiniteProperties(baseobj))
             type->clearAddendum(cx);
         return;
     }
     JS_ASSERT(!type->addendum);
     JS_ASSERT(!(type->flags & OBJECT_FLAG_ADDENDUM_CLEARED));
 
-    gc::AllocKind kind = gc::GetGCObjectKind(state.baseobj->slotSpan());
+    gc::AllocKind kind = gc::GetGCObjectKind(baseobj->slotSpan());
 
     /* We should not have overflowed the maximum number of fixed slots for an object. */
-    JS_ASSERT(gc::GetGCKindSlots(kind) >= state.baseobj->slotSpan());
+    JS_ASSERT(gc::GetGCKindSlots(kind) >= baseobj->slotSpan());
 
     TypeNewScript::Initializer done(TypeNewScript::Initializer::DONE, 0);
 
     /*
      * The base object may have been created with a different finalize kind
      * than we will use for subsequent new objects. Generate an object with the
      * appropriate final shape.
      */
-    RootedShape shape(cx, state.baseobj->lastProperty());
-    state.baseobj = NewReshapedObject(cx, type, state.baseobj->getParent(), kind, shape);
-    if (!state.baseobj ||
-        !type->addDefiniteProperties(cx, state.baseobj) ||
-        !state.initializerList.append(done)) {
+    RootedShape shape(cx, baseobj->lastProperty());
+    baseobj = NewReshapedObject(cx, type, baseobj->getParent(), kind, shape);
+    if (!baseobj ||
+        !type->addDefiniteProperties(cx, baseobj) ||
+        !initializerList.append(done))
+    {
         cx->compartment()->types.setPendingNukeTypes(cx);
         return;
     }
 
     size_t numBytes = sizeof(TypeNewScript)
-                    + (state.initializerList.length() * sizeof(TypeNewScript::Initializer));
+                    + (initializerList.length() * sizeof(TypeNewScript::Initializer));
     TypeNewScript *newScript;
 #ifdef JSGC_ROOT_ANALYSIS
     // calloc can legitimately return a pointer that appears to be poisoned.
     void *p;
     do {
         p = cx->calloc_(numBytes);
     } while (IsPoisonedPtr(p));
     newScript = (TypeNewScript *) p;
@@ -5297,23 +5008,24 @@ CheckNewScriptProperties(JSContext *cx, 
 
     if (!newScript) {
         cx->compartment()->types.setPendingNukeTypes(cx);
         return;
     }
 
     newScript->fun = fun;
     newScript->allocKind = kind;
-    newScript->shape = state.baseobj->lastProperty();
+    newScript->shape = baseobj->lastProperty();
 
     newScript->initializerList = (TypeNewScript::Initializer *)
         ((char *) newScript + sizeof(TypeNewScript));
     PodCopy(newScript->initializerList,
-            state.initializerList.begin(),
-            state.initializerList.length());
+            initializerList.begin(),
+            initializerList.length());
+#endif // JS_ION
 }
 
 /////////////////////////////////////////////////////////////////////
 // Interface functions
 /////////////////////////////////////////////////////////////////////
 
 void
 types::MarkIteratorUnknownSlow(JSContext *cx)
--- a/js/src/jsinfer.h
+++ b/js/src/jsinfer.h
@@ -740,16 +740,23 @@ TypeSet::toStackTypeSet()
 
 inline HeapTypeSet *
 TypeSet::toHeapTypeSet()
 {
     JS_ASSERT(!constraintsPurged());
     return (HeapTypeSet *) this;
 }
 
+bool
+AddClearDefiniteGetterSetterForPrototypeChain(JSContext *cx, TypeObject *type, jsid id);
+
+void
+AddClearDefiniteFunctionUsesInScript(JSContext *cx, TypeObject *type,
+                                     JSScript *script, JSScript *calleeScript);
+
 /*
  * Handler which persists information about dynamic types pushed within a
  * script which can affect its behavior and are not covered by JOF_TYPESET ops,
  * such as integer operations which overflow to a double. These persist across
  * GCs, and are used to re-seed script types when they are reanalyzed.
  */
 struct TypeResult
 {
@@ -927,24 +934,24 @@ struct TypeNewScript : public TypeObject
      */
     HeapPtrShape  shape;
 
     /*
      * Order in which properties become initialized. We need this in case a
      * scripted setter is added to one of the object's prototypes while it is
      * in the middle of being initialized, so we can walk the stack and fixup
      * any objects which look for in-progress objects which were prematurely
-     * set with their final shape. Initialization can traverse stack frames,
-     * in which case FRAME_PUSH/FRAME_POP are used.
+     * set with their final shape. Property assignments in inner frames are
+     * preceded by a series of SETPROP_FRAME entries specifying the stack down
+     * to the frame containing the write.
      */
     struct Initializer {
         enum Kind {
             SETPROP,
-            FRAME_PUSH,
-            FRAME_POP,
+            SETPROP_FRAME,
             DONE
         } kind;
         uint32_t offset;
         Initializer(Kind kind, uint32_t offset)
           : kind(kind), offset(offset)
         {}
     };
     Initializer *initializerList;