Bug 1385278 - Move IsCacheable* functions to CacheIR from Ion. r=jandem
--- a/js/src/jit/CacheIR.cpp
+++ b/js/src/jit/CacheIR.cpp
@@ -122,16 +122,43 @@ GetProxyStubType(JSContext* cx, HandleOb
return ProxyStubType::DOMExpando;
return ProxyStubType::DOMShadowed;
}
MOZ_ASSERT(shadows == DoesntShadow || shadows == DoesntShadowUnique);
return ProxyStubType::DOMUnshadowed;
}
+static bool
+ValueToNameOrSymbolId(JSContext* cx, HandleValue idval, MutableHandleId id,
+ bool* nameOrSymbol)
+{
+ *nameOrSymbol = false;
+
+ if (!idval.isString() && !idval.isSymbol())
+ return true;
+
+ if (!ValueToId<CanGC>(cx, idval, id))
+ return false;
+
+ if (!JSID_IS_STRING(id) && !JSID_IS_SYMBOL(id)) {
+ id.set(JSID_VOID);
+ return true;
+ }
+
+ uint32_t dummy;
+ if (JSID_IS_STRING(id) && JSID_TO_ATOM(id)->isIndex(&dummy)) {
+ id.set(JSID_VOID);
+ return true;
+ }
+
+ *nameOrSymbol = true;
+ return true;
+}
+
bool
GetPropIRGenerator::tryAttachStub()
{
// Idempotent ICs should call tryAttachIdempotentStub instead.
MOZ_ASSERT(!idempotent());
AutoAssertNoPendingException aanpe(cx_);
@@ -268,16 +295,109 @@ GetPropIRGenerator::tryAttachIdempotentS
// Also support native data properties on DOMProxy prototypes.
if (GetProxyStubType(cx_, obj, id) == ProxyStubType::DOMUnshadowed)
return tryAttachDOMProxyUnshadowed(obj, objId, id);
return false;
}
static bool
+IsCacheableProtoChain(JSObject* obj, JSObject* holder)
+{
+ while (obj != holder) {
+ /*
+ * We cannot assume that we find the holder object on the prototype
+ * chain and must check for null proto. The prototype chain can be
+ * altered during the lookupProperty call.
+ */
+ JSObject* proto = obj->staticPrototype();
+ if (!proto || !proto->isNative())
+ return false;
+ obj = proto;
+ }
+ return true;
+}
+
+static bool
+IsCacheableGetPropReadSlot(JSObject* obj, JSObject* holder, PropertyResult prop)
+{
+ if (!prop || !IsCacheableProtoChain(obj, holder))
+ return false;
+
+ Shape* shape = prop.shape();
+ if (!shape->hasSlot() || !shape->hasDefaultGetter())
+ return false;
+
+ return true;
+}
+
+static bool
+IsCacheableGetPropCallNative(JSObject* obj, JSObject* holder, Shape* shape)
+{
+ if (!shape || !IsCacheableProtoChain(obj, holder))
+ return false;
+
+ if (!shape->hasGetterValue() || !shape->getterValue().isObject())
+ return false;
+
+ if (!shape->getterValue().toObject().is<JSFunction>())
+ return false;
+
+ JSFunction& getter = shape->getterValue().toObject().as<JSFunction>();
+ if (!getter.isNative())
+ return false;
+
+ if (getter.isClassConstructor())
+ return false;
+
+ // Check for a getter that has jitinfo and whose jitinfo says it's
+ // OK with both inner and outer objects.
+ if (getter.jitInfo() && !getter.jitInfo()->needsOuterizedThisObject())
+ return true;
+
+ // For getters that need the WindowProxy (instead of the Window) as this
+ // object, don't cache if obj is the Window, since our cache will pass that
+ // instead of the WindowProxy.
+ return !IsWindow(obj);
+}
+
+static bool
+IsCacheableGetPropCallScripted(JSObject* obj, JSObject* holder, Shape* shape,
+ bool* isTemporarilyUnoptimizable = nullptr)
+{
+ if (!shape || !IsCacheableProtoChain(obj, holder))
+ return false;
+
+ if (!shape->hasGetterValue() || !shape->getterValue().isObject())
+ return false;
+
+ if (!shape->getterValue().toObject().is<JSFunction>())
+ return false;
+
+ // See IsCacheableGetPropCallNative.
+ if (IsWindow(obj))
+ return false;
+
+ JSFunction& getter = shape->getterValue().toObject().as<JSFunction>();
+ if (getter.isNative())
+ return false;
+
+ if (!getter.hasJITCode()) {
+ if (isTemporarilyUnoptimizable)
+ *isTemporarilyUnoptimizable = true;
+ return false;
+ }
+
+ if (getter.isClassConstructor())
+ return false;
+
+ return true;
+}
+
+static bool
IsCacheableNoProperty(JSContext* cx, JSObject* obj, JSObject* holder, Shape* shape, jsid id,
jsbytecode* pc, GetPropertyResultFlags resultFlags)
{
if (shape)
return false;
MOZ_ASSERT(!holder);
@@ -322,17 +442,17 @@ CanAttachNativeGetProp(JSContext* cx, Ha
MOZ_ASSERT(!holder);
if (baseHolder) {
if (!baseHolder->isNative())
return CanAttachNone;
holder.set(&baseHolder->as<NativeObject>());
}
shape.set(prop.maybeShape());
- if (IsCacheableGetPropReadSlotForIonOrCacheIR(obj, holder, prop))
+ if (IsCacheableGetPropReadSlot(obj, holder, prop))
return CanAttachReadSlot;
if (IsCacheableNoProperty(cx, obj, holder, shape, id, pc, resultFlags))
return CanAttachReadSlot;
// Idempotent ICs cannot call getters, see tryAttachIdempotentStub.
if (pc && (resultFlags & GetPropertyResultFlags::Monitored)) {
if (IsCacheableGetPropCallScripted(obj, holder, shape, isTemporarilyUnoptimizable))
@@ -1861,18 +1981,17 @@ GetNameIRGenerator::tryAttachGlobalNameV
// non-configurable, and this stub cannot be shared across globals.
size_t dynamicSlotOffset = holder->dynamicSlotIndex(shape->slot()) * sizeof(Value);
writer.loadDynamicSlotResult(objId, dynamicSlotOffset);
} else {
// Check the prototype chain from the global to the holder
// prototype. Ignore the global lexical scope as it doesn't figure
// into the prototype chain. We guard on the global lexical
// scope's shape independently.
- if (!IsCacheableGetPropReadSlotForIonOrCacheIR(&globalLexical->global(), holder,
- PropertyResult(shape)))
+ if (!IsCacheableGetPropReadSlot(&globalLexical->global(), holder, PropertyResult(shape)))
return false;
// Shape guard for global lexical.
writer.guardShape(objId, globalLexical->lastProperty());
// Guard on the shape of the GlobalObject.
ObjOperandId globalId = writer.loadEnclosingEnvironment(objId);
writer.guardShape(globalId, globalLexical->global().lastProperty());
@@ -1982,17 +2101,17 @@ GetNameIRGenerator::tryAttachEnvironment
shape = env->as<NativeObject>().lookup(cx_, id);
if (shape)
break;
env = env->enclosingEnvironment();
}
holder = &env->as<NativeObject>();
- if (!IsCacheableGetPropReadSlotForIonOrCacheIR(holder, holder, PropertyResult(shape)))
+ if (!IsCacheableGetPropReadSlot(holder, holder, PropertyResult(shape)))
return false;
if (holder->getSlot(shape->slot()).isMagic())
return false;
ObjOperandId lastObjId = objId;
env = env_;
while (env) {
if (NeedEnvironmentShapeGuard(env))
@@ -2823,16 +2942,73 @@ SetPropIRGenerator::trackNotAttached()
sp.valueProperty(guard, "property", idVal_);
sp.valueProperty(guard, "value", rhsVal_);
sp.endCache(guard);
}
#endif
}
static bool
+IsCacheableSetPropCallNative(JSObject* obj, JSObject* holder, Shape* shape)
+{
+ if (!shape || !IsCacheableProtoChain(obj, holder))
+ return false;
+
+ if (!shape->hasSetterValue())
+ return false;
+
+ if (!shape->setterObject() || !shape->setterObject()->is<JSFunction>())
+ return false;
+
+ JSFunction& setter = shape->setterObject()->as<JSFunction>();
+ if (!setter.isNative())
+ return false;
+
+ if (setter.isClassConstructor())
+ return false;
+
+ if (setter.jitInfo() && !setter.jitInfo()->needsOuterizedThisObject())
+ return true;
+
+ return !IsWindow(obj);
+}
+
+static bool
+IsCacheableSetPropCallScripted(JSObject* obj, JSObject* holder, Shape* shape,
+ bool* isTemporarilyUnoptimizable = nullptr)
+{
+ if (!shape || !IsCacheableProtoChain(obj, holder))
+ return false;
+
+ if (IsWindow(obj))
+ return false;
+
+ if (!shape->hasSetterValue())
+ return false;
+
+ if (!shape->setterObject() || !shape->setterObject()->is<JSFunction>())
+ return false;
+
+ JSFunction& setter = shape->setterObject()->as<JSFunction>();
+ if (setter.isNative())
+ return false;
+
+ if (!setter.hasJITCode()) {
+ if (isTemporarilyUnoptimizable)
+ *isTemporarilyUnoptimizable = true;
+ return false;
+ }
+
+ if (setter.isClassConstructor())
+ return false;
+
+ return true;
+}
+
+static bool
CanAttachSetter(JSContext* cx, jsbytecode* pc, HandleObject obj, HandleId id,
MutableHandleObject holder, MutableHandleShape propShape,
bool* isTemporarilyUnoptimizable)
{
// Don't attach a setter stub for ops like JSOP_INITELEM.
MOZ_ASSERT(IsPropertySetOp(JSOp(*pc)));
PropertyResult prop;
--- a/js/src/jit/IonCaches.cpp
+++ b/js/src/jit/IonCaches.cpp
@@ -96,196 +96,16 @@ jit::GetReturnAddressToIonCode(JSContext
void* returnAddr = iter.returnAddress();
#ifdef DEBUG
++iter;
MOZ_ASSERT(iter.isIonJS());
#endif
return returnAddr;
}
-// Note: This differs from IsCacheableProtoChain in BaselineIC.cpp in that
-// Ion caches can deal with objects on the proto chain that have uncacheable
-// prototypes.
-bool
-jit::IsCacheableProtoChainForIonOrCacheIR(JSObject* obj, JSObject* holder)
-{
- while (obj != holder) {
- /*
- * We cannot assume that we find the holder object on the prototype
- * chain and must check for null proto. The prototype chain can be
- * altered during the lookupProperty call.
- */
- JSObject* proto = obj->staticPrototype();
- if (!proto || !proto->isNative())
- return false;
- obj = proto;
- }
- return true;
-}
-
-bool
-jit::IsCacheableGetPropReadSlotForIonOrCacheIR(JSObject* obj, JSObject* holder, PropertyResult prop)
-{
- if (!prop || !IsCacheableProtoChainForIonOrCacheIR(obj, holder))
- return false;
-
- Shape* shape = prop.shape();
- if (!shape->hasSlot() || !shape->hasDefaultGetter())
- return false;
-
- return true;
-}
-
-bool
-jit::IsCacheableGetPropCallNative(JSObject* obj, JSObject* holder, Shape* shape)
-{
- if (!shape || !IsCacheableProtoChainForIonOrCacheIR(obj, holder))
- return false;
-
- if (!shape->hasGetterValue() || !shape->getterValue().isObject())
- return false;
-
- if (!shape->getterValue().toObject().is<JSFunction>())
- return false;
-
- JSFunction& getter = shape->getterValue().toObject().as<JSFunction>();
- if (!getter.isNative())
- return false;
-
- if (getter.isClassConstructor())
- return false;
-
- // Check for a getter that has jitinfo and whose jitinfo says it's
- // OK with both inner and outer objects.
- if (getter.jitInfo() && !getter.jitInfo()->needsOuterizedThisObject())
- return true;
-
- // For getters that need the WindowProxy (instead of the Window) as this
- // object, don't cache if obj is the Window, since our cache will pass that
- // instead of the WindowProxy.
- return !IsWindow(obj);
-}
-
-bool
-jit::IsCacheableGetPropCallScripted(JSObject* obj, JSObject* holder, Shape* shape,
- bool* isTemporarilyUnoptimizable)
-{
- if (!shape || !IsCacheableProtoChainForIonOrCacheIR(obj, holder))
- return false;
-
- if (!shape->hasGetterValue() || !shape->getterValue().isObject())
- return false;
-
- if (!shape->getterValue().toObject().is<JSFunction>())
- return false;
-
- // See IsCacheableGetPropCallNative.
- if (IsWindow(obj))
- return false;
-
- JSFunction& getter = shape->getterValue().toObject().as<JSFunction>();
- if (getter.isNative())
- return false;
-
- if (!getter.hasJITCode()) {
- if (isTemporarilyUnoptimizable)
- *isTemporarilyUnoptimizable = true;
- return false;
- }
-
- if (getter.isClassConstructor())
- return false;
-
- return true;
-}
-
-bool
-jit::ValueToNameOrSymbolId(JSContext* cx, HandleValue idval, MutableHandleId id,
- bool* nameOrSymbol)
-{
- *nameOrSymbol = false;
-
- if (!idval.isString() && !idval.isSymbol())
- return true;
-
- if (!ValueToId<CanGC>(cx, idval, id))
- return false;
-
- if (!JSID_IS_STRING(id) && !JSID_IS_SYMBOL(id)) {
- id.set(JSID_VOID);
- return true;
- }
-
- uint32_t dummy;
- if (JSID_IS_STRING(id) && JSID_TO_ATOM(id)->isIndex(&dummy)) {
- id.set(JSID_VOID);
- return true;
- }
-
- *nameOrSymbol = true;
- return true;
-}
-
-bool
-jit::IsCacheableSetPropCallNative(JSObject* obj, JSObject* holder, Shape* shape)
-{
- if (!shape || !IsCacheableProtoChainForIonOrCacheIR(obj, holder))
- return false;
-
- if (!shape->hasSetterValue())
- return false;
-
- if (!shape->setterObject() || !shape->setterObject()->is<JSFunction>())
- return false;
-
- JSFunction& setter = shape->setterObject()->as<JSFunction>();
- if (!setter.isNative())
- return false;
-
- if (setter.isClassConstructor())
- return false;
-
- if (setter.jitInfo() && !setter.jitInfo()->needsOuterizedThisObject())
- return true;
-
- return !IsWindow(obj);
-}
-
-bool
-jit::IsCacheableSetPropCallScripted(JSObject* obj, JSObject* holder, Shape* shape,
- bool* isTemporarilyUnoptimizable)
-{
- if (!shape || !IsCacheableProtoChainForIonOrCacheIR(obj, holder))
- return false;
-
- if (IsWindow(obj))
- return false;
-
- if (!shape->hasSetterValue())
- return false;
-
- if (!shape->setterObject() || !shape->setterObject()->is<JSFunction>())
- return false;
-
- JSFunction& setter = shape->setterObject()->as<JSFunction>();
- if (setter.isNative())
- return false;
-
- if (!setter.hasJITCode()) {
- if (isTemporarilyUnoptimizable)
- *isTemporarilyUnoptimizable = true;
- return false;
- }
-
- if (setter.isClassConstructor())
- return false;
-
- return true;
-}
-
void
jit::EmitIonStoreDenseElement(MacroAssembler& masm, const ConstantOrRegister& value,
Register elements, BaseObjectElementIndex target)
{
// If the ObjectElements::CONVERT_DOUBLE_ELEMENTS flag is set, int32 values
// have to be converted to double first. If the value is not int32, it can
// always be stored directly.
--- a/js/src/jit/IonCaches.h
+++ b/js/src/jit/IonCaches.h
@@ -21,31 +21,16 @@
#include "jit/shared/Assembler-shared.h"
#include "js/TrackedOptimizationInfo.h"
#include "vm/TypedArrayObject.h"
namespace js {
namespace jit {
-bool IsCacheableProtoChainForIonOrCacheIR(JSObject* obj, JSObject* holder);
-bool IsCacheableGetPropReadSlotForIonOrCacheIR(JSObject* obj, JSObject* holder,
- PropertyResult prop);
-
-bool IsCacheableGetPropCallScripted(JSObject* obj, JSObject* holder, Shape* shape,
- bool* isTemporarilyUnoptimizable = nullptr);
-bool IsCacheableGetPropCallNative(JSObject* obj, JSObject* holder, Shape* shape);
-
-bool IsCacheableSetPropCallScripted(JSObject* obj, JSObject* holder, Shape* shape,
- bool* isTemporarilyUnoptimizable = nullptr);
-bool IsCacheableSetPropCallNative(JSObject* obj, JSObject* holder, Shape* shape);
-
-bool ValueToNameOrSymbolId(JSContext* cx, HandleValue idval, MutableHandleId id,
- bool* nameOrSymbol);
-
void* GetReturnAddressToIonCode(JSContext* cx);
void EmitIonStoreDenseElement(MacroAssembler& masm, const ConstantOrRegister& value,
Register elements, BaseObjectElementIndex target);
} // namespace jit
} // namespace js