tor-browser

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

commit 59d50bd368fb5ba1d562b3c8e7bf50e67be6fe7c
parent 653729f665e59fc8ebee035ecb7c8ec46a298de8
Author: Hannes Verschore <hv1989@gmail.com>
Date:   Thu, 18 Dec 2025 16:07:14 +0000

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

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

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

Diffstat:
Mjs/src/jit/CacheIRCompiler.cpp | 26++++++++++++++++++++++++++
Mjs/src/jit/CacheIROps.yaml | 16++++++++++++++++
Mjs/src/jit/CodeGenerator.cpp | 20++++++++++++++++++++
Mjs/src/jit/GenerateMIRFiles.py | 14+++++++++++---
Mjs/src/jit/Lowering.cpp | 19+++++++++++++++++++
Mjs/src/jit/MIR.cpp | 9+++++++++
Mjs/src/jit/MIROps.yaml | 20++++++++++++++++++++
Mjs/src/jit/ScalarReplacement.cpp | 4+++-
Mjs/src/jit/StubFolding.cpp | 60++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mjs/src/jit/WarpCacheIRTranspiler.cpp | 27+++++++++++++++++++++++++++
10 files changed, 211 insertions(+), 4 deletions(-)

diff --git a/js/src/jit/CacheIRCompiler.cpp b/js/src/jit/CacheIRCompiler.cpp @@ -2562,6 +2562,18 @@ 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::emitLoadDynamicSlot(ValOperandId resultId, ObjOperandId objId, uint32_t slotOffset) { @@ -2580,6 +2592,20 @@ 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__); diff --git a/js/src/jit/CacheIROps.yaml b/js/src/jit/CacheIROps.yaml @@ -806,6 +806,14 @@ obj: ObjId offset: RawInt32Field +- name: LoadFixedSlotFromOffsetResult + shared: true + transpile: true + cost_estimate: 2 + args: + obj: ObjId + offset: Int32Id + - name: LoadDynamicSlot shared: true transpile: true @@ -815,6 +823,14 @@ 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/CodeGenerator.cpp b/js/src/jit/CodeGenerator.cpp @@ -4302,6 +4302,16 @@ 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()) { @@ -18034,6 +18044,16 @@ 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); +} + 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,6 +59,8 @@ type_policies = { "Double": "DoublePolicy", "String": "StringPolicy", "Symbol": "SymbolPolicy", + "NoTypePolicy": "NoTypePolicy", + "Slots": "NoTypePolicy", } @@ -66,16 +68,22 @@ 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/Lowering.cpp b/js/src/jit/Lowering.cpp @@ -4207,6 +4207,15 @@ 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())), @@ -5480,6 +5489,16 @@ 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); diff --git a/js/src/jit/MIR.cpp b/js/src/jit/MIR.cpp @@ -7341,6 +7341,15 @@ 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/MIROps.yaml b/js/src/jit/MIROps.yaml @@ -2185,6 +2185,16 @@ - 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 @@ -2807,6 +2817,16 @@ - 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/ScalarReplacement.cpp b/js/src/jit/ScalarReplacement.cpp @@ -295,7 +295,9 @@ static bool IsObjectEscaped(MDefinition* ins, MInstruction* newObject, // captured by resume points. MDefinition* def = (*i)->consumer()->toDefinition(); MOZ_ASSERT(def->op() == MDefinition::Opcode::StoreDynamicSlot || - def->op() == MDefinition::Opcode::LoadDynamicSlot); + def->op() == MDefinition::Opcode::LoadDynamicSlot || + def->op() == + MDefinition::Opcode::LoadDynamicSlotFromOffset); } #endif break; diff --git a/js/src/jit/StubFolding.cpp b/js/src/jit/StubFolding.cpp @@ -50,6 +50,7 @@ static bool TryFoldingGuardShapes(JSContext* cx, ICFallbackStub* fallback, // 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 uint32_t numActive = 0; mozilla::Maybe<uint32_t> foldableShapeOffset; @@ -242,6 +243,7 @@ static bool TryFoldingGuardShapes(JSContext* cx, ICFallbackStub* fallback, // or // (multiple distinct values in offsetList) // - specific GuardShape with GuardMultipleShapesToOffset. + // - subsequent Load / Store with LoadToOffset / StoreToOffset CacheIRWriter writer(cx); CacheIRReader reader(stubInfo); CacheIRCloner cloner(firstStub); @@ -313,6 +315,34 @@ static bool TryFoldingGuardShapes(JSContext* cx, ICFallbackStub* fallback, 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::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: cloner.cloneOp(op, reader, writer); break; @@ -535,6 +565,36 @@ bool js::jit::AddToFoldedStub(JSContext* cx, const CacheIRWriter& writer, 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; + } default: { // Check that the op is the same. if (newOp != stubOp) { diff --git a/js/src/jit/WarpCacheIRTranspiler.cpp b/js/src/jit/WarpCacheIRTranspiler.cpp @@ -1043,6 +1043,21 @@ 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); @@ -1934,6 +1949,18 @@ 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::emitLoadFixedSlotResult(ObjOperandId objId, uint32_t offsetOffset) { int32_t offset = int32StubField(offsetOffset);