Cache inheritorID on JSFunction
https://bugs.webkit.org/show_bug.cgi?id=85853
Reviewed by Geoff Garen & Filip Pizlo.
An object's prototype is indicated via its structure. To create an otherwise
empty object with object A as its prototype, we require a structure with its
prototype set to point to A. We wish to use this same structure for all empty
objects created with a prototype of A, so we presently store this structure as
a property of A, known as the inheritorID.
When a function F is invoked as a constructor, where F has a property 'prototype'
set to point to A, in order to create the 'this' value for the constructor to
use the following steps are taken:
- the 'prototype' proptery of F is read, via a regular [[Get]] access.
- the inheritorID internal property of the prototype is read.
- a new, empty object is constructed with its structure set to point to inheritorID.
There are two drawbacks to the current approach:
- it requires that every object has an inheritorID field.
- it requires a [[Get]] access on every constructor call to access the 'prototype' property.
Instead, switch to caching a copy of the inheritorID on the function. Constructor
calls now only need read the internal property from the callee, saving a [[Get]].
This also means that JSObject::m_inheritorID is no longer commonly read, and in a
future patch we can move to storing this in a more memory efficient fashion.
* JavaScriptCore.xcodeproj/project.pbxproj:
* bytecode/CodeBlock.cpp:
(JSC::CodeBlock::dump):
* bytecode/Opcode.h:
(JSC):
(JSC::padOpcodeName):
* bytecompiler/BytecodeGenerator.cpp:
(JSC::BytecodeGenerator::BytecodeGenerator):
* dfg/DFGAbstractState.cpp:
(JSC::DFG::AbstractState::execute):
* dfg/DFGByteCodeParser.cpp:
(JSC::DFG::ByteCodeParser::parseBlock):
* dfg/DFGNodeType.h:
(DFG):
* dfg/DFGOperations.cpp:
* dfg/DFGOperations.h:
* dfg/DFGPredictionPropagationPhase.cpp:
(JSC::DFG::PredictionPropagationPhase::propagate):
* dfg/DFGSpeculativeJIT32_64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* dfg/DFGSpeculativeJIT64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* interpreter/Interpreter.cpp:
(JSC::Interpreter::privateExecute):
* jit/JITInlineMethods.h:
(JSC::JIT::emitAllocateJSFunction):
* jit/JITOpcodes.cpp:
(JSC::JIT::emit_op_create_this):
(JSC::JIT::emitSlow_op_create_this):
* jit/JITOpcodes32_64.cpp:
(JSC::JIT::emit_op_create_this):
(JSC::JIT::emitSlow_op_create_this):
* jit/JITStubs.cpp:
(JSC::DEFINE_STUB_FUNCTION):
* llint/LLIntSlowPaths.cpp:
(JSC::LLInt::LLINT_SLOW_PATH_DECL):
* llint/LowLevelInterpreter32_64.asm:
* llint/LowLevelInterpreter64.asm:
* runtime/JSFunction.cpp:
(JSC::JSFunction::JSFunction):
(JSC::JSFunction::cacheInheritorID):
(JSC):
(JSC::JSFunction::put):
(JSC::JSFunction::defineOwnProperty):
* runtime/JSFunction.h:
(JSC::JSFunction::cachedInheritorID):
(JSFunction):
(JSC::JSFunction::offsetOfCachedInheritorID):
git-svn-id: http://svn.webkit.org/repository/webkit/trunk@116670 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/Source/JavaScriptCore/ChangeLog b/Source/JavaScriptCore/ChangeLog
index 8aef492..63967f5 100644
--- a/Source/JavaScriptCore/ChangeLog
+++ b/Source/JavaScriptCore/ChangeLog
@@ -1,3 +1,81 @@
+2012-05-10 Gavin Barraclough <barraclough@apple.com>
+
+ Cache inheritorID on JSFunction
+ https://bugs.webkit.org/show_bug.cgi?id=85853
+
+ Reviewed by Geoff Garen & Filip Pizlo.
+
+ An object's prototype is indicated via its structure. To create an otherwise
+ empty object with object A as its prototype, we require a structure with its
+ prototype set to point to A. We wish to use this same structure for all empty
+ objects created with a prototype of A, so we presently store this structure as
+ a property of A, known as the inheritorID.
+
+ When a function F is invoked as a constructor, where F has a property 'prototype'
+ set to point to A, in order to create the 'this' value for the constructor to
+ use the following steps are taken:
+ - the 'prototype' proptery of F is read, via a regular [[Get]] access.
+ - the inheritorID internal property of the prototype is read.
+ - a new, empty object is constructed with its structure set to point to inheritorID.
+
+ There are two drawbacks to the current approach:
+ - it requires that every object has an inheritorID field.
+ - it requires a [[Get]] access on every constructor call to access the 'prototype' property.
+
+ Instead, switch to caching a copy of the inheritorID on the function. Constructor
+ calls now only need read the internal property from the callee, saving a [[Get]].
+ This also means that JSObject::m_inheritorID is no longer commonly read, and in a
+ future patch we can move to storing this in a more memory efficient fashion.
+
+ * JavaScriptCore.xcodeproj/project.pbxproj:
+ * bytecode/CodeBlock.cpp:
+ (JSC::CodeBlock::dump):
+ * bytecode/Opcode.h:
+ (JSC):
+ (JSC::padOpcodeName):
+ * bytecompiler/BytecodeGenerator.cpp:
+ (JSC::BytecodeGenerator::BytecodeGenerator):
+ * dfg/DFGAbstractState.cpp:
+ (JSC::DFG::AbstractState::execute):
+ * dfg/DFGByteCodeParser.cpp:
+ (JSC::DFG::ByteCodeParser::parseBlock):
+ * dfg/DFGNodeType.h:
+ (DFG):
+ * dfg/DFGOperations.cpp:
+ * dfg/DFGOperations.h:
+ * dfg/DFGPredictionPropagationPhase.cpp:
+ (JSC::DFG::PredictionPropagationPhase::propagate):
+ * dfg/DFGSpeculativeJIT32_64.cpp:
+ (JSC::DFG::SpeculativeJIT::compile):
+ * dfg/DFGSpeculativeJIT64.cpp:
+ (JSC::DFG::SpeculativeJIT::compile):
+ * interpreter/Interpreter.cpp:
+ (JSC::Interpreter::privateExecute):
+ * jit/JITInlineMethods.h:
+ (JSC::JIT::emitAllocateJSFunction):
+ * jit/JITOpcodes.cpp:
+ (JSC::JIT::emit_op_create_this):
+ (JSC::JIT::emitSlow_op_create_this):
+ * jit/JITOpcodes32_64.cpp:
+ (JSC::JIT::emit_op_create_this):
+ (JSC::JIT::emitSlow_op_create_this):
+ * jit/JITStubs.cpp:
+ (JSC::DEFINE_STUB_FUNCTION):
+ * llint/LLIntSlowPaths.cpp:
+ (JSC::LLInt::LLINT_SLOW_PATH_DECL):
+ * llint/LowLevelInterpreter32_64.asm:
+ * llint/LowLevelInterpreter64.asm:
+ * runtime/JSFunction.cpp:
+ (JSC::JSFunction::JSFunction):
+ (JSC::JSFunction::cacheInheritorID):
+ (JSC):
+ (JSC::JSFunction::put):
+ (JSC::JSFunction::defineOwnProperty):
+ * runtime/JSFunction.h:
+ (JSC::JSFunction::cachedInheritorID):
+ (JSFunction):
+ (JSC::JSFunction::offsetOfCachedInheritorID):
+
2012-05-10 Michael Saboff <msaboff@apple.com>
Enh: Hash Const JSString in Backing Stores to Save Memory
diff --git a/Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj b/Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj
index d5cf8de..c5a5b63 100644
--- a/Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj
+++ b/Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj
@@ -1041,6 +1041,9 @@
86880F4C14353B2100B08D42 /* DFGSpeculativeJIT64.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = DFGSpeculativeJIT64.cpp; path = dfg/DFGSpeculativeJIT64.cpp; sourceTree = "<group>"; };
869D04AE1193B54D00803475 /* CachedTranscendentalFunction.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CachedTranscendentalFunction.h; sourceTree = "<group>"; };
869EBCB60E8C6D4A008722CC /* ResultType.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ResultType.h; sourceTree = "<group>"; };
+ 86A054461556451B00445157 /* LowLevelInterpreter.asm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.asm.asm; name = LowLevelInterpreter.asm; path = llint/LowLevelInterpreter.asm; sourceTree = "<group>"; };
+ 86A054471556451B00445157 /* LowLevelInterpreter32_64.asm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.asm.asm; name = LowLevelInterpreter32_64.asm; path = llint/LowLevelInterpreter32_64.asm; sourceTree = "<group>"; };
+ 86A054481556451B00445157 /* LowLevelInterpreter64.asm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.asm.asm; name = LowLevelInterpreter64.asm; path = llint/LowLevelInterpreter64.asm; sourceTree = "<group>"; };
86A90ECF0EE7D51F00AB350D /* JITArithmetic.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JITArithmetic.cpp; sourceTree = "<group>"; };
86ADD1430FDDEA980006EEC2 /* ARMv7Assembler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ARMv7Assembler.h; sourceTree = "<group>"; };
86ADD1440FDDEA980006EEC2 /* MacroAssemblerARMv7.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MacroAssemblerARMv7.h; sourceTree = "<group>"; };
@@ -1430,7 +1433,6 @@
45E12D8806A49B0F00E9DF84 /* jsc.cpp */,
A767FF9F14F4502900789059 /* JSCTypedArrayStubs.h */,
F68EBB8C0255D4C601FF60F7 /* config.h */,
- 0F46809C14BA7F4D00BFE272 /* llint */,
1432EBD70A34CAD400717B9F /* API */,
9688CB120ED12B4E001D649F /* assembler */,
969A078F0ED1D3AE00F1F681 /* bytecode */,
@@ -1440,6 +1442,7 @@
142E312A134FF0A600AFADB5 /* heap */,
1429D77A0ED20D7300B89619 /* interpreter */,
1429D92C0ED22D7000B89619 /* jit */,
+ 0F46809C14BA7F4D00BFE272 /* llint */,
7E39D8370EC3A388003AF11A /* parser */,
95AB831A0DA42C6900BC83F3 /* profiler */,
7EF6E0BB0EB7A1EC0079AFAF /* runtime */,
@@ -1489,6 +1492,9 @@
0F46809F14BA7F8200BFE272 /* LLIntSlowPaths.cpp */,
0F4680A014BA7F8200BFE272 /* LLIntSlowPaths.h */,
0F4680A114BA7F8200BFE272 /* LLIntOffsetsExtractor.cpp */,
+ 86A054461556451B00445157 /* LowLevelInterpreter.asm */,
+ 86A054471556451B00445157 /* LowLevelInterpreter32_64.asm */,
+ 86A054481556451B00445157 /* LowLevelInterpreter64.asm */,
);
name = llint;
sourceTree = "<group>";
diff --git a/Source/JavaScriptCore/bytecode/CodeBlock.cpp b/Source/JavaScriptCore/bytecode/CodeBlock.cpp
index 7b828ac..6634b94 100644
--- a/Source/JavaScriptCore/bytecode/CodeBlock.cpp
+++ b/Source/JavaScriptCore/bytecode/CodeBlock.cpp
@@ -519,8 +519,7 @@
}
case op_create_this: {
int r0 = (++it)->u.operand;
- int r1 = (++it)->u.operand;
- dataLog("[%4d] create_this %s %s\n", location, registerName(exec, r0).data(), registerName(exec, r1).data());
+ dataLog("[%4d] create_this %s\n", location, registerName(exec, r0).data());
break;
}
case op_convert_this: {
diff --git a/Source/JavaScriptCore/bytecode/Opcode.h b/Source/JavaScriptCore/bytecode/Opcode.h
index a564de2..a836a4d 100644
--- a/Source/JavaScriptCore/bytecode/Opcode.h
+++ b/Source/JavaScriptCore/bytecode/Opcode.h
@@ -42,7 +42,7 @@
macro(op_create_activation, 2) \
macro(op_init_lazy_reg, 2) \
macro(op_create_arguments, 2) \
- macro(op_create_this, 3) \
+ macro(op_create_this, 2) \
macro(op_get_callee, 2) \
macro(op_convert_this, 2) \
\
diff --git a/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp b/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp
index a520a4c7..b12bf40 100644
--- a/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp
+++ b/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp
@@ -442,17 +442,8 @@
preserveLastVar();
if (isConstructor()) {
- RefPtr<RegisterID> func = newTemporary();
- RefPtr<RegisterID> funcProto = newTemporary();
-
- emitOpcode(op_get_callee);
- instructions().append(func->index());
- // Load prototype.
- emitGetById(funcProto.get(), func.get(), globalData()->propertyNames->prototype);
-
emitOpcode(op_create_this);
instructions().append(m_thisRegister.index());
- instructions().append(funcProto->index());
} else if (!codeBlock->isStrictMode() && (functionBody->usesThis() || codeBlock->usesEval() || m_shouldEmitDebugHooks)) {
emitOpcode(op_convert_this);
instructions().append(m_thisRegister.index());
diff --git a/Source/JavaScriptCore/dfg/DFGAbstractState.cpp b/Source/JavaScriptCore/dfg/DFGAbstractState.cpp
index 6df40ca..3eb5463 100644
--- a/Source/JavaScriptCore/dfg/DFGAbstractState.cpp
+++ b/Source/JavaScriptCore/dfg/DFGAbstractState.cpp
@@ -774,19 +774,16 @@
destination.merge(PredictObjectOther);
break;
}
-
+
case CreateThis: {
- Node& child = m_graph[node.child1()];
AbstractValue& source = forNode(node.child1());
AbstractValue& destination = forNode(nodeIndex);
- if (child.shouldSpeculateFinalObject())
- source.filter(PredictFinalObject);
-
+ source.filter(PredictFunction);
destination.set(PredictFinalObject);
break;
}
-
+
case NewObject:
forNode(nodeIndex).set(m_codeBlock->globalObjectFor(node.codeOrigin)->emptyObjectStructure());
m_haveStructures = true;
diff --git a/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp b/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp
index 7a2d7bd..60841ee 100644
--- a/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp
+++ b/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp
@@ -1534,8 +1534,10 @@
}
case op_create_this: {
- NodeIndex op1 = get(currentInstruction[2].u.operand);
- set(currentInstruction[1].u.operand, addToGraph(CreateThis, op1));
+ if (m_inlineStackTop->m_inlineCallFrame)
+ set(currentInstruction[1].u.operand, addToGraph(CreateThis, getDirect(m_inlineStackTop->m_calleeVR)));
+ else
+ set(currentInstruction[1].u.operand, addToGraph(CreateThis, addToGraph(GetCallee)));
NEXT_OPCODE(op_create_this);
}
diff --git a/Source/JavaScriptCore/dfg/DFGOperations.cpp b/Source/JavaScriptCore/dfg/DFGOperations.cpp
index 0e6e2f9..dfaf5df 100644
--- a/Source/JavaScriptCore/dfg/DFGOperations.cpp
+++ b/Source/JavaScriptCore/dfg/DFGOperations.cpp
@@ -209,39 +209,17 @@
return JSValue::encode(JSValue::decode(encodedOp).toThisObject(exec));
}
-inline JSCell* createThis(ExecState* exec, JSCell* prototype, JSFunction* constructor)
+JSCell* DFG_OPERATION operationCreateThis(ExecState* exec, JSCell* constructor)
{
+ JSGlobalData* globalData = &exec->globalData();
+ NativeCallFrameTracer tracer(globalData, exec);
+
#if !ASSERT_DISABLED
ConstructData constructData;
- ASSERT(constructor->methodTable()->getConstructData(constructor, constructData) == ConstructTypeJS);
+ ASSERT(jsCast<JSFunction*>(constructor)->methodTable()->getConstructData(jsCast<JSFunction*>(constructor), constructData) == ConstructTypeJS);
#endif
- JSGlobalData& globalData = exec->globalData();
- NativeCallFrameTracer tracer(&globalData, exec);
-
- Structure* structure;
- if (prototype->isObject())
- structure = asObject(prototype)->inheritorID(globalData);
- else
- structure = constructor->scope()->globalObject->emptyObjectStructure();
-
- return constructEmptyObject(exec, structure);
-}
-
-JSCell* DFG_OPERATION operationCreateThis(ExecState* exec, JSCell* prototype)
-{
- JSGlobalData* globalData = &exec->globalData();
- NativeCallFrameTracer tracer(globalData, exec);
-
- return createThis(exec, prototype, jsCast<JSFunction*>(exec->callee()));
-}
-
-JSCell* DFG_OPERATION operationCreateThisInlined(ExecState* exec, JSCell* prototype, JSCell* constructor)
-{
- JSGlobalData* globalData = &exec->globalData();
- NativeCallFrameTracer tracer(globalData, exec);
-
- return createThis(exec, prototype, jsCast<JSFunction*>(constructor));
+ return constructEmptyObject(exec, jsCast<JSFunction*>(constructor)->cachedInheritorID(exec));
}
JSCell* DFG_OPERATION operationNewObject(ExecState* exec)
diff --git a/Source/JavaScriptCore/dfg/DFGOperations.h b/Source/JavaScriptCore/dfg/DFGOperations.h
index 52e99cb..601ed76 100644
--- a/Source/JavaScriptCore/dfg/DFGOperations.h
+++ b/Source/JavaScriptCore/dfg/DFGOperations.h
@@ -97,8 +97,7 @@
// These routines are provide callbacks out to C++ implementations of operations too complex to JIT.
JSCell* DFG_OPERATION operationNewObject(ExecState*);
-JSCell* DFG_OPERATION operationCreateThis(ExecState*, JSCell* encodedOp1);
-JSCell* DFG_OPERATION operationCreateThisInlined(ExecState*, JSCell* encodedOp1, JSCell* constructor);
+JSCell* DFG_OPERATION operationCreateThis(ExecState*, JSCell* constructor);
EncodedJSValue DFG_OPERATION operationConvertThis(ExecState*, EncodedJSValue encodedOp1);
EncodedJSValue DFG_OPERATION operationValueAdd(ExecState*, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2);
EncodedJSValue DFG_OPERATION operationValueAddNotNumber(ExecState*, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2);
diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp
index c156e81..05c418d 100644
--- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp
+++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp
@@ -3029,32 +3029,17 @@
// then we speculate because we want to get recompiled if it isn't (since
// otherwise we'd start taking slow path a lot).
- SpeculateCellOperand proto(this, node.child1());
+ SpeculateCellOperand callee(this, node.child1());
GPRTemporary result(this);
GPRTemporary scratch(this);
- GPRReg protoGPR = proto.gpr();
+ GPRReg calleeGPR = callee.gpr();
GPRReg resultGPR = result.gpr();
GPRReg scratchGPR = scratch.gpr();
- proto.use();
-
+ // Load the inheritorID. If the inheritorID is not set, go to slow path.
+ m_jit.loadPtr(MacroAssembler::Address(calleeGPR, JSFunction::offsetOfCachedInheritorID()), scratchGPR);
MacroAssembler::JumpList slowPath;
-
- // Need to verify that the prototype is an object. If we have reason to believe
- // that it's a FinalObject then we speculate on that directly. Otherwise we
- // do the slow (structure-based) check.
- if (at(node.child1()).shouldSpeculateFinalObject()) {
- if (!isFinalObjectPrediction(m_state.forNode(node.child1()).m_type))
- speculationCheck(BadType, JSValueSource::unboxedCell(protoGPR), node.child1(), m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(protoGPR, JSCell::classInfoOffset()), MacroAssembler::TrustedImmPtr(&JSFinalObject::s_info)));
- } else {
- m_jit.loadPtr(MacroAssembler::Address(protoGPR, JSCell::structureOffset()), scratchGPR);
- slowPath.append(m_jit.branch8(MacroAssembler::Below, MacroAssembler::Address(scratchGPR, Structure::typeInfoTypeOffset()), MacroAssembler::TrustedImm32(ObjectType)));
- }
-
- // Load the inheritorID (the Structure that objects who have protoGPR as the prototype
- // use to refer to that prototype). If the inheritorID is not set, go to slow path.
- m_jit.loadPtr(MacroAssembler::Address(protoGPR, JSObject::offsetOfInheritorID()), scratchGPR);
slowPath.append(m_jit.branchTestPtr(MacroAssembler::Zero, scratchGPR));
emitAllocateJSFinalObject(scratchGPR, resultGPR, scratchGPR, slowPath);
@@ -3064,15 +3049,12 @@
slowPath.link(&m_jit);
silentSpillAllRegisters(resultGPR);
- if (node.codeOrigin.inlineCallFrame)
- callOperation(operationCreateThisInlined, resultGPR, protoGPR, node.codeOrigin.inlineCallFrame->callee.get());
- else
- callOperation(operationCreateThis, resultGPR, protoGPR);
+ callOperation(operationCreateThis, resultGPR, calleeGPR);
silentFillAllRegisters(resultGPR);
done.link(&m_jit);
- cellResult(resultGPR, m_compileIndex, UseChildrenCalledExplicitly);
+ cellResult(resultGPR, m_compileIndex);
break;
}
diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp
index a46f8f2..08e7d96 100644
--- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp
+++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp
@@ -3053,32 +3053,17 @@
// then we speculate because we want to get recompiled if it isn't (since
// otherwise we'd start taking slow path a lot).
- SpeculateCellOperand proto(this, node.child1());
+ SpeculateCellOperand callee(this, node.child1());
GPRTemporary result(this);
GPRTemporary scratch(this);
- GPRReg protoGPR = proto.gpr();
+ GPRReg calleeGPR = callee.gpr();
GPRReg resultGPR = result.gpr();
GPRReg scratchGPR = scratch.gpr();
- proto.use();
-
+ // Load the inheritorID. If the inheritorID is not set, go to slow path.
+ m_jit.loadPtr(MacroAssembler::Address(calleeGPR, JSFunction::offsetOfCachedInheritorID()), scratchGPR);
MacroAssembler::JumpList slowPath;
-
- // Need to verify that the prototype is an object. If we have reason to believe
- // that it's a FinalObject then we speculate on that directly. Otherwise we
- // do the slow (structure-based) check.
- if (at(node.child1()).shouldSpeculateFinalObject()) {
- if (!isFinalObjectPrediction(m_state.forNode(node.child1()).m_type))
- speculationCheck(BadType, JSValueRegs(protoGPR), node.child1(), m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(protoGPR, JSCell::classInfoOffset()), MacroAssembler::TrustedImmPtr(&JSFinalObject::s_info)));
- } else {
- m_jit.loadPtr(MacroAssembler::Address(protoGPR, JSCell::structureOffset()), scratchGPR);
- slowPath.append(m_jit.branch8(MacroAssembler::Below, MacroAssembler::Address(scratchGPR, Structure::typeInfoTypeOffset()), MacroAssembler::TrustedImm32(ObjectType)));
- }
-
- // Load the inheritorID (the Structure that objects who have protoGPR as the prototype
- // use to refer to that prototype). If the inheritorID is not set, go to slow path.
- m_jit.loadPtr(MacroAssembler::Address(protoGPR, JSObject::offsetOfInheritorID()), scratchGPR);
slowPath.append(m_jit.branchTestPtr(MacroAssembler::Zero, scratchGPR));
emitAllocateJSFinalObject(scratchGPR, resultGPR, scratchGPR, slowPath);
@@ -3088,15 +3073,12 @@
slowPath.link(&m_jit);
silentSpillAllRegisters(resultGPR);
- if (node.codeOrigin.inlineCallFrame)
- callOperation(operationCreateThisInlined, resultGPR, protoGPR, node.codeOrigin.inlineCallFrame->callee.get());
- else
- callOperation(operationCreateThis, resultGPR, protoGPR);
+ callOperation(operationCreateThis, resultGPR, calleeGPR);
silentFillAllRegisters(resultGPR);
done.link(&m_jit);
- cellResult(resultGPR, m_compileIndex, UseChildrenCalledExplicitly);
+ cellResult(resultGPR, m_compileIndex);
break;
}
diff --git a/Source/JavaScriptCore/interpreter/Interpreter.cpp b/Source/JavaScriptCore/interpreter/Interpreter.cpp
index a358b9a..cae2ffb 100644
--- a/Source/JavaScriptCore/interpreter/Interpreter.cpp
+++ b/Source/JavaScriptCore/interpreter/Interpreter.cpp
@@ -4809,7 +4809,6 @@
*/
int thisRegister = vPC[1].u.operand;
- int protoRegister = vPC[2].u.operand;
JSFunction* constructor = jsCast<JSFunction*>(callFrame->callee());
#if !ASSERT_DISABLED
@@ -4817,12 +4816,7 @@
ASSERT(constructor->methodTable()->getConstructData(constructor, constructData) == ConstructTypeJS);
#endif
- Structure* structure;
- JSValue proto = callFrame->r(protoRegister).jsValue();
- if (proto.isObject())
- structure = asObject(proto)->inheritorID(callFrame->globalData());
- else
- structure = constructor->scope()->globalObject->emptyObjectStructure();
+ Structure* structure = constructor->cachedInheritorID(callFrame);
callFrame->uncheckedR(thisRegister) = constructEmptyObject(callFrame, structure);
vPC += OPCODE_LENGTH(op_create_this);
diff --git a/Source/JavaScriptCore/jit/JITInlineMethods.h b/Source/JavaScriptCore/jit/JITInlineMethods.h
index cd33821..40985ac 100644
--- a/Source/JavaScriptCore/jit/JITInlineMethods.h
+++ b/Source/JavaScriptCore/jit/JITInlineMethods.h
@@ -448,6 +448,9 @@
// store the function's executable member
storePtr(TrustedImmPtr(executable), Address(result, JSFunction::offsetOfExecutable()));
+ // clear the function's inheritorID
+ storePtr(TrustedImmPtr(0), Address(result, JSFunction::offsetOfCachedInheritorID()));
+
// store the function's name
ASSERT(executable->nameValue());
int functionNameOffset = sizeof(JSValue) * m_codeBlock->globalObject()->functionNameOffset();
diff --git a/Source/JavaScriptCore/jit/JITOpcodes.cpp b/Source/JavaScriptCore/jit/JITOpcodes.cpp
index d68f410..2c9d9c1 100644
--- a/Source/JavaScriptCore/jit/JITOpcodes.cpp
+++ b/Source/JavaScriptCore/jit/JITOpcodes.cpp
@@ -1272,33 +1272,22 @@
void JIT::emit_op_create_this(Instruction* currentInstruction)
{
- emitGetVirtualRegister(currentInstruction[2].u.operand, regT2);
- emitJumpSlowCaseIfNotJSCell(regT2, currentInstruction[2].u.operand);
- loadPtr(Address(regT2, JSCell::structureOffset()), regT1);
- addSlowCase(emitJumpIfNotObject(regT1));
-
- // now we know that the prototype is an object, but we don't know if it's got an
- // inheritor ID
-
- loadPtr(Address(regT2, JSObject::offsetOfInheritorID()), regT2);
+ emitGetFromCallFrameHeaderPtr(RegisterFile::Callee, regT0);
+ loadPtr(Address(regT0, JSFunction::offsetOfCachedInheritorID()), regT2);
addSlowCase(branchTestPtr(Zero, regT2));
// now regT2 contains the inheritorID, which is the structure that the newly
// allocated object will have.
emitAllocateJSFinalObject(regT2, regT0, regT1);
-
emitPutVirtualRegister(currentInstruction[1].u.operand);
}
void JIT::emitSlow_op_create_this(Instruction* currentInstruction, Vector<SlowCaseEntry>::iterator& iter)
{
- linkSlowCaseIfNotJSCell(iter, currentInstruction[2].u.operand); // not a cell
- linkSlowCase(iter); // not an object
linkSlowCase(iter); // doesn't have an inheritor ID
linkSlowCase(iter); // allocation failed
JITStubCall stubCall(this, cti_op_create_this);
- stubCall.addArgument(currentInstruction[2].u.operand, regT1);
stubCall.call(currentInstruction[1].u.operand);
}
diff --git a/Source/JavaScriptCore/jit/JITOpcodes32_64.cpp b/Source/JavaScriptCore/jit/JITOpcodes32_64.cpp
index 76e11e4..81b4062 100644
--- a/Source/JavaScriptCore/jit/JITOpcodes32_64.cpp
+++ b/Source/JavaScriptCore/jit/JITOpcodes32_64.cpp
@@ -1532,35 +1532,22 @@
void JIT::emit_op_create_this(Instruction* currentInstruction)
{
- emitLoad(currentInstruction[2].u.operand, regT1, regT0);
- emitJumpSlowCaseIfNotJSCell(currentInstruction[2].u.operand, regT1);
- loadPtr(Address(regT0, JSCell::structureOffset()), regT1);
- addSlowCase(emitJumpIfNotObject(regT1));
-
- // now we know that the prototype is an object, but we don't know if it's got an
- // inheritor ID
-
- loadPtr(Address(regT0, JSObject::offsetOfInheritorID()), regT2);
+ emitGetFromCallFrameHeaderPtr(RegisterFile::Callee, regT0);
+ loadPtr(Address(regT0, JSFunction::offsetOfCachedInheritorID()), regT2);
addSlowCase(branchTestPtr(Zero, regT2));
// now regT2 contains the inheritorID, which is the structure that the newly
// allocated object will have.
emitAllocateJSFinalObject(regT2, regT0, regT1);
-
emitStoreCell(currentInstruction[1].u.operand, regT0);
}
void JIT::emitSlow_op_create_this(Instruction* currentInstruction, Vector<SlowCaseEntry>::iterator& iter)
{
- linkSlowCaseIfNotJSCell(iter, currentInstruction[2].u.operand); // not a cell
- linkSlowCase(iter); // not an object
linkSlowCase(iter); // doesn't have an inheritor ID
linkSlowCase(iter); // allocation failed
- unsigned protoRegister = currentInstruction[2].u.operand;
- emitLoad(protoRegister, regT1, regT0);
JITStubCall stubCall(this, cti_op_create_this);
- stubCall.addArgument(regT1, regT0);
stubCall.call(currentInstruction[1].u.operand);
}
diff --git a/Source/JavaScriptCore/jit/JITStubs.cpp b/Source/JavaScriptCore/jit/JITStubs.cpp
index 29d9954..73f4892 100644
--- a/Source/JavaScriptCore/jit/JITStubs.cpp
+++ b/Source/JavaScriptCore/jit/JITStubs.cpp
@@ -1286,12 +1286,7 @@
ASSERT(constructor->methodTable()->getConstructData(constructor, constructData) == ConstructTypeJS);
#endif
- Structure* structure;
- JSValue proto = stackFrame.args[0].jsValue();
- if (proto.isObject())
- structure = asObject(proto)->inheritorID(*stackFrame.globalData);
- else
- structure = constructor->scope()->globalObject->emptyObjectStructure();
+ Structure* structure = constructor->cachedInheritorID(callFrame);
JSValue result = constructEmptyObject(callFrame, structure);
return JSValue::encode(result);
diff --git a/Source/JavaScriptCore/llint/LLIntSlowPaths.cpp b/Source/JavaScriptCore/llint/LLIntSlowPaths.cpp
index 7ada710..d184b6e 100644
--- a/Source/JavaScriptCore/llint/LLIntSlowPaths.cpp
+++ b/Source/JavaScriptCore/llint/LLIntSlowPaths.cpp
@@ -458,13 +458,7 @@
ASSERT(constructor->methodTable()->getConstructData(constructor, constructData) == ConstructTypeJS);
#endif
- Structure* structure;
- JSValue proto = LLINT_OP(2).jsValue();
- if (proto.isObject())
- structure = asObject(proto)->inheritorID(globalData);
- else
- structure = constructor->scope()->globalObject->emptyObjectStructure();
-
+ Structure* structure = constructor->cachedInheritorID(exec);
LLINT_RETURN(constructEmptyObject(exec, structure));
}
diff --git a/Source/JavaScriptCore/llint/LowLevelInterpreter32_64.asm b/Source/JavaScriptCore/llint/LowLevelInterpreter32_64.asm
index fe244d4..1f38e31 100644
--- a/Source/JavaScriptCore/llint/LowLevelInterpreter32_64.asm
+++ b/Source/JavaScriptCore/llint/LowLevelInterpreter32_64.asm
@@ -343,23 +343,18 @@
_llint_op_create_this:
traceExecution()
- loadi 8[PC], t0
- assertNotConstant(t0)
- bineq TagOffset[cfr, t0, 8], CellTag, .opCreateThisSlow
- loadi PayloadOffset[cfr, t0, 8], t0
- loadp JSCell::m_structure[t0], t1
- bbb Structure::m_typeInfo + TypeInfo::m_type[t1], ObjectType, .opCreateThisSlow
- loadp JSObject::m_inheritorID[t0], t2
+ loadp Callee[cfr], t0
+ loadp JSFunction::m_cachedInheritorID[t0], t2
btpz t2, .opCreateThisSlow
allocateBasicJSObject(JSFinalObjectSizeClassIndex, JSGlobalData::jsFinalObjectClassInfo, t2, t0, t1, t3, .opCreateThisSlow)
loadi 4[PC], t1
storei CellTag, TagOffset[cfr, t1, 8]
storei t0, PayloadOffset[cfr, t1, 8]
- dispatch(3)
+ dispatch(2)
.opCreateThisSlow:
callSlowPath(_llint_slow_path_create_this)
- dispatch(3)
+ dispatch(2)
_llint_op_get_callee:
diff --git a/Source/JavaScriptCore/llint/LowLevelInterpreter64.asm b/Source/JavaScriptCore/llint/LowLevelInterpreter64.asm
index 2b79b85..1b61f97 100644
--- a/Source/JavaScriptCore/llint/LowLevelInterpreter64.asm
+++ b/Source/JavaScriptCore/llint/LowLevelInterpreter64.asm
@@ -221,22 +221,17 @@
_llint_op_create_this:
traceExecution()
- loadis 16[PB, PC, 8], t0
- assertNotConstant(t0)
- loadp [cfr, t0, 8], t0
- btpnz t0, tagMask, .opCreateThisSlow
- loadp JSCell::m_structure[t0], t1
- bbb Structure::m_typeInfo + TypeInfo::m_type[t1], ObjectType, .opCreateThisSlow
- loadp JSObject::m_inheritorID[t0], t2
+ loadp Callee[cfr], t0
+ loadp JSFunction::m_cachedInheritorID[t0], t2
btpz t2, .opCreateThisSlow
allocateBasicJSObject(JSFinalObjectSizeClassIndex, JSGlobalData::jsFinalObjectClassInfo, t2, t0, t1, t3, .opCreateThisSlow)
loadis 8[PB, PC, 8], t1
storep t0, [cfr, t1, 8]
- dispatch(3)
+ dispatch(2)
.opCreateThisSlow:
callSlowPath(_llint_slow_path_create_this)
- dispatch(3)
+ dispatch(2)
_llint_op_get_callee:
diff --git a/Source/JavaScriptCore/runtime/JSFunction.cpp b/Source/JavaScriptCore/runtime/JSFunction.cpp
index 243946b..563325a 100644
--- a/Source/JavaScriptCore/runtime/JSFunction.cpp
+++ b/Source/JavaScriptCore/runtime/JSFunction.cpp
@@ -112,6 +112,16 @@
putDirectOffset(exec->globalData(), scopeChainNode->globalObject->functionNameOffset(), executable->nameValue());
}
+Structure* JSFunction::cacheInheritorID(ExecState* exec)
+{
+ JSValue prototype = get(exec, exec->globalData().propertyNames->prototype);
+ if (prototype.isObject())
+ m_cachedInheritorID.set(exec->globalData(), this, asObject(prototype)->inheritorID(exec->globalData()));
+ else
+ m_cachedInheritorID.set(exec->globalData(), this, globalObject()->emptyObjectStructure());
+ return m_cachedInheritorID.get();
+}
+
const UString& JSFunction::name(ExecState* exec)
{
return asString(getDirect(exec->globalData(), exec->globalData().propertyNames->name))->tryGetValue();
@@ -332,6 +342,7 @@
// following the rules set out in ECMA-262 8.12.9.
PropertySlot slot;
thisObject->methodTable()->getOwnPropertySlot(thisObject, exec, propertyName, slot);
+ thisObject->m_cachedInheritorID.clear();
}
if (thisObject->jsExecutable()->isStrictMode() && (propertyName == exec->propertyNames().arguments || propertyName == exec->propertyNames().caller)) {
// This will trigger the property to be reified, if this is not already the case!
@@ -372,6 +383,7 @@
// following the rules set out in ECMA-262 8.12.9.
PropertySlot slot;
thisObject->methodTable()->getOwnPropertySlot(thisObject, exec, propertyName, slot);
+ thisObject->m_cachedInheritorID.clear();
return Base::defineOwnProperty(object, exec, propertyName, descriptor, throwException);
}
diff --git a/Source/JavaScriptCore/runtime/JSFunction.h b/Source/JavaScriptCore/runtime/JSFunction.h
index 5553115..9de66d7 100644
--- a/Source/JavaScriptCore/runtime/JSFunction.h
+++ b/Source/JavaScriptCore/runtime/JSFunction.h
@@ -121,6 +121,18 @@
return OBJECT_OFFSETOF(JSFunction, m_executable);
}
+ Structure* cachedInheritorID(ExecState* exec)
+ {
+ if (UNLIKELY(!m_cachedInheritorID))
+ return cacheInheritorID(exec);
+ return m_cachedInheritorID.get();
+ }
+
+ static size_t offsetOfCachedInheritorID()
+ {
+ return OBJECT_OFFSETOF(JSFunction, m_cachedInheritorID);
+ }
+
protected:
const static unsigned StructureFlags = OverridesGetOwnPropertySlot | ImplementsHasInstance | OverridesVisitChildren | OverridesGetPropertyNames | JSObject::StructureFlags;
@@ -130,6 +142,8 @@
void finishCreation(ExecState*, NativeExecutable*, int length, const Identifier& name);
void finishCreation(ExecState*, FunctionExecutable*, ScopeChainNode*);
+ Structure* cacheInheritorID(ExecState*);
+
static bool getOwnPropertySlot(JSCell*, ExecState*, const Identifier&, PropertySlot&);
static bool getOwnPropertyDescriptor(JSObject*, ExecState*, const Identifier&, PropertyDescriptor&);
static void getOwnPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode = ExcludeDontEnumProperties);
@@ -152,6 +166,7 @@
WriteBarrier<ExecutableBase> m_executable;
WriteBarrier<ScopeChainNode> m_scopeChain;
+ WriteBarrier<Structure> m_cachedInheritorID;
};
inline bool JSValue::isFunction() const