https://bugs.webkit.org/show_bug.cgi?id=68318
Reviewed by Gavin Barraclough.
Adds Math.min, Math.max, and Math.sqrt intrinsics. Adds support for
a function to have an intrinsic but not a thunk generator. This is
a 7% speed-up on access-nbody, and neutral elsewhere, mainly because
we're still not DFG compiling the bulk of the hot code in Kraken audio
benchmarks.
* create_hash_table:
* dfg/DFGByteCodeParser.cpp:
(JSC::DFG::ByteCodeParser::handleMinMax):
(JSC::DFG::ByteCodeParser::handleIntrinsic):
* dfg/DFGIntrinsic.h:
* dfg/DFGNode.h:
* dfg/DFGPropagator.cpp:
(JSC::DFG::Propagator::propagateNode):
(JSC::DFG::Propagator::fixupNode):
* dfg/DFGSpeculativeJIT.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* jit/JITStubs.cpp:
(JSC::JITThunks::hostFunctionStub):
* runtime/Lookup.cpp:
(JSC::setUpStaticFunctionSlot):
git-svn-id: http://svn.webkit.org/repository/webkit/trunk@95399
268f45cc-cd09-0410-ab3c-
d52691b4dbfc
+2011-09-17 Filip Pizlo <fpizlo@apple.com>
+
+ DFG JIT should inline Math.min, Math.max, and Math.sqrt
+ https://bugs.webkit.org/show_bug.cgi?id=68318
+
+ Reviewed by Gavin Barraclough.
+
+ Adds Math.min, Math.max, and Math.sqrt intrinsics. Adds support for
+ a function to have an intrinsic but not a thunk generator. This is
+ a 7% speed-up on access-nbody, and neutral elsewhere, mainly because
+ we're still not DFG compiling the bulk of the hot code in Kraken audio
+ benchmarks.
+
+ * create_hash_table:
+ * dfg/DFGByteCodeParser.cpp:
+ (JSC::DFG::ByteCodeParser::handleMinMax):
+ (JSC::DFG::ByteCodeParser::handleIntrinsic):
+ * dfg/DFGIntrinsic.h:
+ * dfg/DFGNode.h:
+ * dfg/DFGPropagator.cpp:
+ (JSC::DFG::Propagator::propagateNode):
+ (JSC::DFG::Propagator::fixupNode):
+ * dfg/DFGSpeculativeJIT.cpp:
+ (JSC::DFG::SpeculativeJIT::compile):
+ * jit/JITStubs.cpp:
+ (JSC::JITThunks::hostFunctionStub):
+ * runtime/Lookup.cpp:
+ (JSC::setUpStaticFunctionSlot):
+
2011-09-18 Nico Weber <thakis@chromium.org>
Remove two files from JavaScriptCore.gypi that were removed in r95240
$thunkGenerator = "fromCharCodeThunkGenerator";
}
if ($name eq "mathTable") {
+ if ($key eq "min") {
+ $intrinsic = "DFG::MinIntrinsic";
+ }
+ if ($key eq "max") {
+ $intrinsic = "DFG::MaxIntrinsic";
+ }
if ($key eq "sqrt") {
$thunkGenerator = "sqrtThunkGenerator";
+ $intrinsic = "DFG::SqrtIntrinsic";
}
if ($key eq "pow") {
$thunkGenerator = "powThunkGenerator";
bool parse();
private:
+ // Helper for min and max.
+ bool handleMinMax(bool usesResult, int resultOperand, NodeType op, int firstArg, int lastArg);
+
// Handle intrinsic functions.
bool handleIntrinsic(bool usesResult, int resultOperand, Intrinsic, int firstArg, int lastArg);
// Parse a single basic block of bytecode instructions.
m_currentIndex += OPCODE_LENGTH(name); \
return !m_parseFailed
+bool ByteCodeParser::handleMinMax(bool usesResult, int resultOperand, NodeType op, int firstArg, int lastArg)
+{
+ if (!usesResult)
+ return true;
+
+ if (lastArg == firstArg) {
+ set(resultOperand, constantNaN());
+ return true;
+ }
+
+ if (lastArg == firstArg + 1) {
+ set(resultOperand, getToNumber(firstArg + 1));
+ return true;
+ }
+
+ if (lastArg == firstArg + 2) {
+ set(resultOperand, addToGraph(op, getToNumber(firstArg + 1), getToNumber(firstArg + 2)));
+ return true;
+ }
+
+ // Don't handle >=3 arguments for now.
+ return false;
+}
+
bool ByteCodeParser::handleIntrinsic(bool usesResult, int resultOperand, Intrinsic intrinsic, int firstArg, int lastArg)
{
switch (intrinsic) {
return true;
}
+ case MinIntrinsic:
+ return handleMinMax(usesResult, resultOperand, ArithMin, firstArg, lastArg);
+
+ case MaxIntrinsic:
+ return handleMinMax(usesResult, resultOperand, ArithMax, firstArg, lastArg);
+
+ case SqrtIntrinsic: {
+ if (!usesResult)
+ return true;
+
+ if (firstArg == lastArg) {
+ set(resultOperand, constantNaN());
+ return true;
+ }
+
+ set(resultOperand, addToGraph(ArithSqrt, getToNumber(firstArg + 1)));
+ return true;
+ }
+
default:
ASSERT(intrinsic == NoIntrinsic);
return false;
enum Intrinsic {
NoIntrinsic,
- AbsIntrinsic
+ AbsIntrinsic,
+ MinIntrinsic,
+ MaxIntrinsic,
+ SqrtIntrinsic
};
} } // namespace JSC::DFG
macro(ArithDiv, NodeResultNumber) \
macro(ArithMod, NodeResultNumber) \
macro(ArithAbs, NodeResultNumber) \
+ macro(ArithMin, NodeResultNumber) \
+ macro(ArithMax, NodeResultNumber) \
+ macro(ArithSqrt, NodeResultNumber) \
/* Arithmetic operators call ToNumber on their operands. */\
macro(ValueToNumber, NodeResultNumber | NodeMustGenerate) \
\
case ArithAdd:
case ArithSub:
- case ArithMul: {
+ case ArithMul:
+ case ArithMin:
+ case ArithMax: {
PredictedType left = m_predictions[node.child1()];
PredictedType right = m_predictions[node.child2()];
break;
}
- case ArithDiv: {
+ case ArithDiv:
+ case ArithSqrt: {
changed |= setPrediction(makePrediction(PredictDouble, StrongPrediction));
break;
}
case ArithAdd:
case ArithSub:
- case ArithMul: {
+ case ArithMul:
+ case ArithMin:
+ case ArithMax: {
PredictedType left = m_predictions[node.child1()];
PredictedType right = m_predictions[node.child2()];
break;
}
+ case ArithAbs: {
+ PredictedType prediction = m_predictions[node.child1()];
+ if (isStrongPrediction(prediction) && (prediction & PredictDouble))
+ toDouble(node.child1());
+ break;
+ }
+
+ case ArithSqrt: {
+ toDouble(node.child1());
+ break;
+ }
+
default:
break;
}
case ArithMod:
case ArithDiv:
case ArithAbs:
+ case ArithMin:
+ case ArithMax:
+ case ArithSqrt:
setReplacement(pureCSE(node));
break;
doubleResult(result.fpr(), m_compileIndex);
break;
}
+
+ case ArithMin:
+ case ArithMax: {
+ if (shouldSpeculateInteger(node.child1(), node.child2())) {
+ SpeculateIntegerOperand op1(this, node.child1());
+ SpeculateIntegerOperand op2(this, node.child2());
+ GPRTemporary result(this, op1);
+
+ MacroAssembler::Jump op1Less = m_jit.branch32(op == ArithMin ? MacroAssembler::LessThan : MacroAssembler::GreaterThan, op1.gpr(), op2.gpr());
+ m_jit.move(op2.gpr(), result.gpr());
+ if (op1.gpr() != result.gpr()) {
+ MacroAssembler::Jump done = m_jit.jump();
+ op1Less.link(&m_jit);
+ m_jit.move(op1.gpr(), result.gpr());
+ done.link(&m_jit);
+ } else
+ op1Less.link(&m_jit);
+
+ integerResult(result.gpr(), m_compileIndex);
+ break;
+ }
+
+ SpeculateDoubleOperand op1(this, node.child1());
+ SpeculateDoubleOperand op2(this, node.child2());
+ FPRTemporary result(this, op1);
+
+ MacroAssembler::JumpList done;
+
+ MacroAssembler::Jump op1Less = m_jit.branchDouble(op == ArithMin ? MacroAssembler::DoubleLessThan : MacroAssembler::DoubleGreaterThan, op1.fpr(), op2.fpr());
+
+ // op2 is eather the lesser one or one of then is NaN
+ MacroAssembler::Jump op2Less = m_jit.branchDouble(op == ArithMin ? MacroAssembler::DoubleGreaterThan : MacroAssembler::DoubleLessThan, op1.fpr(), op2.fpr());
+
+ // Unordered case. We don't know which of op1, op2 is NaN. Manufacture NaN by adding
+ // op1 + op2 and putting it into result.
+ m_jit.addDouble(op1.fpr(), op2.fpr(), result.fpr());
+ done.append(m_jit.jump());
+
+ op2Less.link(&m_jit);
+ m_jit.moveDouble(op2.fpr(), result.fpr());
+
+ if (op1.fpr() != result.fpr()) {
+ done.append(m_jit.jump());
+
+ op1Less.link(&m_jit);
+ m_jit.moveDouble(op1.fpr(), result.fpr());
+ } else
+ op1Less.link(&m_jit);
+
+ done.link(&m_jit);
+
+ doubleResult(result.fpr(), m_compileIndex);
+ break;
+ }
+
+ case ArithSqrt: {
+ SpeculateDoubleOperand op1(this, node.child1());
+ FPRTemporary result(this, op1);
+
+ m_jit.sqrtDouble(op1.fpr(), result.fpr());
+
+ doubleResult(result.fpr(), m_compileIndex);
+ break;
+ }
case LogicalNot: {
if (isKnownBoolean(node.child1())) {
{
std::pair<HostFunctionStubMap::iterator, bool> entry = m_hostFunctionStubMap->add(function, Weak<NativeExecutable>());
if (!*entry.first->second) {
- MacroAssemblerCodeRef code = globalData->canUseJIT() ? generator(globalData) : MacroAssemblerCodeRef();
+ MacroAssemblerCodeRef code;
+ if (generator) {
+ if (globalData->canUseJIT())
+ code = generator(globalData);
+ else
+ code = MacroAssemblerCodeRef();
+ } else
+ code = JIT::compileCTINativeCall(globalData, function);
entry.first->second.set(*globalData, NativeExecutable::create(*globalData, code, function, MacroAssemblerCodeRef::createSelfManagedCodeRef(ctiNativeConstruct()), callHostFunctionAsConstructor, intrinsic));
}
return entry.first->second.get();
JSFunction* function;
JSGlobalObject* globalObject = thisObj->globalObject();
#if ENABLE(JIT)
- if (entry->generator())
+ if (entry->generator() || entry->intrinsic() != DFG::NoIntrinsic)
function = JSFunction::create(exec, globalObject, globalObject->functionStructure(), entry->functionLength(), propertyName, exec->globalData().getHostFunction(entry->function(), entry->generator(), entry->intrinsic()));
else
#endif