Support op_typeof in the DFG
authoroliver@apple.com <oliver@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 14 Dec 2012 00:32:55 +0000 (00:32 +0000)
committeroliver@apple.com <oliver@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 14 Dec 2012 00:32:55 +0000 (00:32 +0000)
https://bugs.webkit.org/show_bug.cgi?id=98898

Reviewed by Filip Pizlo.

Adds a TypeOf node to the DFG to support op_typeof.

* dfg/DFGAbstractState.cpp:
(JSC::DFG::AbstractState::execute):
  We try to determine the result early here, and substitute in a constant.
  Otherwise we leave the node intact, and set the result type to SpecString.
* dfg/DFGByteCodeParser.cpp:
(JSC::DFG::ByteCodeParser::parseBlock):
  Parse op_typeof
* dfg/DFGCSEPhase.cpp:
(JSC::DFG::CSEPhase::performNodeCSE):
  TypeOf nodes can be subjected to pure CSE
* dfg/DFGCapabilities.h:
(JSC::DFG::canCompileOpcode):
  We can handle typeof.
* dfg/DFGNodeType.h:
(DFG):
  Define the node.
* dfg/DFGOperations.cpp:
* dfg/DFGOperations.h:
  Add operationTypeOf to support the non-trivial cases.
* dfg/DFGPredictionPropagationPhase.cpp:
(JSC::DFG::PredictionPropagationPhase::propagate):
* dfg/DFGSpeculativeJIT32_64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* dfg/DFGSpeculativeJIT64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
  Actual codegen
* runtime/Operations.cpp:
(JSC::jsTypeStringForValue):
(JSC):
* runtime/Operations.h:
(JSC):
  Some refactoring to allow us to get the type string for an
  object without needing a callframe.

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

13 files changed:
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/dfg/DFGAbstractState.cpp
Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp
Source/JavaScriptCore/dfg/DFGCSEPhase.cpp
Source/JavaScriptCore/dfg/DFGCapabilities.h
Source/JavaScriptCore/dfg/DFGNodeType.h
Source/JavaScriptCore/dfg/DFGOperations.cpp
Source/JavaScriptCore/dfg/DFGOperations.h
Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp
Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp
Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp
Source/JavaScriptCore/runtime/Operations.cpp
Source/JavaScriptCore/runtime/Operations.h

index a29b428..da3b9c0 100644 (file)
@@ -1,3 +1,46 @@
+2012-13-11  Oliver Hunt  <oliver@apple.com>
+
+        Support op_typeof in the DFG
+        https://bugs.webkit.org/show_bug.cgi?id=98898
+
+        Reviewed by Filip Pizlo.
+
+        Adds a TypeOf node to the DFG to support op_typeof. 
+
+        * dfg/DFGAbstractState.cpp:
+        (JSC::DFG::AbstractState::execute):
+          We try to determine the result early here, and substitute in a constant.
+          Otherwise we leave the node intact, and set the result type to SpecString.
+        * dfg/DFGByteCodeParser.cpp:
+        (JSC::DFG::ByteCodeParser::parseBlock):
+          Parse op_typeof
+        * dfg/DFGCSEPhase.cpp:
+        (JSC::DFG::CSEPhase::performNodeCSE):
+          TypeOf nodes can be subjected to pure CSE
+        * dfg/DFGCapabilities.h:
+        (JSC::DFG::canCompileOpcode):
+          We can handle typeof.
+        * dfg/DFGNodeType.h:
+        (DFG):
+          Define the node.
+        * dfg/DFGOperations.cpp:
+        * dfg/DFGOperations.h:
+          Add operationTypeOf to support the non-trivial cases.
+        * dfg/DFGPredictionPropagationPhase.cpp:
+        (JSC::DFG::PredictionPropagationPhase::propagate):
+        * dfg/DFGSpeculativeJIT32_64.cpp:
+        (JSC::DFG::SpeculativeJIT::compile):
+        * dfg/DFGSpeculativeJIT64.cpp:
+        (JSC::DFG::SpeculativeJIT::compile):
+          Actual codegen
+        * runtime/Operations.cpp:
+        (JSC::jsTypeStringForValue):
+        (JSC):
+        * runtime/Operations.h:
+        (JSC):
+          Some refactoring to allow us to get the type string for an
+          object without needing a callframe.
+
 2012-12-12  Filip Pizlo  <fpizlo@apple.com>
 
         OSR exit compiler should emit code for resetting the execution counter that matches the logic of ExecutionCounter.cpp
index 65647a7..bb605a8 100644 (file)
@@ -31,6 +31,7 @@
 #include "CodeBlock.h"
 #include "DFGBasicBlock.h"
 #include "GetByIdStatus.h"
+#include "Operations.h"
 #include "PutByIdStatus.h"
 
 namespace JSC { namespace DFG {
@@ -707,6 +708,11 @@ bool AbstractState::execute(unsigned indexInBlock)
             case IsString:
                 constantWasSet = trySetConstant(nodeIndex, jsBoolean(isJSString(child)));
                 break;
+            case IsObject:
+                if (child.isNull() || !child.isObject()) {
+                    constantWasSet = trySetConstant(nodeIndex, jsBoolean(child.isNull()));
+                    break;
+                }
             default:
                 constantWasSet = false;
                 break;
@@ -716,9 +722,64 @@ bool AbstractState::execute(unsigned indexInBlock)
                 break;
             }
         }
+
         forNode(nodeIndex).set(SpecBoolean);
         break;
     }
+
+    case TypeOf: {
+        JSGlobalData* globalData = m_codeBlock->globalData();
+        JSValue child = forNode(node.child1()).value();
+        AbstractValue& abstractChild = forNode(node.child1());
+        if (child) {
+            JSValue typeString = jsTypeStringForValue(*globalData, m_codeBlock->globalObjectFor(node.codeOrigin), child);
+            if (trySetConstant(nodeIndex, typeString)) {
+                m_foundConstants = true;
+                break;
+            }
+        } else if (isNumberSpeculation(abstractChild.m_type)) {
+            if (trySetConstant(nodeIndex, globalData->smallStrings.numberString(globalData))) {
+                forNode(node.child1()).filter(SpecNumber);
+                m_foundConstants = true;
+                break;
+            }
+        } else if (isStringSpeculation(abstractChild.m_type)) {
+            if (trySetConstant(nodeIndex, globalData->smallStrings.stringString(globalData))) {
+                forNode(node.child1()).filter(SpecString);
+                m_foundConstants = true;
+                break;
+            }
+        } else if (isFinalObjectSpeculation(abstractChild.m_type) || isArraySpeculation(abstractChild.m_type) || isArgumentsSpeculation(abstractChild.m_type)) {
+            if (trySetConstant(nodeIndex, globalData->smallStrings.objectString(globalData))) {
+                forNode(node.child1()).filter(SpecFinalObject | SpecArray | SpecArguments);
+                m_foundConstants = true;
+                break;
+            }
+        } else if (isFunctionSpeculation(abstractChild.m_type)) {
+            if (trySetConstant(nodeIndex, globalData->smallStrings.functionString(globalData))) {
+                forNode(node.child1()).filter(SpecFunction);
+                m_foundConstants = true;
+                break;
+            }
+        } else if (isBooleanSpeculation(abstractChild.m_type)) {
+            if (trySetConstant(nodeIndex, globalData->smallStrings.booleanString(globalData))) {
+                forNode(node.child1()).filter(SpecBoolean);
+                m_foundConstants = true;
+                break;
+            }
+        } else {
+            Node& childNode = m_graph[node.child1()];
+            if (isCellSpeculation(childNode.prediction())) {
+                if (isStringSpeculation(childNode.prediction()))
+                    forNode(node.child1()).filter(SpecString);
+                else
+                    forNode(node.child1()).filter(SpecCell);
+                node.setCanExit(true);
+            }
+        }
+        forNode(nodeIndex).set(SpecString);
+        break;
+    }
             
     case CompareLess:
     case CompareLessEq:
index a56adab..44063f7 100644 (file)
@@ -3255,6 +3255,12 @@ bool ByteCodeParser::parseBlock(unsigned limit)
             NEXT_OPCODE(op_new_func_exp);
         }
 
+        case op_typeof: {
+            set(currentInstruction[1].u.operand,
+                addToGraph(TypeOf, get(currentInstruction[2].u.operand)));
+            NEXT_OPCODE(op_typeof);
+        }
+
         default:
             // Parse failed! This should not happen because the capabilities checker
             // should have caught it.
index 34072ce..fc45002 100644 (file)
@@ -562,6 +562,7 @@ private:
             case CreateThis:
             case AllocatePropertyStorage:
             case ReallocatePropertyStorage:
+            case TypeOf:
                 return NoNode;
                 
             case GetIndexedPropertyStorage:
@@ -1127,6 +1128,7 @@ private:
         case SkipTopScope:
         case SkipScope:
         case GetScopeRegisters:
+        case TypeOf:
             setReplacement(pureCSE(node));
             break;
             
index 38fe7f9..c093a8d 100644 (file)
@@ -196,6 +196,7 @@ inline CapabilityLevel canCompileOpcode(OpcodeID opcodeID, CodeBlock*, Instructi
     case op_jneq_ptr:
     case op_put_to_base_variable:
     case op_put_to_base:
+    case op_typeof:
         return CanCompile;
         
     case op_call_varargs:
index 79dfc5d..59cd1cb 100644 (file)
@@ -210,6 +210,7 @@ namespace JSC { namespace DFG {
     macro(IsString, NodeResultBoolean) \
     macro(IsObject, NodeResultBoolean) \
     macro(IsFunction, NodeResultBoolean) \
+    macro(TypeOf, NodeResultJS) \
     macro(LogicalNot, NodeResultBoolean) \
     macro(ToPrimitive, NodeResultJS | NodeMustGenerate | NodeClobbersWorld) \
     macro(StrCat, NodeResultJS | NodeMustGenerate | NodeHasVarArgs | NodeClobbersWorld) \
index fb2f019..90043f5 100644 (file)
@@ -1370,6 +1370,11 @@ size_t DFG_OPERATION operationIsFunction(EncodedJSValue value)
     return jsIsFunctionType(JSValue::decode(value));
 }
 
+JSCell* DFG_OPERATION operationTypeOf(ExecState* exec, JSCell* value)
+{
+    return jsTypeStringForValue(exec, JSValue(value)).asCell();
+}
+
 void DFG_OPERATION operationReallocateStorageAndFinishPut(ExecState* exec, JSObject* base, Structure* structure, PropertyOffset offset, EncodedJSValue value)
 {
     JSGlobalData& globalData = exec->globalData();
index 17e4ac5..e3253e2 100644 (file)
@@ -198,6 +198,7 @@ JSCell* DFG_OPERATION operationNewFunctionExpression(ExecState*, JSCell*) WTF_IN
 double DFG_OPERATION operationFModOnInts(int32_t, int32_t) WTF_INTERNAL;
 size_t DFG_OPERATION operationIsObject(ExecState*, EncodedJSValue) WTF_INTERNAL;
 size_t DFG_OPERATION operationIsFunction(EncodedJSValue) WTF_INTERNAL;
+JSCell* DFG_OPERATION operationTypeOf(ExecState*, JSCell*) WTF_INTERNAL;
 void DFG_OPERATION operationReallocateStorageAndFinishPut(ExecState*, JSObject*, Structure*, PropertyOffset, EncodedJSValue) WTF_INTERNAL;
 char* DFG_OPERATION operationAllocatePropertyStorageWithInitialCapacity(ExecState*) WTF_INTERNAL;
 char* DFG_OPERATION operationAllocatePropertyStorage(ExecState*, size_t newSize) WTF_INTERNAL;
index 6adfbef..8873f95 100644 (file)
@@ -497,7 +497,13 @@ private:
             changed |= mergeDefaultFlags(node);
             break;
         }
-            
+
+        case TypeOf: {
+            changed |= setPrediction(SpecString);
+            changed |= mergeDefaultFlags(node);
+            break;
+        }
+
         case GetById: {
             changed |= mergePrediction(node.getHeapPrediction());
             changed |= mergeDefaultFlags(node);
index fa33c67..25916cc 100644 (file)
@@ -4558,6 +4558,66 @@ void SpeculativeJIT::compile(Node& node)
         booleanResult(result.gpr(), m_compileIndex);
         break;
     }
+    case TypeOf: {
+        JSValueOperand value(this, node.child1());
+        GPRReg tagGPR = value.tagGPR();
+        GPRReg payloadGPR = value.payloadGPR();
+        GPRTemporary temp(this);
+        GPRReg tempGPR = temp.gpr();
+        GPRResult result(this);
+        GPRReg resultGPR = result.gpr();
+        JITCompiler::JumpList doneJumps;
+
+        flushRegisters();
+
+        JITCompiler::Jump isNotCell = m_jit.branch32(JITCompiler::NotEqual, tagGPR, JITCompiler::TrustedImm32(JSValue::CellTag));
+        Node& child = m_jit.graph()[node.child1()];
+        if (child.shouldSpeculateCell())
+            speculationCheck(BadType, JSValueRegs(tagGPR, payloadGPR), node.child1(), isNotCell);
+
+        if (!child.shouldSpeculateNonStringCell()) {
+            m_jit.loadPtr(JITCompiler::Address(payloadGPR, JSCell::structureOffset()), tempGPR);
+            JITCompiler::Jump notString = m_jit.branch8(JITCompiler::NotEqual, JITCompiler::Address(tempGPR, Structure::typeInfoTypeOffset()), TrustedImm32(StringType));
+            if (child.shouldSpeculateString())
+                speculationCheck(BadType, JSValueRegs(tagGPR, payloadGPR), node.child1(), notString);
+            m_jit.move(TrustedImmPtr(m_jit.globalData()->smallStrings.stringString(m_jit.globalData())), resultGPR);
+            doneJumps.append(m_jit.jump());
+            if (!child.shouldSpeculateString()) {
+                notString.link(&m_jit);
+                callOperation(operationTypeOf, resultGPR, payloadGPR);
+                doneJumps.append(m_jit.jump());
+            }
+        } else {
+            callOperation(operationTypeOf, resultGPR, payloadGPR);
+            doneJumps.append(m_jit.jump());
+        }
+
+        if (!child.shouldSpeculateCell()) {
+            isNotCell.link(&m_jit);
+
+            m_jit.add32(TrustedImm32(1), tagGPR, tempGPR);
+            JITCompiler::Jump notNumber = m_jit.branch32(JITCompiler::AboveOrEqual, tempGPR, JITCompiler::TrustedImm32(JSValue::LowestTag + 1));
+            m_jit.move(TrustedImmPtr(m_jit.globalData()->smallStrings.numberString(m_jit.globalData())), resultGPR);
+            doneJumps.append(m_jit.jump());
+            notNumber.link(&m_jit);
+
+            JITCompiler::Jump notUndefined = m_jit.branch32(JITCompiler::NotEqual, tagGPR, TrustedImm32(JSValue::UndefinedTag));
+            m_jit.move(TrustedImmPtr(m_jit.globalData()->smallStrings.undefinedString(m_jit.globalData())), resultGPR);
+            doneJumps.append(m_jit.jump());
+            notUndefined.link(&m_jit);
+
+            JITCompiler::Jump notNull = m_jit.branch32(JITCompiler::NotEqual, tagGPR, TrustedImm32(JSValue::NullTag));
+            m_jit.move(TrustedImmPtr(m_jit.globalData()->smallStrings.objectString(m_jit.globalData())), resultGPR);
+            doneJumps.append(m_jit.jump());
+            notNull.link(&m_jit);
+
+            // Only boolean left
+            m_jit.move(TrustedImmPtr(m_jit.globalData()->smallStrings.booleanString(m_jit.globalData())), resultGPR);
+        }
+        doneJumps.link(&m_jit);
+        cellResult(resultGPR, m_compileIndex);
+        break;
+    }
 
     case Phi:
     case Flush:
index 96a9a44..569f64d 100644 (file)
@@ -4069,7 +4069,7 @@ void SpeculativeJIT::compile(Node& node)
         cachedGetById(node.codeOrigin, baseGPR, resultGPR, node.identifierNumber(), notCell, DontSpill);
         
         jsValueResult(resultGPR, m_compileIndex, UseChildrenCalledExplicitly);
-        
+
         break;
     }
 
@@ -4484,6 +4484,64 @@ void SpeculativeJIT::compile(Node& node)
         break;
     }
 
+    case TypeOf: {
+        JSValueOperand value(this, node.child1());
+        GPRReg valueGPR = value.gpr();
+        GPRTemporary temp(this);
+        GPRReg tempGPR = temp.gpr();
+        GPRResult result(this);
+        GPRReg resultGPR = result.gpr();
+        JITCompiler::JumpList doneJumps;
+
+        flushRegisters();
+
+        JITCompiler::Jump isNotCell = m_jit.branchTest64(JITCompiler::NonZero, valueGPR, GPRInfo::tagMaskRegister);
+        Node& child = m_jit.graph()[node.child1()];
+        if (child.shouldSpeculateCell())
+            speculationCheck(BadType, JSValueSource(valueGPR), node.child1(), isNotCell);
+
+        if (!child.shouldSpeculateNonStringCell()) {
+            m_jit.loadPtr(JITCompiler::Address(valueGPR, JSCell::structureOffset()), tempGPR);
+            JITCompiler::Jump notString = m_jit.branch8(JITCompiler::NotEqual, JITCompiler::Address(tempGPR, Structure::typeInfoTypeOffset()), TrustedImm32(StringType));
+            if (child.shouldSpeculateString())
+                speculationCheck(BadType, JSValueSource(valueGPR), node.child1(), notString);
+            m_jit.move(TrustedImmPtr(m_jit.globalData()->smallStrings.stringString(m_jit.globalData())), resultGPR);
+            doneJumps.append(m_jit.jump());
+            if (!child.shouldSpeculateString()) {
+                notString.link(&m_jit);
+                callOperation(operationTypeOf, resultGPR, valueGPR);
+                doneJumps.append(m_jit.jump());
+            }
+        } else {
+            callOperation(operationTypeOf, resultGPR, valueGPR);
+            doneJumps.append(m_jit.jump());
+        }
+
+        if (!child.shouldSpeculateCell()) {
+            isNotCell.link(&m_jit);
+            JITCompiler::Jump notNumber = m_jit.branchTest64(JITCompiler::Zero, valueGPR, GPRInfo::tagTypeNumberRegister);
+            m_jit.move(TrustedImmPtr(m_jit.globalData()->smallStrings.numberString(m_jit.globalData())), resultGPR);
+            doneJumps.append(m_jit.jump());
+            notNumber.link(&m_jit);
+
+            JITCompiler::Jump notUndefined = m_jit.branch64(JITCompiler::NotEqual, valueGPR, JITCompiler::TrustedImm64(ValueUndefined));
+            m_jit.move(TrustedImmPtr(m_jit.globalData()->smallStrings.undefinedString(m_jit.globalData())), resultGPR);
+            doneJumps.append(m_jit.jump());
+            notUndefined.link(&m_jit);
+
+            JITCompiler::Jump notNull = m_jit.branch64(JITCompiler::NotEqual, valueGPR, JITCompiler::TrustedImm64(ValueNull));
+            m_jit.move(TrustedImmPtr(m_jit.globalData()->smallStrings.objectString(m_jit.globalData())), resultGPR);
+            doneJumps.append(m_jit.jump());
+            notNull.link(&m_jit);
+
+            // Only boolean left
+            m_jit.move(TrustedImmPtr(m_jit.globalData()->smallStrings.booleanString(m_jit.globalData())), resultGPR);
+        }
+        doneJumps.link(&m_jit);
+        cellResult(resultGPR, m_compileIndex);
+        break;
+    }
+
     case Flush:
     case Phi:
         break;
index d96bae5..2568062 100644 (file)
@@ -56,9 +56,8 @@ NEVER_INLINE JSValue jsAddSlowCase(CallFrame* callFrame, JSValue v1, JSValue v2)
     return jsNumber(p1.toNumber(callFrame) + p2.toNumber(callFrame));
 }
 
-JSValue jsTypeStringForValue(CallFrame* callFrame, JSValue v)
+JSValue jsTypeStringForValue(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue v)
 {
-    JSGlobalData& globalData = callFrame->globalData();
     if (v.isUndefined())
         return globalData.smallStrings.undefinedString(&globalData);
     if (v.isBoolean())
@@ -70,7 +69,7 @@ JSValue jsTypeStringForValue(CallFrame* callFrame, JSValue v)
     if (v.isObject()) {
         // Return "undefined" for objects that should be treated
         // as null when doing comparisons.
-        if (asObject(v)->structure()->masqueradesAsUndefined(callFrame->lexicalGlobalObject()))
+        if (asObject(v)->structure()->masqueradesAsUndefined(globalObject))
             return globalData.smallStrings.undefinedString(&globalData);
         CallData callData;
         JSObject* object = asObject(v);
@@ -80,6 +79,11 @@ JSValue jsTypeStringForValue(CallFrame* callFrame, JSValue v)
     return globalData.smallStrings.objectString(&globalData);
 }
 
+JSValue jsTypeStringForValue(CallFrame* callFrame, JSValue v)
+{
+    return jsTypeStringForValue(callFrame->globalData(), callFrame->lexicalGlobalObject(), v);
+}
+
 bool jsIsObjectType(CallFrame* callFrame, JSValue v)
 {
     if (!v.isCell())
index 7301bf6..a1a3cc9 100644 (file)
@@ -32,6 +32,7 @@ namespace JSC {
 
     NEVER_INLINE JSValue jsAddSlowCase(CallFrame*, JSValue, JSValue);
     JSValue jsTypeStringForValue(CallFrame*, JSValue);
+    JSValue jsTypeStringForValue(JSGlobalData&, JSGlobalObject*, JSValue);
     bool jsIsObjectType(CallFrame*, JSValue);
     bool jsIsFunctionType(JSValue);