#529: jsop_in folding M1244098 M1041586 (partial)

This commit is contained in:
Cameron Kaiser 2018-10-29 19:11:29 -07:00
parent c9ed275af7
commit a8fa640688
5 changed files with 173 additions and 18 deletions

View File

@ -16,6 +16,7 @@ namespace JS {
_(GetProp_ArgumentsCallee) \
_(GetProp_InferredConstant) \
_(GetProp_Constant) \
_(GetProp_NotDefined) \
_(GetProp_StaticName) \
_(GetProp_SimdGetter) \
_(GetProp_TypedObject) \
@ -74,6 +75,7 @@ namespace JS {
_(NotObject) \
_(NotStruct) \
_(NotUnboxed) \
_(NotUndefined) \
_(UnboxedConvertedToNative) \
_(StructNoField) \
_(InconsistentFieldType) \

View File

@ -0,0 +1,32 @@
// Singleton
function f() {
var res = 0;
for (var i=0; i<500; i++)
res += ("abcd" in Math);
return res;
}
assertEq(f(), 0);
Math.abcd = 3;
assertEq(f(), 500);
delete Math.abcd;
assertEq(f(), 0);
// Non-singleton
function O(x) { if (x) this.x = 1; }
var arr = [];
for (var i=0; i<4; i++)
arr.push(new O(i % 2));
function g(arr) {
var res = 0;
for (var i=0; i<500; i++) {
var o = arr[i % arr.length];
res += "x" in o;
res += "abcd" in o;
}
return res;
}
assertEq(g(arr), 250);
arr[0].abcd = 3;
assertEq(g(arr), 375);

View File

@ -135,9 +135,9 @@ function withinIf(i) {
// Check case where one successor can have multiple times the same predecessor.
function unknownLoad(i) {
var obj = { foo: i };
// Unknown properties are inlined as undefined.
assertEq(obj.bar, undefined);
// Unknown properties are using GetPropertyCache.
assertRecoveredOnBailout(obj, false);
assertRecoveredOnBailout(obj, true);
}
// Check with dynamic slots.

View File

@ -7928,6 +7928,51 @@ IonBuilder::testSingletonPropertyTypes(MDefinition* obj, jsid id)
return nullptr;
}
bool
IonBuilder::testNotDefinedProperty(MDefinition* obj, jsid id)
{
TemporaryTypeSet* types = obj->resultTypeSet();
if (!types || types->unknownObject() || types->getKnownMIRType() != MIRType_Object)
return false;
for (unsigned i = 0, count = types->getObjectCount(); i < count; i++) {
TypeSet::ObjectKey* key = types->getObject(i);
if (!key)
continue;
while (true) {
if (!key->hasStableClassAndProto(constraints()) || key->unknownProperties())
return false;
const Class* clasp = key->clasp();
if (!ClassHasEffectlessLookup(clasp) || ObjectHasExtraOwnProperty(compartment, key,
id))
return false;
// If the object is a singleton, we can do a lookup now to avoid
// unnecessary invalidations later on, in case the property types
// have not yet been instantiated.
if (key->isSingleton() &&
key->singleton()->is<NativeObject>() &&
key->singleton()->as<NativeObject>().lookupPure(id))
{
return false;
}
HeapTypeSetKey property = key->property(id);
if (property.isOwnProperty(constraints()))
return false;
JSObject* proto = checkNurseryObject(key->proto().toObjectOrNull());
if (!proto)
break;
key = TypeSet::ObjectKey::get(proto);
}
}
return true;
}
bool
IonBuilder::pushTypeBarrier(MDefinition* def, TemporaryTypeSet* observed, BarrierKind kind)
{
@ -8931,6 +8976,13 @@ IonBuilder::getElemTryGetProp(bool* emitted, MDefinition* obj, MDefinition* inde
return *emitted;
}
trackOptimizationAttempt(TrackedStrategy::GetProp_NotDefined);
if (!getPropTryNotDefined(emitted, obj, id, types) || *emitted) {
if (*emitted)
index->setImplicitlyUsedUnchecked();
return *emitted;
}
return true;
}
@ -10966,6 +11018,11 @@ IonBuilder::jsop_getprop(PropertyName* name)
if (!getPropTryConstant(&emitted, obj, NameToId(name), types) || emitted)
return emitted;
// Try to hardcode known not-defined
trackOptimizationAttempt(TrackedStrategy::GetProp_NotDefined);
if (!getPropTryNotDefined(&emitted, obj, NameToId(name), types) || emitted)
return emitted;
// Try to emit SIMD getter loads
trackOptimizationAttempt(TrackedStrategy::GetProp_SimdGetter);
if (!getPropTrySimdGetter(&emitted, obj, name) || emitted)
@ -11212,6 +11269,30 @@ IonBuilder::getPropTryConstant(bool* emitted, MDefinition* obj, jsid id, Tempora
return true;
}
bool
IonBuilder::getPropTryNotDefined(bool* emitted, MDefinition* obj, jsid id, TemporaryTypeSet* types)
{
MOZ_ASSERT(*emitted == false);
if (!types->mightBeMIRType(MIRType_Undefined)) {
// Only optimize if we expect this property access to return undefined.
trackOptimizationOutcome(TrackedOutcome::NotUndefined);
return true;
}
if (!testNotDefinedProperty(obj, id)) {
trackOptimizationOutcome(TrackedOutcome::GenericFailure);
return true;
}
obj->setImplicitlyUsedUnchecked();
pushConstant(UndefinedValue());
trackOptimizationSuccess();
*emitted = true;
return true;
}
MIRType
IonBuilder::SimdTypeDescrToMIRType(SimdTypeDescr::Type type)
{
@ -13289,21 +13370,13 @@ IonBuilder::jsop_in()
MDefinition* obj = convertUnboxedObjects(current->pop());
MDefinition* id = current->pop();
do {
if (shouldAbortOnPreliminaryGroups(obj))
break;
bool emitted = false;
JSValueType unboxedType = UnboxedArrayElementType(constraints(), obj, id);
if (unboxedType == JSVAL_TYPE_MAGIC) {
if (!ElementAccessIsDenseNative(constraints(), obj, id))
break;
}
if (!inTryDense(&emitted, obj, id) || emitted)
return emitted;
if (ElementAccessHasExtraIndexedProperty(this, obj))
break;
return jsop_in_dense(obj, id, unboxedType);
} while (false);
if (!inTryFold(&emitted, obj, id) || emitted)
return emitted;
MIn* ins = MIn::New(alloc(), id, obj);
@ -13314,8 +13387,24 @@ IonBuilder::jsop_in()
}
bool
IonBuilder::jsop_in_dense(MDefinition* obj, MDefinition* id, JSValueType unboxedType)
IonBuilder::inTryDense(bool* emitted, MDefinition* obj, MDefinition* id)
{
MOZ_ASSERT(!*emitted);
if (shouldAbortOnPreliminaryGroups(obj))
return true;
JSValueType unboxedType = UnboxedArrayElementType(constraints(), obj, id);
if (unboxedType == JSVAL_TYPE_MAGIC) {
if (!ElementAccessIsDenseNative(constraints(), obj, id))
return true;
}
if (ElementAccessHasExtraIndexedProperty(this, obj))
return true;
*emitted = true;
bool needsHoleCheck = !ElementAccessIsPacked(constraints(), obj);
// Ensure id is an integer.
@ -13345,6 +13434,32 @@ IonBuilder::jsop_in_dense(MDefinition* obj, MDefinition* id, JSValueType unboxed
return true;
}
bool
IonBuilder::inTryFold(bool* emitted, MDefinition* obj, MDefinition* id)
{
// Fold |id in obj| to |false|, if we know the object (or an object on its
// prototype chain) does not have this property.
MOZ_ASSERT(!*emitted);
jsid propId;
if (!id->isConstantValue() || !ValueToIdPure(id->constantValue(), &propId))
return true;
if (propId != IdToTypeId(propId))
return true;
if (!testNotDefinedProperty(obj, propId))
return true;
*emitted = true;
pushConstant(BooleanValue(false));
obj->setImplicitlyUsedUnchecked();
id->setImplicitlyUsedUnchecked();
return true;
}
bool
IonBuilder::hasOnProtoChain(TypeSet::ObjectKey* key, JSObject* protoObject, bool* hasOnProto)
{

View File

@ -431,6 +431,7 @@ class IonBuilder
bool getPropTryArgumentsLength(bool* emitted, MDefinition* obj);
bool getPropTryArgumentsCallee(bool* emitted, MDefinition* obj, PropertyName* name);
bool getPropTryConstant(bool* emitted, MDefinition* obj, jsid id, TemporaryTypeSet* types);
bool getPropTryNotDefined(bool* emitted, MDefinition* obj, jsid id, TemporaryTypeSet* types);
bool getPropTryDefiniteSlot(bool* emitted, MDefinition* obj, PropertyName* name,
BarrierKind barrier, TemporaryTypeSet* types);
bool getPropTryModuleNamespace(bool* emitted, MDefinition* obj, PropertyName* name,
@ -503,13 +504,17 @@ class IonBuilder
// jsop_bitnot helpers.
bool bitnotTrySpecialized(bool* emitted, MDefinition* input);
// jsop_compare helpes.
// jsop_compare helpers.
bool compareTrySpecialized(bool* emitted, JSOp op, MDefinition* left, MDefinition* right);
bool compareTryBitwise(bool* emitted, JSOp op, MDefinition* left, MDefinition* right);
bool compareTrySpecializedOnBaselineInspector(bool* emitted, JSOp op, MDefinition* left,
MDefinition* right);
bool compareTrySharedStub(bool* emitted, JSOp op, MDefinition* left, MDefinition* right);
// jsop_in helpers.
bool inTryDense(bool* emitted, MDefinition* obj, MDefinition* id);
bool inTryFold(bool* emitted, MDefinition* obj, MDefinition* id);
// binary data lookup helpers.
TypedObjectPrediction typedObjectPrediction(MDefinition* typedObj);
TypedObjectPrediction typedObjectPrediction(TemporaryTypeSet* types);
@ -732,7 +737,6 @@ class IonBuilder
bool jsop_isnoiter();
bool jsop_iterend();
bool jsop_in();
bool jsop_in_dense(MDefinition* obj, MDefinition* id, JSValueType unboxedType);
bool jsop_instanceof();
bool jsop_getaliasedvar(ScopeCoordinate sc);
bool jsop_setaliasedvar(ScopeCoordinate sc);
@ -980,6 +984,8 @@ class IonBuilder
JSObject* testSingletonProperty(JSObject* obj, jsid id);
JSObject* testSingletonPropertyTypes(MDefinition* obj, jsid id);
bool testNotDefinedProperty(MDefinition* obj, jsid id);
uint32_t getDefiniteSlot(TemporaryTypeSet* types, PropertyName* name, uint32_t* pnfixed);
MDefinition* convertUnboxedObjects(MDefinition* obj);
MDefinition* convertUnboxedObjects(MDefinition* obj,