Title: [90324] trunk/Source/_javascript_Core
Revision
90324
Author
[email protected]
Date
2011-07-02 16:08:23 -0700 (Sat, 02 Jul 2011)

Log Message

https://bugs.webkit.org/show_bug.cgi?id=63866
DFG JIT - implement instanceof

Reviewed by Sam Weinig.

Add ops CheckHasInstance & InstanceOf to implement bytecodes
op_check_has_instance & op_instanceof. This is an initial
functional implementation, performance is a wash. We can
follow up with changes to fuse the InstanceOf node with
a subsequant branch, as we do with other comparisons.

* dfg/DFGByteCodeParser.cpp:
(JSC::DFG::ByteCodeParser::parseBlock):
* dfg/DFGJITCompiler.cpp:
(JSC::DFG::JITCompiler::jitAssertIsCell):
* dfg/DFGJITCompiler.h:
(JSC::DFG::JITCompiler::jitAssertIsCell):
* dfg/DFGNode.h:
* dfg/DFGNonSpeculativeJIT.cpp:
(JSC::DFG::NonSpeculativeJIT::compile):
* dfg/DFGOperations.cpp:
* dfg/DFGOperations.h:
* dfg/DFGSpeculativeJIT.cpp:
(JSC::DFG::SpeculativeJIT::compile):

Modified Paths

Diff

Modified: trunk/Source/_javascript_Core/ChangeLog (90323 => 90324)


--- trunk/Source/_javascript_Core/ChangeLog	2011-07-02 21:59:17 UTC (rev 90323)
+++ trunk/Source/_javascript_Core/ChangeLog	2011-07-02 23:08:23 UTC (rev 90324)
@@ -1,3 +1,30 @@
+2011-07-02  Gavin Barraclough  <[email protected]>
+
+        https://bugs.webkit.org/show_bug.cgi?id=63866
+        DFG JIT - implement instanceof
+
+        Reviewed by Sam Weinig.
+
+        Add ops CheckHasInstance & InstanceOf to implement bytecodes
+        op_check_has_instance & op_instanceof. This is an initial
+        functional implementation, performance is a wash. We can
+        follow up with changes to fuse the InstanceOf node with
+        a subsequant branch, as we do with other comparisons.
+
+        * dfg/DFGByteCodeParser.cpp:
+        (JSC::DFG::ByteCodeParser::parseBlock):
+        * dfg/DFGJITCompiler.cpp:
+        (JSC::DFG::JITCompiler::jitAssertIsCell):
+        * dfg/DFGJITCompiler.h:
+        (JSC::DFG::JITCompiler::jitAssertIsCell):
+        * dfg/DFGNode.h:
+        * dfg/DFGNonSpeculativeJIT.cpp:
+        (JSC::DFG::NonSpeculativeJIT::compile):
+        * dfg/DFGOperations.cpp:
+        * dfg/DFGOperations.h:
+        * dfg/DFGSpeculativeJIT.cpp:
+        (JSC::DFG::SpeculativeJIT::compile):
+
 2011-07-01  Oliver Hunt  <[email protected]>
 
         IE Web Workers demo crashes in JSC::SlotVisitor::visitChildren()

Modified: trunk/Source/_javascript_Core/dfg/DFGByteCodeParser.cpp (90323 => 90324)


--- trunk/Source/_javascript_Core/dfg/DFGByteCodeParser.cpp	2011-07-02 21:59:17 UTC (rev 90323)
+++ trunk/Source/_javascript_Core/dfg/DFGByteCodeParser.cpp	2011-07-02 23:08:23 UTC (rev 90324)
@@ -701,6 +701,18 @@
             NEXT_OPCODE(op_mov);
         }
 
+        case op_check_has_instance:
+            addToGraph(CheckHasInstance, get(currentInstruction[1].u.operand));
+            NEXT_OPCODE(op_check_has_instance);
+
+        case op_instanceof: {
+            NodeIndex value = get(currentInstruction[2].u.operand);
+            NodeIndex baseValue = get(currentInstruction[3].u.operand);
+            NodeIndex prototype = get(currentInstruction[4].u.operand);
+            set(currentInstruction[1].u.operand, addToGraph(InstanceOf, value, baseValue, prototype));
+            NEXT_OPCODE(op_instanceof);
+        }
+
         case op_not: {
             ARITHMETIC_OP();
             NodeIndex value = get(currentInstruction[2].u.operand);

Modified: trunk/Source/_javascript_Core/dfg/DFGJITCompiler.cpp (90323 => 90324)


--- trunk/Source/_javascript_Core/dfg/DFGJITCompiler.cpp	2011-07-02 21:59:17 UTC (rev 90323)
+++ trunk/Source/_javascript_Core/dfg/DFGJITCompiler.cpp	2011-07-02 23:08:23 UTC (rev 90324)
@@ -439,6 +439,13 @@
     breakpoint();
     checkJSNumber.link(this);
 }
+
+void JITCompiler::jitAssertIsCell(GPRReg gpr)
+{
+    Jump checkCell = branchTestPtr(MacroAssembler::Zero, gpr, GPRInfo::tagMaskRegister);
+    breakpoint();
+    checkCell.link(this);
+}
 #endif
 
 #if ENABLE(SAMPLING_COUNTERS) && CPU(X86_64) // Or any other 64-bit platform!

Modified: trunk/Source/_javascript_Core/dfg/DFGJITCompiler.h (90323 => 90324)


--- trunk/Source/_javascript_Core/dfg/DFGJITCompiler.h	2011-07-02 21:59:17 UTC (rev 90323)
+++ trunk/Source/_javascript_Core/dfg/DFGJITCompiler.h	2011-07-02 23:08:23 UTC (rev 90324)
@@ -216,11 +216,13 @@
     void jitAssertIsJSInt32(GPRReg);
     void jitAssertIsJSNumber(GPRReg);
     void jitAssertIsJSDouble(GPRReg);
+    void jitAssertIsCell(GPRReg);
 #else
     void jitAssertIsInt32(GPRReg) {}
     void jitAssertIsJSInt32(GPRReg) {}
     void jitAssertIsJSNumber(GPRReg) {}
     void jitAssertIsJSDouble(GPRReg) {}
+    void jitAssertIsCell(GPRReg) {}
 #endif
 
 #if ENABLE(SAMPLING_COUNTERS)

Modified: trunk/Source/_javascript_Core/dfg/DFGNode.h (90323 => 90324)


--- trunk/Source/_javascript_Core/dfg/DFGNode.h	2011-07-02 21:59:17 UTC (rev 90323)
+++ trunk/Source/_javascript_Core/dfg/DFGNode.h	2011-07-02 23:08:23 UTC (rev 90324)
@@ -138,6 +138,8 @@
     \
     /* Nodes for misc operations. */\
     macro(Breakpoint, NodeMustGenerate) \
+    macro(CheckHasInstance, NodeMustGenerate) \
+    macro(InstanceOf, NodeResultJS) \
     macro(LogicalNot, NodeResultJS) \
     \
     /* Block terminals. */\

Modified: trunk/Source/_javascript_Core/dfg/DFGNonSpeculativeJIT.cpp (90323 => 90324)


--- trunk/Source/_javascript_Core/dfg/DFGNonSpeculativeJIT.cpp	2011-07-02 21:59:17 UTC (rev 90323)
+++ trunk/Source/_javascript_Core/dfg/DFGNonSpeculativeJIT.cpp	2011-07-02 23:08:23 UTC (rev 90324)
@@ -943,6 +943,97 @@
         break;
     }
 
+    case CheckHasInstance: {
+        JSValueOperand base(this, node.child1);
+        GPRTemporary structure(this);
+
+        GPRReg baseReg = base.gpr();
+        GPRReg structureReg = structure.gpr();
+
+        // Check that base is a cell.
+        MacroAssembler::Jump baseNotCell = m_jit.branchTestPtr(MacroAssembler::NonZero, baseReg, GPRInfo::tagMaskRegister);
+
+        // Check that base 'ImplementsHasInstance'.
+        m_jit.loadPtr(MacroAssembler::Address(baseReg, JSCell::structureOffset()), structureReg);
+        MacroAssembler::Jump implementsHasInstance = m_jit.branchTest8(MacroAssembler::NonZero, MacroAssembler::Address(structureReg, Structure::typeInfoFlagsOffset()), MacroAssembler::TrustedImm32(ImplementsHasInstance));
+
+        // At this point we always throw, so no need to preserve registers.
+        baseNotCell.link(&m_jit);
+        m_jit.move(baseReg, GPRInfo::argumentGPR1);
+        m_jit.move(GPRInfo::callFrameRegister, GPRInfo::argumentGPR0);
+        // At some point we could optimize this to plant a direct jump, rather then checking
+        // for an exception (operationThrowHasInstanceError always throws). Probably not worth
+        // adding the extra interface to do this now, but we may also want this for op_throw.
+        appendCallWithExceptionCheck(operationThrowHasInstanceError);
+
+        implementsHasInstance.link(&m_jit);
+        noResult(m_compileIndex);
+        break;
+    }
+
+    case InstanceOf: {
+        JSValueOperand value(this, node.child1);
+        JSValueOperand base(this, node.child2);
+        JSValueOperand prototype(this, node.child3);
+        GPRTemporary scratch(this, base);
+
+        GPRReg valueReg = value.gpr();
+        GPRReg baseReg = base.gpr();
+        GPRReg prototypeReg = prototype.gpr();
+        GPRReg scratchReg = scratch.gpr();
+
+        // Check that operands are cells (base is checked by CheckHasInstance, so we can just assert).
+        MacroAssembler::Jump valueNotCell = m_jit.branchTestPtr(MacroAssembler::NonZero, valueReg, GPRInfo::tagMaskRegister);
+        m_jit.jitAssertIsCell(baseReg);
+        MacroAssembler::Jump prototypeNotCell = m_jit.branchTestPtr(MacroAssembler::NonZero, prototypeReg, GPRInfo::tagMaskRegister);
+
+        // Check that baseVal 'ImplementsDefaultHasInstance'.
+        m_jit.loadPtr(MacroAssembler::Address(baseReg, JSCell::structureOffset()), scratchReg);
+        MacroAssembler::Jump notDefaultHasInstance = m_jit.branchTest8(MacroAssembler::Zero, MacroAssembler::Address(scratchReg, Structure::typeInfoFlagsOffset()), TrustedImm32(ImplementsDefaultHasInstance));
+
+        // Check that prototype is an object
+        m_jit.loadPtr(MacroAssembler::Address(prototypeReg, JSCell::structureOffset()), scratchReg);
+        MacroAssembler::Jump protoNotObject = m_jit.branch8(MacroAssembler::NotEqual, MacroAssembler::Address(scratchReg, Structure::typeInfoTypeOffset()), MacroAssembler::TrustedImm32(ObjectType));
+
+        // Initialize scratchReg with the value being checked.
+        m_jit.move(valueReg, scratchReg);
+
+        // Walk up the prototype chain of the value (in scratchReg), comparing to prototypeReg.
+        MacroAssembler::Label loop(&m_jit);
+        m_jit.loadPtr(MacroAssembler::Address(scratchReg, JSCell::structureOffset()), scratchReg);
+        m_jit.loadPtr(MacroAssembler::Address(scratchReg, Structure::prototypeOffset()), scratchReg);
+        MacroAssembler::Jump isInstance = m_jit.branchPtr(MacroAssembler::Equal, scratchReg, prototypeReg);
+        m_jit.branchTestPtr(MacroAssembler::Zero, scratchReg, GPRInfo::tagMaskRegister).linkTo(loop, &m_jit);
+
+        // No match - result is false.
+        m_jit.move(MacroAssembler::TrustedImmPtr(JSValue::encode(jsBoolean(false))), scratchReg);
+        MacroAssembler::Jump wasNotInstance = m_jit.jump();
+
+        // Link to here if any checks fail that require us to try calling out to an operation to help,
+        // e.g. for an API overridden HasInstance.
+        valueNotCell.link(&m_jit);
+        prototypeNotCell.link(&m_jit);
+        notDefaultHasInstance.link(&m_jit);
+        protoNotObject.link(&m_jit);
+
+        silentSpillAllRegisters(scratchReg, valueReg, baseReg, prototypeReg);
+        setupStubArguments(valueReg, baseReg, prototypeReg);
+        m_jit.move(GPRInfo::callFrameRegister, GPRInfo::argumentGPR0);
+        appendCallWithExceptionCheck(operationInstanceOf);
+        m_jit.move(GPRInfo::returnValueGPR, scratchReg);
+        silentFillAllRegisters(scratchReg);
+
+        MacroAssembler::Jump wasNotDefaultHasInstance = m_jit.jump();
+
+        isInstance.link(&m_jit);
+        m_jit.move(MacroAssembler::TrustedImmPtr(JSValue::encode(jsBoolean(true))), scratchReg);
+
+        wasNotInstance.link(&m_jit);
+        wasNotDefaultHasInstance.link(&m_jit);
+        jsValueResult(scratchReg, m_compileIndex);
+        break;
+    }
+
     case Phi:
         ASSERT_NOT_REACHED();
 

Modified: trunk/Source/_javascript_Core/dfg/DFGOperations.cpp (90323 => 90324)


--- trunk/Source/_javascript_Core/dfg/DFGOperations.cpp	2011-07-02 21:59:17 UTC (rev 90323)
+++ trunk/Source/_javascript_Core/dfg/DFGOperations.cpp	2011-07-02 23:08:23 UTC (rev 90324)
@@ -387,6 +387,45 @@
     return JSValue::strictEqual(exec, JSValue::decode(encodedOp1), JSValue::decode(encodedOp2));
 }
 
+EncodedJSValue operationInstanceOf(ExecState* exec, EncodedJSValue encodedValue, EncodedJSValue encodedBase, EncodedJSValue encodedPrototype)
+{
+    JSValue value = JSValue::decode(encodedValue);
+    JSValue base = JSValue::decode(encodedBase);
+    JSValue prototype = JSValue::decode(encodedPrototype);
+
+    // Otherwise CheckHasInstance should have failed.
+    ASSERT(base.isCell());
+    // At least one of these checks must have failed to get to the slow case.
+    ASSERT(!value.isCell()
+        || !prototype.isCell()
+        || !prototype.isObject() 
+        || (base.asCell()->structure()->typeInfo().flags() & (ImplementsHasInstance | OverridesHasInstance)) != ImplementsHasInstance);
+
+
+    // ECMA-262 15.3.5.3:
+    // Throw an exception either if base is not an object, or if it does not implement 'HasInstance' (i.e. is a function).
+    TypeInfo typeInfo(UnspecifiedType);
+    if (!base.isObject() || !(typeInfo = asObject(base)->structure()->typeInfo()).implementsHasInstance()) {
+        throwError(exec, createInvalidParamError(exec, "instanceof", base));
+        return JSValue::encode(jsUndefined());
+    }
+
+    return JSValue::encode(jsBoolean(asObject(base)->hasInstance(exec, value, prototype)));
+}
+
+void operationThrowHasInstanceError(ExecState* exec, EncodedJSValue encodedBase)
+{
+    JSValue base = JSValue::decode(encodedBase);
+
+#ifndef NDEBUG
+    // We should only call this function if base is not an object, or if it does not implement 'HasInstance'.
+    TypeInfo typeInfo(UnspecifiedType);
+    ASSERT(!base.isObject() || !(typeInfo = asObject(base)->structure()->typeInfo()).implementsHasInstance());
+#endif
+
+    throwError(exec, createInvalidParamError(exec, "instanceof", base));
+}
+
 DFGHandler lookupExceptionHandler(ExecState* exec, ReturnAddressPtr faultLocation)
 {
     JSValue exceptionValue = exec->exception();

Modified: trunk/Source/_javascript_Core/dfg/DFGOperations.h (90323 => 90324)


--- trunk/Source/_javascript_Core/dfg/DFGOperations.h	2011-07-02 21:59:17 UTC (rev 90323)
+++ trunk/Source/_javascript_Core/dfg/DFGOperations.h	2011-07-02 23:08:23 UTC (rev 90324)
@@ -61,6 +61,8 @@
 EncodedJSValue operationGetById(ExecState*, EncodedJSValue encodedBase, Identifier*);
 EncodedJSValue operationGetByIdBuildList(ExecState*, EncodedJSValue encodedBase, Identifier*);
 EncodedJSValue operationGetByIdOptimize(ExecState*, EncodedJSValue encodedBase, Identifier*);
+EncodedJSValue operationInstanceOf(ExecState*, EncodedJSValue value, EncodedJSValue base, EncodedJSValue prototype);
+void operationThrowHasInstanceError(ExecState*, EncodedJSValue base);
 void operationPutByValStrict(ExecState*, EncodedJSValue encodedBase, EncodedJSValue encodedProperty, EncodedJSValue encodedValue);
 void operationPutByValNonStrict(ExecState*, EncodedJSValue encodedBase, EncodedJSValue encodedProperty, EncodedJSValue encodedValue);
 void operationPutByValBeyondArrayBounds(ExecState*, JSArray*, int32_t index, EncodedJSValue encodedValue);

Modified: trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.cpp (90323 => 90324)


--- trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.cpp	2011-07-02 21:59:17 UTC (rev 90323)
+++ trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.cpp	2011-07-02 23:08:23 UTC (rev 90324)
@@ -1103,6 +1103,55 @@
         break;
     }
 
+    case CheckHasInstance: {
+        SpeculateCellOperand base(this, node.child1);
+        GPRTemporary structure(this);
+
+        // Speculate that base 'ImplementsDefaultHasInstance'.
+        m_jit.loadPtr(MacroAssembler::Address(base.gpr(), JSCell::structureOffset()), structure.gpr());
+        speculationCheck(m_jit.branchTest8(MacroAssembler::Zero, MacroAssembler::Address(structure.gpr(), Structure::typeInfoFlagsOffset()), MacroAssembler::TrustedImm32(ImplementsDefaultHasInstance)));
+
+        noResult(m_compileIndex);
+        break;
+    }
+
+    case InstanceOf: {
+        SpeculateCellOperand value(this, node.child1);
+        // Base unused since we speculate default InstanceOf behaviour in CheckHasInstance.
+        SpeculateCellOperand prototype(this, node.child3);
+
+        GPRTemporary scratch(this);
+
+        GPRReg valueReg = value.gpr();
+        GPRReg prototypeReg = prototype.gpr();
+        GPRReg scratchReg = scratch.gpr();
+
+        // Check that prototype is an object.
+        m_jit.loadPtr(MacroAssembler::Address(prototypeReg, JSCell::structureOffset()), scratchReg);
+        speculationCheck(m_jit.branch8(MacroAssembler::NotEqual, MacroAssembler::Address(scratchReg, Structure::typeInfoTypeOffset()), MacroAssembler::TrustedImm32(ObjectType)));
+
+        // Initialize scratchReg with the value being checked.
+        m_jit.move(valueReg, scratchReg);
+
+        // Walk up the prototype chain of the value (in scratchReg), comparing to prototypeReg.
+        MacroAssembler::Label loop(&m_jit);
+        m_jit.loadPtr(MacroAssembler::Address(scratchReg, JSCell::structureOffset()), scratchReg);
+        m_jit.loadPtr(MacroAssembler::Address(scratchReg, Structure::prototypeOffset()), scratchReg);
+        MacroAssembler::Jump isInstance = m_jit.branchPtr(MacroAssembler::Equal, scratchReg, prototypeReg);
+        m_jit.branchTestPtr(MacroAssembler::Zero, scratchReg, GPRInfo::tagMaskRegister).linkTo(loop, &m_jit);
+
+        // No match - result is false.
+        m_jit.move(MacroAssembler::TrustedImmPtr(JSValue::encode(jsBoolean(false))), scratchReg);
+        MacroAssembler::Jump putResult = m_jit.jump();
+
+        isInstance.link(&m_jit);
+        m_jit.move(MacroAssembler::TrustedImmPtr(JSValue::encode(jsBoolean(true))), scratchReg);
+
+        putResult.link(&m_jit);
+        jsValueResult(scratchReg, m_compileIndex);
+        break;
+    }
+
     case Phi:
         ASSERT_NOT_REACHED();
 
_______________________________________________
webkit-changes mailing list
[email protected]
http://lists.webkit.org/mailman/listinfo.cgi/webkit-changes

Reply via email to