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();