mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-16 14:55:47 +00:00
Bug 1022356 Part 01 -- Move from TypedProtoSet to TypedObjectPrediction r=shu
This commit is contained in:
parent
19effe56b7
commit
4bf202a49f
@ -168,6 +168,8 @@ class TypedProto : public JSObject
|
||||
TypeDescr &typeDescr() const {
|
||||
return getReservedSlot(JS_TYPROTO_SLOT_DESCR).toObject().as<TypeDescr>();
|
||||
}
|
||||
|
||||
inline type::Kind kind() const;
|
||||
};
|
||||
|
||||
class TypeDescr : public JSObject
|
||||
@ -1055,4 +1057,11 @@ js::TypedProto::initTypeDescrSlot(TypeDescr &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 */
|
||||
|
@ -30,7 +30,7 @@ function foo() {
|
||||
var s;
|
||||
|
||||
for (var i = 0; i < N; i++) {
|
||||
if ((i % 2) == 0 || true)
|
||||
if ((i % 2) == 0)
|
||||
obj = new PointType2({x: i, y: i+1});
|
||||
else
|
||||
obj = new PointType3({x: i, y: i+1, z: i+2});
|
||||
|
@ -117,7 +117,6 @@ IonBuilder::IonBuilder(JSContext *analysisContext, CompileCompartment *comp,
|
||||
backgroundCodegen_(nullptr),
|
||||
analysisContext(analysisContext),
|
||||
baselineFrame_(baselineFrame),
|
||||
descrSetHash_(nullptr),
|
||||
constraints_(constraints),
|
||||
analysis_(*temp, info->script()),
|
||||
thisTypes(nullptr),
|
||||
@ -6750,26 +6749,24 @@ IonBuilder::getElemTryTypedObject(bool *emitted, MDefinition *obj, MDefinition *
|
||||
{
|
||||
JS_ASSERT(*emitted == false);
|
||||
|
||||
TypeDescrSet objDescrs;
|
||||
if (!lookupTypeDescrSet(obj, &objDescrs))
|
||||
return false;
|
||||
|
||||
if (!objDescrs.allOfArrayKind())
|
||||
TypedObjectPrediction objPrediction = typedObjectPrediction(obj);
|
||||
if (objPrediction.isUseless())
|
||||
return true;
|
||||
|
||||
TypeDescrSet elemDescrs;
|
||||
if (!objDescrs.arrayElementType(*this, &elemDescrs))
|
||||
return false;
|
||||
if (elemDescrs.empty())
|
||||
if (!objPrediction.ofArrayKind())
|
||||
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;
|
||||
if (!elemDescrs.allHaveSameSize(&elemSize))
|
||||
if (!elemPrediction.hasKnownSize(&elemSize))
|
||||
return true;
|
||||
|
||||
switch (elemDescrs.kind()) {
|
||||
switch (elemPrediction.kind()) {
|
||||
case type::X4:
|
||||
// FIXME (bug 894105): load into a MIRType_float32x4 etc
|
||||
return true;
|
||||
@ -6779,15 +6776,15 @@ IonBuilder::getElemTryTypedObject(bool *emitted, MDefinition *obj, MDefinition *
|
||||
return getElemTryComplexElemOfTypedObject(emitted,
|
||||
obj,
|
||||
index,
|
||||
objDescrs,
|
||||
elemDescrs,
|
||||
objPrediction,
|
||||
elemPrediction,
|
||||
elemSize);
|
||||
case type::Scalar:
|
||||
return getElemTryScalarElemOfTypedObject(emitted,
|
||||
obj,
|
||||
index,
|
||||
objDescrs,
|
||||
elemDescrs,
|
||||
objPrediction,
|
||||
elemPrediction,
|
||||
elemSize);
|
||||
|
||||
case type::Reference:
|
||||
@ -6808,7 +6805,7 @@ bool
|
||||
IonBuilder::checkTypedObjectIndexInBounds(int32_t elemSize,
|
||||
MDefinition *obj,
|
||||
MDefinition *index,
|
||||
TypeDescrSet objDescrs,
|
||||
TypedObjectPrediction objPrediction,
|
||||
MDefinition **indexAsByteOffset,
|
||||
bool *canBeNeutered)
|
||||
{
|
||||
@ -6822,7 +6819,7 @@ IonBuilder::checkTypedObjectIndexInBounds(int32_t elemSize,
|
||||
// Value to int32 using truncation.
|
||||
int32_t lenOfAll;
|
||||
MDefinition *length;
|
||||
if (objDescrs.hasKnownArrayLength(&lenOfAll)) {
|
||||
if (objPrediction.hasKnownArrayLength(&lenOfAll)) {
|
||||
length = constantInt(lenOfAll);
|
||||
|
||||
// If we are not loading the length from the object itself,
|
||||
@ -6859,21 +6856,19 @@ bool
|
||||
IonBuilder::getElemTryScalarElemOfTypedObject(bool *emitted,
|
||||
MDefinition *obj,
|
||||
MDefinition *index,
|
||||
TypeDescrSet objDescrs,
|
||||
TypeDescrSet elemDescrs,
|
||||
TypedObjectPrediction objPrediction,
|
||||
TypedObjectPrediction elemPrediction,
|
||||
int32_t elemSize)
|
||||
{
|
||||
JS_ASSERT(objDescrs.allOfArrayKind());
|
||||
JS_ASSERT(objPrediction.ofArrayKind());
|
||||
|
||||
// Must always be loading the same scalar type
|
||||
ScalarTypeDescr::Type elemType;
|
||||
if (!elemDescrs.scalarType(&elemType))
|
||||
return true;
|
||||
ScalarTypeDescr::Type elemType = elemPrediction.scalarType();
|
||||
JS_ASSERT(elemSize == ScalarTypeDescr::alignment(elemType));
|
||||
|
||||
bool canBeNeutered;
|
||||
MDefinition *indexAsByteOffset;
|
||||
if (!checkTypedObjectIndexInBounds(elemSize, obj, index, objDescrs,
|
||||
if (!checkTypedObjectIndexInBounds(elemSize, obj, index, objPrediction,
|
||||
&indexAsByteOffset, &canBeNeutered))
|
||||
{
|
||||
return false;
|
||||
@ -6927,32 +6922,32 @@ bool
|
||||
IonBuilder::getElemTryComplexElemOfTypedObject(bool *emitted,
|
||||
MDefinition *obj,
|
||||
MDefinition *index,
|
||||
TypeDescrSet objDescrs,
|
||||
TypeDescrSet elemDescrs,
|
||||
TypedObjectPrediction objPrediction,
|
||||
TypedObjectPrediction elemPrediction,
|
||||
int32_t elemSize)
|
||||
{
|
||||
JS_ASSERT(objDescrs.allOfArrayKind());
|
||||
JS_ASSERT(objPrediction.ofArrayKind());
|
||||
|
||||
MDefinition *type = loadTypedObjectType(obj);
|
||||
MDefinition *elemTypeObj = typeObjectForElementFromArrayStructType(type);
|
||||
|
||||
bool canBeNeutered;
|
||||
MDefinition *indexAsByteOffset;
|
||||
if (!checkTypedObjectIndexInBounds(elemSize, obj, index, objDescrs,
|
||||
if (!checkTypedObjectIndexInBounds(elemSize, obj, index, objPrediction,
|
||||
&indexAsByteOffset, &canBeNeutered))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return pushDerivedTypedObject(emitted, obj, indexAsByteOffset,
|
||||
elemDescrs, elemTypeObj, canBeNeutered);
|
||||
elemPrediction, elemTypeObj, canBeNeutered);
|
||||
}
|
||||
|
||||
bool
|
||||
IonBuilder::pushDerivedTypedObject(bool *emitted,
|
||||
MDefinition *obj,
|
||||
MDefinition *offset,
|
||||
TypeDescrSet derivedTypeDescrs,
|
||||
TypedObjectPrediction derivedPrediction,
|
||||
MDefinition *derivedTypeObj,
|
||||
bool canBeNeutered)
|
||||
{
|
||||
@ -6962,7 +6957,7 @@ IonBuilder::pushDerivedTypedObject(bool *emitted,
|
||||
|
||||
// Create the derived typed object.
|
||||
MInstruction *derivedTypedObj = MNewDerivedTypedObject::New(alloc(),
|
||||
derivedTypeDescrs,
|
||||
derivedPrediction,
|
||||
derivedTypeObj,
|
||||
owner,
|
||||
ownerOffset);
|
||||
@ -6976,7 +6971,7 @@ IonBuilder::pushDerivedTypedObject(bool *emitted,
|
||||
// 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();
|
||||
const TypedProto *expectedProto = derivedPrediction.getKnownPrototype();
|
||||
JS_ASSERT_IF(expectedClass, IsTypedObjectClass(expectedClass));
|
||||
|
||||
// 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);
|
||||
|
||||
TypeDescrSet objTypeDescrs;
|
||||
if (!lookupTypeDescrSet(obj, &objTypeDescrs))
|
||||
return false;
|
||||
|
||||
if (!objTypeDescrs.allOfArrayKind())
|
||||
TypedObjectPrediction objPrediction = typedObjectPrediction(obj);
|
||||
if (objPrediction.isUseless())
|
||||
return true;
|
||||
|
||||
TypeDescrSet elemTypeDescrs;
|
||||
if (!objTypeDescrs.arrayElementType(*this, &elemTypeDescrs))
|
||||
return false;
|
||||
if (elemTypeDescrs.empty())
|
||||
if (!objPrediction.ofArrayKind())
|
||||
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;
|
||||
if (!elemTypeDescrs.allHaveSameSize(&elemSize))
|
||||
if (!elemPrediction.hasKnownSize(&elemSize))
|
||||
return true;
|
||||
|
||||
switch (elemTypeDescrs.kind()) {
|
||||
switch (elemPrediction.kind()) {
|
||||
case type::X4:
|
||||
// FIXME (bug 894105): store a MIRType_float32x4 etc
|
||||
return true;
|
||||
@ -7698,9 +7691,9 @@ IonBuilder::setElemTryTypedObject(bool *emitted, MDefinition *obj,
|
||||
return setElemTryScalarElemOfTypedObject(emitted,
|
||||
obj,
|
||||
index,
|
||||
objTypeDescrs,
|
||||
objPrediction,
|
||||
value,
|
||||
elemTypeDescrs,
|
||||
elemPrediction,
|
||||
elemSize);
|
||||
}
|
||||
|
||||
@ -7711,20 +7704,18 @@ bool
|
||||
IonBuilder::setElemTryScalarElemOfTypedObject(bool *emitted,
|
||||
MDefinition *obj,
|
||||
MDefinition *index,
|
||||
TypeDescrSet objTypeDescrs,
|
||||
TypedObjectPrediction objPrediction,
|
||||
MDefinition *value,
|
||||
TypeDescrSet elemTypeDescrs,
|
||||
TypedObjectPrediction elemPrediction,
|
||||
int32_t elemSize)
|
||||
{
|
||||
// Must always be loading the same scalar type
|
||||
ScalarTypeDescr::Type elemType;
|
||||
if (!elemTypeDescrs.scalarType(&elemType))
|
||||
return true;
|
||||
ScalarTypeDescr::Type elemType = elemPrediction.scalarType();
|
||||
JS_ASSERT(elemSize == ScalarTypeDescr::alignment(elemType));
|
||||
|
||||
bool canBeNeutered;
|
||||
MDefinition *indexAsByteOffset;
|
||||
if (!checkTypedObjectIndexInBounds(elemSize, obj, index, objTypeDescrs,
|
||||
if (!checkTypedObjectIndexInBounds(elemSize, obj, index, objPrediction,
|
||||
&indexAsByteOffset, &canBeNeutered))
|
||||
{
|
||||
return false;
|
||||
@ -8696,18 +8687,18 @@ IonBuilder::getPropTryConstant(bool *emitted, MDefinition *obj, PropertyName *na
|
||||
}
|
||||
|
||||
bool
|
||||
IonBuilder::getPropTryTypedObject(bool *emitted, MDefinition *obj, PropertyName *name,
|
||||
IonBuilder::getPropTryTypedObject(bool *emitted,
|
||||
MDefinition *obj,
|
||||
PropertyName *name,
|
||||
types::TemporaryTypeSet *resultTypes)
|
||||
{
|
||||
TypeDescrSet fieldDescrs;
|
||||
TypedObjectPrediction fieldPrediction;
|
||||
int32_t fieldOffset;
|
||||
size_t fieldIndex;
|
||||
if (!lookupTypedObjectField(obj, name, &fieldOffset, &fieldDescrs, &fieldIndex))
|
||||
return false;
|
||||
if (fieldDescrs.empty())
|
||||
if (!typedObjectHasField(obj, name, &fieldOffset, &fieldPrediction, &fieldIndex))
|
||||
return true;
|
||||
|
||||
switch (fieldDescrs.kind()) {
|
||||
switch (fieldPrediction.kind()) {
|
||||
case type::Reference:
|
||||
return true;
|
||||
|
||||
@ -8720,7 +8711,7 @@ IonBuilder::getPropTryTypedObject(bool *emitted, MDefinition *obj, PropertyName
|
||||
return getPropTryComplexPropOfTypedObject(emitted,
|
||||
obj,
|
||||
fieldOffset,
|
||||
fieldDescrs,
|
||||
fieldPrediction,
|
||||
fieldIndex,
|
||||
resultTypes);
|
||||
|
||||
@ -8728,7 +8719,7 @@ IonBuilder::getPropTryTypedObject(bool *emitted, MDefinition *obj, PropertyName
|
||||
return getPropTryScalarPropOfTypedObject(emitted,
|
||||
obj,
|
||||
fieldOffset,
|
||||
fieldDescrs,
|
||||
fieldPrediction,
|
||||
resultTypes);
|
||||
|
||||
case type::UnsizedArray:
|
||||
@ -8741,13 +8732,11 @@ IonBuilder::getPropTryTypedObject(bool *emitted, MDefinition *obj, PropertyName
|
||||
bool
|
||||
IonBuilder::getPropTryScalarPropOfTypedObject(bool *emitted, MDefinition *typedObj,
|
||||
int32_t fieldOffset,
|
||||
TypeDescrSet fieldDescrs,
|
||||
TypedObjectPrediction fieldPrediction,
|
||||
types::TemporaryTypeSet *resultTypes)
|
||||
{
|
||||
// Must always be loading the same scalar type.
|
||||
ScalarTypeDescr::Type fieldType;
|
||||
if (!fieldDescrs.scalarType(&fieldType))
|
||||
return true;
|
||||
// Must always be loading the same scalar type
|
||||
ScalarTypeDescr::Type fieldType = fieldPrediction.scalarType();
|
||||
|
||||
// OK, perform the optimization.
|
||||
return pushScalarLoadFromTypedObject(emitted, typedObj, constantInt(fieldOffset),
|
||||
@ -8755,17 +8744,13 @@ IonBuilder::getPropTryScalarPropOfTypedObject(bool *emitted, MDefinition *typedO
|
||||
}
|
||||
|
||||
bool
|
||||
IonBuilder::getPropTryComplexPropOfTypedObject(bool *emitted, MDefinition *typedObj,
|
||||
IonBuilder::getPropTryComplexPropOfTypedObject(bool *emitted,
|
||||
MDefinition *typedObj,
|
||||
int32_t fieldOffset,
|
||||
TypeDescrSet fieldDescrs,
|
||||
TypedObjectPrediction fieldPrediction,
|
||||
size_t fieldIndex,
|
||||
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
|
||||
|
||||
// Identify the type object for the field.
|
||||
@ -8773,7 +8758,7 @@ IonBuilder::getPropTryComplexPropOfTypedObject(bool *emitted, MDefinition *typed
|
||||
MDefinition *fieldTypeObj = typeObjectForFieldFromStructType(type, fieldIndex);
|
||||
|
||||
return pushDerivedTypedObject(emitted, typedObj, constantInt(fieldOffset),
|
||||
fieldDescrs, fieldTypeObj, true);
|
||||
fieldPrediction, fieldTypeObj, true);
|
||||
}
|
||||
|
||||
bool
|
||||
@ -9352,16 +9337,13 @@ bool
|
||||
IonBuilder::setPropTryTypedObject(bool *emitted, MDefinition *obj,
|
||||
PropertyName *name, MDefinition *value)
|
||||
{
|
||||
TypeDescrSet fieldDescrs;
|
||||
TypedObjectPrediction fieldPrediction;
|
||||
int32_t fieldOffset;
|
||||
size_t fieldIndex;
|
||||
if (!lookupTypedObjectField(obj, name, &fieldOffset, &fieldDescrs,
|
||||
&fieldIndex))
|
||||
return false;
|
||||
if (fieldDescrs.empty())
|
||||
if (!typedObjectHasField(obj, name, &fieldOffset, &fieldPrediction, &fieldIndex))
|
||||
return true;
|
||||
|
||||
switch (fieldDescrs.kind()) {
|
||||
switch (fieldPrediction.kind()) {
|
||||
case type::X4:
|
||||
// FIXME (bug 894104): store into a MIRType_float32x4 etc
|
||||
return true;
|
||||
@ -9375,7 +9357,7 @@ IonBuilder::setPropTryTypedObject(bool *emitted, MDefinition *obj,
|
||||
|
||||
case type::Scalar:
|
||||
return setPropTryScalarPropOfTypedObject(emitted, obj, fieldOffset,
|
||||
value, fieldDescrs);
|
||||
value, fieldPrediction);
|
||||
}
|
||||
|
||||
MOZ_ASSUME_UNREACHABLE("Unknown kind");
|
||||
@ -9386,12 +9368,10 @@ IonBuilder::setPropTryScalarPropOfTypedObject(bool *emitted,
|
||||
MDefinition *obj,
|
||||
int32_t fieldOffset,
|
||||
MDefinition *value,
|
||||
TypeDescrSet fieldDescrs)
|
||||
TypedObjectPrediction fieldPrediction)
|
||||
{
|
||||
// Must always be loading the same scalar type
|
||||
ScalarTypeDescr::Type fieldType;
|
||||
if (!fieldDescrs.scalarType(&fieldType))
|
||||
return true;
|
||||
ScalarTypeDescr::Type fieldType = fieldPrediction.scalarType();
|
||||
|
||||
// OK! Perform the optimization.
|
||||
|
||||
@ -10220,56 +10200,37 @@ IonBuilder::bytecodeTypes(jsbytecode *pc)
|
||||
return types::TypeScript::BytecodeTypes(script(), pc, bytecodeTypeMap, &typeArrayHint, typeArray);
|
||||
}
|
||||
|
||||
TypeDescrSetHash *
|
||||
IonBuilder::getOrCreateDescrSetHash()
|
||||
TypedObjectPrediction
|
||||
IonBuilder::typedObjectPrediction(MDefinition *typedObj)
|
||||
{
|
||||
if (!descrSetHash_) {
|
||||
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
|
||||
// Extract TypedObjectPrediction directly if we can
|
||||
if (typedObj->isNewDerivedTypedObject()) {
|
||||
*out = typedObj->toNewDerivedTypedObject()->set();
|
||||
return true;
|
||||
return typedObj->toNewDerivedTypedObject()->prediction();
|
||||
}
|
||||
|
||||
types::TemporaryTypeSet *types = typedObj->resultTypeSet();
|
||||
return typeSetToTypeDescrSet(types, out);
|
||||
return typedObjectPrediction(types);
|
||||
}
|
||||
|
||||
bool
|
||||
IonBuilder::typeSetToTypeDescrSet(types::TemporaryTypeSet *types,
|
||||
TypeDescrSet *out)
|
||||
TypedObjectPrediction
|
||||
IonBuilder::typedObjectPrediction(types::TemporaryTypeSet *types)
|
||||
{
|
||||
// Extract TypeDescrSet directly if we can
|
||||
// Type set must be known to be an object.
|
||||
if (!types || types->getKnownMIRType() != MIRType_Object)
|
||||
return true;
|
||||
return TypedObjectPrediction();
|
||||
|
||||
// And only known objects.
|
||||
if (types->unknownObject())
|
||||
return true;
|
||||
return TypedObjectPrediction();
|
||||
|
||||
TypeDescrSetBuilder set;
|
||||
TypedObjectPrediction out;
|
||||
for (uint32_t i = 0; i < types->getObjectCount(); i++) {
|
||||
types::TypeObject *type = types->getTypeObject(i);
|
||||
if (!type)
|
||||
return true;
|
||||
if (!type || type->unknownProperties())
|
||||
return TypedObjectPrediction();
|
||||
|
||||
if (!IsTypedObjectClass(type->clasp()))
|
||||
return true;
|
||||
return TypedObjectPrediction();
|
||||
|
||||
TaggedProto proto = type->proto();
|
||||
|
||||
@ -10278,11 +10239,10 @@ IonBuilder::typeSetToTypeDescrSet(types::TemporaryTypeSet *types,
|
||||
JS_ASSERT(proto.isObject() && proto.toObject()->is<TypedProto>());
|
||||
|
||||
TypedProto &typedProto = proto.toObject()->as<TypedProto>();
|
||||
if (!set.insert(&typedProto.typeDescr()))
|
||||
return false;
|
||||
out.addProto(typedProto);
|
||||
}
|
||||
|
||||
return set.build(*this, out);
|
||||
return out;
|
||||
}
|
||||
|
||||
MDefinition *
|
||||
@ -10388,36 +10348,27 @@ IonBuilder::loadTypedObjectElements(MDefinition *typedObj,
|
||||
}
|
||||
|
||||
// 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
|
||||
// returned, `*fieldDescrs` might be empty if no useful type/offset
|
||||
// pair could be determined.
|
||||
// set `objTypes` of the field owner. If a field is found, returns true
|
||||
// and sets *fieldOffset, *fieldPrediction, and *fieldIndex. Returns false
|
||||
// otherwise. Infallible.
|
||||
bool
|
||||
IonBuilder::lookupTypedObjectField(MDefinition *typedObj,
|
||||
PropertyName *name,
|
||||
int32_t *fieldOffset,
|
||||
TypeDescrSet *fieldDescrs,
|
||||
size_t *fieldIndex)
|
||||
IonBuilder::typedObjectHasField(MDefinition *typedObj,
|
||||
PropertyName *name,
|
||||
int32_t *fieldOffset,
|
||||
TypedObjectPrediction *fieldPrediction,
|
||||
size_t *fieldIndex)
|
||||
{
|
||||
TypeDescrSet objDescrs;
|
||||
if (!lookupTypeDescrSet(typedObj, &objDescrs))
|
||||
TypedObjectPrediction objPrediction = typedObjectPrediction(typedObj);
|
||||
if (objPrediction.isUseless())
|
||||
return false;
|
||||
|
||||
// Must be accessing a struct.
|
||||
if (!objDescrs.allOfKind(type::Struct))
|
||||
return true;
|
||||
if (objPrediction.kind() != type::Struct)
|
||||
return false;
|
||||
|
||||
// Determine the type/offset of the field `name`, if any.
|
||||
int32_t offset;
|
||||
if (!objDescrs.fieldNamed(*this, NameToId(name), &offset,
|
||||
fieldDescrs, fieldIndex))
|
||||
return false;
|
||||
if (fieldDescrs->empty())
|
||||
return true;
|
||||
|
||||
JS_ASSERT(offset >= 0);
|
||||
*fieldOffset = offset;
|
||||
|
||||
return true;
|
||||
return objPrediction.hasFieldNamed(NameToId(name), fieldOffset,
|
||||
fieldPrediction, fieldIndex);
|
||||
}
|
||||
|
||||
MDefinition *
|
||||
|
@ -17,7 +17,6 @@
|
||||
#include "jit/MIR.h"
|
||||
#include "jit/MIRGenerator.h"
|
||||
#include "jit/MIRGraph.h"
|
||||
#include "jit/TypeDescrSet.h"
|
||||
|
||||
namespace js {
|
||||
namespace jit {
|
||||
@ -413,11 +412,11 @@ class IonBuilder : public MIRGenerator
|
||||
types::TemporaryTypeSet *resultTypes);
|
||||
bool getPropTryScalarPropOfTypedObject(bool *emitted, MDefinition *typedObj,
|
||||
int32_t fieldOffset,
|
||||
TypeDescrSet fieldTypeReprs,
|
||||
TypedObjectPrediction fieldTypeReprs,
|
||||
types::TemporaryTypeSet *resultTypes);
|
||||
bool getPropTryComplexPropOfTypedObject(bool *emitted, MDefinition *typedObj,
|
||||
int32_t fieldOffset,
|
||||
TypeDescrSet fieldTypeReprs,
|
||||
TypedObjectPrediction fieldTypeReprs,
|
||||
size_t fieldIndex,
|
||||
types::TemporaryTypeSet *resultTypes);
|
||||
bool getPropTryInnerize(bool *emitted, MDefinition *obj, PropertyName *name,
|
||||
@ -444,21 +443,19 @@ class IonBuilder : public MIRGenerator
|
||||
MDefinition *obj,
|
||||
int32_t fieldOffset,
|
||||
MDefinition *value,
|
||||
TypeDescrSet fieldTypeReprs);
|
||||
TypedObjectPrediction fieldTypeReprs);
|
||||
bool setPropTryCache(bool *emitted, MDefinition *obj,
|
||||
PropertyName *name, MDefinition *value,
|
||||
bool barrier, types::TemporaryTypeSet *objTypes);
|
||||
|
||||
// binary data lookup helpers.
|
||||
bool lookupTypeDescrSet(MDefinition *typedObj,
|
||||
TypeDescrSet *out);
|
||||
bool typeSetToTypeDescrSet(types::TemporaryTypeSet *types,
|
||||
TypeDescrSet *out);
|
||||
bool lookupTypedObjectField(MDefinition *typedObj,
|
||||
PropertyName *name,
|
||||
int32_t *fieldOffset,
|
||||
TypeDescrSet *fieldTypeReprs,
|
||||
size_t *fieldIndex);
|
||||
TypedObjectPrediction typedObjectPrediction(MDefinition *typedObj);
|
||||
TypedObjectPrediction typedObjectPrediction(types::TemporaryTypeSet *types);
|
||||
bool typedObjectHasField(MDefinition *typedObj,
|
||||
PropertyName *name,
|
||||
int32_t *fieldOffset,
|
||||
TypedObjectPrediction *fieldTypeReprs,
|
||||
size_t *fieldIndex);
|
||||
MDefinition *loadTypedObjectType(MDefinition *value);
|
||||
void loadTypedObjectData(MDefinition *typedObj,
|
||||
MDefinition *offset,
|
||||
@ -483,13 +480,13 @@ class IonBuilder : public MIRGenerator
|
||||
bool checkTypedObjectIndexInBounds(int32_t elemSize,
|
||||
MDefinition *obj,
|
||||
MDefinition *index,
|
||||
TypeDescrSet objTypeDescrs,
|
||||
TypedObjectPrediction objTypeDescrs,
|
||||
MDefinition **indexAsByteOffset,
|
||||
bool *canBeNeutered);
|
||||
bool pushDerivedTypedObject(bool *emitted,
|
||||
MDefinition *obj,
|
||||
MDefinition *offset,
|
||||
TypeDescrSet derivedTypeDescrs,
|
||||
TypedObjectPrediction derivedTypeDescrs,
|
||||
MDefinition *derivedTypeObj,
|
||||
bool canBeNeutered);
|
||||
bool pushScalarLoadFromTypedObject(bool *emitted,
|
||||
@ -515,9 +512,9 @@ class IonBuilder : public MIRGenerator
|
||||
bool setElemTryScalarElemOfTypedObject(bool *emitted,
|
||||
MDefinition *obj,
|
||||
MDefinition *index,
|
||||
TypeDescrSet objTypeReprs,
|
||||
TypedObjectPrediction objTypeReprs,
|
||||
MDefinition *value,
|
||||
TypeDescrSet elemTypeReprs,
|
||||
TypedObjectPrediction elemTypeReprs,
|
||||
int32_t elemSize);
|
||||
|
||||
// jsop_getelem() helpers.
|
||||
@ -532,14 +529,14 @@ class IonBuilder : public MIRGenerator
|
||||
bool getElemTryScalarElemOfTypedObject(bool *emitted,
|
||||
MDefinition *obj,
|
||||
MDefinition *index,
|
||||
TypeDescrSet objTypeReprs,
|
||||
TypeDescrSet elemTypeReprs,
|
||||
TypedObjectPrediction objTypeReprs,
|
||||
TypedObjectPrediction elemTypeReprs,
|
||||
int32_t elemSize);
|
||||
bool getElemTryComplexElemOfTypedObject(bool *emitted,
|
||||
MDefinition *obj,
|
||||
MDefinition *index,
|
||||
TypeDescrSet objTypeReprs,
|
||||
TypeDescrSet elemTypeReprs,
|
||||
TypedObjectPrediction objTypeReprs,
|
||||
TypedObjectPrediction elemTypeReprs,
|
||||
int32_t elemSize);
|
||||
|
||||
enum BoundsChecking { DoBoundsCheck, SkipBoundsCheck };
|
||||
@ -839,8 +836,6 @@ class IonBuilder : public MIRGenerator
|
||||
CodeGenerator *backgroundCodegen() const { return backgroundCodegen_; }
|
||||
void setBackgroundCodegen(CodeGenerator *codegen) { backgroundCodegen_ = codegen; }
|
||||
|
||||
TypeDescrSetHash *getOrCreateDescrSetHash(); // fallible
|
||||
|
||||
types::CompilerConstraintList *constraints() {
|
||||
return constraints_;
|
||||
}
|
||||
@ -856,7 +851,6 @@ class IonBuilder : public MIRGenerator
|
||||
|
||||
JSContext *analysisContext;
|
||||
BaselineFrameInspector *baselineFrame_;
|
||||
TypeDescrSetHash *descrSetHash_;
|
||||
|
||||
// Constraints for recording dependencies on type information.
|
||||
types::CompilerConstraintList *constraints_;
|
||||
|
@ -1449,23 +1449,17 @@ IonBuilder::elementAccessIsTypedObjectArrayOfScalarType(MDefinition* obj, MDefin
|
||||
if (id->type() != MIRType_Int32 && id->type() != MIRType_Double)
|
||||
return false;
|
||||
|
||||
TypeDescrSet objDescrs;
|
||||
if (!lookupTypeDescrSet(obj, &objDescrs))
|
||||
TypedObjectPrediction prediction = typedObjectPrediction(obj);
|
||||
if (prediction.isUseless() || !prediction.ofArrayKind())
|
||||
return false;
|
||||
|
||||
if (!objDescrs.allOfArrayKind())
|
||||
TypedObjectPrediction elemPrediction = prediction.arrayElementType();
|
||||
if (elemPrediction.isUseless() || elemPrediction.kind() != type::Scalar)
|
||||
return false;
|
||||
|
||||
TypeDescrSet elemDescrs;
|
||||
if (!objDescrs.arrayElementType(*this, &elemDescrs))
|
||||
return false;
|
||||
|
||||
if (elemDescrs.empty() || elemDescrs.kind() != type::Scalar)
|
||||
return false;
|
||||
|
||||
JS_ASSERT(TypeDescr::isSized(elemDescrs.kind()));
|
||||
|
||||
return elemDescrs.scalarType(arrayType);
|
||||
JS_ASSERT(type::isSized(elemPrediction.kind()));
|
||||
*arrayType = elemPrediction.scalarType();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -20,7 +20,7 @@
|
||||
#include "jit/IonAllocPolicy.h"
|
||||
#include "jit/IonMacroAssembler.h"
|
||||
#include "jit/MOpcodes.h"
|
||||
#include "jit/TypeDescrSet.h"
|
||||
#include "jit/TypedObjectPrediction.h"
|
||||
#include "jit/TypePolicy.h"
|
||||
#include "vm/ScopeObject.h"
|
||||
#include "vm/TypedArrayObject.h"
|
||||
@ -1769,14 +1769,14 @@ class MNewDerivedTypedObject
|
||||
IntPolicy<2> >
|
||||
{
|
||||
private:
|
||||
TypeDescrSet set_;
|
||||
TypedObjectPrediction prediction_;
|
||||
|
||||
MNewDerivedTypedObject(TypeDescrSet set,
|
||||
MNewDerivedTypedObject(TypedObjectPrediction prediction,
|
||||
MDefinition *type,
|
||||
MDefinition *owner,
|
||||
MDefinition *offset)
|
||||
: MTernaryInstruction(type, owner, offset),
|
||||
set_(set)
|
||||
prediction_(prediction)
|
||||
{
|
||||
setMovable();
|
||||
setResultType(MIRType_Object);
|
||||
@ -1785,14 +1785,14 @@ class MNewDerivedTypedObject
|
||||
public:
|
||||
INSTRUCTION_HEADER(NewDerivedTypedObject);
|
||||
|
||||
static MNewDerivedTypedObject *New(TempAllocator &alloc, TypeDescrSet set,
|
||||
static MNewDerivedTypedObject *New(TempAllocator &alloc, TypedObjectPrediction prediction,
|
||||
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 {
|
||||
return set_;
|
||||
TypedObjectPrediction prediction() const {
|
||||
return prediction_;
|
||||
}
|
||||
|
||||
MDefinition *type() const {
|
||||
|
@ -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);
|
||||
}
|
@ -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
|
379
js/src/jit/TypedObjectPrediction.cpp
Normal file
379
js/src/jit/TypedObjectPrediction.cpp
Normal 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 ¤tDescr = 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");
|
||||
}
|
233
js/src/jit/TypedObjectPrediction.h
Normal file
233
js/src/jit/TypedObjectPrediction.h
Normal 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
|
@ -297,7 +297,7 @@ if CONFIG['ENABLE_ION']:
|
||||
'jit/shared/Lowering-shared.cpp',
|
||||
'jit/Snapshots.cpp',
|
||||
'jit/StupidAllocator.cpp',
|
||||
'jit/TypeDescrSet.cpp',
|
||||
'jit/TypedObjectPrediction.cpp',
|
||||
'jit/TypePolicy.cpp',
|
||||
'jit/UnreachableCodeElimination.cpp',
|
||||
'jit/ValueNumbering.cpp',
|
||||
|
Loading…
Reference in New Issue
Block a user