DFG does not support compiling functions as constructors
authorfpizlo@apple.com <fpizlo@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 21 Sep 2011 22:17:06 +0000 (22:17 +0000)
committerfpizlo@apple.com <fpizlo@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 21 Sep 2011 22:17:06 +0000 (22:17 +0000)
https://bugs.webkit.org/show_bug.cgi?id=68500

Reviewed by Oliver Hunt.

This adds support for compiling constructors to the DFG. It's a
1% speed-up on V8, mostly due to a 6% speed-up on early-boyer.
It's also a 13% win on access-binary-trees, but it's neutral in
the SunSpider and Kraken averages.

* dfg/DFGByteCodeParser.cpp:
(JSC::DFG::ByteCodeParser::parseBlock):
* dfg/DFGCapabilities.h:
(JSC::DFG::mightCompileFunctionForConstruct):
(JSC::DFG::canCompileOpcode):
* dfg/DFGNode.h:
* dfg/DFGOperations.cpp:
* dfg/DFGOperations.h:
* dfg/DFGPropagator.cpp:
(JSC::DFG::Propagator::propagateNodePredictions):
(JSC::DFG::Propagator::performNodeCSE):
* dfg/DFGSpeculativeJIT.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* runtime/Executable.cpp:
(JSC::FunctionExecutable::compileOptimizedForConstruct):
(JSC::FunctionExecutable::compileForConstructInternal):
* runtime/Executable.h:
(JSC::FunctionExecutable::compileForConstruct):
(JSC::FunctionExecutable::compileFor):
(JSC::FunctionExecutable::compileOptimizedFor):

git-svn-id: http://svn.webkit.org/repository/webkit/trunk@95672 268f45cc-cd09-0410-ab3c-d52691b4dbfc

Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp
Source/JavaScriptCore/dfg/DFGCapabilities.h
Source/JavaScriptCore/dfg/DFGNode.h
Source/JavaScriptCore/dfg/DFGOperations.cpp
Source/JavaScriptCore/dfg/DFGOperations.h
Source/JavaScriptCore/dfg/DFGPropagator.cpp
Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp
Source/JavaScriptCore/runtime/Executable.cpp
Source/JavaScriptCore/runtime/Executable.h

index 772f4dc..7a7f967 100644 (file)
@@ -1,3 +1,36 @@
+2011-09-21  Filip Pizlo  <fpizlo@apple.com>
+
+        DFG does not support compiling functions as constructors
+        https://bugs.webkit.org/show_bug.cgi?id=68500
+
+        Reviewed by Oliver Hunt.
+        
+        This adds support for compiling constructors to the DFG. It's a
+        1% speed-up on V8, mostly due to a 6% speed-up on early-boyer.
+        It's also a 13% win on access-binary-trees, but it's neutral in
+        the SunSpider and Kraken averages.
+
+        * dfg/DFGByteCodeParser.cpp:
+        (JSC::DFG::ByteCodeParser::parseBlock):
+        * dfg/DFGCapabilities.h:
+        (JSC::DFG::mightCompileFunctionForConstruct):
+        (JSC::DFG::canCompileOpcode):
+        * dfg/DFGNode.h:
+        * dfg/DFGOperations.cpp:
+        * dfg/DFGOperations.h:
+        * dfg/DFGPropagator.cpp:
+        (JSC::DFG::Propagator::propagateNodePredictions):
+        (JSC::DFG::Propagator::performNodeCSE):
+        * dfg/DFGSpeculativeJIT.cpp:
+        (JSC::DFG::SpeculativeJIT::compile):
+        * runtime/Executable.cpp:
+        (JSC::FunctionExecutable::compileOptimizedForConstruct):
+        (JSC::FunctionExecutable::compileForConstructInternal):
+        * runtime/Executable.h:
+        (JSC::FunctionExecutable::compileForConstruct):
+        (JSC::FunctionExecutable::compileFor):
+        (JSC::FunctionExecutable::compileOptimizedFor):
+
 2011-09-21  Gavin Barraclough  <barraclough@apple.com>
 
         Replace jsFunctionVPtr compares with a type check on the Structure.
index 2d8b195..0348ad0 100644 (file)
@@ -730,6 +730,17 @@ bool ByteCodeParser::parseBlock(unsigned limit)
             NEXT_OPCODE(op_convert_this);
         }
 
+        case op_create_this: {
+            NodeIndex op1 = get(currentInstruction[2].u.operand);
+            set(currentInstruction[1].u.operand, addToGraph(CreateThis, op1));
+            NEXT_OPCODE(op_create_this);
+        }
+            
+        case op_get_callee: {
+            set(currentInstruction[1].u.operand, addToGraph(GetCallee));
+            NEXT_OPCODE(op_get_callee);
+        }
+
         // === Bitwise operations ===
 
         case op_bitand: {
index e4553a7..74ade7d 100644 (file)
@@ -37,7 +37,7 @@ namespace JSC { namespace DFG {
 inline bool mightCompileEval(CodeBlock*) { return true; }
 inline bool mightCompileProgram(CodeBlock*) { return true; }
 inline bool mightCompileFunctionForCall(CodeBlock*) { return true; }
-inline bool mightCompileFunctionForConstruct(CodeBlock*) { return false; }
+inline bool mightCompileFunctionForConstruct(CodeBlock*) { return true; }
 
 // Opcode checking.
 inline bool canCompileOpcode(OpcodeID opcodeID)
@@ -45,6 +45,8 @@ inline bool canCompileOpcode(OpcodeID opcodeID)
     switch (opcodeID) {
     case op_enter:
     case op_convert_this:
+    case op_create_this:
+    case op_get_callee:
     case op_bitand:
     case op_bitor:
     case op_bitxor:
index b567f47..da11a0d 100644 (file)
@@ -211,7 +211,11 @@ static inline const char* arithNodeFlagsAsString(ArithNodeFlags flags)
 #define FOR_EACH_DFG_OP(macro) \
     /* Nodes for constants. */\
     macro(JSConstant, NodeResultJS) \
+    \
+    /* Nodes for handling functions (both as call and as construct). */\
     macro(ConvertThis, NodeResultJS) \
+    macro(CreateThis, NodeResultJS) /* Note this is not MustGenerate since we're returning it anyway. */ \
+    macro(GetCallee, NodeResultJS) \
     \
     /* Nodes for local variable access. */\
     macro(GetLocal, NodeResultJS) \
index 4ee86bc..7866407 100644 (file)
@@ -119,6 +119,27 @@ EncodedJSValue operationConvertThis(ExecState* exec, EncodedJSValue encodedOp)
     return JSValue::encode(JSValue::decode(encodedOp).toThisObject(exec));
 }
 
+EncodedJSValue operationCreateThis(ExecState* exec, EncodedJSValue encodedOp)
+{
+    JSFunction* constructor = asFunction(exec->callee());
+    
+#if !ASSERT_DISABLED
+    ConstructData constructData;
+    ASSERT(constructor->getConstructData(constructData) == ConstructTypeJS);
+#endif
+    
+    JSGlobalData& globalData = exec->globalData();
+    
+    Structure* structure;
+    JSValue proto = JSValue::decode(encodedOp);
+    if (proto.isObject())
+        structure = asObject(proto)->inheritorID(globalData);
+    else
+        structure = constructor->scope()->globalObject->emptyObjectStructure();
+    
+    return JSValue::encode(constructEmptyObject(exec, structure));
+}
+
 EncodedJSValue operationValueAdd(ExecState* exec, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2)
 {
     JSValue op1 = JSValue::decode(encodedOp1);
index 203e1b5..4335c7b 100644 (file)
@@ -56,6 +56,7 @@ typedef void *(*P_DFGOperation_E)(ExecState*);
 
 // These routines are provide callbacks out to C++ implementations of operations too complex to JIT.
 EncodedJSValue operationConvertThis(ExecState*, EncodedJSValue encodedOp1);
+EncodedJSValue operationCreateThis(ExecState*, EncodedJSValue encodedOp1);
 EncodedJSValue operationValueAdd(ExecState*, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2);
 EncodedJSValue operationValueAddNotNumber(ExecState*, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2);
 EncodedJSValue operationArithAdd(EncodedJSValue encodedOp1, EncodedJSValue encodedOp2);
index 39e0a9c..9214c48 100644 (file)
@@ -533,6 +533,17 @@ private:
             changed |= mergeUse(node.child1(), PredictObjectUnknown | StrongPredictionTag);
             break;
         }
+            
+        case GetCallee: {
+            changed |= setPrediction(makePrediction(PredictObjectOther, StrongPrediction));
+            break;
+        }
+            
+        case CreateThis: {
+            changed |= mergeUse(node.child1(), PredictObjectUnknown | StrongPredictionTag);
+            changed |= setPrediction(makePrediction(PredictFinalObject, StrongPrediction));
+            break;
+        }
 
 #ifndef NDEBUG
         // These get ignored because they don't return anything.
@@ -1018,6 +1029,7 @@ private:
         case ArithMin:
         case ArithMax:
         case ArithSqrt:
+        case GetCallee:
             setReplacement(pureCSE(node));
             break;
             
index e22835c..33cc268 100644 (file)
@@ -1527,6 +1527,90 @@ void SpeculativeJIT::compile(Node& node)
         cellResult(thisValue.gpr(), m_compileIndex);
         break;
     }
+        
+    case CreateThis: {
+        // Note that there is not so much profit to speculate here. The only things we
+        // speculate on are (1) that it's a cell, since that eliminates cell checks
+        // later if the proto is reused, and (2) if we have a FinalObject prediction
+        // then we speculate because we want to get recompiled if it isn't (since
+        // otherwise we'd start taking slow path a lot).
+        
+        SpeculateCellOperand proto(this, node.child1());
+        GPRTemporary result(this);
+        GPRTemporary scratch(this);
+        
+        GPRReg protoGPR = proto.gpr();
+        GPRReg resultGPR = result.gpr();
+        GPRReg scratchGPR = scratch.gpr();
+        
+        proto.use();
+        
+        MacroAssembler::JumpList slowPath;
+        
+        // Need to verify that the prototype is an object. If we have reason to believe
+        // that it's a FinalObject then we speculate on that directly. Otherwise we
+        // do the slow (structure-based) check.
+        if (shouldSpeculateFinalObject(node.child1()))
+            speculationCheck(m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(protoGPR), MacroAssembler::TrustedImmPtr(m_jit.globalData()->jsFinalObjectVPtr)));
+        else {
+            m_jit.loadPtr(MacroAssembler::Address(protoGPR, JSCell::structureOffset()), scratchGPR);
+            slowPath.append(m_jit.branch8(MacroAssembler::Below, MacroAssembler::Address(scratchGPR, Structure::typeInfoTypeOffset()), MacroAssembler::TrustedImm32(ObjectType)));
+        }
+        
+        // Load the inheritorID (the Structure that objects who have protoGPR as the prototype
+        // use to refer to that prototype). If the inheritorID is not set, go to slow path.
+        m_jit.loadPtr(MacroAssembler::Address(protoGPR, JSObject::offsetOfInheritorID()), scratchGPR);
+        slowPath.append(m_jit.branchTestPtr(MacroAssembler::Zero, scratchGPR));
+        
+        MarkedSpace::SizeClass* sizeClass = &m_jit.globalData()->heap.sizeClassForObject(sizeof(JSFinalObject));
+        
+        m_jit.loadPtr(&sizeClass->firstFreeCell, resultGPR);
+        slowPath.append(m_jit.branchTestPtr(MacroAssembler::Zero, resultGPR));
+        
+        // The object is half-allocated: we have what we know is a fresh object, but
+        // it's still on the GC's free list.
+        
+        // Ditch the inheritorID by placing it into the structure, so that we can reuse
+        // scratchGPR.
+        m_jit.storePtr(scratchGPR, MacroAssembler::Address(resultGPR, JSObject::structureOffset()));
+        
+        // Now that we have scratchGPR back, remove the object from the free list
+        m_jit.loadPtr(MacroAssembler::Address(resultGPR), scratchGPR);
+        m_jit.storePtr(scratchGPR, &sizeClass->firstFreeCell);
+        
+        // Initialize the object's vtable
+        m_jit.storePtr(MacroAssembler::TrustedImmPtr(m_jit.globalData()->jsFinalObjectVPtr), MacroAssembler::Address(resultGPR));
+        
+        // Initialize the object's inheritorID.
+        m_jit.storePtr(MacroAssembler::TrustedImmPtr(0), MacroAssembler::Address(resultGPR, JSObject::offsetOfInheritorID()));
+        
+        // Initialize the object's property storage pointer.
+        m_jit.addPtr(MacroAssembler::TrustedImm32(sizeof(JSObject)), resultGPR, scratchGPR);
+        m_jit.storePtr(scratchGPR, MacroAssembler::Address(resultGPR, JSFinalObject::offsetOfPropertyStorage()));
+        
+        MacroAssembler::Jump done = m_jit.jump();
+        
+        slowPath.link(&m_jit);
+        
+        silentSpillAllRegisters(resultGPR);
+        m_jit.move(protoGPR, GPRInfo::argumentGPR1);
+        m_jit.move(GPRInfo::callFrameRegister, GPRInfo::argumentGPR0);
+        appendCallWithExceptionCheck(operationCreateThis);
+        m_jit.move(GPRInfo::returnValueGPR, resultGPR);
+        silentFillAllRegisters(resultGPR);
+        
+        done.link(&m_jit);
+        
+        cellResult(resultGPR, m_compileIndex, UseChildrenCalledExplicitly);
+        break;
+    }
+        
+    case GetCallee: {
+        GPRTemporary result(this);
+        m_jit.loadPtr(JITCompiler::addressFor(static_cast<VirtualRegister>(RegisterFile::Callee)), result.gpr());
+        cellResult(result.gpr(), m_compileIndex);
+        break;
+    }
 
     case GetById: {
         SpeculateCellOperand base(this, node.child1());
index af4d2c9..2920567 100644 (file)
@@ -370,13 +370,13 @@ JSObject* FunctionExecutable::compileOptimizedForCall(ExecState* exec, ScopeChai
     return error;
 }
 
-JSObject* FunctionExecutable::compileOptimizedForConstruct(ExecState* exec, ScopeChainNode* scopeChainNode)
+JSObject* FunctionExecutable::compileOptimizedForConstruct(ExecState* exec, ScopeChainNode* scopeChainNode, ExecState* calleeArgsExec)
 {
     ASSERT(exec->globalData().dynamicGlobalObject);
     ASSERT(!!m_codeBlockForConstruct);
     JSObject* error = 0;
     if (m_codeBlockForConstruct->getJITType() != JITCode::topTierJIT())
-        error = compileForConstructInternal(exec, scopeChainNode, JITCode::nextTierJIT(m_codeBlockForConstruct->getJITType()));
+        error = compileForConstructInternal(exec, scopeChainNode, calleeArgsExec, JITCode::nextTierJIT(m_codeBlockForConstruct->getJITType()));
     ASSERT(!!m_codeBlockForConstruct);
     return error;
 }
@@ -459,7 +459,7 @@ JSObject* FunctionExecutable::compileForCallInternal(ExecState* exec, ScopeChain
     return 0;
 }
 
-JSObject* FunctionExecutable::compileForConstructInternal(ExecState* exec, ScopeChainNode* scopeChainNode, JITCode::JITType jitType)
+JSObject* FunctionExecutable::compileForConstructInternal(ExecState* exec, ScopeChainNode* scopeChainNode, ExecState* calleeArgsExec, JITCode::JITType jitType)
 {
     UNUSED_PARAM(jitType);
     
@@ -498,7 +498,8 @@ JSObject* FunctionExecutable::compileForConstructInternal(ExecState* exec, Scope
 #if ENABLE(JIT)
     if (exec->globalData().canUseJIT()) {
         bool dfgCompiled = false;
-        // FIXME: Make it possible to compile constructors with DFG.
+        if (jitType == JITCode::DFGJIT)
+            dfgCompiled = DFG::tryCompileFunction(exec, calleeArgsExec, m_codeBlockForConstruct.get(), m_jitCodeForConstruct, m_jitCodeForConstructWithArityCheck);
         if (dfgCompiled) {
             if (m_codeBlockForConstruct->alternative())
                 m_codeBlockForConstruct->alternative()->unlinkIncomingCalls();
index 43a6333..bd8a97e 100644 (file)
@@ -487,17 +487,17 @@ namespace JSC {
             return *m_codeBlockForCall;
         }
 
-        JSObject* compileForConstruct(ExecState* exec, ScopeChainNode* scopeChainNode)
+        JSObject* compileForConstruct(ExecState* exec, ScopeChainNode* scopeChainNode, ExecState* calleeArgsExec = 0)
         {
             ASSERT(exec->globalData().dynamicGlobalObject);
             JSObject* error = 0;
             if (!m_codeBlockForConstruct)
-                error = compileForConstructInternal(exec, scopeChainNode, JITCode::bottomTierJIT());
+                error = compileForConstructInternal(exec, scopeChainNode, calleeArgsExec, JITCode::bottomTierJIT());
             ASSERT(!error == !!m_codeBlockForConstruct);
             return error;
         }
 
-        JSObject* compileOptimizedForConstruct(ExecState*, ScopeChainNode*);
+        JSObject* compileOptimizedForConstruct(ExecState*, ScopeChainNode*, ExecState* calleeArgsExec = 0);
 
         bool isGeneratedForConstruct() const
         {
@@ -521,7 +521,7 @@ namespace JSC {
             if (kind == CodeForCall)
                 return compileForCall(exec, scopeChainNode, exec);
             ASSERT(kind == CodeForConstruct);
-            return compileForConstruct(exec, scopeChainNode);
+            return compileForConstruct(exec, scopeChainNode, exec);
         }
         
         JSObject* compileOptimizedFor(ExecState* exec, ScopeChainNode* scopeChainNode, CodeSpecializationKind kind)
@@ -535,7 +535,7 @@ namespace JSC {
             if (kind == CodeForCall)
                 return compileOptimizedForCall(exec, scopeChainNode, exec);
             ASSERT(kind == CodeForConstruct);
-            return compileOptimizedForConstruct(exec, scopeChainNode);
+            return compileOptimizedForConstruct(exec, scopeChainNode, exec);
         }
         
         bool isGeneratedFor(CodeSpecializationKind kind)
@@ -587,7 +587,7 @@ namespace JSC {
         FunctionExecutable(ExecState*, const Identifier& name, const SourceCode&, bool forceUsesArguments, FunctionParameters*, bool);
 
         JSObject* compileForCallInternal(ExecState*, ScopeChainNode*, ExecState* calleeArgsExec, JITCode::JITType);
-        JSObject* compileForConstructInternal(ExecState*, ScopeChainNode*, JITCode::JITType);
+        JSObject* compileForConstructInternal(ExecState*, ScopeChainNode*, ExecState* calleeArgsExec, JITCode::JITType);
         
         static const unsigned StructureFlags = OverridesVisitChildren | ScriptExecutable::StructureFlags;
         unsigned m_numCapturedVariables : 31;