Skip to content

Commit de4790b

Browse files
shvaikaleshAlexey Shvayka
authored andcommitted
[WebIDL] Eagerly allocate element wrappers onto a butterfly storage of a static NodeList
https://bugs.webkit.org/show_bug.cgi?id=234538 Reviewed by Yusuke Suzuki and Saam Barati. This is a re-land of r253946 with a suggestedArrayStorageTransition() fixed, debug assertions adjusted, and tests performing less iterations to avoid timeouts. This change greatly speeds up iterating static NodeList iteration (including via Array.prototype.indexOf), making it on par with JSArray. It preserves current (weird) NodeList semantics, including cases of transitioning to slow put array storage mode, and is future-proof against aligning internal methods of DOM collections with other browsers (http://webkit.org/b/218849). There was no way to achieve that with existing indexing types: put_by_val compiled for NonArrayWithContiguous emits only indexing type check, thus incorrectly mutating a NodeList instead of calling put() override. NonArrayWithSlowPutArrayStorage could be semantically correct alternative only if all its elements are onto a sparse map, which won't be much faster. Two viable approaches were either introducing a new Contiguous-like indexing type, or doing something similar to IndexedDirectArgumentsLoad. This change does the former to reuse existing logic for DFG::ArrayMode refining and ArrayIndexOf compilation. Another tricky part is that NodeList's "length" is a configurable getter on prototype, unlike own non-configurable JSArray's "length", and we need to ensure that both the NodeList structure is original (inherits from NodeList.prototype) and the "length" getter wasn't deleted / overriden. For the static NodeList to leverage fast compiled get_by_val, it needs to have original getOwnPropertySlotByIndex() method, so SlowPutArrayStorageVectorPropertiesAreReadOnly out-of-line type info flag is added to reliably preserve ReadOnly attributes of its indices as per spec [1]. This change is a locally confirmed 2% progression on all three Speedometer2/VanillaJS subtests. [1] https://webidl.spec.whatwg.org/#LegacyPlatformObjectGetOwnProperty (step 1.2.7) * .gitignore: * Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj: * Source/JavaScriptCore/bytecode/AccessCase.cpp: (JSC::AccessCase::create): (JSC::AccessCase::guardedByStructureCheckSkippingConstantIdentifierCheck const): (JSC::AccessCase::requiresIdentifierNameMatch const): (JSC::AccessCase::requiresInt32PropertyCheck const): (JSC::AccessCase::needsScratchFPR const): (JSC::AccessCase::forEachDependentCell const): (JSC::AccessCase::doesCalls const): (JSC::AccessCase::canReplace const): (JSC::AccessCase::generateWithGuard): (JSC::AccessCase::generateImpl): (JSC::AccessCase::canBeShared): * Source/JavaScriptCore/bytecode/AccessCase.h: * Source/JavaScriptCore/bytecode/AdaptiveInferredPropertyValueWatchpointBase.cpp: * Source/JavaScriptCore/bytecode/AdaptiveInferredPropertyValueWatchpointBase.h: * Source/JavaScriptCore/bytecode/ArrayProfile.cpp: (JSC::dumpArrayModes): (JSC::ArrayProfile::computeUpdatedPrediction): * Source/JavaScriptCore/bytecode/ArrayProfile.h: (JSC::shouldUseAlwaysSlowPutContiguous): * Source/JavaScriptCore/bytecode/PolymorphicAccess.cpp: (WTF::printInternal): * Source/JavaScriptCore/bytecode/Repatch.cpp: (JSC::tryCacheArrayGetByVal): * Source/JavaScriptCore/bytecode/Watchpoint.h: * Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h: (JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects): * Source/JavaScriptCore/dfg/DFGArrayMode.cpp: (JSC::DFG::ArrayMode::fromObserved): (JSC::DFG::ArrayMode::refine const): (JSC::DFG::ArrayMode::alreadyChecked const): (JSC::DFG::arrayTypeToString): (JSC::DFG::toIndexingShape): (JSC::DFG::permitsBoundsCheckLowering): * Source/JavaScriptCore/dfg/DFGArrayMode.h: (JSC::DFG::ArrayMode::usesButterfly const): (JSC::DFG::ArrayMode::isSlowPut const): (JSC::DFG::ArrayMode::isAnyKindOfContiguous const): (JSC::DFG::ArrayMode::lengthNeedsStorage const): (JSC::DFG::ArrayMode::modeForPut const): (JSC::DFG::ArrayMode::supportsSelfLength const): (JSC::DFG::ArrayMode::arrayModesThatPassFiltering const): * Source/JavaScriptCore/dfg/DFGArrayifySlowPathGenerator.h: * Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp: (JSC::DFG::ByteCodeParser::handleIntrinsicCall): * Source/JavaScriptCore/dfg/DFGClobberize.h: (JSC::DFG::clobberize): * Source/JavaScriptCore/dfg/DFGFixupPhase.cpp: (JSC::DFG::FixupPhase::attemptToMakeGetArrayLength): (JSC::DFG::FixupPhase::attemptToMakeGetArrayLengthForAlwaysSlowPutContiguous): (JSC::DFG::FixupPhase::fixupArrayIndexOf): * Source/JavaScriptCore/dfg/DFGOperations.cpp: (JSC::DFG::JSC_DEFINE_JIT_OPERATION): * Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp: (JSC::DFG::SpeculativeJIT::jumpSlowForUnwantedArrayMode): (JSC::DFG::SpeculativeJIT::checkArray): (JSC::DFG::SpeculativeJIT::compilePutByVal): * Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp: (JSC::DFG::SpeculativeJIT::compileGetByVal): * Source/JavaScriptCore/ftl/FTLAbstractHeapRepository.h: (JSC::FTL::AbstractHeapRepository::forArrayType): * Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp: (JSC::FTL::DFG::LowerDFGToB3::compileArrayify): (JSC::FTL::DFG::LowerDFGToB3::compileGetArrayLength): (JSC::FTL::DFG::LowerDFGToB3::compileGetByValImpl): (JSC::FTL::DFG::LowerDFGToB3::compilePutByVal): (JSC::FTL::DFG::LowerDFGToB3::compileArrayIndexOf): (JSC::FTL::DFG::LowerDFGToB3::compileCompareStrictEq): * Source/JavaScriptCore/llint/LLIntSlowPaths.cpp: (JSC::LLInt::performLLIntGetByID): * Source/JavaScriptCore/llint/LowLevelInterpreter.asm: * Source/JavaScriptCore/llint/LowLevelInterpreter32_64.asm: * Source/JavaScriptCore/llint/LowLevelInterpreter64.asm: * Source/JavaScriptCore/runtime/ArrayPrototype.cpp: (JSC::JSC_DEFINE_HOST_FUNCTION): (JSC::tryFastIndexOf): (JSC::fastIndexOf): Deleted. * Source/JavaScriptCore/runtime/IndexingType.cpp: (JSC::dumpIndexingType): * Source/JavaScriptCore/runtime/IndexingType.h: (JSC::hasAlwaysSlowPutContiguous): (JSC::hasAnyContiguous): * Source/JavaScriptCore/runtime/JSArray.cpp: (JSC::JSArray::fastSlice): * Source/JavaScriptCore/runtime/JSGlobalObject.cpp: (JSC::JSGlobalObject::init): (JSC::JSGlobalObject::initAlwaysSlowPutContiguousWatchpointSet): (JSC::JSGlobalObject::recordOriginalAlwaysSlowPutContiguousStructure): * Source/JavaScriptCore/runtime/JSGlobalObject.h: (JSC::JSGlobalObject::alwaysSlowPutContiguousWatchpointSet): (JSC::JSGlobalObject::isOriginalSlowPutContigiousStructure): (JSC::JSGlobalObject::originalAlwaysSlowPutContiguousStructures const): * Source/JavaScriptCore/runtime/JSGlobalObjectInlines.h: (JSC::JSGlobalObject::alwaysSlowPutContiguousPrototypeChainIsSane): * Source/JavaScriptCore/runtime/JSObject.cpp: (JSC::JSObject::getOwnPropertySlotByIndex): (JSC::JSObject::convertContiguousToArrayStorage): (JSC::JSObject::ensureArrayStorageExistsAndEnterDictionaryIndexingMode): (JSC::JSObject::switchToSlowPutArrayStorage): (JSC::JSObject::putByIndexBeyondVectorLengthWithoutAttributes): * Source/JavaScriptCore/runtime/JSObject.h: (JSC::JSObject::trySetIndexQuickly): (JSC::JSObject::setIndexQuickly): (JSC::JSObject::tryMakeWritableContiguous): * Source/JavaScriptCore/runtime/JSObjectInlines.h: (JSC::JSObject::canDoFastIndexedAccess): * Source/JavaScriptCore/runtime/JSTypeInfo.h: (JSC::TypeInfo::arrayStorageVectorPropertiesAreReadOnly const): * Source/JavaScriptCore/runtime/StructureTransitionTable.h: (JSC::newIndexingType): * Source/JavaScriptCore/tools/JSDollarVM.cpp: (JSC::createAlwaysSlowPutContiguousObject): (JSC::JSC_DEFINE_HOST_FUNCTION): (JSC::JSDollarVM::finishCreation): * Source/WTF/wtf/Vector.h: (WTF::Malloc>::every const): * Source/WebCore/WebCore.xcodeproj/project.pbxproj: * Source/WebCore/bindings/js/JSDOMWindowBase.cpp: (WebCore::JSDOMWindowBase::finishCreation): (WebCore::JSDOMWindowBase::installAlwaysSlowPutContiguousWatchpoints): * Source/WebCore/bindings/js/JSDOMWindowBase.h: * Source/WebCore/bindings/js/JSNodeListCustom.cpp: (WebCore::JSStaticNodeList::createPrototype): (WebCore::JSStaticNodeList::JSStaticNodeList): (WebCore::JSStaticNodeList::finishCreation): (WebCore::createWrapper): * Source/WebCore/bindings/js/JSNodeListCustom.h: (WebCore::JSStaticNodeList::create): (WebCore::JSStaticNodeList::createStructure): * Source/WebCore/dom/NodeList.h: (WebCore::NodeList::isStaticNodeList const): * Source/WebCore/dom/StaticNodeList.h: Canonical link: https://commits.webkit.org/254036@main
1 parent 78a2a01 commit de4790b

235 files changed

Lines changed: 1480 additions & 377 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎.gitignore‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,3 +66,5 @@ __cmake_systeminformation/
6666

6767
# Local overrides configuration files
6868
LocalOverrides.xcconfig
69+
70+
/BenchmarkTemp
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
const length = 100;
2+
3+
var target = $vm.createAlwaysSlowPutContiguousObjectWithOverrides(length);
4+
for (var i = 0; i < length; i++)
5+
target[i] = {index: i};
6+
7+
(function() {
8+
var {indexOf} = Array.prototype;
9+
var searchElement = target[50];
10+
11+
for (var j = 0; j < 1e6; j++) {
12+
if (indexOf.call(target, searchElement) !== 50)
13+
throw new Error(`Bad assert: ${j}!`);
14+
}
15+
})();
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
const length = 100;
2+
3+
var target = new Array(100);
4+
for (var i = 0; i < length; i++)
5+
target[i] = {index: i};
6+
7+
(function() {
8+
var {indexOf} = Array.prototype;
9+
var searchElement = target[50];
10+
11+
for (var j = 0; j < 1e6; j++) {
12+
if (indexOf.call(target, searchElement) !== 50)
13+
throw new Error(`Bad assert: ${j}!`);
14+
}
15+
})();
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
const length = 100;
2+
3+
var target = $vm.createAlwaysSlowPutContiguousObjectWithOverrides(length);
4+
for (var i = 0; i < length; i++)
5+
target[i] = {index: i};
6+
7+
(function() {
8+
var {slice} = Array.prototype;
9+
var lastSlice;
10+
11+
for (var j = 0; j < 1e6; j++)
12+
lastSlice = slice.call(target);
13+
14+
if (lastSlice.pop().index !== 99)
15+
throw new Error(`Bad assert!`);
16+
})();
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
const length = 100;
2+
3+
var target = new Array(length);
4+
for (var i = 0; i < length; i++)
5+
target[i] = {index: i};
6+
7+
(function() {
8+
var {slice} = Array.prototype;
9+
var lastSlice;
10+
11+
for (var j = 0; j < 1e6; j++)
12+
lastSlice = slice.call(target);
13+
14+
if (lastSlice.pop().index !== 99)
15+
throw new Error(`Bad assert!`);
16+
})();
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
const length = 100;
2+
3+
var target = $vm.createAlwaysSlowPutContiguousObjectWithOverrides(length);
4+
for (var i = 0; i < length; i++)
5+
target[i] = {index: i};
6+
7+
(function() {
8+
var el;
9+
10+
for (var j = 0; j < 1e6; j++) {
11+
for (var i = 0; i < length / 2; i++)
12+
el = target[i];
13+
}
14+
15+
if (el.index + 1 !== length / 2)
16+
throw new Error(`Bad assert!`);
17+
})();
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
const length = 100;
2+
3+
var target = new Array(100);
4+
for (var i = 0; i < length; i++)
5+
target[i] = {index: i};
6+
7+
(function() {
8+
var el;
9+
10+
for (var j = 0; j < 1e6; j++) {
11+
for (var i = 0; i < length / 2; i++)
12+
el = target[i];
13+
}
14+
15+
if (el.index + 1 !== length / 2)
16+
throw new Error(`Bad assert!`);
17+
})();
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
function assert(x) { if (!x) throw new Error(`Bad assertion: ${x}!`); }
2+
3+
function putByVal(target, index) { "use strict"; target[index] = {index}; }
4+
noInline(putByVal);
5+
6+
const runs = 1e5;
7+
const length = 100;
8+
9+
var target = $vm.createAlwaysSlowPutContiguousObjectWithOverrides(length);
10+
for (var i = 0; i < length; i++)
11+
target[i] = {index: i};
12+
13+
(function() {
14+
for (var i = 0; i < runs; i++) {
15+
if (i === runs - 1)
16+
target[10000001] = 1;
17+
18+
for (var j = 0; j < length; j++)
19+
putByVal(target, j);
20+
}
21+
22+
assert(target.putByIndexCalls === length + (length * runs) + 1);
23+
assert($vm.indexingMode(target) === "NonArrayWithSlowPutArrayStorage");
24+
assert($vm.hasSparseModeArrayStorage(target));
25+
})();
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
function assert(x) { if (!x) throw new Error(`Bad assertion: ${x}!`); }
2+
3+
function putByVal(target, index) { "use strict"; target[index] = {index}; }
4+
noInline(putByVal);
5+
6+
const runs = 1e5;
7+
const length = 100;
8+
9+
var target = $vm.createAlwaysSlowPutContiguousObjectWithOverrides(length);
10+
for (var i = 0; i < length; i++)
11+
target[i] = {index: i};
12+
13+
(function() {
14+
for (var i = 0; i < runs; i++) {
15+
if (i === runs - 1)
16+
Object.setPrototypeOf(target, { get 42() {}, set 42(_v) {} });
17+
18+
for (var j = 0; j < length; j++)
19+
putByVal(target, j);
20+
}
21+
22+
assert(target.putByIndexCalls === length + (length * runs));
23+
assert($vm.indexingMode(target) === "NonArrayWithSlowPutArrayStorage");
24+
assert($vm.hasSparseModeArrayStorage(target));
25+
})();
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
function assert(x) { if (!x) throw new Error(`Bad assertion: ${x}!`); }
2+
3+
function putByVal(target, index) { "use strict"; target[index] = {index}; }
4+
noInline(putByVal);
5+
6+
const runs = 1e5;
7+
const length = 100;
8+
9+
var target = $vm.createAlwaysSlowPutContiguousObjectWithOverrides(length);
10+
for (var i = 0; i < length; i++)
11+
target[i] = {index: i};
12+
13+
(function() {
14+
for (var i = 0; i < runs; i++) {
15+
if (i === runs - 1)
16+
Object.defineProperty(target, 10000001, { get() {}, set() {} });
17+
18+
for (var j = 0; j < length; j++)
19+
putByVal(target, j);
20+
}
21+
22+
assert(target.putByIndexCalls === length + (length * runs));
23+
assert($vm.indexingMode(target) === "NonArrayWithSlowPutArrayStorage");
24+
assert($vm.hasSparseModeArrayStorage(target));
25+
})();

0 commit comments

Comments
 (0)