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
+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.
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: {
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)
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:
#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) \
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);
// 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);
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.
case ArithMin:
case ArithMax:
case ArithSqrt:
+ case GetCallee:
setReplacement(pureCSE(node));
break;
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());
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;
}
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);
#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();
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
{
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)
if (kind == CodeForCall)
return compileOptimizedForCall(exec, scopeChainNode, exec);
ASSERT(kind == CodeForConstruct);
- return compileOptimizedForConstruct(exec, scopeChainNode);
+ return compileOptimizedForConstruct(exec, scopeChainNode, exec);
}
bool isGeneratedFor(CodeSpecializationKind kind)
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;