Diff
Modified: trunk/Source/_javascript_Core/ChangeLog (202092 => 202093)
--- trunk/Source/_javascript_Core/ChangeLog 2016-06-15 16:30:00 UTC (rev 202092)
+++ trunk/Source/_javascript_Core/ChangeLog 2016-06-15 16:39:19 UTC (rev 202093)
@@ -1,3 +1,29 @@
+2016-06-15 Keith Miller <[email protected]>
+
+ DFGByteCodeParser should be able to infer a property is unset from the Baseline inline cache.
+ https://bugs.webkit.org/show_bug.cgi?id=158774
+
+ Reviewed by Filip Pizlo.
+
+ This patch allows the DFGByteCodeParser to speculatively convert a property access into a
+ constant if that access was always a miss in the Baseline inline cache. This patch does
+ not add support for MultiGetByOffset and unset properties. That functionality will come
+ a future patch.
+
+ * bytecode/ComplexGetStatus.cpp:
+ (JSC::ComplexGetStatus::computeFor):
+ * bytecode/GetByIdStatus.cpp:
+ (JSC::GetByIdStatus::computeForStubInfoWithoutExitSiteFeedback):
+ * bytecode/GetByIdVariant.h:
+ (JSC::GetByIdVariant::isPropertyUnset):
+ * bytecode/PutByIdVariant.h:
+ (JSC::PutByIdVariant::isPropertyUnset):
+ * dfg/DFGByteCodeParser.cpp:
+ (JSC::DFG::ByteCodeParser::load):
+ (JSC::DFG::ByteCodeParser::handleGetById):
+ * tests/stress/undefined-access-then-self-change.js: Added.
+ (foo):
+
2016-06-15 Yusuke Suzuki <[email protected]>
[JSC] Move calling convention flags to WTF
Modified: trunk/Source/_javascript_Core/bytecode/ComplexGetStatus.cpp (202092 => 202093)
--- trunk/Source/_javascript_Core/bytecode/ComplexGetStatus.cpp 2016-06-15 16:30:00 UTC (rev 202092)
+++ trunk/Source/_javascript_Core/bytecode/ComplexGetStatus.cpp 2016-06-15 16:39:19 UTC (rev 202093)
@@ -57,13 +57,12 @@
result.m_conditionSet.numberOfConditionsWithKind(PropertyCondition::Presence);
RELEASE_ASSERT(numberOfSlotBases <= 1);
if (!numberOfSlotBases) {
- // Currently we don't support misses. That's a bummer.
- // FIXME: https://bugs.webkit.org/show_bug.cgi?id=133052
- return takesSlowPath();
+ ASSERT(result.m_offset == invalidOffset);
+ return result;
}
ObjectPropertyCondition base = result.m_conditionSet.slotBaseCondition();
ASSERT(base.kind() == PropertyCondition::Presence);
-
+
result.m_offset = base.offset();
} else
result.m_offset = headStructure->getConcurrently(uid);
Modified: trunk/Source/_javascript_Core/bytecode/GetByIdStatus.cpp (202092 => 202093)
--- trunk/Source/_javascript_Core/bytecode/GetByIdStatus.cpp 2016-06-15 16:30:00 UTC (rev 202092)
+++ trunk/Source/_javascript_Core/bytecode/GetByIdStatus.cpp 2016-06-15 16:39:19 UTC (rev 202093)
@@ -218,7 +218,8 @@
switch (access.type()) {
case AccessCase::Load:
- case AccessCase::GetGetter: {
+ case AccessCase::GetGetter:
+ case AccessCase::Miss: {
break;
}
case AccessCase::IntrinsicGetter: {
@@ -238,7 +239,8 @@
// future. https://bugs.webkit.org/show_bug.cgi?id=133052
return GetByIdStatus(slowPathState, true);
} }
-
+
+ ASSERT((AccessCase::Miss == access.type()) == (access.offset() == invalidOffset));
GetByIdVariant variant(
StructureSet(structure), complexGetStatus.offset(),
complexGetStatus.conditionSet(), WTFMove(callLinkStatus),
Modified: trunk/Source/_javascript_Core/bytecode/GetByIdVariant.h (202092 => 202093)
--- trunk/Source/_javascript_Core/bytecode/GetByIdVariant.h 2016-06-15 16:30:00 UTC (rev 202092)
+++ trunk/Source/_javascript_Core/bytecode/GetByIdVariant.h 2016-06-15 16:39:19 UTC (rev 202093)
@@ -64,6 +64,8 @@
JSFunction* intrinsicFunction() const { return m_intrinsicFunction; }
Intrinsic intrinsic() const { return m_intrinsicFunction ? m_intrinsicFunction->intrinsic() : NoIntrinsic; }
+ bool isPropertyUnset() const { return offset() == invalidOffset; }
+
bool attemptToMerge(const GetByIdVariant& other);
void dump(PrintStream&) const;
Modified: trunk/Source/_javascript_Core/bytecode/PutByIdVariant.h (202092 => 202093)
--- trunk/Source/_javascript_Core/bytecode/PutByIdVariant.h 2016-06-15 16:30:00 UTC (rev 202092)
+++ trunk/Source/_javascript_Core/bytecode/PutByIdVariant.h 2016-06-15 16:39:19 UTC (rev 202093)
@@ -112,7 +112,10 @@
// We don't support intrinsics for Setters (it would be sweet if we did) but we need this for templated helpers.
Intrinsic intrinsic() const { return NoIntrinsic; }
-
+
+ // This is needed for templated helpers.
+ bool isPropertyUnset() const { return false; }
+
PropertyOffset offset() const
{
ASSERT(isSet());
Modified: trunk/Source/_javascript_Core/dfg/DFGByteCodeParser.cpp (202092 => 202093)
--- trunk/Source/_javascript_Core/dfg/DFGByteCodeParser.cpp 2016-06-15 16:30:00 UTC (rev 202092)
+++ trunk/Source/_javascript_Core/dfg/DFGByteCodeParser.cpp 2016-06-15 16:39:19 UTC (rev 202093)
@@ -3008,11 +3008,11 @@
Structure* structure = base->constant()->structure();
if (!structure->dfgShouldWatch()) {
if (!variant.conditionSet().isEmpty()) {
- // This means that we're loading from a prototype. We expect the base not to have the
- // property. We can only use ObjectPropertyCondition if all of the structures in the
- // variant.structureSet() agree on the prototype (it would be hilariously rare if they
- // didn't). Note that we are relying on structureSet() having at least one element. That
- // will always be true here because of how GetByIdStatus/PutByIdStatus work.
+ // This means that we're loading from a prototype or we have a property miss. We expect
+ // the base not to have the property. We can only use ObjectPropertyCondition if all of
+ // the structures in the variant.structureSet() agree on the prototype (it would be
+ // hilariously rare if they didn't). Note that we are relying on structureSet() having
+ // at least one element. That will always be true here because of how GetByIdStatus/PutByIdStatus work.
JSObject* prototype = variant.structureSet()[0]->storedPrototypeObject();
bool allAgree = true;
for (unsigned i = 1; i < variant.structureSet().size(); ++i) {
@@ -3049,7 +3049,13 @@
if (needStructureCheck)
addToGraph(CheckStructure, OpInfo(m_graph.addStructureSet(variant.structureSet())), base);
-
+
+ if (variant.isPropertyUnset()) {
+ if (m_graph.watchConditions(variant.conditionSet()))
+ return jsConstant(jsUndefined());
+ return nullptr;
+ }
+
SpeculatedType loadPrediction;
NodeType loadOp;
if (variant.callLinkStatus() || variant.intrinsic() != NoIntrinsic) {
@@ -3144,7 +3150,7 @@
// optimal, if there is some rarely executed case in the chain that requires a lot
// of checks and those checks are not watchpointable.
for (const GetByIdVariant& variant : getByIdStatus.variants()) {
- if (variant.intrinsic() != NoIntrinsic) {
+ if (variant.intrinsic() != NoIntrinsic || variant.isPropertyUnset()) {
set(VirtualRegister(destinationOperand),
addToGraph(getById, OpInfo(identifierNumber), OpInfo(prediction), base));
return;
Added: trunk/Source/_javascript_Core/tests/stress/undefined-access-then-self-change.js (0 => 202093)
--- trunk/Source/_javascript_Core/tests/stress/undefined-access-then-self-change.js (rev 0)
+++ trunk/Source/_javascript_Core/tests/stress/undefined-access-then-self-change.js 2016-06-15 16:39:19 UTC (rev 202093)
@@ -0,0 +1,18 @@
+function foo(o) {
+ return o.f;
+}
+
+noInline(foo);
+
+var o = Object.create(null);
+
+for (var i = 0; i < 10000; ++i) {
+ var result = foo(o);
+ if (result !== void 0)
+ throw "Error: bad result in loop: " + result;
+}
+
+o.f = 42
+var result = foo(o);
+if (result !== 42)
+ throw "Error: bad result at end: " + result;