DFG JIT should inline Math.min, Math.max, and Math.sqrt
authorfpizlo@apple.com <fpizlo@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sun, 18 Sep 2011 22:29:41 +0000 (22:29 +0000)
committerfpizlo@apple.com <fpizlo@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sun, 18 Sep 2011 22:29:41 +0000 (22:29 +0000)
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

Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/create_hash_table
Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp
Source/JavaScriptCore/dfg/DFGIntrinsic.h
Source/JavaScriptCore/dfg/DFGNode.h
Source/JavaScriptCore/dfg/DFGPropagator.cpp
Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp
Source/JavaScriptCore/jit/JITStubs.cpp
Source/JavaScriptCore/runtime/Lookup.cpp

index 744d1a8..24d8cc8 100644 (file)
@@ -1,3 +1,32 @@
+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
index 3bb9cc2..ab8e8a0 100755 (executable)
@@ -288,8 +288,15 @@ sub output() {
             $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";
index 1ad4441..a05fa77 100644 (file)
@@ -64,6 +64,9 @@ public:
     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.
@@ -600,6 +603,30 @@ private:
     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) {
@@ -620,6 +647,25 @@ bool ByteCodeParser::handleIntrinsic(bool usesResult, int resultOperand, Intrins
         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;
index e18c50e..fefff46 100644 (file)
@@ -30,7 +30,10 @@ namespace JSC { namespace DFG {
 
 enum Intrinsic {
     NoIntrinsic,
-    AbsIntrinsic
+    AbsIntrinsic,
+    MinIntrinsic,
+    MaxIntrinsic,
+    SqrtIntrinsic
 };
 
 } } // namespace JSC::DFG
index 0457a54..8cd22b6 100644 (file)
@@ -155,6 +155,9 @@ private:
     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) \
     \
index a4e5d0f..9632e47 100644 (file)
@@ -225,7 +225,9 @@ private:
             
         case ArithAdd:
         case ArithSub:
-        case ArithMul: {
+        case ArithMul:
+        case ArithMin:
+        case ArithMax: {
             PredictedType left = m_predictions[node.child1()];
             PredictedType right = m_predictions[node.child2()];
             
@@ -238,7 +240,8 @@ private:
             break;
         }
             
-        case ArithDiv: {
+        case ArithDiv:
+        case ArithSqrt: {
             changed |= setPrediction(makePrediction(PredictDouble, StrongPrediction));
             break;
         }
@@ -405,7 +408,9 @@ private:
             
         case ArithAdd:
         case ArithSub:
-        case ArithMul: {
+        case ArithMul:
+        case ArithMin:
+        case ArithMax: {
             PredictedType left = m_predictions[node.child1()];
             PredictedType right = m_predictions[node.child2()];
             
@@ -424,6 +429,18 @@ private:
             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;
         }
@@ -730,6 +747,9 @@ private:
         case ArithMod:
         case ArithDiv:
         case ArithAbs:
+        case ArithMin:
+        case ArithMax:
+        case ArithSqrt:
             setReplacement(pureCSE(node));
             break;
             
index 35d3cd1..7749ce1 100644 (file)
@@ -1083,6 +1083,70 @@ void SpeculativeJIT::compile(Node& node)
         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())) {
index 0d9849b..18e97ef 100644 (file)
@@ -3783,7 +3783,14 @@ NativeExecutable* JITThunks::hostFunctionStub(JSGlobalData* globalData, NativeFu
 {
     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();
index 57c8187..b489599 100644 (file)
@@ -81,7 +81,7 @@ void setUpStaticFunctionSlot(ExecState* exec, const HashEntry* entry, JSObject*
         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