tor-browser

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

commit 374129481e266e1ed253d953ecee3182b20ac498
parent 5e9c8f20bfc9abd501a9340ecfb16d4a58bd4065
Author: Hannes Verschore <hv1989@gmail.com>
Date:   Thu, 11 Dec 2025 22:43:13 +0000

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

Adds the folding and addToFolded code for Store{Fixed|Dynamic}Slot.
This patch contains the cache stub code and ion code.

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

Diffstat:
Mjs/src/jit/CacheIRCompiler.cpp | 40++++++++++++++++++++++++++++++++++++++++
Mjs/src/jit/CacheIROps.yaml | 18++++++++++++++++++
Mjs/src/jit/CodeGenerator.cpp | 84+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mjs/src/jit/LIROps.yaml | 32++++++++++++++++++++++++++++++++
Mjs/src/jit/Lowering.cpp | 34++++++++++++++++++++++++++++++++++
Mjs/src/jit/MIR.h | 62++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mjs/src/jit/MIROps.yaml | 6++++++
Mjs/src/jit/StubFolding.cpp | 65+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mjs/src/jit/TypePolicy.cpp | 2++
Mjs/src/jit/WarpCacheIRTranspiler.cpp | 34++++++++++++++++++++++++++++++++++
10 files changed, 377 insertions(+), 0 deletions(-)

diff --git a/js/src/jit/CacheIRCompiler.cpp b/js/src/jit/CacheIRCompiler.cpp @@ -2574,6 +2574,46 @@ bool CacheIRCompiler::emitLoadFixedSlotFromOffsetResult( 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) { diff --git a/js/src/jit/CacheIROps.yaml b/js/src/jit/CacheIROps.yaml @@ -814,6 +814,24 @@ 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 diff --git a/js/src/jit/CodeGenerator.cpp b/js/src/jit/CodeGenerator.cpp @@ -4345,6 +4345,47 @@ 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())); @@ -18011,6 +18052,49 @@ void CodeGenerator::visitLoadFixedSlotFromOffset( 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/LIROps.yaml b/js/src/jit/LIROps.yaml @@ -2598,6 +2598,22 @@ 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: @@ -2650,6 +2666,22 @@ 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 diff --git a/js/src/jit/Lowering.cpp b/js/src/jit/Lowering.cpp @@ -5639,6 +5639,40 @@ 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); diff --git a/js/src/jit/MIR.h b/js/src/jit/MIR.h @@ -7962,6 +7962,41 @@ 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 { @@ -8394,6 +8429,33 @@ 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 @@ -2149,6 +2149,12 @@ - name: StoreFixedSlot gen_boilerplate: false +- name: StoreFixedSlotFromOffset + gen_boilerplate: false + +- name: StoreDynamicSlotFromOffset + gen_boilerplate: false + - name: GetPropertyCache gen_boilerplate: false diff --git a/js/src/jit/StubFolding.cpp b/js/src/jit/StubFolding.cpp @@ -329,6 +329,34 @@ static bool TryFoldingGuardShapes(JSContext* cx, ICFallbackStub* fallback, 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) { @@ -596,6 +624,43 @@ bool js::jit::AddToFoldedStub(JSContext* cx, const CacheIRWriter& writer, 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: { // Check that the op is the same. if (newOp != stubOp) { diff --git a/js/src/jit/TypePolicy.cpp b/js/src/jit/TypePolicy.cpp @@ -1121,6 +1121,8 @@ 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/WarpCacheIRTranspiler.cpp b/js/src/jit/WarpCacheIRTranspiler.cpp @@ -1961,6 +1961,40 @@ bool WarpCacheIRTranspiler::emitLoadFixedSlotFromOffsetResult( 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);