tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

commit 07b19b9cbad6cd2cdbe47cd0bc62d9a77b2e2a53
parent f776bb0d5fb34a3c41f4833cefa76bfbbe1938e8
Author: alexical <dothayer@mozilla.com>
Date:   Wed, 19 Nov 2025 17:52:28 +0000

Bug 1995077 - Integrate with existing iterator indices optimizations r=iain

Differential Revision: https://phabricator.services.mozilla.com/D270930

Diffstat:
Mjs/src/jit-test/tests/ion/iterator-indices-1.js | 11+++++++++++
Mjs/src/jit-test/tests/ion/iterator-indices-2.js | 11+++++++++++
Mjs/src/jit-test/tests/ion/iterator-indices-3.js | 24++++++++++++++++++++++++
Mjs/src/jit-test/tests/ion/iterator-indices-4.js | 7+++++++
Mjs/src/jit-test/tests/ion/iterator-indices-5.js | 13++++++++++++-
Mjs/src/jit-test/tests/ion/iterator-indices-6.js | 16++++++++++++++++
Mjs/src/jit-test/tests/ion/iterator-indices-7.js | 8++++++++
Mjs/src/jit-test/tests/ion/iterator-indices-8.js | 20++++++++++++++++++++
Mjs/src/jit-test/tests/ion/iterator-indices-9.js | 23+++++++++++++++++++++++
Mjs/src/jit/CodeGenerator.cpp | 139++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------------
Mjs/src/jit/CodeGenerator.h | 8++++++++
Mjs/src/jit/Ion.cpp | 15+++++++++++++++
Mjs/src/jit/IonAnalysis.cpp | 83++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------
Mjs/src/jit/Lowering.cpp | 18++++++++++++++++++
Mjs/src/jit/MIR.cpp | 12++++++++++++
Mjs/src/jit/MIROps.yaml | 31+++++++++++++++++++++++++++++++
Mjs/src/jit/MacroAssembler.cpp | 39+++++++++++++++++++++++++++++++++++++++
Mjs/src/jit/MacroAssembler.h | 4++++
Mjs/src/jit/TypePolicy.cpp | 2++
19 files changed, 427 insertions(+), 57 deletions(-)

diff --git a/js/src/jit-test/tests/ion/iterator-indices-1.js b/js/src/jit-test/tests/ion/iterator-indices-1.js @@ -8,6 +8,16 @@ function test(obj, expected) { assertEq(count, expected); } +function test2(obj, expected) { + var count = 0; + for (var s of Object.keys(obj)) { + if (obj.hasOwnProperty(s)) { + count++; + } + } + assertEq(count, expected); +} + var arr = []; for (var i = 0; i < 20; i++) { var obj = {}; @@ -22,4 +32,5 @@ with ({}) {} for (var i = 0; i < 2000; i++) { var idx = i % arr.length; test(arr[idx], idx); + test2(arr[idx], idx); } diff --git a/js/src/jit-test/tests/ion/iterator-indices-2.js b/js/src/jit-test/tests/ion/iterator-indices-2.js @@ -6,6 +6,16 @@ function test(obj, expected) { assertEq(actual, expected); } +function test2(obj, expected) { + var count = 0; + for (var s of Object.keys(obj)) { + if (obj.hasOwnProperty(s)) { + count++; + } + } + assertEq(count, expected); +} + var arr = []; for (var i = 0; i < 20; i++) { var obj = {}; @@ -20,4 +30,5 @@ with ({}) {} for (var i = 0; i < 2000; i++) { var idx = i % arr.length; test(arr[idx], idx); + test2(arr[idx], idx); } diff --git a/js/src/jit-test/tests/ion/iterator-indices-3.js b/js/src/jit-test/tests/ion/iterator-indices-3.js @@ -8,6 +8,16 @@ function test(o, deleter) { return result; } +function test2(o, deleter) { + var result = 0; + for (var s of Object.keys(o)) { + if (!o.hasOwnProperty(s)) { continue; } + result += o[s]; + deleter(o); + } + return result; +} + with ({}) {} // Ion-compile |test| with a megamorphic getprop and a generic call. @@ -16,6 +26,8 @@ for (var i = 0; i < 2000; i++) { obj["x" + i] = 1; test(obj, () => 1); test(obj, () => 2); + test2(obj, () => 1); + test2(obj, () => 2); } assertEq(test({x: 1, y: 2}, (o) => delete o.y), 1); @@ -29,3 +41,15 @@ assertEq(test([1,2,3,4], (o) => {o.length = 2}), 3); assertEq(test({x: 1, y: 2, z: 3}, (o) => delete o.x), 6); assertEq(test([1,2,3], (o) => delete o[0]), 6); assertEq(test([1], (o) => {o.length = 0}), 1); + +assertEq(test2({x: 1, y: 2}, (o) => delete o.y), 1); +assertEq(test2([1,2], (o) => delete o[1]), 1); +assertEq(test2([1,2], (o) => {o.length = 0}), 1); + +assertEq(test2({x: 1, y: 2, z: 3}, (o) => delete o.y), 4); +assertEq(test2([1,2,3], (o) => delete o[1]), 4); +assertEq(test2([1,2,3,4], (o) => {o.length = 2}), 3); + +assertEq(test2({x: 1, y: 2, z: 3}, (o) => delete o.x), 6); +assertEq(test2([1,2,3], (o) => delete o[0]), 6); +assertEq(test2([1], (o) => {o.length = 0}), 1); diff --git a/js/src/jit-test/tests/ion/iterator-indices-4.js b/js/src/jit-test/tests/ion/iterator-indices-4.js @@ -6,6 +6,12 @@ function foo(obj) { } } +function foo2(obj) { + for (var key of Object.keys(obj)) { + assertEq(id(obj[key]), obj[key]); + } +} + var arr = []; for (var i = 0; i < 8; i++) { var obj = {["x" + i]: 1}; @@ -16,4 +22,5 @@ with ({}) {} for (var i = 0; i < 1000; i++) { let obj = arr[i % arr.length]; foo(obj); + foo2(obj); } diff --git a/js/src/jit-test/tests/ion/iterator-indices-5.js b/js/src/jit-test/tests/ion/iterator-indices-5.js @@ -16,6 +16,15 @@ function foo(o, trigger) { return result; } +function foo2(o, trigger) { + var result; + for (var key of Object.keys(o)) { + result = o[key]; + bar(o, trigger); + } + return result; +} + var arr = []; for (var i = 0; i < 10; i++) { arr.push({["x" + i]: 0, y: 0}); @@ -24,7 +33,9 @@ for (var i = 0; i < 10; i++) { with ({}) {} for (var i = 0; i < 1000; i++) { for (var o of arr) { - foo(o, false) + foo(o, false) + foo2(o, false) } } print(foo(arr[0], true)); +print(foo2(arr[0], true)); diff --git a/js/src/jit-test/tests/ion/iterator-indices-6.js b/js/src/jit-test/tests/ion/iterator-indices-6.js @@ -15,6 +15,21 @@ function test(o1, o2) { assertEq(count, 2); } +function test2(o1, o2) { + var count = 0; + for (var s1 of Object.keys(o1)) { + for (var s2 of Object.keys(o2)) { + if (Object.hasOwn(o1, s1)) { + count += o1[s1]; + } + if (Object.hasOwn(o2, s2)) { + count += o2[s2]; + } + } + } + assertEq(count, 2); +} + var arr = []; for (var i = 0; i < 20; i++) { arr.push({["x_" + i]: 1}); @@ -25,4 +40,5 @@ for (var i = 0; i < 2000; i++) { var idx1 = i % arr.length; var idx2 = 1 + i % (arr.length - 1); test(arr[idx1], arr[idx2]); + test2(arr[idx1], arr[idx2]); } diff --git a/js/src/jit-test/tests/ion/iterator-indices-7.js b/js/src/jit-test/tests/ion/iterator-indices-7.js @@ -5,6 +5,13 @@ function test(obj, expected) { } assertEq(actual, expected); } +function test2(obj, expected) { + var actual = 0; + for (var s of Object.keys(obj)) { + actual += obj[s]; + } + assertEq(actual, expected); +} var arr = []; var elem_obj = []; @@ -22,4 +29,5 @@ with ({}) {} for (var i = 0; i < 2000; i++) { var idx = i % arr.length; test(arr[idx], idx); + test2(arr[idx], idx); } diff --git a/js/src/jit-test/tests/ion/iterator-indices-8.js b/js/src/jit-test/tests/ion/iterator-indices-8.js @@ -11,22 +11,42 @@ function test(obj) { index++; } } +function test2(obj) { + let index = 0; + for (var s of Object.keys(obj)) { + obj[s] = index; + index++; + } + index = 0; + for (var s of Object.keys(obj)) { + assertEq(obj[s], index); + index++; + } +} var arr = []; +var arr2 = []; var elem_obj = []; +var elem_obj2 = []; for (var i = 0; i < 20; i++) { var obj = {}; + var obj2 = {}; for (var j = 0; j < i; j++) { obj["x_" + i + "_" + j] = 1; + obj2["x_" + i + "_" + j] = 1; } arr.push(obj); + arr2.push(obj2); elem_obj.push(1); + elem_obj2.push(1); } arr.push(elem_obj); +arr2.push(elem_obj2); with ({}) {} for (var i = 0; i < 2000; i++) { var idx = i % arr.length; test(arr[idx]); + test2(arr2[idx]); } diff --git a/js/src/jit-test/tests/ion/iterator-indices-9.js b/js/src/jit-test/tests/ion/iterator-indices-9.js @@ -16,19 +16,42 @@ function test(obj) { } } +function test2(obj) { + let index = 0; + for (var s of Object.keys(obj)) { + if (s.startsWith("test")) { + obj[s] = index; + } + index++; + } + index = 0; + for (var s of Object.keys(obj)) { + if (s.startsWith("test")) { + assertEq(obj[s], index); + } + index++; + } +} + var arr = []; +var arr2 = []; for (var i = 0; i < 2; i++) { var obj = {}; + var obj2 = {}; for (var j = 0; j < i * 20; j++) { obj["x_" + i + "_" + j] = 1; + obj2["x_" + i + "_" + j] = 1; } obj.testKey = 1; + obj2.testKey = 1; arr.push(obj); + arr2.push(obj2); } with ({}) {} for (var i = 0; i < 2000; i++) { var idx = i % arr.length; test(arr[idx]); + test2(arr2[idx]); } diff --git a/js/src/jit/CodeGenerator.cpp b/js/src/jit/CodeGenerator.cpp @@ -18535,7 +18535,8 @@ void CodeGenerator::visitObjectToIterator(LObjectToIterator* lir) { // do a VM call to replace the cached iterator with a fresh iterator // including indices. masm.branchTest32(Assembler::NonZero, iterFlagsAddr, - Imm32(NativeIterator::Flags::IndicesSupported), ool->entry()); + Imm32(NativeIterator::Flags::IndicesSupported), + ool->entry()); } if (!lir->mir()->skipRegistration()) { @@ -18607,113 +18608,165 @@ void CodeGenerator::visitIteratorHasIndicesAndBranch( } } -void CodeGenerator::visitLoadSlotByIteratorIndex( - LLoadSlotByIteratorIndex* lir) { - Register object = ToRegister(lir->object()); - Register iterator = ToRegister(lir->iterator()); - Register temp = ToRegister(lir->temp0()); - Register temp2 = ToRegister(lir->temp1()); - ValueOperand result = ToOutValue(lir); - - masm.extractCurrentIndexAndKindFromIterator(iterator, temp, temp2); - +void CodeGenerator::visitLoadSlotByIteratorIndexCommon(Register object, + Register indexScratch, + Register kindScratch, + ValueOperand result) { Label notDynamicSlot, notFixedSlot, done; - masm.branch32(Assembler::NotEqual, temp2, + masm.branch32(Assembler::NotEqual, kindScratch, Imm32(uint32_t(PropertyIndex::Kind::DynamicSlot)), &notDynamicSlot); - masm.loadPtr(Address(object, NativeObject::offsetOfSlots()), temp2); - masm.loadValue(BaseValueIndex(temp2, temp), result); + masm.loadPtr(Address(object, NativeObject::offsetOfSlots()), kindScratch); + masm.loadValue(BaseValueIndex(kindScratch, indexScratch), result); masm.jump(&done); masm.bind(&notDynamicSlot); - masm.branch32(Assembler::NotEqual, temp2, + masm.branch32(Assembler::NotEqual, kindScratch, Imm32(uint32_t(PropertyIndex::Kind::FixedSlot)), &notFixedSlot); // Fixed slot - masm.loadValue(BaseValueIndex(object, temp, sizeof(NativeObject)), result); + masm.loadValue(BaseValueIndex(object, indexScratch, sizeof(NativeObject)), + result); masm.jump(&done); masm.bind(&notFixedSlot); #ifdef DEBUG Label kindOkay; - masm.branch32(Assembler::Equal, temp2, + masm.branch32(Assembler::Equal, kindScratch, Imm32(uint32_t(PropertyIndex::Kind::Element)), &kindOkay); masm.assumeUnreachable("Invalid PropertyIndex::Kind"); masm.bind(&kindOkay); #endif // Dense element - masm.loadPtr(Address(object, NativeObject::offsetOfElements()), temp2); + masm.loadPtr(Address(object, NativeObject::offsetOfElements()), kindScratch); Label indexOkay; - Address initLength(temp2, ObjectElements::offsetOfInitializedLength()); - masm.branch32(Assembler::Above, initLength, temp, &indexOkay); + Address initLength(kindScratch, ObjectElements::offsetOfInitializedLength()); + masm.branch32(Assembler::Above, initLength, indexScratch, &indexOkay); masm.assumeUnreachable("Dense element out of bounds"); masm.bind(&indexOkay); - masm.loadValue(BaseObjectElementIndex(temp2, temp), result); + masm.loadValue(BaseObjectElementIndex(kindScratch, indexScratch), result); masm.bind(&done); } -void CodeGenerator::visitStoreSlotByIteratorIndex( - LStoreSlotByIteratorIndex* lir) { +void CodeGenerator::visitLoadSlotByIteratorIndex( + LLoadSlotByIteratorIndex* lir) { Register object = ToRegister(lir->object()); Register iterator = ToRegister(lir->iterator()); - ValueOperand value = ToValue(lir->value()); - Register temp = ToRegister(lir->temp0()); - Register temp2 = ToRegister(lir->temp1()); + Register indexScratch = ToRegister(lir->temp0()); + Register kindScratch = ToRegister(lir->temp1()); + ValueOperand result = ToOutValue(lir); + + masm.extractCurrentIndexAndKindFromIterator(iterator, indexScratch, + kindScratch); + + visitLoadSlotByIteratorIndexCommon(object, indexScratch, kindScratch, result); +} + +#ifndef JS_CODEGEN_X86 +void CodeGenerator::visitLoadSlotByIteratorIndexIndexed( + LLoadSlotByIteratorIndexIndexed* lir) { + Register object = ToRegister(lir->object()); + Register iterator = ToRegister(lir->iterator()); + Register index = ToRegister(lir->index()); + Register indexScratch = ToRegister(lir->temp0()); + Register kindScratch = ToRegister(lir->temp1()); + ValueOperand result = ToOutValue(lir); - masm.extractCurrentIndexAndKindFromIterator(iterator, temp, temp2); + masm.extractIndexAndKindFromIteratorByIterIndex(iterator, index, kindScratch, + indexScratch); + visitLoadSlotByIteratorIndexCommon(object, indexScratch, kindScratch, result); +} +#endif + +void CodeGenerator::visitStoreSlotByIteratorIndexCommon(Register object, + Register indexScratch, + Register kindScratch, + ValueOperand value) { Label notDynamicSlot, notFixedSlot, done, doStore; - masm.branch32(Assembler::NotEqual, temp2, + masm.branch32(Assembler::NotEqual, kindScratch, Imm32(uint32_t(PropertyIndex::Kind::DynamicSlot)), &notDynamicSlot); - masm.loadPtr(Address(object, NativeObject::offsetOfSlots()), temp2); - masm.computeEffectiveAddress(BaseValueIndex(temp2, temp), temp); + masm.loadPtr(Address(object, NativeObject::offsetOfSlots()), kindScratch); + masm.computeEffectiveAddress(BaseValueIndex(kindScratch, indexScratch), + indexScratch); masm.jump(&doStore); masm.bind(&notDynamicSlot); - masm.branch32(Assembler::NotEqual, temp2, + masm.branch32(Assembler::NotEqual, kindScratch, Imm32(uint32_t(PropertyIndex::Kind::FixedSlot)), &notFixedSlot); // Fixed slot masm.computeEffectiveAddress( - BaseValueIndex(object, temp, sizeof(NativeObject)), temp); + BaseValueIndex(object, indexScratch, sizeof(NativeObject)), indexScratch); masm.jump(&doStore); masm.bind(&notFixedSlot); #ifdef DEBUG Label kindOkay; - masm.branch32(Assembler::Equal, temp2, + masm.branch32(Assembler::Equal, kindScratch, Imm32(uint32_t(PropertyIndex::Kind::Element)), &kindOkay); masm.assumeUnreachable("Invalid PropertyIndex::Kind"); masm.bind(&kindOkay); #endif // Dense element - masm.loadPtr(Address(object, NativeObject::offsetOfElements()), temp2); + masm.loadPtr(Address(object, NativeObject::offsetOfElements()), kindScratch); Label indexOkay; - Address initLength(temp2, ObjectElements::offsetOfInitializedLength()); - masm.branch32(Assembler::Above, initLength, temp, &indexOkay); + Address initLength(kindScratch, ObjectElements::offsetOfInitializedLength()); + masm.branch32(Assembler::Above, initLength, indexScratch, &indexOkay); masm.assumeUnreachable("Dense element out of bounds"); masm.bind(&indexOkay); - BaseObjectElementIndex elementAddress(temp2, temp); - masm.computeEffectiveAddress(elementAddress, temp); + BaseObjectElementIndex elementAddress(kindScratch, indexScratch); + masm.computeEffectiveAddress(elementAddress, indexScratch); masm.bind(&doStore); - Address storeAddress(temp, 0); + Address storeAddress(indexScratch, 0); emitPreBarrier(storeAddress); masm.storeValue(value, storeAddress); - masm.branchPtrInNurseryChunk(Assembler::Equal, object, temp2, &done); - masm.branchValueIsNurseryCell(Assembler::NotEqual, value, temp2, &done); + masm.branchPtrInNurseryChunk(Assembler::Equal, object, kindScratch, &done); + masm.branchValueIsNurseryCell(Assembler::NotEqual, value, kindScratch, &done); - saveVolatile(temp2); + saveVolatile(kindScratch); emitPostWriteBarrier(object); - restoreVolatile(temp2); + restoreVolatile(kindScratch); masm.bind(&done); } +void CodeGenerator::visitStoreSlotByIteratorIndex( + LStoreSlotByIteratorIndex* lir) { + Register object = ToRegister(lir->object()); + Register iterator = ToRegister(lir->iterator()); + ValueOperand value = ToValue(lir->value()); + Register indexScratch = ToRegister(lir->temp0()); + Register kindScratch = ToRegister(lir->temp1()); + + masm.extractCurrentIndexAndKindFromIterator(iterator, indexScratch, + kindScratch); + + visitStoreSlotByIteratorIndexCommon(object, indexScratch, kindScratch, value); +} + +#ifndef JS_CODEGEN_X86 +void CodeGenerator::visitStoreSlotByIteratorIndexIndexed( + LStoreSlotByIteratorIndexIndexed* lir) { + Register object = ToRegister(lir->object()); + Register iterator = ToRegister(lir->iterator()); + Register index = ToRegister(lir->index()); + ValueOperand value = ToValue(lir->value()); + Register indexScratch = ToRegister(lir->temp0()); + Register kindScratch = ToRegister(lir->temp1()); + + masm.extractIndexAndKindFromIteratorByIterIndex(iterator, index, kindScratch, + indexScratch); + + visitStoreSlotByIteratorIndexCommon(object, indexScratch, kindScratch, value); +} +#endif + void CodeGenerator::visitSetPropertyCache(LSetPropertyCache* ins) { LiveRegisterSet liveRegs = ins->safepoint()->liveRegs(); Register objReg = ToRegister(ins->object()); diff --git a/js/src/jit/CodeGenerator.h b/js/src/jit/CodeGenerator.h @@ -172,6 +172,14 @@ class CodeGenerator final : public CodeGeneratorSpecific { void visitPostWriteBarrierCommon(LPostBarrierType* lir, OutOfLineCode* ool); template <class LPostBarrierType> void visitPostWriteBarrierCommonV(LPostBarrierType* lir, OutOfLineCode* ool); + void visitLoadSlotByIteratorIndexCommon(Register object, + Register indexScratch, + Register kindScratch, + ValueOperand result); + void visitStoreSlotByIteratorIndexCommon(Register object, + Register indexScratch, + Register kindScratch, + ValueOperand value); void emitCallInvokeFunction(LInstruction* call, Register callereg, bool isConstructing, bool ignoresReturnValue, diff --git a/js/src/jit/Ion.cpp b/js/src/jit/Ion.cpp @@ -1084,6 +1084,21 @@ bool OptimizeMIR(MIRGenerator* mir) { } } + if (!JitOptions.disableRecoverIns && + mir->optimizationInfo().scalarReplacementEnabled() && + !JitOptions.disableObjectKeysScalarReplacement) { + JitSpewCont(JitSpew_Escape, "\n"); + if (!ReplaceObjectKeys(mir, graph)) { + return false; + } + mir->spewPass("Replace ObjectKeys"); + AssertGraphCoherency(graph); + + if (mir->shouldCancel("Replace ObjectKeys")) { + return false; + } + } + if (!mir->compilingWasm() && !JitOptions.disableIteratorIndices) { if (!OptimizeIteratorIndices(mir, graph)) { return false; diff --git a/js/src/jit/IonAnalysis.cpp b/js/src/jit/IonAnalysis.cpp @@ -5135,12 +5135,24 @@ bool jit::MakeLoopsContiguous(MIRGraph& graph) { return true; } -static MDefinition* SkipUnbox(MDefinition* ins) { +static MDefinition* SkipIterObjectUnbox(MDefinition* ins) { + if (ins->isGuardIsNotProxy()) { + ins = ins->toGuardIsNotProxy()->input(); + } if (ins->isUnbox()) { - return ins->toUnbox()->input(); + ins = ins->toUnbox()->input(); + } + return ins; +} + +#ifndef JS_CODEGEN_X86 +static MDefinition* SkipBox(MDefinition* ins) { + if (ins->isBox()) { + return ins->toBox()->input(); } return ins; } +#endif bool jit::OptimizeIteratorIndices(const MIRGenerator* mir, MIRGraph& graph) { bool changed = false; @@ -5189,8 +5201,8 @@ bool jit::OptimizeIteratorIndices(const MIRGenerator* mir, MIRGraph& graph) { // Given the following structure (that occurs inside for-in loops): // obj: some object // iter: ObjectToIterator <obj> - // iterNext: IteratorMore <iter> - // access: HasProp/GetElem <obj> <iterNext> + // iterLoad: IteratorMore <iter> | LoadIteratorElement <iter, index> + // access: HasProp/GetElem <obj> <iterLoad> // If the iterator object has an indices array, we can speed up the // property access: // 1. If the property access is a HasProp looking for own properties, @@ -5200,17 +5212,42 @@ bool jit::OptimizeIteratorIndices(const MIRGenerator* mir, MIRGraph& graph) { // 2. If the property access is a GetProp, then we can use the contents // of the indices array to find the correct property faster than // the megamorphic cache. - if (!idVal->isIteratorMore()) { - continue; - } - auto* iterNext = idVal->toIteratorMore(); + // 3. If the property access is a SetProp, then we can use the contents + // of the indices array to find the correct slots faster than the + // megamorphic cache. - if (!iterNext->iterator()->isObjectToIterator()) { - continue; - } + MObjectToIterator* iter = nullptr; +#ifndef JS_CODEGEN_X86 + MDefinition* iterElementIndex = nullptr; +#endif + if (idVal->isIteratorMore()) { + auto* iterNext = idVal->toIteratorMore(); + + if (!iterNext->iterator()->isObjectToIterator()) { + continue; + } - MObjectToIterator* iter = iterNext->iterator()->toObjectToIterator(); - if (SkipUnbox(iter->object()) != SkipUnbox(receiver)) { + iter = iterNext->iterator()->toObjectToIterator(); + if (SkipIterObjectUnbox(iter->object()) != + SkipIterObjectUnbox(receiver)) { + continue; + } +#ifndef JS_CODEGEN_X86 + } else if (SkipBox(idVal)->isLoadIteratorElement()) { + auto* iterLoad = SkipBox(idVal)->toLoadIteratorElement(); + + if (!iterLoad->iter()->isObjectToIterator()) { + continue; + } + + iter = iterLoad->iter()->toObjectToIterator(); + if (SkipIterObjectUnbox(iter->object()) != + SkipIterObjectUnbox(receiver)) { + continue; + } + iterElementIndex = iterLoad->index(); +#endif + } else { continue; } @@ -5223,13 +5260,33 @@ bool jit::OptimizeIteratorIndices(const MIRGenerator* mir, MIRGraph& graph) { } else if (ins->isMegamorphicLoadSlotByValue() || ins->isGetPropertyCache()) { MOZ_ASSERT(!setValue); +#ifndef JS_CODEGEN_X86 + if (iterElementIndex) { + replacement = MLoadSlotByIteratorIndexIndexed::New( + graph.alloc(), receiver, iter, iterElementIndex); + } else { + replacement = + MLoadSlotByIteratorIndex::New(graph.alloc(), receiver, iter); + } +#else replacement = MLoadSlotByIteratorIndex::New(graph.alloc(), receiver, iter); +#endif } else { MOZ_ASSERT(ins->isMegamorphicSetElement() || ins->isSetPropertyCache()); MOZ_ASSERT(setValue); +#ifndef JS_CODEGEN_X86 + if (iterElementIndex) { + replacement = MStoreSlotByIteratorIndexIndexed::New( + graph.alloc(), receiver, iter, iterElementIndex, setValue); + } else { + replacement = MStoreSlotByIteratorIndex::New(graph.alloc(), receiver, + iter, setValue); + } +#else replacement = MStoreSlotByIteratorIndex::New(graph.alloc(), receiver, iter, setValue); +#endif } if (!block->wrapInstructionInFastpath(ins, replacement, indicesCheck)) { diff --git a/js/src/jit/Lowering.cpp b/js/src/jit/Lowering.cpp @@ -6271,6 +6271,24 @@ void LIRGenerator::visitStoreSlotByIteratorIndex( add(lir, ins); } +#ifndef JS_CODEGEN_X86 +void LIRGenerator::visitLoadSlotByIteratorIndexIndexed( + MLoadSlotByIteratorIndexIndexed* ins) { + auto* lir = new (alloc()) LLoadSlotByIteratorIndexIndexed( + useRegister(ins->object()), useRegister(ins->iterator()), + useRegister(ins->index()), temp(), temp()); + defineBox(lir, ins); +} + +void LIRGenerator::visitStoreSlotByIteratorIndexIndexed( + MStoreSlotByIteratorIndexIndexed* ins) { + auto* lir = new (alloc()) LStoreSlotByIteratorIndexIndexed( + useRegister(ins->object()), useRegister(ins->iterator()), + useRegister(ins->index()), useBox(ins->value()), temp(), temp()); + add(lir, ins); +} +#endif + void LIRGenerator::visitIteratorHasIndices(MIteratorHasIndices* ins) { MOZ_ASSERT(ins->hasOneUse()); emitAtUses(ins); diff --git a/js/src/jit/MIR.cpp b/js/src/jit/MIR.cpp @@ -7849,6 +7849,18 @@ AliasSet MStoreSlotByIteratorIndex::getAliasSet() const { AliasSet::DynamicSlot | AliasSet::Element); } +#ifndef JS_CODEGEN_X86 +AliasSet MLoadSlotByIteratorIndexIndexed::getAliasSet() const { + return AliasSet::Load(AliasSet::ObjectFields | AliasSet::FixedSlot | + AliasSet::DynamicSlot | AliasSet::Element); +} + +AliasSet MStoreSlotByIteratorIndexIndexed::getAliasSet() const { + return AliasSet::Store(AliasSet::ObjectFields | AliasSet::FixedSlot | + AliasSet::DynamicSlot | AliasSet::Element); +} +#endif + MDefinition* MGuardInt32IsNonNegative::foldsTo(TempAllocator& alloc) { MOZ_ASSERT(index()->type() == MIRType::Int32); diff --git a/js/src/jit/MIROps.yaml b/js/src/jit/MIROps.yaml @@ -2844,6 +2844,37 @@ generate_lir: true lir_temps: 2 + +# The following two instructions need too many registers on x86. We could +# spill or do some cheeky stuff potentially but it's likely not worth the +# complexity +#ifndef JS_CODEGEN_X86 + +# Okay this is a confusing name, but essentially, the above ops load/store a +# slot of an object using the PropertyIndex associated with the *current* key +# for the iterator. The below op uses the PropertyIndex associated with the +# key indexed by `index`, rather than the current one. +- name: LoadSlotByIteratorIndexIndexed + operands: + object: Object + iterator: Object + index: Int32 + result_type: Value + alias_set: custom + generate_lir: true + lir_temps: 2 + +- name: StoreSlotByIteratorIndexIndexed + operands: + object: Object + iterator: Object + index: Int32 + value: Value + alias_set: custom + generate_lir: true + lir_temps: 2 +#endif + # Load the private value expando from a DOM proxy. The target is stored in the # proxy object's private slot. # This is either an UndefinedValue (no expando), ObjectValue (the expando diff --git a/js/src/jit/MacroAssembler.cpp b/js/src/jit/MacroAssembler.cpp @@ -3280,6 +3280,45 @@ void MacroAssembler::extractCurrentIndexAndKindFromIterator(Register iterator, and32(Imm32(PropertyIndex::IndexMask), outIndex); } +void MacroAssembler::extractIndexAndKindFromIteratorByIterIndex( + Register iterator, Register inIndex, Register outKind, Register outIndex) { + // Load iterator object + Address nativeIterAddr(iterator, + PropertyIteratorObject::offsetOfIteratorSlot()); + loadPrivate(nativeIterAddr, outIndex); + + // Load the property count into outKind. + load32(Address(outIndex, NativeIterator::offsetOfPropertyCount()), outKind); + + // We need two bits of wiggle room in a u32 here for the logic below. + static_assert(NativeIterator::PropCountLimit <= 1 << 30); + + // Shift up the property count on 64 bit. Ultimately we want + // sizeof(IteratorProperty) * count + sizeof(PropertyIndex) * cursor. + // If we shift up our count to be on the same scale as cursor right now, + // we can do this all with one register. + static_assert(sizeof(IteratorProperty) == sizeof(PropertyIndex) || + sizeof(IteratorProperty) == sizeof(PropertyIndex) * 2); + if constexpr (sizeof(IteratorProperty) > sizeof(PropertyIndex)) { + lshift32(Imm32(1), outKind); + } + + // Add the index + add32(inIndex, outKind); + + // outKind holds the offset in u32's to our PropertyIndex, so just multiply + // by four and add it to the offset of the first property + load32(BaseIndex(outIndex, outKind, Scale::TimesFour, + NativeIterator::offsetOfFirstProperty()), + outIndex); + + // Extract kind. + rshift32(Imm32(PropertyIndex::KindShift), outIndex, outKind); + + // Extract index. + and32(Imm32(PropertyIndex::IndexMask), outIndex); +} + template <typename IdType> void MacroAssembler::emitMegamorphicCachedSetSlot( IdType id, Register obj, Register scratch1, diff --git a/js/src/jit/MacroAssembler.h b/js/src/jit/MacroAssembler.h @@ -5723,6 +5723,10 @@ class MacroAssembler : public MacroAssemblerSpecific { void extractCurrentIndexAndKindFromIterator(Register iterator, Register outIndex, Register outKind); + void extractIndexAndKindFromIteratorByIterIndex(Register iterator, + Register inOutIndex, + Register outKind, + Register scratch); template <typename IdType> #ifdef JS_CODEGEN_X86 diff --git a/js/src/jit/TypePolicy.cpp b/js/src/jit/TypePolicy.cpp @@ -1072,6 +1072,8 @@ bool ClampPolicy::adjustInputs(TempAllocator& alloc, MInstruction* ins) const { _(MixPolicy<ObjectPolicy<0>, ObjectPolicy<1>, IntPtrPolicy<2>>) \ _(MixPolicy<ObjectPolicy<0>, ObjectPolicy<1>, ObjectPolicy<2>>) \ _(MixPolicy<ObjectPolicy<0>, ObjectPolicy<1>, BoxPolicy<2>>) \ + _(MixPolicy<ObjectPolicy<0>, ObjectPolicy<1>, UnboxedInt32Policy<2>, \ + BoxPolicy<3>>) \ _(MixPolicy<StringPolicy<0>, UnboxedInt32Policy<1>, UnboxedInt32Policy<2>>) \ _(MixPolicy<StringPolicy<0>, ObjectPolicy<1>, StringPolicy<2>>) \ _(MixPolicy<StringPolicy<0>, StringPolicy<1>, StringPolicy<2>>) \