https://bugs.webkit.org/show_bug.cgi?id=185177
Reviewed by Filip Pizlo.
JSTests:
* microbenchmarks/construct-poly-proto-object.js: Added.
(foo.A):
(foo):
* stress/allocation-sinking-new-object-with-poly-proto.js: Added.
(foo.A):
(foo):
(makePolyProto):
(bar):
(baz):
Source/JavaScriptCore:
This patch teaches the DFG/FTL how to constant fold CreateThis with
a known poly proto Structure to NewObject. We do it by emitting a NewObject
followed by a PutByOffset for the prototype value.
We make it so that ObjectAllocationProfile holds the prototype value.
This is sound because JSFunction clears that profile when its 'prototype'
field changes.
This patch also renames underscoreProtoPrivateName to polyProtoName since
that name was nonsensical: it was only used for poly proto.
This is a 2x speedup on the get_callee_polymorphic microbenchmark. I had
regressed that benchmark when I first introduced poly proto.
* builtins/BuiltinNames.cpp:
* builtins/BuiltinNames.h:
(JSC::BuiltinNames::BuiltinNames):
(JSC::BuiltinNames::polyProtoName const):
(JSC::BuiltinNames::underscoreProtoPrivateName const): Deleted.
* bytecode/ObjectAllocationProfile.h:
(JSC::ObjectAllocationProfile::prototype):
(JSC::ObjectAllocationProfile::clear):
(JSC::ObjectAllocationProfile::visitAggregate):
* bytecode/ObjectAllocationProfileInlines.h:
(JSC::ObjectAllocationProfile::initializeProfile):
* dfg/DFGAbstractInterpreterInlines.h:
(JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
* dfg/DFGByteCodeParser.cpp:
(JSC::DFG::ByteCodeParser::parseBlock):
* dfg/DFGConstantFoldingPhase.cpp:
(JSC::DFG::ConstantFoldingPhase::foldConstants):
* dfg/DFGOperations.cpp:
* runtime/CommonSlowPaths.cpp:
(JSC::SLOW_PATH_DECL):
* runtime/FunctionRareData.h:
* runtime/Structure.cpp:
(JSC::Structure::create):
git-svn-id: https://svn.webkit.org/repository/webkit/trunk@231345
268f45cc-cd09-0410-ab3c-
d52691b4dbfc
+2018-05-03 Saam Barati <sbarati@apple.com>
+
+ Don't prevent CreateThis being folded to NewObject when the structure is poly proto
+ https://bugs.webkit.org/show_bug.cgi?id=185177
+
+ Reviewed by Filip Pizlo.
+
+ * microbenchmarks/construct-poly-proto-object.js: Added.
+ (foo.A):
+ (foo):
+ * stress/allocation-sinking-new-object-with-poly-proto.js: Added.
+ (foo.A):
+ (foo):
+ (makePolyProto):
+ (bar):
+ (baz):
+
2018-05-03 Michael Saboff <msaboff@apple.com>
OSR entry pruning of Program Bytecodes doesn't take into account try/catch
--- /dev/null
+function foo() {
+ class A {
+ constructor() {
+ this.x = 25;
+ this.y = 30;
+ }
+ };
+ return A;
+}
+let A = foo();
+let B = foo();
+noInline(A);
+noInline(B);
+
+for (let i = 0; i < 400000; ++i) {
+ let b = !!(i % 2);
+ if (b)
+ new A;
+ else
+ new B;
+}
--- /dev/null
+function foo() {
+ class A {
+ constructor() {
+ }
+ };
+ return A;
+}
+let A = foo();
+let B = foo();
+
+function makePolyProto(o) {
+ return o.x;
+}
+noInline(makePolyProto);
+
+for (let i = 0; i < 1000; ++i) {
+ makePolyProto(i % 2 ? new A : new B);
+}
+
+function bar(b) {
+ let o = new A;
+ if (b) {
+ if (isFinalTier())
+ OSRExit();
+ return o;
+ }
+}
+noInline(bar);
+
+function baz(b) {
+ let o = new A;
+ if (b)
+ return o;
+}
+noInline(baz);
+
+for (let i = 0; i < 100000; ++i) {
+ let b = i % 10 === 0;
+ let r = bar(b);
+ if (b) {
+ if (r.__proto__ !== A.prototype)
+ throw new Error("Bad!");
+ }
+}
+
+for (let i = 0; i < 100000; ++i) {
+ let b = i % 10 === 0;
+ let r = baz(b);
+ if (b) {
+ if (r.__proto__ !== A.prototype)
+ throw new Error("Bad!");
+ }
+}
+2018-05-03 Saam Barati <sbarati@apple.com>
+
+ Don't prevent CreateThis being folded to NewObject when the structure is poly proto
+ https://bugs.webkit.org/show_bug.cgi?id=185177
+
+ Reviewed by Filip Pizlo.
+
+ This patch teaches the DFG/FTL how to constant fold CreateThis with
+ a known poly proto Structure to NewObject. We do it by emitting a NewObject
+ followed by a PutByOffset for the prototype value.
+
+ We make it so that ObjectAllocationProfile holds the prototype value.
+ This is sound because JSFunction clears that profile when its 'prototype'
+ field changes.
+
+ This patch also renames underscoreProtoPrivateName to polyProtoName since
+ that name was nonsensical: it was only used for poly proto.
+
+ This is a 2x speedup on the get_callee_polymorphic microbenchmark. I had
+ regressed that benchmark when I first introduced poly proto.
+
+ * builtins/BuiltinNames.cpp:
+ * builtins/BuiltinNames.h:
+ (JSC::BuiltinNames::BuiltinNames):
+ (JSC::BuiltinNames::polyProtoName const):
+ (JSC::BuiltinNames::underscoreProtoPrivateName const): Deleted.
+ * bytecode/ObjectAllocationProfile.h:
+ (JSC::ObjectAllocationProfile::prototype):
+ (JSC::ObjectAllocationProfile::clear):
+ (JSC::ObjectAllocationProfile::visitAggregate):
+ * bytecode/ObjectAllocationProfileInlines.h:
+ (JSC::ObjectAllocationProfile::initializeProfile):
+ * dfg/DFGAbstractInterpreterInlines.h:
+ (JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
+ * dfg/DFGByteCodeParser.cpp:
+ (JSC::DFG::ByteCodeParser::parseBlock):
+ * dfg/DFGConstantFoldingPhase.cpp:
+ (JSC::DFG::ConstantFoldingPhase::foldConstants):
+ * dfg/DFGOperations.cpp:
+ * runtime/CommonSlowPaths.cpp:
+ (JSC::SLOW_PATH_DECL):
+ * runtime/FunctionRareData.h:
+ * runtime/Structure.cpp:
+ (JSC::Structure::create):
+
2018-05-03 Michael Saboff <msaboff@apple.com>
OSR entry pruning of Program Bytecodes doesn't take into account try/catch
#undef INITIALIZE_BUILTIN_PRIVATE_NAMES
SymbolImpl::StaticSymbolImpl dollarVMPrivateName { "PrivateSymbol.$vm", SymbolImpl::s_flagIsPrivate };
-SymbolImpl::StaticSymbolImpl underscoreProtoPrivateName { "PrivateSymbol.__proto__", SymbolImpl::s_flagIsPrivate };
+SymbolImpl::StaticSymbolImpl polyProtoPrivateName { "PrivateSymbol.PolyProto", SymbolImpl::s_flagIsPrivate };
} // namespace Symbols
} // namespace JSC
#undef DECLARE_BUILTIN_PRIVATE_NAMES
extern SymbolImpl::StaticSymbolImpl dollarVMPrivateName;
-extern SymbolImpl::StaticSymbolImpl underscoreProtoPrivateName;
+extern SymbolImpl::StaticSymbolImpl polyProtoPrivateName;
}
#define INITIALIZE_PRIVATE_TO_PUBLIC_ENTRY(name) m_privateToPublicMap.add(m_##name##PrivateName.impl(), &m_##name);
JSC_COMMON_PRIVATE_IDENTIFIERS_EACH_WELL_KNOWN_SYMBOL(INITIALIZE_BUILTIN_SYMBOLS)
, m_dollarVMName(Identifier::fromString(vm, "$vm"))
, m_dollarVMPrivateName(Identifier::fromUid(vm, &static_cast<SymbolImpl&>(Symbols::dollarVMPrivateName)))
- , m_underscoreProtoPrivateName(Identifier::fromUid(vm, &static_cast<SymbolImpl&>(Symbols::underscoreProtoPrivateName)))
+ , m_polyProtoPrivateName(Identifier::fromUid(vm, &static_cast<SymbolImpl&>(Symbols::polyProtoPrivateName)))
{
JSC_FOREACH_BUILTIN_FUNCTION_NAME(INITIALIZE_PRIVATE_TO_PUBLIC_ENTRY)
JSC_COMMON_PRIVATE_IDENTIFIERS_EACH_PROPERTY_NAME(INITIALIZE_PRIVATE_TO_PUBLIC_ENTRY)
JSC_COMMON_PRIVATE_IDENTIFIERS_EACH_WELL_KNOWN_SYMBOL(INITIALIZE_SYMBOL_PUBLIC_TO_PRIVATE_ENTRY)
m_privateToPublicMap.add(m_dollarVMPrivateName.impl(), &m_dollarVMName);
m_publicToPrivateMap.add(m_dollarVMName.impl(), &m_dollarVMPrivateName);
- m_privateToPublicMap.add(m_underscoreProtoPrivateName.impl(), &commonIdentifiers->underscoreProto);
- m_publicToPrivateMap.add(commonIdentifiers->underscoreProto.impl(), &m_underscoreProtoPrivateName);
}
const Identifier* lookUpPrivateName(const Identifier&) const;
JSC_COMMON_PRIVATE_IDENTIFIERS_EACH_WELL_KNOWN_SYMBOL(DECLARE_BUILTIN_SYMBOL_ACCESSOR)
const JSC::Identifier& dollarVMPublicName() const { return m_dollarVMName; }
const JSC::Identifier& dollarVMPrivateName() const { return m_dollarVMPrivateName; }
- const JSC::Identifier& underscoreProtoPrivateName() const { return m_underscoreProtoPrivateName; }
+ const JSC::Identifier& polyProtoName() const { return m_polyProtoPrivateName; }
private:
Identifier m_emptyIdentifier;
JSC_COMMON_PRIVATE_IDENTIFIERS_EACH_WELL_KNOWN_SYMBOL(DECLARE_BUILTIN_SYMBOLS)
const JSC::Identifier m_dollarVMName;
const JSC::Identifier m_dollarVMPrivateName;
- const JSC::Identifier m_underscoreProtoPrivateName;
+ const JSC::Identifier m_polyProtoPrivateName;
typedef HashMap<RefPtr<UniquedStringImpl>, const Identifier*, IdentifierRepHash> BuiltinNamesMap;
BuiltinNamesMap m_publicToPrivateMap;
BuiltinNamesMap m_privateToPublicMap;
WTF::loadLoadFence();
return structure;
}
+ JSObject* prototype()
+ {
+ JSObject* prototype = m_prototype.get();
+ WTF::loadLoadFence();
+ return prototype;
+ }
unsigned inlineCapacity() { return m_inlineCapacity; }
+
void clear()
{
m_allocator = Allocator();
m_structure.clear();
+ m_prototype.clear();
m_inlineCapacity = 0;
ASSERT(isNull());
}
void visitAggregate(SlotVisitor& visitor)
{
visitor.append(m_structure);
+ visitor.append(m_prototype);
}
private:
Allocator m_allocator; // Precomputed to make things easier for generated code.
WriteBarrier<Structure> m_structure;
+ WriteBarrier<JSObject> m_prototype;
unsigned m_inlineCapacity;
};
{
ASSERT(!m_allocator);
ASSERT(!m_structure);
+ ASSERT(!m_prototype);
ASSERT(!m_inlineCapacity);
- // FIXME: When going poly proto, we should make an allocator and teach
- // create_this' fast path how to allocate a poly proto object.
- // https://bugs.webkit.org/show_bug.cgi?id=177517
+ // FIXME: Teach create_this's fast path how to allocate poly
+ // proto objects: https://bugs.webkit.org/show_bug.cgi?id=177517
+
bool isPolyProto = false;
FunctionExecutable* executable = nullptr;
if (constructor) {
RELEASE_ASSERT(structure->typeInfo().type() == FinalObjectType);
m_allocator = Allocator();
m_structure.set(vm, owner, structure);
+ m_prototype.set(vm, owner, prototype);
m_inlineCapacity = structure->inlineCapacity();
return;
}
m_allocator = allocator;
}
- // Ensure that if another thread sees the structure, it will see it properly created
+ // Ensure that if another thread sees the structure and prototype, it will see it properly created.
WTF::storeStoreFence();
m_structure.set(vm, owner, structure);
+ m_prototype.set(vm, owner, prototype);
m_inlineCapacity = inlineCapacity;
}
if (auto* function = jsDynamicCast<JSFunction*>(m_vm, base)) {
if (FunctionRareData* rareData = function->rareData()) {
if (Structure* structure = rareData->objectAllocationStructure()) {
- // FIXME: we should be able to allocate a poly proto object here:
- // https://bugs.webkit.org/show_bug.cgi?id=177517
- if (structure->hasMonoProto()) {
- m_graph.freeze(rareData);
- m_graph.watchpoints().addLazily(rareData->allocationProfileWatchpointSet());
- m_state.setFoundConstants(true);
- didFoldClobberWorld();
- forNode(node).set(m_graph, structure);
- break;
- }
+ m_graph.freeze(rareData);
+ m_graph.watchpoints().addLazily(rareData->allocationProfileWatchpointSet());
+ m_state.setFoundConstants(true);
+ didFoldClobberWorld();
+ forNode(node).set(m_graph, structure);
+ break;
}
}
}
#include "ArithProfile.h"
#include "ArrayConstructor.h"
#include "BasicBlockLocation.h"
+#include "BuiltinNames.h"
#include "BytecodeStructs.h"
#include "CallLinkStatus.h"
#include "CodeBlock.h"
bool alreadyEmitted = false;
if (function) {
if (FunctionRareData* rareData = function->rareData()) {
- if (Structure* structure = rareData->objectAllocationStructure()) {
- // FIXME: we should be able to allocate a poly proto object here:
- // https://bugs.webkit.org/show_bug.cgi?id=177517
- if (structure->hasMonoProto()) {
+ if (rareData->allocationProfileWatchpointSet().isStillValid()) {
+ Structure* structure = rareData->objectAllocationStructure();
+ JSObject* prototype = rareData->objectAllocationPrototype();
+ if (structure
+ && (structure->hasMonoProto() || prototype)
+ && rareData->allocationProfileWatchpointSet().isStillValid()) {
+
m_graph.freeze(rareData);
m_graph.watchpoints().addLazily(rareData->allocationProfileWatchpointSet());
// The callee is still live up to this point.
addToGraph(Phantom, callee);
- set(VirtualRegister(bytecode.dst()), addToGraph(NewObject, OpInfo(m_graph.registerStructure(structure))));
+ Node* object = addToGraph(NewObject, OpInfo(m_graph.registerStructure(structure)));
+ if (structure->hasPolyProto()) {
+ StorageAccessData* data = m_graph.m_storageAccessData.add();
+ data->offset = knownPolyProtoOffset;
+ data->identifierNumber = m_graph.identifiers().ensure(m_graph.m_vm.propertyNames->builtinNames().polyProtoName().impl());
+ InferredType::Descriptor inferredType = InferredType::Top;
+ data->inferredType = inferredType;
+ m_graph.registerInferredType(inferredType);
+ ASSERT(isInlineOffset(knownPolyProtoOffset));
+ addToGraph(PutByOffset, OpInfo(data), object, object, weakJSConstant(prototype));
+ }
+ set(VirtualRegister(bytecode.dst()), object);
alreadyEmitted = true;
}
}
#if ENABLE(DFG_JIT)
+#include "BuiltinNames.h"
#include "DFGAbstractInterpreterInlines.h"
#include "DFGArgumentsUtilities.h"
#include "DFGBasicBlockInlines.h"
if (JSValue base = m_state.forNode(node->child1()).m_value) {
if (auto* function = jsDynamicCast<JSFunction*>(m_graph.m_vm, base)) {
if (FunctionRareData* rareData = function->rareData()) {
- if (Structure* structure = rareData->objectAllocationStructure()) {
- // FIXME: we should be able to allocate a poly proto object here:
- // https://bugs.webkit.org/show_bug.cgi?id=177517
- if (structure->hasMonoProto()) {
+ if (rareData->allocationProfileWatchpointSet().isStillValid()) {
+ Structure* structure = rareData->objectAllocationStructure();
+ JSObject* prototype = rareData->objectAllocationPrototype();
+ if (structure
+ && (structure->hasMonoProto() || prototype)
+ && rareData->allocationProfileWatchpointSet().isStillValid()) {
+
m_graph.freeze(rareData);
m_graph.watchpoints().addLazily(rareData->allocationProfileWatchpointSet());
node->convertToNewObject(m_graph.registerStructure(structure));
+
+ if (structure->hasPolyProto()) {
+ StorageAccessData* data = m_graph.m_storageAccessData.add();
+ data->offset = knownPolyProtoOffset;
+ data->identifierNumber = m_graph.identifiers().ensure(m_graph.m_vm.propertyNames->builtinNames().polyProtoName().impl());
+ InferredType::Descriptor inferredType = InferredType::Top;
+ data->inferredType = inferredType;
+ m_graph.registerInferredType(inferredType);
+
+ NodeOrigin origin = node->origin.withInvalidExit();
+ Node* prototypeNode = m_insertionSet.insertConstant(
+ indexInBlock + 1, origin, m_graph.freeze(prototype));
+
+ ASSERT(isInlineOffset(knownPolyProtoOffset));
+ m_insertionSet.insertNode(
+ indexInBlock + 1, SpecNone, PutByOffset, origin, OpInfo(data),
+ Edge(node, KnownCellUse), Edge(node, KnownCellUse), Edge(prototypeNode, UntypedUse));
+ }
changed = true;
break;
+
}
}
}
if (constructor->type() == JSFunctionType && jsCast<JSFunction*>(constructor)->canUseAllocationProfile()) {
auto rareData = jsCast<JSFunction*>(constructor)->ensureRareDataAndAllocationProfile(exec, inlineCapacity);
RETURN_IF_EXCEPTION(scope, nullptr);
- Structure* structure = rareData->objectAllocationProfile()->structure();
+ ObjectAllocationProfile* allocationProfile = rareData->objectAllocationProfile();
+ Structure* structure = allocationProfile->structure();
JSObject* result = constructEmptyObject(exec, structure);
if (structure->hasPolyProto()) {
- JSObject* prototype = jsCast<JSFunction*>(constructor)->prototypeForConstruction(vm, exec);
+ JSObject* prototype = allocationProfile->prototype();
+ ASSERT(prototype == jsCast<JSFunction*>(constructor)->prototypeForConstruction(vm, exec));
result->putDirect(vm, knownPolyProtoOffset, prototype);
prototype->didBecomePrototype();
ASSERT_WITH_MESSAGE(!hasIndexedProperties(result->indexingType()), "We rely on JSFinalObject not starting out with an indexing type otherwise we would potentially need to convert to slow put storage");
cachedCallee.setWithoutWriteBarrier(JSCell::seenMultipleCalleeObjects());
size_t inlineCapacity = bytecode.inlineCapacity();
- Structure* structure = constructor->ensureRareDataAndAllocationProfile(exec, inlineCapacity)->objectAllocationProfile()->structure();
+ ObjectAllocationProfile* allocationProfile = constructor->ensureRareDataAndAllocationProfile(exec, inlineCapacity)->objectAllocationProfile();
+ Structure* structure = allocationProfile->structure();
result = constructEmptyObject(exec, structure);
if (structure->hasPolyProto()) {
- JSObject* prototype = constructor->prototypeForConstruction(vm, exec);
+ JSObject* prototype = allocationProfile->prototype();
+ ASSERT(prototype == constructor->prototypeForConstruction(vm, exec));
result->putDirect(vm, knownPolyProtoOffset, prototype);
prototype->didBecomePrototype();
ASSERT_WITH_MESSAGE(!hasIndexedProperties(result->indexingType()), "We rely on JSFinalObject not starting out with an indexing type otherwise we would potentially need to convert to slow put storage");
}
Structure* objectAllocationStructure() { return m_objectAllocationProfile.structure(); }
+ JSObject* objectAllocationPrototype() { return m_objectAllocationProfile.prototype(); }
InlineWatchpointSet& allocationProfileWatchpointSet()
{
unsigned oldOutOfLineCapacity = result->outOfLineCapacity();
result->addPropertyWithoutTransition(
- vm, vm.propertyNames->builtinNames().underscoreProtoPrivateName(), static_cast<unsigned>(PropertyAttribute::DontEnum),
+ vm, vm.propertyNames->builtinNames().polyProtoName(), static_cast<unsigned>(PropertyAttribute::DontEnum),
[&] (const GCSafeConcurrentJSLocker&, PropertyOffset offset, PropertyOffset newLastOffset) {
RELEASE_ASSERT(Structure::outOfLineCapacity(newLastOffset) == oldOutOfLineCapacity);
RELEASE_ASSERT(offset == knownPolyProtoOffset);