author | Nicolas B. Pierron <nicolas.b.pierron@mozilla.com> |
Thu, 11 Jun 2015 14:30:33 +0200 | |
changeset 248314 | ace9cd550bf13f9b4ade331380bb1b1a1f2f419f |
parent 248313 | 02dcf30ba6acc3812aed3a3406b17fa1696d065c |
child 248315 | 46958967c5226d14b405af5e1f12c25414caafee |
push id | 28893 |
push user | kwierso@gmail.com |
push date | Fri, 12 Jun 2015 00:02:58 +0000 |
treeherder | autoland@8cf9d3e497f9 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | bhackett |
bugs | 1166711 |
milestone | 41.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/jit/MIR.cpp +++ b/js/src/jit/MIR.cpp @@ -3924,59 +3924,123 @@ bool MCreateThisWithTemplate::canRecoverOnBailout() const { MOZ_ASSERT(templateObject()->is<PlainObject>() || templateObject()->is<UnboxedPlainObject>()); MOZ_ASSERT_IF(templateObject()->is<PlainObject>(), !templateObject()->as<PlainObject>().denseElementsAreCopyOnWrite()); return true; } -MObjectState::MObjectState(MDefinition* obj) +bool +OperandIndexMap::init(TempAllocator& alloc, JSObject* templateObject) +{ + const UnboxedLayout& layout = + templateObject->as<UnboxedPlainObject>().layoutDontCheckGeneration(); + + // 0 is used as an error code. + const UnboxedLayout::PropertyVector& properties = layout.properties(); + MOZ_ASSERT(properties.length() < 255); + + // Allocate an array of indexes, where the top of each field correspond to + // the index of the operand in the MObjectState instance. + if (!map.init(alloc, layout.size())) + return false; + + // Reset all indexes to 0, which is an error code. + for (size_t i = 0; i < map.length(); i++) + map[i] = 0; + + // Map the property offsets to the indexes of MObjectState operands. + uint8_t index = 1; + for (size_t i = 0; i < properties.length(); i++, index++) + map[properties[i].offset] = index; + + return true; +} + +MObjectState::MObjectState(MObjectState* state) + : numSlots_(state->numSlots_), + numFixedSlots_(state->numFixedSlots_), + operandIndex_(state->operandIndex_) { // This instruction is only used as a summary for bailout paths. setResultType(MIRType_Object); setRecoveredOnBailout(); - NativeObject* templateObject = nullptr; +} + +MObjectState::MObjectState(JSObject *templateObject, OperandIndexMap* operandIndex) +{ + // This instruction is only used as a summary for bailout paths. + setResultType(MIRType_Object); + setRecoveredOnBailout(); + + if (templateObject->is<NativeObject>()) { + NativeObject* nativeObject = &templateObject->as<NativeObject>(); + numSlots_ = nativeObject->slotSpan(); + numFixedSlots_ = nativeObject->numFixedSlots(); + } else { + const UnboxedLayout& layout = + templateObject->as<UnboxedPlainObject>().layoutDontCheckGeneration(); + // Same as UnboxedLayout::makeNativeGroup + numSlots_ = layout.properties().length(); + numFixedSlots_ = gc::GetGCKindSlots(layout.getAllocKind()); + } + + operandIndex_ = operandIndex; +} + +JSObject* +MObjectState::templateObjectOf(MDefinition* obj) +{ if (obj->isNewObject()) - templateObject = &obj->toNewObject()->templateObject()->as<PlainObject>(); + return obj->toNewObject()->templateObject(); else if (obj->isCreateThisWithTemplate()) - templateObject = &obj->toCreateThisWithTemplate()->templateObject()->as<PlainObject>(); + return obj->toCreateThisWithTemplate()->templateObject(); else - templateObject = obj->toNewCallObject()->templateObject(); - numSlots_ = templateObject->slotSpan(); - numFixedSlots_ = templateObject->numFixedSlots(); + return obj->toNewCallObject()->templateObject(); + + return nullptr; } bool MObjectState::init(TempAllocator& alloc, MDefinition* obj) { if (!MVariadicInstruction::init(alloc, numSlots() + 1)) return false; // +1, for the Object. initOperand(0, obj); return true; } MObjectState* MObjectState::New(TempAllocator& alloc, MDefinition* obj, MDefinition* undefinedVal) { - MObjectState* res = new(alloc) MObjectState(obj); + JSObject* templateObject = templateObjectOf(obj); + MOZ_ASSERT(templateObject, "Unexpected object creation."); + + OperandIndexMap* operandIndex = nullptr; + if (templateObject->is<UnboxedPlainObject>()) { + operandIndex = new(alloc) OperandIndexMap; + if (!operandIndex || !operandIndex->init(alloc, templateObject)) + return nullptr; + } + + MObjectState* res = new(alloc) MObjectState(templateObject, operandIndex); if (!res || !res->init(alloc, obj)) return nullptr; for (size_t i = 0; i < res->numSlots(); i++) res->initSlot(i, undefinedVal); return res; } MObjectState* MObjectState::Copy(TempAllocator& alloc, MObjectState* state) { - MDefinition* obj = state->object(); - MObjectState* res = new(alloc) MObjectState(obj); - if (!res || !res->init(alloc, obj)) + MObjectState* res = new(alloc) MObjectState(state); + if (!res || !res->init(alloc, state->object())) return nullptr; for (size_t i = 0; i < res->numSlots(); i++) res->initSlot(i, state->getSlot(i)); return res; } MArrayState::MArrayState(MDefinition* arr) {
--- a/js/src/jit/MIR.h +++ b/js/src/jit/MIR.h @@ -3354,45 +3354,68 @@ class MNewDerivedTypedObject } bool writeRecoverData(CompactBufferWriter& writer) const override; bool canRecoverOnBailout() const override { return true; } }; +// This vector is used when the recovered object is kept unboxed. We map the +// offset of each property to the index of the corresponding operands in the +// object state. +struct OperandIndexMap : public TempObject +{ + // The number of properties is limited by scalar replacement. Thus we cannot + // have any large number of properties. + FixedList<uint8_t> map; + + bool init(TempAllocator& alloc, JSObject* templateObject); +}; + // Represent the content of all slots of an object. This instruction is not // lowered and is not used to generate code. class MObjectState : public MVariadicInstruction, public NoFloatPolicyAfter<1>::Data { private: uint32_t numSlots_; - uint32_t numFixedSlots_; - - explicit MObjectState(MDefinition* obj); + uint32_t numFixedSlots_; // valid if isUnboxed() == false. + OperandIndexMap* operandIndex_; // valid if isUnboxed() == true. + + bool isUnboxed() const { + return operandIndex_ != nullptr; + } + + MObjectState(JSObject *templateObject, OperandIndexMap* operandIndex); + explicit MObjectState(MObjectState* state); bool init(TempAllocator& alloc, MDefinition* obj); void initSlot(uint32_t slot, MDefinition* def) { initOperand(slot + 1, def); } public: INSTRUCTION_HEADER(ObjectState) + // Return the template object of any object creation which can be recovered + // on bailout. + static JSObject* templateObjectOf(MDefinition* obj); + static MObjectState* New(TempAllocator& alloc, MDefinition* obj, MDefinition* undefinedVal); static MObjectState* Copy(TempAllocator& alloc, MObjectState* state); MDefinition* object() const { return getOperand(0); } size_t numFixedSlots() const { + MOZ_ASSERT(!isUnboxed()); return numFixedSlots_; } size_t numSlots() const { return numSlots_; } MDefinition* getSlot(uint32_t slot) const { return getOperand(slot + 1); @@ -3418,16 +3441,28 @@ class MObjectState } MDefinition* getDynamicSlot(uint32_t slot) const { return getSlot(slot + numFixedSlots()); } void setDynamicSlot(uint32_t slot, MDefinition* def) { setSlot(slot + numFixedSlots(), def); } + // Interface reserved for unboxed objects. + bool hasOffset(uint32_t offset) const { + MOZ_ASSERT(isUnboxed()); + return offset < operandIndex_->map.length() && operandIndex_->map[offset] != 0; + } + MDefinition* getOffset(uint32_t offset) const { + return getOperand(operandIndex_->map[offset]); + } + void setOffset(uint32_t offset, MDefinition* def) { + replaceOperand(operandIndex_->map[offset], def); + } + bool writeRecoverData(CompactBufferWriter& writer) const override; bool canRecoverOnBailout() const override { return true; } }; // Represent the contents of all elements of an array. This instruction is not // lowered and is not used to generate code.
--- a/js/src/jit/Recover.cpp +++ b/js/src/jit/Recover.cpp @@ -1364,23 +1364,40 @@ MObjectState::writeRecoverData(CompactBu RObjectState::RObjectState(CompactBufferReader& reader) { numSlots_ = reader.readUnsigned(); } bool RObjectState::recover(JSContext* cx, SnapshotIterator& iter) const { - RootedNativeObject object(cx, &iter.read().toObject().as<NativeObject>()); - MOZ_ASSERT(object->slotSpan() == numSlots()); - + RootedObject object(cx, &iter.read().toObject()); RootedValue val(cx); - for (size_t i = 0; i < numSlots(); i++) { - val = iter.read(); - object->setSlot(i, val); + + if (object->is<UnboxedPlainObject>()) { + const UnboxedLayout& layout = object->as<UnboxedPlainObject>().layout(); + + const UnboxedLayout::PropertyVector& properties = layout.properties(); + for (size_t i = 0; i < properties.length(); i++) { + val = iter.read(); + // This is the default placeholder value of MObjectState, when no + // properties are defined yet. + if (val.isUndefined()) + continue; + + MOZ_ALWAYS_TRUE(object->as<UnboxedPlainObject>().setValue(cx, properties[i], val)); + } + } else { + RootedNativeObject nativeObject(cx, &object->as<NativeObject>()); + MOZ_ASSERT(nativeObject->slotSpan() == numSlots()); + + for (size_t i = 0; i < numSlots(); i++) { + val = iter.read(); + nativeObject->setSlot(i, val); + } } val.setObject(*object); iter.storeInstructionResult(val); return true; } bool
--- a/js/src/jit/ScalarReplacement.cpp +++ b/js/src/jit/ScalarReplacement.cpp @@ -140,37 +140,25 @@ IsObjectEscaped(MInstruction* ins, JSObj { MOZ_ASSERT(ins->type() == MIRType_Object); MOZ_ASSERT(ins->isNewObject() || ins->isGuardShape() || ins->isCreateThisWithTemplate() || ins->isNewCallObject() || ins->isFunctionEnvironment()); JitSpewDef(JitSpew_Escape, "Check object\n", ins); JitSpewIndent spewIndent(JitSpew_Escape); - JSObject* obj = nullptr; - if (ins->isNewObject()) - obj = ins->toNewObject()->templateObject(); - else if (ins->isCreateThisWithTemplate()) - obj = ins->toCreateThisWithTemplate()->templateObject(); - else if (ins->isNewCallObject()) - obj = ins->toNewCallObject()->templateObject(); - else - obj = objDefault; + JSObject* obj = objDefault; + if (!obj) + obj = MObjectState::templateObjectOf(ins); if (!obj) { JitSpew(JitSpew_Escape, "No template object defined."); return true; } - // Don't optimize unboxed objects, which aren't handled by MObjectState. - if (obj->is<UnboxedPlainObject>()) { - JitSpew(JitSpew_Escape, "Template object is an unboxed plain object."); - return true; - } - // Check if the object is escaped. If the object is not the first argument // of either a known Store / Load, then we consider it as escaped. This is a // cheap and conservative escape analysis. for (MUseIterator i(ins->usesBegin()); i != ins->usesEnd(); i++) { MNode* consumer = (*i)->consumer(); if (!consumer->isDefinition()) { // Cannot optimize if it is observable from fun.arguments or others. if (!consumer->toResumePoint()->isRecoverableOperand(*i)) {