Bug 1096539 - Keep track of typed object reference properties in type information, r=nmatsakis.

This commit is contained in:
Brian Hackett 2014-11-20 03:56:13 -07:00
parent 2f9f1b57d2
commit 3fb238eed2
11 changed files with 265 additions and 114 deletions

View File

@ -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

View File

@ -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); \

View File

@ -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;

View File

@ -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;
}

View File

@ -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;

View File

@ -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.

View File

@ -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);

View File

@ -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()));

View File

@ -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)

View File

@ -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

View File

@ -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