Diff
Modified: branches/ftlopt/LayoutTests/ChangeLog (169901 => 169902)
--- branches/ftlopt/LayoutTests/ChangeLog 2014-06-12 17:33:05 UTC (rev 169901)
+++ branches/ftlopt/LayoutTests/ChangeLog 2014-06-12 17:42:10 UTC (rev 169902)
@@ -1,3 +1,17 @@
+2014-06-11 Filip Pizlo <[email protected]>
+
+ [ftlopt] DFG get_by_id should inline chain accesses with a slightly polymorphic base
+ https://bugs.webkit.org/show_bug.cgi?id=133751
+
+ Reviewed by Mark Hahnenberg.
+
+ * js/regress/poly-chain-access-expected.txt: Added.
+ * js/regress/poly-chain-access-simpler-expected.txt: Added.
+ * js/regress/poly-chain-access-simpler.html: Added.
+ * js/regress/poly-chain-access.html: Added.
+ * js/regress/script-tests/poly-chain-access-simpler.js: Added.
+ * js/regress/script-tests/poly-chain-access.js: Added.
+
2014-06-01 Filip Pizlo <[email protected]>
[ftlopt] AI should be able track structure sets larger than 1
Added: branches/ftlopt/LayoutTests/js/regress/poly-chain-access-expected.txt (0 => 169902)
--- branches/ftlopt/LayoutTests/js/regress/poly-chain-access-expected.txt (rev 0)
+++ branches/ftlopt/LayoutTests/js/regress/poly-chain-access-expected.txt 2014-06-12 17:42:10 UTC (rev 169902)
@@ -0,0 +1,10 @@
+JSRegress/poly-chain-access
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS no exception thrown
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
Added: branches/ftlopt/LayoutTests/js/regress/poly-chain-access-simpler-expected.txt (0 => 169902)
--- branches/ftlopt/LayoutTests/js/regress/poly-chain-access-simpler-expected.txt (rev 0)
+++ branches/ftlopt/LayoutTests/js/regress/poly-chain-access-simpler-expected.txt 2014-06-12 17:42:10 UTC (rev 169902)
@@ -0,0 +1,10 @@
+JSRegress/poly-chain-access-simpler
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS no exception thrown
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
Added: branches/ftlopt/LayoutTests/js/regress/poly-chain-access-simpler.html (0 => 169902)
--- branches/ftlopt/LayoutTests/js/regress/poly-chain-access-simpler.html (rev 0)
+++ branches/ftlopt/LayoutTests/js/regress/poly-chain-access-simpler.html 2014-06-12 17:42:10 UTC (rev 169902)
@@ -0,0 +1,12 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html>
+<head>
+<script src=""
+</head>
+<body>
+<script src=""
+<script src=""
+<script src=""
+<script src=""
+</body>
+</html>
Added: branches/ftlopt/LayoutTests/js/regress/poly-chain-access.html (0 => 169902)
--- branches/ftlopt/LayoutTests/js/regress/poly-chain-access.html (rev 0)
+++ branches/ftlopt/LayoutTests/js/regress/poly-chain-access.html 2014-06-12 17:42:10 UTC (rev 169902)
@@ -0,0 +1,12 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html>
+<head>
+<script src=""
+</head>
+<body>
+<script src=""
+<script src=""
+<script src=""
+<script src=""
+</body>
+</html>
Added: branches/ftlopt/LayoutTests/js/regress/script-tests/poly-chain-access-simpler.js (0 => 169902)
--- branches/ftlopt/LayoutTests/js/regress/script-tests/poly-chain-access-simpler.js (rev 0)
+++ branches/ftlopt/LayoutTests/js/regress/script-tests/poly-chain-access-simpler.js 2014-06-12 17:42:10 UTC (rev 169902)
@@ -0,0 +1,26 @@
+(function() {
+ function Foo() { }
+ Foo.prototype.f = 42;
+ function Bar() { }
+ Bar.prototype = new Foo();
+
+ function foo(o, p) {
+ var n = 1000000;
+ var result = 0;
+ for (var i = 0; i < n; ++i) {
+ result += o.f;
+ var tmp = o;
+ o = p;
+ p = tmp;
+ }
+
+ if (result != n * 42)
+ throw "Error: bad result: " + result;
+ }
+
+ var o = new Bar();
+ var p = new Bar();
+ p.g = 43;
+
+ foo(o, p);
+})();
Added: branches/ftlopt/LayoutTests/js/regress/script-tests/poly-chain-access.js (0 => 169902)
--- branches/ftlopt/LayoutTests/js/regress/script-tests/poly-chain-access.js (rev 0)
+++ branches/ftlopt/LayoutTests/js/regress/script-tests/poly-chain-access.js 2014-06-12 17:42:10 UTC (rev 169902)
@@ -0,0 +1,22 @@
+(function() {
+ function Foo() { }
+ Foo.prototype.f = 42;
+ function Bar() { }
+ Bar.prototype = new Foo();
+
+ var o = new Bar();
+ var p = new Bar();
+ p.g = 43;
+
+ var n = 1000000;
+ var result = 0;
+ for (var i = 0; i < n; ++i) {
+ result += o.f;
+ var tmp = o;
+ o = p;
+ p = tmp;
+ }
+
+ if (result != n * 42)
+ throw "Error: bad result: " + result;
+})();
Modified: branches/ftlopt/Source/_javascript_Core/ChangeLog (169901 => 169902)
--- branches/ftlopt/Source/_javascript_Core/ChangeLog 2014-06-12 17:33:05 UTC (rev 169901)
+++ branches/ftlopt/Source/_javascript_Core/ChangeLog 2014-06-12 17:42:10 UTC (rev 169902)
@@ -1,3 +1,36 @@
+2014-06-11 Filip Pizlo <[email protected]>
+
+ [ftlopt] DFG get_by_id should inline chain accesses with a slightly polymorphic base
+ https://bugs.webkit.org/show_bug.cgi?id=133751
+
+ Reviewed by Mark Hahnenberg.
+
+ * bytecode/GetByIdStatus.cpp:
+ (JSC::GetByIdStatus::appendVariant):
+ (JSC::GetByIdStatus::computeForStubInfo):
+ * bytecode/GetByIdVariant.cpp:
+ (JSC::GetByIdVariant::attemptToMerge):
+ * bytecode/GetByIdVariant.h:
+ * bytecode/PutByIdStatus.cpp:
+ (JSC::PutByIdStatus::computeFor):
+ * dfg/DFGByteCodeParser.cpp:
+ (JSC::DFG::ByteCodeParser::emitPrototypeChecks):
+ (JSC::DFG::ByteCodeParser::handleGetById):
+ (JSC::DFG::ByteCodeParser::handlePutById):
+ * runtime/IntendedStructureChain.cpp:
+ (JSC::IntendedStructureChain::IntendedStructureChain):
+ (JSC::IntendedStructureChain::isStillValid):
+ (JSC::IntendedStructureChain::isNormalized):
+ (JSC::IntendedStructureChain::terminalPrototype):
+ (JSC::IntendedStructureChain::operator==):
+ (JSC::IntendedStructureChain::visitChildren):
+ (JSC::IntendedStructureChain::dumpInContext):
+ (JSC::IntendedStructureChain::chain): Deleted.
+ * runtime/IntendedStructureChain.h:
+ (JSC::IntendedStructureChain::prototype):
+ (JSC::IntendedStructureChain::operator!=):
+ (JSC::IntendedStructureChain::head): Deleted.
+
2014-06-11 Matthew Mirman <[email protected]>
Readded native calling to the FTL and Split the DFG nodes
Modified: branches/ftlopt/Source/_javascript_Core/bytecode/GetByIdStatus.cpp (169901 => 169902)
--- branches/ftlopt/Source/_javascript_Core/bytecode/GetByIdStatus.cpp 2014-06-12 17:33:05 UTC (rev 169901)
+++ branches/ftlopt/Source/_javascript_Core/bytecode/GetByIdStatus.cpp 2014-06-12 17:42:10 UTC (rev 169902)
@@ -39,10 +39,20 @@
bool GetByIdStatus::appendVariant(const GetByIdVariant& variant)
{
+ // Attempt to merge this variant with an already existing variant.
for (unsigned i = 0; i < m_variants.size(); ++i) {
+ if (m_variants[i].attemptToMerge(variant))
+ return true;
+ }
+
+ // Make sure there is no overlap. We should have pruned out opportunities for
+ // overlap but it's possible that an inline cache got into a weird state. We are
+ // defensive and bail if we detect crazy.
+ for (unsigned i = 0; i < m_variants.size(); ++i) {
if (m_variants[i].structureSet().overlaps(variant.structureSet()))
return false;
}
+
m_variants.append(variant);
return true;
}
@@ -187,10 +197,12 @@
profiledBlock, structure, list->at(listIndex).chain(),
list->at(listIndex).chainCount()));
- if (!chain->isStillValid())
- return GetByIdStatus(slowPathState, true);
+ if (!chain->isStillValid()) {
+ // This won't ever run again so skip it.
+ continue;
+ }
- if (chain->head()->takesSlowPathInDFGForImpureProperty())
+ if (structure->takesSlowPathInDFGForImpureProperty())
return GetByIdStatus(slowPathState, true);
size_t chainSize = chain->size();
@@ -217,36 +229,7 @@
if (!isValidOffset(myOffset))
return GetByIdStatus(slowPathState, true);
-
- if (!chain && !list->at(listIndex).doesCalls()) {
- // For non-chain, non-getter accesses, we try to do some coalescing.
- bool found = false;
- for (unsigned variantIndex = 0; variantIndex < result.m_variants.size(); ++variantIndex) {
- GetByIdVariant& variant = result.m_variants[variantIndex];
- if (variant.m_chain)
- continue;
-
- if (variant.m_offset != myOffset)
- continue;
-
- if (variant.callLinkStatus())
- continue;
-
- found = true;
- if (variant.m_structureSet.contains(structure))
- break;
-
- if (variant.m_specificValue != JSValue(specificValue))
- variant.m_specificValue = JSValue();
-
- variant.m_structureSet.add(structure);
- break;
- }
- if (found)
- continue;
- }
-
std::unique_ptr<CallLinkStatus> callLinkStatus;
switch (list->at(listIndex).type()) {
case GetByIdAccess::SimpleInline:
@@ -272,6 +255,7 @@
GetByIdVariant variant(
StructureSet(structure), myOffset, specificValue, chain,
std::move(callLinkStatus));
+
if (!result.appendVariant(variant))
return GetByIdStatus(slowPathState, true);
}
Modified: branches/ftlopt/Source/_javascript_Core/bytecode/GetByIdVariant.cpp (169901 => 169902)
--- branches/ftlopt/Source/_javascript_Core/bytecode/GetByIdVariant.cpp 2014-06-12 17:33:05 UTC (rev 169901)
+++ branches/ftlopt/Source/_javascript_Core/bytecode/GetByIdVariant.cpp 2014-06-12 17:42:10 UTC (rev 169902)
@@ -51,6 +51,25 @@
return *this;
}
+bool GetByIdVariant::attemptToMerge(const GetByIdVariant& other)
+{
+ if (!!m_chain != !!other.m_chain)
+ return false;
+ if (m_chain && *m_chain != *other.m_chain)
+ return false;
+ if (m_offset != other.m_offset)
+ return false;
+ if (m_callLinkStatus || other.m_callLinkStatus)
+ return false;
+
+ if (m_specificValue != other.m_specificValue)
+ m_specificValue = JSValue();
+
+ m_structureSet.merge(other.m_structureSet);
+
+ return true;
+}
+
void GetByIdVariant::dump(PrintStream& out) const
{
dumpInContext(out, 0);
Modified: branches/ftlopt/Source/_javascript_Core/bytecode/GetByIdVariant.h (169901 => 169902)
--- branches/ftlopt/Source/_javascript_Core/bytecode/GetByIdVariant.h 2014-06-12 17:33:05 UTC (rev 169901)
+++ branches/ftlopt/Source/_javascript_Core/bytecode/GetByIdVariant.h 2014-06-12 17:42:10 UTC (rev 169902)
@@ -71,6 +71,8 @@
PropertyOffset offset() const { return m_offset; }
CallLinkStatus* callLinkStatus() const { return m_callLinkStatus.get(); }
+ bool attemptToMerge(const GetByIdVariant& other);
+
void dump(PrintStream&) const;
void dumpInContext(PrintStream&, DumpContext*) const;
Modified: branches/ftlopt/Source/_javascript_Core/bytecode/PutByIdStatus.cpp (169901 => 169902)
--- branches/ftlopt/Source/_javascript_Core/bytecode/PutByIdStatus.cpp 2014-06-12 17:33:05 UTC (rev 169901)
+++ branches/ftlopt/Source/_javascript_Core/bytecode/PutByIdStatus.cpp 2014-06-12 17:42:10 UTC (rev 169902)
@@ -309,7 +309,7 @@
// dictionaries if we have evidence to suggest that those objects were never used as
// prototypes in a cacheable prototype access - i.e. there's a good chance that some of
// the other checks below will fail.
- if (!chain->isNormalized())
+ if (structure->isProxy() || !chain->isNormalized())
return PutByIdStatus(TakesSlowPath);
}
Modified: branches/ftlopt/Source/_javascript_Core/dfg/DFGByteCodeParser.cpp (169901 => 169902)
--- branches/ftlopt/Source/_javascript_Core/dfg/DFGByteCodeParser.cpp 2014-06-12 17:33:05 UTC (rev 169901)
+++ branches/ftlopt/Source/_javascript_Core/dfg/DFGByteCodeParser.cpp 2014-06-12 17:42:10 UTC (rev 169902)
@@ -192,7 +192,7 @@
void handlePutById(
Node* base, unsigned identifierNumber, Node* value, const PutByIdStatus&,
bool isDirect);
- Node* emitPrototypeChecks(Structure*, IntendedStructureChain*);
+ Node* emitPrototypeChecks(IntendedStructureChain*);
Node* getScope(bool skipTop, unsigned skipCount);
@@ -1705,18 +1705,18 @@
return result;
}
-Node* ByteCodeParser::emitPrototypeChecks(
- Structure* structure, IntendedStructureChain* chain)
+Node* ByteCodeParser::emitPrototypeChecks(IntendedStructureChain* chain)
{
- ASSERT(structure);
- Node* base = 0;
m_graph.chains().addLazily(chain);
- Structure* currentStructure = structure;
- JSObject* currentObject = 0;
+ JSValue prototype = chain->prototype();
+ if (prototype.isNull())
+ return nullptr;
+ Node* base = nullptr;
for (unsigned i = 0; i < chain->size(); ++i) {
- currentObject = asObject(currentStructure->prototypeForLookup(m_inlineStackTop->m_codeBlock));
- currentStructure = chain->at(i);
+ JSObject* currentObject = asObject(prototype);
+ Structure* currentStructure = chain->at(i);
base = cellConstantWithStructureCheck(currentObject, currentStructure);
+ prototype = currentStructure->prototypeForLookup(m_inlineStackTop->m_codeBlock);
}
return base;
}
@@ -1748,11 +1748,8 @@
// optimal, if there is some rarely executed case in the chain that requires a lot
// of checks and those checks are not watchpointable.
for (unsigned variantIndex = getByIdStatus.numVariants(); variantIndex--;) {
- if (getByIdStatus[variantIndex].chain()) {
- emitPrototypeChecks(
- getByIdStatus[variantIndex].structureSet().onlyStructure(),
- getByIdStatus[variantIndex].chain());
- }
+ if (getByIdStatus[variantIndex].chain())
+ emitPrototypeChecks(getByIdStatus[variantIndex].chain());
}
// 2) Emit a MultiGetByOffset
@@ -1774,10 +1771,8 @@
addToGraph(CheckStructure, OpInfo(m_graph.addStructureSet(variant.structureSet())), base);
- if (variant.chain()) {
- base = emitPrototypeChecks(
- variant.structureSet().onlyStructure(), variant.chain());
- }
+ if (variant.chain())
+ base = emitPrototypeChecks(variant.chain());
// Unless we want bugs like https://bugs.webkit.org/show_bug.cgi?id=88783, we need to
// ensure that the base of the original get_by_id is kept alive until we're done with
@@ -1876,9 +1871,7 @@
continue;
if (!putByIdStatus[variantIndex].structureChain())
continue;
- emitPrototypeChecks(
- putByIdStatus[variantIndex].oldStructure(),
- putByIdStatus[variantIndex].structureChain());
+ emitPrototypeChecks(putByIdStatus[variantIndex].structureChain());
}
}
@@ -1914,7 +1907,7 @@
addToGraph(CheckStructure, OpInfo(m_graph.addStructureSet(variant.oldStructure())), base);
if (!isDirect)
- emitPrototypeChecks(variant.oldStructure(), variant.structureChain());
+ emitPrototypeChecks(variant.structureChain());
ASSERT(variant.oldStructure()->transitionWatchpointSetHasBeenInvalidated());
Modified: branches/ftlopt/Source/_javascript_Core/runtime/IntendedStructureChain.cpp (169901 => 169902)
--- branches/ftlopt/Source/_javascript_Core/runtime/IntendedStructureChain.cpp 2014-06-12 17:33:05 UTC (rev 169901)
+++ branches/ftlopt/Source/_javascript_Core/runtime/IntendedStructureChain.cpp 2014-06-12 17:42:10 UTC (rev 169902)
@@ -33,27 +33,37 @@
namespace JSC {
-IntendedStructureChain::IntendedStructureChain(JSGlobalObject* globalObject, Structure* head)
+IntendedStructureChain::IntendedStructureChain(JSGlobalObject* globalObject, JSValue prototype)
: m_globalObject(globalObject)
- , m_head(head)
+ , m_prototype(prototype)
{
- JSValue prototype = head->prototypeForLookup(globalObject);
+ ASSERT(m_prototype.isNull() || m_prototype.isObject());
if (prototype.isNull())
return;
for (Structure* current = asObject(prototype)->structure(); current; current = current->storedPrototypeStructure())
m_vector.append(current);
}
+IntendedStructureChain::IntendedStructureChain(JSGlobalObject* globalObject, Structure* head)
+ : m_globalObject(globalObject)
+ , m_prototype(head->prototypeForLookup(m_globalObject))
+{
+ if (m_prototype.isNull())
+ return;
+ for (Structure* current = asObject(m_prototype)->structure(); current; current = current->storedPrototypeStructure())
+ m_vector.append(current);
+}
+
IntendedStructureChain::IntendedStructureChain(CodeBlock* codeBlock, Structure* head, Structure* prototypeStructure)
: m_globalObject(codeBlock->globalObject())
- , m_head(head)
+ , m_prototype(head->prototypeForLookup(m_globalObject))
{
m_vector.append(prototypeStructure);
}
IntendedStructureChain::IntendedStructureChain(CodeBlock* codeBlock, Structure* head, StructureChain* chain)
: m_globalObject(codeBlock->globalObject())
- , m_head(head)
+ , m_prototype(head->prototypeForLookup(m_globalObject))
{
for (unsigned i = 0; chain->head()[i]; ++i)
m_vector.append(chain->head()[i].get());
@@ -61,7 +71,7 @@
IntendedStructureChain::IntendedStructureChain(CodeBlock* codeBlock, Structure* head, StructureChain* chain, unsigned count)
: m_globalObject(codeBlock->globalObject())
- , m_head(head)
+ , m_prototype(head->prototypeForLookup(m_globalObject))
{
for (unsigned i = 0; i < count; ++i)
m_vector.append(chain->head()[i].get());
@@ -73,7 +83,7 @@
bool IntendedStructureChain::isStillValid() const
{
- JSValue currentPrototype = m_head->prototypeForLookup(m_globalObject);
+ JSValue currentPrototype = m_prototype;
for (unsigned i = 0; i < m_vector.size(); ++i) {
if (asObject(currentPrototype)->structure() != m_vector[i])
return false;
@@ -93,14 +103,6 @@
return true;
}
-StructureChain* IntendedStructureChain::chain(VM& vm) const
-{
- ASSERT(isStillValid());
- StructureChain* result = StructureChain::create(vm, m_head);
- ASSERT(matches(result));
- return result;
-}
-
bool IntendedStructureChain::mayInterceptStoreTo(VM& vm, StringImpl* uid)
{
for (unsigned i = 0; i < m_vector.size(); ++i) {
@@ -118,8 +120,6 @@
bool IntendedStructureChain::isNormalized()
{
- if (m_head->isProxy())
- return false;
for (unsigned i = 0; i < m_vector.size(); ++i) {
Structure* structure = m_vector[i];
if (structure->isProxy())
@@ -134,14 +134,21 @@
{
ASSERT(!m_vector.isEmpty());
if (m_vector.size() == 1)
- return asObject(m_head->prototypeForLookup(m_globalObject));
+ return asObject(m_prototype);
return asObject(m_vector[m_vector.size() - 2]->storedPrototype());
}
+bool IntendedStructureChain::operator==(const IntendedStructureChain& other) const
+{
+ return m_globalObject == other.m_globalObject
+ && m_prototype == other.m_prototype
+ && m_vector == other.m_vector;
+}
+
void IntendedStructureChain::visitChildren(SlotVisitor& visitor)
{
visitor.appendUnbarrieredPointer(&m_globalObject);
- visitor.appendUnbarrieredPointer(&m_head);
+ visitor.appendUnbarrieredValue(&m_prototype);
for (unsigned i = m_vector.size(); i--;)
visitor.appendUnbarrieredPointer(&m_vector[i]);
}
@@ -155,7 +162,7 @@
{
out.print(
"(global = ", RawPointer(m_globalObject), ", head = ",
- pointerDumpInContext(m_head, context), ", vector = [");
+ inContext(m_prototype, context), ", vector = [");
CommaPrinter comma;
for (unsigned i = 0; i < m_vector.size(); ++i)
out.print(comma, pointerDumpInContext(m_vector[i], context));
Modified: branches/ftlopt/Source/_javascript_Core/runtime/IntendedStructureChain.h (169901 => 169902)
--- branches/ftlopt/Source/_javascript_Core/runtime/IntendedStructureChain.h 2014-06-12 17:33:05 UTC (rev 169901)
+++ branches/ftlopt/Source/_javascript_Core/runtime/IntendedStructureChain.h 2014-06-12 17:42:10 UTC (rev 169902)
@@ -39,7 +39,8 @@
class IntendedStructureChain : public RefCounted<IntendedStructureChain> {
public:
- IntendedStructureChain(JSGlobalObject* globalObject, Structure* head);
+ IntendedStructureChain(JSGlobalObject* globalObject, JSValue prototype);
+ IntendedStructureChain(JSGlobalObject* globalObject, Structure*);
IntendedStructureChain(CodeBlock* codeBlock, Structure* head, Structure* prototypeStructure);
IntendedStructureChain(CodeBlock* codeBlock, Structure* head, StructureChain* chain);
IntendedStructureChain(CodeBlock* codeBlock, Structure* head, StructureChain* chain, unsigned count);
@@ -47,11 +48,10 @@
bool isStillValid() const;
bool matches(StructureChain*) const;
- StructureChain* chain(VM&) const;
bool mayInterceptStoreTo(VM&, StringImpl* uid);
bool isNormalized();
- Structure* head() const { return m_head; }
+ JSValue prototype() const { return m_prototype; }
size_t size() const { return m_vector.size(); }
Structure* at(size_t index) { return m_vector[index]; }
@@ -61,13 +61,19 @@
Structure* last() const { return m_vector.last(); }
+ bool operator==(const IntendedStructureChain&) const;
+ bool operator!=(const IntendedStructureChain& other) const
+ {
+ return !(*this == other);
+ }
+
void visitChildren(SlotVisitor&);
void dump(PrintStream&) const;
void dumpInContext(PrintStream&, DumpContext*) const;
private:
JSGlobalObject* m_globalObject;
- Structure* m_head;
+ JSValue m_prototype;
Vector<Structure*> m_vector;
};