Bug 1185106 - Part 0.1: Refactor JSOP_DEFFUN. r=efaust,jandem,till,h4writer draft
authorMariusz Kierski <mkierski@mozilla.com>
Sun, 17 Jul 2016 10:22:33 +0900
changeset 430921 d1ec57e1c6f0fdbb374b6eae36c1c710faecfd1b
parent 430701 944cb0fd05526894fcd90fbe7d1e625ee53cd73d
child 430922 5d3dbb908bfad2adbbc3d3dfcb99099e11f68a02
push id33945
push userarai_a@mac.com
push dateFri, 28 Oct 2016 11:34:02 +0000
reviewersefaust, jandem, till, h4writer
bugs1185106
milestone52.0a1
Bug 1185106 - Part 0.1: Refactor JSOP_DEFFUN. r=efaust,jandem,till,h4writer MozReview-Commit-ID: 8XpAiHEzWVm
js/src/frontend/BytecodeEmitter.cpp
js/src/jit/BaselineCompiler.cpp
js/src/jit/CodeGenerator.cpp
js/src/jit/IonBuilder.cpp
js/src/jit/Lowering.cpp
js/src/jit/MIR.h
js/src/jit/shared/LIR-shared.h
js/src/vm/Interpreter.cpp
js/src/vm/Opcodes.h
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -6937,16 +6937,23 @@ BytecodeEmitter::emitFunction(ParseNode*
                     return false;
             }
         }
 
         if (needsProto) {
             MOZ_ASSERT(pn->getOp() == JSOP_FUNWITHPROTO || pn->getOp() == JSOP_LAMBDA);
             pn->setOp(JSOP_FUNWITHPROTO);
         }
+
+        if (pn->getOp() == JSOP_DEFFUN) {
+            if (!emitIndex32(JSOP_LAMBDA, index))
+                return false;
+            return emit1(JSOP_DEFFUN);
+        }
+
         return emitIndex32(pn->getOp(), index);
     }
 
     MOZ_ASSERT(!needsProto);
 
     bool topLevelFunction;
     if (sc->isFunctionBox() || (sc->isEvalContext() && sc->strict())) {
         // No nested functions inside other functions are top-level.
@@ -6967,17 +6974,19 @@ BytecodeEmitter::emitFunction(ParseNode*
 
             RootedModuleObject module(cx, sc->asModuleContext()->module());
             if (!module->noteFunctionDeclaration(cx, name, fun))
                 return false;
         } else {
             MOZ_ASSERT(sc->isGlobalContext() || sc->isEvalContext());
             MOZ_ASSERT(pn->getOp() == JSOP_NOP);
             switchToPrologue();
-            if (!emitIndex32(JSOP_DEFFUN, index))
+            if (!emitIndex32(JSOP_LAMBDA, index))
+                return false;
+            if (!emit1(JSOP_DEFFUN))
                 return false;
             if (!updateSourceCoordNotes(pn->pn_pos.begin))
                 return false;
             switchToMain();
         }
     } else {
         // For functions nested within functions and blocks, make a lambda and
         // initialize the binding name of the function in the current scope.
--- a/js/src/jit/BaselineCompiler.cpp
+++ b/js/src/jit/BaselineCompiler.cpp
@@ -2745,25 +2745,24 @@ BaselineCompiler::emit_JSOP_DEFLET()
 
 typedef bool (*DefFunOperationFn)(JSContext*, HandleScript, HandleObject, HandleFunction);
 static const VMFunction DefFunOperationInfo =
     FunctionInfo<DefFunOperationFn>(DefFunOperation, "DefFunOperation");
 
 bool
 BaselineCompiler::emit_JSOP_DEFFUN()
 {
-    RootedFunction fun(cx, script->getFunction(GET_UINT32_INDEX(pc)));
-
-    frame.syncStack(0);
-    masm.loadPtr(frame.addressOfEnvironmentChain(), R0.scratchReg());
+    frame.popRegsAndSync(1);
+    masm.unboxObject(R0, R0.scratchReg());
+    masm.loadPtr(frame.addressOfEnvironmentChain(), R1.scratchReg());
 
     prepareVMCall();
 
-    pushArg(ImmGCPtr(fun));
     pushArg(R0.scratchReg());
+    pushArg(R1.scratchReg());
     pushArg(ImmGCPtr(script));
 
     return callVM(DefFunOperationInfo);
 }
 
 typedef bool (*InitPropGetterSetterFn)(JSContext*, jsbytecode*, HandleObject, HandlePropertyName,
                                        HandleObject);
 static const VMFunction InitPropGetterSetterInfo =
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -4689,17 +4689,18 @@ typedef bool (*DefFunOperationFn)(JSCont
 static const VMFunction DefFunOperationInfo =
     FunctionInfo<DefFunOperationFn>(DefFunOperation, "DefFunOperation");
 
 void
 CodeGenerator::visitDefFun(LDefFun* lir)
 {
     Register envChain = ToRegister(lir->environmentChain());
 
-    pushArg(ImmGCPtr(lir->mir()->fun()));
+    Register fun = ToRegister(lir->fun());
+    pushArg(fun);
     pushArg(envChain);
     pushArg(ImmGCPtr(current->mir()->info().script()));
 
     callVM(DefFunOperationInfo, lir);
 }
 
 typedef bool (*CheckOverRecursedFn)(JSContext*);
 static const VMFunction CheckOverRecursedInfo =
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -13413,23 +13413,19 @@ IonBuilder::jsop_deflexical(uint32_t ind
     current->add(deflex);
 
     return resumeAfter(deflex);
 }
 
 bool
 IonBuilder::jsop_deffun(uint32_t index)
 {
-    JSFunction* fun = script()->getFunction(index);
-    if (IsAsmJSModule(fun))
-        return abort("asm.js module function");
-
     MOZ_ASSERT(analysis().usesEnvironmentChain());
 
-    MDefFun* deffun = MDefFun::New(alloc(), fun, current->environmentChain());
+    MDefFun* deffun = MDefFun::New(alloc(), current->pop(), current->environmentChain());
     current->add(deffun);
 
     return resumeAfter(deffun);
 }
 
 bool
 IonBuilder::jsop_throwsetconst()
 {
--- a/js/src/jit/Lowering.cpp
+++ b/js/src/jit/Lowering.cpp
@@ -202,17 +202,21 @@ LIRGenerator::visitDefLexical(MDefLexica
     LDefLexical* lir = new(alloc()) LDefLexical();
     add(lir, ins);
     assignSafepoint(lir, ins);
 }
 
 void
 LIRGenerator::visitDefFun(MDefFun* ins)
 {
-    LDefFun* lir = new(alloc()) LDefFun(useRegisterAtStart(ins->environmentChain()));
+    MDefinition* fun = ins->fun();
+    MOZ_ASSERT(fun->type() == MIRType::Object);
+
+    LDefFun* lir = new(alloc()) LDefFun(useRegisterAtStart(fun),
+                                        useRegisterAtStart(ins->environmentChain()));
     add(lir, ins);
     assignSafepoint(lir, ins);
 }
 
 void
 LIRGenerator::visitNewArray(MNewArray* ins)
 {
     LNewArray* lir = new(alloc()) LNewArray(temp());
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -8101,41 +8101,32 @@ class MDefLexical
         return attrs_;
     }
     bool appendRoots(MRootList& roots) const override {
         return roots.append(name_);
     }
 };
 
 class MDefFun
-  : public MUnaryInstruction,
-    public NoTypePolicy::Data
-{
-    CompilerFunction fun_;
-
+  : public MBinaryInstruction,
+    public ObjectPolicy<0>::Data
+{
   private:
-    MDefFun(JSFunction* fun, MDefinition* envChain)
-      : MUnaryInstruction(envChain),
-        fun_(fun)
+    MDefFun(MDefinition* fun, MDefinition* envChain)
+      : MBinaryInstruction(fun, envChain)
     {}
 
   public:
     INSTRUCTION_HEADER(DefFun)
     TRIVIAL_NEW_WRAPPERS
-    NAMED_OPERANDS((0, environmentChain))
-
-    JSFunction* fun() const {
-        return fun_;
-    }
+    NAMED_OPERANDS((0, fun), (1, environmentChain))
+
     bool possiblyCalls() const override {
         return true;
     }
-    bool appendRoots(MRootList& roots) const override {
-        return roots.append(fun_);
-    }
 };
 
 class MRegExp : public MNullaryInstruction
 {
     CompilerGCPointer<RegExpObject*> source_;
     bool mustClone_;
 
     MRegExp(CompilerConstraintList* constraints, RegExpObject* source)
--- a/js/src/jit/shared/LIR-shared.h
+++ b/js/src/jit/shared/LIR-shared.h
@@ -1545,28 +1545,32 @@ class LDefLexical : public LCallInstruct
   public:
     LIR_HEADER(DefLexical)
 
     MDefLexical* mir() const {
         return mir_->toDefLexical();
     }
 };
 
-class LDefFun : public LCallInstructionHelper<0, 1, 0>
+class LDefFun : public LCallInstructionHelper<0, 2, 0>
 {
   public:
     LIR_HEADER(DefFun)
 
-    explicit LDefFun(const LAllocation& envChain)
-    {
-        setOperand(0, envChain);
-    }
-
+    LDefFun(const LAllocation& fun, const LAllocation& envChain)
+    {
+        setOperand(0, fun);
+        setOperand(1, envChain);
+    }
+
+    const LAllocation* fun() {
+        return getOperand(0);
+    }
     const LAllocation* environmentChain() {
-        return getOperand(0);
+        return getOperand(1);
     }
     MDefFun* mir() const {
         return mir_->toDefFun();
     }
 };
 
 class LTypeOfV : public LInstructionHelper<1, BOX_PIECES, 1>
 {
--- a/js/src/vm/Interpreter.cpp
+++ b/js/src/vm/Interpreter.cpp
@@ -3441,19 +3441,20 @@ END_CASE(JSOP_DEFLET)
 CASE(JSOP_DEFFUN)
 {
     /*
      * A top-level function defined in Global or Eval code (see ECMA-262
      * Ed. 3), or else a SpiderMonkey extension: a named function statement in
      * a compound statement (not at the top statement level of global code, or
      * at the top level of a function body).
      */
-    ReservedRooted<JSFunction*> fun(&rootFunction0, script->getFunction(GET_UINT32_INDEX(REGS.pc)));
+    ReservedRooted<JSFunction*> fun(&rootFunction0, &REGS.sp[-1].toObject().as<JSFunction>());
     if (!DefFunOperation(cx, script, REGS.fp()->environmentChain(), fun))
         goto error;
+    REGS.sp--;
 }
 END_CASE(JSOP_DEFFUN)
 
 CASE(JSOP_LAMBDA)
 {
     /* Load the specified function object literal. */
     ReservedRooted<JSFunction*> fun(&rootFunction0, script->getFunction(GET_UINT32_INDEX(REGS.pc)));
     JSObject* obj = Lambda(cx, fun, REGS.fp()->environmentChain());
@@ -4325,37 +4326,18 @@ js::LambdaArrow(JSContext* cx, HandleFun
     clone->as<JSFunction>().setExtendedSlot(0, newTargetv);
 
     MOZ_ASSERT(fun->global() == clone->global());
     return clone;
 }
 
 bool
 js::DefFunOperation(JSContext* cx, HandleScript script, HandleObject envChain,
-                    HandleFunction funArg)
-{
-    /*
-     * If static link is not current scope, clone fun's object to link to the
-     * current scope via parent. We do this to enable sharing of compiled
-     * functions among multiple equivalent scopes, amortizing the cost of
-     * compilation over a number of executions.  Examples include XUL scripts
-     * and event handlers shared among Firefox or other Mozilla app chrome
-     * windows, and user-defined JS functions precompiled and then shared among
-     * requests in server-side JS.
-     */
-    RootedFunction fun(cx, funArg);
-    if (fun->isNative() || fun->environment() != envChain) {
-        fun = CloneFunctionObjectIfNotSingleton(cx, fun, envChain, nullptr, TenuredObject);
-        if (!fun)
-            return false;
-    } else {
-        MOZ_ASSERT(script->treatAsRunOnce());
-        MOZ_ASSERT(!script->functionNonDelazifying());
-    }
-
+                    HandleFunction fun)
+{
     /*
      * We define the function as a property of the variable object and not the
      * current scope chain even for the case of function expression statements
      * and functions defined by eval inside let or with blocks.
      */
     RootedObject parent(cx, envChain);
     while (!parent->isQualifiedVarObj())
         parent = parent->enclosingEnvironment();
--- a/js/src/vm/Opcodes.h
+++ b/js/src/vm/Opcodes.h
@@ -1295,20 +1295,20 @@ 1234567890123456789012345678901234567890
     \
     /*
      * Defines the given function on the current scope.
      *
      * This is used for global scripts and also in some cases for function
      * scripts where use of dynamic scoping inhibits optimization.
      *   Category: Variables and Scopes
      *   Type: Variables
-     *   Operands: uint32_t funcIndex
-     *   Stack: =>
+     *   Operands:
+     *   Stack: fun =>
      */ \
-    macro(JSOP_DEFFUN,    127,"deffun",     NULL,         5,  0,  0,  JOF_OBJECT) \
+    macro(JSOP_DEFFUN,    127,"deffun",     NULL,         1,  1,  0,  JOF_BYTE) \
     /* Defines the new constant binding on global lexical scope.
      *
      * Throws if a binding with the same name already exists on the scope, or
      * if a var binding with the same name exists on the global.
      *   Category: Variables and Scopes
      *   Type: Variables
      *   Operands: uint32_t nameIndex
      *   Stack: =>