mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-28 23:31:56 +00:00
Bug 1096539 - Keep track of typed object reference properties in type information, r=nmatsakis.
This commit is contained in:
parent
2f9f1b57d2
commit
3fb238eed2
@ -116,6 +116,7 @@ ConvertAndCopyTo(JSContext *cx,
|
||||
HandleTypeDescr typeObj,
|
||||
HandleTypedObject typedObj,
|
||||
int32_t offset,
|
||||
HandleAtom name,
|
||||
HandleValue val)
|
||||
{
|
||||
RootedFunction func(
|
||||
@ -124,14 +125,18 @@ ConvertAndCopyTo(JSContext *cx,
|
||||
return false;
|
||||
|
||||
InvokeArgs args(cx);
|
||||
if (!args.init(4))
|
||||
if (!args.init(5))
|
||||
return false;
|
||||
|
||||
args.setCallee(ObjectValue(*func));
|
||||
args[0].setObject(*typeObj);
|
||||
args[1].setObject(*typedObj);
|
||||
args[2].setInt32(offset);
|
||||
args[3].set(val);
|
||||
if (name)
|
||||
args[3].setString(name);
|
||||
else
|
||||
args[3].setNull();
|
||||
args[4].set(val);
|
||||
|
||||
return Invoke(cx, args);
|
||||
}
|
||||
@ -140,7 +145,7 @@ static bool
|
||||
ConvertAndCopyTo(JSContext *cx, HandleTypedObject typedObj, HandleValue val)
|
||||
{
|
||||
Rooted<TypeDescr*> type(cx, &typedObj->typeDescr());
|
||||
return ConvertAndCopyTo(cx, type, typedObj, 0, val);
|
||||
return ConvertAndCopyTo(cx, type, typedObj, 0, NullPtr(), val);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1004,6 +1009,8 @@ StructMetaTypeDescr::create(JSContext *cx,
|
||||
if (!LinkConstructorAndPrototype(cx, descr, prototypeObj))
|
||||
return nullptr;
|
||||
|
||||
// Initialize type information for instances of this struct.
|
||||
|
||||
return descr;
|
||||
}
|
||||
|
||||
@ -1959,7 +1966,8 @@ TypedObject::obj_setGeneric(JSContext *cx, HandleObject obj, HandleId id,
|
||||
|
||||
size_t offset = descr->fieldOffset(fieldIndex);
|
||||
Rooted<TypeDescr*> fieldType(cx, &descr->fieldDescr(fieldIndex));
|
||||
return ConvertAndCopyTo(cx, fieldType, typedObj, offset, vp);
|
||||
RootedAtom fieldName(cx, &descr->fieldName(fieldIndex));
|
||||
return ConvertAndCopyTo(cx, fieldType, typedObj, offset, fieldName, vp);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2013,7 +2021,7 @@ TypedObject::obj_setArrayElement(JSContext *cx,
|
||||
Rooted<TypeDescr*> elementType(cx);
|
||||
elementType = &descr->as<ArrayTypeDescr>().elementType();
|
||||
size_t offset = elementType->size() * index;
|
||||
return ConvertAndCopyTo(cx, elementType, typedObj, offset, vp);
|
||||
return ConvertAndCopyTo(cx, elementType, typedObj, offset, NullPtr(), vp);
|
||||
}
|
||||
|
||||
bool
|
||||
@ -2822,21 +2830,27 @@ JS_JITINFO_NATIVE_PARALLEL_THREADSAFE(js::StoreScalar##T::JitInfo,
|
||||
|
||||
#define JS_STORE_REFERENCE_CLASS_IMPL(_constant, T, _name) \
|
||||
bool \
|
||||
js::StoreReference##T::Func(ThreadSafeContext *, unsigned argc, Value *vp) \
|
||||
js::StoreReference##T::Func(ThreadSafeContext *cx, unsigned argc, Value *vp) \
|
||||
{ \
|
||||
CallArgs args = CallArgsFromVp(argc, vp); \
|
||||
MOZ_ASSERT(args.length() == 3); \
|
||||
MOZ_ASSERT(args.length() == 4); \
|
||||
MOZ_ASSERT(args[0].isObject() && args[0].toObject().is<TypedObject>()); \
|
||||
MOZ_ASSERT(args[1].isInt32()); \
|
||||
MOZ_ASSERT(args[2].isString() || args[2].isNull()); \
|
||||
\
|
||||
TypedObject &typedObj = args[0].toObject().as<TypedObject>(); \
|
||||
int32_t offset = args[1].toInt32(); \
|
||||
\
|
||||
jsid id = args[2].isString() \
|
||||
? types::IdToTypeId(AtomToId(&args[2].toString()->asAtom())) \
|
||||
: JSID_VOID; \
|
||||
\
|
||||
/* Should be guaranteed by the typed objects API: */ \
|
||||
MOZ_ASSERT(offset % MOZ_ALIGNOF(T) == 0); \
|
||||
\
|
||||
T *target = reinterpret_cast<T*>(typedObj.typedMem(offset)); \
|
||||
store(target, args[2]); \
|
||||
if (!store(cx, target, args[3], &typedObj, id)) \
|
||||
return false; \
|
||||
args.rval().setUndefined(); \
|
||||
return true; \
|
||||
} \
|
||||
@ -2896,24 +2910,54 @@ JS_JITINFO_NATIVE_PARALLEL_THREADSAFE(js::LoadReference##T::JitInfo,
|
||||
// differs, we abstract it away using specialized variants of the
|
||||
// private methods `store()` and `load()`.
|
||||
|
||||
void
|
||||
StoreReferenceHeapValue::store(HeapValue *heap, const Value &v)
|
||||
bool
|
||||
StoreReferenceHeapValue::store(ThreadSafeContext *cx, HeapValue *heap, const Value &v,
|
||||
TypedObject *obj, jsid id)
|
||||
{
|
||||
// Undefined values are not included in type inference information for
|
||||
// value properties of typed objects, as these properties are always
|
||||
// considered to contain undefined.
|
||||
if (!v.isUndefined()) {
|
||||
if (cx->isJSContext())
|
||||
types::AddTypePropertyId(cx->asJSContext(), obj, id, v);
|
||||
else if (!types::HasTypePropertyId(obj, id, v))
|
||||
return false;
|
||||
}
|
||||
|
||||
*heap = v;
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
StoreReferenceHeapPtrObject::store(HeapPtrObject *heap, const Value &v)
|
||||
bool
|
||||
StoreReferenceHeapPtrObject::store(ThreadSafeContext *cx, HeapPtrObject *heap, const Value &v,
|
||||
TypedObject *obj, jsid id)
|
||||
{
|
||||
MOZ_ASSERT(v.isObjectOrNull()); // or else Store_object is being misused
|
||||
|
||||
// Null pointers are not included in type inference information for
|
||||
// object properties of typed objects, as these properties are always
|
||||
// considered to contain null.
|
||||
if (v.isObject()) {
|
||||
if (cx->isJSContext())
|
||||
types::AddTypePropertyId(cx->asJSContext(), obj, id, v);
|
||||
else if (!types::HasTypePropertyId(obj, id, v))
|
||||
return false;
|
||||
}
|
||||
|
||||
*heap = v.toObjectOrNull();
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
StoreReferenceHeapPtrString::store(HeapPtrString *heap, const Value &v)
|
||||
bool
|
||||
StoreReferenceHeapPtrString::store(ThreadSafeContext *cx, HeapPtrString *heap, const Value &v,
|
||||
TypedObject *obj, jsid id)
|
||||
{
|
||||
MOZ_ASSERT(v.isString()); // or else Store_string is being misused
|
||||
|
||||
// Note: string references are not reflected in type information for the object.
|
||||
*heap = v.toString();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -910,9 +910,9 @@ class StoreScalar##T { \
|
||||
};
|
||||
|
||||
/*
|
||||
* Usage: Store_Any(targetDatum, targetOffset, value)
|
||||
* Store_Object(targetDatum, targetOffset, value)
|
||||
* Store_string(targetDatum, targetOffset, value)
|
||||
* Usage: Store_Any(targetDatum, targetOffset, fieldName, value)
|
||||
* Store_Object(targetDatum, targetOffset, fieldName, value)
|
||||
* Store_string(targetDatum, targetOffset, fieldName, value)
|
||||
*
|
||||
* Intrinsic function. Stores `value` into the memory referenced by
|
||||
* `targetDatum` at the offset `targetOffset`.
|
||||
@ -920,12 +920,13 @@ class StoreScalar##T { \
|
||||
* Assumes (and asserts) that:
|
||||
* - `targetDatum` is attached
|
||||
* - `targetOffset` is a valid offset within the bounds of `targetDatum`
|
||||
* - `value` is an object (`Store_Object`) or string (`Store_string`).
|
||||
* - `value` is an object or null (`Store_Object`) or string (`Store_string`).
|
||||
*/
|
||||
#define JS_STORE_REFERENCE_CLASS_DEFN(_constant, T, _name) \
|
||||
class StoreReference##T { \
|
||||
private: \
|
||||
static void store(T* heap, const Value &v); \
|
||||
static bool store(ThreadSafeContext *cx, T* heap, const Value &v, \
|
||||
TypedObject *obj, jsid id); \
|
||||
\
|
||||
public: \
|
||||
static bool Func(ThreadSafeContext *cx, unsigned argc, Value *vp); \
|
||||
|
@ -181,7 +181,7 @@ function TypedObjectGetSimd(descr, typedObj, offset) {
|
||||
// Writes `fromValue` into the `typedObj` at offset `offset`, adapting
|
||||
// it to `descr` as needed. This is the most general entry point
|
||||
// and works for any type.
|
||||
function TypedObjectSet(descr, typedObj, offset, fromValue) {
|
||||
function TypedObjectSet(descr, typedObj, offset, name, fromValue) {
|
||||
if (!TypedObjectIsAttached(typedObj))
|
||||
ThrowError(JSMSG_TYPEDOBJECT_HANDLE_UNATTACHED);
|
||||
|
||||
@ -191,7 +191,7 @@ function TypedObjectSet(descr, typedObj, offset, fromValue) {
|
||||
return;
|
||||
|
||||
case JS_TYPEREPR_REFERENCE_KIND:
|
||||
TypedObjectSetReference(descr, typedObj, offset, fromValue);
|
||||
TypedObjectSetReference(descr, typedObj, offset, name, fromValue);
|
||||
return;
|
||||
|
||||
case JS_TYPEREPR_SIMD_KIND:
|
||||
@ -217,7 +217,7 @@ function TypedObjectSet(descr, typedObj, offset, fromValue) {
|
||||
var fieldDescr = fieldDescrs[i];
|
||||
var fieldOffset = fieldOffsets[i];
|
||||
var fieldValue = fromValue[fieldName];
|
||||
TypedObjectSet(fieldDescr, typedObj, offset + fieldOffset, fieldValue);
|
||||
TypedObjectSet(fieldDescr, typedObj, offset + fieldOffset, fieldName, fieldValue);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@ -241,7 +241,7 @@ function TypedObjectSetArray(descr, length, typedObj, offset, fromValue) {
|
||||
var elemSize = DESCR_SIZE(elemDescr);
|
||||
var elemOffset = offset;
|
||||
for (var i = 0; i < length; i++) {
|
||||
TypedObjectSet(elemDescr, typedObj, elemOffset, fromValue[i]);
|
||||
TypedObjectSet(elemDescr, typedObj, elemOffset, null, fromValue[i]);
|
||||
elemOffset += elemSize;
|
||||
}
|
||||
}
|
||||
@ -293,18 +293,18 @@ function TypedObjectSetScalar(descr, typedObj, offset, fromValue) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function TypedObjectSetReference(descr, typedObj, offset, fromValue) {
|
||||
function TypedObjectSetReference(descr, typedObj, offset, name, fromValue) {
|
||||
var type = DESCR_TYPE(descr);
|
||||
switch (type) {
|
||||
case JS_REFERENCETYPEREPR_ANY:
|
||||
return Store_Any(typedObj, offset, fromValue);
|
||||
return Store_Any(typedObj, offset, name, fromValue);
|
||||
|
||||
case JS_REFERENCETYPEREPR_OBJECT:
|
||||
var value = (fromValue === null ? fromValue : ToObject(fromValue));
|
||||
return Store_Object(typedObj, offset, value);
|
||||
return Store_Object(typedObj, offset, name, value);
|
||||
|
||||
case JS_REFERENCETYPEREPR_STRING:
|
||||
return Store_string(typedObj, offset, ToString(fromValue));
|
||||
return Store_string(typedObj, offset, name, ToString(fromValue));
|
||||
}
|
||||
|
||||
assert(false, "Unhandled scalar type: " + type);
|
||||
@ -351,6 +351,7 @@ function TypedObjectSetSimd(descr, typedObj, offset, fromValue) {
|
||||
function ConvertAndCopyTo(destDescr,
|
||||
destTypedObj,
|
||||
destOffset,
|
||||
fieldName,
|
||||
fromValue)
|
||||
{
|
||||
assert(IsObject(destDescr) && ObjectIsTypeDescr(destDescr),
|
||||
@ -361,7 +362,7 @@ function ConvertAndCopyTo(destDescr,
|
||||
if (!TypedObjectIsAttached(destTypedObj))
|
||||
ThrowError(JSMSG_TYPEDOBJECT_HANDLE_UNATTACHED);
|
||||
|
||||
TypedObjectSet(destDescr, destTypedObj, destOffset, fromValue);
|
||||
TypedObjectSet(destDescr, destTypedObj, destOffset, fieldName, fromValue);
|
||||
}
|
||||
|
||||
// Wrapper for use from C++ code.
|
||||
@ -815,7 +816,7 @@ function BuildTypedSeqImpl(arrayType, len, depth, func) {
|
||||
var r = callFunction(std_Function_apply, func, undefined, indices);
|
||||
callFunction(std_Array_pop, indices);
|
||||
if (r !== undefined)
|
||||
TypedObjectSet(grainType, result, outOffset, r); // result[...indices] = r;
|
||||
TypedObjectSet(grainType, result, outOffset, null, r); // result[...indices] = r;
|
||||
|
||||
// Increment indices.
|
||||
IncrementIterationSpace(indices, iterationSpace);
|
||||
@ -918,7 +919,7 @@ function MapUntypedSeqImpl(inArray, outputType, maybeFunc) {
|
||||
var r = func(element, i, inArray, out);
|
||||
|
||||
if (r !== undefined)
|
||||
TypedObjectSet(outGrainType, result, outOffset, r); // result[i] = r
|
||||
TypedObjectSet(outGrainType, result, outOffset, null, r); // result[i] = r
|
||||
}
|
||||
|
||||
// Update offset and (implicitly) increment indices.
|
||||
@ -978,7 +979,7 @@ function MapTypedSeqImpl(inArray, depth, outputType, func) {
|
||||
// Invoke: var r = func(element, ...indices, collection, out);
|
||||
var r = func(element, i, inArray, out);
|
||||
if (r !== undefined)
|
||||
TypedObjectSet(outGrainType, result, outOffset, r); // result[i] = r
|
||||
TypedObjectSet(outGrainType, result, outOffset, null, r); // result[i] = r
|
||||
|
||||
// Update offsets and (implicitly) increment indices.
|
||||
inOffset += inUnitSize;
|
||||
@ -1013,7 +1014,7 @@ function MapTypedSeqImpl(inArray, depth, outputType, func) {
|
||||
callFunction(std_Array_push, args, inArray, out);
|
||||
var r = callFunction(std_Function_apply, func, void 0, args);
|
||||
if (r !== undefined)
|
||||
TypedObjectSet(outGrainType, result, outOffset, r); // result[...indices] = r
|
||||
TypedObjectSet(outGrainType, result, outOffset, null, r); // result[...indices] = r
|
||||
|
||||
// Update offsets and explicitly increment indices.
|
||||
inOffset += inUnitSize;
|
||||
|
@ -9109,13 +9109,34 @@ CodeGenerator::visitLoadUnboxedPointerT(LLoadUnboxedPointerT *lir)
|
||||
const LAllocation *index = lir->index();
|
||||
Register out = ToRegister(lir->output());
|
||||
|
||||
if (index->isConstant()) {
|
||||
int32_t offset = ToInt32(index) * sizeof(uintptr_t) + lir->mir()->offsetAdjustment();
|
||||
masm.loadPtr(Address(elements, offset), out);
|
||||
bool bailOnNull;
|
||||
int32_t offsetAdjustment;
|
||||
if (lir->mir()->isLoadUnboxedObjectOrNull()) {
|
||||
MOZ_ASSERT(lir->mir()->toLoadUnboxedObjectOrNull()->bailOnNull());
|
||||
bailOnNull = true;
|
||||
offsetAdjustment = lir->mir()->toLoadUnboxedObjectOrNull()->offsetAdjustment();
|
||||
} else if (lir->mir()->isLoadUnboxedString()) {
|
||||
bailOnNull = false;
|
||||
offsetAdjustment = lir->mir()->toLoadUnboxedString()->offsetAdjustment();
|
||||
} else {
|
||||
masm.loadPtr(BaseIndex(elements, ToRegister(index), ScalePointer,
|
||||
lir->mir()->offsetAdjustment()), out);
|
||||
MOZ_CRASH();
|
||||
}
|
||||
|
||||
if (index->isConstant()) {
|
||||
Address source(elements, ToInt32(index) * sizeof(uintptr_t) + offsetAdjustment);
|
||||
masm.loadPtr(source, out);
|
||||
} else {
|
||||
BaseIndex source(elements, ToRegister(index), ScalePointer, offsetAdjustment);
|
||||
masm.loadPtr(source, out);
|
||||
}
|
||||
|
||||
if (bailOnNull) {
|
||||
Label bail;
|
||||
masm.branchTestPtr(Assembler::Zero, out, out, &bail);
|
||||
if (!bailoutFrom(&bail, lir->snapshot()))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -7043,7 +7043,7 @@ IonBuilder::setStaticName(JSObject *staticObject, PropertyName *name)
|
||||
return jsop_setprop(name);
|
||||
}
|
||||
|
||||
if (!CanWriteProperty(constraints(), property, value))
|
||||
if (!CanWriteProperty(alloc(), constraints(), property, value))
|
||||
return jsop_setprop(name);
|
||||
|
||||
current->pop();
|
||||
@ -7371,7 +7371,7 @@ IonBuilder::getElemTryReferenceElemOfTypedObject(bool *emitted,
|
||||
|
||||
*emitted = true;
|
||||
|
||||
return pushReferenceLoadFromTypedObject(obj, indexAsByteOffset, elemType);
|
||||
return pushReferenceLoadFromTypedObject(obj, indexAsByteOffset, elemType, nullptr);
|
||||
}
|
||||
|
||||
bool
|
||||
@ -7418,7 +7418,8 @@ IonBuilder::pushScalarLoadFromTypedObject(MDefinition *obj,
|
||||
bool
|
||||
IonBuilder::pushReferenceLoadFromTypedObject(MDefinition *typedObj,
|
||||
const LinearSum &byteOffset,
|
||||
ReferenceTypeDescr::Type type)
|
||||
ReferenceTypeDescr::Type type,
|
||||
PropertyName *name)
|
||||
{
|
||||
// Find location within the owner object.
|
||||
MDefinition *elements, *scaledOffset;
|
||||
@ -7429,22 +7430,34 @@ IonBuilder::pushReferenceLoadFromTypedObject(MDefinition *typedObj,
|
||||
types::TemporaryTypeSet *observedTypes = bytecodeTypes(pc);
|
||||
|
||||
MInstruction *load;
|
||||
BarrierKind barrier = BarrierKind::NoBarrier;
|
||||
BarrierKind barrier = PropertyReadNeedsTypeBarrier(analysisContext, constraints(),
|
||||
typedObj, name, observedTypes);
|
||||
|
||||
switch (type) {
|
||||
case ReferenceTypeDescr::TYPE_ANY:
|
||||
case ReferenceTypeDescr::TYPE_ANY: {
|
||||
// Make sure the barrier reflects the possibility of reading undefined.
|
||||
bool bailOnUndefined = barrier == BarrierKind::NoBarrier &&
|
||||
!observedTypes->hasType(types::Type::UndefinedType());
|
||||
if (bailOnUndefined)
|
||||
barrier = BarrierKind::TypeTagOnly;
|
||||
load = MLoadElement::New(alloc(), elements, scaledOffset, false, false, adjustment);
|
||||
if (!observedTypes->unknown())
|
||||
barrier = BarrierKind::TypeSet;
|
||||
break;
|
||||
case ReferenceTypeDescr::TYPE_OBJECT:
|
||||
load = MLoadUnboxedObjectOrNull::New(alloc(), elements, scaledOffset, adjustment);
|
||||
if (!observedTypes->unknownObject() || !observedTypes->hasType(types::Type::NullType()))
|
||||
barrier = BarrierKind::TypeSet;
|
||||
}
|
||||
case ReferenceTypeDescr::TYPE_OBJECT: {
|
||||
// Make sure the barrier reflects the possibility of reading null. When
|
||||
// there is no other barrier needed we include the null bailout with
|
||||
// MLoadUnboxedObjectOrNull, which avoids the need to box the result
|
||||
// for a type barrier instruction.
|
||||
bool bailOnNull = barrier == BarrierKind::NoBarrier &&
|
||||
!observedTypes->hasType(types::Type::NullType());
|
||||
load = MLoadUnboxedObjectOrNull::New(alloc(), elements, scaledOffset, bailOnNull, adjustment);
|
||||
break;
|
||||
case ReferenceTypeDescr::TYPE_STRING:
|
||||
}
|
||||
case ReferenceTypeDescr::TYPE_STRING: {
|
||||
load = MLoadUnboxedString::New(alloc(), elements, scaledOffset, adjustment);
|
||||
observedTypes->addType(types::Type::StringType(), alloc().lifoAlloc());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
current->add(load);
|
||||
@ -8264,8 +8277,8 @@ IonBuilder::setElemTryReferenceElemOfTypedObject(bool *emitted,
|
||||
if (!checkTypedObjectIndexInBounds(elemSize, obj, index, objPrediction, &indexAsByteOffset))
|
||||
return true;
|
||||
|
||||
if (!storeReferenceTypedObjectValue(obj, indexAsByteOffset, elemType, value))
|
||||
return false;
|
||||
if (!storeReferenceTypedObjectValue(obj, indexAsByteOffset, elemType, value, nullptr))
|
||||
return true;
|
||||
|
||||
current->push(value);
|
||||
|
||||
@ -9274,7 +9287,7 @@ IonBuilder::jsop_getprop(PropertyName *name)
|
||||
return emitted;
|
||||
|
||||
// Try to emit loads from known binary data blocks
|
||||
if (!getPropTryTypedObject(&emitted, obj, name, types) || emitted)
|
||||
if (!getPropTryTypedObject(&emitted, obj, name) || emitted)
|
||||
return emitted;
|
||||
|
||||
// Try to emit loads from definite slots.
|
||||
@ -9443,8 +9456,7 @@ IonBuilder::getPropTryConstant(bool *emitted, MDefinition *obj, PropertyName *na
|
||||
bool
|
||||
IonBuilder::getPropTryTypedObject(bool *emitted,
|
||||
MDefinition *obj,
|
||||
PropertyName *name,
|
||||
types::TemporaryTypeSet *resultTypes)
|
||||
PropertyName *name)
|
||||
{
|
||||
TypedObjectPrediction fieldPrediction;
|
||||
size_t fieldOffset;
|
||||
@ -9463,22 +9475,20 @@ IonBuilder::getPropTryTypedObject(bool *emitted,
|
||||
obj,
|
||||
fieldOffset,
|
||||
fieldPrediction,
|
||||
fieldIndex,
|
||||
resultTypes);
|
||||
fieldIndex);
|
||||
|
||||
case type::Reference:
|
||||
return getPropTryReferencePropOfTypedObject(emitted,
|
||||
obj,
|
||||
fieldOffset,
|
||||
fieldPrediction,
|
||||
resultTypes);
|
||||
name);
|
||||
|
||||
case type::Scalar:
|
||||
return getPropTryScalarPropOfTypedObject(emitted,
|
||||
obj,
|
||||
fieldOffset,
|
||||
fieldPrediction,
|
||||
resultTypes);
|
||||
fieldPrediction);
|
||||
}
|
||||
|
||||
MOZ_CRASH("Bad kind");
|
||||
@ -9487,8 +9497,7 @@ IonBuilder::getPropTryTypedObject(bool *emitted,
|
||||
bool
|
||||
IonBuilder::getPropTryScalarPropOfTypedObject(bool *emitted, MDefinition *typedObj,
|
||||
int32_t fieldOffset,
|
||||
TypedObjectPrediction fieldPrediction,
|
||||
types::TemporaryTypeSet *resultTypes)
|
||||
TypedObjectPrediction fieldPrediction)
|
||||
{
|
||||
// Must always be loading the same scalar type
|
||||
Scalar::Type fieldType = fieldPrediction.scalarType();
|
||||
@ -9511,7 +9520,7 @@ bool
|
||||
IonBuilder::getPropTryReferencePropOfTypedObject(bool *emitted, MDefinition *typedObj,
|
||||
int32_t fieldOffset,
|
||||
TypedObjectPrediction fieldPrediction,
|
||||
types::TemporaryTypeSet *resultTypes)
|
||||
PropertyName *name)
|
||||
{
|
||||
ReferenceTypeDescr::Type fieldType = fieldPrediction.referenceType();
|
||||
|
||||
@ -9525,7 +9534,7 @@ IonBuilder::getPropTryReferencePropOfTypedObject(bool *emitted, MDefinition *typ
|
||||
if (!byteOffset.add(fieldOffset))
|
||||
setForceAbort();
|
||||
|
||||
return pushReferenceLoadFromTypedObject(typedObj, byteOffset, fieldType);
|
||||
return pushReferenceLoadFromTypedObject(typedObj, byteOffset, fieldType, name);
|
||||
}
|
||||
|
||||
bool
|
||||
@ -9533,8 +9542,7 @@ IonBuilder::getPropTryComplexPropOfTypedObject(bool *emitted,
|
||||
MDefinition *typedObj,
|
||||
int32_t fieldOffset,
|
||||
TypedObjectPrediction fieldPrediction,
|
||||
size_t fieldIndex,
|
||||
types::TemporaryTypeSet *resultTypes)
|
||||
size_t fieldIndex)
|
||||
{
|
||||
// Don't optimize if the typed object might be neutered.
|
||||
types::TypeObjectKey *globalType = types::TypeObjectKey::get(&script()->global());
|
||||
@ -10022,14 +10030,14 @@ IonBuilder::jsop_setprop(PropertyName *name)
|
||||
if (!setPropTryCommonSetter(&emitted, obj, name, value) || emitted)
|
||||
return emitted;
|
||||
|
||||
types::TemporaryTypeSet *objTypes = obj->resultTypeSet();
|
||||
bool barrier = PropertyWriteNeedsTypeBarrier(alloc(), constraints(), current, &obj, name, &value,
|
||||
/* canModify = */ true);
|
||||
|
||||
// Try to emit stores to known binary data blocks
|
||||
if (!setPropTryTypedObject(&emitted, obj, name, value) || emitted)
|
||||
return emitted;
|
||||
|
||||
types::TemporaryTypeSet *objTypes = obj->resultTypeSet();
|
||||
bool barrier = PropertyWriteNeedsTypeBarrier(alloc(), constraints(), current, &obj, name, &value,
|
||||
/* canModify = */ true);
|
||||
|
||||
if (!barrier) {
|
||||
// Try to emit store from definite slots.
|
||||
if (!setPropTryDefiniteSlot(&emitted, obj, name, value, objTypes) || emitted)
|
||||
@ -10177,7 +10185,7 @@ IonBuilder::setPropTryTypedObject(bool *emitted, MDefinition *obj,
|
||||
|
||||
case type::Reference:
|
||||
return setPropTryReferencePropOfTypedObject(emitted, obj, fieldOffset,
|
||||
value, fieldPrediction);
|
||||
value, fieldPrediction, name);
|
||||
|
||||
case type::Scalar:
|
||||
return setPropTryScalarPropOfTypedObject(emitted, obj, fieldOffset,
|
||||
@ -10196,7 +10204,8 @@ IonBuilder::setPropTryReferencePropOfTypedObject(bool *emitted,
|
||||
MDefinition *obj,
|
||||
int32_t fieldOffset,
|
||||
MDefinition *value,
|
||||
TypedObjectPrediction fieldPrediction)
|
||||
TypedObjectPrediction fieldPrediction,
|
||||
PropertyName *name)
|
||||
{
|
||||
ReferenceTypeDescr::Type fieldType = fieldPrediction.referenceType();
|
||||
|
||||
@ -10208,8 +10217,8 @@ IonBuilder::setPropTryReferencePropOfTypedObject(bool *emitted,
|
||||
if (!byteOffset.add(fieldOffset))
|
||||
setForceAbort();
|
||||
|
||||
if (!storeReferenceTypedObjectValue(obj, byteOffset, fieldType, value))
|
||||
return false;
|
||||
if (!storeReferenceTypedObjectValue(obj, byteOffset, fieldType, value, name))
|
||||
return true;
|
||||
|
||||
current->push(value);
|
||||
|
||||
@ -11390,8 +11399,24 @@ bool
|
||||
IonBuilder::storeReferenceTypedObjectValue(MDefinition *typedObj,
|
||||
const LinearSum &byteOffset,
|
||||
ReferenceTypeDescr::Type type,
|
||||
MDefinition *value)
|
||||
MDefinition *value,
|
||||
PropertyName *name)
|
||||
{
|
||||
// Make sure we aren't adding new type information for writes of object and value
|
||||
// references.
|
||||
if (type != ReferenceTypeDescr::TYPE_STRING) {
|
||||
MOZ_ASSERT(type == ReferenceTypeDescr::TYPE_ANY ||
|
||||
type == ReferenceTypeDescr::TYPE_OBJECT);
|
||||
MIRType implicitType =
|
||||
(type == ReferenceTypeDescr::TYPE_ANY) ? MIRType_Undefined : MIRType_Null;
|
||||
|
||||
if (PropertyWriteNeedsTypeBarrier(alloc(), constraints(), current, &typedObj, name, &value,
|
||||
/* canModify = */ true, implicitType))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Find location within the owner object.
|
||||
MDefinition *elements, *scaledOffset;
|
||||
int32_t adjustment;
|
||||
|
@ -421,21 +421,18 @@ class IonBuilder
|
||||
types::TemporaryTypeSet *types);
|
||||
bool getPropTryInlineAccess(bool *emitted, MDefinition *obj, PropertyName *name,
|
||||
BarrierKind barrier, types::TemporaryTypeSet *types);
|
||||
bool getPropTryTypedObject(bool *emitted, MDefinition *obj, PropertyName *name,
|
||||
types::TemporaryTypeSet *resultTypes);
|
||||
bool getPropTryTypedObject(bool *emitted, MDefinition *obj, PropertyName *name);
|
||||
bool getPropTryScalarPropOfTypedObject(bool *emitted, MDefinition *typedObj,
|
||||
int32_t fieldOffset,
|
||||
TypedObjectPrediction fieldTypeReprs,
|
||||
types::TemporaryTypeSet *resultTypes);
|
||||
TypedObjectPrediction fieldTypeReprs);
|
||||
bool getPropTryReferencePropOfTypedObject(bool *emitted, MDefinition *typedObj,
|
||||
int32_t fieldOffset,
|
||||
TypedObjectPrediction fieldPrediction,
|
||||
types::TemporaryTypeSet *resultTypes);
|
||||
PropertyName *name);
|
||||
bool getPropTryComplexPropOfTypedObject(bool *emitted, MDefinition *typedObj,
|
||||
int32_t fieldOffset,
|
||||
TypedObjectPrediction fieldTypeReprs,
|
||||
size_t fieldIndex,
|
||||
types::TemporaryTypeSet *resultTypes);
|
||||
size_t fieldIndex);
|
||||
bool getPropTryInnerize(bool *emitted, MDefinition *obj, PropertyName *name,
|
||||
types::TemporaryTypeSet *types);
|
||||
bool getPropTryCache(bool *emitted, MDefinition *obj, PropertyName *name,
|
||||
@ -460,7 +457,8 @@ class IonBuilder
|
||||
MDefinition *obj,
|
||||
int32_t fieldOffset,
|
||||
MDefinition *value,
|
||||
TypedObjectPrediction fieldPrediction);
|
||||
TypedObjectPrediction fieldPrediction,
|
||||
PropertyName *name);
|
||||
bool setPropTryScalarPropOfTypedObject(bool *emitted,
|
||||
MDefinition *obj,
|
||||
int32_t fieldOffset,
|
||||
@ -494,7 +492,8 @@ class IonBuilder
|
||||
bool storeReferenceTypedObjectValue(MDefinition *typedObj,
|
||||
const LinearSum &byteOffset,
|
||||
ReferenceTypeDescr::Type type,
|
||||
MDefinition *value);
|
||||
MDefinition *value,
|
||||
PropertyName *name);
|
||||
bool storeScalarTypedObjectValue(MDefinition *typedObj,
|
||||
const LinearSum &byteOffset,
|
||||
ScalarTypeDescr::Type type,
|
||||
@ -515,7 +514,8 @@ class IonBuilder
|
||||
ScalarTypeDescr::Type type);
|
||||
bool pushReferenceLoadFromTypedObject(MDefinition *typedObj,
|
||||
const LinearSum &byteOffset,
|
||||
ReferenceTypeDescr::Type type);
|
||||
ReferenceTypeDescr::Type type,
|
||||
PropertyName *name);
|
||||
MDefinition *neuterCheck(MDefinition *obj);
|
||||
|
||||
// jsop_setelem() helpers.
|
||||
|
@ -4568,8 +4568,9 @@ class LLoadUnboxedPointerT : public LInstructionHelper<1, 2, 0>
|
||||
setOperand(1, index);
|
||||
}
|
||||
|
||||
const MLoadUnboxedString *mir() const {
|
||||
return mir_->toLoadUnboxedString();
|
||||
MDefinition *mir() {
|
||||
MOZ_ASSERT(mir_->isLoadUnboxedObjectOrNull() || mir_->isLoadUnboxedString());
|
||||
return mir_;
|
||||
}
|
||||
const LAllocation *elements() {
|
||||
return getOperand(0);
|
||||
|
@ -2739,7 +2739,17 @@ LIRGenerator::visitLoadUnboxedObjectOrNull(MLoadUnboxedObjectOrNull *ins)
|
||||
{
|
||||
MOZ_ASSERT(IsValidElementsType(ins->elements(), ins->offsetAdjustment()));
|
||||
MOZ_ASSERT(ins->index()->type() == MIRType_Int32);
|
||||
|
||||
if (ins->type() == MIRType_Object) {
|
||||
LLoadUnboxedPointerT *lir = new(alloc()) LLoadUnboxedPointerT(useRegister(ins->elements()),
|
||||
useRegisterOrConstant(ins->index()));
|
||||
if (ins->bailOnNull() && !assignSnapshot(lir, Bailout_TypeBarrierO))
|
||||
return false;
|
||||
return define(lir, ins);
|
||||
}
|
||||
|
||||
MOZ_ASSERT(ins->type() == MIRType_Value);
|
||||
MOZ_ASSERT(!ins->bailOnNull());
|
||||
|
||||
LLoadUnboxedPointerV *lir = new(alloc()) LLoadUnboxedPointerV(useRegister(ins->elements()),
|
||||
useRegisterOrConstant(ins->index()));
|
||||
|
@ -4274,10 +4274,30 @@ jit::AddObjectsForPropertyRead(MDefinition *obj, PropertyName *name,
|
||||
}
|
||||
}
|
||||
|
||||
static bool
|
||||
PropertyTypeIncludes(TempAllocator &alloc, types::HeapTypeSetKey property,
|
||||
MDefinition *value, MIRType implicitType)
|
||||
{
|
||||
// If implicitType is not MIRType_None, it is an additional type which the
|
||||
// property implicitly includes. In this case, make a new type set which
|
||||
// explicitly contains the type.
|
||||
types::TypeSet *types = property.maybeTypes();
|
||||
if (implicitType != MIRType_None) {
|
||||
types::Type newType = types::Type::PrimitiveType(ValueTypeFromMIRType(implicitType));
|
||||
if (types)
|
||||
types = types->clone(alloc.lifoAlloc());
|
||||
else
|
||||
types = alloc.lifoAlloc()->new_<types::TemporaryTypeSet>();
|
||||
types->addType(newType, alloc.lifoAlloc());
|
||||
}
|
||||
|
||||
return TypeSetIncludes(types, value->type(), value->resultTypeSet());
|
||||
}
|
||||
|
||||
static bool
|
||||
TryAddTypeBarrierForWrite(TempAllocator &alloc, types::CompilerConstraintList *constraints,
|
||||
MBasicBlock *current, types::TemporaryTypeSet *objTypes,
|
||||
PropertyName *name, MDefinition **pvalue)
|
||||
PropertyName *name, MDefinition **pvalue, MIRType implicitType)
|
||||
{
|
||||
// Return whether pvalue was modified to include a type barrier ensuring
|
||||
// that writing the value to objTypes/id will not require changing type
|
||||
@ -4301,7 +4321,7 @@ TryAddTypeBarrierForWrite(TempAllocator &alloc, types::CompilerConstraintList *c
|
||||
if (!property.maybeTypes() || property.couldBeConstant(constraints))
|
||||
return false;
|
||||
|
||||
if (TypeSetIncludes(property.maybeTypes(), (*pvalue)->type(), (*pvalue)->resultTypeSet()))
|
||||
if (PropertyTypeIncludes(alloc, property, *pvalue, implicitType))
|
||||
return false;
|
||||
|
||||
// This freeze is not required for correctness, but ensures that we
|
||||
@ -4384,18 +4404,20 @@ AddTypeGuard(TempAllocator &alloc, MBasicBlock *current, MDefinition *obj,
|
||||
|
||||
// Whether value can be written to property without changing type information.
|
||||
bool
|
||||
jit::CanWriteProperty(types::CompilerConstraintList *constraints,
|
||||
types::HeapTypeSetKey property, MDefinition *value)
|
||||
jit::CanWriteProperty(TempAllocator &alloc, types::CompilerConstraintList *constraints,
|
||||
types::HeapTypeSetKey property, MDefinition *value,
|
||||
MIRType implicitType /* = MIRType_None */)
|
||||
{
|
||||
if (property.couldBeConstant(constraints))
|
||||
return false;
|
||||
return TypeSetIncludes(property.maybeTypes(), value->type(), value->resultTypeSet());
|
||||
return PropertyTypeIncludes(alloc, property, value, implicitType);
|
||||
}
|
||||
|
||||
bool
|
||||
jit::PropertyWriteNeedsTypeBarrier(TempAllocator &alloc, types::CompilerConstraintList *constraints,
|
||||
MBasicBlock *current, MDefinition **pobj,
|
||||
PropertyName *name, MDefinition **pvalue, bool canModify)
|
||||
PropertyName *name, MDefinition **pvalue,
|
||||
bool canModify, MIRType implicitType)
|
||||
{
|
||||
// If any value being written is not reflected in the type information for
|
||||
// objects which obj could represent, a type barrier is needed when writing
|
||||
@ -4425,14 +4447,15 @@ jit::PropertyWriteNeedsTypeBarrier(TempAllocator &alloc, types::CompilerConstrai
|
||||
|
||||
jsid id = name ? NameToId(name) : JSID_VOID;
|
||||
types::HeapTypeSetKey property = object->property(id);
|
||||
if (!CanWriteProperty(constraints, property, *pvalue)) {
|
||||
if (!CanWriteProperty(alloc, constraints, property, *pvalue, implicitType)) {
|
||||
// Either pobj or pvalue needs to be modified to filter out the
|
||||
// types which the value could have but are not in the property,
|
||||
// or a VM call is required. A VM call is always required if pobj
|
||||
// and pvalue cannot be modified.
|
||||
if (!canModify)
|
||||
return true;
|
||||
success = TryAddTypeBarrierForWrite(alloc, constraints, current, types, name, pvalue);
|
||||
success = TryAddTypeBarrierForWrite(alloc, constraints, current, types, name, pvalue,
|
||||
implicitType);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -4457,7 +4480,7 @@ jit::PropertyWriteNeedsTypeBarrier(TempAllocator &alloc, types::CompilerConstrai
|
||||
|
||||
jsid id = name ? NameToId(name) : JSID_VOID;
|
||||
types::HeapTypeSetKey property = object->property(id);
|
||||
if (CanWriteProperty(constraints, property, *pvalue))
|
||||
if (CanWriteProperty(alloc, constraints, property, *pvalue, implicitType))
|
||||
continue;
|
||||
|
||||
if ((property.maybeTypes() && !property.maybeTypes()->empty()) || excluded)
|
||||
|
@ -7931,13 +7931,21 @@ class MLoadUnboxedObjectOrNull
|
||||
: public MBinaryInstruction,
|
||||
public SingleObjectPolicy::Data
|
||||
{
|
||||
bool bailOnNull_;
|
||||
int32_t offsetAdjustment_;
|
||||
|
||||
MLoadUnboxedObjectOrNull(MDefinition *elements, MDefinition *index, int32_t offsetAdjustment)
|
||||
MLoadUnboxedObjectOrNull(MDefinition *elements, MDefinition *index,
|
||||
bool bailOnNull, int32_t offsetAdjustment)
|
||||
: MBinaryInstruction(elements, index),
|
||||
bailOnNull_(bailOnNull),
|
||||
offsetAdjustment_(offsetAdjustment)
|
||||
{
|
||||
setResultType(MIRType_Value);
|
||||
if (bailOnNull) {
|
||||
// Don't eliminate loads which bail out on a null pointer, for the
|
||||
// same reason as MLoadElement.
|
||||
setGuard();
|
||||
}
|
||||
setResultType(bailOnNull ? MIRType_Object : MIRType_Value);
|
||||
setMovable();
|
||||
MOZ_ASSERT(IsValidElementsType(elements, offsetAdjustment));
|
||||
MOZ_ASSERT(index->type() == MIRType_Int32);
|
||||
@ -7948,8 +7956,8 @@ class MLoadUnboxedObjectOrNull
|
||||
|
||||
static MLoadUnboxedObjectOrNull *New(TempAllocator &alloc,
|
||||
MDefinition *elements, MDefinition *index,
|
||||
int32_t offsetAdjustment) {
|
||||
return new(alloc) MLoadUnboxedObjectOrNull(elements, index, offsetAdjustment);
|
||||
bool bailOnNull, int32_t offsetAdjustment) {
|
||||
return new(alloc) MLoadUnboxedObjectOrNull(elements, index, bailOnNull, offsetAdjustment);
|
||||
}
|
||||
|
||||
MDefinition *elements() const {
|
||||
@ -7958,13 +7966,21 @@ class MLoadUnboxedObjectOrNull
|
||||
MDefinition *index() const {
|
||||
return getOperand(1);
|
||||
}
|
||||
bool bailOnNull() const {
|
||||
return bailOnNull_;
|
||||
}
|
||||
int32_t offsetAdjustment() const {
|
||||
return offsetAdjustment_;
|
||||
}
|
||||
bool fallible() const {
|
||||
return bailOnNull();
|
||||
}
|
||||
bool congruentTo(const MDefinition *ins) const {
|
||||
if (!ins->isLoadUnboxedObjectOrNull())
|
||||
return false;
|
||||
const MLoadUnboxedObjectOrNull *other = ins->toLoadUnboxedObjectOrNull();
|
||||
if (bailOnNull() != other->bailOnNull())
|
||||
return false;
|
||||
if (offsetAdjustment() != other->offsetAdjustment())
|
||||
return false;
|
||||
return congruentIfOperandsEqual(other);
|
||||
@ -12503,12 +12519,13 @@ bool PropertyReadIsIdempotent(types::CompilerConstraintList *constraints,
|
||||
MDefinition *obj, PropertyName *name);
|
||||
void AddObjectsForPropertyRead(MDefinition *obj, PropertyName *name,
|
||||
types::TemporaryTypeSet *observed);
|
||||
bool CanWriteProperty(types::CompilerConstraintList *constraints,
|
||||
types::HeapTypeSetKey property, MDefinition *value);
|
||||
bool CanWriteProperty(TempAllocator &alloc, types::CompilerConstraintList *constraints,
|
||||
types::HeapTypeSetKey property, MDefinition *value,
|
||||
MIRType implicitType = MIRType_None);
|
||||
bool PropertyWriteNeedsTypeBarrier(TempAllocator &alloc, types::CompilerConstraintList *constraints,
|
||||
MBasicBlock *current, MDefinition **pobj,
|
||||
PropertyName *name, MDefinition **pvalue,
|
||||
bool canModify);
|
||||
bool canModify, MIRType implicitType = MIRType_None);
|
||||
|
||||
} // namespace jit
|
||||
} // namespace js
|
||||
|
@ -1137,9 +1137,12 @@ struct TypeObject : public gc::TenuredCell
|
||||
*
|
||||
* The type sets in the properties of a type object describe the possible
|
||||
* values that can be read out of that property in actual JS objects.
|
||||
* Properties only account for native properties (those with a slot and no
|
||||
* specialized getter hook) and the elements of dense arrays. For accesses
|
||||
* on such properties, the correspondence is as follows:
|
||||
* In native objects, property types account for plain data properties
|
||||
* (those with a slot and no getter or setter hook) and dense elements.
|
||||
* In typed objects, property types account for object and value properties
|
||||
* and elements in the object.
|
||||
*
|
||||
* For accesses on these properties, the correspondence is as follows:
|
||||
*
|
||||
* 1. If the type has unknownProperties(), the possible properties and
|
||||
* value types for associated JSObjects are unknown.
|
||||
@ -1148,16 +1151,21 @@ struct TypeObject : public gc::TenuredCell
|
||||
* which is a property in obj, before obj->getProperty(id) the property
|
||||
* in type for id must reflect the result of the getProperty.
|
||||
*
|
||||
* There is an exception for properties of global JS objects which
|
||||
* are undefined at the point where the property was (lazily) generated.
|
||||
* In such cases the property type set will remain empty, and the
|
||||
* 'undefined' type will only be added after a subsequent assignment or
|
||||
* deletion. After these properties have been assigned a defined value,
|
||||
* the only way they can become undefined again is after such an assign
|
||||
* or deletion.
|
||||
* There are several exceptions to this:
|
||||
*
|
||||
* There is another exception for array lengths, which are special cased
|
||||
* by the compiler and VM and are not reflected in property types.
|
||||
* 1. For properties of global JS objects which are undefined at the point
|
||||
* where the property was (lazily) generated, the property type set will
|
||||
* remain empty, and the 'undefined' type will only be added after a
|
||||
* subsequent assignment or deletion. After these properties have been
|
||||
* assigned a defined value, the only way they can become undefined
|
||||
* again is after such an assign or deletion.
|
||||
*
|
||||
* 2. Array lengths are special cased by the compiler and VM and are not
|
||||
* reflected in property types.
|
||||
*
|
||||
* 3. In typed objects, the initial values of properties (null pointers and
|
||||
* undefined values) are not reflected in the property types. These
|
||||
* values are always possible when reading the property.
|
||||
*
|
||||
* We establish these by using write barriers on calls to setProperty and
|
||||
* defineProperty which are on native properties, and on any jitcode which
|
||||
|
Loading…
Reference in New Issue
Block a user