Bug 928776 - Create template call and decl env objects in baseline, r=jandem.
authorBrian Hackett <bhackett1024@gmail.com>
Wed, 23 Oct 2013 08:01:41 -0600
changeset 151863 f6801c7e6500efa91a05b3cb696b503a2b3b87da
parent 151862 dddecb33b3372783a61abf7f5b24cc9bafb76f6d
child 151864 eef41cc0b232890e7f5f3671f325a8bc471d4fdb
push id25512
push usercbook@mozilla.com
push dateThu, 24 Oct 2013 05:06:01 +0000
treeherderautoland@19fd3388c372 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjandem
bugs928776
milestone27.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 928776 - Create template call and decl env objects in baseline, r=jandem.
js/src/jit/BaselineCompiler.cpp
js/src/jit/BaselineInspector.cpp
js/src/jit/BaselineInspector.h
js/src/jit/BaselineJIT.cpp
js/src/jit/BaselineJIT.h
js/src/jit/IonBuilder.cpp
js/src/jit/IonBuilder.h
js/src/jit/MIR.h
js/src/jit/VMFunctions.cpp
js/src/vm/ScopeObject.cpp
js/src/vm/ScopeObject.h
--- a/js/src/jit/BaselineCompiler.cpp
+++ b/js/src/jit/BaselineCompiler.cpp
@@ -82,41 +82,57 @@ BaselineCompiler::compile()
     if (script->argumentsHasVarBinding()) {
         if (!script->ensureRanAnalysis(cx))
             return Method_Error;
     }
 
     // Pin analysis info during compilation.
     types::AutoEnterAnalysis autoEnterAnalysis(cx);
 
+    JS_ASSERT(!script->hasBaselineScript());
+
     if (!emitPrologue())
         return Method_Error;
 
     MethodStatus status = emitBody();
     if (status != Method_Compiled)
         return status;
 
-
     if (!emitEpilogue())
         return Method_Error;
 
 #ifdef JSGC_GENERATIONAL
     if (!emitOutOfLinePostBarrierSlot())
         return Method_Error;
 #endif
 
     if (masm.oom())
         return Method_Error;
 
     Linker linker(masm);
     IonCode *code = linker.newCode(cx, JSC::BASELINE_CODE);
     if (!code)
         return Method_Error;
 
-    JS_ASSERT(!script->hasBaselineScript());
+    JSObject *templateScope = nullptr;
+    if (script->function()) {
+        RootedFunction fun(cx, script->function());
+        if (fun->isHeavyweight()) {
+            templateScope = CallObject::createTemplateObject(cx, script, gc::TenuredHeap);
+            if (!templateScope)
+                return Method_Error;
+
+            if (fun->isNamedLambda()) {
+                RootedObject declEnvObject(cx, DeclEnvObject::createTemplateObject(cx, fun, gc::TenuredHeap));
+                if (!declEnvObject)
+                    return Method_Error;
+                templateScope->as<ScopeObject>().setEnclosingScope(declEnvObject);
+            }
+        }
+    }
 
     // Encode the pc mapping table. See PCMappingIndexEntry for
     // more information.
     Vector<PCMappingIndexEntry> pcMappingIndexEntries(cx);
     CompactBufferWriter pcEntries;
     uint32_t previousOffset = 0;
 
     for (size_t i = 0; i < pcMappingEntries_.length(); i++) {
@@ -163,16 +179,17 @@ BaselineCompiler::compile()
                                                          icEntries_.length(),
                                                          pcMappingIndexEntries.length(),
                                                          pcEntries.length(),
                                                          bytecodeTypeMapEntries);
     if (!baselineScript)
         return Method_Error;
 
     baselineScript->setMethod(code);
+    baselineScript->setTemplateScope(templateScope);
 
     script->setBaselineScript(baselineScript);
 
     IonSpew(IonSpew_BaselineScripts, "Created BaselineScript %p (raw %p) for %s:%d",
             (void *) script->baselineScript(), (void *) code->raw(),
             script->filename(), script->lineno);
 
 #ifdef JS_ION_PERF
--- a/js/src/jit/BaselineInspector.cpp
+++ b/js/src/jit/BaselineInspector.cpp
@@ -418,8 +418,26 @@ BaselineInspector::getTemplateObjectForN
     const ICEntry &entry = icEntryFromPC(pc);
     for (ICStub *stub = entry.firstStub(); stub; stub = stub->next()) {
         if (stub->isCall_Native() && stub->toCall_Native()->callee()->native() == native)
             return stub->toCall_Native()->templateObject();
     }
 
     return NULL;
 }
+
+DeclEnvObject *
+BaselineInspector::templateDeclEnvObject()
+{
+    JSObject *res = &templateCallObject()->as<ScopeObject>().enclosingScope();
+    JS_ASSERT(res);
+
+    return &res->as<DeclEnvObject>();
+}
+
+CallObject *
+BaselineInspector::templateCallObject()
+{
+    JSObject *res = baselineScript()->templateScope();
+    JS_ASSERT(res);
+
+    return &res->as<CallObject>();
+}
--- a/js/src/jit/BaselineInspector.h
+++ b/js/src/jit/BaselineInspector.h
@@ -108,16 +108,19 @@ class BaselineInspector
     bool hasSeenNonNativeGetElement(jsbytecode *pc);
     bool hasSeenNegativeIndexGetElement(jsbytecode *pc);
     bool hasSeenAccessedGetter(jsbytecode *pc);
     bool hasSeenDoubleResult(jsbytecode *pc);
     bool hasSeenNonStringIterNext(jsbytecode *pc);
 
     JSObject *getTemplateObject(jsbytecode *pc);
     JSObject *getTemplateObjectForNative(jsbytecode *pc, Native native);
+
+    DeclEnvObject *templateDeclEnvObject();
+    CallObject *templateCallObject();
 };
 
 } // namespace jit
 } // namespace js
 
 #endif // JS_ION
 
 #endif /* jit_BaselineInspector_h */
--- a/js/src/jit/BaselineJIT.cpp
+++ b/js/src/jit/BaselineJIT.cpp
@@ -35,16 +35,17 @@ PCMappingSlotInfo::ToSlotLocation(const 
         return SlotInR1;
     }
     JS_ASSERT(stackVal->kind() != StackValue::Stack);
     return SlotIgnore;
 }
 
 BaselineScript::BaselineScript(uint32_t prologueOffset, uint32_t spsPushToggleOffset)
   : method_(nullptr),
+    templateScope_(nullptr),
     fallbackStubSpace_(),
     prologueOffset_(prologueOffset),
 #ifdef DEBUG
     spsOn_(false),
 #endif
     spsPushToggleOffset_(spsPushToggleOffset),
     flags_(0)
 { }
@@ -406,16 +407,18 @@ BaselineScript::New(JSContext *cx, uint3
 
     return script;
 }
 
 void
 BaselineScript::trace(JSTracer *trc)
 {
     MarkIonCode(trc, &method_, "baseline-method");
+    if (templateScope_)
+        MarkObject(trc, &templateScope_, "baseline-template-scope");
 
     // Mark all IC stub codes hanging off the IC stub entries.
     for (size_t i = 0; i < numICEntries(); i++) {
         ICEntry &ent = icEntry(i);
         if (!ent.hasStub())
             continue;
         for (ICStub *stub = ent.firstStub(); stub; stub = stub->next())
             stub->trace(trc);
--- a/js/src/jit/BaselineJIT.h
+++ b/js/src/jit/BaselineJIT.h
@@ -99,16 +99,20 @@ struct BaselineScript
 {
   public:
     static const uint32_t MAX_JSSCRIPT_LENGTH = 0x0fffffffu;
 
   private:
     // Code pointer containing the actual method.
     HeapPtr<IonCode> method_;
 
+    // For heavyweight scripts, template objects to use for the call object and
+    // decl env object (linked via the call object's enclosing scope).
+    HeapPtrObject templateScope_;
+
     // Allocated space for fallback stubs.
     FallbackICStubSpace fallbackStubSpace_;
 
     // Native code offset right before the scope chain is initialized.
     uint32_t prologueOffset_;
 
     // The offsets for the toggledJump instructions for SPS update ICs.
 #ifdef DEBUG
@@ -220,16 +224,24 @@ struct BaselineScript
     IonCode *method() const {
         return method_;
     }
     void setMethod(IonCode *code) {
         JS_ASSERT(!method_);
         method_ = code;
     }
 
+    JSObject *templateScope() const {
+        return templateScope_;
+    }
+    void setTemplateScope(JSObject *templateScope) {
+        JS_ASSERT(!templateScope_);
+        templateScope_ = templateScope;
+    }
+
     void toggleBarriers(bool enabled) {
         method()->togglePreBarriers(enabled);
     }
 
     ICEntry &icEntry(size_t index);
     ICEntry *maybeICEntryFromReturnOffset(CodeOffsetLabel returnOffset);
     ICEntry &icEntryFromReturnOffset(CodeOffsetLabel returnOffset);
     ICEntry &icEntryFromPCOffset(uint32_t pcOffset);
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -4520,30 +4520,19 @@ IonBuilder::inlineCalls(CallInfo &callIn
     graph().moveBlockToEnd(returnBlock);
     setCurrentAndSpecializePhis(returnBlock);
     return true;
 }
 
 MInstruction *
 IonBuilder::createDeclEnvObject(MDefinition *callee, MDefinition *scope)
 {
-    // Create a template CallObject that we'll use to generate inline object
-    // creation. Even though this template will get discarded at the end of
-    // compilation, it is used by the background compilation thread and thus
-    // cannot use the Nursery.
-
-    RootedFunction fun(cx, info().fun());
-    RootedObject templateObj(cx, DeclEnvObject::createTemplateObject(cx, fun, gc::TenuredHeap));
-    if (!templateObj)
-        return nullptr;
-
-    // Add dummy values on the slot of the template object such as we do not try
-    // mark uninitialized values.
-    templateObj->setFixedSlot(DeclEnvObject::enclosingScopeSlot(), MagicValue(JS_GENERIC_MAGIC));
-    templateObj->setFixedSlot(DeclEnvObject::lambdaSlot(), MagicValue(JS_GENERIC_MAGIC));
+    // Get a template CallObject that we'll use to generate inline object
+    // creation.
+    DeclEnvObject *templateObj = inspector->templateDeclEnvObject();
 
     // One field is added to the function to handle its name.  This cannot be a
     // dynamic slot because there is still plenty of room on the DeclEnv object.
     JS_ASSERT(!templateObj->hasDynamicSlots());
 
     // Allocate the actual object. It is important that no intervening
     // instructions could potentially bailout, thus leaking the dynamic slots
     // pointer.
@@ -4558,25 +4547,19 @@ IonBuilder::createDeclEnvObject(MDefinit
     current->add(MStoreFixedSlot::New(declEnvObj, DeclEnvObject::lambdaSlot(), callee));
 
     return declEnvObj;
 }
 
 MInstruction *
 IonBuilder::createCallObject(MDefinition *callee, MDefinition *scope)
 {
-    // Create a template CallObject that we'll use to generate inline object
-    // creation. Even though this template will get discarded at the end of
-    // compilation, it is used by the background compilation thread and thus
-    // cannot use the Nursery.
-
-    RootedScript scriptRoot(cx, script());
-    RootedObject templateObj(cx, CallObject::createTemplateObject(cx, scriptRoot, gc::TenuredHeap));
-    if (!templateObj)
-        return nullptr;
+    // Get a template CallObject that we'll use to generate inline object
+    // creation.
+    CallObject *templateObj = inspector->templateCallObject();
 
     // If the CallObject needs dynamic slots, allocate those now.
     MInstruction *slots;
     if (templateObj->hasDynamicSlots()) {
         size_t nslots = JSObject::dynamicSlotsCount(templateObj->numFixedSlots(),
                                                     templateObj->slotSpan());
         slots = MNewSlots::New(nslots);
     } else {
@@ -4626,21 +4609,21 @@ IonBuilder::createThisScripted(MDefiniti
     // - First try an idempotent property cache.
     // - Upon failing idempotent property cache, we can't use a non-idempotent cache,
     //   therefore we fallback to CallGetProperty
     //
     // Note: both CallGetProperty and GetPropertyCache can trigger a GC,
     //       and thus invalidation.
     MInstruction *getProto;
     if (!invalidatedIdempotentCache()) {
-        MGetPropertyCache *getPropCache = MGetPropertyCache::New(callee, cx->names().prototype);
+        MGetPropertyCache *getPropCache = MGetPropertyCache::New(callee, names().prototype);
         getPropCache->setIdempotent();
         getProto = getPropCache;
     } else {
-        MCallGetProperty *callGetProp = MCallGetProperty::New(callee, cx->names().prototype,
+        MCallGetProperty *callGetProp = MCallGetProperty::New(callee, names().prototype,
                                                               /* callprop = */ false);
         callGetProp->setIdempotent();
         getProto = callGetProp;
     }
     current->add(getProto);
 
     // Create this from prototype
     MCreateThisWithProto *createThis = MCreateThisWithProto::New(callee, getProto);
@@ -4653,17 +4636,17 @@ JSObject *
 IonBuilder::getSingletonPrototype(JSFunction *target)
 {
     if (!target || !target->hasSingletonType())
         return nullptr;
     types::TypeObjectKey *targetType = types::TypeObjectKey::get(target);
     if (targetType->unknownProperties())
         return nullptr;
 
-    jsid protoid = NameToId(cx->names().prototype);
+    jsid protoid = NameToId(names().prototype);
     types::HeapTypeSetKey protoProperty = targetType->property(protoid);
 
     return protoProperty.singleton(constraints());
 }
 
 MDefinition *
 IonBuilder::createThisScriptedSingleton(JSFunction *target, MDefinition *callee)
 {
@@ -6211,22 +6194,22 @@ IonBuilder::getStaticName(JSObject *stat
     jsid id = NameToId(name);
 
     JS_ASSERT(staticObject->is<GlobalObject>() || staticObject->is<CallObject>());
 
     *psucceeded = true;
 
     if (staticObject->is<GlobalObject>()) {
         // Optimize undefined, NaN, and Infinity.
-        if (name == cx->names().undefined)
+        if (name == names().undefined)
             return pushConstant(UndefinedValue());
-        if (name == cx->names().NaN)
-            return pushConstant(cx->runtime()->NaNValue);
-        if (name == cx->names().Infinity)
-            return pushConstant(cx->runtime()->positiveInfinityValue);
+        if (name == names().NaN)
+            return pushConstant(compartment->runtimeFromAnyThread()->NaNValue);
+        if (name == names().Infinity)
+            return pushConstant(compartment->runtimeFromAnyThread()->positiveInfinityValue);
     }
 
     // For the fastest path, the property must be found, and it must be found
     // as a normal data property on exactly the global object.
     Shape *shape = staticObject->nativeLookup(cx, id);
     if (!shape || !shape->hasDefaultGetter() || !shape->hasSlot()) {
         *psucceeded = false;
         return true;
@@ -9443,17 +9426,17 @@ IonBuilder::jsop_instanceof()
         if (!rhsObject || !rhsObject->is<JSFunction>() || rhsObject->isBoundFunction())
             break;
 
         types::TypeObjectKey *rhsType = types::TypeObjectKey::get(rhsObject);
         if (rhsType->unknownProperties())
             break;
 
         types::HeapTypeSetKey protoProperty =
-            rhsType->property(NameToId(cx->names().prototype));
+            rhsType->property(NameToId(names().prototype));
         JSObject *protoObject = protoProperty.singleton(constraints());
         if (!protoObject)
             break;
 
         rhs->setFoldedUnchecked();
 
         MInstanceOf *ins = new MInstanceOf(obj, protoObject);
 
--- a/js/src/jit/IonBuilder.h
+++ b/js/src/jit/IonBuilder.h
@@ -692,16 +692,18 @@ class IonBuilder : public MIRGenerator
         // thread, which after bug 785905 will only occur when doing eager
         // analyses with no available baseline information. Until this bug is
         // completed, both the |cx| member and |context()| may be used.
         if (info().executionMode() == DefinitePropertiesAnalysis)
             return cx;
         return NULL;
     }
 
+    JSAtomState &names() { return compartment->runtimeFromAnyThread()->atomState; }
+
   private:
     bool init();
 
     JSContext *cx;
     BaselineFrame *baselineFrame_;
     AbortReason abortReason_;
     ScopedJSDeletePtr<TypeRepresentationSetHash> reprSetHash_;
 
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -8261,55 +8261,55 @@ class MNewSlots : public MNullaryInstruc
         return true;
     }
 };
 
 class MNewDeclEnvObject : public MNullaryInstruction
 {
     CompilerRootObject templateObj_;
 
-    MNewDeclEnvObject(HandleObject templateObj)
+    MNewDeclEnvObject(JSObject *templateObj)
       : MNullaryInstruction(),
         templateObj_(templateObj)
     {
         setResultType(MIRType_Object);
     }
 
   public:
     INSTRUCTION_HEADER(NewDeclEnvObject);
 
-    static MNewDeclEnvObject *New(HandleObject templateObj) {
+    static MNewDeclEnvObject *New(JSObject *templateObj) {
         return new MNewDeclEnvObject(templateObj);
     }
 
     JSObject *templateObj() {
         return templateObj_;
     }
     AliasSet getAliasSet() const {
         return AliasSet::None();
     }
 };
 
 class MNewCallObject : public MUnaryInstruction
 {
     CompilerRootObject templateObj_;
     bool needsSingletonType_;
 
-    MNewCallObject(HandleObject templateObj, bool needsSingletonType, MDefinition *slots)
+    MNewCallObject(JSObject *templateObj, bool needsSingletonType, MDefinition *slots)
       : MUnaryInstruction(slots),
         templateObj_(templateObj),
         needsSingletonType_(needsSingletonType)
     {
         setResultType(MIRType_Object);
     }
 
   public:
     INSTRUCTION_HEADER(NewCallObject)
 
-    static MNewCallObject *New(HandleObject templateObj, bool needsSingletonType, MDefinition *slots) {
+    static MNewCallObject *New(JSObject *templateObj, bool needsSingletonType, MDefinition *slots) {
         return new MNewCallObject(templateObj, needsSingletonType, slots);
     }
 
     MDefinition *slots() {
         return getOperand(0);
     }
     JSObject *templateObject() {
         return templateObj_;
--- a/js/src/jit/VMFunctions.cpp
+++ b/js/src/jit/VMFunctions.cpp
@@ -486,16 +486,18 @@ NewSlots(JSRuntime *rt, unsigned nslots)
     return reinterpret_cast<HeapSlot *>(slots);
 }
 
 JSObject *
 NewCallObject(JSContext *cx, HandleScript script,
               HandleShape shape, HandleTypeObject type, HeapSlot *slots)
 {
     JSObject *obj = CallObject::create(cx, script, shape, type, slots);
+    if (!obj)
+        return nullptr;
 
 #ifdef JSGC_GENERATIONAL
     // The JIT creates call objects in the nursery, so elides barriers for
     // the initializing writes. The interpreter, however, may have allocated
     // the call object tenured, so barrier as needed before re-entering.
     if (!IsInsideNursery(cx->runtime(), obj))
         cx->runtime()->gcStoreBuffer.putWholeCell(obj);
 #endif
--- a/js/src/vm/ScopeObject.cpp
+++ b/js/src/vm/ScopeObject.cpp
@@ -160,17 +160,17 @@ js::ScopeCoordinateFunctionScript(JSCont
     }
     if (ssi.type() != StaticScopeIter::FUNCTION)
         return nullptr;
     return ssi.funScript();
 }
 
 /*****************************************************************************/
 
-inline void
+void
 ScopeObject::setEnclosingScope(HandleObject obj)
 {
     JS_ASSERT_IF(obj->is<CallObject>() || obj->is<DeclEnvObject>() || obj->is<BlockObject>(),
                  obj->isDelegate());
     setFixedSlot(SCOPE_CHAIN_SLOT, ObjectValue(*obj));
 }
 
 /*
--- a/js/src/vm/ScopeObject.h
+++ b/js/src/vm/ScopeObject.h
@@ -166,17 +166,17 @@ class ScopeObject : public JSObject
      * Since every scope chain terminates with a global object and GlobalObject
      * does not derive ScopeObject (it has a completely different layout), the
      * enclosing scope of a ScopeObject is necessarily non-null.
      */
     inline JSObject &enclosingScope() const {
         return getReservedSlot(SCOPE_CHAIN_SLOT).toObject();
     }
 
-    inline void setEnclosingScope(HandleObject obj);
+    void setEnclosingScope(HandleObject obj);
 
     /*
      * Get or set an aliased variable contained in this scope. Unaliased
      * variables should instead access the StackFrame. Aliased variable access
      * is primarily made through JOF_SCOPECOORD ops which is why these members
      * take a ScopeCoordinate instead of just the slot index.
      */
     inline const Value &aliasedVar(ScopeCoordinate sc);