Bug 1794439 - Inline HasNativeDataPropertyPure cache lookup in Ion r=jandem

I saw something like a 5% improvement to the Elm, React, and React-Redux
subtests of speedometer with this, with maybe a 1% overall Speedometer
improvement, although the confidence is lower.

Differential Revision: https://phabricator.services.mozilla.com/D158983
This commit is contained in:
Doug Thayer 2022-10-26 22:57:23 +00:00
parent a0fe811cf3
commit f5e9af5d08
6 changed files with 185 additions and 5 deletions

View file

@ -0,0 +1,32 @@
setJitCompilerOption("offthread-compilation.enable", 0);
setJitCompilerOption("baseline.warmup.trigger", 5);
setJitCompilerOption("ion.warmup.trigger", 5);
function Base() {
}
Base.prototype.foo = false;
// XXX: tried to do this with ic.force-megamorphic, but it didn't seem to want
// to work. Maybe something is being too clever for my simple test case if I
// don't put things into a megamorphic array?
let objs = [
{a: true},
{b: true},
{c: true},
{d: true},
{e: true},
{f: true},
{g: true},
new Base(),
];
function doTest(i) {
let o = objs[i % objs.length];
assertEq(!o.foo, true);
assertEq(Object.hasOwn(o, "foo"), false);
}
for (var i = 0; i < 50; i++) {
doTest(i);
}

View file

@ -4326,8 +4326,18 @@ void CodeGenerator::visitMegamorphicHasProp(LMegamorphicHasProp* lir) {
ValueOperand idVal = ToValue(lir, LMegamorphicHasProp::IdIndex);
Register temp0 = ToRegister(lir->temp0());
Register temp1 = ToRegister(lir->temp1());
Register temp2 = ToRegister(lir->temp2());
Register output = ToRegister(lir->output());
Label bail, cacheHit;
if (JitOptions.enableWatchtowerMegamorphic) {
masm.emitMegamorphicCacheLookupExists(idVal, obj, temp0, temp1, temp2,
output, &bail, &cacheHit,
lir->mir()->hasOwn());
}
masm.branchIfNonNativeObj(obj, temp0, &bail);
// idVal will be in vp[0], result will be stored in vp[1].
masm.reserveStack(sizeof(Value));
masm.Push(idVal);
@ -4353,12 +4363,15 @@ void CodeGenerator::visitMegamorphicHasProp(LMegamorphicHasProp* lir) {
Label ok;
masm.branchIfTrueBool(temp0, &ok);
masm.freeStack(sizeof(Value)); // Discard result Value.
bailout(lir->snapshot());
masm.jump(&bail);
masm.bind(&ok);
masm.setFramePushed(framePushed);
masm.unboxBoolean(Address(masm.getStackPointer(), 0), output);
masm.freeStack(sizeof(Value));
masm.bind(&cacheHit);
bailoutFrom(&bail, lir->snapshot());
}
void CodeGenerator::visitGuardIsNotArrayBufferMaybeShared(

View file

@ -2522,7 +2522,7 @@
operands:
object: WordSized
id: BoxedValue
num_temps: 2
num_temps: 3
call_instruction: true
mir_op: true

View file

@ -4605,9 +4605,10 @@ void LIRGenerator::visitMegamorphicStoreSlot(MMegamorphicStoreSlot* ins) {
void LIRGenerator::visitMegamorphicHasProp(MMegamorphicHasProp* ins) {
MOZ_ASSERT(ins->object()->type() == MIRType::Object);
MOZ_ASSERT(ins->idVal()->type() == MIRType::Value);
auto* lir = new (alloc()) LMegamorphicHasProp(
useRegisterAtStart(ins->object()), useBoxAtStart(ins->idVal()),
tempFixed(CallTempReg0), tempFixed(CallTempReg1));
auto* lir = new (alloc())
LMegamorphicHasProp(useRegisterAtStart(ins->object()),
useBoxAtStart(ins->idVal()), tempFixed(CallTempReg0),
tempFixed(CallTempReg1), tempFixed(CallTempReg2));
assignSnapshot(lir, ins->bailoutKind());
defineReturn(lir, ins);
}

View file

@ -1902,6 +1902,39 @@ void MacroAssembler::loadMegamorphicCache(Register dest) {
movePtr(ImmPtr(runtime()->addressOfMegamorphicCache()), dest);
}
void MacroAssembler::loadAtomOrSymbolAndHash(ValueOperand value, Register outId,
Register outHash,
Label* cacheMiss) {
Label maybeSymbol, fatInline, done;
ScratchTagScope tag(*this, value);
splitTagForTest(value, tag);
branchTestString(Assembler::NotEqual, tag, &maybeSymbol);
unboxString(value, outId);
branchTest32(Assembler::Zero, Address(outId, JSString::offsetOfFlags()),
Imm32(JSString::ATOM_BIT), cacheMiss);
move32(Imm32(JSString::FAT_INLINE_MASK), outHash);
and32(Address(outId, JSString::offsetOfFlags()), outHash);
branch32(Assembler::Equal, outHash, Imm32(JSString::FAT_INLINE_MASK),
&fatInline);
load32(Address(outId, NormalAtom::offsetOfHash()), outHash);
jump(&done);
bind(&fatInline);
load32(Address(outId, FatInlineAtom::offsetOfHash()), outHash);
jump(&done);
bind(&maybeSymbol);
branchTestSymbol(Assembler::NotEqual, tag, cacheMiss);
unboxSymbol(value, outId);
load32(Address(outId, JS::Symbol::offsetOfHash()), outHash);
bind(&done);
}
void MacroAssembler::emitMegamorphicCacheLookup(
PropertyKey id, Register obj, Register scratch1, Register scratch2,
Register scratch3, ValueOperand output, Label* fail, Label* cacheHit) {
@ -2023,6 +2056,98 @@ void MacroAssembler::emitMegamorphicCacheLookup(
bind(&cacheMiss);
}
void MacroAssembler::emitMegamorphicCacheLookupExists(
ValueOperand id, Register obj, Register scratch1, Register scratch2,
Register scratch3, Register output, Label* fail, Label* cacheHit,
bool hasOwn) {
// A lot of this code is shared with emitMegamorphicCacheLookup. It would
// be nice to be able to avoid the duplication here, but due to a few
// differences like taking the id in a ValueOperand instead of being able
// to bake it in as an immediate, and only needing a Register for the output
// value, it seemed more awkward to read once it was deduplicated.
Label cacheMiss, isMissing, cacheHitFalse;
// scratch2 = obj->shape()
loadPtr(Address(obj, JSObject::offsetOfShape()), scratch2);
movePtr(scratch2, scratch3);
// scratch2 = (scratch2 >> 3) ^ (scratch2 >> 13) + idHash
rshiftPtr(Imm32(MegamorphicCache::ShapeHashShift1), scratch2);
rshiftPtr(Imm32(MegamorphicCache::ShapeHashShift2), scratch3);
xorPtr(scratch3, scratch2);
loadAtomOrSymbolAndHash(id, scratch1, scratch3, &cacheMiss);
addPtr(scratch3, scratch2);
// scratch2 %= MegamorphicCache::NumEntries
constexpr size_t cacheSize = MegamorphicCache::NumEntries;
static_assert(mozilla::IsPowerOfTwo(cacheSize));
size_t cacheMask = cacheSize - 1;
and32(Imm32(cacheMask), scratch2);
loadMegamorphicCache(scratch3);
// scratch2 = &scratch3->entries_[scratch2]
constexpr size_t entrySize = sizeof(MegamorphicCache::Entry);
static_assert(sizeof(void*) == 4 || entrySize == 24);
if constexpr (sizeof(void*) == 4) {
mul32(Imm32(entrySize), scratch2);
computeEffectiveAddress(BaseIndex(scratch3, scratch2, TimesOne,
MegamorphicCache::offsetOfEntries()),
scratch2);
} else {
computeEffectiveAddress(BaseIndex(scratch2, scratch2, TimesTwo), scratch2);
computeEffectiveAddress(BaseIndex(scratch3, scratch2, TimesEight,
MegamorphicCache::offsetOfEntries()),
scratch2);
}
// if (scratch2->key_ != scratch4) goto cacheMiss
branchPtr(Assembler::NotEqual,
Address(scratch2, MegamorphicCache::Entry::offsetOfKey()), scratch1,
&cacheMiss);
loadPtr(Address(obj, JSObject::offsetOfShape()), scratch1);
// if (scratch2->shape_ != scratch1) goto cacheMiss
branchPtr(Assembler::NotEqual,
Address(scratch2, MegamorphicCache::Entry::offsetOfShape()),
scratch1, &cacheMiss);
// scratch3 = scratch3->generation_
load16ZeroExtend(Address(scratch3, MegamorphicCache::offsetOfGeneration()),
scratch3);
load16ZeroExtend(
Address(scratch2, MegamorphicCache::Entry::offsetOfGeneration()),
scratch1);
// if (scratch2->generation_ != scratch3) goto cacheMiss
branch32(Assembler::NotEqual, scratch1, scratch3, &cacheMiss);
// scratch3 = scratch2->numHops_
load8ZeroExtend(Address(scratch2, MegamorphicCache::Entry::offsetOfNumHops()),
scratch3);
branch32(Assembler::Equal, scratch3,
Imm32(MegamorphicCache::Entry::NumHopsForMissingProperty),
&cacheHitFalse);
if (hasOwn) {
branch32(Assembler::NotEqual, scratch3, Imm32(0), &cacheHitFalse);
} else {
branch32(Assembler::Equal, scratch3,
Imm32(MegamorphicCache::Entry::NumHopsForMissingOwnProperty),
&cacheMiss);
}
move32(Imm32(1), output);
jump(cacheHit);
bind(&cacheHitFalse);
xor32(output, output);
jump(cacheHit);
bind(&cacheMiss);
}
void MacroAssembler::guardNonNegativeIntPtrToInt32(Register reg, Label* fail) {
#ifdef DEBUG
Label ok;

View file

@ -5014,11 +5014,20 @@ class MacroAssembler : public MacroAssemblerSpecific {
void loadMegamorphicCache(Register dest);
void loadAtomOrSymbolAndHash(ValueOperand value, Register outId,
Register outHash, Label* cacheMiss);
void emitMegamorphicCacheLookup(PropertyKey id, Register obj,
Register scratch1, Register scratch2,
Register scratch3, ValueOperand output,
Label* fail, Label* cacheHit);
void emitMegamorphicCacheLookupExists(ValueOperand id, Register obj,
Register scratch1, Register scratch2,
Register scratch3, Register output,
Label* fail, Label* cacheHit,
bool hasOwn);
void loadDOMExpandoValueGuardGeneration(
Register obj, ValueOperand output,
JS::ExpandoAndGeneration* expandoAndGeneration, uint64_t generation,