Bug 1022356 Part 01 -- Move from TypedProtoSet to TypedObjectPrediction r=shu

This commit is contained in:
Nicholas D. Matsakis 2014-03-12 08:18:28 -04:00
parent c1e4a09735
commit e7fb87053d
11 changed files with 752 additions and 769 deletions

View File

@ -168,6 +168,8 @@ class TypedProto : public JSObject
TypeDescr &typeDescr() const { TypeDescr &typeDescr() const {
return getReservedSlot(JS_TYPROTO_SLOT_DESCR).toObject().as<TypeDescr>(); return getReservedSlot(JS_TYPROTO_SLOT_DESCR).toObject().as<TypeDescr>();
} }
inline type::Kind kind() const;
}; };
class TypeDescr : public JSObject class TypeDescr : public JSObject
@ -1055,4 +1057,11 @@ js::TypedProto::initTypeDescrSlot(TypeDescr &descr)
initReservedSlot(JS_TYPROTO_SLOT_DESCR, ObjectValue(descr)); initReservedSlot(JS_TYPROTO_SLOT_DESCR, ObjectValue(descr));
} }
inline js::type::Kind
js::TypedProto::kind() const {
// Defined out of line because it depends on def'n of both
// TypedProto and TypeDescr
return typeDescr().kind();
}
#endif /* builtin_TypedObject_h */ #endif /* builtin_TypedObject_h */

View File

@ -30,7 +30,7 @@ function foo() {
var s; var s;
for (var i = 0; i < N; i++) { for (var i = 0; i < N; i++) {
if ((i % 2) == 0 || true) if ((i % 2) == 0)
obj = new PointType2({x: i, y: i+1}); obj = new PointType2({x: i, y: i+1});
else else
obj = new PointType3({x: i, y: i+1, z: i+2}); obj = new PointType3({x: i, y: i+1, z: i+2});

View File

@ -117,7 +117,6 @@ IonBuilder::IonBuilder(JSContext *analysisContext, CompileCompartment *comp,
backgroundCodegen_(nullptr), backgroundCodegen_(nullptr),
analysisContext(analysisContext), analysisContext(analysisContext),
baselineFrame_(baselineFrame), baselineFrame_(baselineFrame),
descrSetHash_(nullptr),
constraints_(constraints), constraints_(constraints),
analysis_(*temp, info->script()), analysis_(*temp, info->script()),
thisTypes(nullptr), thisTypes(nullptr),
@ -6750,26 +6749,24 @@ IonBuilder::getElemTryTypedObject(bool *emitted, MDefinition *obj, MDefinition *
{ {
JS_ASSERT(*emitted == false); JS_ASSERT(*emitted == false);
TypeDescrSet objDescrs; TypedObjectPrediction objPrediction = typedObjectPrediction(obj);
if (!lookupTypeDescrSet(obj, &objDescrs)) if (objPrediction.isUseless())
return false;
if (!objDescrs.allOfArrayKind())
return true; return true;
TypeDescrSet elemDescrs; if (!objPrediction.ofArrayKind())
if (!objDescrs.arrayElementType(*this, &elemDescrs))
return false;
if (elemDescrs.empty())
return true; return true;
JS_ASSERT(TypeDescr::isSized(elemDescrs.kind())); TypedObjectPrediction elemPrediction = objPrediction.arrayElementType();
if (elemPrediction.isUseless())
return true;
JS_ASSERT(TypeDescr::isSized(elemPrediction.kind()));
int32_t elemSize; int32_t elemSize;
if (!elemDescrs.allHaveSameSize(&elemSize)) if (!elemPrediction.hasKnownSize(&elemSize))
return true; return true;
switch (elemDescrs.kind()) { switch (elemPrediction.kind()) {
case type::X4: case type::X4:
// FIXME (bug 894105): load into a MIRType_float32x4 etc // FIXME (bug 894105): load into a MIRType_float32x4 etc
return true; return true;
@ -6779,15 +6776,15 @@ IonBuilder::getElemTryTypedObject(bool *emitted, MDefinition *obj, MDefinition *
return getElemTryComplexElemOfTypedObject(emitted, return getElemTryComplexElemOfTypedObject(emitted,
obj, obj,
index, index,
objDescrs, objPrediction,
elemDescrs, elemPrediction,
elemSize); elemSize);
case type::Scalar: case type::Scalar:
return getElemTryScalarElemOfTypedObject(emitted, return getElemTryScalarElemOfTypedObject(emitted,
obj, obj,
index, index,
objDescrs, objPrediction,
elemDescrs, elemPrediction,
elemSize); elemSize);
case type::Reference: case type::Reference:
@ -6808,7 +6805,7 @@ bool
IonBuilder::checkTypedObjectIndexInBounds(int32_t elemSize, IonBuilder::checkTypedObjectIndexInBounds(int32_t elemSize,
MDefinition *obj, MDefinition *obj,
MDefinition *index, MDefinition *index,
TypeDescrSet objDescrs, TypedObjectPrediction objPrediction,
MDefinition **indexAsByteOffset, MDefinition **indexAsByteOffset,
bool *canBeNeutered) bool *canBeNeutered)
{ {
@ -6822,7 +6819,7 @@ IonBuilder::checkTypedObjectIndexInBounds(int32_t elemSize,
// Value to int32 using truncation. // Value to int32 using truncation.
int32_t lenOfAll; int32_t lenOfAll;
MDefinition *length; MDefinition *length;
if (objDescrs.hasKnownArrayLength(&lenOfAll)) { if (objPrediction.hasKnownArrayLength(&lenOfAll)) {
length = constantInt(lenOfAll); length = constantInt(lenOfAll);
// If we are not loading the length from the object itself, // If we are not loading the length from the object itself,
@ -6859,21 +6856,19 @@ bool
IonBuilder::getElemTryScalarElemOfTypedObject(bool *emitted, IonBuilder::getElemTryScalarElemOfTypedObject(bool *emitted,
MDefinition *obj, MDefinition *obj,
MDefinition *index, MDefinition *index,
TypeDescrSet objDescrs, TypedObjectPrediction objPrediction,
TypeDescrSet elemDescrs, TypedObjectPrediction elemPrediction,
int32_t elemSize) int32_t elemSize)
{ {
JS_ASSERT(objDescrs.allOfArrayKind()); JS_ASSERT(objPrediction.ofArrayKind());
// Must always be loading the same scalar type // Must always be loading the same scalar type
ScalarTypeDescr::Type elemType; ScalarTypeDescr::Type elemType = elemPrediction.scalarType();
if (!elemDescrs.scalarType(&elemType))
return true;
JS_ASSERT(elemSize == ScalarTypeDescr::alignment(elemType)); JS_ASSERT(elemSize == ScalarTypeDescr::alignment(elemType));
bool canBeNeutered; bool canBeNeutered;
MDefinition *indexAsByteOffset; MDefinition *indexAsByteOffset;
if (!checkTypedObjectIndexInBounds(elemSize, obj, index, objDescrs, if (!checkTypedObjectIndexInBounds(elemSize, obj, index, objPrediction,
&indexAsByteOffset, &canBeNeutered)) &indexAsByteOffset, &canBeNeutered))
{ {
return false; return false;
@ -6927,32 +6922,32 @@ bool
IonBuilder::getElemTryComplexElemOfTypedObject(bool *emitted, IonBuilder::getElemTryComplexElemOfTypedObject(bool *emitted,
MDefinition *obj, MDefinition *obj,
MDefinition *index, MDefinition *index,
TypeDescrSet objDescrs, TypedObjectPrediction objPrediction,
TypeDescrSet elemDescrs, TypedObjectPrediction elemPrediction,
int32_t elemSize) int32_t elemSize)
{ {
JS_ASSERT(objDescrs.allOfArrayKind()); JS_ASSERT(objPrediction.ofArrayKind());
MDefinition *type = loadTypedObjectType(obj); MDefinition *type = loadTypedObjectType(obj);
MDefinition *elemTypeObj = typeObjectForElementFromArrayStructType(type); MDefinition *elemTypeObj = typeObjectForElementFromArrayStructType(type);
bool canBeNeutered; bool canBeNeutered;
MDefinition *indexAsByteOffset; MDefinition *indexAsByteOffset;
if (!checkTypedObjectIndexInBounds(elemSize, obj, index, objDescrs, if (!checkTypedObjectIndexInBounds(elemSize, obj, index, objPrediction,
&indexAsByteOffset, &canBeNeutered)) &indexAsByteOffset, &canBeNeutered))
{ {
return false; return false;
} }
return pushDerivedTypedObject(emitted, obj, indexAsByteOffset, return pushDerivedTypedObject(emitted, obj, indexAsByteOffset,
elemDescrs, elemTypeObj, canBeNeutered); elemPrediction, elemTypeObj, canBeNeutered);
} }
bool bool
IonBuilder::pushDerivedTypedObject(bool *emitted, IonBuilder::pushDerivedTypedObject(bool *emitted,
MDefinition *obj, MDefinition *obj,
MDefinition *offset, MDefinition *offset,
TypeDescrSet derivedTypeDescrs, TypedObjectPrediction derivedPrediction,
MDefinition *derivedTypeObj, MDefinition *derivedTypeObj,
bool canBeNeutered) bool canBeNeutered)
{ {
@ -6962,7 +6957,7 @@ IonBuilder::pushDerivedTypedObject(bool *emitted,
// Create the derived typed object. // Create the derived typed object.
MInstruction *derivedTypedObj = MNewDerivedTypedObject::New(alloc(), MInstruction *derivedTypedObj = MNewDerivedTypedObject::New(alloc(),
derivedTypeDescrs, derivedPrediction,
derivedTypeObj, derivedTypeObj,
owner, owner,
ownerOffset); ownerOffset);
@ -6976,7 +6971,7 @@ IonBuilder::pushDerivedTypedObject(bool *emitted,
// determined based on the type descriptor (and is immutable). // determined based on the type descriptor (and is immutable).
types::TemporaryTypeSet *objTypes = obj->resultTypeSet(); types::TemporaryTypeSet *objTypes = obj->resultTypeSet();
const Class *expectedClass = objTypes ? objTypes->getKnownClass() : nullptr; const Class *expectedClass = objTypes ? objTypes->getKnownClass() : nullptr;
JSObject *expectedProto = derivedTypeDescrs.knownPrototype(); const TypedProto *expectedProto = derivedPrediction.getKnownPrototype();
JS_ASSERT_IF(expectedClass, IsTypedObjectClass(expectedClass)); JS_ASSERT_IF(expectedClass, IsTypedObjectClass(expectedClass));
// Determine (if possible) the class/proto that the observed type set // Determine (if possible) the class/proto that the observed type set
@ -7663,26 +7658,24 @@ IonBuilder::setElemTryTypedObject(bool *emitted, MDefinition *obj,
{ {
JS_ASSERT(*emitted == false); JS_ASSERT(*emitted == false);
TypeDescrSet objTypeDescrs; TypedObjectPrediction objPrediction = typedObjectPrediction(obj);
if (!lookupTypeDescrSet(obj, &objTypeDescrs)) if (objPrediction.isUseless())
return false;
if (!objTypeDescrs.allOfArrayKind())
return true; return true;
TypeDescrSet elemTypeDescrs; if (!objPrediction.ofArrayKind())
if (!objTypeDescrs.arrayElementType(*this, &elemTypeDescrs))
return false;
if (elemTypeDescrs.empty())
return true; return true;
JS_ASSERT(TypeDescr::isSized(elemTypeDescrs.kind())); TypedObjectPrediction elemPrediction = objPrediction.arrayElementType();
if (elemPrediction.isUseless())
return true;
JS_ASSERT(TypeDescr::isSized(elemPrediction.kind()));
int32_t elemSize; int32_t elemSize;
if (!elemTypeDescrs.allHaveSameSize(&elemSize)) if (!elemPrediction.hasKnownSize(&elemSize))
return true; return true;
switch (elemTypeDescrs.kind()) { switch (elemPrediction.kind()) {
case type::X4: case type::X4:
// FIXME (bug 894105): store a MIRType_float32x4 etc // FIXME (bug 894105): store a MIRType_float32x4 etc
return true; return true;
@ -7698,9 +7691,9 @@ IonBuilder::setElemTryTypedObject(bool *emitted, MDefinition *obj,
return setElemTryScalarElemOfTypedObject(emitted, return setElemTryScalarElemOfTypedObject(emitted,
obj, obj,
index, index,
objTypeDescrs, objPrediction,
value, value,
elemTypeDescrs, elemPrediction,
elemSize); elemSize);
} }
@ -7711,20 +7704,18 @@ bool
IonBuilder::setElemTryScalarElemOfTypedObject(bool *emitted, IonBuilder::setElemTryScalarElemOfTypedObject(bool *emitted,
MDefinition *obj, MDefinition *obj,
MDefinition *index, MDefinition *index,
TypeDescrSet objTypeDescrs, TypedObjectPrediction objPrediction,
MDefinition *value, MDefinition *value,
TypeDescrSet elemTypeDescrs, TypedObjectPrediction elemPrediction,
int32_t elemSize) int32_t elemSize)
{ {
// Must always be loading the same scalar type // Must always be loading the same scalar type
ScalarTypeDescr::Type elemType; ScalarTypeDescr::Type elemType = elemPrediction.scalarType();
if (!elemTypeDescrs.scalarType(&elemType))
return true;
JS_ASSERT(elemSize == ScalarTypeDescr::alignment(elemType)); JS_ASSERT(elemSize == ScalarTypeDescr::alignment(elemType));
bool canBeNeutered; bool canBeNeutered;
MDefinition *indexAsByteOffset; MDefinition *indexAsByteOffset;
if (!checkTypedObjectIndexInBounds(elemSize, obj, index, objTypeDescrs, if (!checkTypedObjectIndexInBounds(elemSize, obj, index, objPrediction,
&indexAsByteOffset, &canBeNeutered)) &indexAsByteOffset, &canBeNeutered))
{ {
return false; return false;
@ -8696,18 +8687,18 @@ IonBuilder::getPropTryConstant(bool *emitted, MDefinition *obj, PropertyName *na
} }
bool bool
IonBuilder::getPropTryTypedObject(bool *emitted, MDefinition *obj, PropertyName *name, IonBuilder::getPropTryTypedObject(bool *emitted,
MDefinition *obj,
PropertyName *name,
types::TemporaryTypeSet *resultTypes) types::TemporaryTypeSet *resultTypes)
{ {
TypeDescrSet fieldDescrs; TypedObjectPrediction fieldPrediction;
int32_t fieldOffset; int32_t fieldOffset;
size_t fieldIndex; size_t fieldIndex;
if (!lookupTypedObjectField(obj, name, &fieldOffset, &fieldDescrs, &fieldIndex)) if (!typedObjectHasField(obj, name, &fieldOffset, &fieldPrediction, &fieldIndex))
return false;
if (fieldDescrs.empty())
return true; return true;
switch (fieldDescrs.kind()) { switch (fieldPrediction.kind()) {
case type::Reference: case type::Reference:
return true; return true;
@ -8720,7 +8711,7 @@ IonBuilder::getPropTryTypedObject(bool *emitted, MDefinition *obj, PropertyName
return getPropTryComplexPropOfTypedObject(emitted, return getPropTryComplexPropOfTypedObject(emitted,
obj, obj,
fieldOffset, fieldOffset,
fieldDescrs, fieldPrediction,
fieldIndex, fieldIndex,
resultTypes); resultTypes);
@ -8728,7 +8719,7 @@ IonBuilder::getPropTryTypedObject(bool *emitted, MDefinition *obj, PropertyName
return getPropTryScalarPropOfTypedObject(emitted, return getPropTryScalarPropOfTypedObject(emitted,
obj, obj,
fieldOffset, fieldOffset,
fieldDescrs, fieldPrediction,
resultTypes); resultTypes);
case type::UnsizedArray: case type::UnsizedArray:
@ -8741,13 +8732,11 @@ IonBuilder::getPropTryTypedObject(bool *emitted, MDefinition *obj, PropertyName
bool bool
IonBuilder::getPropTryScalarPropOfTypedObject(bool *emitted, MDefinition *typedObj, IonBuilder::getPropTryScalarPropOfTypedObject(bool *emitted, MDefinition *typedObj,
int32_t fieldOffset, int32_t fieldOffset,
TypeDescrSet fieldDescrs, TypedObjectPrediction fieldPrediction,
types::TemporaryTypeSet *resultTypes) types::TemporaryTypeSet *resultTypes)
{ {
// Must always be loading the same scalar type. // Must always be loading the same scalar type
ScalarTypeDescr::Type fieldType; ScalarTypeDescr::Type fieldType = fieldPrediction.scalarType();
if (!fieldDescrs.scalarType(&fieldType))
return true;
// OK, perform the optimization. // OK, perform the optimization.
return pushScalarLoadFromTypedObject(emitted, typedObj, constantInt(fieldOffset), return pushScalarLoadFromTypedObject(emitted, typedObj, constantInt(fieldOffset),
@ -8755,17 +8744,13 @@ IonBuilder::getPropTryScalarPropOfTypedObject(bool *emitted, MDefinition *typedO
} }
bool bool
IonBuilder::getPropTryComplexPropOfTypedObject(bool *emitted, MDefinition *typedObj, IonBuilder::getPropTryComplexPropOfTypedObject(bool *emitted,
MDefinition *typedObj,
int32_t fieldOffset, int32_t fieldOffset,
TypeDescrSet fieldDescrs, TypedObjectPrediction fieldPrediction,
size_t fieldIndex, size_t fieldIndex,
types::TemporaryTypeSet *resultTypes) types::TemporaryTypeSet *resultTypes)
{ {
// Must know the field index so that we can load the new type
// object for the derived value
if (fieldIndex == SIZE_MAX)
return true;
// OK, perform the optimization // OK, perform the optimization
// Identify the type object for the field. // Identify the type object for the field.
@ -8773,7 +8758,7 @@ IonBuilder::getPropTryComplexPropOfTypedObject(bool *emitted, MDefinition *typed
MDefinition *fieldTypeObj = typeObjectForFieldFromStructType(type, fieldIndex); MDefinition *fieldTypeObj = typeObjectForFieldFromStructType(type, fieldIndex);
return pushDerivedTypedObject(emitted, typedObj, constantInt(fieldOffset), return pushDerivedTypedObject(emitted, typedObj, constantInt(fieldOffset),
fieldDescrs, fieldTypeObj, true); fieldPrediction, fieldTypeObj, true);
} }
bool bool
@ -9352,16 +9337,13 @@ bool
IonBuilder::setPropTryTypedObject(bool *emitted, MDefinition *obj, IonBuilder::setPropTryTypedObject(bool *emitted, MDefinition *obj,
PropertyName *name, MDefinition *value) PropertyName *name, MDefinition *value)
{ {
TypeDescrSet fieldDescrs; TypedObjectPrediction fieldPrediction;
int32_t fieldOffset; int32_t fieldOffset;
size_t fieldIndex; size_t fieldIndex;
if (!lookupTypedObjectField(obj, name, &fieldOffset, &fieldDescrs, if (!typedObjectHasField(obj, name, &fieldOffset, &fieldPrediction, &fieldIndex))
&fieldIndex))
return false;
if (fieldDescrs.empty())
return true; return true;
switch (fieldDescrs.kind()) { switch (fieldPrediction.kind()) {
case type::X4: case type::X4:
// FIXME (bug 894104): store into a MIRType_float32x4 etc // FIXME (bug 894104): store into a MIRType_float32x4 etc
return true; return true;
@ -9375,7 +9357,7 @@ IonBuilder::setPropTryTypedObject(bool *emitted, MDefinition *obj,
case type::Scalar: case type::Scalar:
return setPropTryScalarPropOfTypedObject(emitted, obj, fieldOffset, return setPropTryScalarPropOfTypedObject(emitted, obj, fieldOffset,
value, fieldDescrs); value, fieldPrediction);
} }
MOZ_ASSUME_UNREACHABLE("Unknown kind"); MOZ_ASSUME_UNREACHABLE("Unknown kind");
@ -9386,12 +9368,10 @@ IonBuilder::setPropTryScalarPropOfTypedObject(bool *emitted,
MDefinition *obj, MDefinition *obj,
int32_t fieldOffset, int32_t fieldOffset,
MDefinition *value, MDefinition *value,
TypeDescrSet fieldDescrs) TypedObjectPrediction fieldPrediction)
{ {
// Must always be loading the same scalar type // Must always be loading the same scalar type
ScalarTypeDescr::Type fieldType; ScalarTypeDescr::Type fieldType = fieldPrediction.scalarType();
if (!fieldDescrs.scalarType(&fieldType))
return true;
// OK! Perform the optimization. // OK! Perform the optimization.
@ -10220,56 +10200,37 @@ IonBuilder::bytecodeTypes(jsbytecode *pc)
return types::TypeScript::BytecodeTypes(script(), pc, bytecodeTypeMap, &typeArrayHint, typeArray); return types::TypeScript::BytecodeTypes(script(), pc, bytecodeTypeMap, &typeArrayHint, typeArray);
} }
TypeDescrSetHash * TypedObjectPrediction
IonBuilder::getOrCreateDescrSetHash() IonBuilder::typedObjectPrediction(MDefinition *typedObj)
{ {
if (!descrSetHash_) { // Extract TypedObjectPrediction directly if we can
TypeDescrSetHash *hash =
alloc_->lifoAlloc()->new_<TypeDescrSetHash>(alloc());
if (!hash || !hash->init())
return nullptr;
descrSetHash_ = hash;
}
return descrSetHash_;
}
bool
IonBuilder::lookupTypeDescrSet(MDefinition *typedObj,
TypeDescrSet *out)
{
*out = TypeDescrSet(); // default to unknown
// Extract TypeDescrSet directly if we can
if (typedObj->isNewDerivedTypedObject()) { if (typedObj->isNewDerivedTypedObject()) {
*out = typedObj->toNewDerivedTypedObject()->set(); return typedObj->toNewDerivedTypedObject()->prediction();
return true;
} }
types::TemporaryTypeSet *types = typedObj->resultTypeSet(); types::TemporaryTypeSet *types = typedObj->resultTypeSet();
return typeSetToTypeDescrSet(types, out); return typedObjectPrediction(types);
} }
bool TypedObjectPrediction
IonBuilder::typeSetToTypeDescrSet(types::TemporaryTypeSet *types, IonBuilder::typedObjectPrediction(types::TemporaryTypeSet *types)
TypeDescrSet *out)
{ {
// Extract TypeDescrSet directly if we can // Type set must be known to be an object.
if (!types || types->getKnownMIRType() != MIRType_Object) if (!types || types->getKnownMIRType() != MIRType_Object)
return true; return TypedObjectPrediction();
// And only known objects. // And only known objects.
if (types->unknownObject()) if (types->unknownObject())
return true; return TypedObjectPrediction();
TypeDescrSetBuilder set; TypedObjectPrediction out;
for (uint32_t i = 0; i < types->getObjectCount(); i++) { for (uint32_t i = 0; i < types->getObjectCount(); i++) {
types::TypeObject *type = types->getTypeObject(i); types::TypeObject *type = types->getTypeObject(i);
if (!type) if (!type || type->unknownProperties())
return true; return TypedObjectPrediction();
if (!IsTypedObjectClass(type->clasp())) if (!IsTypedObjectClass(type->clasp()))
return true; return TypedObjectPrediction();
TaggedProto proto = type->proto(); TaggedProto proto = type->proto();
@ -10278,11 +10239,10 @@ IonBuilder::typeSetToTypeDescrSet(types::TemporaryTypeSet *types,
JS_ASSERT(proto.isObject() && proto.toObject()->is<TypedProto>()); JS_ASSERT(proto.isObject() && proto.toObject()->is<TypedProto>());
TypedProto &typedProto = proto.toObject()->as<TypedProto>(); TypedProto &typedProto = proto.toObject()->as<TypedProto>();
if (!set.insert(&typedProto.typeDescr())) out.addProto(typedProto);
return false;
} }
return set.build(*this, out); return out;
} }
MDefinition * MDefinition *
@ -10388,36 +10348,27 @@ IonBuilder::loadTypedObjectElements(MDefinition *typedObj,
} }
// Looks up the offset/type-repr-set of the field `id`, given the type // Looks up the offset/type-repr-set of the field `id`, given the type
// set `objTypes` of the field owner. Note that even when true is // set `objTypes` of the field owner. If a field is found, returns true
// returned, `*fieldDescrs` might be empty if no useful type/offset // and sets *fieldOffset, *fieldPrediction, and *fieldIndex. Returns false
// pair could be determined. // otherwise. Infallible.
bool bool
IonBuilder::lookupTypedObjectField(MDefinition *typedObj, IonBuilder::typedObjectHasField(MDefinition *typedObj,
PropertyName *name, PropertyName *name,
int32_t *fieldOffset, int32_t *fieldOffset,
TypeDescrSet *fieldDescrs, TypedObjectPrediction *fieldPrediction,
size_t *fieldIndex) size_t *fieldIndex)
{ {
TypeDescrSet objDescrs; TypedObjectPrediction objPrediction = typedObjectPrediction(typedObj);
if (!lookupTypeDescrSet(typedObj, &objDescrs)) if (objPrediction.isUseless())
return false; return false;
// Must be accessing a struct. // Must be accessing a struct.
if (!objDescrs.allOfKind(type::Struct)) if (objPrediction.kind() != type::Struct)
return true; return false;
// Determine the type/offset of the field `name`, if any. // Determine the type/offset of the field `name`, if any.
int32_t offset; return objPrediction.hasFieldNamed(NameToId(name), fieldOffset,
if (!objDescrs.fieldNamed(*this, NameToId(name), &offset, fieldPrediction, fieldIndex);
fieldDescrs, fieldIndex))
return false;
if (fieldDescrs->empty())
return true;
JS_ASSERT(offset >= 0);
*fieldOffset = offset;
return true;
} }
MDefinition * MDefinition *

View File

@ -17,7 +17,6 @@
#include "jit/MIR.h" #include "jit/MIR.h"
#include "jit/MIRGenerator.h" #include "jit/MIRGenerator.h"
#include "jit/MIRGraph.h" #include "jit/MIRGraph.h"
#include "jit/TypeDescrSet.h"
namespace js { namespace js {
namespace jit { namespace jit {
@ -413,11 +412,11 @@ class IonBuilder : public MIRGenerator
types::TemporaryTypeSet *resultTypes); types::TemporaryTypeSet *resultTypes);
bool getPropTryScalarPropOfTypedObject(bool *emitted, MDefinition *typedObj, bool getPropTryScalarPropOfTypedObject(bool *emitted, MDefinition *typedObj,
int32_t fieldOffset, int32_t fieldOffset,
TypeDescrSet fieldTypeReprs, TypedObjectPrediction fieldTypeReprs,
types::TemporaryTypeSet *resultTypes); types::TemporaryTypeSet *resultTypes);
bool getPropTryComplexPropOfTypedObject(bool *emitted, MDefinition *typedObj, bool getPropTryComplexPropOfTypedObject(bool *emitted, MDefinition *typedObj,
int32_t fieldOffset, int32_t fieldOffset,
TypeDescrSet fieldTypeReprs, TypedObjectPrediction fieldTypeReprs,
size_t fieldIndex, size_t fieldIndex,
types::TemporaryTypeSet *resultTypes); types::TemporaryTypeSet *resultTypes);
bool getPropTryInnerize(bool *emitted, MDefinition *obj, PropertyName *name, bool getPropTryInnerize(bool *emitted, MDefinition *obj, PropertyName *name,
@ -444,21 +443,19 @@ class IonBuilder : public MIRGenerator
MDefinition *obj, MDefinition *obj,
int32_t fieldOffset, int32_t fieldOffset,
MDefinition *value, MDefinition *value,
TypeDescrSet fieldTypeReprs); TypedObjectPrediction fieldTypeReprs);
bool setPropTryCache(bool *emitted, MDefinition *obj, bool setPropTryCache(bool *emitted, MDefinition *obj,
PropertyName *name, MDefinition *value, PropertyName *name, MDefinition *value,
bool barrier, types::TemporaryTypeSet *objTypes); bool barrier, types::TemporaryTypeSet *objTypes);
// binary data lookup helpers. // binary data lookup helpers.
bool lookupTypeDescrSet(MDefinition *typedObj, TypedObjectPrediction typedObjectPrediction(MDefinition *typedObj);
TypeDescrSet *out); TypedObjectPrediction typedObjectPrediction(types::TemporaryTypeSet *types);
bool typeSetToTypeDescrSet(types::TemporaryTypeSet *types, bool typedObjectHasField(MDefinition *typedObj,
TypeDescrSet *out); PropertyName *name,
bool lookupTypedObjectField(MDefinition *typedObj, int32_t *fieldOffset,
PropertyName *name, TypedObjectPrediction *fieldTypeReprs,
int32_t *fieldOffset, size_t *fieldIndex);
TypeDescrSet *fieldTypeReprs,
size_t *fieldIndex);
MDefinition *loadTypedObjectType(MDefinition *value); MDefinition *loadTypedObjectType(MDefinition *value);
void loadTypedObjectData(MDefinition *typedObj, void loadTypedObjectData(MDefinition *typedObj,
MDefinition *offset, MDefinition *offset,
@ -483,13 +480,13 @@ class IonBuilder : public MIRGenerator
bool checkTypedObjectIndexInBounds(int32_t elemSize, bool checkTypedObjectIndexInBounds(int32_t elemSize,
MDefinition *obj, MDefinition *obj,
MDefinition *index, MDefinition *index,
TypeDescrSet objTypeDescrs, TypedObjectPrediction objTypeDescrs,
MDefinition **indexAsByteOffset, MDefinition **indexAsByteOffset,
bool *canBeNeutered); bool *canBeNeutered);
bool pushDerivedTypedObject(bool *emitted, bool pushDerivedTypedObject(bool *emitted,
MDefinition *obj, MDefinition *obj,
MDefinition *offset, MDefinition *offset,
TypeDescrSet derivedTypeDescrs, TypedObjectPrediction derivedTypeDescrs,
MDefinition *derivedTypeObj, MDefinition *derivedTypeObj,
bool canBeNeutered); bool canBeNeutered);
bool pushScalarLoadFromTypedObject(bool *emitted, bool pushScalarLoadFromTypedObject(bool *emitted,
@ -515,9 +512,9 @@ class IonBuilder : public MIRGenerator
bool setElemTryScalarElemOfTypedObject(bool *emitted, bool setElemTryScalarElemOfTypedObject(bool *emitted,
MDefinition *obj, MDefinition *obj,
MDefinition *index, MDefinition *index,
TypeDescrSet objTypeReprs, TypedObjectPrediction objTypeReprs,
MDefinition *value, MDefinition *value,
TypeDescrSet elemTypeReprs, TypedObjectPrediction elemTypeReprs,
int32_t elemSize); int32_t elemSize);
// jsop_getelem() helpers. // jsop_getelem() helpers.
@ -532,14 +529,14 @@ class IonBuilder : public MIRGenerator
bool getElemTryScalarElemOfTypedObject(bool *emitted, bool getElemTryScalarElemOfTypedObject(bool *emitted,
MDefinition *obj, MDefinition *obj,
MDefinition *index, MDefinition *index,
TypeDescrSet objTypeReprs, TypedObjectPrediction objTypeReprs,
TypeDescrSet elemTypeReprs, TypedObjectPrediction elemTypeReprs,
int32_t elemSize); int32_t elemSize);
bool getElemTryComplexElemOfTypedObject(bool *emitted, bool getElemTryComplexElemOfTypedObject(bool *emitted,
MDefinition *obj, MDefinition *obj,
MDefinition *index, MDefinition *index,
TypeDescrSet objTypeReprs, TypedObjectPrediction objTypeReprs,
TypeDescrSet elemTypeReprs, TypedObjectPrediction elemTypeReprs,
int32_t elemSize); int32_t elemSize);
enum BoundsChecking { DoBoundsCheck, SkipBoundsCheck }; enum BoundsChecking { DoBoundsCheck, SkipBoundsCheck };
@ -839,8 +836,6 @@ class IonBuilder : public MIRGenerator
CodeGenerator *backgroundCodegen() const { return backgroundCodegen_; } CodeGenerator *backgroundCodegen() const { return backgroundCodegen_; }
void setBackgroundCodegen(CodeGenerator *codegen) { backgroundCodegen_ = codegen; } void setBackgroundCodegen(CodeGenerator *codegen) { backgroundCodegen_ = codegen; }
TypeDescrSetHash *getOrCreateDescrSetHash(); // fallible
types::CompilerConstraintList *constraints() { types::CompilerConstraintList *constraints() {
return constraints_; return constraints_;
} }
@ -856,7 +851,6 @@ class IonBuilder : public MIRGenerator
JSContext *analysisContext; JSContext *analysisContext;
BaselineFrameInspector *baselineFrame_; BaselineFrameInspector *baselineFrame_;
TypeDescrSetHash *descrSetHash_;
// Constraints for recording dependencies on type information. // Constraints for recording dependencies on type information.
types::CompilerConstraintList *constraints_; types::CompilerConstraintList *constraints_;

View File

@ -1449,23 +1449,17 @@ IonBuilder::elementAccessIsTypedObjectArrayOfScalarType(MDefinition* obj, MDefin
if (id->type() != MIRType_Int32 && id->type() != MIRType_Double) if (id->type() != MIRType_Int32 && id->type() != MIRType_Double)
return false; return false;
TypeDescrSet objDescrs; TypedObjectPrediction prediction = typedObjectPrediction(obj);
if (!lookupTypeDescrSet(obj, &objDescrs)) if (prediction.isUseless() || !prediction.ofArrayKind())
return false; return false;
if (!objDescrs.allOfArrayKind()) TypedObjectPrediction elemPrediction = prediction.arrayElementType();
if (elemPrediction.isUseless() || elemPrediction.kind() != type::Scalar)
return false; return false;
TypeDescrSet elemDescrs; JS_ASSERT(type::isSized(elemPrediction.kind()));
if (!objDescrs.arrayElementType(*this, &elemDescrs)) *arrayType = elemPrediction.scalarType();
return false; return true;
if (elemDescrs.empty() || elemDescrs.kind() != type::Scalar)
return false;
JS_ASSERT(TypeDescr::isSized(elemDescrs.kind()));
return elemDescrs.scalarType(arrayType);
} }
bool bool

View File

@ -20,7 +20,7 @@
#include "jit/IonAllocPolicy.h" #include "jit/IonAllocPolicy.h"
#include "jit/IonMacroAssembler.h" #include "jit/IonMacroAssembler.h"
#include "jit/MOpcodes.h" #include "jit/MOpcodes.h"
#include "jit/TypeDescrSet.h" #include "jit/TypedObjectPrediction.h"
#include "jit/TypePolicy.h" #include "jit/TypePolicy.h"
#include "vm/ScopeObject.h" #include "vm/ScopeObject.h"
#include "vm/TypedArrayObject.h" #include "vm/TypedArrayObject.h"
@ -1769,14 +1769,14 @@ class MNewDerivedTypedObject
IntPolicy<2> > IntPolicy<2> >
{ {
private: private:
TypeDescrSet set_; TypedObjectPrediction prediction_;
MNewDerivedTypedObject(TypeDescrSet set, MNewDerivedTypedObject(TypedObjectPrediction prediction,
MDefinition *type, MDefinition *type,
MDefinition *owner, MDefinition *owner,
MDefinition *offset) MDefinition *offset)
: MTernaryInstruction(type, owner, offset), : MTernaryInstruction(type, owner, offset),
set_(set) prediction_(prediction)
{ {
setMovable(); setMovable();
setResultType(MIRType_Object); setResultType(MIRType_Object);
@ -1785,14 +1785,14 @@ class MNewDerivedTypedObject
public: public:
INSTRUCTION_HEADER(NewDerivedTypedObject); INSTRUCTION_HEADER(NewDerivedTypedObject);
static MNewDerivedTypedObject *New(TempAllocator &alloc, TypeDescrSet set, static MNewDerivedTypedObject *New(TempAllocator &alloc, TypedObjectPrediction prediction,
MDefinition *type, MDefinition *owner, MDefinition *offset) MDefinition *type, MDefinition *owner, MDefinition *offset)
{ {
return new(alloc) MNewDerivedTypedObject(set, type, owner, offset); return new(alloc) MNewDerivedTypedObject(prediction, type, owner, offset);
} }
TypeDescrSet set() const { TypedObjectPrediction prediction() const {
return set_; return prediction_;
} }
MDefinition *type() const { MDefinition *type() const {

View File

@ -1,373 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sts=4 et sw=4 tw=99:
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "jit/TypeDescrSet.h"
#include "mozilla/HashFunctions.h"
#include "builtin/TypedObject.h"
#include "jit/IonBuilder.h"
using namespace js;
using namespace jit;
///////////////////////////////////////////////////////////////////////////
// TypeDescrSet hasher
HashNumber
TypeDescrSetHasher::hash(TypeDescrSet key)
{
HashNumber hn = mozilla::HashGeneric(key.length());
for (size_t i = 0; i < key.length(); i++)
hn = mozilla::AddToHash(hn, uintptr_t(key.get(i)));
return hn;
}
bool
TypeDescrSetHasher::match(TypeDescrSet key1, TypeDescrSet key2)
{
if (key1.length() != key2.length())
return false;
// Note: entries are always sorted
for (size_t i = 0; i < key1.length(); i++) {
if (key1.get(i) != key2.get(i))
return false;
}
return true;
}
///////////////////////////////////////////////////////////////////////////
// TypeDescrSetBuilder
TypeDescrSetBuilder::TypeDescrSetBuilder()
: invalid_(false)
{}
bool
TypeDescrSetBuilder::insert(TypeDescr *descr)
{
// All type descriptors should be tenured, so it is safe to assume
// that the pointers do not change during compilation, since no
// major GC can overlap with compilation.
JS_ASSERT(!GetIonContext()->runtime->isInsideNursery(descr));
if (invalid_)
return true;
if (entries_.empty())
return entries_.append(descr);
// Check that this new type repr is of the same basic kind as the
// ones we have seen thus far. If not, for example if we have an
// `int` and a `struct`, then convert this set to the invalid set.
TypeDescr *entry0 = entries_[0];
if (descr->kind() != entry0->kind()) {
invalid_ = true;
entries_.clear();
return true;
}
// Otherwise, use binary search to find the right place to insert
// the TypeDescr. We keep the list sorted by the *address* of
// the TypeDescrs within.
uintptr_t descrAddr = (uintptr_t) descr;
size_t min = 0;
size_t max = entries_.length();
while (min != max) {
size_t i = min + ((max - min) >> 1); // average w/o fear of overflow
uintptr_t entryiaddr = (uintptr_t) entries_[i];
if (entryiaddr == descrAddr)
return true; // descr already present in the set
if (entryiaddr < descrAddr) {
// descr lies to the right of entry i
min = i + 1;
} else {
// descr lies to the left of entry i
max = i;
}
}
// As a sanity check, give up if the TypeDescrSet grows too large.
if (entries_.length() >= 512) {
invalid_ = true;
entries_.clear();
return true;
}
// Not present. Insert at position `min`.
if (min == entries_.length())
return entries_.append(descr);
TypeDescr **insertLoc = &entries_[min];
return entries_.insert(insertLoc, descr) != nullptr;
}
bool
TypeDescrSetBuilder::build(IonBuilder &builder, TypeDescrSet *out)
{
if (invalid_) {
*out = TypeDescrSet();
return true;
}
TypeDescrSetHash *table = builder.getOrCreateDescrSetHash();
if (!table)
return false;
// Check if there is already a copy in the hashtable.
size_t length = entries_.length();
TypeDescrSet tempSet(length, entries_.begin());
TypeDescrSetHash::AddPtr p = table->lookupForAdd(tempSet);
if (p) {
*out = *p;
return true;
}
// If not, allocate a permanent copy in Ion temp memory and add it.
size_t space = sizeof(TypeDescr*) * length;
TypeDescr **array = (TypeDescr**)
GetIonContext()->temp->allocate(space);
if (!array)
return false;
memcpy(array, entries_.begin(), space);
TypeDescrSet permSet(length, array);
if (!table->add(p, permSet))
return false;
*out = permSet;
return true;
}
///////////////////////////////////////////////////////////////////////////
// TypeDescrSet
TypeDescrSet::TypeDescrSet(const TypeDescrSet &c)
: length_(c.length_),
entries_(c.entries_)
{}
TypeDescrSet::TypeDescrSet(size_t length,
TypeDescr **entries)
: length_(length),
entries_(entries)
{}
TypeDescrSet::TypeDescrSet()
: length_(0),
entries_(nullptr)
{}
bool
TypeDescrSet::empty() const
{
return length_ == 0;
}
bool
TypeDescrSet::allOfArrayKind()
{
if (empty())
return false;
switch (kind()) {
case type::SizedArray:
case type::UnsizedArray:
return true;
case type::X4:
case type::Reference:
case type::Scalar:
case type::Struct:
return false;
}
MOZ_ASSUME_UNREACHABLE("Invalid kind() in TypeDescrSet");
}
bool
TypeDescrSet::allOfKind(type::Kind aKind)
{
if (empty())
return false;
return kind() == aKind;
}
bool
TypeDescrSet::allHaveSameSize(int32_t *out)
{
if (empty())
return false;
JS_ASSERT(TypeDescr::isSized(kind()));
int32_t size = get(0)->as<SizedTypeDescr>().size();
for (size_t i = 1; i < length(); i++) {
if (get(i)->as<SizedTypeDescr>().size() != size)
return false;
}
*out = size;
return true;
}
JSObject *
TypeDescrSet::knownPrototype() const
{
JS_ASSERT(!empty());
if (length() > 1 || !get(0)->is<ComplexTypeDescr>())
return nullptr;
return &get(0)->as<ComplexTypeDescr>().instancePrototype();
}
type::Kind
TypeDescrSet::kind()
{
JS_ASSERT(!empty());
return get(0)->kind();
}
template<typename T>
bool
TypeDescrSet::genericType(typename T::Type *out)
{
JS_ASSERT(allOfKind(type::Scalar));
typename T::Type type = get(0)->as<T>().type();
for (size_t i = 1; i < length(); i++) {
if (get(i)->as<T>().type() != type)
return false;
}
*out = type;
return true;
}
bool
TypeDescrSet::scalarType(ScalarTypeDescr::Type *out)
{
return genericType<ScalarTypeDescr>(out);
}
bool
TypeDescrSet::referenceType(ReferenceTypeDescr::Type *out)
{
return genericType<ReferenceTypeDescr>(out);
}
bool
TypeDescrSet::x4Type(X4TypeDescr::Type *out)
{
return genericType<X4TypeDescr>(out);
}
bool
TypeDescrSet::hasKnownArrayLength(int32_t *l)
{
switch (kind()) {
case type::UnsizedArray:
return false;
case type::SizedArray:
{
const size_t result = get(0)->as<SizedArrayTypeDescr>().length();
for (size_t i = 1; i < length(); i++) {
size_t l = get(i)->as<SizedArrayTypeDescr>().length();
if (l != result)
return false;
}
*l = result;
return true;
}
default:
MOZ_ASSUME_UNREACHABLE("Invalid array size for call to arrayLength()");
}
}
bool
TypeDescrSet::arrayElementType(IonBuilder &builder, TypeDescrSet *out)
{
TypeDescrSetBuilder elementTypes;
for (size_t i = 0; i < length(); i++) {
switch (kind()) {
case type::UnsizedArray:
if (!elementTypes.insert(&get(i)->as<UnsizedArrayTypeDescr>().elementType()))
return false;
break;
case type::SizedArray:
if (!elementTypes.insert(&get(i)->as<SizedArrayTypeDescr>().elementType()))
return false;
break;
default:
MOZ_ASSUME_UNREACHABLE("Invalid kind for arrayElementType()");
}
}
return elementTypes.build(builder, out);
}
bool
TypeDescrSet::fieldNamed(IonBuilder &builder,
jsid id,
int32_t *offset,
TypeDescrSet *out,
size_t *index)
{
JS_ASSERT(kind() == type::Struct);
// Initialize `*offset` and `*out` for the case where incompatible
// or absent fields are found.
*offset = -1;
*index = SIZE_MAX;
*out = TypeDescrSet();
// Remember offset of the first field.
int32_t offset0;
size_t index0;
TypeDescrSetBuilder fieldTypes;
{
StructTypeDescr &descr0 = get(0)->as<StructTypeDescr>();
if (!descr0.fieldIndex(id, &index0))
return true;
offset0 = descr0.fieldOffset(index0);
if (!fieldTypes.insert(&descr0.fieldDescr(index0)))
return false;
}
// Check that all subsequent fields are at the same offset
// and compute the union of their types.
for (size_t i = 1; i < length(); i++) {
StructTypeDescr &descri = get(i)->as<StructTypeDescr>();
size_t indexi;
if (!descri.fieldIndex(id, &indexi))
return true;
// Track whether all indices agree, but do not require it to be true
if (indexi != index0)
index0 = SIZE_MAX;
// Require that all offsets agree
if (descri.fieldOffset(indexi) != offset0)
return true;
if (!fieldTypes.insert(&descri.fieldDescr(indexi)))
return false;
}
// All struct types had a field named `id` at the same offset
// (though it's still possible that the types are incompatible and
// that the indices disagree).
*offset = offset0;
*index = index0;
return fieldTypes.build(builder, out);
}

View File

@ -1,204 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sts=4 et sw=4 tw=99:
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef jit_TypeRepresentationSet_h
#define jit_TypeRepresentationSet_h
#include "builtin/TypedObject.h"
#include "jit/IonAllocPolicy.h"
#include "js/HashTable.h"
// TypeRepresentationSet stores a set of TypeRepresentation* objects,
// representing the possible types of the binary data associated with
// a typed object value. Often TypeRepresentationSets will be
// singleton sets, but it is also possible to have cases where many
// type representations flow into a single point. In such cases, the
// various type representations may differ in their details but often
// have a common prefix. We try to optimize this case as well.
//
// So, for example, consider some code like:
//
// var Point2Type = new StructType({x: uint8, y: uint8});
// var Point3Type = new StructType({x: uint8, y: uint8, z: uint8});
//
// function distance2d(pnt) {
// return Math.sqrt(pnt.x * pnt.x + pnt.y * pnt.y);
// }
//
// Even if the function `distance2d()` were used with instances of
// both Point2Type and Point3Type, we can still generate optimal code,
// because both of those types contain fields named `x` and `y` with
// the same types at the same offset.
namespace js {
namespace jit {
class IonBuilder;
class TypeDescrSet;
class TypeDescrSetBuilder {
private:
Vector<TypeDescr *, 4, SystemAllocPolicy> entries_;
bool invalid_;
public:
TypeDescrSetBuilder();
bool insert(TypeDescr *typeRepr);
bool build(IonBuilder &builder, TypeDescrSet *out);
};
class TypeDescrSet {
private:
friend struct TypeDescrSetHasher;
friend class TypeDescrSetBuilder;
size_t length_;
TypeDescr **entries_; // Allocated using temp policy
TypeDescrSet(size_t length, TypeDescr **entries);
size_t length() const {
return length_;
}
TypeDescr *get(uint32_t i) const {
return entries_[i];
}
template<typename T>
bool genericType(typename T::Type *out);
public:
//////////////////////////////////////////////////////////////////////
// Constructors
//
// For more flexible constructors, see
// TypeDescrSetBuilder above.
TypeDescrSet(const TypeDescrSet &c);
TypeDescrSet(); // empty set
//////////////////////////////////////////////////////////////////////
// Query the set
bool empty() const;
bool allOfKind(type::Kind kind);
// Returns true only when non-empty and `kind()` is
// `TypeDescr::Array`
bool allOfArrayKind();
// Returns true only if (1) non-empty, (2) for all types t in this
// set, t is sized, and (3) there is some size S such that for all
// types t in this set, `t.size() == S`. When the above holds,
// then also sets `*out` to S; otherwise leaves `*out` unchanged
// and returns false.
//
// At the moment condition (2) trivially holds. When Bug 922115
// lands, some array types will be unsized.
bool allHaveSameSize(int32_t *out);
types::TemporaryTypeSet *suitableTypeSet(IonBuilder &builder,
const Class *knownClass);
//////////////////////////////////////////////////////////////////////
// The following operations are only valid on a non-empty set:
type::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
//
// Only valid when `kind() == type::Scalar`
// If all type descrs in this set have a single type, returns true
// and sets *out. Else returns false.
bool scalarType(ScalarTypeDescr::Type *out);
//////////////////////////////////////////////////////////////////////
// Reference operations
//
// Only valid when `kind() == type::Reference`
// If all type descrs in this set have a single type, returns true
// and sets *out. Else returns false.
bool referenceType(ReferenceTypeDescr::Type *out);
//////////////////////////////////////////////////////////////////////
// Reference operations
//
// Only valid when `kind() == type::X4`
// If all type descrs in this set have a single type, returns true
// and sets *out. Else returns false.
bool x4Type(X4TypeDescr::Type *out);
//////////////////////////////////////////////////////////////////////
// SizedArray operations
//
// Only valid when `kind() == type::SizedArray`
// Determines whether all arrays in this set have the same,
// statically known, array length and return that length
// (via `*length`) if so. Otherwise returns false.
bool hasKnownArrayLength(int32_t *length);
// Returns a `TypeDescrSet` representing the element
// types of the various array types in this set. The returned set
// may be the empty set.
bool arrayElementType(IonBuilder &builder, TypeDescrSet *out);
//////////////////////////////////////////////////////////////////////
// Struct operations
//
// Only valid when `kind() == type::Struct`
// Searches the type in the set for a field named `id`. All
// possible types must agree on the offset of the field within the
// structure and the possible types of the field must be
// compatible. If any pair of types disagree on the offset or have
// incompatible types for the field, then `*out` will be set to
// the empty set.
//
// Upon success, `out` will be set to the set of possible types of
// the field and `offset` will be set to the field's offset within
// the struct (measured in bytes).
//
// The parameter `*index` is special. If all types agree on the
// index of the field, then `*index` is set to the field index.
// Otherwise, it is set to SIZE_MAX. Note that two types may agree
// on the type and offset of a field but disagree about its index,
// e.g. the field `c` in `new StructType({a: uint8, b: uint8, c:
// uint16})` and `new StructType({a: uint16, c: uint16})`.
bool fieldNamed(IonBuilder &builder,
jsid id,
int32_t *offset,
TypeDescrSet *out,
size_t *index);
};
struct TypeDescrSetHasher
{
typedef TypeDescrSet Lookup;
static HashNumber hash(TypeDescrSet key);
static bool match(TypeDescrSet key1,
TypeDescrSet key2);
};
typedef js::HashSet<TypeDescrSet,
TypeDescrSetHasher,
IonAllocPolicy> TypeDescrSetHash;
} // namespace jit
} // namespace js
#endif

View File

@ -0,0 +1,379 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sts=4 et sw=4 tw=99:
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "jit/TypedObjectPrediction.h"
using namespace js;
using namespace jit;
static const size_t ALL_FIELDS = SIZE_MAX;
// Sets the prediction to be the common prefix of descrA and descrB,
// considering at most the first max fields.
//
// In the case where the current prediction is a specific struct,
// and we are now seeing a second struct, then descrA and descrB will be
// the current and new struct and max will be ALL_FIELDS.
//
// In the case where the current prediction is already a prefix, and
// we are now seeing an additional struct, then descrA will be the
// current struct and max will be the current prefix length, and
// descrB will be the new struct.
//
// (Note that in general it is not important which struct is passed as
// descrA and which struct is passed as descrB, as the operation is
// symmetric.)
void
TypedObjectPrediction::markAsCommonPrefix(const StructTypeDescr &descrA,
const StructTypeDescr &descrB,
size_t max)
{
// count is the number of fields in common. It begins as the min
// of the number of fields from descrA, descrB, and max, and then
// is decremented as we find uncommon fields.
if (max > descrA.fieldCount())
max = descrA.fieldCount();
if (max > descrB.fieldCount())
max = descrB.fieldCount();
size_t i = 0;
for (; i < max; i++) {
if (&descrA.fieldName(i) != &descrB.fieldName(i))
break;
if (&descrA.fieldDescr(i) != &descrB.fieldDescr(i))
break;
JS_ASSERT(descrA.fieldOffset(i) == descrB.fieldOffset(i));
}
if (i == 0) {
// empty prefix is not particularly useful.
markInconsistent();
} else {
setPrefix(descrA, i);
}
}
void
TypedObjectPrediction::addProto(const TypedProto &proto)
{
switch (predictionKind()) {
case Empty:
return setProto(proto);
case Inconsistent:
return; // keep same state
case Proto: {
if (&proto == data_.proto)
return; // keep same state
if (proto.kind() != data_.proto->kind())
return markInconsistent();
if (proto.kind() != type::Struct)
return markInconsistent();
const StructTypeDescr &structDescr = proto.typeDescr().as<StructTypeDescr>();
const StructTypeDescr &currentDescr = data_.proto->typeDescr().as<StructTypeDescr>();
markAsCommonPrefix(structDescr, currentDescr, ALL_FIELDS);
return;
}
case Descr:
// First downgrade from descr to proto, which is less precise,
// and then recurse.
setProto(data_.descr->typedProto());
return addProto(proto);
case Prefix:
if (proto.kind() != type::Struct)
return markInconsistent();
markAsCommonPrefix(*data_.prefix.descr,
proto.typeDescr().as<StructTypeDescr>(),
data_.prefix.fields);
return;
}
MOZ_ASSUME_UNREACHABLE("Bad predictionKind");
}
type::Kind
TypedObjectPrediction::kind() const
{
switch (predictionKind()) {
case TypedObjectPrediction::Empty:
case TypedObjectPrediction::Inconsistent:
break;
case TypedObjectPrediction::Proto:
return proto().kind();
case TypedObjectPrediction::Descr:
return descr().kind();
case TypedObjectPrediction::Prefix:
return prefix().descr->kind();
}
MOZ_ASSUME_UNREACHABLE("Bad prediction kind");
}
bool
TypedObjectPrediction::ofArrayKind() const
{
switch (kind()) {
case type::Scalar:
case type::Reference:
case type::X4:
case type::Struct:
return false;
case type::SizedArray:
case type::UnsizedArray:
return true;
}
MOZ_ASSUME_UNREACHABLE("Bad kind");
}
static bool
DescrHasKnownSize(const TypeDescr &descr, int32_t *out)
{
if (!descr.is<SizedTypeDescr>())
return false;
*out = descr.as<SizedTypeDescr>().size();
return true;
}
bool
TypedObjectPrediction::hasKnownSize(int32_t *out) const
{
switch (predictionKind()) {
case TypedObjectPrediction::Empty:
case TypedObjectPrediction::Inconsistent:
break;
case TypedObjectPrediction::Proto:
switch (kind()) {
case type::Scalar:
case type::Reference:
case type::X4:
case type::Struct:
*out = proto().typeDescr().as<SizedTypeDescr>().size();
return true;
case type::SizedArray:
case type::UnsizedArray:
// The prototype does not track the precise dimensions of arrays.
return false;
}
MOZ_ASSUME_UNREACHABLE("Unknown kind");
case TypedObjectPrediction::Descr:
return DescrHasKnownSize(descr(), out);
case TypedObjectPrediction::Prefix:
// We only know a prefix of the struct fields, hence we do not
// know its complete size.
return false;
}
MOZ_ASSUME_UNREACHABLE("Bad prediction kind");
}
const TypedProto *
TypedObjectPrediction::getKnownPrototype() const
{
switch (predictionKind()) {
case TypedObjectPrediction::Empty:
case TypedObjectPrediction::Inconsistent:
return nullptr;
case TypedObjectPrediction::Proto:
switch (proto().kind()) {
case type::Scalar:
case type::Reference:
return nullptr;
case type::X4:
case type::Struct:
case type::SizedArray:
case type::UnsizedArray:
return &proto();
}
MOZ_ASSUME_UNREACHABLE("Invalid proto().kind()");
case TypedObjectPrediction::Descr:
if (descr().is<ComplexTypeDescr>())
return &descr().as<ComplexTypeDescr>().instancePrototype();
return nullptr;
case TypedObjectPrediction::Prefix:
// We only know a prefix of the struct fields, hence we cannot
// say for certain what its prototype will be.
return nullptr;
}
MOZ_ASSUME_UNREACHABLE("Bad prediction kind");
}
template<typename T>
typename T::Type
TypedObjectPrediction::extractType() const
{
JS_ASSERT(kind() == T::Kind);
switch (predictionKind()) {
case TypedObjectPrediction::Empty:
case TypedObjectPrediction::Inconsistent:
break;
case TypedObjectPrediction::Proto:
return proto().typeDescr().as<T>().type();
case TypedObjectPrediction::Descr:
return descr().as<T>().type();
case TypedObjectPrediction::Prefix:
break; // Prefixes are always structs, never scalars etc
}
MOZ_ASSUME_UNREACHABLE("Bad prediction kind");
}
ScalarTypeDescr::Type
TypedObjectPrediction::scalarType() const
{
return extractType<ScalarTypeDescr>();
}
ReferenceTypeDescr::Type
TypedObjectPrediction::referenceType() const
{
return extractType<ReferenceTypeDescr>();
}
X4TypeDescr::Type
TypedObjectPrediction::x4Type() const
{
return extractType<X4TypeDescr>();
}
bool
TypedObjectPrediction::hasKnownArrayLength(int32_t *length) const
{
JS_ASSERT(ofArrayKind());
switch (predictionKind()) {
case TypedObjectPrediction::Empty:
case TypedObjectPrediction::Inconsistent:
break;
case TypedObjectPrediction::Proto:
// The prototype does not track the lengths of arrays.
return false;
case TypedObjectPrediction::Descr:
// In later patches, this condition will always be true
// so long as this represents an array
if (descr().is<SizedArrayTypeDescr>()) {
*length = descr().as<SizedArrayTypeDescr>().length();
return true;
}
return false;
case TypedObjectPrediction::Prefix:
break; // Prefixes are always structs, never arrays
}
MOZ_ASSUME_UNREACHABLE("Bad prediction kind");
}
static TypeDescr &
DescrArrayElementType(const TypeDescr &descr) {
return (descr.is<SizedArrayTypeDescr>()
? descr.as<SizedArrayTypeDescr>().elementType()
: descr.as<UnsizedArrayTypeDescr>().elementType());
}
TypedObjectPrediction
TypedObjectPrediction::arrayElementType() const
{
JS_ASSERT(ofArrayKind());
switch (predictionKind()) {
case TypedObjectPrediction::Empty:
case TypedObjectPrediction::Inconsistent:
break;
case TypedObjectPrediction::Proto:
return TypedObjectPrediction(DescrArrayElementType(proto().typeDescr()));
case TypedObjectPrediction::Descr:
return TypedObjectPrediction(DescrArrayElementType(descr()));
case TypedObjectPrediction::Prefix:
break; // Prefixes are always structs, never arrays
}
MOZ_ASSUME_UNREACHABLE("Bad prediction kind");
}
bool
TypedObjectPrediction::hasFieldNamedPrefix(const StructTypeDescr &descr,
size_t fieldCount,
jsid id,
int32_t *offset,
TypedObjectPrediction *out,
size_t *index) const
{
// Initialize |*offset| and |*out| for the case where incompatible
// or absent fields are found.
*offset = SIZE_MAX;
*index = SIZE_MAX;
*out = TypedObjectPrediction();
// Find the index of the field |id| if any.
if (!descr.fieldIndex(id, index))
return false;
// Check whether the index falls within our known safe prefix.
if (*index >= fieldCount)
return false;
// Load the offset and type.
*offset = descr.fieldOffset(*index);
*out = TypedObjectPrediction(descr.fieldDescr(*index));
return true;
}
bool
TypedObjectPrediction::hasFieldNamed(jsid id,
int32_t *fieldOffset,
TypedObjectPrediction *fieldType,
size_t *fieldIndex) const
{
JS_ASSERT(kind() == type::Struct);
switch (predictionKind()) {
case TypedObjectPrediction::Empty:
case TypedObjectPrediction::Inconsistent:
break;
case TypedObjectPrediction::Proto:
return hasFieldNamedPrefix(
proto().typeDescr().as<StructTypeDescr>(), ALL_FIELDS,
id, fieldOffset, fieldType, fieldIndex);
case TypedObjectPrediction::Descr:
return hasFieldNamedPrefix(
descr().as<StructTypeDescr>(), ALL_FIELDS,
id, fieldOffset, fieldType, fieldIndex);
case TypedObjectPrediction::Prefix:
return hasFieldNamedPrefix(
*prefix().descr, prefix().fields,
id, fieldOffset, fieldType, fieldIndex);
}
MOZ_ASSUME_UNREACHABLE("Bad prediction kind");
}

View File

@ -0,0 +1,233 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sts=4 et sw=4 tw=99:
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef jit_TypedObjectPrediction_h
#define jit_TypedObjectPrediction_h
#include "builtin/TypedObject.h"
#include "jit/IonAllocPolicy.h"
namespace js {
namespace jit {
// A TypedObjectPrediction summarizes what we know about the type of a
// typed object at a given point (if anything). The prediction will
// begin as precise as possible and degrade to less precise as more
// typed object types are merged using |addProto()|.
//
// - Precise type descriptor: the precise TypeDescr is known, which gives
// us all possible information, including precise dimensons in the case
// of an array.
// - Proto: precise TypedProto is known. This is almost as precise as the
// type descriptor, but does not include array dimensions.
// - Prefix: the type is known to be a struct and we can track a prefix
// of its fields. This doesn't tell us how big the struct is etc but
// can give us fast access to those fields we know about. Useful when
// modeling inheritance.
// - Empty/Inconsistent: nothing useful is known.
//
// To create a TypedObjectPrediction from TI, one initially creates an
// empty prediction using the |TypedObjectPrediction()| constructor,
// and then invokes |addProto()| with the prototype of each typed
// object. The prediction will automatically downgrade to less and
// less specific settings as needed. Note that creating a prediction
// in this way can never yield precise array dimensions, since TI only
// tracks the prototype.
//
// TypedObjectPredictions can also result from other predictions using
// the query methods (e.g., |arrayElementType()|). In those cases, the
// precise array dimensions may be known.
//
// To query a prediction, you must first check whether it is "useless"
// using |isUseless()|. If this is true, there is no usable
// information to be extracted. Otherwise, you can inquire after the
// |kind()| of the data (struct, array, etc) and from there make more
// specific queries.
class TypedObjectPrediction {
public:
enum PredictionKind {
// No data.
Empty,
// Inconsistent data.
Inconsistent,
// Multiple different struct types flow into the same location,
// but they share fields in common. Prefix indicates that the first
// N fields of some struct type are known to be valid. This occurs
// in a subtyping scenario.
Prefix,
// The TypedProto of the value is known. This is generally
// less precise than the type descriptor because typed protos
// do not track array bounds.
Proto,
// The TypeDescr of the value is known. This is the most specific
// possible value and includes precise array bounds. Generally
// this only happens if we access the field of a struct.
Descr
};
struct PrefixData {
const StructTypeDescr *descr;
size_t fields;
};
union Data {
const TypedProto *proto;
const TypeDescr *descr;
PrefixData prefix;
};
private:
PredictionKind kind_;
Data data_;
PredictionKind predictionKind() const {
return kind_;
}
void markInconsistent() {
kind_ = Inconsistent;
}
const TypedProto &proto() const {
JS_ASSERT(predictionKind() == Proto);
return *data_.proto;
}
const TypeDescr &descr() const {
JS_ASSERT(predictionKind() == Descr);
return *data_.descr;
}
const PrefixData &prefix() const {
JS_ASSERT(predictionKind() == Prefix);
return data_.prefix;
}
void setProto(const TypedProto &proto) {
kind_ = Proto;
data_.proto = &proto;
}
void setDescr(const TypeDescr &descr) {
kind_ = Descr;
data_.descr = &descr;
}
void setPrefix(const StructTypeDescr &descr, size_t fields) {
kind_ = Prefix;
data_.prefix.descr = &descr;
data_.prefix.fields = fields;
}
void markAsCommonPrefix(const StructTypeDescr &descrA,
const StructTypeDescr &descrB,
size_t max);
template<typename T>
typename T::Type extractType() const;
bool hasFieldNamedPrefix(const StructTypeDescr &descr,
size_t fieldCount,
jsid id,
int32_t *offset,
TypedObjectPrediction *out,
size_t *index) const;
public:
///////////////////////////////////////////////////////////////////////////
// Constructing a prediction. Generally, you start with an empty
// prediction and invoke addProto() repeatedly.
TypedObjectPrediction() {
kind_ = Empty;
}
TypedObjectPrediction(const TypedProto &proto) {
setProto(proto);
}
TypedObjectPrediction(const TypeDescr &descr) {
setDescr(descr);
}
TypedObjectPrediction(const StructTypeDescr &descr, size_t fields) {
setPrefix(descr, fields);
}
void addProto(const TypedProto &proto);
///////////////////////////////////////////////////////////////////////////
// Queries that are always valid.
bool isUseless() const {
return predictionKind() == Empty || predictionKind() == Inconsistent;
}
// Determines whether we can predict the prototype for the typed
// object instance. Returns null if we cannot or if the typed
// object is of scalar/reference kind, in which case instances are
// not objects and hence do not have a (publicly available)
// prototype.
const TypedProto *getKnownPrototype() const;
///////////////////////////////////////////////////////////////////////////
// Queries that are valid if not useless.
type::Kind kind() const;
bool ofArrayKind() const;
// Returns true if the size of this typed object is statically
// known and sets |*out| to that size. Otherwise returns false.
//
// The size may not be statically known if (1) the object is
// an array whose dimensions are unknown or (2) only a prefix
// of its type is known.
bool hasKnownSize(int32_t *out) const;
//////////////////////////////////////////////////////////////////////
// Simple operations
//
// Only valid when |kind()| is Scalar, Reference, or x4 (as appropriate).
ScalarTypeDescr::Type scalarType() const;
ReferenceTypeDescr::Type referenceType() const;
X4TypeDescr::Type x4Type() const;
///////////////////////////////////////////////////////////////////////////
// Queries valid only for arrays.
// Returns true if the length of the array is statically known,
// and sets |*length| appropriately. Otherwise returns false.
bool hasKnownArrayLength(int32_t *length) const;
// Returns a prediction for the array element type, if any.
TypedObjectPrediction arrayElementType() const;
//////////////////////////////////////////////////////////////////////
// Struct operations
//
// Only valid when |kind() == TypeDescr::Struct|
// Returns true if the predicted type includes a field named |id|
// and sets |*fieldOffset|, |*fieldType|, and |*fieldIndex| with
// the offset (in bytes), type, and index of the field
// respectively. Otherwise returns false.
bool hasFieldNamed(jsid id,
int32_t *fieldOffset,
TypedObjectPrediction *fieldType,
size_t *fieldIndex) const;
};
} // namespace jit
} // namespace js
#endif

View File

@ -297,7 +297,7 @@ if CONFIG['ENABLE_ION']:
'jit/shared/Lowering-shared.cpp', 'jit/shared/Lowering-shared.cpp',
'jit/Snapshots.cpp', 'jit/Snapshots.cpp',
'jit/StupidAllocator.cpp', 'jit/StupidAllocator.cpp',
'jit/TypeDescrSet.cpp', 'jit/TypedObjectPrediction.cpp',
'jit/TypePolicy.cpp', 'jit/TypePolicy.cpp',
'jit/UnreachableCodeElimination.cpp', 'jit/UnreachableCodeElimination.cpp',
'jit/ValueNumbering.cpp', 'jit/ValueNumbering.cpp',