WebKit Bugzilla
Attachment 356953 Details for
Bug 190047
: [JSC] Optimize Object.keys by caching own keys results in StructureRareData
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Requests
|
Help
|
New Account
|
Log In
Remember
[x]
|
Forgot Password
Login:
[x]
[patch]
Patch
bug-190047-20181210201423.patch (text/plain), 54.96 KB, created by
Yusuke Suzuki
on 2018-12-10 03:14:24 PST
(
hide
)
Description:
Patch
Filename:
MIME Type:
Creator:
Yusuke Suzuki
Created:
2018-12-10 03:14:24 PST
Size:
54.96 KB
patch
obsolete
>Subversion Revision: 239024 >diff --git a/Source/JavaScriptCore/ChangeLog b/Source/JavaScriptCore/ChangeLog >index 8fef43acb605a59b1112f8305ea99033543ad43b..5524636816a1f0aca7411a65c50524e9ab6c7421 100644 >--- a/Source/JavaScriptCore/ChangeLog >+++ b/Source/JavaScriptCore/ChangeLog >@@ -1,3 +1,96 @@ >+2018-12-10 Yusuke Suzuki <yusukesuzuki@slowstart.org> >+ >+ [JSC] Optimize Object.keys by caching own keys results in StructureRareData >+ https://bugs.webkit.org/show_bug.cgi?id=190047 >+ >+ Reviewed by NOBODY (OOPS!). >+ >+ Object.keys is one of the most frequently used function in web-tooling-benchmarks (WTB). >+ Object.keys is dominant in lebab of WTB, and frequently called in babel and others. >+ Since our Structure knows the shape of JSObject, we can cache the result of Object.keys >+ in Structure (StructureRareData) as we cache JSPropertyNameEnumerator in StructureRareData. >+ >+ This patch caches the result of Object.keys in StructureRareData. The cached array is created >+ as JSImmutableButterfly. And Object.keys creates CoW from this data. Currently, the lifetime >+ strategy of this JSImmutableButterfly is the same to cached JSPropertyNameEnumerator. It is >+ referenced from Structure, and collected when Structure is collected. >+ >+ This improves several benchmarks in SixSpeed. >+ >+ baseline patched >+ >+ object-assign.es5 350.1710+-3.6303 ^ 226.0368+-4.7558 ^ definitely 1.5492x faster >+ for-of-object.es6 269.1941+-3.3430 ^ 127.9317+-2.3875 ^ definitely 2.1042x faster >+ >+ And it improves WTB lebab by 11.8%. >+ >+ Before: lebab: 6.10 runs/s >+ After: lebab: 6.82 runs/s >+ >+ * dfg/DFGAbstractInterpreterInlines.h: >+ (JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects): >+ * dfg/DFGByteCodeParser.cpp: >+ (JSC::DFG::ByteCodeParser::handleIntrinsicCall): >+ * dfg/DFGClobberize.h: >+ (JSC::DFG::clobberize): >+ * dfg/DFGConstantFoldingPhase.cpp: >+ (JSC::DFG::ConstantFoldingPhase::foldConstants): >+ * dfg/DFGDoesGC.cpp: >+ (JSC::DFG::doesGC): >+ * dfg/DFGFixupPhase.cpp: >+ (JSC::DFG::FixupPhase::fixupNode): >+ * dfg/DFGNode.cpp: >+ (JSC::DFG::Node::convertToNewArrayBuffer): >+ * dfg/DFGNode.h: >+ * dfg/DFGNodeType.h: >+ * dfg/DFGOperations.cpp: >+ * dfg/DFGOperations.h: >+ * dfg/DFGPredictionPropagationPhase.cpp: >+ * dfg/DFGSafeToExecute.h: >+ (JSC::DFG::safeToExecute): >+ * dfg/DFGSpeculativeJIT.cpp: >+ (JSC::DFG::SpeculativeJIT::compileObjectKeys): >+ * dfg/DFGSpeculativeJIT.h: >+ * dfg/DFGSpeculativeJIT32_64.cpp: >+ (JSC::DFG::SpeculativeJIT::compile): >+ * dfg/DFGSpeculativeJIT64.cpp: >+ (JSC::DFG::SpeculativeJIT::compile): >+ * ftl/FTLAbstractHeapRepository.h: >+ * ftl/FTLCapabilities.cpp: >+ (JSC::FTL::canCompile): >+ * ftl/FTLLowerDFGToB3.cpp: >+ (JSC::FTL::DFG::LowerDFGToB3::compileNode): >+ (JSC::FTL::DFG::LowerDFGToB3::compileObjectKeys): >+ * runtime/Intrinsic.cpp: >+ (JSC::intrinsicName): >+ * runtime/Intrinsic.h: >+ * runtime/JSImmutableButterfly.h: >+ (JSC::JSImmutableButterfly::createSentinel): >+ * runtime/ObjectConstructor.cpp: >+ (JSC::ownPropertyKeys): >+ * runtime/Structure.cpp: >+ (JSC::Structure::canCachePropertyNameEnumerator const): >+ * runtime/Structure.h: >+ * runtime/StructureInlines.h: >+ (JSC::Structure::setCachedOwnKeys): >+ (JSC::Structure::cachedOwnKeys const): >+ (JSC::Structure::canCacheOwnKeys const): >+ * runtime/StructureRareData.cpp: >+ (JSC::StructureRareData::visitChildren): >+ (JSC::StructureRareData::cachedPropertyNameEnumerator const): Deleted. >+ (JSC::StructureRareData::setCachedPropertyNameEnumerator): Deleted. >+ * runtime/StructureRareData.h: >+ * runtime/StructureRareDataInlines.h: >+ (JSC::StructureRareData::cachedPropertyNameEnumerator const): >+ (JSC::StructureRareData::setCachedPropertyNameEnumerator): >+ (JSC::StructureRareData::cachedOwnKeys const): >+ (JSC::StructureRareData::cachedOwnKeysConcurrently const): >+ (JSC::StructureRareData::setCachedOwnKeys): >+ (JSC::StructureRareData::previousID const): Deleted. >+ * runtime/VM.cpp: >+ (JSC::VM::VM): >+ * runtime/VM.h: >+ > 2018-12-08 Mark Lam <mark.lam@apple.com> > > Reduce size of PropertySlot and PutPropertySlot. >diff --git a/Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h b/Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h >index 25144b5b14240c7db2ce8716b79b9bfac38b547e..e5993482f6806fb2d4b4b6a3a83ab2b31cc26872 100644 >--- a/Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h >+++ b/Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h >@@ -43,6 +43,7 @@ > #include "Operations.h" > #include "PutByIdStatus.h" > #include "StringObject.h" >+#include "StructureRareDataInlines.h" > #include <wtf/BooleanLattice.h> > #include <wtf/CheckedArithmetic.h> > >@@ -2565,6 +2566,30 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi > break; > } > >+ case ObjectKeys: { >+ if (node->child1().useKind() == ObjectUse) { >+ auto& structureSet = forNode(node->child1()).m_structure; >+ if (structureSet.isFinite() && structureSet.size() == 1) { >+ RegisteredStructure structure = structureSet.onlyStructure(); >+ if (auto* rareData = structure->rareDataConcurrently()) { >+ auto* immutableButterfly = rareData->cachedOwnKeysConcurrently(); >+ if (immutableButterfly && immutableButterfly != m_vm.sentinelImmutableButterfly.get()) { >+ if (m_graph.isWatchingHavingABadTimeWatchpoint(node)) { >+ m_state.setFoundConstants(true); >+ didFoldClobberWorld(); >+ setTypeForNode(node, SpecArray); >+ break; >+ } >+ } >+ } >+ } >+ } >+ >+ clobberWorld(); >+ setTypeForNode(node, SpecArray); >+ break; >+ } >+ > case ToObject: > case CallObjectConstructor: { > AbstractValue& source = forNode(node->child1()); >diff --git a/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp b/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp >index 71fc92b19449ab2f7f7c4120f832ca2e2856ba76..e32489736d5c57283e60870ab4cc0aab29631b9f 100644 >--- a/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp >+++ b/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp >@@ -2684,6 +2684,15 @@ bool ByteCodeParser::handleIntrinsicCall(Node* callee, VirtualRegister result, I > return true; > } > >+ case ObjectKeysIntrinsic: { >+ if (argumentCountIncludingThis < 2) >+ return false; >+ >+ insertChecks(); >+ set(result, addToGraph(ObjectKeys, get(virtualRegisterForArgument(1, registerOffset)))); >+ return true; >+ } >+ > case ReflectGetPrototypeOfIntrinsic: { > if (argumentCountIncludingThis != 2) > return false; >diff --git a/Source/JavaScriptCore/dfg/DFGClobberize.h b/Source/JavaScriptCore/dfg/DFGClobberize.h >index 788eecc3c3bad1d915ba857f63a7578256705a0c..b2ebf42a7dd0cb70d0b30145e1832c930ddc9e6f 100644 >--- a/Source/JavaScriptCore/dfg/DFGClobberize.h >+++ b/Source/JavaScriptCore/dfg/DFGClobberize.h >@@ -666,6 +666,7 @@ void clobberize(Graph& graph, Node* node, const ReadFunctor& read, const WriteFu > case CreateThis: > case InstanceOf: > case StringValueOf: >+ case ObjectKeys: > read(World); > write(Heap); > return; >@@ -1527,7 +1528,6 @@ void clobberize(Graph& graph, Node* node, const ReadFunctor& read, const WriteFu > } > } > >- > case NewObject: > case NewRegexp: > case NewStringObject: >diff --git a/Source/JavaScriptCore/dfg/DFGConstantFoldingPhase.cpp b/Source/JavaScriptCore/dfg/DFGConstantFoldingPhase.cpp >index fb1f6dc231abe448d5f916c6348fd1f137fba023..03f84f55fcf51d81adce812945e158b43eb8f0b0 100644 >--- a/Source/JavaScriptCore/dfg/DFGConstantFoldingPhase.cpp >+++ b/Source/JavaScriptCore/dfg/DFGConstantFoldingPhase.cpp >@@ -766,6 +766,26 @@ class ConstantFoldingPhase : public Phase { > break; > } > >+ case ObjectKeys: { >+ if (node->child1().useKind() == ObjectUse) { >+ auto& structureSet = m_state.forNode(node->child1()).m_structure; >+ if (structureSet.isFinite() && structureSet.size() == 1) { >+ RegisteredStructure structure = structureSet.onlyStructure(); >+ if (auto* rareData = structure->rareDataConcurrently()) { >+ auto* immutableButterfly = rareData->cachedOwnKeysConcurrently(); >+ if (immutableButterfly && immutableButterfly != m_graph.m_vm.sentinelImmutableButterfly.get()) { >+ if (m_graph.isWatchingHavingABadTimeWatchpoint(node)) { >+ node->convertToNewArrayBuffer(m_graph.freeze(immutableButterfly)); >+ changed = true; >+ break; >+ } >+ } >+ } >+ } >+ } >+ break; >+ } >+ > case ToNumber: { > if (m_state.forNode(node->child1()).m_type & ~SpecBytecodeNumber) > break; >diff --git a/Source/JavaScriptCore/dfg/DFGDoesGC.cpp b/Source/JavaScriptCore/dfg/DFGDoesGC.cpp >index 6aa263cfa2a40a973ea229d83fa4215b9decabd1..43e3c869dda311903945b6e64b00363ca5d68f4e 100644 >--- a/Source/JavaScriptCore/dfg/DFGDoesGC.cpp >+++ b/Source/JavaScriptCore/dfg/DFGDoesGC.cpp >@@ -337,6 +337,7 @@ bool doesGC(Graph& graph, Node* node) > case ToThis: > case CreateThis: > case ObjectCreate: >+ case ObjectKeys: > case AllocatePropertyStorage: > case ReallocatePropertyStorage: > case Arrayify: >diff --git a/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp b/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp >index 964e5bc65998d14c5bfdfb9edf0de6dfeb7ce9b8..6ee698ce80c528e7e3291b8f69afae5d330ed498 100644 >--- a/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp >+++ b/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp >@@ -1537,6 +1537,14 @@ class FixupPhase : public Phase { > break; > } > >+ case ObjectKeys: { >+ if (node->child1()->shouldSpeculateObject()) { >+ watchHavingABadTime(node); >+ fixEdge<ObjectUse>(node->child1()); >+ } >+ break; >+ } >+ > case CheckStringIdent: { > fixEdge<StringIdentUse>(node->child1()); > break; >diff --git a/Source/JavaScriptCore/dfg/DFGNode.cpp b/Source/JavaScriptCore/dfg/DFGNode.cpp >index dd967c8ad23720c8856675873506e52d150fb771..3d1c3ba137621e3b2f03415bb91d1412363ce4f1 100644 >--- a/Source/JavaScriptCore/dfg/DFGNode.cpp >+++ b/Source/JavaScriptCore/dfg/DFGNode.cpp >@@ -31,6 +31,7 @@ > #include "DFGGraph.h" > #include "DFGPromotedHeapLocation.h" > #include "JSCInlines.h" >+#include "JSImmutableButterfly.h" > > namespace JSC { namespace DFG { > >@@ -223,6 +224,17 @@ void Node::convertToLazyJSConstant(Graph& graph, LazyJSValue value) > children.reset(); > } > >+void Node::convertToNewArrayBuffer(FrozenValue* immutableButterfly) >+{ >+ setOpAndDefaultFlags(NewArrayBuffer); >+ NewArrayBufferData data { }; >+ data.indexingMode = immutableButterfly->cast<JSImmutableButterfly*>()->indexingMode(); >+ data.vectorLengthHint = immutableButterfly->cast<JSImmutableButterfly*>()->toButterfly()->vectorLength(); >+ children.reset(); >+ m_opInfo = immutableButterfly; >+ m_opInfo2 = data.asQuadWord; >+} >+ > void Node::convertToDirectCall(FrozenValue* executable) > { > NodeType newOp = LastNodeType; >diff --git a/Source/JavaScriptCore/dfg/DFGNode.h b/Source/JavaScriptCore/dfg/DFGNode.h >index 9952806583e727dd7f583578896a4947b6837b16..bbf49f1f5544a121141ee0065410924f50adca78 100644 >--- a/Source/JavaScriptCore/dfg/DFGNode.h >+++ b/Source/JavaScriptCore/dfg/DFGNode.h >@@ -761,6 +761,8 @@ struct Node { > m_opInfo = structure; > m_opInfo2 = OpInfoWrapper(); > } >+ >+ void convertToNewArrayBuffer(FrozenValue* immutableButterfly); > > void convertToDirectCall(FrozenValue*); > >diff --git a/Source/JavaScriptCore/dfg/DFGNodeType.h b/Source/JavaScriptCore/dfg/DFGNodeType.h >index 509fcc9043a695d8f9f907f6490a53aab6c1b36f..77357f1e842136933e19bd03c47843af6015aa1f 100644 >--- a/Source/JavaScriptCore/dfg/DFGNodeType.h >+++ b/Source/JavaScriptCore/dfg/DFGNodeType.h >@@ -263,6 +263,7 @@ namespace JSC { namespace DFG { > macro(ParseInt, NodeMustGenerate | NodeResultJS) \ > macro(GetPrototypeOf, NodeMustGenerate | NodeResultJS) \ > macro(ObjectCreate, NodeMustGenerate | NodeResultJS) \ >+ macro(ObjectKeys, NodeMustGenerate | NodeResultJS) \ > \ > /* Atomics object functions. */\ > macro(AtomicsAdd, NodeResultJS | NodeMustGenerate | NodeHasVarArgs) \ >diff --git a/Source/JavaScriptCore/dfg/DFGOperations.cpp b/Source/JavaScriptCore/dfg/DFGOperations.cpp >index 3f1f494a56a300105024a00d278fdd3cad2e27bf..9555f6feb8bc7e21b42d277c4bac039d13deaa3d 100644 >--- a/Source/JavaScriptCore/dfg/DFGOperations.cpp >+++ b/Source/JavaScriptCore/dfg/DFGOperations.cpp >@@ -248,6 +248,25 @@ EncodedJSValue JIT_OPERATION operationToThisStrict(ExecState* exec, EncodedJSVal > return JSValue::encode(JSValue::decode(encodedOp).toThis(exec, StrictMode)); > } > >+JSArray* JIT_OPERATION operationObjectKeys(ExecState* exec, EncodedJSValue encodedObject) >+{ >+ VM& vm = exec->vm(); >+ NativeCallFrameTracer tracer(&vm, exec); >+ auto scope = DECLARE_THROW_SCOPE(vm); >+ >+ JSObject* object = JSValue::decode(encodedObject).toObject(exec); >+ RETURN_IF_EXCEPTION(scope, nullptr); >+ scope.release(); >+ return ownPropertyKeys(exec, object, PropertyNameMode::Strings, DontEnumPropertiesMode::Exclude); >+} >+ >+JSArray* JIT_OPERATION operationObjectKeysObject(ExecState* exec, JSObject* object) >+{ >+ VM& vm = exec->vm(); >+ NativeCallFrameTracer tracer(&vm, exec); >+ return ownPropertyKeys(exec, object, PropertyNameMode::Strings, DontEnumPropertiesMode::Exclude); >+} >+ > JSCell* JIT_OPERATION operationObjectCreate(ExecState* exec, EncodedJSValue encodedPrototype) > { > VM& vm = exec->vm(); >diff --git a/Source/JavaScriptCore/dfg/DFGOperations.h b/Source/JavaScriptCore/dfg/DFGOperations.h >index 512ac11153c8ff522cf62671551b9968df40c44e..ff5bec4d908f8d2f6f375dd83b00299182d76e41 100644 >--- a/Source/JavaScriptCore/dfg/DFGOperations.h >+++ b/Source/JavaScriptCore/dfg/DFGOperations.h >@@ -43,6 +43,8 @@ EncodedJSValue JIT_OPERATION operationStringFromCharCodeUntyped(ExecState*, Enco > // These routines provide callbacks out to C++ implementations of operations too complex to JIT. > JSCell* JIT_OPERATION operationCallObjectConstructor(ExecState*, JSGlobalObject*, EncodedJSValue encodedTarget) WTF_INTERNAL; > JSCell* JIT_OPERATION operationToObject(ExecState*, JSGlobalObject*, EncodedJSValue encodedTarget, UniquedStringImpl*) WTF_INTERNAL; >+JSArray* JIT_OPERATION operationObjectKeys(ExecState*, EncodedJSValue) WTF_INTERNAL; >+JSArray* JIT_OPERATION operationObjectKeysObject(ExecState*, JSObject*) WTF_INTERNAL; > JSCell* JIT_OPERATION operationObjectCreate(ExecState*, EncodedJSValue) WTF_INTERNAL; > JSCell* JIT_OPERATION operationObjectCreateObject(ExecState*, JSObject*) WTF_INTERNAL; > JSCell* JIT_OPERATION operationCreateThis(ExecState*, JSObject* constructor, uint32_t inlineCapacity) WTF_INTERNAL; >diff --git a/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp b/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp >index f8209fb048c0bcc1ea0cb23c2c1556e6f09a9283..43eb56a6dc22ebca7ff4e95b8cee910906dc1551 100644 >--- a/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp >+++ b/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp >@@ -968,7 +968,8 @@ class PredictionPropagationPhase : public Phase { > case NewArray: > case NewArrayWithSize: > case CreateRest: >- case NewArrayBuffer: { >+ case NewArrayBuffer: >+ case ObjectKeys: { > setPrediction(SpecArray); > break; > } >diff --git a/Source/JavaScriptCore/dfg/DFGSafeToExecute.h b/Source/JavaScriptCore/dfg/DFGSafeToExecute.h >index b43b7dfda9e779fd222eec3ef505d06fbd992d0e..0e097671a2ea0976df240ab79d1c7a8a34b5def7 100644 >--- a/Source/JavaScriptCore/dfg/DFGSafeToExecute.h >+++ b/Source/JavaScriptCore/dfg/DFGSafeToExecute.h >@@ -178,6 +178,7 @@ bool safeToExecute(AbstractStateType& state, Graph& graph, Node* node, bool igno > case ToThis: > case CreateThis: > case ObjectCreate: >+ case ObjectKeys: > case GetCallee: > case SetCallee: > case GetArgumentCountIncludingThis: >diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp >index 2cda06dd6e37771551ddeacc161c9f25e7375893..e0578e78170c762ba741faa1e4908f6fcb47764b 100644 >--- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp >+++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp >@@ -12301,6 +12301,94 @@ void SpeculativeJIT::compileToThis(Node* node) > jsValueResult(tempRegs, node); > } > >+void SpeculativeJIT::compileObjectKeys(Node* node) >+{ >+ switch (node->child1().useKind()) { >+ case ObjectUse: { >+ if (m_graph.isWatchingHavingABadTimeWatchpoint(node)) { >+ SpeculateCellOperand object(this, node->child1()); >+ GPRTemporary structure(this); >+ GPRTemporary scratch(this); >+ GPRTemporary scratch2(this); >+ GPRTemporary scratch3(this); >+ GPRTemporary result(this); >+ >+ GPRReg objectGPR = object.gpr(); >+ GPRReg structureGPR = structure.gpr(); >+ GPRReg scratchGPR = scratch.gpr(); >+ GPRReg scratch2GPR = scratch2.gpr(); >+ GPRReg scratch3GPR = scratch3.gpr(); >+ GPRReg resultGPR = result.gpr(); >+ >+ speculateObject(node->child1(), objectGPR); >+ >+ CCallHelpers::JumpList slowCases; >+ m_jit.emitLoadStructure(*m_jit.vm(), objectGPR, structureGPR, scratchGPR); >+ m_jit.loadPtr(CCallHelpers::Address(structureGPR, Structure::previousOrRareDataOffset()), scratchGPR); >+ >+ slowCases.append(m_jit.branchTestPtr(CCallHelpers::Zero, scratchGPR)); >+ slowCases.append(m_jit.branch32(CCallHelpers::Equal, CCallHelpers::Address(scratchGPR, JSCell::structureIDOffset()), TrustedImm32(bitwise_cast<int32_t>(m_jit.vm()->structureStructure->structureID())))); >+ >+ m_jit.loadPtr(CCallHelpers::Address(scratchGPR, StructureRareData::offsetOfCachedOwnKeys()), scratchGPR); >+ >+ slowCases.append(m_jit.branchTestPtr(CCallHelpers::Zero, scratchGPR)); >+ slowCases.append(m_jit.branchPtr(CCallHelpers::Equal, scratchGPR, TrustedImmPtr::weakPointer(m_jit.graph(), m_jit.vm()->sentinelImmutableButterfly.get()))); >+ >+ MacroAssembler::JumpList slowButArrayBufferCases; >+ >+ JSGlobalObject* globalObject = m_jit.graph().globalObjectFor(node->origin.semantic); >+ RegisteredStructure arrayStructure = m_jit.graph().registerStructure(globalObject->arrayStructureForIndexingTypeDuringAllocation(CopyOnWriteArrayWithContiguous)); >+ >+ m_jit.move(scratchGPR, scratch3GPR); >+ m_jit.addPtr(TrustedImmPtr(JSImmutableButterfly::offsetOfData()), scratchGPR); >+ >+ emitAllocateJSObject<JSArray>(resultGPR, TrustedImmPtr(arrayStructure), scratchGPR, structureGPR, scratch2GPR, slowButArrayBufferCases); >+ >+ addSlowPathGenerator(slowPathCall(slowButArrayBufferCases, this, operationNewArrayBuffer, resultGPR, arrayStructure, scratch3GPR)); >+ >+ addSlowPathGenerator(slowPathCall(slowCases, this, operationObjectKeysObject, resultGPR, objectGPR)); >+ >+ cellResult(resultGPR, node); >+ break; >+ } >+ >+ SpeculateCellOperand object(this, node->child1()); >+ >+ GPRReg objectGPR = object.gpr(); >+ >+ speculateObject(node->child1(), objectGPR); >+ >+ flushRegisters(); >+ GPRFlushedCallResult result(this); >+ GPRReg resultGPR = result.gpr(); >+ callOperation(operationObjectKeysObject, resultGPR, objectGPR); >+ m_jit.exceptionCheck(); >+ >+ cellResult(resultGPR, node); >+ break; >+ } >+ >+ case UntypedUse: { >+ JSValueOperand object(this, node->child1()); >+ >+ JSValueRegs objectRegs = object.jsValueRegs(); >+ >+ flushRegisters(); >+ GPRFlushedCallResult result(this); >+ GPRReg resultGPR = result.gpr(); >+ callOperation(operationObjectKeys, resultGPR, objectRegs); >+ m_jit.exceptionCheck(); >+ >+ cellResult(resultGPR, node); >+ break; >+ } >+ >+ default: >+ RELEASE_ASSERT_NOT_REACHED(); >+ break; >+ } >+} >+ > void SpeculativeJIT::compileObjectCreate(Node* node) > { > switch (node->child1().useKind()) { >diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h >index adb3ba1c8e17c0a435c772f0fb40c3837074571f..983636ff214550ffb0850eb62fc2db90a21c1e55 100644 >--- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h >+++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h >@@ -1476,6 +1476,7 @@ class SpeculativeJIT { > void compileNewArrayWithSize(Node*); > void compileNewTypedArray(Node*); > void compileToThis(Node*); >+ void compileObjectKeys(Node*); > void compileObjectCreate(Node*); > void compileCreateThis(Node*); > void compileNewObject(Node*); >diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp >index ce3e26635248959b19df566c8ee125838d758d5a..828a40e7acc64d9cc76edb55c1ee51ab9ef112dd 100644 >--- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp >+++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp >@@ -3153,6 +3153,11 @@ void SpeculativeJIT::compile(Node* node) > break; > } > >+ case ObjectKeys: { >+ compileObjectKeys(node); >+ break; >+ } >+ > case CreateThis: { > compileCreateThis(node); > break; >diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp >index fb161a72b54074f15abadbfc755356d20380558e..822cbfe26061f30387bfc69a6840cde6192d994a 100644 >--- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp >+++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp >@@ -3395,6 +3395,11 @@ void SpeculativeJIT::compile(Node* node) > break; > } > >+ case ObjectKeys: { >+ compileObjectKeys(node); >+ break; >+ } >+ > case CreateThis: { > compileCreateThis(node); > break; >diff --git a/Source/JavaScriptCore/ftl/FTLAbstractHeapRepository.h b/Source/JavaScriptCore/ftl/FTLAbstractHeapRepository.h >index 3ffca17049671cfc6e4817f1ea56ed6181d1a7d3..5843fb3d4962ff01f04bd3b4710656be5ef45b0b 100644 >--- a/Source/JavaScriptCore/ftl/FTLAbstractHeapRepository.h >+++ b/Source/JavaScriptCore/ftl/FTLAbstractHeapRepository.h >@@ -117,8 +117,10 @@ namespace JSC { namespace FTL { > macro(Structure_globalObject, Structure::globalObjectOffset()) \ > macro(Structure_indexingModeIncludingHistory, Structure::indexingModeIncludingHistoryOffset()) \ > macro(Structure_inlineCapacity, Structure::inlineCapacityOffset()) \ >+ macro(Structure_previousOrRareData, Structure::previousOrRareDataOffset()) \ > macro(Structure_prototype, Structure::prototypeOffset()) \ > macro(Structure_structureID, Structure::structureIDOffset()) \ >+ macro(StructureRareData_cachedOwnKeys, StructureRareData::offsetOfCachedOwnKeys()) \ > macro(HashMapImpl_capacity, HashMapImpl<HashMapBucket<HashMapBucketDataKey>>::offsetOfCapacity()) \ > macro(HashMapImpl_buffer, HashMapImpl<HashMapBucket<HashMapBucketDataKey>>::offsetOfBuffer()) \ > macro(HashMapImpl_head, HashMapImpl<HashMapBucket<HashMapBucketDataKey>>::offsetOfHead()) \ >diff --git a/Source/JavaScriptCore/ftl/FTLCapabilities.cpp b/Source/JavaScriptCore/ftl/FTLCapabilities.cpp >index da113812e17e1d0b044ad28c86ab8f8e74b78b9f..0f7e0a6e03ba9558f11acecc0f00bd415e369db9 100644 >--- a/Source/JavaScriptCore/ftl/FTLCapabilities.cpp >+++ b/Source/JavaScriptCore/ftl/FTLCapabilities.cpp >@@ -198,6 +198,7 @@ inline CapabilityLevel canCompile(Node* node) > case CallObjectConstructor: > case CallStringConstructor: > case ObjectCreate: >+ case ObjectKeys: > case MakeRope: > case NewArrayWithSize: > case TryGetById: >diff --git a/Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp b/Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp >index fb0439732c43a4b3d4cb5afa3a5deca3910ea945..3c111883e72810df485dc596bcd1dec7b2f46d55 100644 >--- a/Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp >+++ b/Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp >@@ -860,6 +860,9 @@ class LowerDFGToB3 { > case ObjectCreate: > compileObjectCreate(); > break; >+ case ObjectKeys: >+ compileObjectKeys(); >+ break; > case NewObject: > compileNewObject(); > break; >@@ -5470,6 +5473,75 @@ class LowerDFGToB3 { > setInt32(m_out.phi(Int32, zeroLengthResult, nonZeroLengthResult)); > } > >+ void compileObjectKeys() >+ { >+ switch (m_node->child1().useKind()) { >+ case ObjectUse: { >+ if (m_graph.isWatchingHavingABadTimeWatchpoint(m_node)) { >+ LBasicBlock notNullCase = m_out.newBlock(); >+ LBasicBlock rareDataCase = m_out.newBlock(); >+ LBasicBlock notNullCacheCase = m_out.newBlock(); >+ LBasicBlock useCacheCase = m_out.newBlock(); >+ LBasicBlock slowButArrayBufferCase = m_out.newBlock(); >+ LBasicBlock slowCase = m_out.newBlock(); >+ LBasicBlock continuation = m_out.newBlock(); >+ >+ LValue object = lowObject(m_node->child1()); >+ LValue structure = loadStructure(object); >+ LValue previousOrRareData = m_out.loadPtr(structure, m_heaps.Structure_previousOrRareData); >+ m_out.branch(m_out.notNull(previousOrRareData), unsure(notNullCase), unsure(slowCase)); >+ >+ LBasicBlock lastNext = m_out.appendTo(notNullCase, rareDataCase); >+ m_out.branch( >+ m_out.notEqual(m_out.load32(previousOrRareData, m_heaps.JSCell_structureID), m_out.constInt32(m_graph.m_vm.structureStructure->structureID())), >+ unsure(rareDataCase), unsure(slowCase)); >+ >+ m_out.appendTo(rareDataCase, notNullCacheCase); >+ LValue cachedOwnKeys = m_out.loadPtr(previousOrRareData, m_heaps.StructureRareData_cachedOwnKeys); >+ m_out.branch(m_out.notNull(cachedOwnKeys), unsure(notNullCacheCase), unsure(slowCase)); >+ >+ m_out.appendTo(notNullCacheCase, useCacheCase); >+ m_out.branch(m_out.notEqual(cachedOwnKeys, weakPointer(m_graph.m_vm.sentinelImmutableButterfly.get())), unsure(useCacheCase), unsure(slowCase)); >+ >+ m_out.appendTo(useCacheCase, slowButArrayBufferCase); >+ JSGlobalObject* globalObject = m_graph.globalObjectFor(m_node->origin.semantic); >+ RegisteredStructure arrayStructure = m_graph.registerStructure(globalObject->arrayStructureForIndexingTypeDuringAllocation(CopyOnWriteArrayWithContiguous)); >+ LValue fastArray = allocateObject<JSArray>(arrayStructure, m_out.addPtr(cachedOwnKeys, JSImmutableButterfly::offsetOfData()), slowButArrayBufferCase); >+ ValueFromBlock fastResult = m_out.anchor(fastArray); >+ m_out.jump(continuation); >+ >+ m_out.appendTo(slowButArrayBufferCase, slowCase); >+ LValue slowArray = vmCall(Int64, m_out.operation(operationNewArrayBuffer), m_callFrame, weakStructure(arrayStructure), cachedOwnKeys); >+ ValueFromBlock slowButArrayBufferResult = m_out.anchor(slowArray); >+ m_out.jump(continuation); >+ >+ m_out.appendTo(slowCase, continuation); >+ VM& vm = this->vm(); >+ LValue slowResultValue = lazySlowPath( >+ [=, &vm] (const Vector<Location>& locations) -> RefPtr<LazySlowPath::Generator> { >+ return createLazyCallGenerator(vm, >+ operationObjectKeysObject, locations[0].directGPR(), locations[1].directGPR()); >+ }, >+ object); >+ ValueFromBlock slowResult = m_out.anchor(slowResultValue); >+ m_out.jump(continuation); >+ >+ m_out.appendTo(continuation, lastNext); >+ setJSValue(m_out.phi(pointerType(), fastResult, slowButArrayBufferResult, slowResult)); >+ break; >+ } >+ setJSValue(vmCall(Int64, m_out.operation(operationObjectKeysObject), m_callFrame, lowObject(m_node->child1()))); >+ break; >+ } >+ case UntypedUse: >+ setJSValue(vmCall(Int64, m_out.operation(operationObjectKeys), m_callFrame, lowJSValue(m_node->child1()))); >+ break; >+ default: >+ RELEASE_ASSERT_NOT_REACHED(); >+ break; >+ } >+ } >+ > void compileObjectCreate() > { > switch (m_node->child1().useKind()) { >diff --git a/Source/JavaScriptCore/runtime/Intrinsic.cpp b/Source/JavaScriptCore/runtime/Intrinsic.cpp >index abe6f17286e3be391e0c374c1f6c574c5bd867fb..fbc4ef3cd8f2ec62da9300da271b1023397434fc 100644 >--- a/Source/JavaScriptCore/runtime/Intrinsic.cpp >+++ b/Source/JavaScriptCore/runtime/Intrinsic.cpp >@@ -119,6 +119,8 @@ const char* intrinsicName(Intrinsic intrinsic) > return "ObjectGetPrototypeOfIntrinsic"; > case ObjectIsIntrinsic: > return "ObjectIsIntrinsic"; >+ case ObjectKeysIntrinsic: >+ return "ObjectKeysIntrinsic"; > case ReflectGetPrototypeOfIntrinsic: > return "ReflectGetPrototypeOfIntrinsic"; > case StringPrototypeValueOfIntrinsic: >diff --git a/Source/JavaScriptCore/runtime/Intrinsic.h b/Source/JavaScriptCore/runtime/Intrinsic.h >index cfbd18496352bb9d4e26a02c5693b8c460019025..b47f46d9e4de0013de8346e82e6816f5fd02f746 100644 >--- a/Source/JavaScriptCore/runtime/Intrinsic.h >+++ b/Source/JavaScriptCore/runtime/Intrinsic.h >@@ -72,6 +72,7 @@ enum Intrinsic { > ObjectCreateIntrinsic, > ObjectGetPrototypeOfIntrinsic, > ObjectIsIntrinsic, >+ ObjectKeysIntrinsic, > ReflectGetPrototypeOfIntrinsic, > StringPrototypeValueOfIntrinsic, > StringPrototypeReplaceIntrinsic, >diff --git a/Source/JavaScriptCore/runtime/JSImmutableButterfly.h b/Source/JavaScriptCore/runtime/JSImmutableButterfly.h >index 284783c0c75596adda5a1f98f36b069e0eb8e4a1..1ff4d276775328bd0c6ca2e4146703827515e8bc 100644 >--- a/Source/JavaScriptCore/runtime/JSImmutableButterfly.h >+++ b/Source/JavaScriptCore/runtime/JSImmutableButterfly.h >@@ -67,6 +67,11 @@ class JSImmutableButterfly : public JSCell { > return array; > } > >+ static JSImmutableButterfly* createSentinel(VM& vm) >+ { >+ return create(vm, CopyOnWriteArrayWithContiguous, 0); >+ } >+ > unsigned publicLength() const { return m_header.publicLength(); } > unsigned vectorLength() const { return m_header.vectorLength(); } > unsigned length() const { return m_header.publicLength(); } >diff --git a/Source/JavaScriptCore/runtime/ObjectConstructor.cpp b/Source/JavaScriptCore/runtime/ObjectConstructor.cpp >index 90a36a935578f2b8e313113d29d90ba9d466a945..fcf99b8ecd295f18e44a230bf30464418bd14525 100644 >--- a/Source/JavaScriptCore/runtime/ObjectConstructor.cpp >+++ b/Source/JavaScriptCore/runtime/ObjectConstructor.cpp >@@ -30,6 +30,7 @@ > #include "JSFunction.h" > #include "JSGlobalObject.h" > #include "JSGlobalObjectFunctions.h" >+#include "JSImmutableButterfly.h" > #include "Lookup.h" > #include "ObjectPrototype.h" > #include "PropertyDescriptor.h" >@@ -73,7 +74,7 @@ const ClassInfo ObjectConstructor::s_info = { "Function", &InternalFunction::s_i > getOwnPropertyDescriptors objectConstructorGetOwnPropertyDescriptors DontEnum|Function 1 > getOwnPropertyNames objectConstructorGetOwnPropertyNames DontEnum|Function 1 > getOwnPropertySymbols objectConstructorGetOwnPropertySymbols DontEnum|Function 1 >- keys objectConstructorKeys DontEnum|Function 1 >+ keys objectConstructorKeys DontEnum|Function 1 ObjectKeysIntrinsic > defineProperty objectConstructorDefineProperty DontEnum|Function 3 > defineProperties objectConstructorDefineProperties DontEnum|Function 2 > create objectConstructorCreate DontEnum|Function 2 ObjectCreateIntrinsic >@@ -271,7 +272,6 @@ EncodedJSValue JSC_HOST_CALL objectConstructorGetOwnPropertySymbols(ExecState* e > RELEASE_AND_RETURN(scope, JSValue::encode(ownPropertyKeys(exec, object, PropertyNameMode::Symbols, DontEnumPropertiesMode::Include))); > } > >-// FIXME: Use the enumeration cache. > EncodedJSValue JSC_HOST_CALL objectConstructorKeys(ExecState* exec) > { > VM& vm = exec->vm(); >@@ -892,11 +892,25 @@ EncodedJSValue JSC_HOST_CALL objectConstructorIs(ExecState* exec) > return JSValue::encode(jsBoolean(sameValue(exec, exec->argument(0), exec->argument(1)))); > } > >-// FIXME: Use the enumeration cache. > JSArray* ownPropertyKeys(ExecState* exec, JSObject* object, PropertyNameMode propertyNameMode, DontEnumPropertiesMode dontEnumPropertiesMode) > { > VM& vm = exec->vm(); > auto scope = DECLARE_THROW_SCOPE(vm); >+ >+ auto* globalObject = exec->lexicalGlobalObject(); >+ bool isObjectKeys = propertyNameMode == PropertyNameMode::Strings && dontEnumPropertiesMode == DontEnumPropertiesMode::Exclude; >+ // We attempt to look up own property keys cache in Object.keys case. >+ if (isObjectKeys) { >+ if (LIKELY(!globalObject->isHavingABadTime())) { >+ if (auto* immutableButterfly = object->structure(vm)->cachedOwnKeys()) { >+ if (immutableButterfly != vm.sentinelImmutableButterfly.get()) { >+ Structure* arrayStructure = globalObject->originalArrayStructureForIndexingType(immutableButterfly->indexingMode()); >+ return JSArray::createWithButterfly(vm, nullptr, arrayStructure, immutableButterfly->toButterfly()); >+ } >+ } >+ } >+ } >+ > PropertyNameArray properties(&vm, propertyNameMode, PrivateSymbolMode::Exclude); > object->methodTable(vm)->getOwnPropertyNames(object, exec, properties, EnumerationMode(dontEnumPropertiesMode)); > RETURN_IF_EXCEPTION(scope, nullptr); >@@ -918,8 +932,31 @@ JSArray* ownPropertyKeys(ExecState* exec, JSObject* object, PropertyNameMode pro > if (propertyNameMode != PropertyNameMode::StringsAndSymbols) { > ASSERT(propertyNameMode == PropertyNameMode::Strings || propertyNameMode == PropertyNameMode::Symbols); > if (!mustFilterProperty && properties.size() < MIN_SPARSE_ARRAY_INDEX) { >- auto* globalObject = exec->lexicalGlobalObject(); > if (LIKELY(!globalObject->isHavingABadTime())) { >+ if (isObjectKeys) { >+ Structure* structure = object->structure(vm); >+ if (structure->canCacheOwnKeys()) { >+ auto* cachedButterfly = structure->cachedOwnKeys(); >+ if (cachedButterfly == vm.sentinelImmutableButterfly.get()) { >+ // Cache the immutable butterfly! >+ size_t numProperties = properties.size(); >+ auto* newButterfly = JSImmutableButterfly::create(vm, CopyOnWriteArrayWithContiguous, numProperties); >+ for (size_t i = 0; i < numProperties; i++) { >+ const auto& identifier = properties[i]; >+ ASSERT(!identifier.isSymbol()); >+ newButterfly->setIndex(vm, i, jsOwnedString(&vm, identifier.string())); >+ } >+ >+ structure->setCachedOwnKeys(vm, newButterfly); >+ Structure* arrayStructure = globalObject->originalArrayStructureForIndexingType(newButterfly->indexingMode()); >+ return JSArray::createWithButterfly(vm, nullptr, arrayStructure, newButterfly->toButterfly()); >+ } >+ >+ if (cachedButterfly == nullptr) >+ structure->setCachedOwnKeys(vm, jsCast<JSImmutableButterfly*>(vm.sentinelImmutableButterfly.get())); >+ } >+ } >+ > size_t numProperties = properties.size(); > JSArray* keys = JSArray::create(vm, globalObject->originalArrayStructureForIndexingType(ArrayWithContiguous), numProperties); > WriteBarrier<Unknown>* buffer = keys->butterfly()->contiguous().data(); >diff --git a/Source/JavaScriptCore/runtime/Structure.cpp b/Source/JavaScriptCore/runtime/Structure.cpp >index 58d83bce4afdade803d384ba710e0ab9d6ad0918..3542827de3a270454ff6c63402682fc0582348b1 100644 >--- a/Source/JavaScriptCore/runtime/Structure.cpp >+++ b/Source/JavaScriptCore/runtime/Structure.cpp >@@ -1252,17 +1252,7 @@ JSPropertyNameEnumerator* Structure::cachedPropertyNameEnumerator() const > > bool Structure::canCachePropertyNameEnumerator() const > { >- auto canCache = [] (const Structure* structure) { >- if (structure->isDictionary()) >- return false; >- if (hasIndexedProperties(structure->indexingType())) >- return false; >- if (structure->typeInfo().overridesGetPropertyNames()) >- return false; >- return true; >- }; >- >- if (!canCache(this)) >+ if (!this->canCacheOwnKeys()) > return false; > > StructureChain* structureChain = m_cachedPrototypeChain.get(); >@@ -1271,7 +1261,7 @@ bool Structure::canCachePropertyNameEnumerator() const > while (true) { > if (!structure->get()) > return true; >- if (!canCache(structure->get())) >+ if (!structure->get()->canCacheOwnKeys()) > return false; > structure++; > } >diff --git a/Source/JavaScriptCore/runtime/Structure.h b/Source/JavaScriptCore/runtime/Structure.h >index 8ded825bfba93a85090b6148c0f50da19dfac9b7..1e84c4d19b415db346d52bf5e5e57a1b752b09d7 100644 >--- a/Source/JavaScriptCore/runtime/Structure.h >+++ b/Source/JavaScriptCore/runtime/Structure.h >@@ -38,7 +38,6 @@ > #include "PutPropertySlot.h" > #include "StructureIDBlob.h" > #include "StructureRareData.h" >-#include "StructureRareDataInlines.h" > #include "StructureTransitionTable.h" > #include "JSTypeInfo.h" > #include "Watchpoint.h" >@@ -326,6 +325,15 @@ class Structure final : public JSCell { > return static_cast<const StructureRareData*>(m_previousOrRareData.get()); > } > >+ const StructureRareData* rareDataConcurrently() const >+ { >+ JSCell* cell = m_previousOrRareData.get(); >+ WTF::loadLoadFence(); >+ if (isRareData(cell)) >+ return static_cast<StructureRareData*>(cell); >+ return nullptr; >+ } >+ > StructureRareData* ensureRareData(VM& vm) > { > if (!hasRareData()) >@@ -472,6 +480,10 @@ class Structure final : public JSCell { > bool canCachePropertyNameEnumerator() const; > bool canAccessPropertiesQuicklyForEnumeration() const; > >+ void setCachedOwnKeys(VM&, JSImmutableButterfly*); >+ JSImmutableButterfly* cachedOwnKeys() const; >+ bool canCacheOwnKeys() const; >+ > void getPropertyNamesFromStructure(VM&, PropertyNameArray&, EnumerationMode); > > JSString* objectToStringValue() >@@ -520,6 +532,11 @@ class Structure final : public JSCell { > return OBJECT_OFFSETOF(Structure, m_inlineCapacity); > } > >+ static ptrdiff_t previousOrRareDataOffset() >+ { >+ return OBJECT_OFFSETOF(Structure, m_previousOrRareData); >+ } >+ > static Structure* createStructure(VM&); > > bool transitionWatchpointSetHasBeenInvalidated() const >diff --git a/Source/JavaScriptCore/runtime/StructureInlines.h b/Source/JavaScriptCore/runtime/StructureInlines.h >index b3eb1b41e9f1c781e96f0229051000e042705d5e..ab7e206dde5ece4f70bac7a4e930ee58f5575e39 100644 >--- a/Source/JavaScriptCore/runtime/StructureInlines.h >+++ b/Source/JavaScriptCore/runtime/StructureInlines.h >@@ -219,6 +219,29 @@ inline bool Structure::transitivelyTransitionedFrom(Structure* structureToFind) > return false; > } > >+inline void Structure::setCachedOwnKeys(VM& vm, JSImmutableButterfly* ownKeys) >+{ >+ ensureRareData(vm)->setCachedOwnKeys(vm, ownKeys); >+} >+ >+inline JSImmutableButterfly* Structure::cachedOwnKeys() const >+{ >+ if (!hasRareData()) >+ return nullptr; >+ return rareData()->cachedOwnKeys(); >+} >+ >+inline bool Structure::canCacheOwnKeys() const >+{ >+ if (isDictionary()) >+ return false; >+ if (hasIndexedProperties(indexingType())) >+ return false; >+ if (typeInfo().overridesGetPropertyNames()) >+ return false; >+ return true; >+} >+ > ALWAYS_INLINE JSValue prototypeForLookupPrimitiveImpl(JSGlobalObject* globalObject, const Structure* structure) > { > ASSERT(!structure->isObject()); >diff --git a/Source/JavaScriptCore/runtime/StructureRareData.cpp b/Source/JavaScriptCore/runtime/StructureRareData.cpp >index 8984ce91956a81d51ad0d5233630e699e36deb53..d8b2f00fa2139180521f11a06c112429b70be755 100644 >--- a/Source/JavaScriptCore/runtime/StructureRareData.cpp >+++ b/Source/JavaScriptCore/runtime/StructureRareData.cpp >@@ -27,6 +27,7 @@ > #include "StructureRareData.h" > > #include "AdaptiveInferredPropertyValueWatchpointBase.h" >+#include "JSImmutableButterfly.h" > #include "JSPropertyNameEnumerator.h" > #include "JSString.h" > #include "JSCInlines.h" >@@ -70,16 +71,7 @@ void StructureRareData::visitChildren(JSCell* cell, SlotVisitor& visitor) > visitor.append(thisObject->m_previous); > visitor.append(thisObject->m_objectToStringValue); > visitor.append(thisObject->m_cachedPropertyNameEnumerator); >-} >- >-JSPropertyNameEnumerator* StructureRareData::cachedPropertyNameEnumerator() const >-{ >- return m_cachedPropertyNameEnumerator.get(); >-} >- >-void StructureRareData::setCachedPropertyNameEnumerator(VM& vm, JSPropertyNameEnumerator* enumerator) >-{ >- m_cachedPropertyNameEnumerator.set(vm, this, enumerator); >+ visitor.append(thisObject->m_cachedOwnKeys); > } > > // ----------- Object.prototype.toString() helper watchpoint classes ----------- >diff --git a/Source/JavaScriptCore/runtime/StructureRareData.h b/Source/JavaScriptCore/runtime/StructureRareData.h >index 11c181dd61e7eb6754eba97054bf12a503fb88a7..f2d2af3fad579d6705ecba55ff464053634e3a56 100644 >--- a/Source/JavaScriptCore/runtime/StructureRareData.h >+++ b/Source/JavaScriptCore/runtime/StructureRareData.h >@@ -58,7 +58,10 @@ class StructureRareData final : public JSCell { > > static Structure* createStructure(VM&, JSGlobalObject*, JSValue prototype); > >- Structure* previousID() const; >+ Structure* previousID() const >+ { >+ return m_previous.get(); >+ } > void setPreviousID(VM&, Structure*); > void clearPreviousID(); > >@@ -68,11 +71,20 @@ class StructureRareData final : public JSCell { > JSPropertyNameEnumerator* cachedPropertyNameEnumerator() const; > void setCachedPropertyNameEnumerator(VM&, JSPropertyNameEnumerator*); > >+ JSImmutableButterfly* cachedOwnKeys() const; >+ JSImmutableButterfly* cachedOwnKeysConcurrently() const; >+ void setCachedOwnKeys(VM&, JSImmutableButterfly*); >+ > Box<InlineWatchpointSet> copySharedPolyProtoWatchpoint() const { return m_polyProtoWatchpoint; } > const Box<InlineWatchpointSet>& sharedPolyProtoWatchpoint() const { return m_polyProtoWatchpoint; } > void setSharedPolyProtoWatchpoint(Box<InlineWatchpointSet>&& sharedPolyProtoWatchpoint) { m_polyProtoWatchpoint = WTFMove(sharedPolyProtoWatchpoint); } > bool hasSharedPolyProtoWatchpoint() const { return static_cast<bool>(m_polyProtoWatchpoint); } > >+ static ptrdiff_t offsetOfCachedOwnKeys() >+ { >+ return OBJECT_OFFSETOF(StructureRareData, m_cachedOwnKeys); >+ } >+ > DECLARE_EXPORT_INFO; > > private: >@@ -87,6 +99,7 @@ class StructureRareData final : public JSCell { > WriteBarrier<Structure> m_previous; > WriteBarrier<JSString> m_objectToStringValue; > WriteBarrier<JSPropertyNameEnumerator> m_cachedPropertyNameEnumerator; >+ WriteBarrier<JSImmutableButterfly> m_cachedOwnKeys; > > typedef HashMap<PropertyOffset, RefPtr<WatchpointSet>, WTF::IntHash<PropertyOffset>, WTF::UnsignedWithZeroKeyHashTraits<PropertyOffset>> PropertyWatchpointMap; > std::unique_ptr<PropertyWatchpointMap> m_replacementWatchpointSets; >diff --git a/Source/JavaScriptCore/runtime/StructureRareDataInlines.h b/Source/JavaScriptCore/runtime/StructureRareDataInlines.h >index e4e2496a5624ddf7d997cc2032f09ea2501dba9d..896f59a59e35b6aae0658d7e01b93f2ccee83222 100644 >--- a/Source/JavaScriptCore/runtime/StructureRareDataInlines.h >+++ b/Source/JavaScriptCore/runtime/StructureRareDataInlines.h >@@ -25,16 +25,13 @@ > > #pragma once > >+#include "JSImmutableButterfly.h" >+#include "JSPropertyNameEnumerator.h" > #include "JSString.h" > #include "StructureRareData.h" > > namespace JSC { > >-inline Structure* StructureRareData::previousID() const >-{ >- return m_previous.get(); >-} >- > inline void StructureRareData::setPreviousID(VM& vm, Structure* structure) > { > m_previous.set(vm, this, structure); >@@ -50,4 +47,32 @@ inline JSString* StructureRareData::objectToStringValue() const > return m_objectToStringValue.get(); > } > >+inline JSPropertyNameEnumerator* StructureRareData::cachedPropertyNameEnumerator() const >+{ >+ return m_cachedPropertyNameEnumerator.get(); >+} >+ >+inline void StructureRareData::setCachedPropertyNameEnumerator(VM& vm, JSPropertyNameEnumerator* enumerator) >+{ >+ m_cachedPropertyNameEnumerator.set(vm, this, enumerator); >+} >+ >+inline JSImmutableButterfly* StructureRareData::cachedOwnKeys() const >+{ >+ return m_cachedOwnKeys.get(); >+} >+ >+inline JSImmutableButterfly* StructureRareData::cachedOwnKeysConcurrently() const >+{ >+ auto* result = m_cachedOwnKeys.get(); >+ WTF::loadLoadFence(); >+ return result; >+} >+ >+inline void StructureRareData::setCachedOwnKeys(VM& vm, JSImmutableButterfly* butterfly) >+{ >+ WTF::storeStoreFence(); >+ m_cachedOwnKeys.set(vm, this, butterfly); >+} >+ > } // namespace JSC >diff --git a/Source/JavaScriptCore/runtime/VM.cpp b/Source/JavaScriptCore/runtime/VM.cpp >index 256a583671775a41e6e3a03ddcfde0412da4d04a..a774fa5f76e05a5deb78937b91dd6034ae105aee 100644 >--- a/Source/JavaScriptCore/runtime/VM.cpp >+++ b/Source/JavaScriptCore/runtime/VM.cpp >@@ -434,6 +434,7 @@ VM::VM(VMType vmType, HeapType heapType) > exceptionStructure.set(*this, Exception::createStructure(*this, 0, jsNull())); > promiseDeferredStructure.set(*this, JSPromiseDeferred::createStructure(*this, 0, jsNull())); > internalPromiseDeferredStructure.set(*this, JSInternalPromiseDeferred::createStructure(*this, 0, jsNull())); >+ nativeStdFunctionCellStructure.set(*this, NativeStdFunctionCell::createStructure(*this, 0, jsNull())); > programCodeBlockStructure.set(*this, ProgramCodeBlock::createStructure(*this, 0, jsNull())); > moduleProgramCodeBlockStructure.set(*this, ModuleProgramCodeBlock::createStructure(*this, 0, jsNull())); > evalCodeBlockStructure.set(*this, EvalCodeBlock::createStructure(*this, 0, jsNull())); >@@ -447,8 +448,8 @@ VM::VM(VMType vmType, HeapType heapType) > > sentinelSetBucket.set(*this, JSSet::BucketType::createSentinel(*this)); > sentinelMapBucket.set(*this, JSMap::BucketType::createSentinel(*this)); >+ sentinelImmutableButterfly.set(*this, JSImmutableButterfly::createSentinel(*this)); > >- nativeStdFunctionCellStructure.set(*this, NativeStdFunctionCell::createStructure(*this, 0, jsNull())); > smallStrings.initializeCommonStrings(*this); > > Thread::current().setCurrentAtomicStringTable(existingEntryAtomicStringTable); >diff --git a/Source/JavaScriptCore/runtime/VM.h b/Source/JavaScriptCore/runtime/VM.h >index e1ef4687840f92bd8029e090f2e51bbb2bc368bf..0fb7b8215c9d7fc4fad5799649ac5c4086e4fe0a 100644 >--- a/Source/JavaScriptCore/runtime/VM.h >+++ b/Source/JavaScriptCore/runtime/VM.h >@@ -571,6 +571,7 @@ class VM : public ThreadSafeRefCounted<VM>, public DoublyLinkedListNode<VM> { > Strong<JSCell> emptyPropertyNameEnumerator; > Strong<JSCell> sentinelSetBucket; > Strong<JSCell> sentinelMapBucket; >+ Strong<JSCell> sentinelImmutableButterfly; > > std::unique_ptr<PromiseDeferredTimer> promiseDeferredTimer; > >diff --git a/JSTests/ChangeLog b/JSTests/ChangeLog >index fcf0b8e7f3e55b6785368133f1d167f739d458e5..6f814f46a06c6cc3fe0aecdf302f4563b76d3312 100644 >--- a/JSTests/ChangeLog >+++ b/JSTests/ChangeLog >@@ -1,3 +1,30 @@ >+2018-12-10 Yusuke Suzuki <yusukesuzuki@slowstart.org> >+ >+ [JSC] Optimize Object.keys by caching own keys results in StructureRareData >+ https://bugs.webkit.org/show_bug.cgi?id=190047 >+ >+ Reviewed by NOBODY (OOPS!). >+ >+ * stress/object-keys-cached-zero.js: Added. >+ (shouldBe): >+ (test): >+ * stress/object-keys-changed-attribute.js: Added. >+ (shouldBe): >+ (test): >+ * stress/object-keys-changed-index.js: Added. >+ (shouldBe): >+ (test): >+ * stress/object-keys-changed.js: Added. >+ (shouldBe): >+ (test): >+ * stress/object-keys-indexed-non-cache.js: Added. >+ (shouldBe): >+ (test): >+ * stress/object-keys-overrides-get-property-names.js: Added. >+ (shouldBe): >+ (test): >+ (noInline): >+ > 2018-12-06 Keith Miller <keith_miller@apple.com> > > stress/big-wasm-memory tests failing on 32-bit JSC bot >diff --git a/JSTests/stress/object-keys-cached-zero.js b/JSTests/stress/object-keys-cached-zero.js >new file mode 100644 >index 0000000000000000000000000000000000000000..f7cc96afacfa848b1030d631b6539157d707b965 >--- /dev/null >+++ b/JSTests/stress/object-keys-cached-zero.js >@@ -0,0 +1,21 @@ >+function shouldBe(actual, expected) >+{ >+ if (actual !== expected) >+ throw new Error('bad value: ' + actual); >+} >+ >+function test(object) >+{ >+ return Object.keys(object); >+} >+noInline(test); >+ >+var object = {}; >+for (var i = 0; i < 1e6; ++i) { >+ var result = test(object); >+ shouldBe(result.length, 0); >+ shouldBe(result[0], undefined); >+ result[0] = i; >+ shouldBe(result.length, 1); >+ shouldBe(result[0], i); >+} >diff --git a/JSTests/stress/object-keys-changed-attribute.js b/JSTests/stress/object-keys-changed-attribute.js >new file mode 100644 >index 0000000000000000000000000000000000000000..c3f143ec27f002059ae4172569f9b743c13bd1a6 >--- /dev/null >+++ b/JSTests/stress/object-keys-changed-attribute.js >@@ -0,0 +1,28 @@ >+function shouldBe(actual, expected) >+{ >+ if (actual !== expected) >+ throw new Error('bad value: ' + actual); >+} >+ >+function test(object) >+{ >+ return Object.keys(object); >+} >+noInline(test); >+ >+var object = { Cocoa: 42 }; >+for (var i = 0; i < 1e6; ++i) { >+ var result = test(object); >+ shouldBe(result.length, 1); >+ shouldBe(result[0], 'Cocoa'); >+} >+ >+Reflect.defineProperty(object, 'Cocoa', { >+ enumerable: false >+}); >+ >+for (var i = 0; i < 1e6; ++i) { >+ var result = test(object); >+ shouldBe(result.length, 0); >+ shouldBe(result[0], undefined); >+} >diff --git a/JSTests/stress/object-keys-changed-index.js b/JSTests/stress/object-keys-changed-index.js >new file mode 100644 >index 0000000000000000000000000000000000000000..423e09257b29da454bdf6f3fd73ba3ba6b3f7479 >--- /dev/null >+++ b/JSTests/stress/object-keys-changed-index.js >@@ -0,0 +1,28 @@ >+function shouldBe(actual, expected) >+{ >+ if (actual !== expected) >+ throw new Error('bad value: ' + actual); >+} >+ >+function test(object) >+{ >+ return Object.keys(object); >+} >+noInline(test); >+ >+var object = {}; >+for (var i = 0; i < 1e6; ++i) { >+ var result = test(object); >+ shouldBe(result.length, 0); >+ shouldBe(result[0], undefined); >+ result[0] = i; >+ shouldBe(result.length, 1); >+ shouldBe(result[0], i); >+} >+ >+object[0] = 42; >+for (var i = 0; i < 1e6; ++i) { >+ var result = test(object); >+ shouldBe(result.length, 1); >+ shouldBe(result[0], '0'); >+} >diff --git a/JSTests/stress/object-keys-changed.js b/JSTests/stress/object-keys-changed.js >new file mode 100644 >index 0000000000000000000000000000000000000000..1d3a3053fbe6d64d902297b083dfa720db46c282 >--- /dev/null >+++ b/JSTests/stress/object-keys-changed.js >@@ -0,0 +1,28 @@ >+function shouldBe(actual, expected) >+{ >+ if (actual !== expected) >+ throw new Error('bad value: ' + actual); >+} >+ >+function test(object) >+{ >+ return Object.keys(object); >+} >+noInline(test); >+ >+var object = {}; >+for (var i = 0; i < 1e6; ++i) { >+ var result = test(object); >+ shouldBe(result.length, 0); >+ shouldBe(result[0], undefined); >+ result[0] = i; >+ shouldBe(result.length, 1); >+ shouldBe(result[0], i); >+} >+ >+object.Cocoa = 42; >+for (var i = 0; i < 1e6; ++i) { >+ var result = test(object); >+ shouldBe(result.length, 1); >+ shouldBe(result[0], 'Cocoa'); >+} >diff --git a/JSTests/stress/object-keys-indexed-non-cache.js b/JSTests/stress/object-keys-indexed-non-cache.js >new file mode 100644 >index 0000000000000000000000000000000000000000..09c9cf4ee112a7f317cc6790131c00f4b870d68b >--- /dev/null >+++ b/JSTests/stress/object-keys-indexed-non-cache.js >@@ -0,0 +1,25 @@ >+function shouldBe(actual, expected) >+{ >+ if (actual !== expected) >+ throw new Error('bad value: ' + actual); >+} >+ >+function test(object) >+{ >+ return Object.keys(object); >+} >+noInline(test); >+ >+var object = {0: 42}; >+for (var i = 0; i < 1e3; ++i) { >+ var result = test(object); >+ shouldBe(result.length, 1); >+ shouldBe(result[0], '0'); >+} >+object[1] = 44; >+for (var i = 0; i < 1e3; ++i) { >+ var result = test(object); >+ shouldBe(result.length, 2); >+ shouldBe(result[0], '0'); >+ shouldBe(result[1], '1'); >+} >diff --git a/JSTests/stress/object-keys-overrides-get-property-names.js b/JSTests/stress/object-keys-overrides-get-property-names.js >new file mode 100644 >index 0000000000000000000000000000000000000000..530d8bb17e9198e507be75c5d1961e914b0b7fdc >--- /dev/null >+++ b/JSTests/stress/object-keys-overrides-get-property-names.js >@@ -0,0 +1,57 @@ >+function shouldBe(actual, expected) >+{ >+ if (actual !== expected) >+ throw new Error('bad value: ' + actual); >+} >+ >+function test(object) >+{ >+ return Object.keys(object); >+} >+noInline(test); >+ >+{ >+ let object = new String("Cocoa"); >+ for (let i = 0; i < 1e3; ++i) { >+ let result = test(object); >+ shouldBe(result.length, 5); >+ shouldBe(result[0], '0'); >+ shouldBe(result[1], '1'); >+ shouldBe(result[2], '2'); >+ shouldBe(result[3], '3'); >+ shouldBe(result[4], '4'); >+ } >+ >+ object.Cocoa = 42; >+ let result = test(object); >+ shouldBe(result.length, 6); >+ shouldBe(result[0], '0'); >+ shouldBe(result[1], '1'); >+ shouldBe(result[2], '2'); >+ shouldBe(result[3], '3'); >+ shouldBe(result[4], '4'); >+ shouldBe(result[5], 'Cocoa'); >+} >+ >+{ >+ let object = new String("Cocoa"); >+ for (let i = 0; i < 1e3; ++i) { >+ let result = test(object); >+ shouldBe(result.length, 5); >+ shouldBe(result[0], '0'); >+ shouldBe(result[1], '1'); >+ shouldBe(result[2], '2'); >+ shouldBe(result[3], '3'); >+ shouldBe(result[4], '4'); >+ } >+ >+ object[8] = 42; >+ let result = test(object); >+ shouldBe(result.length, 6); >+ shouldBe(result[0], '0'); >+ shouldBe(result[1], '1'); >+ shouldBe(result[2], '2'); >+ shouldBe(result[3], '3'); >+ shouldBe(result[4], '4'); >+ shouldBe(result[5], '8'); >+}
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Formatted Diff
|
Diff
Attachments on
bug 190047
:
351069
|
351070
|
356924
|
356953
|
357399
|
357412
|
357427