Bug 983977 - Omit type barriers where possible r=jandem

This commit is contained in:
Nicholas D. Matsakis 2014-03-15 06:32:38 -04:00
parent 8186a5dafa
commit 4e7c85fecb
8 changed files with 140 additions and 34 deletions

View File

@ -211,6 +211,7 @@ CreateX4Class(JSContext *cx, Handle<GlobalObject*> global)
return nullptr;
x4->initReservedSlot(JS_DESCR_SLOT_TYPE_REPR, ObjectValue(*typeReprObj));
x4->initReservedSlot(JS_DESCR_SLOT_TYPE, Int32Value(T::type));
x4->initReservedSlot(JS_DESCR_SLOT_PROTO, ObjectValue(*proto));
// Link constructor to prototype and install properties.

View File

@ -608,6 +608,8 @@ ArrayMetaTypeDescr::create(JSContext *cx,
if (!prototypeObj)
return nullptr;
obj->initReservedSlot(JS_DESCR_SLOT_PROTO, ObjectValue(*prototypeObj));
if (!LinkConstructorAndPrototype(cx, obj, prototypeObj))
return nullptr;
@ -979,6 +981,8 @@ StructMetaTypeDescr::create(JSContext *cx,
if (!prototypeObj)
return nullptr;
descr->initReservedSlot(JS_DESCR_SLOT_PROTO, ObjectValue(*prototypeObj));
if (!LinkConstructorAndPrototype(cx, descr, prototypeObj))
return nullptr;

View File

@ -135,6 +135,14 @@ static T ConvertScalar(double d)
class TypeDescr : public JSObject
{
public:
// This is *intentionally* not defined so as to produce link
// errors if a is<FooTypeDescr>() etc goes wrong. Otherwise, the
// default implementation resolves this to a reference to
// FooTypeDescr::class_ which resolves to
// JSObject::class_. Debugging the resulting errors leads to much
// fun and rejoicing.
static const Class class_;
enum Kind {
Scalar = JS_TYPEREPR_SCALAR_KIND,
Reference = JS_TYPEREPR_REFERENCE_KIND,
@ -269,10 +277,22 @@ class ReferenceTypeDescr : public SimpleTypeDescr
macro_(ReferenceTypeDescr::TYPE_OBJECT, HeapPtrObject, Object) \
macro_(ReferenceTypeDescr::TYPE_STRING, HeapPtrString, string)
// Type descriptors whose instances are objects and hence which have
// an associated `prototype` property.
class ComplexTypeDescr : public SizedTypeDescr
{
public:
// Returns the prototype that instances of this type descriptor
// will have.
JSObject &instancePrototype() const {
return getReservedSlot(JS_DESCR_SLOT_PROTO).toObject();
}
};
/*
* Type descriptors `float32x4` and `int32x4`
*/
class X4TypeDescr : public SizedTypeDescr
class X4TypeDescr : public ComplexTypeDescr
{
public:
enum Type {
@ -346,6 +366,11 @@ class ArrayMetaTypeDescr : public JSObject
/*
* Type descriptor created by `new ArrayType(typeObj)`
*
* These have a prototype, and hence *could* be a subclass of
* `ComplexTypeDescr`, but it would require some reshuffling of the
* hierarchy, and it's not worth the trouble since they will be going
* away as part of bug 973238.
*/
class UnsizedArrayTypeDescr : public TypeDescr
{
@ -364,7 +389,7 @@ class UnsizedArrayTypeDescr : public TypeDescr
/*
* Type descriptor created by `unsizedArrayTypeObj.dimension()`
*/
class SizedArrayTypeDescr : public SizedTypeDescr
class SizedArrayTypeDescr : public ComplexTypeDescr
{
public:
static const Class class_;
@ -413,7 +438,8 @@ class StructMetaTypeDescr : public JSObject
static bool construct(JSContext *cx, unsigned argc, Value *vp);
};
class StructTypeDescr : public SizedTypeDescr {
class StructTypeDescr : public ComplexTypeDescr
{
public:
static const Class class_;
@ -873,13 +899,19 @@ IsSimpleTypeDescrClass(const Class* clasp)
clasp == &ReferenceTypeDescr::class_;
}
inline bool
IsComplexTypeDescrClass(const Class* clasp)
{
return clasp == &StructTypeDescr::class_ ||
clasp == &SizedArrayTypeDescr::class_ ||
clasp == &X4TypeDescr::class_;
}
inline bool
IsSizedTypeDescrClass(const Class* clasp)
{
return IsSimpleTypeDescrClass(clasp) ||
clasp == &StructTypeDescr::class_ ||
clasp == &SizedArrayTypeDescr::class_ ||
clasp == &X4TypeDescr::class_;
IsComplexTypeDescrClass(clasp);
}
inline bool
@ -908,6 +940,13 @@ JSObject::is<js::SizedTypeDescr>() const
return IsSizedTypeDescrClass(getClass());
}
template <>
inline bool
JSObject::is<js::ComplexTypeDescr>() const
{
return IsComplexTypeDescrClass(getClass());
}
template <>
inline bool
JSObject::is<js::TypeDescr>() const

View File

@ -21,23 +21,24 @@
#define JS_DESCR_SLOT_TYPE_REPR 0 // Associated Type Representation
#define JS_DESCR_SLOT_ALIGNMENT 1 // Alignment in bytes
#define JS_DESCR_SLOT_SIZE 2 // Size in bytes, if sized, else 0
#define JS_DESCR_SLOT_PROTO 3 // Prototype for instances, if any
// Slots on scalars, references, and x4s
#define JS_DESCR_SLOT_TYPE 3 // Type code
#define JS_DESCR_SLOT_TYPE 4 // Type code
// Slots on all array descriptors
#define JS_DESCR_SLOT_ARRAY_ELEM_TYPE 3
#define JS_DESCR_SLOT_ARRAY_ELEM_TYPE 4
// Slots on sized array descriptors
#define JS_DESCR_SLOT_SIZED_ARRAY_LENGTH 4
#define JS_DESCR_SLOT_SIZED_ARRAY_LENGTH 5
// Slots on struct type objects
#define JS_DESCR_SLOT_STRUCT_FIELD_NAMES 3
#define JS_DESCR_SLOT_STRUCT_FIELD_TYPES 4
#define JS_DESCR_SLOT_STRUCT_FIELD_OFFSETS 5
#define JS_DESCR_SLOT_STRUCT_FIELD_NAMES 4
#define JS_DESCR_SLOT_STRUCT_FIELD_TYPES 5
#define JS_DESCR_SLOT_STRUCT_FIELD_OFFSETS 6
// Maximum number of slots for any descriptor
#define JS_DESCR_SLOTS 6
#define JS_DESCR_SLOTS 7
///////////////////////////////////////////////////////////////////////////
// Slots for type representation objects

View File

@ -0,0 +1,23 @@
// API Surface Test: check that mutating prototypes
// of type descriptors has no effect.
if (!this.hasOwnProperty("TypedObject"))
quit();
var {StructType, uint32, Object, Any, storage, objectType} = TypedObject;
function main() { // once a C programmer, always a C programmer.
var Uints = new StructType({f: uint32, g: uint32});
var p = Uints.prototype;
Uints.prototype = {}; // no effect
assertEq(p, Uints.prototype);
var Uintss = Uints.array(2);
var p = Uintss.prototype;
Uintss.prototype = {}; // no effect
assertEq(p, Uintss.prototype);
print("Tests complete");
}
main();

View File

@ -6855,27 +6855,48 @@ IonBuilder::pushDerivedTypedObject(bool *emitted,
current->add(derivedTypedObj);
current->push(derivedTypedObj);
// Insert a barrier. This is necessary because, while we know from
// the inputs that the result of this access operation will be a
// derived typed object, and we know the set of type descriptor(s)
// it will be associated with (`derivedDescrs`), we do *not* know
// precisely what TI type object the result will have at
// runtime. The observed type set could be incomplete for two
// reasons:
// Determine (if possible) the class/proto that `derivedTypedObj`
// will have. For derived typed objects, the class (transparent vs
// opaque) will be the same as the incoming object from which the
// derived typed object is, well, derived. The prototype will be
// determined based on the type descriptor (and is immutable).
types::TemporaryTypeSet *objTypes = obj->resultTypeSet();
const Class *expectedClass = objTypes ? objTypes->getKnownClass() : nullptr;
JSObject *expectedProto = derivedTypeDescrs.knownPrototype();
JS_ASSERT_IF(expectedClass, IsTypedObjectClass(expectedClass));
// Determine (if possible) the class/proto that the observed type set
// describes.
types::TemporaryTypeSet *observedTypes = bytecodeTypes(pc);
const Class *observedClass = observedTypes->getKnownClass();
JSObject *observedProto = observedTypes->getCommonPrototype();
// If expectedClass/expectedProto are both non-null (and hence
// known), we can predict precisely what TI type object
// derivedTypedObj will have. Therefore, if we observe that this
// TI type object is already contained in the set of
// observedTypes, we can skip the barrier.
//
// 1. We may simply not have executed this instruction yet.
// This occurs frequently with --ion-eager but can happen
// under other scenarios as well.
// Barriers still wind up being needed in some relatively
// rare cases:
//
// 2. Users can mutate the prototypes of descriptors,
// and hence a single descriptor can be associated with multiple
// type objects over the course of the execution. Therefore,
// even if we have executed this instruction, the TI type object
// of the result might be different this time around from
// previous executions.
types::TemporaryTypeSet *resultTypes = bytecodeTypes(pc);
if (!pushTypeBarrier(derivedTypedObj, resultTypes, true))
return false;
// - if multiple kinds of typed objects flow into this point,
// in which case we will not be able to predict expectedClass
// nor expectedProto.
//
// - if the code has never executed, in which case the set of
// observed types will be incomplete.
//
// Barriers are particularly expensive here because they prevent
// us from optimizing the MNewDerivedTypedObject away.
if (observedClass && observedProto && observedClass == expectedClass &&
observedProto == expectedProto)
{
derivedTypedObj->setResultTypeSet(observedTypes);
} else {
if (!pushTypeBarrier(derivedTypedObj, observedTypes, true))
return false;
}
*emitted = true;
return true;

View File

@ -164,7 +164,7 @@ TypeDescrSet::TypeDescrSet()
{}
bool
TypeDescrSet::empty()
TypeDescrSet::empty() const
{
return length_ == 0;
}
@ -217,6 +217,15 @@ TypeDescrSet::allHaveSameSize(size_t *out)
return true;
}
JSObject *
TypeDescrSet::knownPrototype() const
{
JS_ASSERT(!empty());
if (length() > 1 || !get(0)->is<ComplexTypeDescr>())
return nullptr;
return &get(0)->as<ComplexTypeDescr>().instancePrototype();
}
TypeDescr::Kind
TypeDescrSet::kind()
{

View File

@ -85,7 +85,7 @@ class TypeDescrSet {
//////////////////////////////////////////////////////////////////////
// Query the set
bool empty();
bool empty() const;
bool allOfKind(TypeDescr::Kind kind);
// Returns true only when non-empty and `kind()` is
@ -102,11 +102,19 @@ class TypeDescrSet {
// lands, some array types will be unsized.
bool allHaveSameSize(size_t *out);
types::TemporaryTypeSet *suitableTypeSet(IonBuilder &builder,
const Class *knownClass);
//////////////////////////////////////////////////////////////////////
// The following operations are only valid on a non-empty set:
TypeDescr::Kind kind();
// Returns the prototype that a typed object whose type is within
// this TypeDescrSet would have. Returns `null` if this cannot be
// predicted or instances of the type are not objects (e.g., uint8).
JSObject *knownPrototype() const;
//////////////////////////////////////////////////////////////////////
// Scalar operations
//