Bug 1357759 - Support megamorphic IC for JSOP_IN
MozReview-Commit-ID: Ll94KKuN5FH
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/has.js
@@ -0,0 +1,49 @@
+var max = 40;
+setJitCompilerOption("ion.warmup.trigger", max - 10);
+
+function simple() {
+ var array = [{a: 1}, {b: 1, a: 1}, {c: 1, a: 1}];
+ for (var i = 0; i < array.length; i++) {
+ var x = array[i];
+ assertEq("a" in x, true);
+ assertEq("d" in x, false);
+ }
+}
+
+function megamorphic() {
+ var array = [{a: 1}, {b: 1, a: 1}, {c: 1, a: 1},
+ {a: 1, b: 1}, {c: 1, e: 1, a: 1},
+ {__proto__:{e: 1, f: 1, a: 1, g: 1}},
+ {__proto__:{e: 1, f: 1, a: 1, g: 1, h: 1}}];
+ for (var i = 0; i < array.length; i++) {
+ var x = array[i];
+ assertEq("a" in x, true);
+ assertEq("d" in x, false);
+ }
+}
+
+function proto() {
+ var base = {a: 1};
+ var array = [{__proto__: base},
+ {__proto__: base, b: 1, a: 1},
+ {__proto__: base, c: 1, a: 1}];
+ for (var j = 0; j < 2; j++) {
+ for (var i = 0; i < array.length; i++) {
+ var x = array[i];
+ assertEq("a" in x, true);
+ assertEq("d" in x, (j > 0));
+ }
+ base.d = 1; // Define property on prototype
+ }
+}
+
+function test() {
+ for (var i = 0; i < max; i++) {
+ simple();
+ megamorphic();
+ proto();
+ }
+}
+
+test();
+test();
--- a/js/src/jit/CacheIR.cpp
+++ b/js/src/jit/CacheIR.cpp
@@ -2414,23 +2414,22 @@ HasPropIRGenerator::tryAttachNamedProp(H
return true;
return false;
}
bool
HasPropIRGenerator::tryAttachMegamorphic(ObjOperandId objId, ValOperandId keyId)
{
+ bool hasOwn = (cacheKind_ == CacheKind::HasOwn);
+
if (mode_ != ICState::Mode::Megamorphic)
return false;
- if (cacheKind_ != CacheKind::HasOwn)
- return false;
-
- writer.megamorphicHasOwnResult(objId, keyId);
+ writer.megamorphicHasPropResult(objId, keyId, hasOwn);
writer.returnFromIC();
trackAttached("MegamorphicHasProp");
return true;
}
bool
HasPropIRGenerator::tryAttachNative(JSObject* obj, ObjOperandId objId, jsid key,
ValOperandId keyId, PropertyResult prop, JSObject* holder)
--- a/js/src/jit/CacheIR.h
+++ b/js/src/jit/CacheIR.h
@@ -199,17 +199,17 @@ extern const char* CacheKindNames[];
_(LoadProto) \
_(LoadEnclosingEnvironment) \
_(LoadWrapperTarget) \
\
_(MegamorphicLoadSlotResult) \
_(MegamorphicLoadSlotByValueResult) \
_(MegamorphicStoreSlot) \
_(MegamorphicSetElement) \
- _(MegamorphicHasOwnResult) \
+ _(MegamorphicHasPropResult) \
\
/* See CacheIR.cpp 'DOM proxies' comment. */ \
_(LoadDOMExpandoValue) \
_(LoadDOMExpandoValueGuardGeneration) \
_(LoadDOMExpandoValueIgnoreGeneration)\
_(GuardDOMExpandoMissingOrGuardShape) \
\
_(StoreFixedSlot) \
@@ -882,19 +882,20 @@ class MOZ_RAII CacheIRWriter : public JS
buffer_.writeByte(needsTypeBarrier);
}
void megamorphicSetElement(ObjOperandId obj, ValOperandId id, ValOperandId rhs, bool strict) {
writeOpWithOperandId(CacheOp::MegamorphicSetElement, obj);
writeOperandId(id);
writeOperandId(rhs);
buffer_.writeByte(uint32_t(strict));
}
- void megamorphicHasOwnResult(ObjOperandId obj, ValOperandId id) {
- writeOpWithOperandId(CacheOp::MegamorphicHasOwnResult, obj);
+ void megamorphicHasPropResult(ObjOperandId obj, ValOperandId id, bool hasOwn) {
+ writeOpWithOperandId(CacheOp::MegamorphicHasPropResult, obj);
writeOperandId(id);
+ buffer_.writeByte(uint32_t(hasOwn));
}
void loadBooleanResult(bool val) {
writeOp(CacheOp::LoadBooleanResult);
buffer_.writeByte(uint32_t(val));
}
void loadUndefinedResult() {
writeOp(CacheOp::LoadUndefinedResult);
--- a/js/src/jit/CacheIRCompiler.cpp
+++ b/js/src/jit/CacheIRCompiler.cpp
@@ -2536,22 +2536,23 @@ CacheIRCompiler::emitMegamorphicLoadSlot
masm.bind(&ok);
masm.setFramePushed(framePushed);
masm.loadTypedOrValue(Address(masm.getStackPointer(), 0), output);
masm.adjustStack(sizeof(Value));
return true;
}
bool
-CacheIRCompiler::emitMegamorphicHasOwnResult()
+CacheIRCompiler::emitMegamorphicHasPropResult()
{
AutoOutputRegister output(*this);
Register obj = allocator.useRegister(masm, reader.objOperandId());
ValueOperand idVal = allocator.useValueRegister(masm, reader.valOperandId());
+ bool hasOwn = reader.readBool();
AutoScratchRegisterMaybeOutput scratch(allocator, masm, output);
FailurePath* failure;
if (!addFailurePath(&failure))
return false;
// idVal will be in vp[0], result will be stored in vp[1].
@@ -2564,17 +2565,20 @@ CacheIRCompiler::emitMegamorphicHasOwnRe
volatileRegs.takeUnchecked(idVal);
masm.PushRegsInMask(volatileRegs);
masm.setupUnalignedABICall(scratch);
masm.loadJSContext(scratch);
masm.passABIArg(scratch);
masm.passABIArg(obj);
masm.passABIArg(idVal.scratchReg());
- masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, HasOwnNativeDataProperty));
+ if (hasOwn)
+ masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, HasNativeDataProperty<true>));
+ else
+ masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, HasNativeDataProperty<false>));
masm.mov(ReturnReg, scratch);
masm.PopRegsInMask(volatileRegs);
masm.Pop(idVal);
Label ok;
uint32_t framePushed = masm.framePushed();
masm.branchIfTrueBool(scratch, &ok);
--- a/js/src/jit/CacheIRCompiler.h
+++ b/js/src/jit/CacheIRCompiler.h
@@ -57,17 +57,17 @@ namespace jit {
_(LoadTypeOfObjectResult) \
_(CompareStringResult) \
_(CompareObjectResult) \
_(CompareSymbolResult) \
_(ArrayJoinResult) \
_(CallPrintString) \
_(Breakpoint) \
_(MegamorphicLoadSlotByValueResult) \
- _(MegamorphicHasOwnResult) \
+ _(MegamorphicHasPropResult) \
_(WrapResult)
// Represents a Value on the Baseline frame's expression stack. Slot 0 is the
// value on top of the stack (the most recently pushed value), slot 1 is the
// value pushed before that, etc.
class BaselineFrameSlot
{
uint32_t slot_;
--- a/js/src/jit/VMFunctions.cpp
+++ b/js/src/jit/VMFunctions.cpp
@@ -1776,53 +1776,73 @@ ObjectHasGetterSetter(JSContext* cx, JSO
return false;
if (!proto->isNative())
return false;
nobj = &proto->as<NativeObject>();
}
}
+template <bool HasOwn>
bool
-HasOwnNativeDataProperty(JSContext* cx, JSObject* obj, Value* vp)
+HasNativeDataProperty(JSContext* cx, JSObject* obj, Value* vp)
{
AutoUnsafeCallWithABI unsafe;
// vp[0] contains the id, result will be stored in vp[1].
Value idVal = vp[0];
jsid id;
if (!ValueToAtomOrSymbol(cx, idVal, &id))
return false;
- if (!obj->isNative()) {
- if (obj->is<UnboxedPlainObject>()) {
- bool res = obj->as<UnboxedPlainObject>().containsUnboxedOrExpandoProperty(cx, id);
- vp[1].setBoolean(res);
- return true;
- }
- return false;
- }
+ do {
+ if (obj->isNative()) {
+ if (obj->as<NativeObject>().lastProperty()->search(cx, id)) {
+ vp[1].setBoolean(true);
+ return true;
+ }
- NativeObject* nobj = &obj->as<NativeObject>();
- if (nobj->lastProperty()->search(cx, id)) {
- vp[1].setBoolean(true);
- return true;
- }
+ // Fail if there's a resolve hook, unless the mayResolve hook tells
+ // us the resolve hook won't define a property with this id.
+ if (MOZ_UNLIKELY(ClassMayResolveId(cx->names(), obj->getClass(), id, obj)))
+ return false;
+ } else if (obj->is<UnboxedPlainObject>()) {
+ if (obj->as<UnboxedPlainObject>().containsUnboxedOrExpandoProperty(cx, id)) {
+ vp[1].setBoolean(true);
+ return true;
+ }
+ } else if (obj->is<TypedObject>()) {
+ if (obj->as<TypedObject>().typeDescr().hasProperty(cx->names(), id)) {
+ vp[1].setBoolean(true);
+ return true;
+ }
+ } else {
+ return false;
+ }
- // Property not found. Watch out for Class hooks.
- if (MOZ_UNLIKELY(!nobj->is<PlainObject>())) {
- if (ClassMayResolveId(cx->names(), nobj->getClass(), id, nobj))
- return false;
- }
+ // If implementing Object.hasOwnProperty, don't follow protochain.
+ if (HasOwn)
+ break;
+
+ // Get prototype. Objects that may allow dynamic prototypes are already
+ // filtered out above.
+ obj = obj->staticPrototype();
+ } while (obj);
// Missing property.
vp[1].setBoolean(false);
return true;
}
+template bool
+HasNativeDataProperty<true>(JSContext* cx, JSObject* obj, Value* vp);
+
+template bool
+HasNativeDataProperty<false>(JSContext* cx, JSObject* obj, Value* vp);
+
JSString*
TypeOfObject(JSObject* obj, JSRuntime* rt)
{
AutoUnsafeCallWithABI unsafe;
JSType type = js::TypeOfObject(obj);
return TypeName(type, *rt->commonNames);
}
--- a/js/src/jit/VMFunctions.h
+++ b/js/src/jit/VMFunctions.h
@@ -871,18 +871,19 @@ CheckIsCallable(JSContext* cx, HandleVal
template <bool HandleMissing>
bool
GetNativeDataProperty(JSContext* cx, JSObject* obj, PropertyName* name, Value* vp);
template <bool HandleMissing>
bool
GetNativeDataPropertyByValue(JSContext* cx, JSObject* obj, Value* vp);
+template <bool HasOwn>
bool
-HasOwnNativeDataProperty(JSContext* cx, JSObject* obj, Value* vp);
+HasNativeDataProperty(JSContext* cx, JSObject* obj, Value* vp);
template <bool NeedsTypeBarrier>
bool
SetNativeDataProperty(JSContext* cx, JSObject* obj, PropertyName* name, Value* val);
bool
ObjectHasGetterSetter(JSContext* cx, JSObject* obj, Shape* propShape);