Bug 1932305 part 13 - Optimize calls to Map.prototype.delete and Set.prototype.delete. r=iain

Differential Revision: https://phabricator.services.mozilla.com/D229613
This commit is contained in:
Jan de Mooij 2024-11-22 13:40:21 +00:00
parent b1494ee1be
commit edd79fcb43
15 changed files with 227 additions and 2 deletions

View File

@ -433,7 +433,7 @@ const JSFunctionSpec MapObject::methods[] = {
JS_INLINABLE_FN("get", get, 1, 0, MapGet),
JS_INLINABLE_FN("has", has, 1, 0, MapHas),
JS_INLINABLE_FN("set", set, 2, 0, MapSet),
JS_FN("delete", delete_, 1, 0),
JS_INLINABLE_FN("delete", delete_, 1, 0, MapDelete),
JS_FN("keys", keys, 0, 0),
JS_FN("values", values, 0, 0),
JS_FN("clear", clear, 0, 0),
@ -1192,7 +1192,7 @@ const JSPropertySpec SetObject::properties[] = {
const JSFunctionSpec SetObject::methods[] = {
JS_INLINABLE_FN("has", has, 1, 0, SetHas),
JS_INLINABLE_FN("add", add, 1, 0, SetAdd),
JS_FN("delete", delete_, 1, 0),
JS_INLINABLE_FN("delete", delete_, 1, 0, SetDelete),
JS_FN("entries", entries, 0, 0),
JS_FN("clear", clear, 0, 0),
JS_SELF_HOSTED_FN("forEach", "SetForEach", 2, 0),

View File

@ -0,0 +1,17 @@
function test() {
var m = new Map();
var s = new Set();
for (var i = 0; i < 100; i++) {
assertEq(m.delete("a" + (i - 1)), i > 0);
assertEq(m.delete("a" + (i - 1)), false);
assertEq(s.delete("b" + (i - 1)), i > 0);
assertEq(s.delete("b" + (i - 1)), false);
m.set("a" + i, i);
s.add("b" + i);
}
assertEq(m.size, 1);
assertEq(s.size, 1);
}
test();

View File

@ -10242,6 +10242,37 @@ AttachDecision InlinableNativeIRGenerator::tryAttachSetHas() {
return AttachDecision::Attach;
}
AttachDecision InlinableNativeIRGenerator::tryAttachSetDelete() {
// Ensure |this| is a SetObject.
if (!thisval_.isObject() || !thisval_.toObject().is<SetObject>()) {
return AttachDecision::NoAction;
}
// Need a single argument.
if (argc_ != 1) {
return AttachDecision::NoAction;
}
// Initialize the input operand.
initializeInputOperand();
// Guard callee is the 'delete' native function.
emitNativeCalleeGuard();
// Guard |this| is a SetObject.
ValOperandId thisValId =
writer.loadArgumentFixedSlot(ArgumentKind::This, argc_);
ObjOperandId objId = writer.guardToObject(thisValId);
emitOptimisticClassGuard(objId, &thisval_.toObject(), GuardClassKind::Set);
ValOperandId argId = writer.loadArgumentFixedSlot(ArgumentKind::Arg0, argc_);
writer.setDeleteResult(objId, argId);
writer.returnFromIC();
trackAttached("SetDelete");
return AttachDecision::Attach;
}
AttachDecision InlinableNativeIRGenerator::tryAttachSetAdd() {
// Ensure |this| is a SetObject.
if (!thisval_.isObject() || !thisval_.toObject().is<SetObject>()) {
@ -10477,6 +10508,37 @@ AttachDecision InlinableNativeIRGenerator::tryAttachMapGet() {
return AttachDecision::Attach;
}
AttachDecision InlinableNativeIRGenerator::tryAttachMapDelete() {
// Ensure |this| is a MapObject.
if (!thisval_.isObject() || !thisval_.toObject().is<MapObject>()) {
return AttachDecision::NoAction;
}
// Need a single argument.
if (argc_ != 1) {
return AttachDecision::NoAction;
}
// Initialize the input operand.
initializeInputOperand();
// Guard callee is the 'delete' native function.
emitNativeCalleeGuard();
// Guard |this| is a MapObject.
ValOperandId thisValId =
writer.loadArgumentFixedSlot(ArgumentKind::This, argc_);
ObjOperandId objId = writer.guardToObject(thisValId);
emitOptimisticClassGuard(objId, &thisval_.toObject(), GuardClassKind::Map);
ValOperandId argId = writer.loadArgumentFixedSlot(ArgumentKind::Arg0, argc_);
writer.mapDeleteResult(objId, argId);
writer.returnFromIC();
trackAttached("MapDelete");
return AttachDecision::Attach;
}
AttachDecision InlinableNativeIRGenerator::tryAttachMapSet() {
#ifdef JS_CODEGEN_X86
// 32-bit x86 does not have enough registers for the AutoCallVM output, the
@ -12401,6 +12463,8 @@ AttachDecision InlinableNativeIRGenerator::tryAttachStub() {
return AttachDecision::NoAction; // Not callable.
case InlinableNative::SetHas:
return tryAttachSetHas();
case InlinableNative::SetDelete:
return tryAttachSetDelete();
case InlinableNative::SetAdd:
return tryAttachSetAdd();
case InlinableNative::SetSize:
@ -12413,6 +12477,8 @@ AttachDecision InlinableNativeIRGenerator::tryAttachStub() {
return tryAttachMapHas();
case InlinableNative::MapGet:
return tryAttachMapGet();
case InlinableNative::MapDelete:
return tryAttachMapDelete();
case InlinableNative::MapSet:
return tryAttachMapSet();

View File

@ -10584,6 +10584,24 @@ bool CacheIRCompiler::emitSetHasObjectResult(ObjOperandId setId,
return true;
}
bool CacheIRCompiler::emitSetDeleteResult(ObjOperandId setId,
ValOperandId valId) {
JitSpew(JitSpew_Codegen, "%s", __FUNCTION__);
AutoCallVM callvm(masm, this, allocator);
Register set = allocator.useRegister(masm, setId);
ValueOperand val = allocator.useValueRegister(masm, valId);
callvm.prepare();
masm.Push(val);
masm.Push(set);
using Fn = bool (*)(JSContext*, Handle<SetObject*>, HandleValue, bool*);
callvm.call<Fn, jit::SetObjectDelete>();
return true;
}
bool CacheIRCompiler::emitSetAddResult(ObjOperandId setId, ValOperandId keyId) {
JitSpew(JitSpew_Codegen, "%s", __FUNCTION__);
@ -10753,6 +10771,24 @@ bool CacheIRCompiler::emitMapGetResult(ObjOperandId mapId, ValOperandId valId) {
return true;
}
bool CacheIRCompiler::emitMapDeleteResult(ObjOperandId mapId,
ValOperandId valId) {
JitSpew(JitSpew_Codegen, "%s", __FUNCTION__);
AutoCallVM callvm(masm, this, allocator);
Register map = allocator.useRegister(masm, mapId);
ValueOperand val = allocator.useValueRegister(masm, valId);
callvm.prepare();
masm.Push(val);
masm.Push(map);
using Fn = bool (*)(JSContext*, Handle<MapObject*>, HandleValue, bool*);
callvm.call<Fn, jit::MapObjectDelete>();
return true;
}
bool CacheIRCompiler::emitMapSetResult(ObjOperandId mapId, ValOperandId keyId,
ValOperandId valId) {
JitSpew(JitSpew_Codegen, "%s", __FUNCTION__);

View File

@ -771,10 +771,12 @@ class MOZ_RAII InlinableNativeIRGenerator {
AttachDecision tryAttachBigIntAsIntN();
AttachDecision tryAttachBigIntAsUintN();
AttachDecision tryAttachSetHas();
AttachDecision tryAttachSetDelete();
AttachDecision tryAttachSetAdd();
AttachDecision tryAttachSetSize();
AttachDecision tryAttachMapHas();
AttachDecision tryAttachMapGet();
AttachDecision tryAttachMapDelete();
AttachDecision tryAttachMapSet();
AttachDecision tryAttachDateGetTime(InlinableNative native);
AttachDecision tryAttachDateGet(DateComponent component);

View File

@ -3420,6 +3420,14 @@
set: ObjId
obj: ObjId
- name: SetDeleteResult
shared: true
transpile: true
cost_estimate: 5
args:
set: ObjId
val: ValId
- name: SetAddResult
shared: true
transpile: true
@ -3531,6 +3539,14 @@
map: ObjId
obj: ObjId
- name: MapDeleteResult
shared: true
transpile: true
cost_estimate: 5
args:
map: ObjId
val: ValId
- name: MapSetResult
shared: true
transpile: true

View File

@ -21667,6 +21667,13 @@ void CodeGenerator::visitSetObjectHasValueVMCall(
callVM<Fn, jit::SetObjectHas>(ins);
}
void CodeGenerator::visitSetObjectDelete(LSetObjectDelete* ins) {
pushArg(ToValue(ins, LSetObjectDelete::KeyIndex));
pushArg(ToRegister(ins->setObject()));
using Fn = bool (*)(JSContext*, Handle<SetObject*>, HandleValue, bool*);
callVM<Fn, jit::SetObjectDelete>(ins);
}
void CodeGenerator::visitSetObjectAdd(LSetObjectAdd* ins) {
pushArg(ToValue(ins, LSetObjectAdd::KeyIndex));
pushArg(ToRegister(ins->setObject()));
@ -21779,6 +21786,13 @@ void CodeGenerator::visitMapObjectGetValueVMCall(
callVM<Fn, jit::MapObjectGet>(ins);
}
void CodeGenerator::visitMapObjectDelete(LMapObjectDelete* ins) {
pushArg(ToValue(ins, LMapObjectDelete::KeyIndex));
pushArg(ToRegister(ins->mapObject()));
using Fn = bool (*)(JSContext*, Handle<MapObject*>, HandleValue, bool*);
callVM<Fn, jit::MapObjectDelete>(ins);
}
void CodeGenerator::visitMapObjectSet(LMapObjectSet* ins) {
pushArg(ToValue(ins, LMapObjectSet::ValueIndex));
pushArg(ToValue(ins, LMapObjectSet::KeyIndex));

View File

@ -313,6 +313,7 @@ bool js::jit::CanInlineNativeCrossRealm(InlinableNative native) {
case InlinableNative::MapConstructor:
case InlinableNative::MapGet:
case InlinableNative::MapHas:
case InlinableNative::MapDelete:
case InlinableNative::MapSet:
case InlinableNative::Number:
case InlinableNative::NumberParseInt:
@ -320,6 +321,7 @@ bool js::jit::CanInlineNativeCrossRealm(InlinableNative native) {
case InlinableNative::ReflectGetPrototypeOf:
case InlinableNative::SetConstructor:
case InlinableNative::SetHas:
case InlinableNative::SetDelete:
case InlinableNative::SetAdd:
case InlinableNative::SetSize:
case InlinableNative::String:

View File

@ -97,6 +97,7 @@
_(IntlGuardToSegmentIterator) \
\
_(MapConstructor) \
_(MapDelete) \
_(MapGet) \
_(MapHas) \
_(MapSet) \
@ -155,6 +156,7 @@
_(GetFirstDollarIndex) \
\
_(SetConstructor) \
_(SetDelete) \
_(SetHas) \
_(SetAdd) \
_(SetSize) \

View File

@ -7666,6 +7666,13 @@ void LIRGenerator::visitSetObjectHasValueVMCall(MSetObjectHasValueVMCall* ins) {
assignSafepoint(lir, ins);
}
void LIRGenerator::visitSetObjectDelete(MSetObjectDelete* ins) {
auto* lir = new (alloc()) LSetObjectDelete(
useRegisterAtStart(ins->setObject()), useBoxAtStart(ins->key()));
defineReturn(lir, ins);
assignSafepoint(lir, ins);
}
void LIRGenerator::visitSetObjectAdd(MSetObjectAdd* ins) {
auto* lir = new (alloc()) LSetObjectAdd(useRegisterAtStart(ins->setObject()),
useBoxAtStart(ins->key()));
@ -7734,6 +7741,13 @@ void LIRGenerator::visitMapObjectGetValueVMCall(MMapObjectGetValueVMCall* ins) {
assignSafepoint(lir, ins);
}
void LIRGenerator::visitMapObjectDelete(MMapObjectDelete* ins) {
auto* lir = new (alloc()) LMapObjectDelete(
useRegisterAtStart(ins->mapObject()), useBoxAtStart(ins->key()));
defineReturn(lir, ins);
assignSafepoint(lir, ins);
}
void LIRGenerator::visitMapObjectSet(MMapObjectSet* ins) {
auto* lir = new (alloc())
LMapObjectSet(useRegisterAtStart(ins->mapObject()),

View File

@ -3252,6 +3252,14 @@
alias_set: custom
possibly_calls: true
- name: SetObjectDelete
operands:
setObject: Object
key: Value
result_type: Boolean
possibly_calls: true
generate_lir: true
- name: SetObjectAdd
operands:
setObject: Object
@ -3349,6 +3357,14 @@
alias_set: custom
possibly_calls: true
- name: MapObjectDelete
operands:
mapObject: Object
key: Value
result_type: Boolean
possibly_calls: true
generate_lir: true
- name: MapObjectSet
operands:
mapObject: Object

View File

@ -248,6 +248,7 @@ namespace jit {
_(LinearizeForCharAccess, js::jit::LinearizeForCharAccess) \
_(LoadAliasedDebugVar, js::LoadAliasedDebugVar) \
_(MapObjectCreate, js::MapObject::create) \
_(MapObjectDelete, js::jit::MapObjectDelete) \
_(MapObjectGet, js::jit::MapObjectGet) \
_(MapObjectHas, js::jit::MapObjectHas) \
_(MapObjectSet, js::jit::MapObjectSet) \
@ -309,6 +310,7 @@ namespace jit {
_(SetObjectAdd, js::jit::SetObjectAdd) \
_(SetObjectAddFromIC, js::jit::SetObjectAddFromIC) \
_(SetObjectCreate, js::SetObject::create) \
_(SetObjectDelete, js::jit::SetObjectDelete) \
_(SetObjectHas, js::jit::SetObjectHas) \
_(SetPropertyMegamorphicNoCache, js::jit::SetPropertyMegamorphic<false>) \
_(SetPropertyMegamorphicYesCache, js::jit::SetPropertyMegamorphic<true>) \

View File

@ -3115,6 +3115,11 @@ bool SetObjectHas(JSContext* cx, Handle<SetObject*> obj, HandleValue key,
return obj->has(cx, key, rval);
}
bool SetObjectDelete(JSContext* cx, Handle<SetObject*> obj, HandleValue key,
bool* rval) {
return obj->delete_(cx, key, rval);
}
bool SetObjectAdd(JSContext* cx, Handle<SetObject*> obj, HandleValue key) {
return obj->add(cx, key);
}
@ -3138,6 +3143,11 @@ bool MapObjectGet(JSContext* cx, Handle<MapObject*> obj, HandleValue key,
return obj->get(cx, key, rval);
}
bool MapObjectDelete(JSContext* cx, Handle<MapObject*> obj, HandleValue key,
bool* rval) {
return obj->delete_(cx, key, rval);
}
bool MapObjectSet(JSContext* cx, Handle<MapObject*> obj, HandleValue key,
HandleValue val) {
return obj->set(cx, key, val);

View File

@ -705,6 +705,8 @@ JSAtom* AtomizeStringNoGC(JSContext* cx, JSString* str);
bool SetObjectHas(JSContext* cx, Handle<SetObject*> obj, HandleValue key,
bool* rval);
bool SetObjectDelete(JSContext* cx, Handle<SetObject*> obj, HandleValue key,
bool* rval);
bool SetObjectAdd(JSContext* cx, Handle<SetObject*> obj, HandleValue key);
bool SetObjectAddFromIC(JSContext* cx, Handle<SetObject*> obj, HandleValue key,
MutableHandleValue rval);
@ -712,6 +714,8 @@ bool MapObjectHas(JSContext* cx, Handle<MapObject*> obj, HandleValue key,
bool* rval);
bool MapObjectGet(JSContext* cx, Handle<MapObject*> obj, HandleValue key,
MutableHandleValue rval);
bool MapObjectDelete(JSContext* cx, Handle<MapObject*> obj, HandleValue key,
bool* rval);
bool MapObjectSet(JSContext* cx, Handle<MapObject*> obj, HandleValue key,
HandleValue val);
bool MapObjectSetFromIC(JSContext* cx, Handle<MapObject*> obj, HandleValue key,

View File

@ -5282,6 +5282,18 @@ bool WarpCacheIRTranspiler::emitSetHasResult(ObjOperandId setId,
return true;
}
bool WarpCacheIRTranspiler::emitSetDeleteResult(ObjOperandId setId,
ValOperandId keyId) {
MDefinition* set = getOperand(setId);
MDefinition* key = getOperand(keyId);
auto* ins = MSetObjectDelete::New(alloc(), set, key);
addEffectful(ins);
pushResult(ins);
return resumeAfter(ins);
}
bool WarpCacheIRTranspiler::emitSetAddResult(ObjOperandId setId,
ValOperandId keyId) {
MDefinition* set = getOperand(setId);
@ -5512,6 +5524,18 @@ bool WarpCacheIRTranspiler::emitMapGetResult(ObjOperandId mapId,
return true;
}
bool WarpCacheIRTranspiler::emitMapDeleteResult(ObjOperandId mapId,
ValOperandId keyId) {
MDefinition* map = getOperand(mapId);
MDefinition* key = getOperand(keyId);
auto* ins = MMapObjectDelete::New(alloc(), map, key);
addEffectful(ins);
pushResult(ins);
return resumeAfter(ins);
}
bool WarpCacheIRTranspiler::emitMapSetResult(ObjOperandId mapId,
ValOperandId keyId,
ValOperandId valId) {