author | Jan de Mooij <jdemooij@mozilla.com> |
Thu, 02 Feb 2012 13:41:58 +0100 | |
changeset 105656 | 550a780f73aeb23ea958cab93de141376aa12f3a |
parent 105655 | 41b54805815bfc9c01e42b1ed4680e9851f28dd1 |
child 105657 | 35cda526bd052dd220486c4fa25070b709085ba6 |
push id | 23447 |
push user | danderson@mozilla.com |
push date | Tue, 11 Sep 2012 17:34:27 +0000 |
treeherder | mozilla-central@fdfaef738a00 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | dvander |
bugs | 706328 |
milestone | 13.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/js/src/ion/AliasAnalysis.cpp +++ b/js/src/ion/AliasAnalysis.cpp @@ -188,17 +188,17 @@ AliasAnalysis::analyze() return false; } } else { // This instruction depends on stores inside the loop body. Mark it as having a // dependency on the last instruction of the loop header. The last instruction is a // control instruction and these are never hoisted. MControlInstruction *controlIns = loop_->loopHeader()->lastIns(); IonSpew(IonSpew_Alias, "Load %d depends on %d (due to stores in loop body)", - ins->id(), controlIns); + ins->id(), controlIns->id()); ins->setDependency(controlIns); } } loop_ = loop_->outer(); } } JS_ASSERT(loop_ == NULL);
--- a/js/src/ion/CodeGenerator.cpp +++ b/js/src/ion/CodeGenerator.cpp @@ -952,16 +952,193 @@ CodeGenerator::visitBoundsCheckRange(LBo bool CodeGenerator::visitBoundsCheckLower(LBoundsCheckLower *lir) { int32 min = lir->mir()->minimum(); masm.cmp32(ToRegister(lir->index()), Imm32(min)); return bailoutIf(Assembler::LessThan, lir->snapshot()); } +class OutOfLineStoreElementHole : public OutOfLineCodeBase<CodeGenerator> +{ + LInstruction *ins_; + Label rejoinStore_; + + public: + OutOfLineStoreElementHole(LInstruction *ins) + : ins_(ins) + { + JS_ASSERT(ins->isStoreElementHoleV() || ins->isStoreElementHoleT()); + } + + bool accept(CodeGenerator *codegen) { + return codegen->visitOutOfLineStoreElementHole(this); + } + LInstruction *ins() const { + return ins_; + } + Label *rejoinStore() { + return &rejoinStore_; + } +}; + +bool +CodeGenerator::visitStoreElementT(LStoreElementT *store) +{ + storeElementTyped(store->value(), store->mir()->value()->type(), store->mir()->elementType(), + ToRegister(store->elements()), store->index()); + return true; +} + +bool +CodeGenerator::visitStoreElementV(LStoreElementV *lir) +{ + const ValueOperand value = ToValue(lir, LStoreElementV::Value); + Register elements = ToRegister(lir->elements()); + + if (lir->index()->isConstant()) + masm.storeValue(value, Address(elements, ToInt32(lir->index()) * sizeof(js::Value))); + else + masm.storeValue(value, BaseIndex(elements, ToRegister(lir->index()), TimesEight)); + return true; +} + +bool +CodeGenerator::visitStoreElementHoleT(LStoreElementHoleT *lir) +{ + OutOfLineStoreElementHole *ool = new OutOfLineStoreElementHole(lir); + if (!addOutOfLineCode(ool)) + return false; + + Register elements = ToRegister(lir->elements()); + const LAllocation *index = lir->index(); + + // OOL path if index >= initializedLength. + Address initLength(elements, ObjectElements::offsetOfInitializedLength()); + masm.branchKey(Assembler::BelowOrEqual, initLength, ToInt32Key(index), ool->entry()); + + masm.bind(ool->rejoinStore()); + storeElementTyped(lir->value(), lir->mir()->value()->type(), lir->mir()->elementType(), + elements, index); + + masm.bind(ool->rejoin()); + return true; +} + +bool +CodeGenerator::visitStoreElementHoleV(LStoreElementHoleV *lir) +{ + OutOfLineStoreElementHole *ool = new OutOfLineStoreElementHole(lir); + if (!addOutOfLineCode(ool)) + return false; + + Register elements = ToRegister(lir->elements()); + const LAllocation *index = lir->index(); + const ValueOperand value = ToValue(lir, LStoreElementHoleV::Value); + + // OOL path if index >= initializedLength. + Address initLength(elements, ObjectElements::offsetOfInitializedLength()); + masm.branchKey(Assembler::BelowOrEqual, initLength, ToInt32Key(index), ool->entry()); + + masm.bind(ool->rejoinStore()); + if (lir->index()->isConstant()) + masm.storeValue(value, Address(elements, ToInt32(lir->index()) * sizeof(js::Value))); + else + masm.storeValue(value, BaseIndex(elements, ToRegister(lir->index()), TimesEight)); + + masm.bind(ool->rejoin()); + return true; +} + +bool +CodeGenerator::visitOutOfLineStoreElementHole(OutOfLineStoreElementHole *ool) +{ + Register object, elements; + LInstruction *ins = ool->ins(); + const LAllocation *index; + MIRType valueType; + ConstantOrRegister value; + + if (ins->isStoreElementHoleV()) { + LStoreElementHoleV *store = ins->toStoreElementHoleV(); + object = ToRegister(store->object()); + elements = ToRegister(store->elements()); + index = store->index(); + valueType = store->mir()->value()->type(); + value = TypedOrValueRegister(ToValue(store, LStoreElementHoleV::Value)); + } else { + LStoreElementHoleT *store = ins->toStoreElementHoleT(); + 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()); + else + value = TypedOrValueRegister(valueType, ToAnyRegister(store->value())); + } + + // If index == initializedLength, try to bump the initialized length inline. + // If index > initializedLength, call a stub. Note that this relies on the + // condition flags sticking from the incoming branch. + Label callStub; + masm.j(Assembler::NotEqual, &callStub); + + Int32Key key = ToInt32Key(index); + + // Check array capacity. + masm.branchKey(Assembler::BelowOrEqual, Address(elements, ObjectElements::offsetOfCapacity()), + key, &callStub); + + // Update initialized length. The capacity guard above ensures this won't overflow, + // due to NELEMENTS_LIMIT. + masm.bumpKey(&key, 1); + masm.storeKey(key, Address(elements, ObjectElements::offsetOfInitializedLength())); + + // Update length if length < initializedLength. + Label dontUpdate; + masm.branchKey(Assembler::AboveOrEqual, Address(elements, ObjectElements::offsetOfLength()), + key, &dontUpdate); + masm.storeKey(key, Address(elements, ObjectElements::offsetOfLength())); + masm.bind(&dontUpdate); + + masm.bumpKey(&key, -1); + + if (ins->isStoreElementHoleT() && 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. + storeElementTyped(ins->toStoreElementHoleT()->value(), valueType, MIRType_None, elements, + index); + masm.jump(ool->rejoin()); + } else { + // Jump to the inline path where we will store the value. + masm.jump(ool->rejoinStore()); + } + + masm.bind(&callStub); + saveLive(ins); + + typedef bool (*pf)(JSContext *, JSObject *, const Value &, const Value &); + static const VMFunction Info = FunctionInfo<pf>(SetObjectElement); + + pushArg(value); + if (index->isConstant()) + pushArg(*index->toConstant()); + else + pushArg(TypedOrValueRegister(MIRType_Int32, ToAnyRegister(index))); + pushArg(object); + if (!callVM(Info, ins)) + return false; + + restoreLive(ins); + masm.jump(ool->rejoin()); + return true; +} + bool CodeGenerator::generate() { JSContext *cx = gen->cx; if (!safepoints_.init(graph.localSlotCount())) return false;
--- a/js/src/ion/CodeGenerator.h +++ b/js/src/ion/CodeGenerator.h @@ -53,16 +53,17 @@ #endif namespace js { namespace ion { class CheckOverRecursedFailure; class OutOfLineUnboxDouble; class OutOfLineCache; +class OutOfLineStoreElementHole; class CodeGenerator : public CodeGeneratorSpecific { bool generateArgumentsChecks(); bool generateInvalidateEpilogue(); bool generateBody(); public: @@ -114,24 +115,29 @@ class CodeGenerator : public CodeGenerat bool visitCallGetProperty(LCallGetProperty *lir); bool visitCallGetName(LCallGetName *lir); bool visitCallGetNameTypeOf(LCallGetNameTypeOf *lir); bool visitCallGetElement(LCallGetElement *lir); bool visitCallSetElement(LCallSetElement *lir); bool visitThrow(LThrow *lir); bool visitLoadElementV(LLoadElementV *load); bool visitLoadElementHole(LLoadElementHole *lir); + bool visitStoreElementT(LStoreElementT *lir); + bool visitStoreElementV(LStoreElementV *lir); + bool visitStoreElementHoleT(LStoreElementHoleT *lir); + bool visitStoreElementHoleV(LStoreElementHoleV *lir); bool visitCheckOverRecursed(LCheckOverRecursed *lir); bool visitCheckOverRecursedFailure(CheckOverRecursedFailure *ool); bool visitUnboxDouble(LUnboxDouble *lir); bool visitOutOfLineUnboxDouble(OutOfLineUnboxDouble *ool); bool visitOutOfLineCacheGetProperty(OutOfLineCache *ool); bool visitOutOfLineCacheSetProperty(OutOfLineCache *ool); + bool visitOutOfLineStoreElementHole(OutOfLineStoreElementHole *ool); bool visitGetPropertyCacheV(LGetPropertyCacheV *ins) { return visitCache(ins); } bool visitGetPropertyCacheT(LGetPropertyCacheT *ins) { return visitCache(ins); } bool visitCacheSetPropertyV(LCacheSetPropertyV *ins) {
--- a/js/src/ion/IonBuilder.cpp +++ b/js/src/ion/IonBuilder.cpp @@ -3194,42 +3194,56 @@ IonBuilder::jsop_setelem_dense() MInstruction *idInt32 = MToInt32::New(id); current->add(idInt32); id = idInt32; // Get the elements vector. MElements *elements = MElements::New(obj); current->add(elements); - // Read and check length. - - MInitializedLength *initLength = MInitializedLength::New(elements); - current->add(initLength); - - MBoundsCheck *check = MBoundsCheck::New(id, initLength); - current->add(check); - - // Store the value. - MStoreElement *store = MStoreElement::New(elements, id, value); - current->add(store); - current->push(value); + // 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. + MStoreElementCommon *store; + if (oracle->setElementHasWrittenHoles(script, pc)) { + MStoreElementHole *ins = MStoreElementHole::New(obj, elements, id, value); + store = ins; + + current->add(ins); + current->push(value); + + if (!resumeAfter(ins)) + return false; + } else { + MInitializedLength *initLength = MInitializedLength::New(elements); + current->add(initLength); + + MBoundsCheck *check = MBoundsCheck::New(id, initLength); + current->add(check); + + MStoreElement *ins = MStoreElement::New(elements, id, value); + store = ins; + + current->add(ins); + current->push(value); + + if (!resumeAfter(ins)) + return false; + } #ifdef JSGC_INCREMENTAL // Determine whether a write barrier is required. - if (cx->compartment->needsBarrier() && - oracle->propertyWriteNeedsBarrier(script, pc, JSID_VOID)) - { + if (cx->compartment->needsBarrier() && oracle->propertyWriteNeedsBarrier(script, pc, JSID_VOID)) store->setNeedsBarrier(true); - } #endif if (elementType != MIRType_None && packed) store->setElementType(elementType); - return resumeAfter(store); + return true; } bool IonBuilder::jsop_length() { if (jsop_length_fastPath()) return true; return jsop_getprop(info().getAtom(pc));
--- a/js/src/ion/IonMacroAssembler.h +++ b/js/src/ion/IonMacroAssembler.h @@ -230,15 +230,36 @@ class MacroAssembler : public MacroAssem pushValue(val); framePushed_ += sizeof(Value); } void Push(JSValueType type, Register reg) { pushValue(type, reg); framePushed_ += sizeof(Value); } + + void bumpKey(Int32Key *key, int diff) { + if (key->isRegister()) + add32(Imm32(diff), key->reg()); + else + key->bumpConstant(diff); + } + + void storeKey(const Int32Key &key, const Address &dest) { + if (key.isRegister()) + store32(key.reg(), dest); + else + store32(Imm32(key.constant()), dest); + } + + void branchKey(Condition cond, const Address &dest, const Int32Key &key, Label *label) { + if (key.isRegister()) + branch32(cond, dest, key.reg(), label); + else + branch32(cond, dest, Imm32(key.constant()), label); + } }; } // namespace ion } // namespace js #endif // jsion_macro_assembler_h__
--- a/js/src/ion/IonRegisters.h +++ b/js/src/ion/IonRegisters.h @@ -277,25 +277,25 @@ class TypedOrValueRegister } TypedOrValueRegister(ValueOperand value) : type_(MIRType_Value) { dataValue() = value; } - MIRType type() { + MIRType type() const { return type_; } - bool hasTyped() { + bool hasTyped() const { return type() != MIRType_None && type() != MIRType_Value; } - bool hasValue() { + bool hasValue() const { return type() == MIRType_Value; } AnyRegister typedReg() { return dataTyped(); } ValueOperand valueReg() { @@ -349,16 +349,48 @@ class ConstantOrRegister return dataValue(); } TypedOrValueRegister reg() { return dataReg(); } }; +struct Int32Key { + bool isRegister_; + union { + Register reg_; + int32_t constant_; + }; + + explicit Int32Key(Register reg) + : isRegister_(true), reg_(reg) + { } + + explicit Int32Key(int32_t index) + : isRegister_(false), constant_(index) + { } + + inline void bumpConstant(int diff) { + JS_ASSERT(!isRegister_); + constant_ += diff; + } + inline Register reg() const { + JS_ASSERT(isRegister_); + return reg_; + } + inline int32_t constant() const { + JS_ASSERT(!isRegister_); + return constant_; + } + inline bool isRegister() const { + return isRegister_; + } +}; + template <typename T> class TypedRegisterSet { uint32 bits_; public: explicit TypedRegisterSet(uint32 bits) : bits_(bits)
--- a/js/src/ion/LIR-Common.h +++ b/js/src/ion/LIR-Common.h @@ -1181,16 +1181,76 @@ class LStoreElementT : public LInstructi const LAllocation *index() { return getOperand(1); } const LAllocation *value() { return getOperand(2); } }; +// Like LStoreElementV, but supports indexes >= initialized length. +class LStoreElementHoleV : public LInstructionHelper<0, 3 + BOX_PIECES, 0> +{ + public: + LIR_HEADER(StoreElementHoleV); + + LStoreElementHoleV(const LAllocation &object, const LAllocation &elements, + const LAllocation &index) { + setOperand(0, object); + setOperand(1, elements); + setOperand(2, index); + } + + static const size_t Value = 3; + + const MStoreElementHole *mir() const { + return mir_->toStoreElementHole(); + } + const LAllocation *object() { + return getOperand(0); + } + const LAllocation *elements() { + return getOperand(1); + } + const LAllocation *index() { + return getOperand(2); + } +}; + +// Like LStoreElementT, but supports indexes >= initialized length. +class LStoreElementHoleT : public LInstructionHelper<0, 4, 0> +{ + public: + LIR_HEADER(StoreElementHoleT); + + LStoreElementHoleT(const LAllocation &object, const LAllocation &elements, + const LAllocation &index, const LAllocation &value) { + setOperand(0, object); + setOperand(1, elements); + setOperand(2, index); + setOperand(3, value); + } + + const MStoreElementHole *mir() const { + return mir_->toStoreElementHole(); + } + const LAllocation *object() { + return getOperand(0); + } + const LAllocation *elements() { + return getOperand(1); + } + const LAllocation *index() { + return getOperand(2); + } + const LAllocation *value() { + return getOperand(3); + } +}; + // Load a boxed value from an object's fixed slot. class LLoadFixedSlotV : public LInstructionHelper<BOX_PIECES, 1, 0> { public: LIR_HEADER(LoadFixedSlotV); BOX_OUTPUT_ACCESSORS(); LLoadFixedSlotV(const LAllocation &object) {
--- a/js/src/ion/LOpcodes.h +++ b/js/src/ion/LOpcodes.h @@ -106,16 +106,18 @@ _(BoundsCheck) \ _(BoundsCheckRange) \ _(BoundsCheckLower) \ _(LoadElementV) \ _(LoadElementT) \ _(LoadElementHole) \ _(StoreElementV) \ _(StoreElementT) \ + _(StoreElementHoleV) \ + _(StoreElementHoleT) \ _(LoadFixedSlotV) \ _(LoadFixedSlotT) \ _(StoreFixedSlotV) \ _(StoreFixedSlotT) \ _(FunctionEnvironment) \ _(GetPropertyCacheV) \ _(GetPropertyCacheT) \ _(CallGetProperty) \
--- a/js/src/ion/Lowering.cpp +++ b/js/src/ion/Lowering.cpp @@ -910,38 +910,71 @@ LIRGenerator::visitStoreElement(MStoreEl JS_ASSERT(ins->elements()->type() == MIRType_Elements); JS_ASSERT(ins->index()->type() == MIRType_Int32); #ifdef JSGC_INCREMENTAL if (ins->needsBarrier() && !emitWriteBarrier(ins, ins->value())) return false; #endif + const LUse elements = useRegister(ins->elements()); + const LAllocation index = useRegisterOrConstant(ins->index()); + switch (ins->value()->type()) { case MIRType_Value: { - LInstruction *lir = new LStoreElementV(useRegister(ins->elements()), - useRegisterOrConstant(ins->index())); + LInstruction *lir = new LStoreElementV(elements, index); if (!useBox(lir, LStoreElementV::Value, ins->value())) return false; return add(lir, ins); } - case MIRType_Double: - return add(new LStoreElementT(useRegister(ins->elements()), - useRegisterOrConstant(ins->index()), - useRegister(ins->value())), ins); default: - return add(new LStoreElementT(useRegister(ins->elements()), - useRegisterOrConstant(ins->index()), - useRegisterOrConstant(ins->value())), ins); + { + const LAllocation value = useRegisterOrNonDoubleConstant(ins->value()); + return add(new LStoreElementT(elements, index, value), ins); + } } } bool +LIRGenerator::visitStoreElementHole(MStoreElementHole *ins) +{ + JS_ASSERT(ins->elements()->type() == MIRType_Elements); + JS_ASSERT(ins->index()->type() == MIRType_Int32); + +#ifdef JSGC_INCREMENTAL + if (ins->needsBarrier() && !emitWriteBarrier(ins, ins->value())) + return false; +#endif + + const LUse object = useRegister(ins->object()); + const LUse elements = useRegister(ins->elements()); + const LAllocation index = useRegisterOrConstant(ins->index()); + + LInstruction *lir; + switch (ins->value()->type()) { + case MIRType_Value: + lir = new LStoreElementHoleV(object, elements, index); + if (!useBox(lir, LStoreElementHoleV::Value, ins->value())) + return false; + break; + + default: + { + const LAllocation value = useRegisterOrNonDoubleConstant(ins->value()); + lir = new LStoreElementHoleT(object, elements, index, value); + break; + } + } + + return add(lir, ins) && assignSafepoint(lir, ins); +} + +bool LIRGenerator::visitLoadFixedSlot(MLoadFixedSlot *ins) { JS_ASSERT(ins->object()->type() == MIRType_Object); if (ins->type() == MIRType_Value) { LLoadFixedSlotV *lir = new LLoadFixedSlotV(useRegister(ins->object())); return defineBox(lir, ins); }
--- a/js/src/ion/Lowering.h +++ b/js/src/ion/Lowering.h @@ -152,16 +152,17 @@ class LIRGenerator : public LIRGenerator bool visitTypeBarrier(MTypeBarrier *ins); bool visitArrayLength(MArrayLength *ins); bool visitInitializedLength(MInitializedLength *ins); bool visitBoundsCheck(MBoundsCheck *ins); bool visitBoundsCheckLower(MBoundsCheckLower *ins); bool visitLoadElement(MLoadElement *ins); bool visitLoadElementHole(MLoadElementHole *ins); bool visitStoreElement(MStoreElement *ins); + bool visitStoreElementHole(MStoreElementHole *ins); bool visitLoadFixedSlot(MLoadFixedSlot *ins); bool visitStoreFixedSlot(MStoreFixedSlot *ins); bool visitGetPropertyCache(MGetPropertyCache *ins); bool visitGuardClass(MGuardClass *ins); bool visitCallGetProperty(MCallGetProperty *ins); bool visitCallGetName(MCallGetName *ins); bool visitCallGetNameTypeOf(MCallGetNameTypeOf *ins); bool visitCallGetElement(MCallGetElement *ins);
--- a/js/src/ion/MIR.h +++ b/js/src/ion/MIR.h @@ -2352,71 +2352,129 @@ class MLoadElementHole bool needsHoleCheck() const { return needsHoleCheck_; } AliasSet getAliasSet() const { return AliasSet::Load(AliasSet::Element); } }; +class MStoreElementCommon +{ + bool needsBarrier_; + MIRType elementType_; + + protected: + MStoreElementCommon() + : needsBarrier_(false), + elementType_(MIRType_None) + { } + + public: + MIRType elementType() const { + return elementType_; + } + void setElementType(MIRType elementType) { + JS_ASSERT(elementType != MIRType_None); + elementType_ = elementType; + } + bool needsBarrier() const { + return needsBarrier_; + } + void setNeedsBarrier(bool needsBarrier) { + needsBarrier_ = needsBarrier; + } +}; + // Store a value to a dense array slots vector. class MStoreElement : public MAryInstruction<3>, + public MStoreElementCommon, public ObjectPolicy { - MIRType elementType_; - bool needsBarrier_; - - MStoreElement(MDefinition *elements, MDefinition *index, MDefinition *value) - : needsBarrier_(false) - { + MStoreElement(MDefinition *elements, MDefinition *index, MDefinition *value) { initOperand(0, elements); initOperand(1, index); initOperand(2, value); JS_ASSERT(elements->type() == MIRType_Elements); JS_ASSERT(index->type() == MIRType_Int32); } public: INSTRUCTION_HEADER(StoreElement); static MStoreElement *New(MDefinition *elements, MDefinition *index, MDefinition *value) { return new MStoreElement(elements, index, value); } - - TypePolicy *typePolicy() { - return this; - } MDefinition *elements() const { return getOperand(0); } MDefinition *index() const { return getOperand(1); } MDefinition *value() const { return getOperand(2); } - MIRType elementType() const { - return elementType_; - } - void setElementType(MIRType elementType) { - JS_ASSERT(elementType != MIRType_None); - elementType_ = elementType; - } - bool needsBarrier() const { - return needsBarrier_; - } - void setNeedsBarrier(bool needsBarrier) { - needsBarrier_ = needsBarrier; + TypePolicy *typePolicy() { + return this; } AliasSet getAliasSet() const { return AliasSet::Store(AliasSet::Element); } }; +// Like MStoreElement, but supports indexes >= initialized length. The downside +// is that we cannot hoist the elements vector and bounds check, since this +// instruction may update the (initialized) length and reallocate the elements +// vector. +class MStoreElementHole + : public MAryInstruction<4>, + public MStoreElementCommon, + public ObjectPolicy +{ + MStoreElementHole(MDefinition *object, MDefinition *elements, + MDefinition *index, MDefinition *value) { + initOperand(0, object); + initOperand(1, elements); + initOperand(2, index); + initOperand(3, value); + JS_ASSERT(elements->type() == MIRType_Elements); + JS_ASSERT(index->type() == MIRType_Int32); + } + + public: + INSTRUCTION_HEADER(StoreElementHole); + + static MStoreElementHole *New(MDefinition *object, MDefinition *elements, + MDefinition *index, MDefinition *value) { + return new MStoreElementHole(object, elements, index, value); + } + + MDefinition *object() const { + return getOperand(0); + } + MDefinition *elements() const { + return getOperand(1); + } + MDefinition *index() const { + return getOperand(2); + } + MDefinition *value() const { + return getOperand(3); + } + TypePolicy *typePolicy() { + return this; + } + AliasSet getAliasSet() const { + // StoreElementHole can update the initialized length, the array length + // or reallocate obj->elements. + return AliasSet::Store(AliasSet::Element | AliasSet::ObjectFields); + } +}; + class MLoadFixedSlot : public MUnaryInstruction, public ObjectPolicy { size_t slot_; MLoadFixedSlot(MDefinition *obj, size_t slot) : MUnaryInstruction(obj), slot_(slot) { setResultType(MIRType_Value);
--- a/js/src/ion/MOpcodes.h +++ b/js/src/ion/MOpcodes.h @@ -100,16 +100,17 @@ namespace ion { _(GuardClass) \ _(ArrayLength) \ _(InitializedLength) \ _(BoundsCheck) \ _(BoundsCheckLower) \ _(LoadElement) \ _(LoadElementHole) \ _(StoreElement) \ + _(StoreElementHole) \ _(LoadFixedSlot) \ _(StoreFixedSlot) \ _(CallGetProperty) \ _(CallGetName) \ _(CallGetNameTypeOf) \ _(CallGetElement) \ _(CallSetElement) \ _(GenericSetProperty) \
--- a/js/src/ion/TypeOracle.cpp +++ b/js/src/ion/TypeOracle.cpp @@ -241,16 +241,22 @@ TypeInferenceOracle::elementWriteIsDense bool TypeInferenceOracle::elementWriteIsPacked(JSScript *script, jsbytecode *pc) { types::TypeSet *types = script->analysis()->poppedTypes(pc, 2); return !types->hasObjectFlags(cx, types::OBJECT_FLAG_NON_PACKED_ARRAY); } +bool +TypeInferenceOracle::setElementHasWrittenHoles(JSScript *script, jsbytecode *pc) +{ + return script->analysis()->getCode(pc).arrayWriteHole; +} + MIRType TypeInferenceOracle::elementWrite(JSScript *script, jsbytecode *pc) { types::TypeSet *objTypes = script->analysis()->poppedTypes(pc, 2); MIRType elementType = MIRType_None; unsigned count = objTypes->getObjectCount(); for (unsigned i = 0; i < count; i++) {
--- a/js/src/ion/TypeOracle.h +++ b/js/src/ion/TypeOracle.h @@ -119,16 +119,19 @@ class TypeOracle return NULL; } virtual bool elementReadIsDense(JSScript *script, jsbytecode *pc) { return false; } virtual bool elementReadIsPacked(JSScript *script, jsbytecode *pc) { return false; } + virtual bool setElementHasWrittenHoles(JSScript *script, jsbytecode *pc) { + return true; + } virtual bool elementWriteIsDense(JSScript *script, jsbytecode *pc) { return false; } virtual bool elementWriteIsPacked(JSScript *script, jsbytecode *pc) { return false; } virtual bool propertyWriteCanSpecialize(JSScript *script, jsbytecode *pc) { return true; @@ -212,16 +215,17 @@ class TypeInferenceOracle : public TypeO types::TypeSet *propertyReadBarrier(JSScript *script, jsbytecode *pc); types::TypeSet *globalPropertyWrite(JSScript *script, jsbytecode *pc, jsid id, bool *canSpecialize); types::TypeSet *returnTypeSet(JSScript *script, jsbytecode *pc, types::TypeSet **barrier); types::TypeSet *getCallTarget(JSScript *caller, uint32 argc, jsbytecode *pc); bool elementReadIsDense(JSScript *script, jsbytecode *pc); bool elementReadIsPacked(JSScript *script, jsbytecode *pc); bool elementWriteIsDense(JSScript *script, jsbytecode *pc); bool elementWriteIsPacked(JSScript *script, jsbytecode *pc); + bool setElementHasWrittenHoles(JSScript *script, jsbytecode *pc); bool propertyWriteCanSpecialize(JSScript *script, jsbytecode *pc); bool propertyWriteNeedsBarrier(JSScript *script, jsbytecode *pc, jsid id); MIRType elementWrite(JSScript *script, jsbytecode *pc); bool arrayPrototypeHasIndexedProperty(); bool canInlineCalls(); bool canEnterInlinedScript(JSScript *inlineScript); };
--- a/js/src/ion/arm/CodeGenerator-arm.cpp +++ b/js/src/ion/arm/CodeGenerator-arm.cpp @@ -1123,69 +1123,53 @@ CodeGeneratorARM::visitLoadElementT(LLoa masm.ma_ldr(DTRAddr(base, DtrRegImmShift(ToRegister(load->index()), LSL, 3)), ToRegister(load->output())); } } JS_ASSERT(!load->mir()->needsHoleCheck()); return true; } -bool -CodeGeneratorARM::visitStoreElementV(LStoreElementV *store) +void +CodeGeneratorARM::storeElementTyped(const LAllocation *value, MIRType valueType, MIRType elementType, + const Register &elements, const LAllocation *index) { - const ValueOperand out = ToOutValue(store); - Register base = ToRegister(store->elements()); - if (store->index()->isConstant()) - masm.storeValue(out, Address(base, ToInt32(store->index()) * sizeof(Value))); - else - masm.storeValue(out, base, ToRegister(store->index())); - return true; -} - -bool -CodeGeneratorARM::visitStoreElementT(LStoreElementT *store) -{ - const LAllocation *value = store->value(); - MIRType valueType = store->mir()->value()->type(); - Register base = ToRegister(store->elements()); - if (store->index()->isConstant()) { - int32 index = ToInt32(store->index()) * sizeof(Value); - Address dest = Address(base, index); + if (index->isConstant()) { + Address dest = Address(elements, ToInt32(index) * sizeof(Value)); if (valueType == MIRType_Double) { masm.ma_vstr(ToFloatRegister(value), Operand(dest)); - return true; + return; } // Store the type tag if needed. - if (valueType != store->mir()->elementType()) + if (valueType != elementType) masm.storeTypeTag(ImmType(ValueTypeFromMIRType(valueType)), dest); // Store the payload. if (value->isConstant()) masm.storePayload(*value->toConstant(), dest); else masm.storePayload(ToRegister(value), dest); } else { - Register index = ToRegister(store->index()); + Register indexReg = ToRegister(index); if (valueType == MIRType_Double) { - masm.ma_vstr(ToFloatRegister(value), base, index); - return true; + masm.ma_vstr(ToFloatRegister(value), elements, indexReg); + return; } // Store the type tag if needed. - if (valueType != store->mir()->elementType()) - masm.storeTypeTag(ImmType(ValueTypeFromMIRType(valueType)), base, index); + if (valueType != elementType) + masm.storeTypeTag(ImmType(ValueTypeFromMIRType(valueType)), elements, indexReg); // Store the payload. if (value->isConstant()) - masm.storePayload(*value->toConstant(), base, index); + masm.storePayload(*value->toConstant(), elements, index); else - masm.storePayload(ToRegister(value), base, index); + masm.storePayload(ToRegister(value), elements, indexReg); } - return false; } bool CodeGeneratorARM::visitGuardShape(LGuardShape *guard) { Register obj = ToRegister(guard->input()); Register tmp = ToRegister(guard->tempInt()); masm.ma_ldr(DTRAddr(obj, DtrOffImm(JSObject::offsetOfShape())), tmp);
--- a/js/src/ion/arm/CodeGenerator-arm.h +++ b/js/src/ion/arm/CodeGenerator-arm.h @@ -127,16 +127,19 @@ class CodeGeneratorARM : public CodeGene protected: ValueOperand ToValue(LInstruction *ins, size_t pos); ValueOperand ToOutValue(LInstruction *ins); // Functions for LTestVAndBranch. Register splitTagForTest(const ValueOperand &value); Assembler::Condition testStringTruthy(bool truthy, const ValueOperand &value); + void storeElementTyped(const LAllocation *value, MIRType valueType, MIRType elementType, + const Register &elements, const LAllocation *index); + protected: void linkAbsoluteLabels(); public: CodeGeneratorARM(MIRGenerator *gen, LIRGraph &graph); public: bool visitBox(LBox *box); @@ -147,18 +150,16 @@ class CodeGeneratorARM : public CodeGene bool visitDouble(LDouble *ins); bool visitLoadSlotV(LLoadSlotV *load); bool visitLoadSlotT(LLoadSlotT *load); bool visitStoreSlotT(LStoreSlotT *load); bool visitWriteBarrierT(LWriteBarrierT *barrier); bool visitLoadElementT(LLoadElementT *load); - bool visitStoreElementV(LStoreElementV *store); - bool visitStoreElementT(LStoreElementT *store); bool visitGuardShape(LGuardShape *guard); bool visitGuardClass(LGuardClass *guard); bool visitImplicitThis(LImplicitThis *lir); bool visitRecompileCheck(LRecompileCheck *lir); };
--- a/js/src/ion/arm/MacroAssembler-arm.cpp +++ b/js/src/ion/arm/MacroAssembler-arm.cpp @@ -1144,16 +1144,29 @@ MacroAssemblerARMCompat::loadPtr(const I void MacroAssemblerARMCompat::store32(Register src, const ImmWord &imm) { storePtr(src, imm); } void +MacroAssemblerARMCompat::store32(Register src, const Address &address) +{ + storePtr(src, address); +} + +void +MacroAssemblerARMCompat::store32(Imm32 src, const Address &address) +{ + move32(src, ScratchRegister); + storePtr(ScratchRegister, address); +} + +void MacroAssemblerARMCompat::storePtr(Register src, const Address &address) { ma_str(src, Operand(address)); } void MacroAssemblerARMCompat::storePtr(Register src, const ImmWord &imm) {
--- a/js/src/ion/arm/MacroAssembler-arm.h +++ b/js/src/ion/arm/MacroAssembler-arm.h @@ -491,16 +491,24 @@ class MacroAssemblerARMCompat : public M void branch32(Condition cond, Register lhs, Register rhs, Label *label) { ma_cmp(rhs, lhs); ma_b(label, cond); } void branch32(Condition cond, Register lhs, Imm32 imm, Label *label) { ma_cmp(lhs, imm); ma_b(label, InvertCondition(cond)); } + void branch32(Condition cond, const Address &lhs, Register rhs, Label *label) { + move32(lhs, ScratchRegister); + branch32(cond, ScratchRegister, rhs, label); + } + void branch32(Condition cond, const Address &lhs, Imm32 rhs, Label *label) { + move32(lhs, ScratchRegister); + branch32(cond, ScratchRegister, rhs, label); + } template<typename T> void branchTestDouble(Condition cond, const T & t, Label *label) { Condition c = testDouble(cond, t); ma_b(label, c); } template<typename T> void branchTestNull(Condition cond, const T & t, Label *label) { Condition c = testNull(cond, t); @@ -690,16 +698,18 @@ class MacroAssemblerARMCompat : public M void movePtr(const Address &src, const Register &dest); void load32(const Address &address, const Register &dest); void load32(const ImmWord &imm, const Register &dest); void loadPtr(const Address &address, const Register &dest); void loadPtr(const ImmWord &imm, const Register &dest); void store32(Register src, const ImmWord &imm); + void store32(Register src, const Address &address); + void store32(Imm32 src, const Address &address); void storePtr(Register src, const Address &address); void storePtr(Register src, const ImmWord &imm); void cmp32(const Register &lhs, const Imm32 &rhs); void cmp32(const Register &lhs, const Register &rhs); void cmpPtr(const Register &lhs, const ImmWord &rhs); void subPtr(Imm32 imm, const Register dest);
--- a/js/src/ion/shared/Assembler-x86-shared.h +++ b/js/src/ion/shared/Assembler-x86-shared.h @@ -447,16 +447,25 @@ class AssemblerX86Shared case Operand::ADDRESS: masm.cmpl_im(imm.value, op.address()); break; #endif default: JS_NOT_REACHED("unexpected operand kind"); } } + void cmpl(const Operand &lhs, const Register &rhs) { + switch (lhs.kind()) { + case Operand::REG_DISP: + masm.cmpl_rm(rhs.code(), lhs.disp(), lhs.base()); + break; + default: + JS_NOT_REACHED("unexpected operand kind"); + } + } void cmpl(const Operand &op, ImmWord imm) { switch (op.kind()) { case Operand::REG: masm.cmpl_ir(imm.value, op.reg()); break; case Operand::REG_DISP: masm.cmpl_im(imm.value, op.disp(), op.base()); break;
--- a/js/src/ion/shared/CodeGenerator-shared-inl.h +++ b/js/src/ion/shared/CodeGenerator-shared-inl.h @@ -40,16 +40,27 @@ * ***** END LICENSE BLOCK ***** */ #ifndef jsion_codegen_inl_h__ #define jsion_codegen_inl_h__ namespace js { namespace ion { +static inline int32 +ToInt32(const LAllocation *a) +{ + if (a->isConstantValue()) + return a->toConstant()->toInt32(); + if (a->isConstantIndex()) + return a->toConstantIndex()->index(); + JS_NOT_REACHED("this is not a constant!"); + return -1; +} + static inline Register ToRegister(const LAllocation &a) { JS_ASSERT(a.isGeneralReg()); return a.toGeneralReg()->reg(); } static inline Register @@ -99,16 +110,24 @@ ToAnyRegister(const LAllocation *a) } static inline AnyRegister ToAnyRegister(const LDefinition *def) { return ToAnyRegister(def->output()); } +static inline Int32Key +ToInt32Key(const LAllocation *a) +{ + if (a->isConstant()) + return Int32Key(ToInt32(a)); + return Int32Key(ToRegister(a)); +} + static inline ValueOperand GetValueOutput(LInstruction *ins) { #if defined(JS_NUNBOX32) return ValueOperand(ToRegister(ins->getDef(TYPE_INDEX)), ToRegister(ins->getDef(PAYLOAD_INDEX))); #elif defined(JS_PUNBOX64) return ValueOperand(ToRegister(ins->getDef(0)));
--- a/js/src/ion/shared/CodeGenerator-shared.h +++ b/js/src/ion/shared/CodeGenerator-shared.h @@ -81,27 +81,16 @@ class CodeGeneratorShared : public LInst js::Vector<OsiIndex, 0, SystemAllocPolicy> osiIndices_; // Mapping from bailout table ID to an offset in the snapshot buffer. js::Vector<SnapshotOffset, 0, SystemAllocPolicy> bailouts_; // Vector of information about generated polymorphic inline caches. js::Vector<IonCache, 0, SystemAllocPolicy> cacheList_; - static inline int32 ToInt32(const LAllocation *a) { - if (a->isConstantValue()) { - return a->toConstant()->toInt32(); - } - if (a->isConstantIndex()) { - return a->toConstantIndex()->index(); - } - JS_NOT_REACHED("this is not a constant!"); - return -1; - } - protected: // The offset of the first instruction of the OSR entry block from the // beginning of the code buffer. size_t osrEntryOffset_; inline void setOsrEntryOffset(size_t offset) { JS_ASSERT(osrEntryOffset_ == 0); osrEntryOffset_ = offset;
--- a/js/src/ion/shared/Lowering-shared-inl.h +++ b/js/src/ion/shared/Lowering-shared-inl.h @@ -284,16 +284,25 @@ LIRGeneratorShared::useOrConstant(MDefin LAllocation LIRGeneratorShared::useRegisterOrConstant(MDefinition *mir) { if (mir->isConstant()) return LAllocation(mir->toConstant()->vp()); return use(mir, LUse(LUse::REGISTER)); } + +LAllocation +LIRGeneratorShared::useRegisterOrNonDoubleConstant(MDefinition *mir) +{ + if (mir->isConstant() && mir->type() != MIRType_Double) + return LAllocation(mir->toConstant()->vp()); + return use(mir, LUse(LUse::REGISTER)); +} + #if defined(JS_CPU_ARM) LAllocation LIRGeneratorShared::useAnyOrConstant(MDefinition *mir) { return useRegisterOrConstant(mir); } #else LAllocation
--- a/js/src/ion/shared/Lowering-shared.h +++ b/js/src/ion/shared/Lowering-shared.h @@ -108,16 +108,17 @@ class LIRGeneratorShared : public MInstr inline LUse useFixed(MDefinition *mir, Register reg); inline LUse useFixed(MDefinition *mir, FloatRegister reg); inline LAllocation useOrConstant(MDefinition *mir); // "Any" is architecture dependent, and will include registers and stack slots on X86, // and only registers on ARM. inline LAllocation useAnyOrConstant(MDefinition *mir); inline LAllocation useKeepaliveOrConstant(MDefinition *mir); inline LAllocation useRegisterOrConstant(MDefinition *mir); + inline LAllocation useRegisterOrNonDoubleConstant(MDefinition *mir); #ifdef JS_NUNBOX32 inline LUse useType(MDefinition *mir, LUse::Policy policy); inline LUse usePayload(MDefinition *mir, LUse::Policy policy); inline LUse usePayloadAtStart(MDefinition *mir, LUse::Policy policy); inline LUse usePayloadInRegisterAtStart(MDefinition *mir); // Adds a box input to an instruction, setting operand |n| to the type and
--- a/js/src/ion/shared/MacroAssembler-x86-shared.h +++ b/js/src/ion/shared/MacroAssembler-x86-shared.h @@ -122,16 +122,24 @@ class MacroAssemblerX86Shared : public A } void add32(Imm32 imm, Register dest) { addl(imm, dest); } void sub32(Imm32 imm, Register dest) { subl(imm, dest); } + void branch32(Condition cond, const Address &lhs, const Register &rhs, Label *label) { + cmpl(Operand(lhs), rhs); + j(cond, label); + } + void branch32(Condition cond, const Address &lhs, Imm32 imm, Label *label) { + cmpl(Operand(lhs), imm); + j(cond, label); + } void branch32(Condition cond, const Register &lhs, Imm32 imm, Label *label) { cmpl(lhs, imm); j(cond, label); } void branch32(Condition cond, const Register &lhs, const Register &rhs, Label *label) { cmpl(lhs, rhs); j(cond, label); } @@ -179,16 +187,22 @@ class MacroAssemblerX86Shared : public A Condition testDoubleTruthy(bool truthy, const FloatRegister ®) { xorpd(ScratchFloatReg, ScratchFloatReg); ucomisd(ScratchFloatReg, reg); return truthy ? NonZero : Zero; } void load32(const Address &address, Register dest) { movl(Operand(address), dest); } + void store32(Imm32 src, const Address &dest) { + movl(src, Operand(dest)); + } + void store32(const Register &src, const Address &dest) { + movl(src, Operand(dest)); + } void callWithExitFrame(IonCode *target) { uint32 descriptor = MakeFrameDescriptor(framePushed(), IonFrame_JS); Push(Imm32(descriptor)); call(target); } void callIon(const Register &callee) { call(callee); }
--- a/js/src/ion/x64/CodeGenerator-x64.cpp +++ b/js/src/ion/x64/CodeGenerator-x64.cpp @@ -296,37 +296,23 @@ CodeGeneratorX64::visitLoadElementT(LLoa { Operand source = createArrayElementOperand(ToRegister(load->elements()), load->index()); loadUnboxedValue(source, load->mir()->type(), load->output()); JS_ASSERT(!load->mir()->needsHoleCheck()); return true; } -bool -CodeGeneratorX64::visitStoreElementV(LStoreElementV *store) + +void +CodeGeneratorX64::storeElementTyped(const LAllocation *value, MIRType valueType, MIRType elementType, + const Register &elements, const LAllocation *index) { - Operand dest = createArrayElementOperand(ToRegister(store->elements()), store->index()); - const ValueOperand value = ToValue(store, LStoreElementV::Value); - - masm.storeValue(value, dest); - return true; -} - -bool -CodeGeneratorX64::visitStoreElementT(LStoreElementT *store) -{ - Operand dest = createArrayElementOperand(ToRegister(store->elements()), store->index()); - - const LAllocation *value = store->value(); - MIRType valueType = store->mir()->value()->type(); - MIRType elementType = store->mir()->elementType(); - + Operand dest = createArrayElementOperand(elements, index); storeUnboxedValue(value, valueType, dest, elementType); - return true; } bool CodeGeneratorX64::visitWriteBarrierV(LWriteBarrierV *barrier) { // TODO: Perform C++ call to some WriteBarrier stub. // For now, we just guard and breakpoint on failure.
--- a/js/src/ion/x64/CodeGenerator-x64.h +++ b/js/src/ion/x64/CodeGenerator-x64.h @@ -60,33 +60,34 @@ class CodeGeneratorX64 : public CodeGene // This returns the tag in ScratchReg. Assembler::Condition testStringTruthy(bool truthy, const ValueOperand &value); void loadUnboxedValue(Operand source, MIRType type, const LDefinition *dest); void storeUnboxedValue(const LAllocation *value, MIRType valueType, Operand dest, MIRType slotType); + void storeElementTyped(const LAllocation *value, MIRType valueType, MIRType elementType, + const Register &elements, const LAllocation *index); + public: CodeGeneratorX64(MIRGenerator *gen, LIRGraph &graph); public: bool visitValue(LValue *value); bool visitOsrValue(LOsrValue *value); bool visitBox(LBox *box); bool visitUnbox(LUnbox *unbox); bool visitDouble(LDouble *ins); bool visitLoadSlotV(LLoadSlotV *ins); bool visitLoadSlotT(LLoadSlotT *load); bool visitStoreSlotT(LStoreSlotT *store); bool visitWriteBarrierV(LWriteBarrierV *barrier); bool visitWriteBarrierT(LWriteBarrierT *barrier); bool visitLoadElementT(LLoadElementT *load); - bool visitStoreElementV(LStoreElementV *store); - bool visitStoreElementT(LStoreElementT *store); bool visitImplicitThis(LImplicitThis *lir); bool visitRecompileCheck(LRecompileCheck *lir); }; typedef CodeGeneratorX64 CodeGeneratorSpecific; } // namespace ion } // namespace js
--- a/js/src/ion/x64/MacroAssembler-x64.h +++ b/js/src/ion/x64/MacroAssembler-x64.h @@ -129,16 +129,19 @@ class MacroAssemblerX64 : public MacroAs } void storeValue(const Value &val, Address dest) { jsval_layout jv = JSVAL_TO_IMPL(val); movq(ImmWord(jv.asBits), ScratchReg); if (val.isMarkable()) writeDataRelocation(masm.currentOffset()); movq(ScratchReg, Operand(dest)); } + void storeValue(ValueOperand val, BaseIndex dest) { + storeValue(val, Operand(dest)); + } void loadValue(Operand src, ValueOperand val) { movq(src, val.valueReg()); } void loadValue(Address src, ValueOperand val) { loadValue(Operand(src), val); } void loadValue(const BaseIndex &src, ValueOperand val) { loadValue(Operand(src), val);
--- a/js/src/ion/x86/CodeGenerator-x86.cpp +++ b/js/src/ion/x86/CodeGenerator-x86.cpp @@ -269,50 +269,36 @@ CodeGeneratorX86::visitLoadElementT(LLoa masm.loadInt32OrDouble(source, ToFloatRegister(load->output())); else masm.movl(masm.ToPayload(source), ToRegister(load->output())); JS_ASSERT(!load->mir()->needsHoleCheck()); return true; } -bool -CodeGeneratorX86::visitStoreElementV(LStoreElementV *store) +void +CodeGeneratorX86::storeElementTyped(const LAllocation *value, MIRType valueType, MIRType elementType, + const Register &elements, const LAllocation *index) { - Operand dest = createArrayElementOperand(ToRegister(store->elements()), store->index()); - const ValueOperand value = ToValue(store, LStoreElementV::Value); - - masm.storeValue(value, dest); - return true; -} - -bool -CodeGeneratorX86::visitStoreElementT(LStoreElementT *store) -{ - Operand dest = createArrayElementOperand(ToRegister(store->elements()), store->index()); - - const LAllocation *value = store->value(); - MIRType valueType = store->mir()->value()->type(); + Operand dest = createArrayElementOperand(elements, index); if (valueType == MIRType_Double) { masm.movsd(ToFloatRegister(value), dest); - return true; + return; } // Store the type tag if needed. - if (valueType != store->mir()->elementType()) + if (valueType != elementType) masm.storeTypeTag(ImmType(ValueTypeFromMIRType(valueType)), dest); // Store the payload. if (value->isConstant()) masm.storePayload(*value->toConstant(), dest); else masm.storePayload(ToRegister(value), dest); - - return true; } bool CodeGeneratorX86::visitWriteBarrierV(LWriteBarrierV *barrier) { // TODO: Perform C++ call to some WriteBarrier stub. // For now, we just guard and breakpoint on failure.
--- a/js/src/ion/x86/CodeGenerator-x86.h +++ b/js/src/ion/x86/CodeGenerator-x86.h @@ -76,16 +76,19 @@ class CodeGeneratorX86 : public CodeGene protected: ValueOperand ToValue(LInstruction *ins, size_t pos); ValueOperand ToOutValue(LInstruction *ins); // Functions for LTestVAndBranch. Assembler::Condition testStringTruthy(bool truthy, const ValueOperand &value); + void storeElementTyped(const LAllocation *value, MIRType valueType, MIRType elementType, + const Register &elements, const LAllocation *index); + protected: void linkAbsoluteLabels(); public: CodeGeneratorX86(MIRGenerator *gen, LIRGraph &graph); public: bool visitBox(LBox *box); @@ -95,18 +98,16 @@ class CodeGeneratorX86 : public CodeGene bool visitOsrValue(LOsrValue *value); bool visitDouble(LDouble *ins); bool visitLoadSlotV(LLoadSlotV *load); bool visitLoadSlotT(LLoadSlotT *load); bool visitStoreSlotT(LStoreSlotT *store); bool visitWriteBarrierV(LWriteBarrierV *barrier); bool visitWriteBarrierT(LWriteBarrierT *barrier); bool visitLoadElementT(LLoadElementT *load); - bool visitStoreElementV(LStoreElementV *store); - bool visitStoreElementT(LStoreElementT *store); bool visitImplicitThis(LImplicitThis *lir); bool visitRecompileCheck(LRecompileCheck *lir); }; typedef CodeGeneratorX86 CodeGeneratorSpecific; } // namespace ion } // namespace js
--- a/js/src/ion/x86/MacroAssembler-x86.h +++ b/js/src/ion/x86/MacroAssembler-x86.h @@ -140,16 +140,19 @@ class MacroAssemblerX86 : public MacroAs storeTypeTag(ImmTag(JSVAL_TYPE_TO_TAG(type)), Operand(dest)); storePayload(reg, Operand(dest)); } void storeValue(const Value &val, Address dest) { jsval_layout jv = JSVAL_TO_IMPL(val); storeTypeTag(ImmTag(jv.s.tag), Operand(dest)); storePayload(val, Operand(dest)); } + void storeValue(ValueOperand val, BaseIndex dest) { + storeValue(val, Operand(dest)); + } void loadValue(Operand src, ValueOperand val) { Operand payload = ToPayload(src); Operand type = ToType(src); // Ensure that loading the payload does not erase the pointer to the // Value in memory. if (Register::FromCode(type.base()) != val.payloadReg()) { movl(payload, val.payloadReg()); @@ -171,17 +174,20 @@ class MacroAssemblerX86 : public MacroAs } void popValue(ValueOperand val) { pop(val.payloadReg()); pop(val.typeReg()); } void pushValue(const Value &val) { jsval_layout jv = JSVAL_TO_IMPL(val); push(Imm32(jv.s.tag)); - push(Imm32(jv.s.payload.i32)); + if (val.isGCThing()) + push(ImmGCPtr(reinterpret_cast<gc::Cell *>(val.toGCThing()))); + else + push(Imm32(jv.s.payload.i32)); } void pushValue(JSValueType type, Register reg) { push(ImmTag(JSVAL_TYPE_TO_TAG(type))); push(reg); } void storePayload(const Value &val, Operand dest) { jsval_layout jv = JSVAL_TO_IMPL(val); if (val.isMarkable())
new file mode 100644 --- /dev/null +++ b/js/src/jit-test/tests/ion/setelem-hole.js @@ -0,0 +1,68 @@ +// Storing a typed value. +function test1() { + var a = []; + for (var i=0; i<130; i++) { + a[i] = i + 1; + } + return a; +} + +var arr = test1(); +assertEq(arr.length, 130); + +for (var i=0; i<130; i++) + assertEq(arr[i], i + 1); + +// Storing a Value. +function getValue(x) { + var y = x & 0x3; + if (y == 0) return null; + if (y == 1) return true; + if (y == 2) return 1.23; + if (y == 3) return Math; + assertEq(0, 1); +} +getValue(0); +getValue(1); + +function test2() { + var a = []; + for (var i=0; i<130; i++) { + a[i] = getValue(i); + } + return a; +} + +var arr = test2(); +assertEq(arr.length, 130); + +for (var i=0; i<130; i++) + assertEq(arr[i], getValue(i)); + +// Make sure the length-property is not updated if it's greater than +// the (new) initialized length. +function test3(arr, start, end) { + for (var i=start; i<end; i++) { + arr[i] = 10; + } +} +var a = new Array(200); +test3(a, 10, 130); +assertEq(a.length, 200); + +for (var i=10; i<130; i++) + assertEq(a[i], 10); + +test3(a, 130, 220); +assertEq(a.length, 220); + +// Test constant index. +function test4() { + var a = [0, 1, 2, 3, 4, 5]; + for (var i=0; i<150; i++) { + a[6] = i; + } + return a; +} +var arr = test4(); +assertEq(arr[6], 149);