Bug 1283334 - Part 3: Do not sparsify dense arrays when freezing - Ion. r=nbp

--HG--
extra : rebase_source : 338ff0f30ed6c80da1b93c2fd1a847b5e57c2b7f
extra : histedit_source : 3d60a20c97952902cdf07419887e39eb3316d8b5
This commit is contained in:
Leo Gaspard 2016-08-25 16:28:31 -07:00
parent 07fa31ec7c
commit cac188d1af
14 changed files with 308 additions and 16 deletions

View File

@ -90,6 +90,7 @@ GetObject(const MDefinition* ins)
case MDefinition::Op_ArrayLength:
case MDefinition::Op_SetArrayLength:
case MDefinition::Op_StoreElementHole:
case MDefinition::Op_FallibleStoreElement:
case MDefinition::Op_TypedObjectDescr:
case MDefinition::Op_Slots:
case MDefinition::Op_Elements:

View File

@ -7975,7 +7975,8 @@ class OutOfLineStoreElementHole : public OutOfLineCodeBase<CodeGenerator>
explicit OutOfLineStoreElementHole(LInstruction* ins)
: ins_(ins)
{
MOZ_ASSERT(ins->isStoreElementHoleV() || ins->isStoreElementHoleT());
MOZ_ASSERT(ins->isStoreElementHoleV() || ins->isStoreElementHoleT() ||
ins->isFallibleStoreElementV() || ins->isFallibleStoreElementT());
}
void accept(CodeGenerator* codegen) {
@ -8069,9 +8070,12 @@ CodeGenerator::visitStoreElementV(LStoreElementV* lir)
}
}
void
CodeGenerator::visitStoreElementHoleT(LStoreElementHoleT* lir)
template <typename T> void
CodeGenerator::emitStoreElementHoleT(T* lir)
{
static_assert(std::is_same<T, LStoreElementHoleT>::value || std::is_same<T, LFallibleStoreElementT>::value,
"emitStoreElementHoleT called with unexpected argument type");
OutOfLineStoreElementHole* ool = new(alloc()) OutOfLineStoreElementHole(lir);
addOutOfLineCode(ool, lir->mir());
@ -8120,15 +8124,24 @@ CodeGenerator::visitStoreElementHoleT(LStoreElementHoleT* lir)
}
void
CodeGenerator::visitStoreElementHoleV(LStoreElementHoleV* lir)
CodeGenerator::visitStoreElementHoleT(LStoreElementHoleT* lir)
{
emitStoreElementHoleT(lir);
}
template <typename T> void
CodeGenerator::emitStoreElementHoleV(T* lir)
{
static_assert(std::is_same<T, LStoreElementHoleV>::value || std::is_same<T, LFallibleStoreElementV>::value,
"emitStoreElementHoleV called with unexpected parameter type");
OutOfLineStoreElementHole* ool = new(alloc()) OutOfLineStoreElementHole(lir);
addOutOfLineCode(ool, lir->mir());
Register obj = ToRegister(lir->object());
Register elements = ToRegister(lir->elements());
const LAllocation* index = lir->index();
const ValueOperand value = ToValue(lir, LStoreElementHoleV::Value);
const ValueOperand value = ToValue(lir, T::Value);
RegisterOrInt32Constant key = ToRegisterOrInt32Constant(index);
JSValueType unboxedType = lir->mir()->unboxedType();
@ -8170,6 +8183,66 @@ CodeGenerator::visitStoreElementHoleV(LStoreElementHoleV* lir)
masm.bind(ool->rejoin());
}
void
CodeGenerator::visitStoreElementHoleV(LStoreElementHoleV* lir)
{
emitStoreElementHoleV(lir);
}
typedef bool (*ThrowReadOnlyFn)(JSContext*, HandleObject);
static const VMFunction ThrowReadOnlyInfo =
FunctionInfo<ThrowReadOnlyFn>(ThrowReadOnlyError, "ThrowReadOnlyError");
void
CodeGenerator::visitFallibleStoreElementT(LFallibleStoreElementT* lir)
{
Register elements = ToRegister(lir->elements());
// Handle frozen objects
Label isFrozen;
Address flags(elements, ObjectElements::offsetOfFlags());
if (!lir->mir()->strict()) {
masm.branchTest32(Assembler::NonZero, flags, Imm32(ObjectElements::FROZEN), &isFrozen);
} else {
Register object = ToRegister(lir->object());
OutOfLineCode* ool = oolCallVM(ThrowReadOnlyInfo, lir,
ArgList(object), StoreNothing());
masm.branchTest32(Assembler::NonZero, flags, Imm32(ObjectElements::FROZEN), ool->entry());
// This OOL code should have thrown an exception, so will never return.
// So, do not bind ool->rejoin() anywhere, so that it implicitly (and without the cost
// of a jump) does a masm.assumeUnreachable().
}
emitStoreElementHoleT(lir);
masm.bind(&isFrozen);
}
void
CodeGenerator::visitFallibleStoreElementV(LFallibleStoreElementV* lir)
{
Register elements = ToRegister(lir->elements());
// Handle frozen objects
Label isFrozen;
Address flags(elements, ObjectElements::offsetOfFlags());
if (!lir->mir()->strict()) {
masm.branchTest32(Assembler::NonZero, flags, Imm32(ObjectElements::FROZEN), &isFrozen);
} else {
Register object = ToRegister(lir->object());
OutOfLineCode* ool = oolCallVM(ThrowReadOnlyInfo, lir,
ArgList(object), StoreNothing());
masm.branchTest32(Assembler::NonZero, flags, Imm32(ObjectElements::FROZEN), ool->entry());
// This OOL code should have thrown an exception, so will never return.
// So, do not bind ool->rejoin() anywhere, so that it implicitly (and without the cost
// of a jump) does a masm.assumeUnreachable().
}
emitStoreElementHoleV(lir);
masm.bind(&isFrozen);
}
typedef bool (*SetDenseOrUnboxedArrayElementFn)(JSContext*, HandleObject, int32_t,
HandleValue, bool strict);
static const VMFunction SetDenseOrUnboxedArrayElementInfo =
@ -8196,7 +8269,16 @@ CodeGenerator::visitOutOfLineStoreElementHole(OutOfLineStoreElementHole* ool)
value = TypedOrValueRegister(ToValue(store, LStoreElementHoleV::Value));
unboxedType = store->mir()->unboxedType();
temp = store->getTemp(0);
} else {
} else if (ins->isFallibleStoreElementV()) {
LFallibleStoreElementV* store = ins->toFallibleStoreElementV();
object = ToRegister(store->object());
elements = ToRegister(store->elements());
index = store->index();
valueType = store->mir()->value()->type();
value = TypedOrValueRegister(ToValue(store, LFallibleStoreElementV::Value));
unboxedType = store->mir()->unboxedType();
temp = store->getTemp(0);
} else if (ins->isStoreElementHoleT()) {
LStoreElementHoleT* store = ins->toStoreElementHoleT();
object = ToRegister(store->object());
elements = ToRegister(store->elements());
@ -8208,6 +8290,18 @@ CodeGenerator::visitOutOfLineStoreElementHole(OutOfLineStoreElementHole* ool)
value = TypedOrValueRegister(valueType, ToAnyRegister(store->value()));
unboxedType = store->mir()->unboxedType();
temp = store->getTemp(0);
} else { // ins->isFallibleStoreElementT()
LFallibleStoreElementT* store = ins->toFallibleStoreElementT();
object = ToRegister(store->object());
elements = ToRegister(store->elements());
index = store->index();
valueType = store->mir()->value()->type();
if (store->value()->isConstant())
value = ConstantOrRegister(store->value()->toConstant()->toJSValue());
else
value = TypedOrValueRegister(valueType, ToAnyRegister(store->value()));
unboxedType = store->mir()->unboxedType();
temp = store->getTemp(0);
}
RegisterOrInt32Constant key = ToRegisterOrInt32Constant(index);
@ -8264,13 +8358,21 @@ CodeGenerator::visitOutOfLineStoreElementHole(OutOfLineStoreElementHole* ool)
masm.bind(&dontUpdate);
}
if (ins->isStoreElementHoleT() && unboxedType == JSVAL_TYPE_MAGIC && valueType != MIRType::Double) {
// The inline path for StoreElementHoleT does not always store the type tag,
// so we do the store on the OOL path. We use MIRType::None for the element type
// so that storeElementTyped will always store the type tag.
emitStoreElementTyped(ins->toStoreElementHoleT()->value(), valueType, MIRType::None,
elements, index, 0);
masm.jump(ool->rejoin());
if ((ins->isStoreElementHoleT() || ins->isFallibleStoreElementT()) &&
unboxedType == JSVAL_TYPE_MAGIC && valueType != MIRType::Double)
{
// The inline path for StoreElementHoleT and FallibleStoreElementT does not always store
// the type tag, so we do the store on the OOL path. We use MIRType::None for the element
// type so that storeElementTyped will always store the type tag.
if (ins->isStoreElementHoleT()) {
emitStoreElementTyped(ins->toStoreElementHoleT()->value(), valueType, MIRType::None,
elements, index, 0);
masm.jump(ool->rejoin());
} else if (ins->isFallibleStoreElementT()) {
emitStoreElementTyped(ins->toFallibleStoreElementT()->value(), valueType,
MIRType::None, elements, index, 0);
masm.jump(ool->rejoin());
}
} else {
// Jump to the inline path where we will store the value.
masm.jump(ool->rejoinStore());

View File

@ -298,8 +298,12 @@ class CodeGenerator final : public CodeGeneratorSpecific
void visitUnboxObjectOrNull(LUnboxObjectOrNull* lir);
void visitStoreElementT(LStoreElementT* lir);
void visitStoreElementV(LStoreElementV* lir);
template <typename T> void emitStoreElementHoleT(T* lir);
template <typename T> void emitStoreElementHoleV(T* lir);
void visitStoreElementHoleT(LStoreElementHoleT* lir);
void visitStoreElementHoleV(LStoreElementHoleV* lir);
void visitFallibleStoreElementV(LFallibleStoreElementV* lir);
void visitFallibleStoreElementT(LFallibleStoreElementT* lir);
void visitStoreUnboxedPointer(LStoreUnboxedPointer* lir);
void visitConvertUnboxedObjectToNative(LConvertUnboxedObjectToNative* lir);
void emitArrayPopShift(LInstruction* lir, const MArrayPopShift* mir, Register obj,

View File

@ -3563,6 +3563,13 @@ jit::AddKeepAliveInstructions(MIRGraph& graph)
continue;
}
if (use->isFallibleStoreElement()) {
// See StoreElementHole case above.
MOZ_ASSERT_IF(!use->toFallibleStoreElement()->object()->isUnbox() && !ownerObject->isUnbox(),
use->toFallibleStoreElement()->object() == ownerObject);
continue;
}
if (use->isInArray()) {
// See StoreElementHole case above.
MOZ_ASSERT_IF(!use->toInArray()->object()->isUnbox() && !ownerObject->isUnbox(),

View File

@ -10459,7 +10459,9 @@ IonBuilder::jsop_setelem_dense(TemporaryTypeSet::DoubleConversion conversion,
// Writes which are on holes in the object do not have to bail out if they
// cannot hit another indexed property on the object or its prototypes.
bool writeOutOfBounds = !ElementAccessHasExtraIndexedProperty(this, obj);
bool hasNoExtraIndexedProperty = !ElementAccessHasExtraIndexedProperty(this, obj);
bool mayBeFrozen = ElementAccessMightBeFrozen(constraints(), obj);
// Ensure id is an integer.
MInstruction* idInt32 = MToInt32::New(alloc(), id);
@ -10505,20 +10507,31 @@ IonBuilder::jsop_setelem_dense(TemporaryTypeSet::DoubleConversion conversion,
// Use MStoreElementHole if this SETELEM has written to out-of-bounds
// indexes in the past. Otherwise, use MStoreElement so that we can hoist
// the initialized length and bounds check.
// If an object may have been frozen, no previous expectation hold and we
// fallback to MFallibleStoreElement.
MInstruction* store;
MStoreElementCommon *common = nullptr;
if (writeHole && writeOutOfBounds) {
if (writeHole && hasNoExtraIndexedProperty && !mayBeFrozen) {
MStoreElementHole* ins = MStoreElementHole::New(alloc(), obj, elements, id, newValue, unboxedType);
store = ins;
common = ins;
current->add(ins);
current->push(value);
} else if (hasNoExtraIndexedProperty && mayBeFrozen) {
bool strict = IsStrictSetPC(pc);
MFallibleStoreElement* ins = MFallibleStoreElement::New(alloc(), obj, elements, id,
newValue, unboxedType, strict);
store = ins;
common = ins;
current->add(ins);
current->push(value);
} else {
MInstruction* initLength = initializedLength(obj, elements, unboxedType);
id = addBoundsCheck(id, initLength);
bool needsHoleCheck = !packed && !writeOutOfBounds;
bool needsHoleCheck = !packed && !hasNoExtraIndexedProperty;
if (unboxedType != JSVAL_TYPE_MAGIC) {
store = storeUnboxedValue(obj, elements, 0, id, unboxedType, newValue);

View File

@ -3156,6 +3156,38 @@ LIRGenerator::visitStoreElementHole(MStoreElementHole* ins)
assignSafepoint(lir, ins);
}
void
LIRGenerator::visitFallibleStoreElement(MFallibleStoreElement* ins)
{
MOZ_ASSERT(ins->elements()->type() == MIRType::Elements);
MOZ_ASSERT(ins->index()->type() == MIRType::Int32);
const LUse object = useRegister(ins->object());
const LUse elements = useRegister(ins->elements());
const LAllocation index = useRegisterOrConstant(ins->index());
// Use a temp register when adding new elements to unboxed arrays.
LDefinition tempDef = LDefinition::BogusTemp();
if (ins->unboxedType() != JSVAL_TYPE_MAGIC)
tempDef = temp();
LInstruction* lir;
switch (ins->value()->type()) {
case MIRType::Value:
lir = new(alloc()) LFallibleStoreElementV(object, elements, index, useBox(ins->value()),
tempDef);
break;
default:
const LAllocation value = useRegisterOrNonDoubleConstant(ins->value());
lir = new(alloc()) LFallibleStoreElementT(object, elements, index, value, tempDef);
break;
}
add(lir, ins);
assignSafepoint(lir, ins);
}
void
LIRGenerator::visitStoreUnboxedObjectOrNull(MStoreUnboxedObjectOrNull* ins)
{

View File

@ -223,6 +223,7 @@ class LIRGenerator : public LIRGeneratorSpecific
void visitLoadUnboxedString(MLoadUnboxedString* ins);
void visitStoreElement(MStoreElement* ins);
void visitStoreElementHole(MStoreElementHole* ins);
void visitFallibleStoreElement(MFallibleStoreElement* ins);
void visitStoreUnboxedObjectOrNull(MStoreUnboxedObjectOrNull* ins);
void visitStoreUnboxedString(MStoreUnboxedString* ins);
void visitConvertUnboxedObjectToNative(MConvertUnboxedObjectToNative* ins);

View File

@ -2917,6 +2917,7 @@ NeedNegativeZeroCheck(MDefinition* def)
}
case MDefinition::Op_StoreElement:
case MDefinition::Op_StoreElementHole:
case MDefinition::Op_FallibleStoreElement:
case MDefinition::Op_LoadElement:
case MDefinition::Op_LoadElementHole:
case MDefinition::Op_LoadUnboxedScalar:
@ -5691,6 +5692,13 @@ jit::ElementAccessMightBeCopyOnWrite(CompilerConstraintList* constraints, MDefin
return !types || types->hasObjectFlags(constraints, OBJECT_FLAG_COPY_ON_WRITE);
}
bool
jit::ElementAccessMightBeFrozen(CompilerConstraintList* constraints, MDefinition* obj)
{
TemporaryTypeSet* types = obj->resultTypeSet();
return !types || types->hasObjectFlags(constraints, OBJECT_FLAG_FROZEN);
}
bool
jit::ElementAccessHasExtraIndexedProperty(IonBuilder* builder, MDefinition* obj)
{

View File

@ -9400,6 +9400,50 @@ class MStoreElementHole
ALLOW_CLONE(MStoreElementHole)
};
// Try to store a value to a dense array slots vector. May fail due to the object being frozen.
// Cannot be used on an object that has extra indexed properties.
class MFallibleStoreElement
: public MAryInstruction<4>,
public MStoreElementCommon,
public MixPolicy<SingleObjectPolicy, NoFloatPolicy<3> >::Data
{
JSValueType unboxedType_;
bool strict_;
MFallibleStoreElement(MDefinition* object, MDefinition* elements,
MDefinition* index, MDefinition* value,
JSValueType unboxedType, bool strict)
: unboxedType_(unboxedType)
{
initOperand(0, object);
initOperand(1, elements);
initOperand(2, index);
initOperand(3, value);
strict_ = strict;
MOZ_ASSERT(elements->type() == MIRType::Elements);
MOZ_ASSERT(index->type() == MIRType::Int32);
}
public:
INSTRUCTION_HEADER(FallibleStoreElement)
TRIVIAL_NEW_WRAPPERS
NAMED_OPERANDS((0, object), (1, elements), (2, index), (3, value))
JSValueType unboxedType() const {
return unboxedType_;
}
AliasSet getAliasSet() const override {
return AliasSet::Store(AliasSet::ObjectFields |
AliasSet::BoxedOrUnboxedElements(unboxedType()));
}
bool strict() const {
return strict_;
}
ALLOW_CLONE(MFallibleStoreElement)
};
// Store an unboxed object or null pointer to a v\ector.
class MStoreUnboxedObjectOrNull
: public MAryInstruction<4>,
@ -13877,6 +13921,7 @@ bool ElementAccessIsTypedArray(CompilerConstraintList* constraints,
Scalar::Type* arrayType);
bool ElementAccessIsPacked(CompilerConstraintList* constraints, MDefinition* obj);
bool ElementAccessMightBeCopyOnWrite(CompilerConstraintList* constraints, MDefinition* obj);
bool ElementAccessMightBeFrozen(CompilerConstraintList* constraints, MDefinition* obj);
bool ElementAccessHasExtraIndexedProperty(IonBuilder* builder, MDefinition* obj);
MIRType DenseNativeElementType(CompilerConstraintList* constraints, MDefinition* obj);
BarrierKind PropertyReadNeedsTypeBarrier(JSContext* propertycx,

View File

@ -212,6 +212,7 @@ namespace jit {
_(LoadUnboxedString) \
_(StoreElement) \
_(StoreElementHole) \
_(FallibleStoreElement) \
_(StoreUnboxedScalar) \
_(StoreUnboxedObjectOrNull) \
_(StoreUnboxedString) \

View File

@ -1298,6 +1298,15 @@ ThrowRuntimeLexicalError(JSContext* cx, unsigned errorNumber)
return false;
}
bool
ThrowReadOnlyError(JSContext* cx, HandleObject handle)
{
HandleNativeObject obj = handle.as<NativeObject>();
RootedValue val(cx, ObjectValue(*obj));
ReportValueError(cx, JSMSG_READ_ONLY, JSDVG_IGNORE_STACK, val, nullptr);
return false;
}
bool
ThrowBadDerivedReturn(JSContext* cx, HandleValue v)
{

View File

@ -786,6 +786,8 @@ bool ObjectIsConstructor(JSObject* obj);
MOZ_MUST_USE bool
ThrowRuntimeLexicalError(JSContext* cx, unsigned errorNumber);
MOZ_MUST_USE bool
ThrowReadOnlyError(JSContext* cx, HandleObject obj);
MOZ_MUST_USE bool
BaselineThrowUninitializedThis(JSContext* cx, BaselineFrame* frame);
MOZ_MUST_USE bool
ThrowBadDerivedReturn(JSContext* cx, HandleValue v);

View File

@ -5734,6 +5734,71 @@ class LStoreElementHoleT : public LInstructionHelper<0, 4, 1>
}
};
// Like LStoreElementV, but can just ignore assignment (for eg. frozen objects)
class LFallibleStoreElementV : public LInstructionHelper<0, 3 + BOX_PIECES, 1>
{
public:
LIR_HEADER(FallibleStoreElementV)
LFallibleStoreElementV(const LAllocation& object, const LAllocation& elements,
const LAllocation& index, const LBoxAllocation& value,
const LDefinition& temp) {
setOperand(0, object);
setOperand(1, elements);
setOperand(2, index);
setBoxOperand(Value, value);
setTemp(0, temp);
}
static const size_t Value = 3;
const MFallibleStoreElement* mir() const {
return mir_->toFallibleStoreElement();
}
const LAllocation* object() {
return getOperand(0);
}
const LAllocation* elements() {
return getOperand(1);
}
const LAllocation* index() {
return getOperand(2);
}
};
// Like LStoreElementT, but can just ignore assignment (for eg. frozen objects)
class LFallibleStoreElementT : public LInstructionHelper<0, 4, 1>
{
public:
LIR_HEADER(FallibleStoreElementT)
LFallibleStoreElementT(const LAllocation& object, const LAllocation& elements,
const LAllocation& index, const LAllocation& value,
const LDefinition& temp) {
setOperand(0, object);
setOperand(1, elements);
setOperand(2, index);
setOperand(3, value);
setTemp(0, temp);
}
const MFallibleStoreElement* mir() const {
return mir_->toFallibleStoreElement();
}
const LAllocation* object() {
return getOperand(0);
}
const LAllocation* elements() {
return getOperand(1);
}
const LAllocation* index() {
return getOperand(2);
}
const LAllocation* value() {
return getOperand(3);
}
};
class LStoreUnboxedPointer : public LInstructionHelper<0, 3, 0>
{
public:

View File

@ -294,6 +294,8 @@
_(ArrayJoin) \
_(StoreElementHoleV) \
_(StoreElementHoleT) \
_(FallibleStoreElementV) \
_(FallibleStoreElementT) \
_(LoadTypedArrayElementHole) \
_(LoadTypedArrayElementStatic) \
_(StoreTypedArrayElementHole) \