Commit bb159ec2 authored by fpizlo@apple.com's avatar 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):



git-svn-id: http://svn.webkit.org/repository/webkit/trunk@95672 268f45cc-cd09-0410-ab3c-d52691b4dbfc
parent cff58874
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.
......@@ -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: {
......
......@@ -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:
......
......@@ -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) \
......
......@@ -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);
......
......@@ -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);
......
......@@ -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;
......
......@@ -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());
......
......@@ -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();
......
......@@ -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;
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment