Bug 903394 - IonMonkey: Refactor IonBuilder jsop_getelem, r=jandem

This commit is contained in:
Hannes Verschore 2013-08-13 22:57:19 +02:00
Родитель 11280c4dfe
Коммит 6dba40cf21
2 изменённых файлов: 289 добавлений и 193 удалений

Просмотреть файл

@ -6532,42 +6532,277 @@ GetElemKnownType(bool needsHoleCheck, types::StackTypeSet *types)
bool
IonBuilder::jsop_getelem()
{
MDefinition *obj = current->peek(-2);
MDefinition *index = current->peek(-1);
MDefinition *index = current->pop();
MDefinition *obj = current->pop();
if (ElementAccessIsDenseNative(obj, index)) {
// Don't generate a fast path if there have been bounds check failures
// and this access might be on a sparse property.
if (!ElementAccessHasExtraIndexedProperty(cx, obj) || !failedBoundsCheck_)
return jsop_getelem_dense();
}
bool emitted = false;
int arrayType = TypedArrayObject::TYPE_MAX;
if (ElementAccessIsTypedArray(obj, index, &arrayType))
return jsop_getelem_typed(arrayType);
if (!getElemTryDense(&emitted, obj, index) || emitted)
return emitted;
if (obj->type() == MIRType_String)
return jsop_getelem_string();
if (!getElemTryTypedStatic(&emitted, obj, index) || emitted)
return emitted;
if (obj->type() == MIRType_Magic)
return jsop_arguments_getelem();
if (!getElemTryTyped(&emitted, obj, index) || emitted)
return emitted;
if (!getElemTryString(&emitted, obj, index) || emitted)
return emitted;
if (!getElemTryArguments(&emitted, obj, index) || emitted)
return emitted;
if (!getElemTryArgumentsInlined(&emitted, obj, index) || emitted)
return emitted;
if (script()->argumentsHasVarBinding() && obj->mightBeType(MIRType_Magic))
return abort("Type is not definitely lazy arguments.");
current->popn(2);
if (!getElemTryCache(&emitted, obj, index) || emitted)
return emitted;
MInstruction *ins;
// Emit call.
MInstruction *ins = MCallGetElement::New(obj, index);
bool cacheable = obj->mightBeType(MIRType_Object) && !obj->mightBeType(MIRType_String) &&
(index->mightBeType(MIRType_Int32) || index->mightBeType(MIRType_String));
current->add(ins);
current->push(ins);
bool nonNativeGetElement = inspector->hasSeenNonNativeGetElement(pc);
if (!resumeAfter(ins))
return false;
types::StackTypeSet *types = types::TypeScript::BytecodeTypes(script(), pc);
return pushTypeBarrier(ins, types, true);
}
bool
IonBuilder::getElemTryDense(bool *emitted, MDefinition *obj, MDefinition *index)
{
JS_ASSERT(*emitted == false);
if (!ElementAccessIsDenseNative(obj, index))
return true;
// Don't generate a fast path if there have been bounds check failures
// and this access might be on a sparse property.
if (ElementAccessHasExtraIndexedProperty(cx, obj) && failedBoundsCheck_)
return true;
// Emit dense getelem variant.
if (!jsop_getelem_dense(obj, index))
return false;
*emitted = true;
return true;
}
bool
IonBuilder::getElemTryTypedStatic(bool *emitted, MDefinition *obj, MDefinition *index)
{
JS_ASSERT(*emitted == false);
int arrayType = TypedArrayObject::TYPE_MAX;
if (!ElementAccessIsTypedArray(obj, index, &arrayType))
return true;
if (!LIRGenerator::allowStaticTypedArrayAccesses())
return true;
if (ElementAccessHasExtraIndexedProperty(cx, obj))
return true;
if (!obj->resultTypeSet())
return true;
JSObject *tarrObj = obj->resultTypeSet()->getSingleton();
if (!tarrObj)
return true;
TypedArrayObject *tarr = &tarrObj->as<TypedArrayObject>();
ArrayBufferView::ViewType viewType = JS_GetArrayBufferViewType(tarr);
// LoadTypedArrayElementStatic currently treats uint32 arrays as int32.
if (viewType == ArrayBufferView::TYPE_UINT32)
return true;
MDefinition *ptr = convertShiftToMaskForStaticTypedArray(index, viewType);
if (!ptr)
return true;
// Emit LoadTypedArrayElementStatic.
obj->setFoldedUnchecked();
MLoadTypedArrayElementStatic *load = MLoadTypedArrayElementStatic::New(tarr, ptr);
current->add(load);
current->push(load);
// The load is infallible if an undefined result will be coerced to the
// appropriate numeric type if the read is out of bounds. The truncation
// analysis picks up some of these cases, but is incomplete with respect
// to others. For now, sniff the bytecode for simple patterns following
// the load which guarantee a truncation or numeric conversion.
if (viewType == ArrayBufferView::TYPE_FLOAT32 || viewType == ArrayBufferView::TYPE_FLOAT64) {
jsbytecode *next = pc + JSOP_GETELEM_LENGTH;
if (*next == JSOP_POS)
load->setInfallible();
} else {
jsbytecode *next = pc + JSOP_GETELEM_LENGTH;
if (*next == JSOP_ZERO && *(next + JSOP_ZERO_LENGTH) == JSOP_BITOR)
load->setInfallible();
}
*emitted = true;
return true;
}
bool
IonBuilder::getElemTryTyped(bool *emitted, MDefinition *obj, MDefinition *index)
{
JS_ASSERT(*emitted == false);
int arrayType = TypedArrayObject::TYPE_MAX;
if (!ElementAccessIsTypedArray(obj, index, &arrayType))
return true;
// Emit typed getelem variant.
if (!jsop_getelem_typed(obj, index, arrayType))
return false;
*emitted = true;
return true;
}
bool
IonBuilder::getElemTryString(bool *emitted, MDefinition *obj, MDefinition *index)
{
JS_ASSERT(*emitted == false);
if (obj->type() != MIRType_String)
return true;
// Emit fast path for string[index].
MInstruction *idInt32 = MToInt32::New(index);
current->add(idInt32);
index = idInt32;
MStringLength *length = MStringLength::New(obj);
current->add(length);
index = addBoundsCheck(index, length);
MCharCodeAt *charCode = MCharCodeAt::New(obj, index);
current->add(charCode);
MFromCharCode *result = MFromCharCode::New(charCode);
current->add(result);
current->push(result);
*emitted = true;
return true;
}
bool
IonBuilder::getElemTryArguments(bool *emitted, MDefinition *obj, MDefinition *index)
{
JS_ASSERT(*emitted == false);
if (inliningDepth_ > 0)
return true;
if (obj->type() != MIRType_Magic)
return true;
// Emit GetArgument.
JS_ASSERT(!info().argsObjAliasesFormals());
// Type Inference has guaranteed this is an optimized arguments object.
obj->setFoldedUnchecked();
// To ensure that we are not looking above the number of actual arguments.
MArgumentsLength *length = MArgumentsLength::New();
current->add(length);
// Ensure index is an integer.
MInstruction *idInt32 = MToInt32::New(index);
current->add(idInt32);
index = idInt32;
// Bailouts if we read more than the number of actual arguments.
index = addBoundsCheck(index, length);
// Load the argument from the actual arguments.
MGetArgument *load = MGetArgument::New(index);
current->add(load);
current->push(load);
types::StackTypeSet *types = types::TypeScript::BytecodeTypes(script(), pc);
if (!pushTypeBarrier(load, types, true))
return false;
*emitted = true;
return true;
}
bool
IonBuilder::getElemTryArgumentsInlined(bool *emitted, MDefinition *obj, MDefinition *index)
{
JS_ASSERT(*emitted == false);
if (inliningDepth_ == 0)
return true;
if (obj->type() != MIRType_Magic)
return true;
// Emit inlined arguments.
JS_ASSERT(!info().argsObjAliasesFormals());
// When the id is constant, we can just return the corresponding inlined argument
if (index->isConstant() && index->toConstant()->value().isInt32()) {
JS_ASSERT(inliningDepth_ > 0);
int32_t id = index->toConstant()->value().toInt32();
index->setFoldedUnchecked();
if (id < (int32_t)inlineCallInfo_->argc() && id >= 0)
current->push(inlineCallInfo_->getArg(id));
else
pushConstant(UndefinedValue());
*emitted = true;
return true;
}
// inlined not constant not supported, yet.
return abort("NYI inlined not constant get argument element");
}
bool
IonBuilder::getElemTryCache(bool *emitted, MDefinition *obj, MDefinition *index)
{
JS_ASSERT(*emitted == false);
// Make sure we have at least an object.
if (!obj->mightBeType(MIRType_Object))
return true;
// Don't cache for strings.
if (obj->mightBeType(MIRType_String))
return true;
// Index should be integer or string
if (!index->mightBeType(MIRType_Int32) && !index->mightBeType(MIRType_String))
return true;
// Turn off cacheing if the element is int32 and we've seen non-native objects as the target
// of this getelem.
bool nonNativeGetElement = inspector->hasSeenNonNativeGetElement(pc);
if (index->mightBeType(MIRType_Int32) && nonNativeGetElement)
cacheable = false;
return true;
// Emit GetElementCache.
types::StackTypeSet *types = types::TypeScript::BytecodeTypes(script(), pc);
bool barrier = PropertyReadNeedsTypeBarrier(cx, obj, NULL, types);
@ -6577,12 +6812,7 @@ IonBuilder::jsop_getelem()
if (index->mightBeType(MIRType_String))
barrier = true;
if (cacheable) {
ins = MGetElementCache::New(obj, index, barrier);
} else {
ins = MCallGetElement::New(obj, index);
barrier = true;
}
MInstruction *ins = MGetElementCache::New(obj, index, barrier);
current->add(ins);
current->push(ins);
@ -6590,7 +6820,8 @@ IonBuilder::jsop_getelem()
if (!resumeAfter(ins))
return false;
if (cacheable && index->type() == MIRType_Int32 && !barrier) {
// Spice up type information.
if (index->type() == MIRType_Int32 && !barrier) {
bool needHoleCheck = !ElementAccessIsPacked(cx, obj);
JSValueType knownType = GetElemKnownType(needHoleCheck, types);
@ -6598,18 +6829,19 @@ IonBuilder::jsop_getelem()
ins->setResultType(MIRTypeFromValueType(knownType));
}
return pushTypeBarrier(ins, types, barrier);
if (!pushTypeBarrier(ins, types, barrier))
return false;
*emitted = true;
return true;
}
bool
IonBuilder::jsop_getelem_dense()
IonBuilder::jsop_getelem_dense(MDefinition *obj, MDefinition *index)
{
MDefinition *id = current->pop();
MDefinition *obj = current->pop();
types::StackTypeSet *types = types::TypeScript::BytecodeTypes(script(), pc);
if (JSOp(*pc) == JSOP_CALLELEM && !id->mightBeType(MIRType_String) && types->noConstraints()) {
if (JSOp(*pc) == JSOP_CALLELEM && !index->mightBeType(MIRType_String) && types->noConstraints()) {
// Indexed call on an element of an array. Populate the observed types
// with any objects that could be in the array, to avoid extraneous
// type barriers.
@ -6630,10 +6862,10 @@ IonBuilder::jsop_getelem_dense()
if (!barrier)
knownType = GetElemKnownType(needsHoleCheck, types);
// Ensure id is an integer.
MInstruction *idInt32 = MToInt32::New(id);
// Ensure index is an integer.
MInstruction *idInt32 = MToInt32::New(index);
current->add(idInt32);
id = idInt32;
index = idInt32;
// Get the elements vector.
MInstruction *elements = MElements::New(obj);
@ -6672,15 +6904,15 @@ IonBuilder::jsop_getelem_dense()
// in-bounds elements, and the array is packed or its holes are not
// read. This is the best case: we can separate the bounds check for
// hoisting.
id = addBoundsCheck(id, initLength);
index = addBoundsCheck(index, initLength);
load = MLoadElement::New(elements, id, needsHoleCheck, loadDouble);
load = MLoadElement::New(elements, index, needsHoleCheck, loadDouble);
current->add(load);
} else {
// This load may return undefined, so assume that we *can* read holes,
// or that we can read out-of-bounds accesses. In this case, the bounds
// check is part of the opcode.
load = MLoadElementHole::New(elements, id, initLength, needsHoleCheck);
load = MLoadElementHole::New(elements, index, initLength, needsHoleCheck);
current->add(load);
// If maybeUndefined was true, the typeset must have undefined, and
@ -6765,75 +6997,10 @@ IonBuilder::convertShiftToMaskForStaticTypedArray(MDefinition *id,
}
bool
IonBuilder::jsop_getelem_typed_static(bool *psucceeded)
IonBuilder::jsop_getelem_typed(MDefinition *obj, MDefinition *index, int arrayType)
{
if (!LIRGenerator::allowStaticTypedArrayAccesses())
return true;
MDefinition *id = current->peek(-1);
MDefinition *obj = current->peek(-2);
if (ElementAccessHasExtraIndexedProperty(cx, obj))
return true;
if (!obj->resultTypeSet())
return true;
JSObject *tarrObj = obj->resultTypeSet()->getSingleton();
if (!tarrObj)
return true;
TypedArrayObject *tarr = &tarrObj->as<TypedArrayObject>();
ArrayBufferView::ViewType viewType = JS_GetArrayBufferViewType(tarr);
// LoadTypedArrayElementStatic currently treats uint32 arrays as int32.
if (viewType == ArrayBufferView::TYPE_UINT32)
return true;
MDefinition *ptr = convertShiftToMaskForStaticTypedArray(id, viewType);
if (!ptr)
return true;
obj->setFoldedUnchecked();
MLoadTypedArrayElementStatic *load = MLoadTypedArrayElementStatic::New(tarr, ptr);
current->add(load);
// The load is infallible if an undefined result will be coerced to the
// appropriate numeric type if the read is out of bounds. The truncation
// analysis picks up some of these cases, but is incomplete with respect
// to others. For now, sniff the bytecode for simple patterns following
// the load which guarantee a truncation or numeric conversion.
if (viewType == ArrayBufferView::TYPE_FLOAT32 || viewType == ArrayBufferView::TYPE_FLOAT64) {
jsbytecode *next = pc + JSOP_GETELEM_LENGTH;
if (*next == JSOP_POS)
load->setInfallible();
} else {
jsbytecode *next = pc + JSOP_GETELEM_LENGTH;
if (*next == JSOP_ZERO && *(next + JSOP_ZERO_LENGTH) == JSOP_BITOR)
load->setInfallible();
}
current->popn(2);
current->push(load);
*psucceeded = true;
return true;
}
bool
IonBuilder::jsop_getelem_typed(int arrayType)
{
bool staticAccess = false;
if (!jsop_getelem_typed_static(&staticAccess))
return false;
if (staticAccess)
return true;
types::StackTypeSet *types = types::TypeScript::BytecodeTypes(script(), pc);
MDefinition *id = current->pop();
MDefinition *obj = current->pop();
bool maybeUndefined = types->hasType(types::Type::UndefinedType());
// Reading from an Uint32Array will result in a double for values
@ -6842,9 +7009,9 @@ IonBuilder::jsop_getelem_typed(int arrayType)
bool allowDouble = types->hasType(types::Type::DoubleType());
// Ensure id is an integer.
MInstruction *idInt32 = MToInt32::New(id);
MInstruction *idInt32 = MToInt32::New(index);
current->add(idInt32);
id = idInt32;
index = idInt32;
if (!maybeUndefined) {
// Assume the index is in range, so that we can hoist the length,
@ -6880,14 +7047,14 @@ IonBuilder::jsop_getelem_typed(int arrayType)
current->add(length);
// Bounds check.
id = addBoundsCheck(id, length);
index = addBoundsCheck(index, length);
// Get the elements vector.
MInstruction *elements = getTypedArrayElements(obj);
current->add(elements);
// Load the element.
MLoadTypedArrayElement *load = MLoadTypedArrayElement::New(elements, id, arrayType);
MLoadTypedArrayElement *load = MLoadTypedArrayElement::New(elements, index, arrayType);
current->add(load);
current->push(load);
@ -6925,7 +7092,7 @@ IonBuilder::jsop_getelem_typed(int arrayType)
// bounds check will be part of the instruction, and the instruction
// will always return a Value.
MLoadTypedArrayElementHole *load =
MLoadTypedArrayElementHole::New(obj, id, arrayType, allowDouble);
MLoadTypedArrayElementHole::New(obj, index, arrayType, allowDouble);
current->add(load);
current->push(load);
@ -6933,30 +7100,6 @@ IonBuilder::jsop_getelem_typed(int arrayType)
}
}
bool
IonBuilder::jsop_getelem_string()
{
MDefinition *id = current->pop();
MDefinition *str = current->pop();
MInstruction *idInt32 = MToInt32::New(id);
current->add(idInt32);
id = idInt32;
MStringLength *length = MStringLength::New(str);
current->add(length);
id = addBoundsCheck(id, length);
MCharCodeAt *charCode = MCharCodeAt::New(str, id);
current->add(charCode);
MFromCharCode *result = MFromCharCode::New(charCode);
current->add(result);
current->push(result);
return true;
}
bool
IonBuilder::jsop_setelem()
{
@ -7407,60 +7550,6 @@ IonBuilder::jsop_arguments_length()
return pushConstant(Int32Value(inlineCallInfo_->argv().length()));
}
bool
IonBuilder::jsop_arguments_getelem()
{
JS_ASSERT(!info().argsObjAliasesFormals());
// Get the argument id
MDefinition *idx = current->pop();
// Type Inference has guaranteed this is an optimized arguments object.
MDefinition *args = current->pop();
args->setFoldedUnchecked();
// When we are not inlining, we can just get the arguments from the stack.
if (inliningDepth_ == 0) {
// To ensure that we are not looking above the number of actual arguments.
MArgumentsLength *length = MArgumentsLength::New();
current->add(length);
// Ensure idx is an integer.
MInstruction *index = MToInt32::New(idx);
current->add(index);
// Bailouts if we read more than the number of actual arguments.
index = addBoundsCheck(index, length);
// Load the argument from the actual arguments.
MGetArgument *load = MGetArgument::New(index);
current->add(load);
current->push(load);
types::StackTypeSet *types = types::TypeScript::BytecodeTypes(script(), pc);
return pushTypeBarrier(load, types, true);
}
// When the id is constant, we can just return the corresponding inlined argument
if (idx->isConstant() && idx->toConstant()->value().isInt32()) {
JS_ASSERT(inliningDepth_ > 0);
int32_t id = idx->toConstant()->value().toInt32();
idx->setFoldedUnchecked();
if (id < (int32_t)inlineCallInfo_->argc() && id >= 0)
current->push(inlineCallInfo_->getArg(id));
else
pushConstant(UndefinedValue());
return true;
}
// inlined not constant not supported, yet.
return abort("NYI inlined not constant get argument element");
}
static JSObject *
CreateRestArgumentsTemplateObject(JSContext *cx, unsigned length)
{

Просмотреть файл

@ -397,6 +397,15 @@ class IonBuilder : public MIRGenerator
bool setElemTryCache(bool *emitted, MDefinition *object,
MDefinition *index, MDefinition *value);
// jsop_getelem() helpers.
bool getElemTryDense(bool *emitted, MDefinition *obj, MDefinition *index);
bool getElemTryTypedStatic(bool *emitted, MDefinition *obj, MDefinition *index);
bool getElemTryTyped(bool *emitted, MDefinition *obj, MDefinition *index);
bool getElemTryString(bool *emitted, MDefinition *obj, MDefinition *index);
bool getElemTryArguments(bool *emitted, MDefinition *obj, MDefinition *index);
bool getElemTryArgumentsInlined(bool *emitted, MDefinition *obj, MDefinition *index);
bool getElemTryCache(bool *emitted, MDefinition *obj, MDefinition *index);
// Typed array helpers.
MInstruction *getTypedArrayLength(MDefinition *obj);
MInstruction *getTypedArrayElements(MDefinition *obj);
@ -430,10 +439,8 @@ class IonBuilder : public MIRGenerator
bool jsop_intrinsic(HandlePropertyName name);
bool jsop_bindname(PropertyName *name);
bool jsop_getelem();
bool jsop_getelem_dense();
bool jsop_getelem_typed(int arrayType);
bool jsop_getelem_typed_static(bool *psucceeded);
bool jsop_getelem_string();
bool jsop_getelem_dense(MDefinition *obj, MDefinition *index);
bool jsop_getelem_typed(MDefinition *obj, MDefinition *index, int arrayType);
bool jsop_setelem();
bool jsop_setelem_dense(types::StackTypeSet::DoubleConversion conversion,
SetElemSafety safety,