tor-browser

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

commit 84a64ae8b98b0530766adab9ed2a23ba86b8d644
parent d2c9637c107bd9e5c54a4d14ab598249daca97fb
Author: Alexandru Marc <amarc@mozilla.com>
Date:   Fri, 12 Dec 2025 03:03:27 +0200

Revert "Bug 1999828: Part 7 - Add tests. r=iain" for causing bc failures at ScalarReplacement.cpp

This reverts commit 437712a9e466460933a4d854eacb4d2ec87d5053.

Revert "Bug 1999828: Part 6 - Implement StoreFixedSlotFromOffset and StoreDynamicSlotFromOffset. r=iain"

This reverts commit 374129481e266e1ed253d953ecee3182b20ac498.

Revert "Bug 1999828: Part 5 - Implement LoadFixedSlotFromOffset and LoadDynamicSlotFromOffset. r=iain"

This reverts commit 5e9c8f20bfc9abd501a9340ecfb16d4a58bd4065.

Revert "Bug 1999828: Part 4 - Implement GuardShapeListToOffset. r=iain"

This reverts commit 74a159492d658aa8964c2078f3abebccfc1ede06.

Revert "Bug 1999828: Part 3 - Implement GuardMultipleShapesToOffset. r=iain"

This reverts commit 1e3056aabe563607243f09c16028da94443f6f03.

Revert "Bug 1999828: Part 2 - Add additional stubfolding spew information. r=iain"

This reverts commit 9b4615ce61d33853b7cd054f0a11463a1a54aedd.

Revert "Bug 1999828: Part 1 - Split TryFoldingStubs into two passes. One to detect the shape (and later slot offset) and another to gather the values. r=iain"

This reverts commit b90c10909db17cd4d89c5ce56016fbf07a25f2f6.

Diffstat:
Djs/src/jit-test/tests/cacheir/guard-shape-to-offset-list.js | 28----------------------------
Djs/src/jit-test/tests/cacheir/ion-store-load-from-offset.js | 58----------------------------------------------------------
Djs/src/jit-test/tests/cacheir/ops-guard-shapelist-to-offset.js | 37-------------------------------------
Djs/src/jit-test/tests/cacheir/ops-using-from-offset.js | 29-----------------------------
Djs/src/jit-test/tests/cacheir/stored-dynamic-slot-from-offset.js | 34----------------------------------
Djs/src/jit-test/tests/cacheir/stored-fixed-slot-from-offset.js | 28----------------------------
Mjs/src/jit/CacheIR.h | 2--
Mjs/src/jit/CacheIRCompiler.cpp | 114++++---------------------------------------------------------------------------
Mjs/src/jit/CacheIROps.yaml | 44--------------------------------------------
Mjs/src/jit/CacheIRWriter.h | 11++---------
Mjs/src/jit/CodeGenerator.cpp | 174+------------------------------------------------------------------------------
Mjs/src/jit/GenerateMIRFiles.py | 14+++-----------
Mjs/src/jit/JitScript.cpp | 16----------------
Mjs/src/jit/JitSpewer.cpp | 5-----
Mjs/src/jit/JitSpewer.h | 200+++++++++++++++++++++++++++++++++++++++----------------------------------------
Mjs/src/jit/LIROps.yaml | 40----------------------------------------
Mjs/src/jit/Lowering.cpp | 80-------------------------------------------------------------------------------
Mjs/src/jit/MIR.cpp | 43-------------------------------------------
Mjs/src/jit/MIR.h | 63---------------------------------------------------------------
Mjs/src/jit/MIROps.yaml | 49-------------------------------------------------
Mjs/src/jit/MacroAssembler.cpp | 40+++++++++++++---------------------------
Mjs/src/jit/MacroAssembler.h | 19++++---------------
Mjs/src/jit/ShapeList.cpp | 96-------------------------------------------------------------------------------
Mjs/src/jit/ShapeList.h | 22----------------------
Mjs/src/jit/StubFolding.cpp | 559+++++++++++++------------------------------------------------------------------
Mjs/src/jit/TypePolicy.cpp | 2--
Mjs/src/jit/WarpBuilder.cpp | 3---
Mjs/src/jit/WarpCacheIRTranspiler.cpp | 83-------------------------------------------------------------------------------
Mjs/src/jit/WarpOracle.cpp | 24------------------------
Mjs/src/jit/WarpSnapshot.cpp | 19-------------------
Mjs/src/jit/WarpSnapshot.h | 73++++++++++++++++---------------------------------------------------------
31 files changed, 235 insertions(+), 1774 deletions(-)

diff --git a/js/src/jit-test/tests/cacheir/guard-shape-to-offset-list.js b/js/src/jit-test/tests/cacheir/guard-shape-to-offset-list.js @@ -1,28 +0,0 @@ -function test() { - var n = 3; - var arr = []; - for (var i = 0; i < n; i++) { - var obj = {}; - for (var j = 0; j < i+4; j++) { - obj["x_" + i + "_" + j] = 1; - } - arr.push(obj); - } - for (var i = 0; i < n; i++) { - arr[i].a = -1; - arr[i].b = -1; - } - - for (let i=0; i<10000; i++) { - arr[i%n].a = 0; - arr[i%n].b = 1; - } - - - for (var i = 0; i < n; i++) { - assertEq(arr[i].a, 0); - assertEq(arr[i].b, 1); - } -} - -test(); diff --git a/js/src/jit-test/tests/cacheir/ion-store-load-from-offset.js b/js/src/jit-test/tests/cacheir/ion-store-load-from-offset.js @@ -1,58 +0,0 @@ -// |jit-test| --ion-offthread-compile=off; -// -function test(arr, max) { - - // LoadFixedFromOffset - var test = 0; - for (let i=0; i<20; i++) { - test = arr[i%max].a; - } - - // StoreFixedFromOffset - var test = 0; - for (let i=0; i<20; i++) { - arr[i%max].a = i%max; - } - - for (let i=0; i<max; i++) { - assertEq(arr[i].a, i); - } -} - -var arr = []; -arr[0] = {a:0}; -arr[1] = {b:0, a:1}; -arr[2] = {c:0, a:2}; - -// GuardShapeListToOffset (3) -for (let i=0; i<1000; i++) { - test(arr, arr.length); -} - -arr[3] = {d:0, a:3}; - -// GuardShapeListToOffset (4) -for (let i=0; i<1000; i++) { - test(arr, arr.length); -} - -arr[4] = {e:0, a:4}; - -// GuardMultipleShapesToOffset (5) -for (let i=0; i<1000; i++) { - test(arr, arr.length); -} - -arr[5] = {f:0, a:4}; - -// GuardMultipleShapesToOffset (6) -for (let i=0; i<1000; i++) { - test(arr, arr.length); -} - -arr[6] = {g:0, a:4}; - -// GuardMultipleShapesToOffset (7) -for (let i=0; i<1000; i++) { - test(arr, arr.length); -} diff --git a/js/src/jit-test/tests/cacheir/ops-guard-shapelist-to-offset.js b/js/src/jit-test/tests/cacheir/ops-guard-shapelist-to-offset.js @@ -1,37 +0,0 @@ -function test() { - var arr = []; - var num = 2; - for (var i = 0; i < 20; i++) { - var obj = {}; - for (var j = 0; j < i+4; j++) { - obj["x_" + i + "_" + j] = 1; - } - arr.push(obj); - } - for (var i = 0; i < arr.length; i++) { - arr[i].a = 0; - arr[i].b = -i; - } - - for (let i=0; i<10000; i++) { - arr[i%num].a = i%num; - } - - var t = 0; - for (let i=0; i<10000; i++) { - t = arr[i%num].a; - } - - var t = 0; - for (let i=0; i<10000; i++) { - t = arr[i%num].a; - t = arr[i%num].b; - assertEq(t, -(i%num)) - } - - for (let i=0; i<num; i++) { - assertEq(arr[i].a, i); - } -} - -test(); diff --git a/js/src/jit-test/tests/cacheir/ops-using-from-offset.js b/js/src/jit-test/tests/cacheir/ops-using-from-offset.js @@ -1,29 +0,0 @@ -function test() { - var arr = []; - var num = 5; - for (var i = 0; i < 20; i++) { - var obj = {}; - for (var j = 0; j < i+4; j++) { - obj["x_" + i + "_" + j] = 1; - } - arr.push(obj); - } - for (var i = 0; i < arr.length; i++) { - arr[i].a = 0; - } - - for (let i=0; i<10000; i++) { - arr[i%num].a = i%num; - } - - var t = 0; - for (let i=0; i<10000; i++) { - t = arr[i%num].a; - } - - for (let i=0; i<num; i++) { - assertEq(arr[i].a, i); - } -} - -test(); diff --git a/js/src/jit-test/tests/cacheir/stored-dynamic-slot-from-offset.js b/js/src/jit-test/tests/cacheir/stored-dynamic-slot-from-offset.js @@ -1,34 +0,0 @@ -function test() { - var arr = []; - for (var i = 0; i < 20; i++) { - var obj = {}; - for (var j = 0; j < i+4; j++) { - obj["x_" + i + "_" + j] = 1; - } - arr.push(obj); - } - for (var i = 0; i < arr.length; i++) { - arr[i].a = 0; - } - - for (let i=0; i<10000; i++) { - arr[i%9].a = i%9; - } - - assertEq(arr[0].a, 0); - assertEq(arr[1].a, 1); - assertEq(arr[2].a, 2); - assertEq(arr[3].a, 3); - assertEq(arr[4].a, 4); - assertEq(arr[5].a, 5); - assertEq(arr[6].a, 6); - assertEq(arr[7].a, 7); - assertEq(arr[8].a, 8); - - for (let i=0; i<10000; i++) { - var a = arr[i%9].a; - assertEq(a, i%9); - } -} - -test(); diff --git a/js/src/jit-test/tests/cacheir/stored-fixed-slot-from-offset.js b/js/src/jit-test/tests/cacheir/stored-fixed-slot-from-offset.js @@ -1,28 +0,0 @@ -function test() { - var arr = []; - arr[0] = {a:0}; - arr[1] = {b:0, a:0}; - arr[2] = {c:0, a:0}; - arr[3] = {d:0, a:0}; - arr[4] = {e:0, a:0}; - arr[5] = {f:0, a:0}; - arr[6] = {g:0, a:0}; - arr[7] = {h:0, a:0}; - arr[8] = {i:0, a:0}; - - for (let i=0; i<10000; i++) { - arr[i%9].a = ((i%9)==8)?2:1; - } - - assertEq(arr[0].a, 1); - assertEq(arr[1].a, 1); - assertEq(arr[2].a, 1); - assertEq(arr[3].a, 1); - assertEq(arr[4].a, 1); - assertEq(arr[5].a, 1); - assertEq(arr[6].a, 1); - assertEq(arr[7].a, 1); - assertEq(arr[8].a, 2); -} - -test(); diff --git a/js/src/jit/CacheIR.h b/js/src/jit/CacheIR.h @@ -81,8 +81,6 @@ class ValOperandId : public OperandId { public: ValOperandId() = default; explicit ValOperandId(uint16_t id) : OperandId(id) {} - - bool operator==(const ValOperandId& other) const { return id_ == other.id_; } }; class ValueTagOperandId : public OperandId { diff --git a/js/src/jit/CacheIRCompiler.cpp b/js/src/jit/CacheIRCompiler.cpp @@ -1478,26 +1478,19 @@ bool CacheIRWriter::stubDataEquals(const uint8_t* stubData) const { return true; } -bool CacheIRWriter::stubDataEqualsIgnoringShapeAndOffset( - const uint8_t* stubData, uint32_t shapeFieldOffset, - mozilla::Maybe<uint32_t> offsetFieldOffset) const { +bool CacheIRWriter::stubDataEqualsIgnoring(const uint8_t* stubData, + uint32_t ignoreOffset) const { MOZ_ASSERT(!failed()); uint32_t offset = 0; for (const StubField& field : stubFields_) { - if (offset == shapeFieldOffset) { - // Don't compare shapeField. - } else if (offsetFieldOffset.isSome() && offset == *offsetFieldOffset) { - // Skip offsetField, the "FromOffset" variant doesn't have this. - continue; - } else { + if (offset != ignoreOffset) { if (field.sizeIsWord()) { uintptr_t raw = *reinterpret_cast<const uintptr_t*>(stubData + offset); if (field.asWord() != raw) { return false; } } else { - MOZ_ASSERT(field.sizeIsInt64()); uint64_t raw = *reinterpret_cast<const uint64_t*>(stubData + offset); if (field.asInt64() != raw) { return false; @@ -2562,58 +2555,6 @@ bool CacheIRCompiler::emitLoadFixedSlot(ValOperandId resultId, return true; } -bool CacheIRCompiler::emitLoadFixedSlotFromOffsetResult( - ObjOperandId objId, Int32OperandId offsetId) { - JitSpew(JitSpew_Codegen, "%s", __FUNCTION__); - AutoOutputRegister output(*this); - Register obj = allocator.useRegister(masm, objId); - Register offset = allocator.useRegister(masm, offsetId); - - // Load the value at the offset reg. - masm.loadValue(BaseIndex(obj, offset, TimesOne), output.valueReg()); - return true; -} - -bool CacheIRCompiler::emitStoreFixedSlotFromOffset(ObjOperandId objId, - Int32OperandId offsetId, - ValOperandId rhsId) { - JitSpew(JitSpew_Codegen, "%s", __FUNCTION__); - - AutoScratchRegister scratch(allocator, masm); - Register obj = allocator.useRegister(masm, objId); - Register offset = allocator.useRegister(masm, offsetId); - ValueOperand val = allocator.useValueRegister(masm, rhsId); - - BaseIndex slot(obj, offset, TimesOne); - EmitPreBarrier(masm, slot, MIRType::Value); - - masm.storeValue(val, slot); - - emitPostBarrierSlot(obj, val, scratch); - - return true; -} - -bool CacheIRCompiler::emitStoreDynamicSlotFromOffset(ObjOperandId objId, - Int32OperandId offsetId, - ValOperandId rhsId) { - JitSpew(JitSpew_Codegen, "%s", __FUNCTION__); - - Register obj = allocator.useRegister(masm, objId); - Register offset = allocator.useRegister(masm, offsetId); - ValueOperand val = allocator.useValueRegister(masm, rhsId); - - AutoScratchRegister slots(allocator, masm); - - masm.loadPtr(Address(obj, NativeObject::offsetOfSlots()), slots); - BaseIndex slot(slots, offset, TimesOne); - EmitPreBarrier(masm, slot, MIRType::Value); - masm.storeValue(val, slot); - - emitPostBarrierSlot(obj, val, /*scratch=*/slots); - return true; -} - bool CacheIRCompiler::emitLoadDynamicSlot(ValOperandId resultId, ObjOperandId objId, uint32_t slotOffset) { @@ -2632,20 +2573,6 @@ bool CacheIRCompiler::emitLoadDynamicSlot(ValOperandId resultId, return true; } -bool CacheIRCompiler::emitLoadDynamicSlotFromOffsetResult( - ObjOperandId objId, Int32OperandId offsetId) { - JitSpew(JitSpew_Codegen, "%s", __FUNCTION__); - AutoOutputRegister output(*this); - Register obj = allocator.useRegister(masm, objId); - Register offset = allocator.useRegister(masm, offsetId); - AutoScratchRegister scratch(allocator, masm); - - // obj->slots - masm.loadPtr(Address(obj, NativeObject::offsetOfSlots()), scratch); - masm.loadValue(BaseIndex(scratch, offset, TimesOne), output.valueReg()); - return true; -} - bool CacheIRCompiler::emitGuardIsNativeObject(ObjOperandId objId) { JitSpew(JitSpew_Codegen, "%s", __FUNCTION__); @@ -9992,39 +9919,8 @@ bool CacheIRCompiler::emitGuardMultipleShapes(ObjOperandId objId, emitLoadStubField(shapeArray, shapes); masm.loadPtr(Address(shapes, NativeObject::offsetOfElements()), shapes); - masm.branchTestObjShapeList(obj, shapes, scratch, scratch2, spectreScratch, - failure->label()); - return true; -} - -bool CacheIRCompiler::emitGuardMultipleShapesToOffset(ObjOperandId objId, - uint32_t shapesOffset, - Int32OperandId offsetId) { - JitSpew(JitSpew_Codegen, "%s", __FUNCTION__); - Register obj = allocator.useRegister(masm, objId); - Register offset = allocator.defineRegister(masm, offsetId); - AutoScratchRegister shapes(allocator, masm); - AutoScratchRegister scratch(allocator, masm); - AutoScratchRegister scratch2(allocator, masm); - - bool needSpectreMitigations = objectGuardNeedsSpectreMitigations(objId); - - // We can re-use the output (offset) as scratch spectre register, - // since the output is only set after all branches. - Register spectreScratch = needSpectreMitigations ? offset : InvalidReg; - - FailurePath* failure; - if (!addFailurePath(&failure)) { - return false; - } - - // The stub field contains a ListObject. Load its elements. - StubFieldOffset shapeArray(shapesOffset, StubField::Type::JSObject); - emitLoadStubField(shapeArray, shapes); - masm.loadPtr(Address(shapes, NativeObject::offsetOfElements()), shapes); - - masm.branchTestObjShapeListSetOffset(obj, shapes, offset, scratch, scratch2, - spectreScratch, failure->label()); + masm.branchTestObjShapeList(Assembler::NotEqual, obj, shapes, scratch, + scratch2, spectreScratch, failure->label()); return true; } diff --git a/js/src/jit/CacheIROps.yaml b/js/src/jit/CacheIROps.yaml @@ -248,16 +248,6 @@ obj: ObjId shapes: ObjectField -- name: GuardMultipleShapesToOffset - shared: true - transpile: true - cost_estimate: 2 - custom_writer: true - args: - obj: ObjId - shapes: ObjectField - result: Int32Id - - name: GuardProto shared: false transpile: true @@ -806,32 +796,6 @@ obj: ObjId offset: RawInt32Field -- name: LoadFixedSlotFromOffsetResult - shared: true - transpile: true - cost_estimate: 2 - args: - obj: ObjId - offset: Int32Id - -- name: StoreFixedSlotFromOffset - shared: true - transpile: true - cost_estimate: 6 - args: - obj: ObjId - offset: Int32Id - rhs: ValId - -- name: StoreDynamicSlotFromOffset - shared: true - transpile: true - cost_estimate: 6 - args: - obj: ObjId - offset: Int32Id - rhs: ValId - - name: LoadDynamicSlot shared: true transpile: true @@ -841,14 +805,6 @@ obj: ObjId slot: RawInt32Field -- name: LoadDynamicSlotFromOffsetResult - shared: true - transpile: true - cost_estimate: 2 - args: - obj: ObjId - offset: Int32Id - - name: GuardNoAllocationMetadataBuilder shared: true transpile: true diff --git a/js/src/jit/CacheIRWriter.h b/js/src/jit/CacheIRWriter.h @@ -393,9 +393,8 @@ class MOZ_RAII CacheIRWriter : public JS::CustomAutoRooter { size_t stubDataSize() const { return stubDataSize_; } void copyStubData(uint8_t* dest) const; bool stubDataEquals(const uint8_t* stubData) const; - bool stubDataEqualsIgnoringShapeAndOffset( - const uint8_t* stubData, uint32_t shapeFieldOffset, - mozilla::Maybe<uint32_t> offsetFieldOffset) const; + bool stubDataEqualsIgnoring(const uint8_t* stubData, + uint32_t ignoreOffset) const; bool operandIsDead(uint32_t operandId, uint32_t currentInstruction) const { if (operandId >= operandLastUsed_.length()) { @@ -730,12 +729,6 @@ class MOZ_RAII CacheIRWriter : public JS::CustomAutoRooter { guardMultipleShapes_(obj, shapes); } - Int32OperandId guardMultipleShapesToOffset(ObjOperandId obj, - ListObject* shapes) { - MOZ_ASSERT(shapes->length() > 0); - return guardMultipleShapesToOffset_(obj, shapes); - } - friend class CacheIRCloner; CACHE_IR_WRITER_GENERATED diff --git a/js/src/jit/CodeGenerator.cpp b/js/src/jit/CodeGenerator.cpp @@ -4300,16 +4300,6 @@ void CodeGenerator::visitLoadDynamicSlotV(LLoadDynamicSlotV* lir) { masm.loadValue(Address(base, offset), dest); } -void CodeGenerator::visitLoadDynamicSlotFromOffset( - LLoadDynamicSlotFromOffset* lir) { - ValueOperand dest = ToOutValue(lir); - Register slots = ToRegister(lir->slots()); - Register offset = ToRegister(lir->offset()); - - // slots[offset] - masm.loadValue(BaseIndex(slots, offset, TimesOne), dest); -} - static ConstantOrRegister ToConstantOrRegister(const LAllocation* value, MIRType valueType) { if (value->isConstant()) { @@ -4345,47 +4335,6 @@ void CodeGenerator::visitStoreDynamicSlotV(LStoreDynamicSlotV* lir) { masm.storeValue(value, Address(base, offset)); } -void CodeGenerator::visitStoreDynamicSlotFromOffsetV( - LStoreDynamicSlotFromOffsetV* lir) { - Register slots = ToRegister(lir->slots()); - Register offset = ToRegister(lir->offset()); - ValueOperand value = ToValue(lir->value()); - Register temp = ToRegister(lir->temp0()); - - BaseIndex baseIndex(slots, offset, TimesOne); - masm.computeEffectiveAddress(baseIndex, temp); - - Address address(temp, 0); - - emitPreBarrier(address); - - // obj->slots[offset] - masm.storeValue(value, address); -} - -void CodeGenerator::visitStoreDynamicSlotFromOffsetT( - LStoreDynamicSlotFromOffsetT* lir) { - Register slots = ToRegister(lir->slots()); - Register offset = ToRegister(lir->offset()); - const LAllocation* value = lir->value(); - MIRType valueType = lir->mir()->value()->type(); - Register temp = ToRegister(lir->temp0()); - - BaseIndex baseIndex(slots, offset, TimesOne); - masm.computeEffectiveAddress(baseIndex, temp); - - Address address(temp, 0); - - emitPreBarrier(address); - - // obj->slots[offset] - ConstantOrRegister nvalue = - value->isConstant() - ? ConstantOrRegister(value->toConstant()->toJSValue()) - : TypedOrValueRegister(valueType, ToAnyRegister(value)); - masm.storeConstantOrRegister(nvalue, address); -} - void CodeGenerator::visitElements(LElements* lir) { Address elements(ToRegister(lir->object()), NativeObject::offsetOfElements()); masm.loadPtr(elements, ToRegister(lir->output())); @@ -4547,7 +4496,8 @@ void CodeGenerator::visitGuardMultipleShapes(LGuardMultipleShapes* guard) { Label bail; masm.loadPtr(Address(shapeList, NativeObject::offsetOfElements()), temp); - masm.branchTestObjShapeList(obj, temp, temp2, temp3, spectre, &bail); + masm.branchTestObjShapeList(Assembler::NotEqual, obj, temp, temp2, temp3, + spectre, &bail); bailoutFrom(&bail, guard->snapshot()); } @@ -4589,73 +4539,6 @@ void CodeGenerator::visitGuardShapeList(LGuardShapeList* guard) { bailoutFrom(&bail, guard->snapshot()); } -void CodeGenerator::visitGuardShapeListToOffset( - LGuardShapeListToOffset* guard) { - Register obj = ToRegister(guard->object()); - Register temp = ToRegister(guard->temp0()); - Register spectre = ToTempRegisterOrInvalid(guard->temp1()); - Register offset = ToRegister(guard->output()); - - Label done, bail; - masm.loadObjShapeUnsafe(obj, temp); - - // Count the number of branches to emit. - const auto& shapes = guard->mir()->shapeList()->shapes(); - const auto& offsets = guard->mir()->shapeList()->offsets(); - size_t branchesLeft = std::count_if(shapes.begin(), shapes.end(), - [](Shape* s) { return s != nullptr; }); - MOZ_RELEASE_ASSERT(branchesLeft > 0); - - size_t index = 0; - for (Shape* shape : shapes) { - if (!shape) { - index++; - continue; - } - - if (branchesLeft > 1) { - Label next; - masm.branchPtr(Assembler::NotEqual, temp, ImmGCPtr(shape), &next); - if (spectre != InvalidReg) { - masm.spectreMovePtr(Assembler::NotEqual, spectre, obj); - } - masm.move32(Imm32(offsets[index]), offset); - masm.jump(&done); - masm.bind(&next); - } else { - masm.branchPtr(Assembler::NotEqual, temp, ImmGCPtr(shape), &bail); - if (spectre != InvalidReg) { - masm.spectreMovePtr(Assembler::NotEqual, spectre, obj); - } - masm.move32(Imm32(offsets[index]), offset); - } - - branchesLeft--; - index++; - } - MOZ_ASSERT(branchesLeft == 0); - - masm.bind(&done); - bailoutFrom(&bail, guard->snapshot()); -} - -void CodeGenerator::visitGuardMultipleShapesToOffset( - LGuardMultipleShapesToOffset* guard) { - Register obj = ToRegister(guard->object()); - Register shapeList = ToRegister(guard->shapeList()); - Register temp = ToRegister(guard->temp0()); - Register temp1 = ToRegister(guard->temp1()); - Register temp2 = ToRegister(guard->temp2()); - Register offset = ToRegister(guard->output()); - Register spectre = JitOptions.spectreObjectMitigations ? offset : InvalidReg; - - Label bail; - masm.loadPtr(Address(shapeList, NativeObject::offsetOfElements()), temp); - masm.branchTestObjShapeListSetOffset(obj, temp, offset, temp1, temp2, spectre, - &bail); - bailoutFrom(&bail, guard->snapshot()); -} - void CodeGenerator::visitGuardProto(LGuardProto* guard) { Register obj = ToRegister(guard->object()); Register expected = ToRegister(guard->expected()); @@ -18042,59 +17925,6 @@ void CodeGenerator::visitLoadFixedSlotT(LLoadFixedSlotT* ins) { type, result); } -void CodeGenerator::visitLoadFixedSlotFromOffset( - LLoadFixedSlotFromOffset* lir) { - Register obj = ToRegister(lir->object()); - Register offset = ToRegister(lir->offset()); - ValueOperand out = ToOutValue(lir); - - // obj[offset] - masm.loadValue(BaseIndex(obj, offset, TimesOne), out); -} - -void CodeGenerator::visitStoreFixedSlotFromOffsetV( - LStoreFixedSlotFromOffsetV* lir) { - Register obj = ToRegister(lir->object()); - Register offset = ToRegister(lir->offset()); - ValueOperand value = ToValue(lir->value()); - Register temp = ToRegister(lir->temp0()); - - BaseIndex baseIndex(obj, offset, TimesOne); - masm.computeEffectiveAddress(baseIndex, temp); - - Address slot(temp, 0); - if (lir->mir()->needsBarrier()) { - emitPreBarrier(slot); - } - - // obj[offset] - masm.storeValue(value, slot); -} - -void CodeGenerator::visitStoreFixedSlotFromOffsetT( - LStoreFixedSlotFromOffsetT* lir) { - Register obj = ToRegister(lir->object()); - Register offset = ToRegister(lir->offset()); - const LAllocation* value = lir->value(); - MIRType valueType = lir->mir()->value()->type(); - Register temp = ToRegister(lir->temp0()); - - BaseIndex baseIndex(obj, offset, TimesOne); - masm.computeEffectiveAddress(baseIndex, temp); - - Address slot(temp, 0); - if (lir->mir()->needsBarrier()) { - emitPreBarrier(slot); - } - - // obj[offset] - ConstantOrRegister nvalue = - value->isConstant() - ? ConstantOrRegister(value->toConstant()->toJSValue()) - : TypedOrValueRegister(valueType, ToAnyRegister(value)); - masm.storeConstantOrRegister(nvalue, slot); -} - template <typename T> static void EmitLoadAndUnbox(MacroAssembler& masm, const T& src, MIRType type, bool fallible, AnyRegister dest, Register64 temp, diff --git a/js/src/jit/GenerateMIRFiles.py b/js/src/jit/GenerateMIRFiles.py @@ -59,8 +59,6 @@ type_policies = { "Double": "DoublePolicy", "String": "StringPolicy", "Symbol": "SymbolPolicy", - "NoTypePolicy": "NoTypePolicy", - "Slots": "NoTypePolicy", } @@ -68,22 +66,16 @@ def decide_type_policy(types, no_type_policy): if no_type_policy: return "public NoTypePolicy::Data" + if len(types) == 1: + return f"public {type_policies[types[0]]}<0>::Data" + type_num = 0 mixed_type_policies = [] for mir_type in types: policy = type_policies[mir_type] - if policy == "NoTypePolicy": - type_num += 1 - continue mixed_type_policies.append(f"{policy}<{type_num}>") type_num += 1 - if len(mixed_type_policies) == 0: - return "public NoTypePolicy::Data" - - if len(mixed_type_policies) == 1: - return f"public {mixed_type_policies[0]}::Data" - return "public MixPolicy<{}>::Data".format(", ".join(mixed_type_policies)) diff --git a/js/src/jit/JitScript.cpp b/js/src/jit/JitScript.cpp @@ -1024,22 +1024,6 @@ HashNumber ICScript::hash(JSContext* cx) { } break; } - case CacheOp::GuardMultipleShapesToOffset: { - auto args = reader.argsForGuardMultipleShapesToOffset(); - JSObject* shapes = - stubInfo->getStubField<StubField::Type::JSObject>( - stub->toCacheIRStub(), args.shapesOffset); - auto* shapesObject = &shapes->as<ShapeListWithOffsetsObject>(); - size_t numShapes = shapesObject->numShapes(); - if (ShapeListSnapshot::shouldSnapshot(numShapes)) { - for (size_t i = 0; i < numShapes; i++) { - Shape* shape = shapesObject->getShapeUnbarriered(i); - h = mozilla::AddToHash(h, shape); - h = mozilla::AddToHash(h, shapesObject->getOffset(i)); - } - } - break; - } default: reader.skip(CacheIROpInfos[size_t(op)].argLength); break; diff --git a/js/src/jit/JitSpewer.cpp b/js/src/jit/JitSpewer.cpp @@ -338,8 +338,6 @@ static void PrintHelpAndExit(int status = 0) { " gcbarriers Redundant GC barrier elimination\n" " loadkeys Loads used as property keys\n" " stubfolding CacheIR stub folding\n" - " stubfolding-details Same as stubfolding, but with spewing of stub " - "content.\n" " logs JSON visualization logging\n" " logs-sync Same as logs, but flushes between each pass (sync. " "compiled functions only).\n" @@ -448,9 +446,6 @@ void jit::CheckLogging() { EnableChannel(JitSpew_MarkLoadsUsedAsPropertyKeys); } else if (IsFlag(found, "stubfolding")) { EnableChannel(JitSpew_StubFolding); - } else if (IsFlag(found, "stubfolding-details")) { - EnableChannel(JitSpew_StubFolding); - EnableChannel(JitSpew_StubFoldingDetails); } else if (IsFlag(found, "logs")) { EnableIonDebugAsyncLogging(); } else if (IsFlag(found, "logs-sync")) { diff --git a/js/src/jit/JitSpewer.h b/js/src/jit/JitSpewer.h @@ -24,107 +24,105 @@ namespace js { namespace jit { // New channels may be added below. -#define JITSPEW_CHANNEL_LIST(_) \ - /* Information during sinking */ \ - _(Prune) \ - /* Information during escape analysis */ \ - _(Escape) \ - /* Information during alias analysis */ \ - _(Alias) \ - /* Information during alias analysis */ \ - _(AliasSummaries) \ - /* Information during GVN */ \ - _(GVN) \ - /* Information during sinking */ \ - _(Sink) \ - /* Information during Range analysis */ \ - _(Range) \ - /* Information during LICM */ \ - _(LICM) \ - /* Information during Branch Hinting */ \ - _(BranchHint) \ - /* Info about fold linear constants */ \ - _(FLAC) \ - /* Effective address analysis info */ \ - _(EAA) \ - /* Wasm Bounds Check Elimination */ \ - _(WasmBCE) \ - /* Information during regalloc */ \ - _(RegAlloc) \ - /* Information during inlining */ \ - _(Inlining) \ - /* Information during codegen */ \ - _(Codegen) \ - /* Debug info about safepoints */ \ - _(Safepoints) \ - /* Debug info about Pools*/ \ - _(Pools) \ - /* Profiling-related information */ \ - _(Profiling) \ - /* Debug info about the I$ */ \ - _(CacheFlush) \ - /* Info about redundant shape guards */ \ - _(RedundantShapeGuards) \ - /* Info about redundant GC barriers */ \ - _(RedundantGCBarriers) \ - /* Info about loads used as keys */ \ - _(MarkLoadsUsedAsPropertyKeys) \ - /* Output a list of MIR expressions */ \ - _(MIRExpressions) \ - /* Summary info about loop unrolling */ \ - _(Unroll) \ - /* Detailed info about loop unrolling */ \ - _(UnrollDetails) \ - /* Information about stub folding */ \ - _(StubFolding) \ - /* Additional information about stub folding */ \ - _(StubFoldingDetails) \ - \ - /* BASELINE COMPILER SPEW */ \ - \ - /* Aborting Script Compilation. */ \ - _(BaselineAbort) \ - /* Script Compilation. */ \ - _(BaselineScripts) \ - /* Detailed op-specific spew. */ \ - _(BaselineOp) \ - /* Inline caches. */ \ - _(BaselineIC) \ - /* Inline cache fallbacks. */ \ - _(BaselineICFallback) \ - /* OSR from Baseline => Ion. */ \ - _(BaselineOSR) \ - /* Bailouts. */ \ - _(BaselineBailouts) \ - /* Debug Mode On Stack Recompile . */ \ - _(BaselineDebugModeOSR) \ - \ - /* ION COMPILER SPEW */ \ - \ - /* Used to abort SSA construction */ \ - _(IonAbort) \ - /* Information about compiled scripts */ \ - _(IonScripts) \ - /* Info about failing to log script */ \ - _(IonSyncLogs) \ - /* Information during MIR building */ \ - _(IonMIR) \ - /* Information during bailouts */ \ - _(IonBailouts) \ - /* Information during OSI */ \ - _(IonInvalidate) \ - /* Debug info about snapshots */ \ - _(IonSnapshots) \ - /* Generated inline cache stubs */ \ - _(IonIC) \ - \ - /* WARP SPEW */ \ - \ - /* Generated WarpSnapshots */ \ - _(WarpSnapshots) \ - /* CacheIR transpiler logging */ \ - _(WarpTranspiler) \ - /* Trial inlining for Warp */ \ +#define JITSPEW_CHANNEL_LIST(_) \ + /* Information during sinking */ \ + _(Prune) \ + /* Information during escape analysis */ \ + _(Escape) \ + /* Information during alias analysis */ \ + _(Alias) \ + /* Information during alias analysis */ \ + _(AliasSummaries) \ + /* Information during GVN */ \ + _(GVN) \ + /* Information during sinking */ \ + _(Sink) \ + /* Information during Range analysis */ \ + _(Range) \ + /* Information during LICM */ \ + _(LICM) \ + /* Information during Branch Hinting */ \ + _(BranchHint) \ + /* Info about fold linear constants */ \ + _(FLAC) \ + /* Effective address analysis info */ \ + _(EAA) \ + /* Wasm Bounds Check Elimination */ \ + _(WasmBCE) \ + /* Information during regalloc */ \ + _(RegAlloc) \ + /* Information during inlining */ \ + _(Inlining) \ + /* Information during codegen */ \ + _(Codegen) \ + /* Debug info about safepoints */ \ + _(Safepoints) \ + /* Debug info about Pools*/ \ + _(Pools) \ + /* Profiling-related information */ \ + _(Profiling) \ + /* Debug info about the I$ */ \ + _(CacheFlush) \ + /* Info about redundant shape guards */ \ + _(RedundantShapeGuards) \ + /* Info about redundant GC barriers */ \ + _(RedundantGCBarriers) \ + /* Info about loads used as keys */ \ + _(MarkLoadsUsedAsPropertyKeys) \ + /* Output a list of MIR expressions */ \ + _(MIRExpressions) \ + /* Summary info about loop unrolling */ \ + _(Unroll) \ + /* Detailed info about loop unrolling */ \ + _(UnrollDetails) \ + /* Information about stub folding */ \ + _(StubFolding) \ + \ + /* BASELINE COMPILER SPEW */ \ + \ + /* Aborting Script Compilation. */ \ + _(BaselineAbort) \ + /* Script Compilation. */ \ + _(BaselineScripts) \ + /* Detailed op-specific spew. */ \ + _(BaselineOp) \ + /* Inline caches. */ \ + _(BaselineIC) \ + /* Inline cache fallbacks. */ \ + _(BaselineICFallback) \ + /* OSR from Baseline => Ion. */ \ + _(BaselineOSR) \ + /* Bailouts. */ \ + _(BaselineBailouts) \ + /* Debug Mode On Stack Recompile . */ \ + _(BaselineDebugModeOSR) \ + \ + /* ION COMPILER SPEW */ \ + \ + /* Used to abort SSA construction */ \ + _(IonAbort) \ + /* Information about compiled scripts */ \ + _(IonScripts) \ + /* Info about failing to log script */ \ + _(IonSyncLogs) \ + /* Information during MIR building */ \ + _(IonMIR) \ + /* Information during bailouts */ \ + _(IonBailouts) \ + /* Information during OSI */ \ + _(IonInvalidate) \ + /* Debug info about snapshots */ \ + _(IonSnapshots) \ + /* Generated inline cache stubs */ \ + _(IonIC) \ + \ + /* WARP SPEW */ \ + \ + /* Generated WarpSnapshots */ \ + _(WarpSnapshots) \ + /* CacheIR transpiler logging */ \ + _(WarpTranspiler) \ + /* Trial inlining for Warp */ \ _(WarpTrialInlining) enum JitSpewChannel { diff --git a/js/src/jit/LIROps.yaml b/js/src/jit/LIROps.yaml @@ -2598,22 +2598,6 @@ value: WordSized mir_op: StoreFixedSlot -- name: StoreFixedSlotFromOffsetV - operands: - object: WordSized - offset: WordSized - value: BoxedValue - num_temps: 1 - mir_op: StoreFixedSlotFromOffset - -- name: StoreFixedSlotFromOffsetT - operands: - object: WordSized - offset: WordSized - value: WordSized - num_temps: 1 - mir_op: StoreFixedSlotFromOffset - - name: GetPropSuperCache result_type: BoxedValue operands: @@ -2666,22 +2650,6 @@ value: WordSized mir_op: StoreDynamicSlot -- name: StoreDynamicSlotFromOffsetV - operands: - slots: WordSized - offset: WordSized - value: BoxedValue - num_temps: 1 - mir_op: StoreDynamicSlotFromOffset - -- name: StoreDynamicSlotFromOffsetT - operands: - slots: WordSized - offset: WordSized - value: WordSized - num_temps: 1 - mir_op: StoreDynamicSlotFromOffset - # Take the floor of a double precision number and converts it to an int32. # Implements Math.floor(). - name: Floor @@ -3066,14 +3034,6 @@ num: WordSized mir_op: true -- name: GuardMultipleShapesToOffset - result_type: WordSized - operands: - object: WordSized - shapeList: WordSized - num_temps: 3 - mir_op: true - - name: GuardProto operands: object: WordSized diff --git a/js/src/jit/Lowering.cpp b/js/src/jit/Lowering.cpp @@ -4207,15 +4207,6 @@ void LIRGenerator::visitLoadDynamicSlot(MLoadDynamicSlot* ins) { } } -void LIRGenerator::visitLoadDynamicSlotFromOffset( - MLoadDynamicSlotFromOffset* ins) { - MOZ_ASSERT(ins->slots()->type() == MIRType::Slots); - - auto* lir = new (alloc()) LLoadDynamicSlotFromOffset( - useRegisterAtStart(ins->slots()), useRegisterAtStart(ins->offset())); - defineBox(lir, ins); -} - void LIRGenerator::visitFunctionEnvironment(MFunctionEnvironment* ins) { define(new (alloc()) LFunctionEnvironment(useRegisterAtStart(ins->function())), @@ -5505,16 +5496,6 @@ void LIRGenerator::visitLoadFixedSlot(MLoadFixedSlot* ins) { } } -void LIRGenerator::visitLoadFixedSlotFromOffset(MLoadFixedSlotFromOffset* ins) { - MDefinition* obj = ins->object(); - MOZ_ASSERT(obj->type() == MIRType::Object); - MOZ_ASSERT(ins->type() == MIRType::Value); - - auto* lir = new (alloc()) LLoadFixedSlotFromOffset( - useRegisterAtStart(obj), useRegisterAtStart(ins->offset())); - defineBox(lir, ins); -} - void LIRGenerator::visitLoadFixedSlotAndUnbox(MLoadFixedSlotAndUnbox* ins) { MDefinition* obj = ins->object(); MOZ_ASSERT(obj->type() == MIRType::Object); @@ -5639,40 +5620,6 @@ void LIRGenerator::visitStoreFixedSlot(MStoreFixedSlot* ins) { } } -void LIRGenerator::visitStoreFixedSlotFromOffset( - MStoreFixedSlotFromOffset* ins) { - MOZ_ASSERT(ins->object()->type() == MIRType::Object); - - if (ins->value()->type() == MIRType::Value) { - LStoreFixedSlotFromOffsetV* lir = new (alloc()) LStoreFixedSlotFromOffsetV( - useRegister(ins->object()), useRegister(ins->offset()), - useBox(ins->value()), temp()); - add(lir, ins); - } else { - LStoreFixedSlotFromOffsetT* lir = new (alloc()) LStoreFixedSlotFromOffsetT( - useRegister(ins->object()), useRegister(ins->offset()), - useRegisterOrConstant(ins->value()), temp()); - add(lir, ins); - } -} - -void LIRGenerator::visitStoreDynamicSlotFromOffset( - MStoreDynamicSlotFromOffset* ins) { - MOZ_ASSERT(ins->slots()->type() == MIRType::Slots); - - if (ins->value()->type() == MIRType::Value) { - auto* lir = new (alloc()) LStoreDynamicSlotFromOffsetV( - useRegister(ins->slots()), useRegister(ins->offset()), - useBox(ins->value()), temp()); - add(lir, ins); - } else { - auto* lir = new (alloc()) LStoreDynamicSlotFromOffsetT( - useRegister(ins->slots()), useRegister(ins->offset()), - useRegisterOrConstant(ins->value()), temp()); - add(lir, ins); - } -} - void LIRGenerator::visitGetNameCache(MGetNameCache* ins) { MOZ_ASSERT(ins->envObj()->type() == MIRType::Object); @@ -5878,33 +5825,6 @@ void LIRGenerator::visitGuardShapeList(MGuardShapeList* ins) { } } -void LIRGenerator::visitGuardShapeListToOffset(MGuardShapeListToOffset* ins) { - MOZ_ASSERT(ins->object()->type() == MIRType::Object); - - if (JitOptions.spectreObjectMitigations) { - auto* lir = new (alloc()) - LGuardShapeListToOffset(useRegister(ins->object()), temp(), temp()); - assignSnapshot(lir, ins->bailoutKind()); - define(lir, ins); - } else { - auto* lir = new (alloc()) LGuardShapeListToOffset( - useRegister(ins->object()), temp(), LDefinition::BogusTemp()); - assignSnapshot(lir, ins->bailoutKind()); - define(lir, ins); - } -} - -void LIRGenerator::visitGuardMultipleShapesToOffset( - MGuardMultipleShapesToOffset* ins) { - MOZ_ASSERT(ins->object()->type() == MIRType::Object); - - auto* lir = new (alloc()) LGuardMultipleShapesToOffset( - useRegister(ins->object()), useRegister(ins->shapeList()), temp(), temp(), - temp()); - assignSnapshot(lir, ins->bailoutKind()); - define(lir, ins); -} - void LIRGenerator::visitGuardProto(MGuardProto* ins) { MOZ_ASSERT(ins->object()->type() == MIRType::Object); MOZ_ASSERT(ins->expected()->type() == MIRType::Object); diff --git a/js/src/jit/MIR.cpp b/js/src/jit/MIR.cpp @@ -570,14 +570,6 @@ const MDefinition* MDefinition::skipObjectGuards() const { result = result->toGuardMultipleShapes()->object(); continue; } - if (result->isGuardShapeListToOffset()) { - result = result->toGuardShapeListToOffset()->object(); - continue; - } - if (result->isGuardMultipleShapesToOffset()) { - result = result->toGuardMultipleShapesToOffset()->object(); - continue; - } if (result->isGuardNullProto()) { result = result->toGuardNullProto()->object(); continue; @@ -7233,28 +7225,6 @@ AliasSet MGuardShapeList::getAliasSet() const { return AliasSet::Load(AliasSet::ObjectFields); } -bool MGuardShapeListToOffset::congruentTo(const MDefinition* ins) const { - if (!congruentIfOperandsEqual(ins)) { - return false; - } - - const auto& shapesA = this->shapeList()->shapes(); - const auto& shapesB = ins->toGuardShapeListToOffset()->shapeList()->shapes(); - if (!std::equal(shapesA.begin(), shapesA.end(), shapesB.begin(), - shapesB.end())) - return false; - - const auto& offsetsA = this->shapeList()->offsets(); - const auto& offsetsB = - ins->toGuardShapeListToOffset()->shapeList()->offsets(); - return std::equal(offsetsA.begin(), offsetsA.end(), offsetsB.begin(), - offsetsB.end()); -} - -AliasSet MGuardShapeListToOffset::getAliasSet() const { - return AliasSet::Load(AliasSet::ObjectFields); -} - bool MHasShape::congruentTo(const MDefinition* ins) const { if (!ins->isHasShape()) { return false; @@ -7337,19 +7307,6 @@ AliasSet MGuardMultipleShapes::getAliasSet() const { return AliasSet::Load(AliasSet::ObjectFields); } -AliasSet MGuardMultipleShapesToOffset::getAliasSet() const { - return AliasSet::Load(AliasSet::ObjectFields); -} - -AliasSet MLoadFixedSlotFromOffset::getAliasSet() const { - return AliasSet::Load(AliasSet::FixedSlot); -} - -AliasSet MLoadDynamicSlotFromOffset::getAliasSet() const { - MOZ_ASSERT(slots()->type() == MIRType::Slots); - return AliasSet::Load(AliasSet::DynamicSlot); -} - AliasSet MGuardGlobalGeneration::getAliasSet() const { return AliasSet::Load(AliasSet::GlobalGenerationCounter); } diff --git a/js/src/jit/MIR.h b/js/src/jit/MIR.h @@ -75,7 +75,6 @@ namespace jit { class CallInfo; class ShapeListSnapshot; -class ShapeListWithOffsetsSnapshot; #ifdef JS_JITSPEW // Helper for debug printing. Avoids creating a MIR.h <--> MIRGraph.h cycle. @@ -7962,41 +7961,6 @@ class MStoreFixedSlot ALLOW_CLONE(MStoreFixedSlot) }; -class MStoreFixedSlotFromOffset - : public MTernaryInstruction, - public MixPolicy<ObjectPolicy<0>, UnboxedInt32Policy<1>, - NoFloatPolicy<2>>::Data { - bool needsBarrier_; - - MStoreFixedSlotFromOffset(MDefinition* obj, MDefinition* offset, - MDefinition* rval, bool barrier) - : MTernaryInstruction(classOpcode, obj, offset, rval), - needsBarrier_(barrier) { - MOZ_ASSERT(obj->type() == MIRType::Object); - } - - public: - INSTRUCTION_HEADER(StoreFixedSlotFromOffset) - NAMED_OPERANDS((0, object), (1, offset), (2, value)) - - static MStoreFixedSlotFromOffset* NewBarriered(TempAllocator& alloc, - MDefinition* obj, - MDefinition* offset, - MDefinition* rval) { - return new (alloc) MStoreFixedSlotFromOffset(obj, offset, rval, true); - } - - AliasSet getAliasSet() const override { - return AliasSet::Store(AliasSet::FixedSlot); - } - bool needsBarrier() const { return needsBarrier_; } - void setNeedsBarrier(bool needsBarrier = true) { - needsBarrier_ = needsBarrier; - } - - ALLOW_CLONE(MStoreFixedSlotFromOffset) -}; - class MGetPropertyCache : public MBinaryInstruction, public MixPolicy<BoxExceptPolicy<0, MIRType::Object>, CacheIdPolicy<1>>::Data { @@ -8429,33 +8393,6 @@ class MStoreDynamicSlot : public MBinaryInstruction, ALLOW_CLONE(MStoreDynamicSlot) }; -class MStoreDynamicSlotFromOffset - : public MTernaryInstruction, - public MixPolicy<UnboxedInt32Policy<1>, NoFloatPolicy<2>>::Data { - MStoreDynamicSlotFromOffset(MDefinition* slots, MDefinition* offset, - MDefinition* rval, bool barrier) - : MTernaryInstruction(classOpcode, slots, offset, rval) { - MOZ_ASSERT(slots->type() == MIRType::Slots); - } - - public: - INSTRUCTION_HEADER(StoreDynamicSlotFromOffset) - NAMED_OPERANDS((0, slots), (1, offset), (2, value)) - - static MStoreDynamicSlotFromOffset* New(TempAllocator& alloc, - MDefinition* slots, - MDefinition* offset, - MDefinition* rval) { - return new (alloc) MStoreDynamicSlotFromOffset(slots, offset, rval, true); - } - - AliasSet getAliasSet() const override { - return AliasSet::Store(AliasSet::DynamicSlot); - } - - ALLOW_CLONE(MStoreDynamicSlotFromOffset) -}; - class MSetPropertyCache : public MTernaryInstruction, public MixPolicy<SingleObjectPolicy, CacheIdPolicy<1>, NoFloatPolicy<2>>::Data { diff --git a/js/src/jit/MIROps.yaml b/js/src/jit/MIROps.yaml @@ -2130,16 +2130,6 @@ - name: LoadFixedSlot gen_boilerplate: false -- name: LoadFixedSlotFromOffset - operands: - object: Object - offset: Int32 - result_type: Value - movable: true - congruent_to: if_operands_equal - alias_set: custom - generate_lir: true - - name: LoadFixedSlotAndUnbox gen_boilerplate: false @@ -2149,12 +2139,6 @@ - name: StoreFixedSlot gen_boilerplate: false -- name: StoreFixedSlotFromOffset - gen_boilerplate: false - -- name: StoreDynamicSlotFromOffset - gen_boilerplate: false - - name: GetPropertyCache gen_boilerplate: false @@ -2254,29 +2238,6 @@ generate_lir: true lir_temps: 2 -- name: GuardShapeListToOffset - operands: - object: Object - arguments: - shapeList: const ShapeListWithOffsetsSnapshot* - result_type: Int32 - guard: true - movable: true - congruent_to: custom - alias_set: custom - generate_lir: true - lir_temps: 2 - -- name: GuardMultipleShapesToOffset - operands: - object: Object - shapeList: Object - result_type: Int32 - guard: true - movable: true - congruent_to: if_operands_equal - alias_set: custom - - name: GuardProto gen_boilerplate: false @@ -2720,16 +2681,6 @@ - name: LoadDynamicSlot gen_boilerplate: false -- name: LoadDynamicSlotFromOffset - operands: - slots: Slots - offset: Int32 - result_type: Value - movable: true - congruent_to: if_operands_equal - alias_set: custom - generate_lir: true - # Inline call to access a function's environment (scope chain). - name: FunctionEnvironment operands: diff --git a/js/src/jit/MacroAssembler.cpp b/js/src/jit/MacroAssembler.cpp @@ -5763,13 +5763,16 @@ void MacroAssembler::branchTestType(Condition cond, Register tag, } } -void MacroAssembler::branchTestObjShapeListImpl( - Register obj, Register shapeElements, size_t itemSize, - Register shapeScratch, Register endScratch, Register spectreScratch, - Label* fail) { +void MacroAssembler::branchTestObjShapeList( + Condition cond, Register obj, Register shapeElements, Register shapeScratch, + Register endScratch, Register spectreScratch, Label* label) { + MOZ_ASSERT(cond == Assembler::Equal || cond == Assembler::NotEqual); + bool needSpectreMitigations = spectreScratch != InvalidReg; Label done; + Label* onMatch = cond == Assembler::Equal ? label : &done; + Label* onNoMatch = cond == Assembler::Equal ? &done : label; // Load the object's shape pointer into shapeScratch, and prepare to compare // it with the shapes in the list. The shapes are stored as private values so @@ -5780,7 +5783,7 @@ void MacroAssembler::branchTestObjShapeListImpl( Address lengthAddr(shapeElements, ObjectElements::offsetOfInitializedLength()); load32(lengthAddr, endScratch); - branch32(Assembler::Equal, endScratch, Imm32(0), fail); + branch32(Assembler::Equal, endScratch, Imm32(0), onNoMatch); BaseObjectElementIndex endPtrAddr(shapeElements, endScratch); computeEffectiveAddress(endPtrAddr, endScratch); @@ -5794,38 +5797,21 @@ void MacroAssembler::branchTestObjShapeListImpl( if (needSpectreMitigations) { move32(Imm32(0), spectreScratch); } - branchPtr(Assembler::Equal, Address(shapeElements, 0), shapeScratch, &done); + branchPtr(Assembler::Equal, Address(shapeElements, 0), shapeScratch, onMatch); if (needSpectreMitigations) { spectreMovePtr(Assembler::Equal, spectreScratch, obj); } // Advance to next shape and loop if not finished. - addPtr(Imm32(itemSize), shapeElements); + addPtr(Imm32(sizeof(Value)), shapeElements); branchPtr(Assembler::Below, shapeElements, endScratch, &loop); - jump(fail); + if (cond == Assembler::NotEqual) { + jump(label); + } bind(&done); } -void MacroAssembler::branchTestObjShapeList( - Register obj, Register shapeElements, Register shapeScratch, - Register endScratch, Register spectreScratch, Label* fail) { - branchTestObjShapeListImpl(obj, shapeElements, sizeof(Value), shapeScratch, - endScratch, spectreScratch, fail); -} - -void MacroAssembler::branchTestObjShapeListSetOffset( - Register obj, Register shapeElements, Register offset, - Register shapeScratch, Register endScratch, Register spectreScratch, - Label* fail) { - branchTestObjShapeListImpl(obj, shapeElements, 2 * sizeof(Value), - shapeScratch, endScratch, spectreScratch, fail); - - // The shapeElements register points to the matched shape (if found). - // The corresponding offset is saved in the array as the next value. - load32(Address(shapeElements, sizeof(Value)), offset); -} - void MacroAssembler::branchTestObjCompartment(Condition cond, Register obj, const Address& compartment, Register scratch, Label* label) { diff --git a/js/src/jit/MacroAssembler.h b/js/src/jit/MacroAssembler.h @@ -1843,21 +1843,10 @@ class MacroAssembler : public MacroAssemblerSpecific { const Shape* shape, Label* label); - private: - void branchTestObjShapeListImpl(Register obj, Register shapeElements, - size_t itemSize, Register shapeScratch, - Register endScratch, Register spectreScratch, - Label* fail); - - public: - void branchTestObjShapeList(Register obj, Register shapeElements, - Register shapeScratch, Register endScratch, - Register spectreScratch, Label* fail); - - void branchTestObjShapeListSetOffset(Register obj, Register shapeElements, - Register offset, Register shapeScratch, - Register endScratch, - Register spectreScratch, Label* fail); + void branchTestObjShapeList(Condition cond, Register obj, + Register shapeElements, Register shapeScratch, + Register endScratch, Register spectreScratch, + Label* label); inline void branchTestClassIsFunction(Condition cond, Register clasp, Label* label); diff --git a/js/src/jit/ShapeList.cpp b/js/src/jit/ShapeList.cpp @@ -94,99 +94,3 @@ bool ShapeListObject::traceWeak(JSTracer* trc) { return length != 0; } - -const JSClass ShapeListWithOffsetsObject::class_ = { - "JIT ShapeList", - 0, - &classOps_, -}; - -const JSClassOps ShapeListWithOffsetsObject::classOps_ = { - nullptr, // addProperty - nullptr, // delProperty - nullptr, // enumerate - nullptr, // newEnumerate - nullptr, // resolve - nullptr, // mayResolve - nullptr, // finalize - nullptr, // call - nullptr, // construct - ShapeListWithOffsetsObject::trace, // trace -}; - -/* static */ ShapeListWithOffsetsObject* ShapeListWithOffsetsObject::create( - JSContext* cx) { - NativeObject* obj = NewTenuredObjectWithGivenProto(cx, &class_, nullptr); - if (!obj) { - return nullptr; - } - - // Register this object so the GC can sweep its weak pointers. - if (!cx->zone()->registerObjectWithWeakPointers(obj)) { - ReportOutOfMemory(cx); - return nullptr; - } - - return &obj->as<ShapeListWithOffsetsObject>(); -} - -Shape* ShapeListWithOffsetsObject::getShape(uint32_t index) const { - Shape* shape = getShapeUnbarriered(index); - gc::ReadBarrier(shape); - return shape; -} - -Shape* ShapeListWithOffsetsObject::getShapeUnbarriered(uint32_t index) const { - Value value = ListObject::get(index * 2); - return static_cast<Shape*>(value.toPrivate()); -} - -uint32_t ShapeListWithOffsetsObject::getOffset(uint32_t index) const { - Value value = ListObject::get(index * 2 + 1); - return value.toPrivateUint32(); -} - -uint32_t ShapeListWithOffsetsObject::numShapes() const { - MOZ_ASSERT(length() % 2 == 0); - return length() / 2; -}; - -void ShapeListWithOffsetsObject::trace(JSTracer* trc, JSObject* obj) { - if (trc->traceWeakEdges()) { - obj->as<ShapeListWithOffsetsObject>().traceWeak(trc); - } -} - -bool ShapeListWithOffsetsObject::traceWeak(JSTracer* trc) { - uint32_t length = getDenseInitializedLength(); - if (length == 0) { - return false; // Object may be uninitialized. - } - - const HeapSlot* src = elements_; - const HeapSlot* end = src + length; - HeapSlot* dst = elements_; - while (src != end) { - Shape* shape = static_cast<Shape*>(src[0].toPrivate()); - uint32_t offset = src[1].toPrivateUint32(); - MOZ_ASSERT(shape->is<Shape>()); - if (TraceManuallyBarrieredWeakEdge(trc, &shape, - "ShapeListWithOffsetsObject shape")) { - dst[0].unbarrieredSet(PrivateValue(shape)); - dst[1].unbarrieredSet(PrivateUint32Value(offset)); - dst += 2; - } - src += 2; - } - - MOZ_ASSERT(dst <= end); - uint32_t newLength = dst - elements_; - setDenseInitializedLength(newLength); - - if (length != newLength) { - JitSpew(JitSpew_StubFolding, "Cleared %u/%u shapes from %p", - (length - newLength) / 2, (length) / 2, this); - } - - return length != 0; -} diff --git a/js/src/jit/ShapeList.h b/js/src/jit/ShapeList.h @@ -35,28 +35,6 @@ class ShapeListObject : public ListObject { bool traceWeak(JSTracer* trc); }; -// Similar to ShapeListObject. But here we keep a list of the shape and the -// corresponding offset of a particular access (e.g. obj.a). -// The values are saved as: [shape1, offset1, shape2, offset2 ...]. -class ShapeListWithOffsetsObject : public ListObject { - public: - static const JSClass class_; - static const JSClassOps classOps_; - - static constexpr size_t MaxLength = 16; - - static ShapeListWithOffsetsObject* create(JSContext* cx); - static void trace(JSTracer* trc, JSObject* obj); - - uint32_t numShapes() const; - Shape* getShape(uint32_t index) const; - Shape* getShapeUnbarriered(uint32_t index) const; - - uint32_t getOffset(uint32_t index) const; - - bool traceWeak(JSTracer* trc); -}; - } // namespace js::jit #endif // jit_ShapeList_h diff --git a/js/src/jit/StubFolding.cpp b/js/src/jit/StubFolding.cpp @@ -24,47 +24,43 @@ using namespace js; using namespace js::jit; -static bool TryFoldingGuardShapes(JSContext* cx, ICFallbackStub* fallback, - JSScript* script, ICScript* icScript) { - // Try folding similar stubs with GuardShapes - // into GuardMultipleShapes or GuardMultipleShapesToOffset - +bool js::jit::TryFoldingStubs(JSContext* cx, ICFallbackStub* fallback, + JSScript* script, ICScript* icScript) { ICEntry* icEntry = icScript->icEntryForStub(fallback); ICStub* entryStub = icEntry->firstStub(); - ICCacheIRStub* firstStub = entryStub->toCacheIRStub(); - // The caller guarantees that there are at least two stubs. - MOZ_ASSERT(entryStub != fallback); - MOZ_ASSERT(!firstStub->next()->isFallback()); + // Don't fold unless there are at least two stubs. + if (entryStub == fallback) { + return true; + } + ICCacheIRStub* firstStub = entryStub->toCacheIRStub(); + if (firstStub->next()->isFallback()) { + return true; + } const uint8_t* firstStubData = firstStub->stubDataStart(); const CacheIRStubInfo* stubInfo = firstStub->stubInfo(); // Check to see if: // a) all of the stubs in this chain have the exact same code. - // b) all of the stubs have the same stub field data, except for a single - // GuardShape (and/or consecutive RawInt32) where they differ. + // b) all of the stubs have the same stub field data, except + // for a single GuardShape where they differ. // c) at least one stub after the first has a non-zero entry count. // d) All shapes in the GuardShape have the same realm. // // If all of these conditions hold, then we generate a single stub - // that covers all the existing cases by - // 1) replacing GuardShape with GuardMultipleShapes. - // 2) replacing Load/Store with equivalent LoadToOffset/StoreToOffset + // that covers all the existing cases by replacing GuardShape with + // GuardMultipleShapes. uint32_t numActive = 0; - mozilla::Maybe<uint32_t> foldableShapeOffset; - mozilla::Maybe<uint32_t> foldableOffsetOffset; + mozilla::Maybe<uint32_t> foldableFieldOffset; GCVector<Value, 8> shapeList(cx); - GCVector<Value, 8> offsetList(cx); - // Helper function: Keep list of different shapes. - // Can fail on OOM or for cross-realm shapes. + // Try to add a shape to the list. Can fail on OOM or for cross-realm shapes. // Returns true if the shape was successfully added to the list, and false // (with no pending exception) otherwise. auto addShape = [&shapeList, cx](uintptr_t rawShape) -> bool { Shape* shape = reinterpret_cast<Shape*>(rawShape); - // Only add same realm shapes. if (shape->realm() != cx->realm()) { return false; @@ -79,175 +75,75 @@ static bool TryFoldingGuardShapes(JSContext* cx, ICFallbackStub* fallback, return true; }; - // Helper function: Keep list of "possible" different offsets (slotOffset). - // At this stage we don't know if they differ. Therefore only keep track - // of the first offset until we see a different offset and fill list equal to - // shapeList if that happens. - auto lazyAddOffset = [&offsetList, &shapeList, cx](uintptr_t slotOffset) { - Value v = PrivateUint32Value(static_cast<uint32_t>(slotOffset)); - if (offsetList.length() == 1) { - if (v == offsetList[0]) return true; - - while (offsetList.length() + 1 < shapeList.length()) { - if (!offsetList.append(offsetList[0])) { - cx->recoverFromOutOfMemory(); - return false; - } - } - } - - if (!offsetList.append(v)) { - cx->recoverFromOutOfMemory(); - return false; - } - return true; - }; - -#ifdef JS_JITSPEW - JitSpew(JitSpew_StubFolding, "Trying to fold stubs at offset %u @ %s:%u:%u", - fallback->pcOffset(), script->filename(), script->lineno(), - script->column().oneOriginValue()); - - if (JitSpewEnabled(JitSpew_StubFoldingDetails)) { - Fprinter& printer(JitSpewPrinter()); - uint32_t i = 0; - for (ICCacheIRStub* stub = firstStub; stub; stub = stub->nextCacheIR()) { - printer.printf("- stub %d (enteredCount: %d)\n", i, stub->enteredCount()); - ICCacheIRStub* cache_stub = stub->toCacheIRStub(); - - CacheIRReader reader(cache_stub->stubInfo()); - SpewCacheIROps(printer, " ", cache_stub->stubInfo()); - i++; - } - } -#endif - - // Find the offset of the first Shape that differs. - // Also see if the next field is RawInt32, which is - // the case for a Fixed/Dynamic slot if it follows the ShapeGuard. for (ICCacheIRStub* other = firstStub->nextCacheIR(); other; other = other->nextCacheIR()) { // Verify that the stubs share the same code. if (other->stubInfo() != stubInfo) { return true; } + const uint8_t* otherStubData = other->stubDataStart(); if (other->enteredCount() > 0) { numActive++; } - if (foldableShapeOffset.isSome()) { - // Already found. - // Continue through all stubs to run above code. - continue; - } - - const uint8_t* otherStubData = other->stubDataStart(); uint32_t fieldIndex = 0; size_t offset = 0; while (stubInfo->fieldType(fieldIndex) != StubField::Type::Limit) { StubField::Type fieldType = stubInfo->fieldType(fieldIndex); - // Continue if the fields have same value. - if (StubField::sizeIsInt64(fieldType)) { - if (stubInfo->getStubRawInt64(firstStubData, offset) == - stubInfo->getStubRawInt64(otherStubData, offset)) { - offset += StubField::sizeInBytes(fieldType); - fieldIndex++; - continue; + if (StubField::sizeIsWord(fieldType)) { + uintptr_t firstRaw = stubInfo->getStubRawWord(firstStubData, offset); + uintptr_t otherRaw = stubInfo->getStubRawWord(otherStubData, offset); + + if (firstRaw != otherRaw) { + if (fieldType != StubField::Type::WeakShape) { + // Case 1: a field differs that is not a Shape. We only support + // folding GuardShape to GuardMultipleShapes. + return true; + } + if (foldableFieldOffset.isNothing()) { + // Case 2: this is the first field where the stub data differs. + foldableFieldOffset.emplace(offset); + if (!addShape(firstRaw) || !addShape(otherRaw)) { + return true; + } + } else if (*foldableFieldOffset == offset) { + // Case 3: this is the corresponding offset in a different stub. + if (!addShape(otherRaw)) { + return true; + } + } else { + // Case 4: we have found more than one field that differs. + return true; + } } } else { - MOZ_ASSERT(StubField::sizeIsWord(fieldType)); - if (stubInfo->getStubRawWord(firstStubData, offset) == - stubInfo->getStubRawWord(otherStubData, offset)) { - offset += StubField::sizeInBytes(fieldType); - fieldIndex++; - continue; - } - } + MOZ_ASSERT(StubField::sizeIsInt64(fieldType)); - // Early abort if it is a non-shape field that differs. - if (fieldType != StubField::Type::WeakShape) { - return true; + // We do not support folding any ops with int64-sized fields. + if (stubInfo->getStubRawInt64(firstStubData, offset) != + stubInfo->getStubRawInt64(otherStubData, offset)) { + return true; + } } - // Save the offset - foldableShapeOffset.emplace(offset); - - // Test if the consecutive field is potentially Load{Fixed|Dynamic}Slot offset += StubField::sizeInBytes(fieldType); fieldIndex++; - if (stubInfo->fieldType(fieldIndex) == StubField::Type::RawInt32) { - foldableOffsetOffset.emplace(offset); - } - - break; } - } - if (foldableShapeOffset.isNothing()) { - return true; + // We should never attach two completely identical stubs. + MOZ_ASSERT(foldableFieldOffset.isSome()); } if (numActive == 0) { return true; } - // Make sure the shape and offset is the only value that differ. - // Collect the shape and offset values at the same time. - for (ICCacheIRStub* stub = firstStub; stub; stub = stub->nextCacheIR()) { - const uint8_t* stubData = stub->stubDataStart(); - uint32_t fieldIndex = 0; - size_t offset = 0; - - while (stubInfo->fieldType(fieldIndex) != StubField::Type::Limit) { - StubField::Type fieldType = stubInfo->fieldType(fieldIndex); - if (offset == *foldableShapeOffset) { - // Save the shapes of all stubs. - MOZ_ASSERT(fieldType == StubField::Type::WeakShape); - uintptr_t raw = stubInfo->getStubRawWord(stubData, offset); - if (!addShape(raw)) { - return true; - } - } else if (foldableOffsetOffset.isSome() && - offset == *foldableOffsetOffset) { - // Save the offsets of all stubs. - MOZ_ASSERT(fieldType == StubField::Type::RawInt32); - uintptr_t raw = stubInfo->getStubRawWord(stubData, offset); - if (!lazyAddOffset(raw)) { - return true; - } - } else { - // Check all other fields are the same. - if (StubField::sizeIsInt64(fieldType)) { - if (stubInfo->getStubRawInt64(firstStubData, offset) != - stubInfo->getStubRawInt64(stubData, offset)) { - return true; - } - } else { - MOZ_ASSERT(StubField::sizeIsWord(fieldType)); - if (stubInfo->getStubRawWord(firstStubData, offset) != - stubInfo->getStubRawWord(stubData, offset)) { - return true; - } - } - } - - offset += StubField::sizeInBytes(fieldType); - fieldIndex++; - } - } - - // Clone the CacheIR and replace - // - specific GuardShape with GuardMultipleShapes. - // or - // (multiple distinct values in offsetList) - // - specific GuardShape with GuardMultipleShapesToOffset. - // - subsequent Load / Store with LoadToOffset / StoreToOffset + // Clone the CacheIR, replacing GuardShape with GuardMultipleShapes. CacheIRWriter writer(cx); CacheIRReader reader(stubInfo); CacheIRCloner cloner(firstStub); - bool hasSlotOffsets = offsetList.length() > 1; // Initialize the operands. CacheKind cacheKind = stubInfo->kind(); @@ -255,120 +151,40 @@ static bool TryFoldingGuardShapes(JSContext* cx, ICFallbackStub* fallback, writer.setInputOperandId(i); } - // Create the shapeList to bake in the new stub. - Rooted<ListObject*> shapeObj(cx); - { - gc::AutoSuppressGC suppressGC(cx); - - if (!hasSlotOffsets) { - shapeObj.set(ShapeListObject::create(cx)); - } else { - shapeObj.set(ShapeListWithOffsetsObject::create(cx)); - } - - if (!shapeObj) { - return false; - } - - MOZ_ASSERT_IF(hasSlotOffsets, shapeList.length() == offsetList.length()); - - for (uint32_t i = 0; i < shapeList.length(); i++) { - if (!shapeObj->append(cx, shapeList[i])) { - cx->recoverFromOutOfMemory(); - return false; - } - - if (hasSlotOffsets) { - if (!shapeObj->append(cx, offsetList[i])) { - cx->recoverFromOutOfMemory(); - return false; - } - } - - MOZ_ASSERT(static_cast<Shape*>(shapeList[i].toPrivate())->realm() == - shapeObj->realm()); - } - } - - mozilla::Maybe<Int32OperandId> offsetId; - bool shapeSuccess = false; - bool offsetSuccess = false; + bool success = false; while (reader.more()) { CacheOp op = reader.readOp(); switch (op) { case CacheOp::GuardShape: { auto [objId, shapeOffset] = reader.argsForGuardShape(); - if (shapeOffset != *foldableShapeOffset) { - // Unrelated GuardShape. + if (shapeOffset == *foldableFieldOffset) { + // Ensure that the allocation of the ShapeListObject doesn't trigger a + // GC and free the stubInfo we're currently reading. Note that + // AutoKeepJitScripts isn't sufficient, because optimized stubs can be + // discarded even if the JitScript is preserved. + gc::AutoSuppressGC suppressGC(cx); + + Rooted<ShapeListObject*> shapeObj(cx, ShapeListObject::create(cx)); + if (!shapeObj) { + return false; + } + for (uint32_t i = 0; i < shapeList.length(); i++) { + if (!shapeObj->append(cx, shapeList[i])) { + return false; + } + + MOZ_ASSERT(static_cast<Shape*>(shapeList[i].toPrivate())->realm() == + shapeObj->realm()); + } + + writer.guardMultipleShapes(objId, shapeObj); + success = true; + } else { WeakHeapPtr<Shape*>& ptr = stubInfo->getStubField<StubField::Type::WeakShape>(firstStub, shapeOffset); writer.guardShape(objId, ptr.unbarrieredGet()); - break; - } - - if (hasSlotOffsets) { - offsetId.emplace(writer.guardMultipleShapesToOffset(objId, shapeObj)); - } else { - writer.guardMultipleShapes(objId, shapeObj); - } - shapeSuccess = true; - break; - } - case CacheOp::LoadFixedSlotResult: { - auto [objId, offsetOffset] = reader.argsForLoadFixedSlotResult(); - if (!hasSlotOffsets || offsetOffset != *foldableOffsetOffset) { - // Unrelated LoadFixedSlotResult - uint32_t offset = stubInfo->getStubRawWord(firstStub, offsetOffset); - writer.loadFixedSlotResult(objId, offset); - break; - } - - MOZ_ASSERT(offsetId.isSome()); - writer.loadFixedSlotFromOffsetResult(objId, offsetId.value()); - offsetSuccess = true; - break; - } - case CacheOp::StoreFixedSlot: { - auto [objId, offsetOffset, rhsId] = reader.argsForStoreFixedSlot(); - if (!hasSlotOffsets || offsetOffset != *foldableOffsetOffset) { - // Unrelated StoreFixedSlot - uint32_t offset = stubInfo->getStubRawWord(firstStub, offsetOffset); - writer.storeFixedSlot(objId, offset, rhsId); - break; - } - - MOZ_ASSERT(offsetId.isSome()); - writer.storeFixedSlotFromOffset(objId, offsetId.value(), rhsId); - offsetSuccess = true; - break; - } - case CacheOp::StoreDynamicSlot: { - auto [objId, offsetOffset, rhsId] = reader.argsForStoreDynamicSlot(); - if (!hasSlotOffsets || offsetOffset != *foldableOffsetOffset) { - // Unrelated StoreDynamicSlot - uint32_t offset = stubInfo->getStubRawWord(firstStub, offsetOffset); - writer.storeDynamicSlot(objId, offset, rhsId); - break; } - - MOZ_ASSERT(offsetId.isSome()); - writer.storeDynamicSlotFromOffset(objId, offsetId.value(), rhsId); - offsetSuccess = true; - break; - } - case CacheOp::LoadDynamicSlotResult: { - auto [objId, offsetOffset] = reader.argsForLoadDynamicSlotResult(); - if (!hasSlotOffsets || offsetOffset != *foldableOffsetOffset) { - // Unrelated LoadDynamicSlotResult - uint32_t offset = stubInfo->getStubRawWord(firstStub, offsetOffset); - writer.loadDynamicSlotResult(objId, offset); - break; - } - - MOZ_ASSERT(offsetId.isSome()); - writer.loadDynamicSlotFromOffsetResult(objId, offsetId.value()); - offsetSuccess = true; break; } default: @@ -376,30 +192,9 @@ static bool TryFoldingGuardShapes(JSContext* cx, ICFallbackStub* fallback, break; } } - - if (!shapeSuccess) { + if (!success) { // If the shape field that differed was not part of a GuardShape, // we can't fold these stubs together. - JitSpew(JitSpew_StubFolding, - "Foldable shape field at offset %u was not a GuardShape " - "(icScript: %p) with %zu shapes (%s:%u:%u)", - fallback->pcOffset(), icScript, shapeList.length(), - script->filename(), script->lineno(), - script->column().oneOriginValue()); - return true; - } - - if (hasSlotOffsets && !offsetSuccess) { - // If we found a differing offset field but it was not part of the - // Load{Fixed | Dynamic}SlotResult then we can't fold these stubs - // together. - JitSpew(JitSpew_StubFolding, - "Failed to fold GuardShape into GuardMultipleShapesToOffset at " - "offset %u " - "(icScript: %p) with %zu shapes (%s:%u:%u)", - fallback->pcOffset(), icScript, shapeList.length(), - script->filename(), script->lineno(), - script->column().oneOriginValue()); return true; } @@ -420,40 +215,7 @@ static bool TryFoldingGuardShapes(JSContext* cx, ICFallbackStub* fallback, script->filename(), script->lineno(), script->column().oneOriginValue()); -#ifdef JS_JITSPEW - if (JitSpewEnabled(JitSpew_StubFoldingDetails)) { - ICStub* newEntryStub = icEntry->firstStub(); - ICCacheIRStub* newStub = newEntryStub->toCacheIRStub(); - - Fprinter& printer(JitSpewPrinter()); - printer.printf("- stub 0 (enteredCount: %d)\n", newStub->enteredCount()); - CacheIRReader reader(newStub->stubInfo()); - SpewCacheIROps(printer, " ", newStub->stubInfo()); - } -#endif - fallback->setMayHaveFoldedStub(); - - return true; -} - -bool js::jit::TryFoldingStubs(JSContext* cx, ICFallbackStub* fallback, - JSScript* script, ICScript* icScript) { - ICEntry* icEntry = icScript->icEntryForStub(fallback); - ICStub* entryStub = icEntry->firstStub(); - - // Don't fold unless there are at least two stubs. - if (entryStub == fallback) { - return true; - } - - ICCacheIRStub* firstStub = entryStub->toCacheIRStub(); - if (firstStub->next()->isFallback()) { - return true; - } - - if (!TryFoldingGuardShapes(cx, fallback, script, icScript)) return false; - return true; } @@ -475,10 +237,8 @@ bool js::jit::AddToFoldedStub(JSContext* cx, const CacheIRWriter& writer, const uint8_t* stubData = stub->stubDataStart(); mozilla::Maybe<uint32_t> shapeFieldOffset; - mozilla::Maybe<uint32_t> offsetFieldOffset; RootedValue newShape(cx); - RootedValue newOffset(cx); - Rooted<ListObject*> shapeList(cx); + Rooted<ShapeListObject*> foldedShapes(cx); CacheIRReader stubReader(stubInfo); CacheIRReader newReader(writer); @@ -491,72 +251,19 @@ bool js::jit::AddToFoldedStub(JSContext* cx, const CacheIRWriter& writer, if (newOp != CacheOp::GuardShape) { return false; } - // Check that the object being guarded is the same. - if (newReader.objOperandId() != stubReader.objOperandId()) { - return false; - } - - // Check that the shape offset is the same. - uint32_t newShapeOffset = newReader.stubOffset(); - uint32_t stubShapesOffset = stubReader.stubOffset(); - if (newShapeOffset != stubShapesOffset) { - return false; - } - - MOZ_ASSERT(shapeList == nullptr); - shapeFieldOffset.emplace(newShapeOffset); - - // Get the shape from the new stub - StubField shapeField = - writer.readStubField(newShapeOffset, StubField::Type::WeakShape); - Shape* shape = reinterpret_cast<Shape*>(shapeField.asWord()); - newShape = PrivateValue(shape); - - // Get the shape array from the old stub. - JSObject* obj = stubInfo->getStubField<StubField::Type::JSObject>( - stub, stubShapesOffset); - shapeList = &obj->as<ShapeListObject>(); - MOZ_ASSERT(shapeList->compartment() == shape->compartment()); - - // Don't add a shape if it's from a different realm than the first - // shape. - // - // Since the list was created in the realm which guarded all the shapes - // added to it, we can use its realm to check and ensure we're not - // adding a cross-realm shape. - // - // The assert verifies this property by checking the first element has - // the same realm (and since everything in the list has the same realm, - // checking the first element suffices) - Realm* shapesRealm = shapeList->realm(); - MOZ_ASSERT_IF( - !shapeList->isEmpty(), - shapeList->as<ShapeListObject>().getUnbarriered(0)->realm() == - shapesRealm); - if (shapesRealm != shape->realm()) { - return false; - } - break; - } - case CacheOp::GuardMultipleShapesToOffset: { - // Check that the new stub has a corresponding GuardShape. - if (newOp != CacheOp::GuardShape) { - return false; - } // Check that the object being guarded is the same. if (newReader.objOperandId() != stubReader.objOperandId()) { return false; } - // Check that the shape offset is the same. + // Check that the field offset is the same. uint32_t newShapeOffset = newReader.stubOffset(); uint32_t stubShapesOffset = stubReader.stubOffset(); if (newShapeOffset != stubShapesOffset) { return false; } - - MOZ_ASSERT(shapeList == nullptr); + MOZ_ASSERT(shapeFieldOffset.isNothing()); shapeFieldOffset.emplace(newShapeOffset); // Get the shape from the new stub @@ -566,10 +273,10 @@ bool js::jit::AddToFoldedStub(JSContext* cx, const CacheIRWriter& writer, newShape = PrivateValue(shape); // Get the shape array from the old stub. - JSObject* obj = stubInfo->getStubField<StubField::Type::JSObject>( + JSObject* shapeList = stubInfo->getStubField<StubField::Type::JSObject>( stub, stubShapesOffset); - shapeList = &obj->as<ShapeListWithOffsetsObject>(); - MOZ_ASSERT(shapeList->compartment() == shape->compartment()); + foldedShapes = &shapeList->as<ShapeListObject>(); + MOZ_ASSERT(foldedShapes->compartment() == shape->compartment()); // Don't add a shape if it's from a different realm than the first // shape. @@ -581,84 +288,13 @@ bool js::jit::AddToFoldedStub(JSContext* cx, const CacheIRWriter& writer, // The assert verifies this property by checking the first element has // the same realm (and since everything in the list has the same realm, // checking the first element suffices) - Realm* shapesRealm = shapeList->realm(); - MOZ_ASSERT_IF( - !shapeList->isEmpty(), - shapeList->as<ShapeListWithOffsetsObject>().getShape(0)->realm() == - shapesRealm); + Realm* shapesRealm = foldedShapes->realm(); + MOZ_ASSERT_IF(!foldedShapes->isEmpty(), + foldedShapes->getUnbarriered(0)->realm() == shapesRealm); if (shapesRealm != shape->realm()) { return false; } - // Consume the offsetId argument. - stubReader.skip(); - break; - } - case CacheOp::LoadFixedSlotFromOffsetResult: - case CacheOp::LoadDynamicSlotFromOffsetResult: { - // Check that the new stub has a corresponding - // Load{Fixed|Dynamic}SlotResult - if (stubOp == CacheOp::LoadFixedSlotFromOffsetResult && - newOp != CacheOp::LoadFixedSlotResult) { - return false; - } - if (stubOp == CacheOp::LoadDynamicSlotFromOffsetResult && - newOp != CacheOp::LoadDynamicSlotResult) { - return false; - } - - // Verify operand ID. - if (newReader.objOperandId() != stubReader.objOperandId()) { - return false; - } - - MOZ_ASSERT(offsetFieldOffset.isNothing()); - offsetFieldOffset.emplace(newReader.stubOffset()); - - // Get the offset from the new stub - StubField offsetField = - writer.readStubField(*offsetFieldOffset, StubField::Type::RawInt32); - newOffset = PrivateUint32Value(offsetField.asWord()); - - // Consume the offsetId argument. - stubReader.skip(); - break; - } - case CacheOp::StoreFixedSlotFromOffset: - case CacheOp::StoreDynamicSlotFromOffset: { - // Check that the new stub has a corresponding Store{Fixed|Dynamic}Slot - if (stubOp == CacheOp::StoreFixedSlotFromOffset && - newOp != CacheOp::StoreFixedSlot) { - return false; - } - if (stubOp == CacheOp::StoreDynamicSlotFromOffset && - newOp != CacheOp::StoreDynamicSlot) { - return false; - } - - // Verify operand ID. - if (newReader.objOperandId() != stubReader.objOperandId()) { - return false; - } - - MOZ_ASSERT(offsetFieldOffset.isNothing()); - offsetFieldOffset.emplace(newReader.stubOffset()); - - // Get the offset from the new stub - StubField offsetField = - writer.readStubField(*offsetFieldOffset, StubField::Type::RawInt32); - newOffset = PrivateUint32Value(offsetField.asWord()); - - // Consume the offsetId argument. - stubReader.skip(); - - // Verify rhs ID. - if (newReader.valOperandId() != stubReader.valOperandId()) { - return false; - } - - MOZ_ASSERT(stubReader.peekOp() == CacheOp::ReturnFromIC); - MOZ_ASSERT(newReader.peekOp() == CacheOp::ReturnFromIC); break; } default: { @@ -684,40 +320,27 @@ bool js::jit::AddToFoldedStub(JSContext* cx, const CacheIRWriter& writer, return false; } - if (!writer.stubDataEqualsIgnoringShapeAndOffset(stubData, *shapeFieldOffset, - offsetFieldOffset)) { + // Check to verify that all the other stub fields are the same. + if (!writer.stubDataEqualsIgnoring(stubData, *shapeFieldOffset)) { return false; } - // ShapeListWithSlotsObject uses two spaces per shape. - uint32_t numShapes = offsetFieldOffset.isNothing() ? shapeList->length() - : shapeList->length() / 2; - // Limit the maximum number of shapes we will add before giving up. // If we give up, transition the stub. - if (numShapes == ShapeListObject::MaxLength) { + if (foldedShapes->length() == ShapeListObject::MaxLength) { MOZ_ASSERT(fallback->state().mode() != ICState::Mode::Generic); fallback->state().forceTransition(); fallback->discardStubs(cx->zone(), icEntry); return false; } - if (!shapeList->append(cx, newShape)) { + if (!foldedShapes->append(cx, newShape)) { cx->recoverFromOutOfMemory(); return false; } - if (offsetFieldOffset.isSome()) { - if (!shapeList->append(cx, newOffset)) { - // Drop corresponding shape if we failed adding offset. - shapeList->shrinkElements(cx, shapeList->length() - 1); - cx->recoverFromOutOfMemory(); - return false; - } - } + JitSpew(JitSpew_StubFolding, "ShapeListObject %p: new length: %u", + foldedShapes.get(), foldedShapes->length()); - JitSpew(JitSpew_StubFolding, "ShapeList%sObject %p: new length: %u", - offsetFieldOffset.isNothing() ? "" : "WithOffset", shapeList.get(), - shapeList->length()); return true; } diff --git a/js/src/jit/TypePolicy.cpp b/js/src/jit/TypePolicy.cpp @@ -1121,8 +1121,6 @@ bool ClampPolicy::adjustInputs(TempAllocator& alloc, MInstruction* ins) const { _(MixPolicy<UnboxedInt32Policy<0>, UnboxedInt32Policy<1>, \ NoFloatPolicyAfter<2>>) \ _(MixPolicy<IntPtrPolicy<0>, IntPtrPolicy<1>, IntPtrPolicy<2>>) \ - _(MixPolicy<ObjectPolicy<0>, UnboxedInt32Policy<1>, NoFloatPolicy<2>>) \ - _(MixPolicy<UnboxedInt32Policy<1>, NoFloatPolicy<2>>) \ _(NoFloatPolicy<0>) \ _(NoFloatPolicy<1>) \ _(NoFloatPolicy<2>) \ diff --git a/js/src/jit/WarpBuilder.cpp b/js/src/jit/WarpBuilder.cpp @@ -3383,9 +3383,6 @@ bool WarpBuilder::buildIC(BytecodeLocation loc, CacheKind kind, const WarpCacheIRBase* cacheIRSnapshot = getOpSnapshot<WarpCacheIR>(loc); if (!cacheIRSnapshot) { cacheIRSnapshot = getOpSnapshot<WarpCacheIRWithShapeList>(loc); - if (!cacheIRSnapshot) { - cacheIRSnapshot = getOpSnapshot<WarpCacheIRWithShapeListAndOffsets>(loc); - } } if (cacheIRSnapshot) { return TranspileCacheIRToMIR(this, loc, cacheIRSnapshot, inputs); diff --git a/js/src/jit/WarpCacheIRTranspiler.cpp b/js/src/jit/WarpCacheIRTranspiler.cpp @@ -529,28 +529,6 @@ bool WarpCacheIRTranspiler::emitGuardMultipleShapes(ObjOperandId objId, return true; } -bool WarpCacheIRTranspiler::emitGuardMultipleShapesToOffset( - ObjOperandId objId, uint32_t shapesOffset, Int32OperandId offsetId) { - MDefinition* obj = getOperand(objId); - - // Use MGuardShapeListToOffset if we snapshotted the list of shapes on the - // main thread. - MInstruction* ins; - if (cacheIRSnapshot_->is<WarpCacheIRWithShapeListAndOffsets>()) { - auto* shapes = (ShapeListWithOffsetsSnapshot*)cacheIRSnapshot_ - ->as<WarpCacheIRWithShapeListAndOffsets>() - ->shapes(); - ins = MGuardShapeListToOffset::New(alloc(), obj, shapes); - } else { - MInstruction* shapeList = objectStubField(shapesOffset); - ins = MGuardMultipleShapesToOffset::New(alloc(), obj, shapeList); - ins->setBailoutKind(BailoutKind::StubFoldingGuardMultipleShapes); - } - add(ins); - - return defineOperand(offsetId, ins); -} - bool WarpCacheIRTranspiler::emitGuardNullProto(ObjOperandId objId) { MDefinition* def = getOperand(objId); @@ -1043,21 +1021,6 @@ bool WarpCacheIRTranspiler::emitLoadDynamicSlot(ValOperandId resultId, return defineOperand(resultId, load); } -bool WarpCacheIRTranspiler::emitLoadDynamicSlotFromOffsetResult( - ObjOperandId objId, Int32OperandId offsetId) { - MDefinition* obj = getOperand(objId); - MDefinition* offset = getOperand(offsetId); - - auto* slots = MSlots::New(alloc(), obj); - add(slots); - - auto* load = MLoadDynamicSlotFromOffset::New(alloc(), slots, offset); - add(load); - - pushResult(load); - return true; -} - bool WarpCacheIRTranspiler::emitGuardDynamicSlotIsNotObject( ObjOperandId objId, uint32_t slotOffset) { size_t slotIndex = int32StubField(slotOffset); @@ -1949,52 +1912,6 @@ bool WarpCacheIRTranspiler::emitLoadFixedSlot(ValOperandId resultId, return defineOperand(resultId, load); } -bool WarpCacheIRTranspiler::emitLoadFixedSlotFromOffsetResult( - ObjOperandId objId, Int32OperandId offsetId) { - MDefinition* obj = getOperand(objId); - MDefinition* offset = getOperand(offsetId); - - auto* ins = MLoadFixedSlotFromOffset::New(alloc(), obj, offset); - add(ins); - - pushResult(ins); - return true; -} - -bool WarpCacheIRTranspiler::emitStoreFixedSlotFromOffset( - ObjOperandId objId, Int32OperandId offsetId, ValOperandId rhsId) { - MDefinition* obj = getOperand(objId); - MDefinition* offset = getOperand(offsetId); - MDefinition* rhs = getOperand(rhsId); - - auto* barrier = MPostWriteBarrier::New(alloc(), obj, rhs); - add(barrier); - - auto* store = - MStoreFixedSlotFromOffset::NewBarriered(alloc(), obj, offset, rhs); - addEffectful(store); - - return resumeAfter(store); -} - -bool WarpCacheIRTranspiler::emitStoreDynamicSlotFromOffset( - ObjOperandId objId, Int32OperandId offsetId, ValOperandId rhsId) { - MDefinition* obj = getOperand(objId); - MDefinition* offset = getOperand(offsetId); - MDefinition* rhs = getOperand(rhsId); - - auto* barrier = MPostWriteBarrier::New(alloc(), obj, rhs); - add(barrier); - - auto* slots = MSlots::New(alloc(), obj); - add(slots); - - auto* store = MStoreDynamicSlotFromOffset::New(alloc(), slots, offset, rhs); - addEffectful(store); - - return resumeAfter(store); -} - bool WarpCacheIRTranspiler::emitLoadFixedSlotResult(ObjOperandId objId, uint32_t offsetOffset) { int32_t offset = int32StubField(offsetOffset); diff --git a/js/src/jit/WarpOracle.cpp b/js/src/jit/WarpOracle.cpp @@ -1106,7 +1106,6 @@ AbortReasonOr<Ok> WarpScriptOracle::maybeInlineIC(WarpOpSnapshotList& snapshots, // List of shapes for a GuardMultipleShapes op with a small number of shapes. mozilla::Maybe<ShapeListSnapshot> shapeList; - mozilla::Maybe<ShapeListWithOffsetsSnapshot> shapeListWithOffsets; // Only create a snapshot if all opcodes are supported by the transpiler. CacheIRReader reader(stubInfo); @@ -1224,22 +1223,6 @@ AbortReasonOr<Ok> WarpScriptOracle::maybeInlineIC(WarpOpSnapshotList& snapshots, } break; } - case CacheOp::GuardMultipleShapesToOffset: { - auto args = reader.argsForGuardMultipleShapesToOffset(); - JSObject* shapes = stubInfo->getStubField<StubField::Type::JSObject>( - stub, args.shapesOffset); - auto* shapesObject = &shapes->as<ShapeListWithOffsetsObject>(); - MOZ_ASSERT(shapeListWithOffsets.isNothing()); - size_t numShapes = shapesObject->numShapes(); - if (ShapeListSnapshot::shouldSnapshot(numShapes)) { - shapeListWithOffsets.emplace(); - for (size_t i = 0; i < numShapes; i++) { - shapeListWithOffsets->init(i, shapesObject->getShape(i), - shapesObject->getOffset(i)); - } - } - break; - } default: reader.skip(opInfo.argLength); break; @@ -1293,18 +1276,11 @@ AbortReasonOr<Ok> WarpScriptOracle::maybeInlineIC(WarpOpSnapshotList& snapshots, } if (shapeList.isSome()) { - MOZ_ASSERT(shapeListWithOffsets.isNothing()); if (!AddOpSnapshot<WarpCacheIRWithShapeList>(alloc_, snapshots, offset, jitCode, stubInfo, stubDataCopy, *shapeList)) { return abort(AbortReason::Alloc); } - } else if (shapeListWithOffsets.isSome()) { - if (!AddOpSnapshot<WarpCacheIRWithShapeListAndOffsets>( - alloc_, snapshots, offset, jitCode, stubInfo, stubDataCopy, - *shapeListWithOffsets)) { - return abort(AbortReason::Alloc); - } } else { if (!AddOpSnapshot<WarpCacheIR>(alloc_, snapshots, offset, jitCode, stubInfo, stubDataCopy)) { diff --git a/js/src/jit/WarpSnapshot.cpp b/js/src/jit/WarpSnapshot.cpp @@ -216,20 +216,6 @@ void WarpCacheIRWithShapeList::dumpData(GenericPrinter& out) const { } } -void WarpCacheIRWithShapeListAndOffsets::dumpData(GenericPrinter& out) const { - WarpCacheIRBase::dumpData(out); - uint32_t index = 0; - for (Shape* shape : shapes_.shapes()) { - out.printf(" shape %u: 0x%p\n", index, shape); - index++; - } - index = 0; - for (uint32_t offset : shapes_.offsets()) { - out.printf(" offset %u: %u\n", index, offset); - index++; - } -} - void WarpInlinedCall::dumpData(GenericPrinter& out) const { out.printf(" scriptSnapshot: 0x%p\n", scriptSnapshot_); out.printf(" info: 0x%p\n", info_); @@ -467,11 +453,6 @@ void WarpCacheIRWithShapeList::traceData(JSTracer* trc) { shapes_.trace(trc); } -void WarpCacheIRWithShapeListAndOffsets::traceData(JSTracer* trc) { - WarpCacheIRBase::traceData(trc); - shapes_.trace(trc); -} - void WarpInlinedCall::traceData(JSTracer* trc) { // Note: scriptSnapshot_ is traced through WarpSnapshot. cacheIRSnapshot_->trace(trc); diff --git a/js/src/jit/WarpSnapshot.h b/js/src/jit/WarpSnapshot.h @@ -34,22 +34,21 @@ class CacheIRStubInfo; class CompileInfo; class WarpScriptSnapshot; -#define WARP_OP_SNAPSHOT_LIST(_) \ - _(WarpArguments) \ - _(WarpRegExp) \ - _(WarpBuiltinObject) \ - _(WarpGetIntrinsic) \ - _(WarpGetImport) \ - _(WarpRest) \ - _(WarpBindUnqualifiedGName) \ - _(WarpVarEnvironment) \ - _(WarpLexicalEnvironment) \ - _(WarpClassBodyEnvironment) \ - _(WarpBailout) \ - _(WarpCacheIR) \ - _(WarpCacheIRWithShapeList) \ - _(WarpCacheIRWithShapeListAndOffsets) \ - _(WarpInlinedCall) \ +#define WARP_OP_SNAPSHOT_LIST(_) \ + _(WarpArguments) \ + _(WarpRegExp) \ + _(WarpBuiltinObject) \ + _(WarpGetIntrinsic) \ + _(WarpGetImport) \ + _(WarpRest) \ + _(WarpBindUnqualifiedGName) \ + _(WarpVarEnvironment) \ + _(WarpLexicalEnvironment) \ + _(WarpClassBodyEnvironment) \ + _(WarpBailout) \ + _(WarpCacheIR) \ + _(WarpCacheIRWithShapeList) \ + _(WarpInlinedCall) \ _(WarpPolymorphicTypes) // WarpOpSnapshot is the base class for data attached to a single bytecode op by @@ -283,27 +282,11 @@ class ShapeListSnapshot { void trace(JSTracer* trc) const; - protected: + private: static constexpr size_t NumShapes = 4; mozilla::Array<OffthreadGCPtr<Shape*>, NumShapes> shapes_{}; }; -class ShapeListWithOffsetsSnapshot : public ShapeListSnapshot { - public: - ShapeListWithOffsetsSnapshot() = default; - - void init(size_t index, Shape* shape, uint32_t offset) { - MOZ_ASSERT(shape); - shapes_[index].init(shape); - offsets_[index] = offset; - } - - const auto& offsets() const { return offsets_; } - - private: - mozilla::Array<uint32_t, NumShapes> offsets_{}; -}; - // Like WarpCacheIR, but also includes a ShapeListSnapshot for the // GuardMultipleShapes CacheIR op. class WarpCacheIRWithShapeList : public WarpCacheIRBase { @@ -328,30 +311,6 @@ class WarpCacheIRWithShapeList : public WarpCacheIRBase { const ShapeListSnapshot* shapes() const { return &shapes_; } }; -// Like WarpCacheIR, but also includes a ShapeListWithOffsetsSnapshot for the -// GuardMultipleShapesToOffset CacheIR op. -class WarpCacheIRWithShapeListAndOffsets : public WarpCacheIRBase { - const ShapeListWithOffsetsSnapshot shapes_; - - public: - static constexpr Kind ThisKind = Kind::WarpCacheIRWithShapeListAndOffsets; - - WarpCacheIRWithShapeListAndOffsets(uint32_t offset, JitCode* stubCode, - const CacheIRStubInfo* stubInfo, - const uint8_t* stubData, - const ShapeListWithOffsetsSnapshot& shapes) - : WarpCacheIRBase(ThisKind, offset, stubCode, stubInfo, stubData), - shapes_(shapes) {} - - void traceData(JSTracer* trc); - -#ifdef JS_JITSPEW - void dumpData(GenericPrinter& out) const; -#endif - - const ShapeListWithOffsetsSnapshot* shapes() const { return &shapes_; } -}; - // [SMDOC] Warp Nursery Object/Value support // // CacheIR stub data can contain nursery allocated objects or values. This can