Diff
Modified: trunk/Source/_javascript_Core/ChangeLog (199278 => 199279)
--- trunk/Source/_javascript_Core/ChangeLog 2016-04-10 01:46:27 UTC (rev 199278)
+++ trunk/Source/_javascript_Core/ChangeLog 2016-04-10 03:38:44 UTC (rev 199279)
@@ -1,3 +1,71 @@
+2016-04-09 Keith Miller <[email protected]>
+
+ tryGetById should be supported by the DFG/FTL
+ https://bugs.webkit.org/show_bug.cgi?id=156378
+
+ Reviewed by Filip Pizlo.
+
+ This patch adds support for tryGetById in the DFG/FTL. It adds a new DFG node
+ TryGetById, which acts similarly to the normal GetById DFG node. One key
+ difference between GetById and TryGetById is that in the LLInt and Baseline
+ we do not profile the result type. This profiling is unnessary for the current
+ use case of tryGetById, which is expected to be a strict equality comparision
+ against a specific object or undefined. In either case other DFG optimizations
+ will make this equally fast with or without the profiling information.
+
+ Additionally, this patch adds new reuse modes for JSValueRegsTemporary that take
+ an operand and attempt to reuse the registers for that operand if they are free
+ after the current DFG node.
+
+ * bytecode/GetByIdStatus.cpp:
+ (JSC::GetByIdStatus::computeFromLLInt):
+ (JSC::GetByIdStatus::computeForStubInfoWithoutExitSiteFeedback):
+ * dfg/DFGAbstractInterpreterInlines.h:
+ (JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
+ * dfg/DFGByteCodeParser.cpp:
+ (JSC::DFG::ByteCodeParser::handleGetById):
+ (JSC::DFG::ByteCodeParser::parseBlock):
+ * dfg/DFGCapabilities.cpp:
+ (JSC::DFG::capabilityLevel):
+ * dfg/DFGClobberize.h:
+ (JSC::DFG::clobberize):
+ * dfg/DFGDoesGC.cpp:
+ (JSC::DFG::doesGC):
+ * dfg/DFGFixupPhase.cpp:
+ (JSC::DFG::FixupPhase::fixupNode):
+ * dfg/DFGNode.h:
+ (JSC::DFG::Node::hasIdentifier):
+ * dfg/DFGNodeType.h:
+ * dfg/DFGPredictionPropagationPhase.cpp:
+ (JSC::DFG::PredictionPropagationPhase::propagate):
+ * dfg/DFGSafeToExecute.h:
+ (JSC::DFG::safeToExecute):
+ * dfg/DFGSpeculativeJIT.cpp:
+ (JSC::DFG::SpeculativeJIT::compileTryGetById):
+ (JSC::DFG::JSValueRegsTemporary::JSValueRegsTemporary):
+ * dfg/DFGSpeculativeJIT.h:
+ (JSC::DFG::GPRTemporary::operator=):
+ * dfg/DFGSpeculativeJIT32_64.cpp:
+ (JSC::DFG::SpeculativeJIT::cachedGetById):
+ (JSC::DFG::SpeculativeJIT::compile):
+ * dfg/DFGSpeculativeJIT64.cpp:
+ (JSC::DFG::SpeculativeJIT::cachedGetById):
+ (JSC::DFG::SpeculativeJIT::compile):
+ * ftl/FTLCapabilities.cpp:
+ (JSC::FTL::canCompile):
+ * ftl/FTLLowerDFGToB3.cpp:
+ (JSC::FTL::DFG::LowerDFGToB3::compileNode):
+ (JSC::FTL::DFG::LowerDFGToB3::compileGetById):
+ (JSC::FTL::DFG::LowerDFGToB3::getById):
+ * jit/JITOperations.cpp:
+ * jit/JITOperations.h:
+ * tests/stress/try-get-by-id.js:
+ (tryGetByIdTextStrict):
+ (get let):
+ (let.get createBuiltin):
+ (get throw):
+ (getCaller.obj.1.throw.new.Error): Deleted.
+
2016-04-09 Saam barati <[email protected]>
Allocation sinking SSA Defs are allowed to have replacements
Modified: trunk/Source/_javascript_Core/bytecode/GetByIdStatus.cpp (199278 => 199279)
--- trunk/Source/_javascript_Core/bytecode/GetByIdStatus.cpp 2016-04-10 01:46:27 UTC (rev 199278)
+++ trunk/Source/_javascript_Core/bytecode/GetByIdStatus.cpp 2016-04-10 03:38:44 UTC (rev 199279)
@@ -75,7 +75,7 @@
Instruction* instruction = profiledBlock->instructions().begin() + bytecodeIndex;
- if (instruction[0].u.opcode == LLInt::getOpcode(op_get_array_length))
+ if (instruction[0].u.opcode == LLInt::getOpcode(op_get_array_length) || instruction[0].u.opcode == LLInt::getOpcode(op_try_get_by_id))
return GetByIdStatus(NoInformation, false);
StructureID structureID = instruction[4].u.structureID;
@@ -210,7 +210,8 @@
JSFunction* intrinsicFunction = nullptr;
switch (access.type()) {
- case AccessCase::Load: {
+ case AccessCase::Load:
+ case AccessCase::GetGetter: {
break;
}
case AccessCase::IntrinsicGetter: {
Modified: trunk/Source/_javascript_Core/dfg/DFGAbstractInterpreterInlines.h (199278 => 199279)
--- trunk/Source/_javascript_Core/dfg/DFGAbstractInterpreterInlines.h 2016-04-10 01:46:27 UTC (rev 199278)
+++ trunk/Source/_javascript_Core/dfg/DFGAbstractInterpreterInlines.h 2016-04-10 03:38:44 UTC (rev 199279)
@@ -1972,7 +1972,13 @@
case PutToArguments:
break;
-
+
+ case TryGetById:
+ // FIXME: This should constant fold at least as well as the normal GetById case.
+ // https://bugs.webkit.org/show_bug.cgi?id=156422
+ forNode(node).makeHeapTop();
+ break;
+
case GetById:
case GetByIdFlush: {
if (!node->prediction()) {
Modified: trunk/Source/_javascript_Core/dfg/DFGByteCodeParser.cpp (199278 => 199279)
--- trunk/Source/_javascript_Core/dfg/DFGByteCodeParser.cpp 2016-04-10 01:46:27 UTC (rev 199278)
+++ trunk/Source/_javascript_Core/dfg/DFGByteCodeParser.cpp 2016-04-10 03:38:44 UTC (rev 199279)
@@ -231,9 +231,10 @@
Node* load(SpeculatedType, Node* base, unsigned identifierNumber, const VariantType&);
Node* store(Node* base, unsigned identifier, const PutByIdVariant&, Node* value);
-
+
+ void handleTryGetById(int destinationOperand, Node* base, unsigned identifierNumber, const GetByIdStatus&);
void handleGetById(
- int destinationOperand, SpeculatedType, Node* base, unsigned identifierNumber, GetByIdStatus);
+ int destinationOperand, SpeculatedType, Node* base, unsigned identifierNumber, GetByIdStatus, AccessType);
void emitPutById(
Node* base, unsigned identifierNumber, Node* value, const PutByIdStatus&, bool isDirect);
void handlePutById(
@@ -2887,7 +2888,7 @@
void ByteCodeParser::handleGetById(
int destinationOperand, SpeculatedType prediction, Node* base, unsigned identifierNumber,
- GetByIdStatus getByIdStatus)
+ GetByIdStatus getByIdStatus, AccessType type)
{
// Attempt to reduce the set of things in the GetByIdStatus.
if (base->op() == NewObject) {
@@ -2905,8 +2906,13 @@
getByIdStatus.filter(base->structure());
}
- NodeType getById = getByIdStatus.makesCalls() ? GetByIdFlush : GetById;
-
+ NodeType getById;
+ if (type == AccessType::Get)
+ getById = getByIdStatus.makesCalls() ? GetByIdFlush : GetById;
+ else
+ getById = TryGetById;
+
+ ASSERT(type == AccessType::Get || !getByIdStatus.makesCalls());
if (!getByIdStatus.isSimple() || !getByIdStatus.numVariants() || !Options::useAccessInlining()) {
set(VirtualRegister(destinationOperand),
addToGraph(getById, OpInfo(identifierNumber), OpInfo(prediction), base));
@@ -2976,6 +2982,7 @@
if (m_graph.compilation())
m_graph.compilation()->noticeInlinedGetById();
+ ASSERT(type == AccessType::Get || !variant.callLinkStatus());
if (!variant.callLinkStatus() && variant.intrinsic() == NoIntrinsic) {
set(VirtualRegister(destinationOperand), loadedValue);
return;
@@ -2991,8 +2998,7 @@
return;
}
- if (variant.intrinsic() != NoIntrinsic)
- ASSERT(variant.intrinsic() == NoIntrinsic);
+ ASSERT(variant.intrinsic() == NoIntrinsic);
// Make a call. We don't try to get fancy with using the smallest operand number because
// the stack layout phase should compress the stack anyway.
@@ -3793,7 +3799,7 @@
locker, m_inlineStackTop->m_profiledBlock,
byValInfo->stubInfo, currentCodeOrigin(), uid);
- handleGetById(currentInstruction[1].u.operand, prediction, base, identifierNumber, getByIdStatus);
+ handleGetById(currentInstruction[1].u.operand, prediction, base, identifierNumber, getByIdStatus, AccessType::Get);
}
}
@@ -3847,7 +3853,22 @@
NEXT_OPCODE(op_put_by_val);
}
-
+
+ case op_try_get_by_id: {
+ Node* base = get(VirtualRegister(currentInstruction[2].u.operand));
+ unsigned identifierNumber = m_inlineStackTop->m_identifierRemap[currentInstruction[3].u.operand];
+ UniquedStringImpl* uid = m_graph.identifiers()[identifierNumber];
+
+ GetByIdStatus getByIdStatus = GetByIdStatus::computeFor(
+ m_inlineStackTop->m_profiledBlock, m_dfgCodeBlock,
+ m_inlineStackTop->m_stubInfos, m_dfgStubInfos,
+ currentCodeOrigin(), uid);
+
+ handleGetById(currentInstruction[1].u.operand, SpecHeapTop, base, identifierNumber, getByIdStatus, AccessType::GetPure);
+
+ NEXT_OPCODE(op_try_get_by_id);
+ }
+
case op_get_by_id:
case op_get_array_length: {
SpeculatedType prediction = getPrediction();
@@ -3862,7 +3883,7 @@
currentCodeOrigin(), uid);
handleGetById(
- currentInstruction[1].u.operand, prediction, base, identifierNumber, getByIdStatus);
+ currentInstruction[1].u.operand, prediction, base, identifierNumber, getByIdStatus, AccessType::Get);
NEXT_OPCODE(op_get_by_id);
}
Modified: trunk/Source/_javascript_Core/dfg/DFGCapabilities.cpp (199278 => 199279)
--- trunk/Source/_javascript_Core/dfg/DFGCapabilities.cpp 2016-04-10 01:46:27 UTC (rev 199278)
+++ trunk/Source/_javascript_Core/dfg/DFGCapabilities.cpp 2016-04-10 03:38:44 UTC (rev 199279)
@@ -152,6 +152,7 @@
case op_get_by_val:
case op_put_by_val:
case op_put_by_val_direct:
+ case op_try_get_by_id:
case op_get_by_id:
case op_get_array_length:
case op_put_by_id:
Modified: trunk/Source/_javascript_Core/dfg/DFGClobberize.h (199278 => 199279)
--- trunk/Source/_javascript_Core/dfg/DFGClobberize.h 2016-04-10 01:46:27 UTC (rev 199278)
+++ trunk/Source/_javascript_Core/dfg/DFGClobberize.h 2016-04-10 03:38:44 UTC (rev 199279)
@@ -848,7 +848,12 @@
def(HeapLocation(NamedPropertyLoc, heap, node->child2()), LazyNode(node));
return;
}
-
+
+ case TryGetById: {
+ read(Heap);
+ return;
+ }
+
case MultiGetByOffset: {
read(JSCell_structureID);
read(JSObject_butterfly);
Modified: trunk/Source/_javascript_Core/dfg/DFGDoesGC.cpp (199278 => 199279)
--- trunk/Source/_javascript_Core/dfg/DFGDoesGC.cpp 2016-04-10 01:46:27 UTC (rev 199278)
+++ trunk/Source/_javascript_Core/dfg/DFGDoesGC.cpp 2016-04-10 03:38:44 UTC (rev 199279)
@@ -96,6 +96,7 @@
case ArithCos:
case ArithLog:
case ValueAdd:
+ case TryGetById:
case GetById:
case GetByIdFlush:
case PutById:
Modified: trunk/Source/_javascript_Core/dfg/DFGFixupPhase.cpp (199278 => 199279)
--- trunk/Source/_javascript_Core/dfg/DFGFixupPhase.cpp 2016-04-10 01:46:27 UTC (rev 199278)
+++ trunk/Source/_javascript_Core/dfg/DFGFixupPhase.cpp 2016-04-10 03:38:44 UTC (rev 199279)
@@ -1069,6 +1069,12 @@
break;
}
+ case TryGetById: {
+ if (node->child1()->shouldSpeculateCell())
+ fixEdge<CellUse>(node->child1());
+ break;
+ }
+
case GetById:
case GetByIdFlush: {
// FIXME: This should be done in the ByteCodeParser based on reading the
Modified: trunk/Source/_javascript_Core/dfg/DFGNode.h (199278 => 199279)
--- trunk/Source/_javascript_Core/dfg/DFGNode.h 2016-04-10 01:46:27 UTC (rev 199278)
+++ trunk/Source/_javascript_Core/dfg/DFGNode.h 2016-04-10 03:38:44 UTC (rev 199279)
@@ -866,6 +866,7 @@
bool hasIdentifier()
{
switch (op()) {
+ case TryGetById:
case GetById:
case GetByIdFlush:
case PutById:
Modified: trunk/Source/_javascript_Core/dfg/DFGNodeType.h (199278 => 199279)
--- trunk/Source/_javascript_Core/dfg/DFGNodeType.h 2016-04-10 01:46:27 UTC (rev 199278)
+++ trunk/Source/_javascript_Core/dfg/DFGNodeType.h 2016-04-10 03:38:44 UTC (rev 199279)
@@ -182,6 +182,7 @@
macro(PutByValDirect, NodeMustGenerate | NodeHasVarArgs) \
macro(PutByVal, NodeMustGenerate | NodeHasVarArgs) \
macro(PutByValAlias, NodeMustGenerate | NodeHasVarArgs) \
+ macro(TryGetById, NodeResultJS) \
macro(GetById, NodeResultJS | NodeMustGenerate) \
macro(GetByIdFlush, NodeResultJS | NodeMustGenerate) \
macro(PutById, NodeMustGenerate) \
Modified: trunk/Source/_javascript_Core/dfg/DFGPredictionPropagationPhase.cpp (199278 => 199279)
--- trunk/Source/_javascript_Core/dfg/DFGPredictionPropagationPhase.cpp 2016-04-10 01:46:27 UTC (rev 199278)
+++ trunk/Source/_javascript_Core/dfg/DFGPredictionPropagationPhase.cpp 2016-04-10 03:38:44 UTC (rev 199279)
@@ -172,7 +172,12 @@
changed |= setPrediction(SpecInt32);
break;
}
-
+
+ case TryGetById: {
+ changed |= setPrediction(SpecBytecodeTop);
+ break;
+ }
+
case ArrayPop:
case ArrayPush:
case RegExpExec:
Modified: trunk/Source/_javascript_Core/dfg/DFGSafeToExecute.h (199278 => 199279)
--- trunk/Source/_javascript_Core/dfg/DFGSafeToExecute.h 2016-04-10 01:46:27 UTC (rev 199278)
+++ trunk/Source/_javascript_Core/dfg/DFGSafeToExecute.h 2016-04-10 03:38:44 UTC (rev 199279)
@@ -191,6 +191,7 @@
case ArithCos:
case ArithLog:
case ValueAdd:
+ case TryGetById:
case GetById:
case GetByIdFlush:
case PutById:
Modified: trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.cpp (199278 => 199279)
--- trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.cpp 2016-04-10 01:46:27 UTC (rev 199278)
+++ trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.cpp 2016-04-10 03:38:44 UTC (rev 199279)
@@ -964,6 +964,47 @@
}
}
+void SpeculativeJIT::compileTryGetById(Node* node)
+{
+ switch (node->child1().useKind()) {
+ case CellUse: {
+ SpeculateCellOperand base(this, node->child1());
+ JSValueRegsTemporary result(this, Reuse, base);
+
+ JSValueRegs baseRegs = JSValueRegs::payloadOnly(base.gpr());
+ JSValueRegs resultRegs = result.regs();
+
+ base.use();
+
+ cachedGetById(node->origin.semantic, baseRegs, resultRegs, node->identifierNumber(), JITCompiler::Jump(), DontSpill, AccessType::GetPure);
+
+ jsValueResult(resultRegs, node, DataFormatJS, UseChildrenCalledExplicitly);
+ break;
+ }
+
+ case UntypedUse: {
+ JSValueOperand base(this, node->child1());
+ JSValueRegsTemporary result(this, Reuse, base);
+
+ JSValueRegs baseRegs = base.jsValueRegs();
+ JSValueRegs resultRegs = result.regs();
+
+ base.use();
+
+ JITCompiler::Jump notCell = m_jit.branchIfNotCell(baseRegs);
+
+ cachedGetById(node->origin.semantic, baseRegs, resultRegs, node->identifierNumber(), notCell, DontSpill, AccessType::GetPure);
+
+ jsValueResult(resultRegs, node, DataFormatJS, UseChildrenCalledExplicitly);
+ break;
+ }
+
+ default:
+ DFG_CRASH(m_jit.graph(), node, "Bad use kind");
+ break;
+ }
+}
+
void SpeculativeJIT::compileIn(Node* node)
{
SpeculateCellOperand base(this, node->child2());
@@ -1168,6 +1209,44 @@
{
}
+#if USE(JSVALUE64)
+template<typename T>
+JSValueRegsTemporary::JSValueRegsTemporary(SpeculativeJIT* jit, ReuseTag, T& operand, WhichValueWord)
+ : m_gpr(jit, Reuse, operand)
+{
+}
+#else
+template<typename T>
+JSValueRegsTemporary::JSValueRegsTemporary(SpeculativeJIT* jit, ReuseTag, T& operand, WhichValueWord resultWord)
+{
+ if (resultWord == PayloadWord) {
+ m_payloadGPR = GPRTemporary(jit, Reuse, operand);
+ m_tagGPR = GPRTemporary(jit);
+ } else {
+ m_payloadGPR = GPRTemporary(jit);
+ m_tagGPR = GPRTemporary(jit, Reuse, operand);
+ }
+}
+#endif
+
+#if USE(JSVALUE64)
+JSValueRegsTemporary::JSValueRegsTemporary(SpeculativeJIT* jit, ReuseTag, JSValueOperand& operand)
+{
+ m_gpr = GPRTemporary(jit, Reuse, operand);
+}
+#else
+JSValueRegsTemporary::JSValueRegsTemporary(SpeculativeJIT* jit, ReuseTag, JSValueOperand& operand)
+{
+ if (jit->canReuse(operand.node())) {
+ m_payloadGPR = GPRTemporary(jit, Reuse, operand, PayloadWord);
+ m_tagGPR = GPRTemporary(jit, Reuse, operand, TagWord);
+ } else {
+ m_payloadGPR = GPRTemporary(jit);
+ m_tagGPR = GPRTemporary(jit);
+ }
+}
+#endif
+
JSValueRegsTemporary::~JSValueRegsTemporary() { }
JSValueRegs JSValueRegsTemporary::regs()
Modified: trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.h (199278 => 199279)
--- trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.h 2016-04-10 01:46:27 UTC (rev 199278)
+++ trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.h 2016-04-10 03:38:44 UTC (rev 199279)
@@ -703,14 +703,17 @@
void compileMovHint(Node*);
void compileMovHintAndCheck(Node*);
+ void cachedGetById(CodeOrigin, JSValueRegs base, JSValueRegs result, unsigned identifierNumber, JITCompiler::Jump slowPathTarget = JITCompiler::Jump(), SpillRegistersMode = NeedToSpill, AccessType = AccessType::Get);
+
#if USE(JSVALUE64)
- void cachedGetById(CodeOrigin, GPRReg baseGPR, GPRReg resultGPR, unsigned identifierNumber, JITCompiler::Jump slowPathTarget = JITCompiler::Jump(), SpillRegistersMode = NeedToSpill);
+ void cachedGetById(CodeOrigin, GPRReg baseGPR, GPRReg resultGPR, unsigned identifierNumber, JITCompiler::Jump slowPathTarget = JITCompiler::Jump(), SpillRegistersMode = NeedToSpill, AccessType = AccessType::Get);
void cachedPutById(CodeOrigin, GPRReg base, GPRReg value, GPRReg scratchGPR, unsigned identifierNumber, PutKind, JITCompiler::Jump slowPathTarget = JITCompiler::Jump(), SpillRegistersMode = NeedToSpill);
#elif USE(JSVALUE32_64)
- void cachedGetById(CodeOrigin, GPRReg baseTagGPROrNone, GPRReg basePayloadGPR, GPRReg resultTagGPR, GPRReg resultPayloadGPR, unsigned identifierNumber, JITCompiler::Jump slowPathTarget = JITCompiler::Jump(), SpillRegistersMode = NeedToSpill);
+ void cachedGetById(CodeOrigin, GPRReg baseTagGPROrNone, GPRReg basePayloadGPR, GPRReg resultTagGPR, GPRReg resultPayloadGPR, unsigned identifierNumber, JITCompiler::Jump slowPathTarget = JITCompiler::Jump(), SpillRegistersMode = NeedToSpill, AccessType = AccessType::Get);
void cachedPutById(CodeOrigin, GPRReg basePayloadGPR, GPRReg valueTagGPR, GPRReg valuePayloadGPR, GPRReg scratchGPR, unsigned identifierNumber, PutKind, JITCompiler::Jump slowPathTarget = JITCompiler::Jump(), SpillRegistersMode = NeedToSpill);
#endif
-
+
+ void compileTryGetById(Node*);
void compileIn(Node*);
void compileBaseValueStoreBarrier(Edge& baseEdge, Edge& valueEdge);
@@ -2940,6 +2943,17 @@
GPRTemporary(SpeculativeJIT*, ReuseTag, JSValueOperand&, WhichValueWord);
#endif
+ GPRTemporary(GPRTemporary& other) = delete;
+
+ GPRTemporary& operator=(GPRTemporary&& other)
+ {
+ ASSERT(!m_jit);
+ ASSERT(m_gpr == InvalidGPRReg);
+ std::swap(m_jit, other.m_jit);
+ std::swap(m_gpr, other.m_gpr);
+ return *this;
+ }
+
void adopt(GPRTemporary&);
~GPRTemporary()
@@ -2962,6 +2976,9 @@
public:
JSValueRegsTemporary();
JSValueRegsTemporary(SpeculativeJIT*);
+ template<typename T>
+ JSValueRegsTemporary(SpeculativeJIT*, ReuseTag, T& operand, WhichValueWord resultRegWord = PayloadWord);
+ JSValueRegsTemporary(SpeculativeJIT*, ReuseTag, JSValueOperand&);
~JSValueRegsTemporary();
JSValueRegs regs();
Modified: trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT32_64.cpp (199278 => 199279)
--- trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT32_64.cpp 2016-04-10 01:46:27 UTC (rev 199278)
+++ trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT32_64.cpp 2016-04-10 03:38:44 UTC (rev 199279)
@@ -165,9 +165,14 @@
}
}
+void SpeculativeJIT::cachedGetById(CodeOrigin origin, JSValueRegs base, JSValueRegs result, unsigned identifierNumber, JITCompiler::Jump slowPathTarget , SpillRegistersMode mode, AccessType type)
+{
+ cachedGetById(origin, base.tagGPR(), base.payloadGPR(), result.tagGPR(), result.payloadGPR(), identifierNumber, slowPathTarget, mode, type);
+}
+
void SpeculativeJIT::cachedGetById(
CodeOrigin codeOrigin, GPRReg baseTagGPROrNone, GPRReg basePayloadGPR, GPRReg resultTagGPR, GPRReg resultPayloadGPR,
- unsigned identifierNumber, JITCompiler::Jump slowPathTarget, SpillRegistersMode spillMode)
+ unsigned identifierNumber, JITCompiler::Jump slowPathTarget, SpillRegistersMode spillMode, AccessType type)
{
// This is a hacky fix for when the register allocator decides to alias the base payload with the result tag. This only happens
// in the case of GetByIdFlush, which has a relatively expensive register allocation story already so we probably don't need to
@@ -194,7 +199,7 @@
JITGetByIdGenerator gen(
m_jit.codeBlock(), codeOrigin, callSite, usedRegisters,
JSValueRegs(baseTagGPROrNone, basePayloadGPR),
- JSValueRegs(resultTagGPR, resultPayloadGPR), AccessType::Get);
+ JSValueRegs(resultTagGPR, resultPayloadGPR), type);
gen.generateFastPath(m_jit);
@@ -203,16 +208,22 @@
slowCases.append(slowPathTarget);
slowCases.append(gen.slowPathJump());
+ J_JITOperation_ESsiJI getByIdFunction;
+ if (type == AccessType::Get)
+ getByIdFunction = operationGetByIdOptimize;
+ else
+ getByIdFunction = operationTryGetByIdOptimize;
+
std::unique_ptr<SlowPathGenerator> slowPath;
if (baseTagGPROrNone == InvalidGPRReg) {
slowPath = slowPathCall(
- slowCases, this, operationGetByIdOptimize,
+ slowCases, this, getByIdFunction,
JSValueRegs(resultTagGPR, resultPayloadGPR), gen.stubInfo(),
static_cast<int32_t>(JSValue::CellTag), basePayloadGPR,
identifierUID(identifierNumber));
} else {
slowPath = slowPathCall(
- slowCases, this, operationGetByIdOptimize,
+ slowCases, this, getByIdFunction,
JSValueRegs(resultTagGPR, resultPayloadGPR), gen.stubInfo(), baseTagGPROrNone,
basePayloadGPR, identifierUID(identifierNumber));
}
@@ -3961,7 +3972,12 @@
noResult(node);
break;
}
-
+
+ case TryGetById: {
+ compileTryGetById(node);
+ break;
+ }
+
case GetById: {
ASSERT(node->prediction());
Modified: trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT64.cpp (199278 => 199279)
--- trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT64.cpp 2016-04-10 01:46:27 UTC (rev 199278)
+++ trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT64.cpp 2016-04-10 03:38:44 UTC (rev 199279)
@@ -153,8 +153,13 @@
}
}
-void SpeculativeJIT::cachedGetById(CodeOrigin codeOrigin, GPRReg baseGPR, GPRReg resultGPR, unsigned identifierNumber, JITCompiler::Jump slowPathTarget, SpillRegistersMode spillMode)
+void SpeculativeJIT::cachedGetById(CodeOrigin origin, JSValueRegs base, JSValueRegs result, unsigned identifierNumber, JITCompiler::Jump slowPathTarget , SpillRegistersMode mode, AccessType type)
{
+ cachedGetById(origin, base.gpr(), result.gpr(), identifierNumber, slowPathTarget, mode, type);
+}
+
+void SpeculativeJIT::cachedGetById(CodeOrigin codeOrigin, GPRReg baseGPR, GPRReg resultGPR, unsigned identifierNumber, JITCompiler::Jump slowPathTarget, SpillRegistersMode spillMode, AccessType type)
+{
CallSiteIndex callSite = m_jit.recordCallSiteAndGenerateExceptionHandlingOSRExitIfNeeded(codeOrigin, m_stream->size());
RegisterSet usedRegisters = this->usedRegisters();
if (spillMode == DontSpill) {
@@ -164,7 +169,7 @@
}
JITGetByIdGenerator gen(
m_jit.codeBlock(), codeOrigin, callSite, usedRegisters, JSValueRegs(baseGPR),
- JSValueRegs(resultGPR), AccessType::Get);
+ JSValueRegs(resultGPR), type);
gen.generateFastPath(m_jit);
JITCompiler::JumpList slowCases;
@@ -173,7 +178,7 @@
slowCases.append(gen.slowPathJump());
auto slowPath = slowPathCall(
- slowCases, this, operationGetByIdOptimize, resultGPR, gen.stubInfo(), baseGPR,
+ slowCases, this, type == AccessType::Get ? operationGetByIdOptimize : operationTryGetByIdOptimize, resultGPR, gen.stubInfo(), baseGPR,
identifierUID(identifierNumber), spillMode);
m_jit.addGetById(gen, slowPath.get());
@@ -4019,6 +4024,12 @@
noResult(node);
break;
}
+
+ case TryGetById: {
+ compileTryGetById(node);
+ break;
+ }
+
case GetById: {
ASSERT(node->prediction());
Modified: trunk/Source/_javascript_Core/ftl/FTLCapabilities.cpp (199278 => 199279)
--- trunk/Source/_javascript_Core/ftl/FTLCapabilities.cpp 2016-04-10 01:46:27 UTC (rev 199278)
+++ trunk/Source/_javascript_Core/ftl/FTLCapabilities.cpp 2016-04-10 03:38:44 UTC (rev 199279)
@@ -166,6 +166,7 @@
case CallStringConstructor:
case MakeRope:
case NewArrayWithSize:
+ case TryGetById:
case GetById:
case GetByIdFlush:
case ToThis:
Modified: trunk/Source/_javascript_Core/ftl/FTLLowerDFGToB3.cpp (199278 => 199279)
--- trunk/Source/_javascript_Core/ftl/FTLLowerDFGToB3.cpp 2016-04-10 01:46:27 UTC (rev 199278)
+++ trunk/Source/_javascript_Core/ftl/FTLLowerDFGToB3.cpp 2016-04-10 03:38:44 UTC (rev 199279)
@@ -593,9 +593,12 @@
case PutStructure:
compilePutStructure();
break;
+ case TryGetById:
+ compileGetById(AccessType::GetPure);
+ break;
case GetById:
case GetByIdFlush:
- compileGetById();
+ compileGetById(AccessType::Get);
break;
case In:
compileIn();
@@ -2302,11 +2305,12 @@
cell, m_heaps.JSCell_structureID);
}
- void compileGetById()
+ void compileGetById(AccessType type)
{
+ ASSERT(type == AccessType::Get || type == AccessType::GetPure);
switch (m_node->child1().useKind()) {
case CellUse: {
- setJSValue(getById(lowCell(m_node->child1())));
+ setJSValue(getById(lowCell(m_node->child1()), type));
return;
}
@@ -2324,12 +2328,18 @@
isCell(value, provenType(m_node->child1())), unsure(cellCase), unsure(notCellCase));
LBasicBlock lastNext = m_out.appendTo(cellCase, notCellCase);
- ValueFromBlock cellResult = m_out.anchor(getById(value));
+ ValueFromBlock cellResult = m_out.anchor(getById(value, type));
m_out.jump(continuation);
-
+
+ J_JITOperation_EJI getByIdFunction;
+ if (type == AccessType::Get)
+ getByIdFunction = operationGetByIdGeneric;
+ else
+ getByIdFunction = operationTryGetByIdGeneric;
+
m_out.appendTo(notCellCase, continuation);
ValueFromBlock notCellResult = m_out.anchor(vmCall(
- m_out.int64, m_out.operation(operationGetByIdGeneric),
+ m_out.int64, m_out.operation(getByIdFunction),
m_callFrame, value,
m_out.constIntPtr(m_graph.identifiers()[m_node->identifierNumber()])));
m_out.jump(continuation);
@@ -7293,7 +7303,7 @@
return m_out.phi(m_out.intPtr, fastButterfly, slowButterfly);
}
- LValue getById(LValue base)
+ LValue getById(LValue base, AccessType type)
{
Node* node = m_node;
UniquedStringImpl* uid = m_graph.identifiers()[node->identifierNumber()];
@@ -7331,7 +7341,7 @@
auto generator = Box<JITGetByIdGenerator>::create(
jit.codeBlock(), node->origin.semantic, callSiteIndex,
params.unavailableRegisters(), JSValueRegs(params[1].gpr()),
- JSValueRegs(params[0].gpr()), AccessType::Get);
+ JSValueRegs(params[0].gpr()), type);
generator->generateFastPath(jit);
CCallHelpers::Label done = jit.label();
@@ -7340,11 +7350,17 @@
[=] (CCallHelpers& jit) {
AllowMacroScratchRegisterUsage allowScratch(jit);
+ J_JITOperation_ESsiJI optimizationFunction;
+ if (type == AccessType::Get)
+ optimizationFunction = operationGetByIdOptimize;
+ else
+ optimizationFunction = operationTryGetByIdOptimize;
+
generator->slowPathJump().link(&jit);
CCallHelpers::Label slowPathBegin = jit.label();
CCallHelpers::Call slowPathCall = callOperation(
*state, params.unavailableRegisters(), jit, node->origin.semantic,
- exceptions.get(), operationGetByIdOptimize, params[0].gpr(),
+ exceptions.get(), optimizationFunction, params[0].gpr(),
CCallHelpers::TrustedImmPtr(generator->stubInfo()), params[1].gpr(),
CCallHelpers::TrustedImmPtr(uid)).call();
jit.jump().linkTo(done, &jit);
Modified: trunk/Source/_javascript_Core/jit/JITOperations.cpp (199278 => 199279)
--- trunk/Source/_javascript_Core/jit/JITOperations.cpp 2016-04-10 01:46:27 UTC (rev 199278)
+++ trunk/Source/_javascript_Core/jit/JITOperations.cpp 2016-04-10 03:38:44 UTC (rev 199279)
@@ -170,6 +170,20 @@
return JSValue::encode(slot.getPureResult());
}
+
+EncodedJSValue JIT_OPERATION operationTryGetByIdGeneric(ExecState* exec, EncodedJSValue base, UniquedStringImpl* uid)
+{
+ VM* vm = &exec->vm();
+ NativeCallFrameTracer tracer(vm, exec);
+ Identifier ident = Identifier::fromUid(vm, uid);
+
+ JSValue baseValue = JSValue::decode(base);
+ PropertySlot slot(baseValue, PropertySlot::InternalMethodType::VMInquiry);
+ baseValue.getPropertySlot(exec, ident, slot);
+
+ return JSValue::encode(slot.getPureResult());
+}
+
EncodedJSValue JIT_OPERATION operationTryGetByIdOptimize(ExecState* exec, StructureStubInfo* stubInfo, EncodedJSValue base, UniquedStringImpl* uid)
{
VM* vm = &exec->vm();
Modified: trunk/Source/_javascript_Core/jit/JITOperations.h (199278 => 199279)
--- trunk/Source/_javascript_Core/jit/JITOperations.h 2016-04-10 01:46:27 UTC (rev 199278)
+++ trunk/Source/_javascript_Core/jit/JITOperations.h 2016-04-10 03:38:44 UTC (rev 199279)
@@ -288,6 +288,7 @@
int32_t JIT_OPERATION operationCallArityCheck(ExecState*) WTF_INTERNAL;
int32_t JIT_OPERATION operationConstructArityCheck(ExecState*) WTF_INTERNAL;
EncodedJSValue JIT_OPERATION operationTryGetById(ExecState*, StructureStubInfo*, EncodedJSValue, UniquedStringImpl*) WTF_INTERNAL;
+EncodedJSValue JIT_OPERATION operationTryGetByIdGeneric(ExecState*, EncodedJSValue, UniquedStringImpl*) WTF_INTERNAL;
EncodedJSValue JIT_OPERATION operationTryGetByIdOptimize(ExecState*, StructureStubInfo*, EncodedJSValue, UniquedStringImpl*) WTF_INTERNAL;
EncodedJSValue JIT_OPERATION operationGetById(ExecState*, StructureStubInfo*, EncodedJSValue, UniquedStringImpl*) WTF_INTERNAL;
EncodedJSValue JIT_OPERATION operationGetByIdGeneric(ExecState*, EncodedJSValue, UniquedStringImpl*) WTF_INTERNAL;
Modified: trunk/Source/_javascript_Core/tests/stress/try-get-by-id.js (199278 => 199279)
--- trunk/Source/_javascript_Core/tests/stress/try-get-by-id.js 2016-04-10 01:46:27 UTC (rev 199278)
+++ trunk/Source/_javascript_Core/tests/stress/try-get-by-id.js 2016-04-10 03:38:44 UTC (rev 199279)
@@ -1,27 +1,38 @@
function tryGetByIdText(propertyName) { return `(function (base) { return @tryGetById(base, '${propertyName}'); })`; }
+function tryGetByIdTextStrict(propertyName) { return `(function (base) { "use strict"; return @tryGetById(base, '${propertyName}'); })`; }
// Test get value off object.
{
- let getCaller = createBuiltin(tryGetByIdText("caller"));
- noInline(getCaller);
+ let get = createBuiltin(tryGetByIdText("caller"));
+ noInline(get);
+ let getStrict = createBuiltin(tryGetByIdTextStrict("caller"));
+ noInline(getStrict);
+
let obj = {caller: 1};
for (let i = 0; i < 100000; i++) {
- if (getCaller(obj) !== 1)
+ if (get(obj) !== 1)
throw new Error("wrong on iteration: " + i);
+ if (getStrict(obj) !== 1)
+ throw new Error("wrong on iteration: " + i);
}
}
// Test slot is custom function trap for a value.
{
- let getCaller = createBuiltin(tryGetByIdText("caller"));
- noInline(getCaller);
+ let get = createBuiltin(tryGetByIdText("caller"));
+ noInline(get);
+ let getStrict = createBuiltin(tryGetByIdTextStrict("caller"));
+ noInline(getStrict);
+
let func = function () {};
for (let i = 0; i < 100000; i++) {
- if (getCaller(func) !== null)
+ if (get(func) !== null)
throw new Error("wrong on iteration: " + i);
+ if (getStrict(func) !== null)
+ throw new Error("wrong on iteration: " + i);
}
}
@@ -29,12 +40,17 @@
{
let get = createBuiltin(tryGetByIdText("getterSetter"));
noInline(get);
+ let getStrict = createBuiltin(tryGetByIdTextStrict("getterSetter"));
+ noInline(getStrict);
+
let obj = {};
Object.defineProperty(obj, "getterSetter", { get: function () { throw new Error("should not be called"); } });
for (let i = 0; i < 100000; i++) {
if (get(obj) !== getGetterSetter(obj, "getterSetter"))
throw new Error("wrong on iteration: " + i);
+ if (getStrict(obj) !== getGetterSetter(obj, "getterSetter"))
+ throw new Error("wrong on iteration: " + i);
}
}
@@ -42,11 +58,16 @@
{
let get = createBuiltin(tryGetByIdText("getterSetter"));
noInline(get);
+ let getStrict = createBuiltin(tryGetByIdTextStrict("getterSetter"));
+ noInline(getStrict);
+
let obj = {};
for (let i = 0; i < 100000; i++) {
if (get(obj) !== undefined)
throw new Error("wrong on iteration: " + i);
+ if (getStrict(obj) !== undefined)
+ throw new Error("wrong on iteration: " + i);
}
}
@@ -54,6 +75,8 @@
{
let get = createBuiltin(tryGetByIdText("value"));
noInline(get);
+ let getStrict = createBuiltin(tryGetByIdTextStrict("value"));
+ noInline(getStrict);
let obj = {value: 1};
let p = new Proxy(obj, { get: function() { throw new Error("should not be called"); } });
@@ -61,6 +84,8 @@
for (let i = 0; i < 100000; i++) {
if (get(p) !== null)
throw new Error("wrong on iteration: " + i);
+ if (getStrict(p) !== null)
+ throw new Error("wrong on iteration: " + i);
}
}
@@ -68,6 +93,8 @@
{
let get = createBuiltin(tryGetByIdText("caller"));
noInline(get);
+ let getStrict = createBuiltin(tryGetByIdTextStrict("caller"));
+ noInline(getStrict);
let obj = {caller : 1};
let func = function() {};
@@ -76,9 +103,13 @@
if (i % 100 === 0) {
if (get(func) !== null)
throw new Error("wrong on iteration: " + i);
+ if (getStrict(func) !== null)
+ throw new Error("wrong on iteration: " + i);
} else {
if (get(obj) !== 1)
throw new Error("wrong on iteration: " + i);
+ if (getStrict(obj) !== 1)
+ throw new Error("wrong on iteration: " + i);
}
}
}
@@ -87,6 +118,8 @@
{
let get = createBuiltin(tryGetByIdText("caller"));
noInline(get);
+ let getStrict = createBuiltin(tryGetByIdTextStrict("caller"));
+ noInline(getStrict);
let func = function() {};
@@ -94,10 +127,14 @@
if (i % 100 === 0) {
if (get(func) !== null)
throw new Error("wrong on iteration: " + i);
+ if (getStrict(func) !== null)
+ throw new Error("wrong on iteration: " + i);
} else {
let obj = {caller : 1};
if (get(obj) !== 1)
throw new Error("wrong on iteration: " + i);
+ if (getStrict(obj) !== 1)
+ throw new Error("wrong on iteration: " + i);
}
}
}
@@ -106,12 +143,30 @@
{
let get = createBuiltin(tryGetByIdText("length"));
noInline(get);
+ let getStrict = createBuiltin(tryGetByIdTextStrict("length"));
+ noInline(getStrict);
let arr = [];
for (let i = 0; i < 100000; i++) {
if (get(arr) !== null)
throw new Error("wrong on iteration: " + i);
+ if (getStrict(arr) !== null)
+ throw new Error("wrong on iteration: " + i);
+ }
+}
+// Test with non-object.
+{
+ let get = createBuiltin(tryGetByIdText("length"));
+ noInline(get);
+ let getStrict = createBuiltin(tryGetByIdTextStrict("length"));
+ noInline(getStrict);
+
+ for (let i = 0; i < 100000; i++) {
+ if (get(1) !== undefined)
+ throw new Error("wrong on iteration: " + i);
+ if (getStrict(1) !== undefined)
+ throw new Error("wrong on iteration: " + i);
}
}