mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-26 22:32:46 +00:00
Bug 1932305 part 12 - Optimize calls to Map.prototype.set and Set.prototype.add. r=iain
This is just a VM call for now but this could be optimized more later. These functions return the `Map`/`Set` object so that the calls can be chained, but we don't have a good way to do this in the CacheIR compiler after the VM call. This adds separate VM functions for the ICs that return the object. Differential Revision: https://phabricator.services.mozilla.com/D229608
This commit is contained in:
parent
65be165dc2
commit
b1494ee1be
@ -432,7 +432,7 @@ const JSPropertySpec MapObject::properties[] = {
|
||||
const JSFunctionSpec MapObject::methods[] = {
|
||||
JS_INLINABLE_FN("get", get, 1, 0, MapGet),
|
||||
JS_INLINABLE_FN("has", has, 1, 0, MapHas),
|
||||
JS_FN("set", set, 2, 0),
|
||||
JS_INLINABLE_FN("set", set, 2, 0, MapSet),
|
||||
JS_FN("delete", delete_, 1, 0),
|
||||
JS_FN("keys", keys, 0, 0),
|
||||
JS_FN("values", values, 0, 0),
|
||||
@ -1191,7 +1191,7 @@ const JSPropertySpec SetObject::properties[] = {
|
||||
|
||||
const JSFunctionSpec SetObject::methods[] = {
|
||||
JS_INLINABLE_FN("has", has, 1, 0, SetHas),
|
||||
JS_FN("add", add, 1, 0),
|
||||
JS_INLINABLE_FN("add", add, 1, 0, SetAdd),
|
||||
JS_FN("delete", delete_, 1, 0),
|
||||
JS_FN("entries", entries, 0, 0),
|
||||
JS_FN("clear", clear, 0, 0),
|
||||
|
11
js/src/jit-test/tests/cacheir/map-set-chaining.js
Normal file
11
js/src/jit-test/tests/cacheir/map-set-chaining.js
Normal file
@ -0,0 +1,11 @@
|
||||
function test() {
|
||||
var m = new Map();
|
||||
var s = new Set();
|
||||
for (var i = 0; i < 100; i++) {
|
||||
m.set("a", 0).set(i, i).set("a", 1);
|
||||
s.add("a").add(i).add("a");
|
||||
}
|
||||
assertEq(m.size, 101);
|
||||
assertEq(s.size, 101);
|
||||
}
|
||||
test();
|
@ -10242,6 +10242,37 @@ AttachDecision InlinableNativeIRGenerator::tryAttachSetHas() {
|
||||
return AttachDecision::Attach;
|
||||
}
|
||||
|
||||
AttachDecision InlinableNativeIRGenerator::tryAttachSetAdd() {
|
||||
// Ensure |this| is a SetObject.
|
||||
if (!thisval_.isObject() || !thisval_.toObject().is<SetObject>()) {
|
||||
return AttachDecision::NoAction;
|
||||
}
|
||||
|
||||
// Need one argument.
|
||||
if (argc_ != 1) {
|
||||
return AttachDecision::NoAction;
|
||||
}
|
||||
|
||||
// Initialize the input operand.
|
||||
initializeInputOperand();
|
||||
|
||||
// Guard callee is the 'add' 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 keyId = writer.loadArgumentFixedSlot(ArgumentKind::Arg0, argc_);
|
||||
writer.setAddResult(objId, keyId);
|
||||
writer.returnFromIC();
|
||||
|
||||
trackAttached("SetAdd");
|
||||
return AttachDecision::Attach;
|
||||
}
|
||||
|
||||
AttachDecision InlinableNativeIRGenerator::tryAttachSetSize() {
|
||||
// Ensure |this| is a SetObject.
|
||||
if (!thisval_.isObject() || !thisval_.toObject().is<SetObject>()) {
|
||||
@ -10446,6 +10477,44 @@ AttachDecision InlinableNativeIRGenerator::tryAttachMapGet() {
|
||||
return AttachDecision::Attach;
|
||||
}
|
||||
|
||||
AttachDecision InlinableNativeIRGenerator::tryAttachMapSet() {
|
||||
#ifdef JS_CODEGEN_X86
|
||||
// 32-bit x86 does not have enough registers for the AutoCallVM output, the
|
||||
// MapObject*, and two Values.
|
||||
return AttachDecision::NoAction;
|
||||
#endif
|
||||
|
||||
// Ensure |this| is a MapObject.
|
||||
if (!thisval_.isObject() || !thisval_.toObject().is<MapObject>()) {
|
||||
return AttachDecision::NoAction;
|
||||
}
|
||||
|
||||
// Need two arguments.
|
||||
if (argc_ != 2) {
|
||||
return AttachDecision::NoAction;
|
||||
}
|
||||
|
||||
// Initialize the input operand.
|
||||
initializeInputOperand();
|
||||
|
||||
// Guard callee is the 'set' 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 keyId = writer.loadArgumentFixedSlot(ArgumentKind::Arg0, argc_);
|
||||
ValOperandId valId = writer.loadArgumentFixedSlot(ArgumentKind::Arg1, argc_);
|
||||
writer.mapSetResult(objId, keyId, valId);
|
||||
writer.returnFromIC();
|
||||
|
||||
trackAttached("MapSet");
|
||||
return AttachDecision::Attach;
|
||||
}
|
||||
|
||||
AttachDecision InlinableNativeIRGenerator::tryAttachDateGetTime(
|
||||
InlinableNative native) {
|
||||
// Ensure |this| is a DateObject.
|
||||
@ -12332,6 +12401,8 @@ AttachDecision InlinableNativeIRGenerator::tryAttachStub() {
|
||||
return AttachDecision::NoAction; // Not callable.
|
||||
case InlinableNative::SetHas:
|
||||
return tryAttachSetHas();
|
||||
case InlinableNative::SetAdd:
|
||||
return tryAttachSetAdd();
|
||||
case InlinableNative::SetSize:
|
||||
return tryAttachSetSize();
|
||||
|
||||
@ -12342,6 +12413,8 @@ AttachDecision InlinableNativeIRGenerator::tryAttachStub() {
|
||||
return tryAttachMapHas();
|
||||
case InlinableNative::MapGet:
|
||||
return tryAttachMapGet();
|
||||
case InlinableNative::MapSet:
|
||||
return tryAttachMapSet();
|
||||
|
||||
// Date natives and intrinsics.
|
||||
case InlinableNative::DateGetTime:
|
||||
|
@ -10584,6 +10584,24 @@ bool CacheIRCompiler::emitSetHasObjectResult(ObjOperandId setId,
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CacheIRCompiler::emitSetAddResult(ObjOperandId setId, ValOperandId keyId) {
|
||||
JitSpew(JitSpew_Codegen, "%s", __FUNCTION__);
|
||||
|
||||
AutoCallVM callvm(masm, this, allocator);
|
||||
|
||||
Register set = allocator.useRegister(masm, setId);
|
||||
ValueOperand key = allocator.useValueRegister(masm, keyId);
|
||||
|
||||
callvm.prepare();
|
||||
masm.Push(key);
|
||||
masm.Push(set);
|
||||
|
||||
using Fn =
|
||||
bool (*)(JSContext*, Handle<SetObject*>, HandleValue, MutableHandleValue);
|
||||
callvm.call<Fn, jit::SetObjectAddFromIC>();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CacheIRCompiler::emitSetSizeResult(ObjOperandId setId) {
|
||||
JitSpew(JitSpew_Codegen, "%s", __FUNCTION__);
|
||||
|
||||
@ -10735,6 +10753,27 @@ bool CacheIRCompiler::emitMapGetResult(ObjOperandId mapId, ValOperandId valId) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CacheIRCompiler::emitMapSetResult(ObjOperandId mapId, ValOperandId keyId,
|
||||
ValOperandId valId) {
|
||||
JitSpew(JitSpew_Codegen, "%s", __FUNCTION__);
|
||||
|
||||
AutoCallVM callvm(masm, this, allocator);
|
||||
|
||||
Register map = allocator.useRegister(masm, mapId);
|
||||
ValueOperand key = allocator.useValueRegister(masm, keyId);
|
||||
ValueOperand val = allocator.useValueRegister(masm, valId);
|
||||
|
||||
callvm.prepare();
|
||||
masm.Push(val);
|
||||
masm.Push(key);
|
||||
masm.Push(map);
|
||||
|
||||
using Fn = bool (*)(JSContext*, Handle<MapObject*>, HandleValue, HandleValue,
|
||||
MutableHandleValue);
|
||||
callvm.call<Fn, jit::MapObjectSetFromIC>();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CacheIRCompiler::emitMapGetNonGCThingResult(ObjOperandId mapId,
|
||||
ValOperandId valId) {
|
||||
JitSpew(JitSpew_Codegen, "%s", __FUNCTION__);
|
||||
|
@ -771,9 +771,11 @@ class MOZ_RAII InlinableNativeIRGenerator {
|
||||
AttachDecision tryAttachBigIntAsIntN();
|
||||
AttachDecision tryAttachBigIntAsUintN();
|
||||
AttachDecision tryAttachSetHas();
|
||||
AttachDecision tryAttachSetAdd();
|
||||
AttachDecision tryAttachSetSize();
|
||||
AttachDecision tryAttachMapHas();
|
||||
AttachDecision tryAttachMapGet();
|
||||
AttachDecision tryAttachMapSet();
|
||||
AttachDecision tryAttachDateGetTime(InlinableNative native);
|
||||
AttachDecision tryAttachDateGet(DateComponent component);
|
||||
#ifdef FUZZING_JS_FUZZILLI
|
||||
|
@ -3420,6 +3420,14 @@
|
||||
set: ObjId
|
||||
obj: ObjId
|
||||
|
||||
- name: SetAddResult
|
||||
shared: true
|
||||
transpile: true
|
||||
cost_estimate: 5
|
||||
args:
|
||||
set: ObjId
|
||||
key: ValId
|
||||
|
||||
- name: SetSizeResult
|
||||
shared: true
|
||||
transpile: true
|
||||
@ -3523,6 +3531,15 @@
|
||||
map: ObjId
|
||||
obj: ObjId
|
||||
|
||||
- name: MapSetResult
|
||||
shared: true
|
||||
transpile: true
|
||||
cost_estimate: 5
|
||||
args:
|
||||
map: ObjId
|
||||
key: ValId
|
||||
val: ValId
|
||||
|
||||
- name: MapSizeResult
|
||||
shared: true
|
||||
transpile: true
|
||||
|
@ -21667,6 +21667,13 @@ void CodeGenerator::visitSetObjectHasValueVMCall(
|
||||
callVM<Fn, jit::SetObjectHas>(ins);
|
||||
}
|
||||
|
||||
void CodeGenerator::visitSetObjectAdd(LSetObjectAdd* ins) {
|
||||
pushArg(ToValue(ins, LSetObjectAdd::KeyIndex));
|
||||
pushArg(ToRegister(ins->setObject()));
|
||||
using Fn = bool (*)(JSContext*, Handle<SetObject*>, HandleValue);
|
||||
callVM<Fn, jit::SetObjectAdd>(ins);
|
||||
}
|
||||
|
||||
void CodeGenerator::visitSetObjectSize(LSetObjectSize* ins) {
|
||||
Register setObj = ToRegister(ins->setObject());
|
||||
Register output = ToRegister(ins->output());
|
||||
@ -21772,6 +21779,14 @@ void CodeGenerator::visitMapObjectGetValueVMCall(
|
||||
callVM<Fn, jit::MapObjectGet>(ins);
|
||||
}
|
||||
|
||||
void CodeGenerator::visitMapObjectSet(LMapObjectSet* ins) {
|
||||
pushArg(ToValue(ins, LMapObjectSet::ValueIndex));
|
||||
pushArg(ToValue(ins, LMapObjectSet::KeyIndex));
|
||||
pushArg(ToRegister(ins->mapObject()));
|
||||
using Fn = bool (*)(JSContext*, Handle<MapObject*>, HandleValue, HandleValue);
|
||||
callVM<Fn, jit::MapObjectSet>(ins);
|
||||
}
|
||||
|
||||
void CodeGenerator::visitMapObjectSize(LMapObjectSize* ins) {
|
||||
Register mapObj = ToRegister(ins->mapObject());
|
||||
Register output = ToRegister(ins->output());
|
||||
|
@ -313,12 +313,14 @@ bool js::jit::CanInlineNativeCrossRealm(InlinableNative native) {
|
||||
case InlinableNative::MapConstructor:
|
||||
case InlinableNative::MapGet:
|
||||
case InlinableNative::MapHas:
|
||||
case InlinableNative::MapSet:
|
||||
case InlinableNative::Number:
|
||||
case InlinableNative::NumberParseInt:
|
||||
case InlinableNative::NumberToString:
|
||||
case InlinableNative::ReflectGetPrototypeOf:
|
||||
case InlinableNative::SetConstructor:
|
||||
case InlinableNative::SetHas:
|
||||
case InlinableNative::SetAdd:
|
||||
case InlinableNative::SetSize:
|
||||
case InlinableNative::String:
|
||||
case InlinableNative::StringToString:
|
||||
|
@ -99,6 +99,7 @@
|
||||
_(MapConstructor) \
|
||||
_(MapGet) \
|
||||
_(MapHas) \
|
||||
_(MapSet) \
|
||||
\
|
||||
_(MathAbs) \
|
||||
_(MathFloor) \
|
||||
@ -155,6 +156,7 @@
|
||||
\
|
||||
_(SetConstructor) \
|
||||
_(SetHas) \
|
||||
_(SetAdd) \
|
||||
_(SetSize) \
|
||||
\
|
||||
_(String) \
|
||||
|
@ -7666,6 +7666,13 @@ void LIRGenerator::visitSetObjectHasValueVMCall(MSetObjectHasValueVMCall* ins) {
|
||||
assignSafepoint(lir, ins);
|
||||
}
|
||||
|
||||
void LIRGenerator::visitSetObjectAdd(MSetObjectAdd* ins) {
|
||||
auto* lir = new (alloc()) LSetObjectAdd(useRegisterAtStart(ins->setObject()),
|
||||
useBoxAtStart(ins->key()));
|
||||
add(lir, ins);
|
||||
assignSafepoint(lir, ins);
|
||||
}
|
||||
|
||||
void LIRGenerator::visitSetObjectSize(MSetObjectSize* ins) {
|
||||
auto* lir = new (alloc()) LSetObjectSize(useRegisterAtStart(ins->set()));
|
||||
define(lir, ins);
|
||||
@ -7727,6 +7734,14 @@ void LIRGenerator::visitMapObjectGetValueVMCall(MMapObjectGetValueVMCall* ins) {
|
||||
assignSafepoint(lir, ins);
|
||||
}
|
||||
|
||||
void LIRGenerator::visitMapObjectSet(MMapObjectSet* ins) {
|
||||
auto* lir = new (alloc())
|
||||
LMapObjectSet(useRegisterAtStart(ins->mapObject()),
|
||||
useBoxAtStart(ins->key()), useBoxAtStart(ins->value()));
|
||||
add(lir, ins);
|
||||
assignSafepoint(lir, ins);
|
||||
}
|
||||
|
||||
void LIRGenerator::visitMapObjectSize(MMapObjectSize* ins) {
|
||||
auto* lir = new (alloc()) LMapObjectSize(useRegisterAtStart(ins->map()));
|
||||
define(lir, ins);
|
||||
|
@ -3252,6 +3252,13 @@
|
||||
alias_set: custom
|
||||
possibly_calls: true
|
||||
|
||||
- name: SetObjectAdd
|
||||
operands:
|
||||
setObject: Object
|
||||
key: Value
|
||||
possibly_calls: true
|
||||
generate_lir: true
|
||||
|
||||
- name: SetObjectSize
|
||||
operands:
|
||||
set: Object
|
||||
@ -3342,6 +3349,14 @@
|
||||
alias_set: custom
|
||||
possibly_calls: true
|
||||
|
||||
- name: MapObjectSet
|
||||
operands:
|
||||
mapObject: Object
|
||||
key: Value
|
||||
value: Value
|
||||
possibly_calls: true
|
||||
generate_lir: true
|
||||
|
||||
- name: MapObjectSize
|
||||
operands:
|
||||
map: Object
|
||||
|
@ -250,6 +250,8 @@ namespace jit {
|
||||
_(MapObjectCreate, js::MapObject::create) \
|
||||
_(MapObjectGet, js::jit::MapObjectGet) \
|
||||
_(MapObjectHas, js::jit::MapObjectHas) \
|
||||
_(MapObjectSet, js::jit::MapObjectSet) \
|
||||
_(MapObjectSetFromIC, js::jit::MapObjectSetFromIC) \
|
||||
_(MutatePrototype, js::jit::MutatePrototype) \
|
||||
_(NamedLambdaObjectCreateWithoutEnclosing, \
|
||||
js::NamedLambdaObject::createWithoutEnclosing) \
|
||||
@ -304,6 +306,8 @@ namespace jit {
|
||||
_(SetElementSuper, js::SetElementSuper) \
|
||||
_(SetFunctionName, js::SetFunctionName) \
|
||||
_(SetIntrinsicOperation, js::SetIntrinsicOperation) \
|
||||
_(SetObjectAdd, js::jit::SetObjectAdd) \
|
||||
_(SetObjectAddFromIC, js::jit::SetObjectAddFromIC) \
|
||||
_(SetObjectCreate, js::SetObject::create) \
|
||||
_(SetObjectHas, js::jit::SetObjectHas) \
|
||||
_(SetPropertyMegamorphicNoCache, js::jit::SetPropertyMegamorphic<false>) \
|
||||
|
@ -3115,6 +3115,19 @@ bool SetObjectHas(JSContext* cx, Handle<SetObject*> obj, HandleValue key,
|
||||
return obj->has(cx, key, rval);
|
||||
}
|
||||
|
||||
bool SetObjectAdd(JSContext* cx, Handle<SetObject*> obj, HandleValue key) {
|
||||
return obj->add(cx, key);
|
||||
}
|
||||
|
||||
bool SetObjectAddFromIC(JSContext* cx, Handle<SetObject*> obj, HandleValue key,
|
||||
MutableHandleValue rval) {
|
||||
if (!SetObjectAdd(cx, obj, key)) {
|
||||
return false;
|
||||
}
|
||||
rval.setObject(*obj);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MapObjectHas(JSContext* cx, Handle<MapObject*> obj, HandleValue key,
|
||||
bool* rval) {
|
||||
return obj->has(cx, key, rval);
|
||||
@ -3125,6 +3138,20 @@ bool MapObjectGet(JSContext* cx, Handle<MapObject*> obj, HandleValue key,
|
||||
return obj->get(cx, key, rval);
|
||||
}
|
||||
|
||||
bool MapObjectSet(JSContext* cx, Handle<MapObject*> obj, HandleValue key,
|
||||
HandleValue val) {
|
||||
return obj->set(cx, key, val);
|
||||
}
|
||||
|
||||
bool MapObjectSetFromIC(JSContext* cx, Handle<MapObject*> obj, HandleValue key,
|
||||
HandleValue val, MutableHandleValue rval) {
|
||||
if (!MapObjectSet(cx, obj, key, val)) {
|
||||
return false;
|
||||
}
|
||||
rval.setObject(*obj);
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
template <class T>
|
||||
static mozilla::HashNumber HashValue(JSContext* cx, T* obj,
|
||||
|
@ -705,10 +705,17 @@ JSAtom* AtomizeStringNoGC(JSContext* cx, JSString* str);
|
||||
|
||||
bool SetObjectHas(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);
|
||||
bool MapObjectHas(JSContext* cx, Handle<MapObject*> obj, HandleValue key,
|
||||
bool* rval);
|
||||
bool MapObjectGet(JSContext* cx, Handle<MapObject*> obj, HandleValue key,
|
||||
MutableHandleValue rval);
|
||||
bool MapObjectSet(JSContext* cx, Handle<MapObject*> obj, HandleValue key,
|
||||
HandleValue val);
|
||||
bool MapObjectSetFromIC(JSContext* cx, Handle<MapObject*> obj, HandleValue key,
|
||||
HandleValue val, MutableHandleValue rval);
|
||||
|
||||
void AssertSetObjectHash(JSContext* cx, SetObject* obj, const Value* value,
|
||||
mozilla::HashNumber actualHash);
|
||||
|
@ -5282,6 +5282,18 @@ bool WarpCacheIRTranspiler::emitSetHasResult(ObjOperandId setId,
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WarpCacheIRTranspiler::emitSetAddResult(ObjOperandId setId,
|
||||
ValOperandId keyId) {
|
||||
MDefinition* set = getOperand(setId);
|
||||
MDefinition* key = getOperand(keyId);
|
||||
|
||||
auto* ins = MSetObjectAdd::New(alloc(), set, key);
|
||||
addEffectful(ins);
|
||||
|
||||
pushResult(set);
|
||||
return resumeAfter(ins);
|
||||
}
|
||||
|
||||
bool WarpCacheIRTranspiler::emitSetSizeResult(ObjOperandId setId) {
|
||||
MDefinition* set = getOperand(setId);
|
||||
|
||||
@ -5500,6 +5512,20 @@ bool WarpCacheIRTranspiler::emitMapGetResult(ObjOperandId mapId,
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WarpCacheIRTranspiler::emitMapSetResult(ObjOperandId mapId,
|
||||
ValOperandId keyId,
|
||||
ValOperandId valId) {
|
||||
MDefinition* map = getOperand(mapId);
|
||||
MDefinition* key = getOperand(keyId);
|
||||
MDefinition* val = getOperand(valId);
|
||||
|
||||
auto* ins = MMapObjectSet::New(alloc(), map, key, val);
|
||||
addEffectful(ins);
|
||||
|
||||
pushResult(map);
|
||||
return resumeAfter(ins);
|
||||
}
|
||||
|
||||
bool WarpCacheIRTranspiler::emitMapSizeResult(ObjOperandId mapId) {
|
||||
MDefinition* map = getOperand(mapId);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user