diff --git a/js/src/frontend/BytecodeEmitter.cpp b/js/src/frontend/BytecodeEmitter.cpp index ad85ca4df452..0ffd472883d7 100644 --- a/js/src/frontend/BytecodeEmitter.cpp +++ b/js/src/frontend/BytecodeEmitter.cpp @@ -6430,7 +6430,7 @@ BytecodeEmitter::emitCallOrNew(ParseNode* pn) return false; } } else { - if (!emitArray(pn2->pn_next, argc, JSOP_SPREADCALLARRAY)) + if (!emitArray(pn2->pn_next, argc)) return false; } emittingForInit = oldEmittingForInit; @@ -6897,7 +6897,7 @@ BytecodeEmitter::emitSpread() } bool -BytecodeEmitter::emitArray(ParseNode* pn, uint32_t count, JSOp op) +BytecodeEmitter::emitArray(ParseNode* pn, uint32_t count) { /* * Emit code for [a, b, c] that is equivalent to constructing a new @@ -6907,7 +6907,6 @@ BytecodeEmitter::emitArray(ParseNode* pn, uint32_t count, JSOp op) * to avoid dup'ing and popping the array as each element is added, as * JSOP_SETELEM/JSOP_SETPROP would do. */ - MOZ_ASSERT(op == JSOP_NEWARRAY || op == JSOP_SPREADCALLARRAY); int32_t nspread = 0; for (ParseNode* elt = pn; elt; elt = elt->pn_next) { @@ -6916,9 +6915,9 @@ BytecodeEmitter::emitArray(ParseNode* pn, uint32_t count, JSOp op) } ptrdiff_t off; - if (!emitN(op, 3, &off)) // ARRAY + if (!emitN(JSOP_NEWARRAY, 3, &off)) // ARRAY return false; - checkTypeSet(op); + checkTypeSet(JSOP_NEWARRAY); jsbytecode* pc = code(off); // For arrays with spread, this is a very pessimistic allocation, the @@ -7576,7 +7575,7 @@ BytecodeEmitter::emitTree(ParseNode* pn) } } - ok = emitArray(pn->pn_head, pn->pn_count, JSOP_NEWARRAY); + ok = emitArray(pn->pn_head, pn->pn_count); break; case PNK_ARRAYCOMP: diff --git a/js/src/frontend/BytecodeEmitter.h b/js/src/frontend/BytecodeEmitter.h index 72ac2fddaf5a..f3e38612868b 100644 --- a/js/src/frontend/BytecodeEmitter.h +++ b/js/src/frontend/BytecodeEmitter.h @@ -427,7 +427,7 @@ struct BytecodeEmitter bool emitAtomOp(JSAtom* atom, JSOp op); bool emitAtomOp(ParseNode* pn, JSOp op); - bool emitArray(ParseNode* pn, uint32_t count, JSOp op); + bool emitArray(ParseNode* pn, uint32_t count); bool emitArrayComp(ParseNode* pn); bool emitInternedObjectOp(uint32_t index, JSOp op); diff --git a/js/src/jit-test/tests/ion/recover-arrays.js b/js/src/jit-test/tests/ion/recover-arrays.js index 9cf8651cae7f..23d5e3465ae2 100644 --- a/js/src/jit-test/tests/ion/recover-arrays.js +++ b/js/src/jit-test/tests/ion/recover-arrays.js @@ -1,8 +1,8 @@ // Ion eager fails the test below because we have not yet created any // template object in baseline before running the content of the top-level // function. -if (getJitCompilerOptions()["ion.warmup.trigger"] <= 100) - setJitCompilerOption("ion.warmup.trigger", 100); +if (getJitCompilerOptions()["ion.warmup.trigger"] <= 20) + setJitCompilerOption("ion.warmup.trigger", 20); // This function is used to force a bailout when it is inlined, and to recover // the frame which is inlining this function. diff --git a/js/src/jit/BaselineCompiler.cpp b/js/src/jit/BaselineCompiler.cpp index b077ecb53df6..f874c9115449 100644 --- a/js/src/jit/BaselineCompiler.cpp +++ b/js/src/jit/BaselineCompiler.cpp @@ -1740,11 +1740,23 @@ BaselineCompiler::emit_JSOP_NEWARRAY() frame.syncStack(0); uint32_t length = GET_UINT24(pc); + RootedObjectGroup group(cx); + if (!ObjectGroup::useSingletonForAllocationSite(script, pc, JSProto_Array)) { + group = ObjectGroup::allocationSiteGroup(cx, script, pc, JSProto_Array); + if (!group) + return false; + } - // Pass length in R0. + // Pass length in R0, group in R1. masm.move32(Imm32(length), R0.scratchReg()); + masm.movePtr(ImmGCPtr(group), R1.scratchReg()); - ICNewArray_Fallback::Compiler stubCompiler(cx); + ArrayObject* templateObject = NewDenseUnallocatedArray(cx, length, NullPtr(), TenuredObject); + if (!templateObject) + return false; + templateObject->setGroup(group); + + ICNewArray_Fallback::Compiler stubCompiler(cx, templateObject); if (!emitOpIC(stubCompiler.getStub(&stubSpace_))) return false; @@ -1752,12 +1764,6 @@ BaselineCompiler::emit_JSOP_NEWARRAY() return true; } -bool -BaselineCompiler::emit_JSOP_SPREADCALLARRAY() -{ - return emit_JSOP_NEWARRAY(); -} - typedef JSObject* (*NewArrayCopyOnWriteFn)(JSContext*, HandleArrayObject, gc::InitialHeap); const VMFunction jit::NewArrayCopyOnWriteInfo = FunctionInfo(js::NewDenseCopyOnWriteArray); @@ -1823,11 +1829,24 @@ BaselineCompiler::emit_JSOP_NEWINIT() frame.syncStack(0); JSProtoKey key = JSProtoKey(GET_UINT8(pc)); - if (key == JSProto_Array) { - // Pass length in R0. - masm.move32(Imm32(0), R0.scratchReg()); + RootedObjectGroup group(cx); + if (!ObjectGroup::useSingletonForAllocationSite(script, pc, key)) { + group = ObjectGroup::allocationSiteGroup(cx, script, pc, key); + if (!group) + return false; + } - ICNewArray_Fallback::Compiler stubCompiler(cx); + if (key == JSProto_Array) { + // Pass length in R0, group in R1. + masm.move32(Imm32(0), R0.scratchReg()); + masm.movePtr(ImmGCPtr(group), R1.scratchReg()); + + ArrayObject* templateObject = NewDenseUnallocatedArray(cx, 0, NullPtr(), TenuredObject); + if (!templateObject) + return false; + templateObject->setGroup(group); + + ICNewArray_Fallback::Compiler stubCompiler(cx, templateObject); if (!emitOpIC(stubCompiler.getStub(&stubSpace_))) return false; } else { diff --git a/js/src/jit/BaselineCompiler.h b/js/src/jit/BaselineCompiler.h index 1cb5c5f2742d..ddc9646d428c 100644 --- a/js/src/jit/BaselineCompiler.h +++ b/js/src/jit/BaselineCompiler.h @@ -93,7 +93,6 @@ namespace jit { _(JSOP_BITNOT) \ _(JSOP_NEG) \ _(JSOP_NEWARRAY) \ - _(JSOP_SPREADCALLARRAY) \ _(JSOP_NEWARRAY_COPYONWRITE) \ _(JSOP_INITELEM_ARRAY) \ _(JSOP_NEWOBJECT) \ diff --git a/js/src/jit/BaselineIC.cpp b/js/src/jit/BaselineIC.cpp index bf3af18f82f2..c3760b2c98e0 100644 --- a/js/src/jit/BaselineIC.cpp +++ b/js/src/jit/BaselineIC.cpp @@ -182,7 +182,7 @@ ReceiverGuard::StackGuard::StackGuard(JSObject* obj) group = obj->group(); if (UnboxedExpandoObject* expando = obj->as().maybeExpando()) shape = expando->lastProperty(); - } else if (obj->is() || obj->is()) { + } else if (obj->is()) { group = obj->group(); } else { shape = obj->maybeShape(); @@ -298,28 +298,22 @@ ICStub::trace(JSTracer* trc) TraceEdge(trc, &getElemStub->shape(), "baseline-getelem-dense-shape"); break; } - case ICStub::GetElem_UnboxedArray: { - ICGetElem_UnboxedArray* getElemStub = toGetElem_UnboxedArray(); - TraceEdge(trc, &getElemStub->group(), "baseline-getelem-unboxed-array-group"); - break; - } case ICStub::GetElem_TypedArray: { ICGetElem_TypedArray* getElemStub = toGetElem_TypedArray(); TraceEdge(trc, &getElemStub->shape(), "baseline-getelem-typedarray-shape"); break; } - case ICStub::SetElem_DenseOrUnboxedArray: { - ICSetElem_DenseOrUnboxedArray* setElemStub = toSetElem_DenseOrUnboxedArray(); - if (setElemStub->shape()) - TraceEdge(trc, &setElemStub->shape(), "baseline-getelem-dense-shape"); + case ICStub::SetElem_Dense: { + ICSetElem_Dense* setElemStub = toSetElem_Dense(); + TraceEdge(trc, &setElemStub->shape(), "baseline-getelem-dense-shape"); TraceEdge(trc, &setElemStub->group(), "baseline-setelem-dense-group"); break; } - case ICStub::SetElem_DenseOrUnboxedArrayAdd: { - ICSetElem_DenseOrUnboxedArrayAdd* setElemStub = toSetElem_DenseOrUnboxedArrayAdd(); + case ICStub::SetElem_DenseAdd: { + ICSetElem_DenseAdd* setElemStub = toSetElem_DenseAdd(); TraceEdge(trc, &setElemStub->group(), "baseline-setelem-denseadd-group"); - JS_STATIC_ASSERT(ICSetElem_DenseOrUnboxedArrayAdd::MAX_PROTO_CHAIN_DEPTH == 4); + JS_STATIC_ASSERT(ICSetElem_DenseAdd::MAX_PROTO_CHAIN_DEPTH == 4); switch (setElemStub->protoChainDepth()) { case 0: setElemStub->toImpl<0>()->traceShapes(trc); break; @@ -566,8 +560,7 @@ ICStub::trace(JSTracer* trc) } case ICStub::NewArray_Fallback: { ICNewArray_Fallback* stub = toNewArray_Fallback(); - if (stub->templateObject()) - TraceEdge(trc, &stub->templateObject(), "baseline-newarray-template"); + TraceEdge(trc, &stub->templateObject(), "baseline-newarray-template"); break; } case ICStub::NewObject_Fallback: { @@ -1503,8 +1496,9 @@ DoTypeUpdateFallback(JSContext* cx, BaselineFrame* frame, ICUpdatedStub* stub, H RootedId id(cx); switch(stub->kind()) { - case ICStub::SetElem_DenseOrUnboxedArray: - case ICStub::SetElem_DenseOrUnboxedArrayAdd: { + case ICStub::SetElem_Dense: + case ICStub::SetElem_DenseAdd: { + MOZ_ASSERT(obj->isNative()); id = JSID_VOID; AddTypePropertyId(cx, obj, id, value); break; @@ -1714,37 +1708,20 @@ ICThis_Fallback::Compiler::generateStubCode(MacroAssembler& masm) // static bool -DoNewArray(JSContext* cx, BaselineFrame* frame, ICNewArray_Fallback* stub, uint32_t length, - MutableHandleValue res) +DoNewArray(JSContext* cx, ICNewArray_Fallback* stub, uint32_t length, + HandleObjectGroup group, MutableHandleValue res) { FallbackICSpew(cx, stub, "NewArray"); - RootedObject obj(cx); - if (stub->templateObject()) { - RootedObject templateObject(cx, stub->templateObject()); - obj = NewArrayOperationWithTemplate(cx, templateObject); - if (!obj) - return false; - } else { - RootedScript script(cx, frame->script()); - jsbytecode* pc = stub->icEntry()->pc(script); - obj = NewArrayOperation(cx, script, pc, length); - if (!obj) - return false; - - if (obj && !obj->isSingleton() && !obj->group()->maybePreliminaryObjects()) { - JSObject* templateObject = NewArrayOperation(cx, script, pc, length, TenuredObject); - if (!templateObject) - return false; - stub->setTemplateObject(templateObject); - } - } + JSObject* obj = NewDenseArray(cx, length, group, NewArray_FullyAllocating); + if (!obj) + return false; res.setObject(*obj); return true; } -typedef bool(*DoNewArrayFn)(JSContext*, BaselineFrame*, ICNewArray_Fallback*, uint32_t, +typedef bool(*DoNewArrayFn)(JSContext*, ICNewArray_Fallback*, uint32_t, HandleObjectGroup, MutableHandleValue); static const VMFunction DoNewArrayInfo = FunctionInfo(DoNewArray, TailCall); @@ -1753,9 +1730,9 @@ ICNewArray_Fallback::Compiler::generateStubCode(MacroAssembler& masm) { EmitRestoreTailCallReg(masm); + masm.push(R1.scratchReg()); // type masm.push(R0.scratchReg()); // length masm.push(BaselineStubReg); // stub. - masm.pushBaselineFramePtr(BaselineFrameReg, R0.scratchReg()); return tailCallVM(DoNewArrayInfo, masm); } @@ -3427,9 +3404,6 @@ CheckHasNoSuchProperty(JSContext* cx, HandleObject obj, HandlePropertyName name, } else if (curObj->is()) { if (curObj->as().containsUnboxedOrExpandoProperty(cx, NameToId(name))) return false; - } else if (curObj->is()) { - if (name == cx->names().length) - return false; } else if (curObj->is()) { if (curObj->as().typeDescr().hasProperty(cx->names(), NameToId(name))) return false; @@ -3458,12 +3432,8 @@ IsCacheableProtoChain(JSObject* obj, JSObject* holder, bool isDOMProxy=false) if (!isDOMProxy && !obj->isNative()) { if (obj == holder) return false; - if (!obj->is() && - !obj->is() && - !obj->is()) - { + if (!obj->is() && !obj->is()) return false; - } } // Don't handle objects which require a prototype guard. This should @@ -4038,16 +4008,6 @@ IsNativeDenseElementAccess(HandleObject obj, HandleValue key) return false; } -static bool -IsNativeOrUnboxedDenseElementAccess(HandleObject obj, HandleValue key) -{ - if (!obj->isNative() && !obj->is()) - return false; - if (key.isInt32() && key.toInt32() >= 0 && !IsAnyTypedArray(obj.get())) - return true; - return false; -} - static bool TryAttachGetElemStub(JSContext* cx, JSScript* script, jsbytecode* pc, ICGetElem_Fallback* stub, HandleValue lhs, HandleValue rhs, HandleValue res) @@ -4135,19 +4095,6 @@ TryAttachGetElemStub(JSContext* cx, JSScript* script, jsbytecode* pc, ICGetElem_ script = rootedScript; } - // Check for UnboxedArray[int] accesses. - if (obj->is() && rhs.isInt32() && rhs.toInt32() >= 0) { - JitSpew(JitSpew_BaselineIC, " Generating GetElem(UnboxedArray[Int32]) stub"); - ICGetElem_UnboxedArray::Compiler compiler(cx, stub->fallbackMonitorStub()->firstMonitorStub(), - obj->group()); - ICStub* unboxedStub = compiler.getStub(compiler.getStubSpace(script)); - if (!unboxedStub) - return false; - - stub->addNewStub(unboxedStub); - return true; - } - // Check for TypedArray[int] => Number and TypedObject[int] => Number accesses. if ((IsAnyTypedArray(obj.get()) || IsPrimitiveArrayTypedObject(obj)) && rhs.isNumber() && @@ -4779,54 +4726,6 @@ ICGetElem_Dense::Compiler::generateStubCode(MacroAssembler& masm) return true; } -// -// GetElem_UnboxedArray -// - -bool -ICGetElem_UnboxedArray::Compiler::generateStubCode(MacroAssembler& masm) -{ - Label failure; - masm.branchTestObject(Assembler::NotEqual, R0, &failure); - masm.branchTestInt32(Assembler::NotEqual, R1, &failure); - - AllocatableGeneralRegisterSet regs(availableGeneralRegs(2)); - Register scratchReg = regs.takeAny(); - - // Unbox R0 and group guard. - Register obj = masm.extractObject(R0, ExtractTemp0); - masm.loadPtr(Address(BaselineStubReg, ICGetElem_UnboxedArray::offsetOfGroup()), scratchReg); - masm.branchTestObjGroup(Assembler::NotEqual, obj, scratchReg, &failure); - - // Unbox key. - Register key = masm.extractInt32(R1, ExtractTemp1); - - // Bounds check. - masm.load32(Address(obj, UnboxedArrayObject::offsetOfCapacityIndexAndInitializedLength()), - scratchReg); - masm.and32(Imm32(UnboxedArrayObject::InitializedLengthMask), scratchReg); - masm.branch32(Assembler::BelowOrEqual, scratchReg, key, &failure); - - // Load obj->elements. - masm.loadPtr(Address(obj, UnboxedArrayObject::offsetOfElements()), scratchReg); - - // Load value. - size_t width = UnboxedTypeSize(elementType_); - BaseIndex addr(scratchReg, key, ScaleFromElemWidth(width)); - masm.loadUnboxedProperty(addr, elementType_, R0); - - // Only monitor the result if its type might change. - if (elementType_ == JSVAL_TYPE_OBJECT) - EmitEnterTypeMonitorIC(masm); - else - EmitReturnFromIC(masm); - - // Failure case - jump to next stub - masm.bind(&failure); - EmitStubGuardFailure(masm); - return true; -} - // // GetElem_TypedArray // @@ -5128,48 +5027,42 @@ ICGetElem_Arguments::Compiler::generateStubCode(MacroAssembler& masm) // static bool -SetElemAddHasSameShapes(ICSetElem_DenseOrUnboxedArrayAdd* stub, JSObject* obj) +SetElemDenseAddHasSameShapes(ICSetElem_DenseAdd* stub, NativeObject* obj) { - static const size_t MAX_DEPTH = ICSetElem_DenseOrUnboxedArrayAdd::MAX_PROTO_CHAIN_DEPTH; - ICSetElem_DenseOrUnboxedArrayAddImpl* nstub = stub->toImplUnchecked(); - - if (obj->maybeShape() != nstub->shape(0)) - return false; - - JSObject* proto = obj->getProto(); - for (size_t i = 0; i < stub->protoChainDepth(); i++) { - if (!proto->isNative()) + size_t numShapes = stub->protoChainDepth() + 1; + for (size_t i = 0; i < numShapes; i++) { + static const size_t MAX_DEPTH = ICSetElem_DenseAdd::MAX_PROTO_CHAIN_DEPTH; + if (obj->lastProperty() != stub->toImplUnchecked()->shape(i)) return false; - if (proto->as().lastProperty() != nstub->shape(i + 1)) - return false; - proto = obj->getProto(); + JSObject* proto = obj->getProto(); if (!proto) { - if (i != stub->protoChainDepth() - 1) + if (i != numShapes - 1) return false; break; } + if (!proto->isNative()) + return false; + obj = &proto->as(); } return true; } static bool -DenseOrUnboxedArraySetElemStubExists(JSContext* cx, ICStub::Kind kind, - ICSetElem_Fallback* stub, HandleObject obj) +DenseSetElemStubExists(JSContext* cx, ICStub::Kind kind, ICSetElem_Fallback* stub, HandleNativeObject obj) { - MOZ_ASSERT(kind == ICStub::SetElem_DenseOrUnboxedArray || - kind == ICStub::SetElem_DenseOrUnboxedArrayAdd); + MOZ_ASSERT(kind == ICStub::SetElem_Dense || kind == ICStub::SetElem_DenseAdd); for (ICStubConstIterator iter = stub->beginChainConst(); !iter.atEnd(); iter++) { - if (kind == ICStub::SetElem_DenseOrUnboxedArray && iter->isSetElem_DenseOrUnboxedArray()) { - ICSetElem_DenseOrUnboxedArray* nstub = iter->toSetElem_DenseOrUnboxedArray(); - if (obj->maybeShape() == nstub->shape() && obj->getGroup(cx) == nstub->group()) + if (kind == ICStub::SetElem_Dense && iter->isSetElem_Dense()) { + ICSetElem_Dense* dense = iter->toSetElem_Dense(); + if (obj->lastProperty() == dense->shape() && obj->getGroup(cx) == dense->group()) return true; } - if (kind == ICStub::SetElem_DenseOrUnboxedArrayAdd && iter->isSetElem_DenseOrUnboxedArrayAdd()) { - ICSetElem_DenseOrUnboxedArrayAdd* nstub = iter->toSetElem_DenseOrUnboxedArrayAdd(); - if (obj->getGroup(cx) == nstub->group() && SetElemAddHasSameShapes(nstub, obj)) + if (kind == ICStub::SetElem_DenseAdd && iter->isSetElem_DenseAdd()) { + ICSetElem_DenseAdd* dense = iter->toSetElem_DenseAdd(); + if (obj->getGroup(cx) == dense->group() && SetElemDenseAddHasSameShapes(dense, obj)) return true; } } @@ -5208,29 +5101,13 @@ RemoveExistingTypedArraySetElemStub(JSContext* cx, ICSetElem_Fallback* stub, Han return false; } -static size_t -SetElemObjectInitializedLength(JSObject *obj) -{ - if (obj->isNative()) - return obj->as().getDenseInitializedLength(); - return obj->as().initializedLength(); -} - -static size_t -SetElemObjectCapacity(JSObject *obj) -{ - if (obj->isNative()) - return obj->as().getDenseInitializedLength(); - return obj->as().capacity(); -} - static bool -CanOptimizeDenseOrUnboxedArraySetElem(JSObject* obj, uint32_t index, - Shape* oldShape, uint32_t oldCapacity, uint32_t oldInitLength, - bool* isAddingCaseOut, size_t* protoDepthOut) +CanOptimizeDenseSetElem(NativeObject* obj, uint32_t index, + Shape* oldShape, uint32_t oldCapacity, uint32_t oldInitLength, + bool* isAddingCaseOut, size_t* protoDepthOut) { - uint32_t initLength = SetElemObjectInitializedLength(obj); - uint32_t capacity = SetElemObjectCapacity(obj); + uint32_t initLength = obj->getDenseInitializedLength(); + uint32_t capacity = obj->getDenseCapacity(); *isAddingCaseOut = false; *protoDepthOut = 0; @@ -5239,7 +5116,7 @@ CanOptimizeDenseOrUnboxedArraySetElem(JSObject* obj, uint32_t index, if (initLength < oldInitLength || capacity < oldCapacity) return false; - Shape* shape = obj->maybeShape(); + Shape* shape = obj->lastProperty(); // Cannot optimize if the shape changed. if (oldShape != shape) @@ -5254,7 +5131,7 @@ CanOptimizeDenseOrUnboxedArraySetElem(JSObject* obj, uint32_t index, return false; // Cannot optimize if the value at position after the set is a hole. - if (obj->isNative() && !obj->as().containsDenseElement(index)) + if (!obj->containsDenseElement(index)) return false; // At this point, if we know that the initLength did not change, then @@ -5273,17 +5150,22 @@ CanOptimizeDenseOrUnboxedArraySetElem(JSObject* obj, uint32_t index, // either directly, or via a prototype, or via the target object for a prototype // which is a proxy, that handles a particular integer write. // Scan the prototype and shape chain to make sure that this is not the case. - if (obj->isIndexed()) - return false; - JSObject* curObj = obj->getProto(); + JSObject* curObj = obj; while (curObj) { - ++*protoDepthOut; - if (!curObj->isNative() || curObj->isIndexed()) + // Ensure object is native. + if (!curObj->isNative()) return false; + + // Ensure all indexed properties are stored in dense elements. + if (curObj->isIndexed()) + return false; + curObj = curObj->getProto(); + if (curObj) + ++*protoDepthOut; } - if (*protoDepthOut > ICSetElem_DenseOrUnboxedArrayAdd::MAX_PROTO_CHAIN_DEPTH) + if (*protoDepthOut > ICSetElem_DenseAdd::MAX_PROTO_CHAIN_DEPTH) return false; *isAddingCaseOut = true; @@ -5353,57 +5235,49 @@ DoSetElemFallback(JSContext* cx, BaselineFrame* frame, ICSetElem_Fallback* stub_ } // Try to generate new stubs. - if (IsNativeOrUnboxedDenseElementAccess(obj, index) && !rhs.isMagic(JS_ELEMENTS_HOLE)) { + if (IsNativeDenseElementAccess(obj, index) && !rhs.isMagic(JS_ELEMENTS_HOLE)) + { + HandleNativeObject nobj = obj.as(); + bool addingCase; size_t protoDepth; - if (CanOptimizeDenseOrUnboxedArraySetElem(obj, index.toInt32(), - oldShape, oldCapacity, oldInitLength, - &addingCase, &protoDepth)) + if (CanOptimizeDenseSetElem(nobj, index.toInt32(), + oldShape, oldCapacity, oldInitLength, + &addingCase, &protoDepth)) { - RootedShape shape(cx, obj->maybeShape()); + RootedShape shape(cx, nobj->lastProperty()); RootedObjectGroup group(cx, obj->getGroup(cx)); if (!group) return false; - if (addingCase && - !DenseOrUnboxedArraySetElemStubExists(cx, ICStub::SetElem_DenseOrUnboxedArrayAdd, - stub, obj)) - { + if (addingCase && !DenseSetElemStubExists(cx, ICStub::SetElem_DenseAdd, stub, nobj)) { JitSpew(JitSpew_BaselineIC, - " Generating SetElem_DenseOrUnboxedArrayAdd stub " + " Generating SetElem_DenseAdd stub " "(shape=%p, group=%p, protoDepth=%u)", - shape.get(), group.get(), protoDepth); - ICSetElemDenseOrUnboxedArrayAddCompiler compiler(cx, obj, protoDepth); - ICUpdatedStub* newStub = compiler.getStub(compiler.getStubSpace(script)); - if (!newStub) + nobj->lastProperty(), group.get(), protoDepth); + ICSetElemDenseAddCompiler compiler(cx, obj, protoDepth); + ICUpdatedStub* denseStub = compiler.getStub(compiler.getStubSpace(script)); + if (!denseStub) return false; - if (compiler.needsUpdateStubs() && - !newStub->addUpdateStubForValue(cx, script, obj, JSID_VOIDHANDLE, rhs)) - { + if (!denseStub->addUpdateStubForValue(cx, script, obj, JSID_VOIDHANDLE, rhs)) return false; - } - stub->addNewStub(newStub); + stub->addNewStub(denseStub); } else if (!addingCase && - !DenseOrUnboxedArraySetElemStubExists(cx, - ICStub::SetElem_DenseOrUnboxedArray, - stub, obj)) + !DenseSetElemStubExists(cx, ICStub::SetElem_Dense, stub, nobj)) { JitSpew(JitSpew_BaselineIC, - " Generating SetElem_DenseOrUnboxedArray stub (shape=%p, group=%p)", - shape.get(), group.get()); - ICSetElem_DenseOrUnboxedArray::Compiler compiler(cx, shape, group); - ICUpdatedStub* newStub = compiler.getStub(compiler.getStubSpace(script)); - if (!newStub) + " Generating SetElem_Dense stub (shape=%p, group=%p)", + nobj->lastProperty(), group.get()); + ICSetElem_Dense::Compiler compiler(cx, shape, group); + ICUpdatedStub* denseStub = compiler.getStub(compiler.getStubSpace(script)); + if (!denseStub) return false; - if (compiler.needsUpdateStubs() && - !newStub->addUpdateStubForValue(cx, script, obj, JSID_VOIDHANDLE, rhs)) - { + if (!denseStub->addUpdateStubForValue(cx, script, obj, JSID_VOIDHANDLE, rhs)) return false; - } - stub->addNewStub(newStub); + stub->addNewStub(denseStub); } } @@ -5513,163 +5387,125 @@ BaselineScript::noteArrayWriteHole(uint32_t pcOffset) } // -// SetElem_DenseOrUnboxedArray +// SetElem_Dense // -template -void -EmitUnboxedPreBarrierForBaseline(MacroAssembler &masm, T address, JSValueType type) -{ - if (type == JSVAL_TYPE_OBJECT) - EmitPreBarrier(masm, address, MIRType_Object); - else if (type == JSVAL_TYPE_STRING) - EmitPreBarrier(masm, address, MIRType_String); - else - MOZ_ASSERT(!UnboxedTypeNeedsPreBarrier(type)); -} - bool -ICSetElem_DenseOrUnboxedArray::Compiler::generateStubCode(MacroAssembler& masm) +ICSetElem_Dense::Compiler::generateStubCode(MacroAssembler& masm) { // R0 = object // R1 = key // Stack = { ... rhs-value, ? } - Label failure, failurePopR0; + Label failure; + Label failureUnstow; masm.branchTestObject(Assembler::NotEqual, R0, &failure); masm.branchTestInt32(Assembler::NotEqual, R1, &failure); AllocatableGeneralRegisterSet regs(availableGeneralRegs(2)); Register scratchReg = regs.takeAny(); - // Unbox R0 and guard on its group and, if this is a native access, its shape. + // Unbox R0 and guard on its shape. Register obj = masm.extractObject(R0, ExtractTemp0); - masm.loadPtr(Address(BaselineStubReg, ICSetElem_DenseOrUnboxedArray::offsetOfGroup()), - scratchReg); - masm.branchTestObjGroup(Assembler::NotEqual, obj, scratchReg, &failure); - if (unboxedType_ == JSVAL_TYPE_MAGIC) { - masm.loadPtr(Address(BaselineStubReg, ICSetElem_DenseOrUnboxedArray::offsetOfShape()), - scratchReg); - masm.branchTestObjShape(Assembler::NotEqual, obj, scratchReg, &failure); - } + masm.loadPtr(Address(BaselineStubReg, ICSetElem_Dense::offsetOfShape()), scratchReg); + masm.branchTestObjShape(Assembler::NotEqual, obj, scratchReg, &failure); - if (needsUpdateStubs()) { - // Stow both R0 and R1 (object and key) - // But R0 and R1 still hold their values. - EmitStowICValues(masm, 2); + // Stow both R0 and R1 (object and key) + // But R0 and R1 still hold their values. + EmitStowICValues(masm, 2); - // Stack is now: { ..., rhs-value, object-value, key-value, maybe?-RET-ADDR } - // Load rhs-value into R0 - masm.loadValue(Address(BaselineStackReg, 2 * sizeof(Value) + ICStackValueOffset), R0); + // We may need to free up some registers. + regs = availableGeneralRegs(0); + regs.take(R0); - // Call the type-update stub. - if (!callTypeUpdateIC(masm, sizeof(Value))) - return false; + // Guard that the object group matches. + Register typeReg = regs.takeAny(); + masm.loadPtr(Address(BaselineStubReg, ICSetElem_Dense::offsetOfGroup()), typeReg); + masm.branchPtr(Assembler::NotEqual, Address(obj, JSObject::offsetOfGroup()), typeReg, + &failureUnstow); + regs.add(typeReg); - // Unstow R0 and R1 (object and key) - EmitUnstowICValues(masm, 2); + // Stack is now: { ..., rhs-value, object-value, key-value, maybe?-RET-ADDR } + // Load rhs-value in to R0 + masm.loadValue(Address(BaselineStackReg, 2 * sizeof(Value) + ICStackValueOffset), R0); - // Restore object. - obj = masm.extractObject(R0, ExtractTemp0); + // Call the type-update stub. + if (!callTypeUpdateIC(masm, sizeof(Value))) + return false; - // Trigger post barriers here on the value being written. Fields which - // objects can be written to also need update stubs. - masm.Push(R1); - masm.loadValue(Address(BaselineStackReg, sizeof(Value) + ICStackValueOffset), R1); + // Unstow R0 and R1 (object and key) + EmitUnstowICValues(masm, 2); - LiveGeneralRegisterSet saveRegs; - saveRegs.add(R0); - saveRegs.addUnchecked(obj); - saveRegs.add(BaselineStubReg); - emitPostWriteBarrierSlot(masm, obj, R1, scratchReg, saveRegs); + // Reset register set. + regs = availableGeneralRegs(2); + scratchReg = regs.takeAny(); - masm.Pop(R1); - } - - // Unbox key. + // Unbox object and key. + obj = masm.extractObject(R0, ExtractTemp0); Register key = masm.extractInt32(R1, ExtractTemp1); + // Load obj->elements in scratchReg. + masm.loadPtr(Address(obj, NativeObject::offsetOfElements()), scratchReg); + + // Bounds check. + Address initLength(scratchReg, ObjectElements::offsetOfInitializedLength()); + masm.branch32(Assembler::BelowOrEqual, initLength, key, &failure); + + // Hole check. + BaseIndex element(scratchReg, key, TimesEight); + masm.branchTestMagic(Assembler::Equal, element, &failure); + + // Perform a single test to see if we either need to convert double + // elements or clone the copy on write elements in the object. + Label noSpecialHandling; + Address elementsFlags(scratchReg, ObjectElements::offsetOfFlags()); + masm.branchTest32(Assembler::Zero, elementsFlags, + Imm32(ObjectElements::CONVERT_DOUBLE_ELEMENTS | + ObjectElements::COPY_ON_WRITE), + &noSpecialHandling); + + // Fail if we need to clone copy on write elements. + masm.branchTest32(Assembler::NonZero, elementsFlags, + Imm32(ObjectElements::COPY_ON_WRITE), + &failure); + + // Failure is not possible now. Free up registers. + regs.add(R0); + regs.add(R1); + regs.takeUnchecked(obj); + regs.takeUnchecked(key); Address valueAddr(BaselineStackReg, ICStackValueOffset); - if (unboxedType_ == JSVAL_TYPE_MAGIC) { - // Set element on a native object. + // We need to convert int32 values being stored into doubles. In this case + // the heap typeset is guaranteed to contain both int32 and double, so it's + // okay to store a double. Note that double arrays are only created by + // IonMonkey, so if we have no floating-point support Ion is disabled and + // there should be no double arrays. + if (cx->runtime()->jitSupportsFloatingPoint) + masm.convertInt32ValueToDouble(valueAddr, regs.getAny(), &noSpecialHandling); + else + masm.assumeUnreachable("There shouldn't be double arrays when there is no FP support."); - // Load obj->elements in scratchReg. - masm.loadPtr(Address(obj, NativeObject::offsetOfElements()), scratchReg); + masm.bind(&noSpecialHandling); - // Bounds check. - Address initLength(scratchReg, ObjectElements::offsetOfInitializedLength()); - masm.branch32(Assembler::BelowOrEqual, initLength, key, &failure); - - // Hole check. - BaseIndex element(scratchReg, key, TimesEight); - masm.branchTestMagic(Assembler::Equal, element, &failure); - - // Perform a single test to see if we either need to convert double - // elements or clone the copy on write elements in the object. - Label noSpecialHandling; - Address elementsFlags(scratchReg, ObjectElements::offsetOfFlags()); - masm.branchTest32(Assembler::Zero, elementsFlags, - Imm32(ObjectElements::CONVERT_DOUBLE_ELEMENTS | - ObjectElements::COPY_ON_WRITE), - &noSpecialHandling); - - // Fail if we need to clone copy on write elements. - masm.branchTest32(Assembler::NonZero, elementsFlags, - Imm32(ObjectElements::COPY_ON_WRITE), - &failure); - - // Failure is not possible now. Free up registers. - regs.add(R0); - regs.add(R1); - regs.takeUnchecked(obj); - regs.takeUnchecked(key); - - // We need to convert int32 values being stored into doubles. In this case - // the heap typeset is guaranteed to contain both int32 and double, so it's - // okay to store a double. Note that double arrays are only created by - // IonMonkey, so if we have no floating-point support Ion is disabled and - // there should be no double arrays. - if (cx->runtime()->jitSupportsFloatingPoint) - masm.convertInt32ValueToDouble(valueAddr, regs.getAny(), &noSpecialHandling); - else - masm.assumeUnreachable("There shouldn't be double arrays when there is no FP support."); - - masm.bind(&noSpecialHandling); - - ValueOperand tmpVal = regs.takeAnyValue(); - masm.loadValue(valueAddr, tmpVal); - EmitPreBarrier(masm, element, MIRType_Value); - masm.storeValue(tmpVal, element); - } else { - // Set element on an unboxed array. - - // Bounds check. - Address initLength(obj, UnboxedArrayObject::offsetOfCapacityIndexAndInitializedLength()); - masm.load32(initLength, scratchReg); - masm.and32(Imm32(UnboxedArrayObject::InitializedLengthMask), scratchReg); - masm.branch32(Assembler::BelowOrEqual, scratchReg, key, &failure); - - // Load obj->elements. - masm.loadPtr(Address(obj, UnboxedArrayObject::offsetOfElements()), scratchReg); - - // Compute the address being written to. - BaseIndex address(obj, key, ScaleFromElemWidth(UnboxedTypeSize(unboxedType_))); - - EmitUnboxedPreBarrierForBaseline(masm, address, unboxedType_); - - masm.Push(R0); - masm.loadValue(valueAddr, R0); - masm.storeUnboxedProperty(address, unboxedType_, - ConstantOrRegister(TypedOrValueRegister(R0)), &failurePopR0); - masm.Pop(R0); + // Don't overwrite R0 becuase |obj| might overlap with it, and it's needed + // for post-write barrier later. + ValueOperand tmpVal = regs.takeAnyValue(); + masm.loadValue(valueAddr, tmpVal); + EmitPreBarrier(masm, element, MIRType_Value); + masm.storeValue(tmpVal, element); + regs.add(key); + if (cx->runtime()->gc.nursery.exists()) { + Register r = regs.takeAny(); + LiveGeneralRegisterSet saveRegs; + emitPostWriteBarrierSlot(masm, obj, tmpVal, r, saveRegs); + regs.add(r); } - EmitReturnFromIC(masm); - if (failurePopR0.used()) { - masm.bind(&failurePopR0); - masm.Pop(R0); - } + + // Failure case - fail but first unstow R0 and R1 + masm.bind(&failureUnstow); + EmitUnstowICValues(masm, 2); // Failure case - jump to next stub masm.bind(&failure); @@ -5691,20 +5527,20 @@ GetProtoShapes(JSObject* obj, size_t protoChainDepth, AutoShapeVector* shapes) } // -// SetElem_DenseOrUnboxedArrayAdd +// SetElem_DenseAdd // ICUpdatedStub* -ICSetElemDenseOrUnboxedArrayAddCompiler::getStub(ICStubSpace* space) +ICSetElemDenseAddCompiler::getStub(ICStubSpace* space) { AutoShapeVector shapes(cx); - if (!shapes.append(obj_->maybeShape())) + if (!shapes.append(obj_->as().lastProperty())) return nullptr; if (!GetProtoShapes(obj_, protoChainDepth_, &shapes)) return nullptr; - JS_STATIC_ASSERT(ICSetElem_DenseOrUnboxedArrayAdd::MAX_PROTO_CHAIN_DEPTH == 4); + JS_STATIC_ASSERT(ICSetElem_DenseAdd::MAX_PROTO_CHAIN_DEPTH == 4); ICUpdatedStub* stub = nullptr; switch (protoChainDepth_) { @@ -5721,28 +5557,24 @@ ICSetElemDenseOrUnboxedArrayAddCompiler::getStub(ICStubSpace* space) } bool -ICSetElemDenseOrUnboxedArrayAddCompiler::generateStubCode(MacroAssembler& masm) +ICSetElemDenseAddCompiler::generateStubCode(MacroAssembler& masm) { // R0 = object // R1 = key // Stack = { ... rhs-value, ? } - Label failure, failurePopR0, failureUnstow; + Label failure; + Label failureUnstow; masm.branchTestObject(Assembler::NotEqual, R0, &failure); masm.branchTestInt32(Assembler::NotEqual, R1, &failure); AllocatableGeneralRegisterSet regs(availableGeneralRegs(2)); Register scratchReg = regs.takeAny(); - // Unbox R0 and guard on its group and, if this is a native access, its shape. + // Unbox R0 and guard on its shape. Register obj = masm.extractObject(R0, ExtractTemp0); - masm.loadPtr(Address(BaselineStubReg, ICSetElem_DenseOrUnboxedArrayAdd::offsetOfGroup()), + masm.loadPtr(Address(BaselineStubReg, ICSetElem_DenseAddImpl<0>::offsetOfShape(0)), scratchReg); - masm.branchTestObjGroup(Assembler::NotEqual, obj, scratchReg, &failure); - if (unboxedType_ == JSVAL_TYPE_MAGIC) { - masm.loadPtr(Address(BaselineStubReg, ICSetElem_DenseOrUnboxedArrayAddImpl<0>::offsetOfShape(0)), - scratchReg); - masm.branchTestObjShape(Assembler::NotEqual, obj, scratchReg, &failure); - } + masm.branchTestObjShape(Assembler::NotEqual, obj, scratchReg, &failure); // Stow both R0 and R1 (object and key) // But R0 and R1 still hold their values. @@ -5751,160 +5583,110 @@ ICSetElemDenseOrUnboxedArrayAddCompiler::generateStubCode(MacroAssembler& masm) // We may need to free up some registers. regs = availableGeneralRegs(0); regs.take(R0); - regs.take(scratchReg); + + // Guard that the object group matches. + Register typeReg = regs.takeAny(); + masm.loadPtr(Address(BaselineStubReg, ICSetElem_DenseAdd::offsetOfGroup()), typeReg); + masm.branchPtr(Assembler::NotEqual, Address(obj, JSObject::offsetOfGroup()), typeReg, + &failureUnstow); + regs.add(typeReg); // Shape guard objects on the proto chain. + scratchReg = regs.takeAny(); Register protoReg = regs.takeAny(); for (size_t i = 0; i < protoChainDepth_; i++) { masm.loadObjProto(i == 0 ? obj : protoReg, protoReg); masm.branchTestPtr(Assembler::Zero, protoReg, protoReg, &failureUnstow); - masm.loadPtr(Address(BaselineStubReg, ICSetElem_DenseOrUnboxedArrayAddImpl<0>::offsetOfShape(i + 1)), + masm.loadPtr(Address(BaselineStubReg, ICSetElem_DenseAddImpl<0>::offsetOfShape(i + 1)), scratchReg); masm.branchTestObjShape(Assembler::NotEqual, protoReg, scratchReg, &failureUnstow); } regs.add(protoReg); regs.add(scratchReg); - if (needsUpdateStubs()) { - // Stack is now: { ..., rhs-value, object-value, key-value, maybe?-RET-ADDR } - // Load rhs-value in to R0 - masm.loadValue(Address(BaselineStackReg, 2 * sizeof(Value) + ICStackValueOffset), R0); + // Stack is now: { ..., rhs-value, object-value, key-value, maybe?-RET-ADDR } + // Load rhs-value in to R0 + masm.loadValue(Address(BaselineStackReg, 2 * sizeof(Value) + ICStackValueOffset), R0); - // Call the type-update stub. - if (!callTypeUpdateIC(masm, sizeof(Value))) - return false; - } + // Call the type-update stub. + if (!callTypeUpdateIC(masm, sizeof(Value))) + return false; // Unstow R0 and R1 (object and key) EmitUnstowICValues(masm, 2); - // Restore object. - obj = masm.extractObject(R0, ExtractTemp0); - - if (needsUpdateStubs()) { - // Trigger post barriers here on the value being written. Fields which - // objects can be written to also need update stubs. - masm.Push(R1); - masm.loadValue(Address(BaselineStackReg, sizeof(Value) + ICStackValueOffset), R1); - - LiveGeneralRegisterSet saveRegs; - saveRegs.add(R0); - saveRegs.addUnchecked(obj); - saveRegs.add(BaselineStubReg); - emitPostWriteBarrierSlot(masm, obj, R1, scratchReg, saveRegs); - - masm.Pop(R1); - } - // Reset register set. regs = availableGeneralRegs(2); scratchReg = regs.takeAny(); - // Unbox key. + // Unbox obj and key. + obj = masm.extractObject(R0, ExtractTemp0); Register key = masm.extractInt32(R1, ExtractTemp1); + // Load obj->elements in scratchReg. + masm.loadPtr(Address(obj, NativeObject::offsetOfElements()), scratchReg); + + // Bounds check (key == initLength) + Address initLength(scratchReg, ObjectElements::offsetOfInitializedLength()); + masm.branch32(Assembler::NotEqual, initLength, key, &failure); + + // Capacity check. + Address capacity(scratchReg, ObjectElements::offsetOfCapacity()); + masm.branch32(Assembler::BelowOrEqual, capacity, key, &failure); + + // Check for copy on write elements. + Address elementsFlags(scratchReg, ObjectElements::offsetOfFlags()); + masm.branchTest32(Assembler::NonZero, elementsFlags, + Imm32(ObjectElements::COPY_ON_WRITE), + &failure); + + // Failure is not possible now. Free up registers. + regs.add(R0); + regs.add(R1); + regs.takeUnchecked(obj); + regs.takeUnchecked(key); + + // Increment initLength before write. + masm.add32(Imm32(1), initLength); + + // If length is now <= key, increment length before write. + Label skipIncrementLength; + Address length(scratchReg, ObjectElements::offsetOfLength()); + masm.branch32(Assembler::Above, length, key, &skipIncrementLength); + masm.add32(Imm32(1), length); + masm.bind(&skipIncrementLength); + Address valueAddr(BaselineStackReg, ICStackValueOffset); - if (unboxedType_ == JSVAL_TYPE_MAGIC) { - // Adding element to a native object. + // Convert int32 values to double if convertDoubleElements is set. In this + // case the heap typeset is guaranteed to contain both int32 and double, so + // it's okay to store a double. + Label dontConvertDoubles; + masm.branchTest32(Assembler::Zero, elementsFlags, + Imm32(ObjectElements::CONVERT_DOUBLE_ELEMENTS), + &dontConvertDoubles); + // Note that double arrays are only created by IonMonkey, so if we have no + // floating-point support Ion is disabled and there should be no double arrays. + if (cx->runtime()->jitSupportsFloatingPoint) + masm.convertInt32ValueToDouble(valueAddr, regs.getAny(), &dontConvertDoubles); + else + masm.assumeUnreachable("There shouldn't be double arrays when there is no FP support."); + masm.bind(&dontConvertDoubles); - // Load obj->elements in scratchReg. - masm.loadPtr(Address(obj, NativeObject::offsetOfElements()), scratchReg); - - // Bounds check (key == initLength) - Address initLength(scratchReg, ObjectElements::offsetOfInitializedLength()); - masm.branch32(Assembler::NotEqual, initLength, key, &failure); - - // Capacity check. - Address capacity(scratchReg, ObjectElements::offsetOfCapacity()); - masm.branch32(Assembler::BelowOrEqual, capacity, key, &failure); - - // Check for copy on write elements. - Address elementsFlags(scratchReg, ObjectElements::offsetOfFlags()); - masm.branchTest32(Assembler::NonZero, elementsFlags, - Imm32(ObjectElements::COPY_ON_WRITE), - &failure); - - // Failure is not possible now. Free up registers. - regs.add(R0); - regs.add(R1); - regs.takeUnchecked(obj); - regs.takeUnchecked(key); - - // Increment initLength before write. - masm.add32(Imm32(1), initLength); - - // If length is now <= key, increment length before write. - Label skipIncrementLength; - Address length(scratchReg, ObjectElements::offsetOfLength()); - masm.branch32(Assembler::Above, length, key, &skipIncrementLength); - masm.add32(Imm32(1), length); - masm.bind(&skipIncrementLength); - - // Convert int32 values to double if convertDoubleElements is set. In this - // case the heap typeset is guaranteed to contain both int32 and double, so - // it's okay to store a double. - Label dontConvertDoubles; - masm.branchTest32(Assembler::Zero, elementsFlags, - Imm32(ObjectElements::CONVERT_DOUBLE_ELEMENTS), - &dontConvertDoubles); - // Note that double arrays are only created by IonMonkey, so if we have no - // floating-point support Ion is disabled and there should be no double arrays. - if (cx->runtime()->jitSupportsFloatingPoint) - masm.convertInt32ValueToDouble(valueAddr, regs.getAny(), &dontConvertDoubles); - else - masm.assumeUnreachable("There shouldn't be double arrays when there is no FP support."); - masm.bind(&dontConvertDoubles); - - // Write the value. No need for pre-barrier since we're not overwriting an old value. - ValueOperand tmpVal = regs.takeAnyValue(); - BaseIndex element(scratchReg, key, TimesEight); - masm.loadValue(valueAddr, tmpVal); - masm.storeValue(tmpVal, element); - } else { - // Adding element to an unboxed array. - - // Bounds check (key == initLength) - Address initLengthAddr(obj, UnboxedArrayObject::offsetOfCapacityIndexAndInitializedLength()); - masm.load32(initLengthAddr, scratchReg); - masm.and32(Imm32(UnboxedArrayObject::InitializedLengthMask), scratchReg); - masm.branch32(Assembler::NotEqual, scratchReg, key, &failure); - - Address lengthAddr(obj, UnboxedArrayObject::offsetOfLength()); - - // Capacity check. - masm.checkUnboxedArrayCapacity(obj, Int32Key(key), scratchReg, &failure); - - // Increment initLength before write. - masm.add32(Imm32(1), initLengthAddr); - - // If length is now <= key, increment length before write. - Label skipIncrementLength; - masm.branch32(Assembler::Above, lengthAddr, key, &skipIncrementLength); - masm.add32(Imm32(1), lengthAddr); - masm.bind(&skipIncrementLength); - - // Load obj->elements. - masm.loadPtr(Address(obj, UnboxedArrayObject::offsetOfElements()), scratchReg); - - masm.Push(R0); - masm.loadValue(valueAddr, R0); - - // Write the value. No need for pre-barrier since we're not overwriting an old value. - BaseIndex address(obj, key, ScaleFromElemWidth(UnboxedTypeSize(unboxedType_))); - masm.storeUnboxedProperty(address, unboxedType_, - ConstantOrRegister(TypedOrValueRegister(R0)), &failurePopR0); - masm.Pop(R0); + // Write the value. No need for pre-barrier since we're not overwriting an old value. + ValueOperand tmpVal = regs.takeAnyValue(); + BaseIndex element(scratchReg, key, TimesEight); + masm.loadValue(valueAddr, tmpVal); + masm.storeValue(tmpVal, element); + regs.add(key); + if (cx->runtime()->gc.nursery.exists()) { + Register r = regs.takeAny(); + LiveGeneralRegisterSet saveRegs; + emitPostWriteBarrierSlot(masm, obj, tmpVal, r, saveRegs); + regs.add(r); } - EmitReturnFromIC(masm); - if (failurePopR0.used()) { - masm.bind(&failurePopR0); - masm.Pop(R0); - masm.jump(&failure); - } - // Failure case - fail but first unstow R0 and R1 masm.bind(&failureUnstow); EmitUnstowICValues(masm, 2); @@ -7072,18 +6854,6 @@ TryAttachLengthStub(JSContext* cx, JSScript* script, ICGetProp_Fallback* stub, H return true; } - if (obj->is() && res.isInt32()) { - JitSpew(JitSpew_BaselineIC, " Generating GetProp(UnboxedArray.length) stub"); - ICGetProp_UnboxedArrayLength::Compiler compiler(cx); - ICStub* newStub = compiler.getStub(compiler.getStubSpace(script)); - if (!newStub) - return false; - - *attached = true; - stub->addNewStub(newStub); - return true; - } - if (obj->is() && res.isInt32()) { JitSpew(JitSpew_BaselineIC, " Generating GetProp(ArgsObj.length %s) stub", obj->is() ? "Strict" : "Normal"); @@ -7848,30 +7618,6 @@ ICGetProp_ArrayLength::Compiler::generateStubCode(MacroAssembler& masm) return true; } -bool -ICGetProp_UnboxedArrayLength::Compiler::generateStubCode(MacroAssembler& masm) -{ - Label failure; - masm.branchTestObject(Assembler::NotEqual, R0, &failure); - - Register scratch = R1.scratchReg(); - - // Unbox R0 and guard it's an unboxed array. - Register obj = masm.extractObject(R0, ExtractTemp0); - masm.branchTestObjClass(Assembler::NotEqual, obj, scratch, &UnboxedArrayObject::class_, &failure); - - // Load obj->length. - masm.load32(Address(obj, UnboxedArrayObject::offsetOfLength()), scratch); - - masm.tagValue(JSVAL_TYPE_INT32, scratch, R0); - EmitReturnFromIC(masm); - - // Failure case - jump to next stub - masm.bind(&failure); - EmitStubGuardFailure(masm); - return true; -} - bool ICGetProp_StringLength::Compiler::generateStubCode(MacroAssembler& masm) { @@ -7974,7 +7720,7 @@ GuardReceiverObject(MacroAssembler& masm, ReceiverGuard::StackGuard guard, masm.loadPtr(groupAddress, scratch); masm.branchTestObjGroup(Assembler::NotEqual, object, scratch, failure); - if (guard.group->clasp() == &UnboxedPlainObject::class_ && !guard.shape) { + if (guard.group->maybeUnboxedLayout() && !guard.shape) { // Guard the unboxed object has no expando object. masm.branchPtr(Assembler::NotEqual, expandoAddress, ImmWord(0), failure); } @@ -7982,7 +7728,7 @@ GuardReceiverObject(MacroAssembler& masm, ReceiverGuard::StackGuard guard, if (guard.shape) { masm.loadPtr(shapeAddress, scratch); - if (guard.group && guard.group->clasp() == &UnboxedPlainObject::class_) { + if (guard.group && guard.group->maybeUnboxedLayout()) { // Guard the unboxed object has a matching expando object. masm.branchPtr(Assembler::Equal, expandoAddress, ImmWord(0), failure); Label done; @@ -9548,7 +9294,13 @@ ICSetProp_Unboxed::Compiler::generateStubCode(MacroAssembler& masm) masm.load32(Address(BaselineStubReg, ICSetProp_Unboxed::offsetOfFieldOffset()), scratch); BaseIndex address(object, scratch, TimesOne); - EmitUnboxedPreBarrierForBaseline(masm, address, fieldType_); + if (fieldType_ == JSVAL_TYPE_OBJECT) + EmitPreBarrier(masm, address, MIRType_Object); + else if (fieldType_ == JSVAL_TYPE_STRING) + EmitPreBarrier(masm, address, MIRType_String); + else + MOZ_ASSERT(!UnboxedTypeNeedsPreBarrier(fieldType_)); + masm.storeUnboxedProperty(address, fieldType_, ConstantOrRegister(TypedOrValueRegister(R1)), &failure); @@ -9995,16 +9747,13 @@ GetTemplateObjectForNative(JSContext* cx, HandleScript script, jsbytecode* pc, } if (native == js::array_concat) { - if (args.thisv().isObject() && - args.thisv().toObject().is() && - !args.thisv().toObject().isSingleton() && - !args.thisv().toObject().group()->hasUnanalyzedPreliminaryObjects()) + if (args.thisv().isObject() && args.thisv().toObject().is() && + !args.thisv().toObject().isSingleton()) { RootedObject proto(cx, args.thisv().toObject().getProto()); res.set(NewDenseEmptyArray(cx, proto, TenuredObject)); if (!res) return false; - res->setGroup(args.thisv().toObject().group()); return true; } @@ -12583,19 +12332,6 @@ ICGetElem_Dense::Clone(ICStubSpace* space, ICStub* firstMonitorStub, return New(space, other.jitCode(), firstMonitorStub, other.shape_); } -ICGetElem_UnboxedArray::ICGetElem_UnboxedArray(JitCode* stubCode, ICStub* firstMonitorStub, - ObjectGroup *group) - : ICMonitoredStub(GetElem_UnboxedArray, stubCode, firstMonitorStub), - group_(group) -{ } - -/* static */ ICGetElem_UnboxedArray* -ICGetElem_UnboxedArray::Clone(ICStubSpace* space, ICStub* firstMonitorStub, - ICGetElem_UnboxedArray& other) -{ - return New(space, other.jitCode(), firstMonitorStub, other.group_); -} - ICGetElem_TypedArray::ICGetElem_TypedArray(JitCode* stubCode, Shape* shape, Scalar::Type type) : ICStub(GetElem_TypedArray, stubCode), shape_(shape) @@ -12611,15 +12347,15 @@ ICGetElem_Arguments::Clone(ICStubSpace* space, ICStub* firstMonitorStub, return New(space, other.jitCode(), firstMonitorStub, other.which()); } -ICSetElem_DenseOrUnboxedArray::ICSetElem_DenseOrUnboxedArray(JitCode* stubCode, Shape* shape, ObjectGroup* group) - : ICUpdatedStub(SetElem_DenseOrUnboxedArray, stubCode), +ICSetElem_Dense::ICSetElem_Dense(JitCode* stubCode, Shape* shape, ObjectGroup* group) + : ICUpdatedStub(SetElem_Dense, stubCode), shape_(shape), group_(group) { } -ICSetElem_DenseOrUnboxedArrayAdd::ICSetElem_DenseOrUnboxedArrayAdd(JitCode* stubCode, ObjectGroup* group, - size_t protoChainDepth) - : ICUpdatedStub(SetElem_DenseOrUnboxedArrayAdd, stubCode), +ICSetElem_DenseAdd::ICSetElem_DenseAdd(JitCode* stubCode, ObjectGroup* group, + size_t protoChainDepth) + : ICUpdatedStub(SetElem_DenseAdd, stubCode), group_(group) { MOZ_ASSERT(protoChainDepth <= MAX_PROTO_CHAIN_DEPTH); @@ -12628,13 +12364,13 @@ ICSetElem_DenseOrUnboxedArrayAdd::ICSetElem_DenseOrUnboxedArrayAdd(JitCode* stub template ICUpdatedStub* -ICSetElemDenseOrUnboxedArrayAddCompiler::getStubSpecific(ICStubSpace* space, const AutoShapeVector* shapes) +ICSetElemDenseAddCompiler::getStubSpecific(ICStubSpace* space, const AutoShapeVector* shapes) { RootedObjectGroup group(cx, obj_->getGroup(cx)); if (!group) return nullptr; Rooted stubCode(cx, getStubCode()); - return ICStub::New>(space, stubCode, group, shapes); + return ICStub::New>(space, stubCode, group, shapes); } ICSetElem_TypedArray::ICSetElem_TypedArray(JitCode* stubCode, Shape* shape, Scalar::Type type, diff --git a/js/src/jit/BaselineIC.h b/js/src/jit/BaselineIC.h index f1ed5af567b0..a9b935bf05b9 100644 --- a/js/src/jit/BaselineIC.h +++ b/js/src/jit/BaselineIC.h @@ -397,13 +397,12 @@ class ICEntry _(GetElem_NativePrototypeCallScripted) \ _(GetElem_String) \ _(GetElem_Dense) \ - _(GetElem_UnboxedArray) \ _(GetElem_TypedArray) \ _(GetElem_Arguments) \ \ _(SetElem_Fallback) \ - _(SetElem_DenseOrUnboxedArray) \ - _(SetElem_DenseOrUnboxedArrayAdd) \ + _(SetElem_Dense) \ + _(SetElem_DenseAdd) \ _(SetElem_TypedArray) \ \ _(In_Fallback) \ @@ -429,7 +428,6 @@ class ICEntry \ _(GetProp_Fallback) \ _(GetProp_ArrayLength) \ - _(GetProp_UnboxedArrayLength) \ _(GetProp_Primitive) \ _(GetProp_StringLength) \ _(GetProp_Native) \ @@ -1739,33 +1737,31 @@ class ICNewArray_Fallback : public ICFallbackStub { friend class ICStubSpace; - HeapPtrObject templateObject_; + HeapPtrArrayObject templateObject_; - explicit ICNewArray_Fallback(JitCode* stubCode) - : ICFallbackStub(ICStub::NewArray_Fallback, stubCode), templateObject_(nullptr) + ICNewArray_Fallback(JitCode* stubCode, ArrayObject* templateObject) + : ICFallbackStub(ICStub::NewArray_Fallback, stubCode), templateObject_(templateObject) {} public: class Compiler : public ICStubCompiler { + RootedArrayObject templateObject; bool generateStubCode(MacroAssembler& masm); public: - explicit Compiler(JSContext* cx) - : ICStubCompiler(cx, ICStub::NewArray_Fallback) + Compiler(JSContext* cx, ArrayObject* templateObject) + : ICStubCompiler(cx, ICStub::NewArray_Fallback), + templateObject(cx, templateObject) {} ICStub* getStub(ICStubSpace* space) { - return ICStub::New(space, getStubCode()); + return ICStub::New(space, getStubCode(), templateObject); } }; - HeapPtrObject& templateObject() { + HeapPtrArrayObject& templateObject() { return templateObject_; } - - void setTemplateObject(JSObject* obj) { - templateObject_ = obj; - } }; class ICNewObject_Fallback : public ICFallbackStub @@ -3030,54 +3026,6 @@ class ICGetElem_Dense : public ICMonitoredStub }; }; -class ICGetElem_UnboxedArray : public ICMonitoredStub -{ - friend class ICStubSpace; - - HeapPtrObjectGroup group_; - - ICGetElem_UnboxedArray(JitCode* stubCode, ICStub* firstMonitorStub, ObjectGroup* group); - - public: - static ICGetElem_UnboxedArray* Clone(ICStubSpace* space, ICStub* firstMonitorStub, - ICGetElem_UnboxedArray& other); - - static size_t offsetOfGroup() { - return offsetof(ICGetElem_UnboxedArray, group_); - } - - HeapPtrObjectGroup& group() { - return group_; - } - - class Compiler : public ICStubCompiler { - ICStub* firstMonitorStub_; - RootedObjectGroup group_; - JSValueType elementType_; - - protected: - bool generateStubCode(MacroAssembler& masm); - - virtual int32_t getKey() const { - return static_cast(kind) | - (static_cast(elementType_) << 16); - } - - public: - Compiler(JSContext* cx, ICStub* firstMonitorStub, ObjectGroup* group) - : ICStubCompiler(cx, ICStub::GetElem_UnboxedArray), - firstMonitorStub_(firstMonitorStub), - group_(cx, group), - elementType_(group->unboxedLayout().elementType()) - {} - - ICStub* getStub(ICStubSpace* space) { - return ICStub::New(space, getStubCode(), firstMonitorStub_, - group_); - } - }; -}; - // Enum for stubs handling a combination of typed arrays and typed objects. enum TypedThingLayout { Layout_TypedArray, @@ -3235,21 +3183,21 @@ class ICSetElem_Fallback : public ICFallbackStub }; }; -class ICSetElem_DenseOrUnboxedArray : public ICUpdatedStub +class ICSetElem_Dense : public ICUpdatedStub { friend class ICStubSpace; - HeapPtrShape shape_; // null for unboxed arrays + HeapPtrShape shape_; HeapPtrObjectGroup group_; - ICSetElem_DenseOrUnboxedArray(JitCode* stubCode, Shape* shape, ObjectGroup* group); + ICSetElem_Dense(JitCode* stubCode, Shape* shape, ObjectGroup* group); public: static size_t offsetOfShape() { - return offsetof(ICSetElem_DenseOrUnboxedArray, shape_); + return offsetof(ICSetElem_Dense, shape_); } static size_t offsetOfGroup() { - return offsetof(ICSetElem_DenseOrUnboxedArray, group_); + return offsetof(ICSetElem_Dense, group_); } HeapPtrShape& shape() { @@ -3261,41 +3209,33 @@ class ICSetElem_DenseOrUnboxedArray : public ICUpdatedStub class Compiler : public ICStubCompiler { RootedShape shape_; - RootedObjectGroup group_; - JSValueType unboxedType_; + + // Compiler is only live on stack during compilation, it should + // outlive any RootedObjectGroup it's passed. So it can just + // use the handle. + HandleObjectGroup group_; bool generateStubCode(MacroAssembler& masm); public: - virtual int32_t getKey() const { - return static_cast(kind) | - (static_cast(unboxedType_) << 16); - } - Compiler(JSContext* cx, Shape* shape, HandleObjectGroup group) - : ICStubCompiler(cx, ICStub::SetElem_DenseOrUnboxedArray), + : ICStubCompiler(cx, ICStub::SetElem_Dense), shape_(cx, shape), - group_(cx, group), - unboxedType_(shape ? JSVAL_TYPE_MAGIC : group->unboxedLayout().elementType()) + group_(group) {} ICUpdatedStub* getStub(ICStubSpace* space) { - ICSetElem_DenseOrUnboxedArray* stub = - ICStub::New(space, getStubCode(), shape_, group_); + ICSetElem_Dense* stub = ICStub::New(space, getStubCode(), shape_, group_); if (!stub || !stub->initUpdatingChain(cx, space)) return nullptr; return stub; } - - bool needsUpdateStubs() { - return unboxedType_ == JSVAL_TYPE_MAGIC || unboxedType_ == JSVAL_TYPE_OBJECT; - } }; }; -template class ICSetElem_DenseOrUnboxedArrayAddImpl; +template class ICSetElem_DenseAddImpl; -class ICSetElem_DenseOrUnboxedArrayAdd : public ICUpdatedStub +class ICSetElem_DenseAdd : public ICUpdatedStub { friend class ICStubSpace; @@ -3305,11 +3245,11 @@ class ICSetElem_DenseOrUnboxedArrayAdd : public ICUpdatedStub protected: HeapPtrObjectGroup group_; - ICSetElem_DenseOrUnboxedArrayAdd(JitCode* stubCode, ObjectGroup* group, size_t protoChainDepth); + ICSetElem_DenseAdd(JitCode* stubCode, ObjectGroup* group, size_t protoChainDepth); public: static size_t offsetOfGroup() { - return offsetof(ICSetElem_DenseOrUnboxedArrayAdd, group_); + return offsetof(ICSetElem_DenseAdd, group_); } HeapPtrObjectGroup& group() { @@ -3321,29 +3261,28 @@ class ICSetElem_DenseOrUnboxedArrayAdd : public ICUpdatedStub } template - ICSetElem_DenseOrUnboxedArrayAddImpl* toImplUnchecked() { - return static_cast*>(this); + ICSetElem_DenseAddImpl* toImplUnchecked() { + return static_cast*>(this); } template - ICSetElem_DenseOrUnboxedArrayAddImpl* toImpl() { + ICSetElem_DenseAddImpl* toImpl() { MOZ_ASSERT(ProtoChainDepth == protoChainDepth()); return toImplUnchecked(); } }; template -class ICSetElem_DenseOrUnboxedArrayAddImpl : public ICSetElem_DenseOrUnboxedArrayAdd +class ICSetElem_DenseAddImpl : public ICSetElem_DenseAdd { friend class ICStubSpace; - // Note: for unboxed arrays, the first shape is null. static const size_t NumShapes = ProtoChainDepth + 1; mozilla::Array shapes_; - ICSetElem_DenseOrUnboxedArrayAddImpl(JitCode* stubCode, ObjectGroup* group, - const AutoShapeVector* shapes) - : ICSetElem_DenseOrUnboxedArrayAdd(stubCode, group, ProtoChainDepth) + ICSetElem_DenseAddImpl(JitCode* stubCode, ObjectGroup* group, + const AutoShapeVector* shapes) + : ICSetElem_DenseAdd(stubCode, group, ProtoChainDepth) { MOZ_ASSERT(shapes->length() == NumShapes); for (size_t i = 0; i < NumShapes; i++) @@ -3352,52 +3291,40 @@ class ICSetElem_DenseOrUnboxedArrayAddImpl : public ICSetElem_DenseOrUnboxedArra public: void traceShapes(JSTracer* trc) { - for (size_t i = 0; i < NumShapes; i++) { - if (shapes_[i]) - TraceEdge(trc, &shapes_[i], "baseline-setelem-denseadd-stub-shape"); - } + for (size_t i = 0; i < NumShapes; i++) + TraceEdge(trc, &shapes_[i], "baseline-setelem-denseadd-stub-shape"); } Shape* shape(size_t i) const { MOZ_ASSERT(i < NumShapes); return shapes_[i]; } static size_t offsetOfShape(size_t idx) { - return offsetof(ICSetElem_DenseOrUnboxedArrayAddImpl, shapes_) + idx * sizeof(HeapPtrShape); + return offsetof(ICSetElem_DenseAddImpl, shapes_) + idx * sizeof(HeapPtrShape); } }; -class ICSetElemDenseOrUnboxedArrayAddCompiler : public ICStubCompiler { +class ICSetElemDenseAddCompiler : public ICStubCompiler { RootedObject obj_; size_t protoChainDepth_; - JSValueType unboxedType_; bool generateStubCode(MacroAssembler& masm); protected: virtual int32_t getKey() const { - return static_cast(kind) | - (static_cast(protoChainDepth_) << 16) | - (static_cast(unboxedType_) << 19); + return static_cast(kind) | (static_cast(protoChainDepth_) << 16); } public: - ICSetElemDenseOrUnboxedArrayAddCompiler(JSContext* cx, HandleObject obj, size_t protoChainDepth) - : ICStubCompiler(cx, ICStub::SetElem_DenseOrUnboxedArrayAdd), + ICSetElemDenseAddCompiler(JSContext* cx, HandleObject obj, size_t protoChainDepth) + : ICStubCompiler(cx, ICStub::SetElem_DenseAdd), obj_(cx, obj), - protoChainDepth_(protoChainDepth), - unboxedType_(obj->is() - ? obj->as().elementType() - : JSVAL_TYPE_MAGIC) + protoChainDepth_(protoChainDepth) {} template ICUpdatedStub* getStubSpecific(ICStubSpace* space, const AutoShapeVector* shapes); ICUpdatedStub* getStub(ICStubSpace* space); - - bool needsUpdateStubs() { - return unboxedType_ == JSVAL_TYPE_MAGIC || unboxedType_ == JSVAL_TYPE_OBJECT; - } }; // Accesses scalar elements of a typed array or typed object. @@ -4049,30 +3976,6 @@ class ICGetProp_ArrayLength : public ICStub }; }; -// Stub for accessing an unboxed array's length. -class ICGetProp_UnboxedArrayLength : public ICStub -{ - friend class ICStubSpace; - - explicit ICGetProp_UnboxedArrayLength(JitCode* stubCode) - : ICStub(GetProp_UnboxedArrayLength, stubCode) - {} - - public: - class Compiler : public ICStubCompiler { - bool generateStubCode(MacroAssembler& masm); - - public: - explicit Compiler(JSContext* cx) - : ICStubCompiler(cx, ICStub::GetProp_UnboxedArrayLength) - {} - - ICStub* getStub(ICStubSpace* space) { - return ICStub::New(space, getStubCode()); - } - }; -}; - // Stub for accessing a property on a primitive's prototype. class ICGetProp_Primitive : public ICMonitoredStub { diff --git a/js/src/jit/BaselineInspector.cpp b/js/src/jit/BaselineInspector.cpp index a68d07f5c570..f52bc92d2698 100644 --- a/js/src/jit/BaselineInspector.cpp +++ b/js/src/jit/BaselineInspector.cpp @@ -21,9 +21,9 @@ SetElemICInspector::sawOOBDenseWrite() const if (!icEntry_) return false; - // Check for an element adding stub. + // Check for a SetElem_DenseAdd stub. for (ICStub* stub = icEntry_->firstStub(); stub; stub = stub->next()) { - if (stub->isSetElem_DenseOrUnboxedArrayAdd()) + if (stub->isSetElem_DenseAdd()) return true; } @@ -59,7 +59,7 @@ SetElemICInspector::sawDenseWrite() const // Check for a SetElem_DenseAdd or SetElem_Dense stub. for (ICStub* stub = icEntry_->firstStub(); stub; stub = stub->next()) { - if (stub->isSetElem_DenseOrUnboxedArrayAdd() || stub->isSetElem_DenseOrUnboxedArray()) + if (stub->isSetElem_DenseAdd() || stub->isSetElem_Dense()) return true; } return false; diff --git a/js/src/jit/CodeGenerator.cpp b/js/src/jit/CodeGenerator.cpp index 59952a798537..b4d2e3095566 100644 --- a/js/src/jit/CodeGenerator.cpp +++ b/js/src/jit/CodeGenerator.cpp @@ -2339,18 +2339,6 @@ CodeGenerator::visitGetPropertyPolymorphicT(LGetPropertyPolymorphicT* ins) emitGetPropertyPolymorphic(ins, obj, temp, output); } -template -static void -EmitUnboxedPreBarrier(MacroAssembler &masm, T address, JSValueType type) -{ - if (type == JSVAL_TYPE_OBJECT) - masm.patchableCallPreBarrier(address, MIRType_Object); - else if (type == JSVAL_TYPE_STRING) - masm.patchableCallPreBarrier(address, MIRType_String); - else - MOZ_ASSERT(!UnboxedTypeNeedsPreBarrier(type)); -} - void CodeGenerator::emitSetPropertyPolymorphic(LInstruction* ins, Register obj, Register scratch, const ConstantOrRegister& value) @@ -2389,7 +2377,13 @@ CodeGenerator::emitSetPropertyPolymorphic(LInstruction* ins, Register obj, Regis receiver.group->unboxedLayout().lookup(mir->name()); Address propertyAddr(obj, UnboxedPlainObject::offsetOfData() + property->offset); - EmitUnboxedPreBarrier(masm, propertyAddr, property->type); + if (property->type == JSVAL_TYPE_OBJECT) + masm.patchableCallPreBarrier(propertyAddr, MIRType_Object); + else if (property->type == JSVAL_TYPE_STRING) + masm.patchableCallPreBarrier(propertyAddr, MIRType_String); + else + MOZ_ASSERT(!UnboxedTypeNeedsPreBarrier(property->type)); + masm.storeUnboxedProperty(propertyAddr, property->type, value, nullptr); } @@ -2431,9 +2425,7 @@ CodeGenerator::visitSetPropertyPolymorphicT(LSetPropertyPolymorphicT* ins) void CodeGenerator::visitElements(LElements* lir) { - Address elements(ToRegister(lir->object()), - lir->mir()->unboxed() ? UnboxedArrayObject::offsetOfElements() - : NativeObject::offsetOfElements()); + Address elements(ToRegister(lir->object()), NativeObject::offsetOfElements()); masm.loadPtr(elements, ToRegister(lir->output())); } @@ -4064,11 +4056,6 @@ class OutOfLineNewArray : public OutOfLineCodeBase } }; -typedef JSObject* (*NewArrayOperationFn)(JSContext*, HandleScript, jsbytecode*, uint32_t, - NewObjectKind); -static const VMFunction NewArrayOperationInfo = - FunctionInfo(NewArrayOperation); - typedef ArrayObject* (*NewDenseArrayFn)(ExclusiveContext*, uint32_t, HandleObjectGroup, AllocatingBehaviour, bool); static const VMFunction NewDenseArrayInfo = FunctionInfo(NewDenseArray); @@ -4082,22 +4069,15 @@ CodeGenerator::visitNewArrayCallVM(LNewArray* lir) saveLive(lir); JSObject* templateObject = lir->mir()->templateObject(); + ObjectGroup* group = + templateObject->isSingleton() ? nullptr : templateObject->group(); - if (templateObject && !templateObject->is()) { - pushArg(Imm32(lir->mir()->convertDoubleElements())); - pushArg(Imm32(lir->mir()->allocatingBehaviour())); - pushArg(ImmGCPtr(templateObject->group())); - pushArg(Imm32(lir->mir()->count())); + pushArg(Imm32(lir->mir()->convertDoubleElements())); + pushArg(Imm32(lir->mir()->allocatingBehaviour())); + pushArg(ImmGCPtr(group)); + pushArg(Imm32(lir->mir()->count())); - callVM(NewDenseArrayInfo, lir); - } else { - pushArg(Imm32(GenericObject)); - pushArg(Imm32(lir->mir()->count())); - pushArg(ImmPtr(lir->mir()->pc())); - pushArg(ImmGCPtr(lir->mir()->block()->info().script())); - - callVM(NewArrayOperationInfo, lir); - } + callVM(NewDenseArrayInfo, lir); if (ReturnReg != objReg) masm.movePtr(ReturnReg, objReg); @@ -4167,7 +4147,7 @@ CodeGenerator::visitNewArray(LNewArray* lir) { Register objReg = ToRegister(lir->output()); Register tempReg = ToRegister(lir->temp()); - JSObject* templateObject = lir->mir()->templateObject(); + ArrayObject* templateObject = lir->mir()->templateObject(); DebugOnly count = lir->mir()->count(); MOZ_ASSERT(count < NativeObject::NELEMENTS_LIMIT); @@ -6296,30 +6276,6 @@ CodeGenerator::visitSetInitializedLength(LSetInitializedLength* lir) masm.bumpKey(&index, -1); } -void -CodeGenerator::visitUnboxedArrayLength(LUnboxedArrayLength* lir) -{ - Register obj = ToRegister(lir->object()); - Register result = ToRegister(lir->output()); - masm.load32(Address(obj, UnboxedArrayObject::offsetOfLength()), result); -} - -void -CodeGenerator::visitUnboxedArrayInitializedLength(LUnboxedArrayInitializedLength* lir) -{ - Register obj = ToRegister(lir->object()); - Register result = ToRegister(lir->output()); - masm.load32(Address(obj, UnboxedArrayObject::offsetOfCapacityIndexAndInitializedLength()), result); - masm.and32(Imm32(UnboxedArrayObject::InitializedLengthMask), result); -} - -void -CodeGenerator::visitIncrementUnboxedArrayInitializedLength(LIncrementUnboxedArrayInitializedLength* lir) -{ - Register obj = ToRegister(lir->object()); - masm.add32(Imm32(1), Address(obj, UnboxedArrayObject::offsetOfCapacityIndexAndInitializedLength())); -} - void CodeGenerator::visitNotO(LNotO* lir) { @@ -6524,21 +6480,18 @@ CodeGenerator::emitStoreHoleCheck(Register elements, const LAllocation* index, bailoutFrom(&bail, snapshot); } -static ConstantOrRegister -ToConstantOrRegister(const LAllocation* value, MIRType valueType) -{ - if (value->isConstant()) - return ConstantOrRegister(*value->toConstant()); - return TypedOrValueRegister(valueType, ToAnyRegister(value)); -} - void CodeGenerator::emitStoreElementTyped(const LAllocation* value, MIRType valueType, MIRType elementType, Register elements, const LAllocation* index, int32_t offsetAdjustment) { - ConstantOrRegister v = ToConstantOrRegister(value, valueType); + ConstantOrRegister v; + if (value->isConstant()) + v = ConstantOrRegister(*value->toConstant()); + else + v = TypedOrValueRegister(valueType, ToAnyRegister(value)); + if (index->isConstant()) { Address dest(elements, ToInt32(index) * sizeof(js::Value) + offsetAdjustment); masm.storeUnboxedValue(v, valueType, dest, elementType); @@ -6595,45 +6548,19 @@ CodeGenerator::visitStoreElementHoleT(LStoreElementHoleT* lir) 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(); - JSValueType unboxedType = lir->mir()->unboxedType(); - if (unboxedType == JSVAL_TYPE_MAGIC) { - Address initLength(elements, ObjectElements::offsetOfInitializedLength()); - masm.branchKey(Assembler::BelowOrEqual, initLength, ToInt32Key(index), ool->entry()); + // OOL path if index >= initializedLength. + Address initLength(elements, ObjectElements::offsetOfInitializedLength()); + masm.branchKey(Assembler::BelowOrEqual, initLength, ToInt32Key(index), ool->entry()); - if (lir->mir()->needsBarrier()) - emitPreBarrier(elements, index); + if (lir->mir()->needsBarrier()) + emitPreBarrier(elements, index); - masm.bind(ool->rejoinStore()); - emitStoreElementTyped(lir->value(), lir->mir()->value()->type(), lir->mir()->elementType(), - elements, index, 0); - } else { - Register temp = ToRegister(lir->getTemp(0)); - Address initLength(obj, UnboxedArrayObject::offsetOfCapacityIndexAndInitializedLength()); - masm.load32(initLength, temp); - masm.and32(Imm32(UnboxedArrayObject::InitializedLengthMask), temp); - masm.branchKey(Assembler::BelowOrEqual, temp, ToInt32Key(index), ool->entry()); - - ConstantOrRegister v = ToConstantOrRegister(lir->value(), lir->mir()->value()->type()); - - if (index->isConstant()) { - Address address(elements, ToInt32(index) * UnboxedTypeSize(unboxedType)); - EmitUnboxedPreBarrier(masm, address, unboxedType); - - masm.bind(ool->rejoinStore()); - masm.storeUnboxedProperty(address, unboxedType, v, nullptr); - } else { - BaseIndex address(elements, ToRegister(index), - ScaleFromElemWidth(UnboxedTypeSize(unboxedType))); - EmitUnboxedPreBarrier(masm, address, unboxedType); - - masm.bind(ool->rejoinStore()); - masm.storeUnboxedProperty(address, unboxedType, v, nullptr); - } - } + masm.bind(ool->rejoinStore()); + emitStoreElementTyped(lir->value(), lir->mir()->value()->type(), lir->mir()->elementType(), + elements, index, 0); masm.bind(ool->rejoin()); } @@ -6644,54 +6571,29 @@ CodeGenerator::visitStoreElementHoleV(LStoreElementHoleV* lir) 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); - JSValueType unboxedType = lir->mir()->unboxedType(); - if (unboxedType == JSVAL_TYPE_MAGIC) { - Address initLength(elements, ObjectElements::offsetOfInitializedLength()); - masm.branchKey(Assembler::BelowOrEqual, initLength, ToInt32Key(index), ool->entry()); + // OOL path if index >= initializedLength. + Address initLength(elements, ObjectElements::offsetOfInitializedLength()); + masm.branchKey(Assembler::BelowOrEqual, initLength, ToInt32Key(index), ool->entry()); - if (lir->mir()->needsBarrier()) - emitPreBarrier(elements, index); + if (lir->mir()->needsBarrier()) + emitPreBarrier(elements, index); - masm.bind(ool->rejoinStore()); - if (index->isConstant()) - masm.storeValue(value, Address(elements, ToInt32(index) * sizeof(js::Value))); - else - masm.storeValue(value, BaseIndex(elements, ToRegister(index), TimesEight)); - } else { - Register temp = ToRegister(lir->getTemp(0)); - Address initLength(obj, UnboxedArrayObject::offsetOfCapacityIndexAndInitializedLength()); - masm.load32(initLength, temp); - masm.and32(Imm32(UnboxedArrayObject::InitializedLengthMask), temp); - masm.branchKey(Assembler::BelowOrEqual, temp, ToInt32Key(index), ool->entry()); - - if (index->isConstant()) { - Address address(elements, ToInt32(index) * UnboxedTypeSize(unboxedType)); - EmitUnboxedPreBarrier(masm, address, unboxedType); - - masm.bind(ool->rejoinStore()); - masm.storeUnboxedProperty(address, unboxedType, ConstantOrRegister(value), nullptr); - } else { - BaseIndex address(elements, ToRegister(index), - ScaleFromElemWidth(UnboxedTypeSize(unboxedType))); - EmitUnboxedPreBarrier(masm, address, unboxedType); - - masm.bind(ool->rejoinStore()); - masm.storeUnboxedProperty(address, unboxedType, ConstantOrRegister(value), nullptr); - } - } + 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()); } -typedef bool (*SetDenseOrUnboxedArrayElementFn)(JSContext*, HandleObject, int32_t, - HandleValue, bool strict); -static const VMFunction SetDenseOrUnboxedArrayElementInfo = - FunctionInfo(SetDenseOrUnboxedArrayElement); +typedef bool (*SetDenseElementFn)(JSContext*, HandleNativeObject, int32_t, HandleValue, + bool strict); +static const VMFunction SetDenseElementInfo = FunctionInfo(SetDenseElement); void CodeGenerator::visitOutOfLineStoreElementHole(OutOfLineStoreElementHole* ool) @@ -6701,8 +6603,6 @@ CodeGenerator::visitOutOfLineStoreElementHole(OutOfLineStoreElementHole* ool) const LAllocation* index; MIRType valueType; ConstantOrRegister value; - JSValueType unboxedType; - LDefinition *temp = nullptr; if (ins->isStoreElementHoleV()) { LStoreElementHoleV* store = ins->toStoreElementHoleV(); @@ -6711,8 +6611,6 @@ CodeGenerator::visitOutOfLineStoreElementHole(OutOfLineStoreElementHole* ool) index = store->index(); valueType = store->mir()->value()->type(); value = TypedOrValueRegister(ToValue(store, LStoreElementHoleV::Value)); - unboxedType = store->mir()->unboxedType(); - temp = store->getTemp(0); } else { LStoreElementHoleT* store = ins->toStoreElementHoleT(); object = ToRegister(store->object()); @@ -6723,8 +6621,6 @@ CodeGenerator::visitOutOfLineStoreElementHole(OutOfLineStoreElementHole* ool) value = ConstantOrRegister(*store->value()->toConstant()); else value = TypedOrValueRegister(valueType, ToAnyRegister(store->value())); - unboxedType = store->mir()->unboxedType(); - temp = store->getTemp(0); } // If index == initializedLength, try to bump the initialized length inline. @@ -6733,55 +6629,33 @@ CodeGenerator::visitOutOfLineStoreElementHole(OutOfLineStoreElementHole* ool) Label callStub; #ifdef JS_CODEGEN_MIPS // Had to reimplement for MIPS because there are no flags. - if (unboxedType == JSVAL_TYPE_MAGIC) { - Address initLength(elements, ObjectElements::offsetOfInitializedLength()); - masm.branchKey(Assembler::NotEqual, initLength, ToInt32Key(index), &callStub); - } else { - Address initLength(obj, UnboxedArrayObject::offsetOfCapacityIndexAndInitializedLength()); - masm.load32(initLength, ToRegister(temp)); - masm.and32(Imm32(UnboxedArrayObject::InitializedLengthMask), ToRegister(temp)); - masm.branchKey(Assembler::NotEqual, ToRegister(temp), ToInt32Key(index), &callStub); - } + Address initLength(elements, ObjectElements::offsetOfInitializedLength()); + masm.branchKey(Assembler::NotEqual, initLength, ToInt32Key(index), &callStub); #else masm.j(Assembler::NotEqual, &callStub); #endif Int32Key key = ToInt32Key(index); - if (unboxedType == JSVAL_TYPE_MAGIC) { - // Check array capacity. - masm.branchKey(Assembler::BelowOrEqual, Address(elements, ObjectElements::offsetOfCapacity()), - key, &callStub); + // 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 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); + // 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); - } else { - // Check array capacity. - masm.checkUnboxedArrayCapacity(object, key, ToRegister(temp), &callStub); + masm.bumpKey(&key, -1); - // Update initialized length. - masm.add32(Imm32(1), Address(object, UnboxedArrayObject::offsetOfCapacityIndexAndInitializedLength())); - - // Update length if length < initializedLength. - Address lengthAddr(object, UnboxedArrayObject::offsetOfLength()); - Label dontUpdate; - masm.branchKey(Assembler::Above, lengthAddr, key, &dontUpdate); - masm.add32(Imm32(1), lengthAddr); - masm.bind(&dontUpdate); - } - - if (ins->isStoreElementHoleT() && unboxedType == JSVAL_TYPE_MAGIC && valueType != MIRType_Double) { + 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. @@ -6803,7 +6677,7 @@ CodeGenerator::visitOutOfLineStoreElementHole(OutOfLineStoreElementHole* ool) else pushArg(ToRegister(index)); pushArg(object); - callVM(SetDenseOrUnboxedArrayElementInfo, ins); + callVM(SetDenseElementInfo, ins); restoreLive(ins); masm.jump(ool->rejoin()); @@ -8706,27 +8580,13 @@ CodeGenerator::visitLoadElementHole(LLoadElementHole* lir) // If the index is out of bounds, load |undefined|. Otherwise, load the // value. Label undefined, done; - if (lir->index()->isConstant()) + if (lir->index()->isConstant()) { masm.branch32(Assembler::BelowOrEqual, initLength, Imm32(ToInt32(lir->index())), &undefined); - else - masm.branch32(Assembler::BelowOrEqual, initLength, ToRegister(lir->index()), &undefined); - - if (mir->unboxedType() != JSVAL_TYPE_MAGIC) { - size_t width = UnboxedTypeSize(mir->unboxedType()); - if (lir->index()->isConstant()) { - Address addr(elements, ToInt32(lir->index()) * width); - masm.loadUnboxedProperty(addr, mir->unboxedType(), out); - } else { - BaseIndex addr(elements, ToRegister(lir->index()), ScaleFromElemWidth(width)); - masm.loadUnboxedProperty(addr, mir->unboxedType(), out); - } + NativeObject::elementsSizeMustNotOverflow(); + masm.loadValue(Address(elements, ToInt32(lir->index()) * sizeof(Value)), out); } else { - if (lir->index()->isConstant()) { - NativeObject::elementsSizeMustNotOverflow(); - masm.loadValue(Address(elements, ToInt32(lir->index()) * sizeof(Value)), out); - } else { - masm.loadValue(BaseObjectElementIndex(elements, ToRegister(lir->index())), out); - } + masm.branch32(Assembler::BelowOrEqual, initLength, ToRegister(lir->index()), &undefined); + masm.loadValue(BaseObjectElementIndex(elements, ToRegister(lir->index())), out); } // If a hole check is needed, and the value wasn't a hole, we're done. diff --git a/js/src/jit/CodeGenerator.h b/js/src/jit/CodeGenerator.h index f35e69a37951..be99521b0085 100644 --- a/js/src/jit/CodeGenerator.h +++ b/js/src/jit/CodeGenerator.h @@ -188,9 +188,6 @@ class CodeGenerator : public CodeGeneratorSpecific void visitSubstr(LSubstr* lir); void visitInitializedLength(LInitializedLength* lir); void visitSetInitializedLength(LSetInitializedLength* lir); - void visitUnboxedArrayLength(LUnboxedArrayLength* lir); - void visitUnboxedArrayInitializedLength(LUnboxedArrayInitializedLength* lir); - void visitIncrementUnboxedArrayInitializedLength(LIncrementUnboxedArrayInitializedLength* lir); void visitNotO(LNotO* ins); void visitNotV(LNotV* ins); void visitBoundsCheck(LBoundsCheck* lir); diff --git a/js/src/jit/IonBuilder.cpp b/js/src/jit/IonBuilder.cpp index 3995917cb429..2f09e29f073c 100644 --- a/js/src/jit/IonBuilder.cpp +++ b/js/src/jit/IonBuilder.cpp @@ -6477,20 +6477,35 @@ bool IonBuilder::jsop_newarray(uint32_t count) { JSObject* templateObject = inspector->getTemplateObject(pc); - gc::InitialHeap heap; - MConstant* templateConst; - - if (templateObject) { - heap = templateObject->group()->initialHeap(constraints()); - templateConst = MConstant::NewConstraintlessObject(alloc(), templateObject); - } else { - heap = gc::DefaultHeap; - templateConst = MConstant::New(alloc(), NullValue()); + if (!templateObject) { + if (info().analysisMode() == Analysis_ArgumentsUsage) { + MUnknownValue* unknown = MUnknownValue::New(alloc()); + current->add(unknown); + current->push(unknown); + return true; + } + return abort("No template object for NEWARRAY"); } + + MOZ_ASSERT(templateObject->is()); + if (templateObject->group()->unknownProperties()) { + if (info().analysisMode() == Analysis_ArgumentsUsage) { + MUnknownValue* unknown = MUnknownValue::New(alloc()); + current->add(unknown); + current->push(unknown); + return true; + } + // We will get confused in jsop_initelem_array if we can't find the + // object group being initialized. + return abort("New array has unknown properties"); + } + + MConstant* templateConst = MConstant::NewConstraintlessObject(alloc(), templateObject); current->add(templateConst); MNewArray* ins = MNewArray::New(alloc(), constraints(), count, templateConst, - heap, NewArray_FullyAllocating, pc); + templateObject->group()->initialHeap(constraints()), + NewArray_FullyAllocating); current->add(ins); current->push(ins); return true; @@ -6566,20 +6581,10 @@ IonBuilder::jsop_initelem_array() // intializer, and that arrays are marked as non-packed when writing holes // to them during initialization. bool needStub = false; - JSValueType unboxedType = JSVAL_TYPE_MAGIC; - if (shouldAbortOnPreliminaryGroups(obj)) { - needStub = true; - } else if (!obj->resultTypeSet() || obj->resultTypeSet()->getObjectCount() != 1) { + if (obj->isUnknownValue() || shouldAbortOnPreliminaryGroups(obj)) { needStub = true; } else { - MOZ_ASSERT(obj->resultTypeSet()->getObjectCount() == 1); TypeSet::ObjectKey* initializer = obj->resultTypeSet()->getObject(0); - if (initializer->clasp() == &UnboxedArrayObject::class_) { - if (initializer->group()->unboxedLayout().nativeGroup()) - needStub = true; - else - unboxedType = initializer->group()->unboxedLayout().elementType(); - } if (value->type() == MIRType_MagicHole) { if (!initializer->hasFlags(constraints(), OBJECT_FLAG_NON_PACKED)) needStub = true; @@ -6592,6 +6597,9 @@ IonBuilder::jsop_initelem_array() } } + if (NeedsPostBarrier(info(), value)) + current->add(MPostWriteBarrier::New(alloc(), obj, value)); + if (needStub) { MCallInitElementArray* store = MCallInitElementArray::New(alloc(), obj, GET_UINT24(pc), value); current->add(store); @@ -6602,44 +6610,29 @@ IonBuilder::jsop_initelem_array() current->add(id); // Get the elements vector. - MElements* elements = MElements::New(alloc(), obj, unboxedType != JSVAL_TYPE_MAGIC); + MElements* elements = MElements::New(alloc(), obj); current->add(elements); - if (unboxedType != JSVAL_TYPE_MAGIC) { - // Note: storeUnboxedValue takes care of any post barriers on the value. - storeUnboxedValue(obj, elements, 0, id, unboxedType, value); - - MInstruction* increment = MIncrementUnboxedArrayInitializedLength::New(alloc(), obj); - current->add(increment); - - if (!resumeAfter(increment)) - return false; - } else { - if (NeedsPostBarrier(info(), value)) - current->add(MPostWriteBarrier::New(alloc(), obj, value)); - - if (obj->toNewArray()->convertDoubleElements()) { - MInstruction* valueDouble = MToDouble::New(alloc(), value); - current->add(valueDouble); - value = valueDouble; - } - - // Store the value. - MStoreElement* store = MStoreElement::New(alloc(), elements, id, value, - /* needsHoleCheck = */ false); - current->add(store); - - // Update the initialized length. (The template object for this array has - // the array's ultimate length, so the length field is already correct: no - // updating needed.) - MSetInitializedLength* initLength = MSetInitializedLength::New(alloc(), elements, id); - current->add(initLength); - - if (!resumeAfter(initLength)) - return false; + if (obj->toNewArray()->convertDoubleElements()) { + MInstruction* valueDouble = MToDouble::New(alloc(), value); + current->add(valueDouble); + value = valueDouble; } - return true; + // Store the value. + MStoreElement* store = MStoreElement::New(alloc(), elements, id, value, /* needsHoleCheck = */ false); + current->add(store); + + // Update the initialized length. (The template object for this array has + // the array's ultimate length, so the length field is already correct: no + // updating needed.) + MSetInitializedLength* initLength = MSetInitializedLength::New(alloc(), elements, id); + current->add(initLength); + + if (!resumeAfter(initLength)) + return false; + + return true; } bool @@ -8122,12 +8115,9 @@ IonBuilder::getElemTryDense(bool* emitted, MDefinition* obj, MDefinition* index) { MOZ_ASSERT(*emitted == false); - JSValueType unboxedType = UnboxedArrayElementType(constraints(), obj, index); - if (unboxedType == JSVAL_TYPE_MAGIC) { - if (!ElementAccessIsDenseNative(constraints(), obj, index)) { - trackOptimizationOutcome(TrackedOutcome::AccessNotDense); - return true; - } + if (!ElementAccessIsDenseNative(constraints(), obj, index)) { + trackOptimizationOutcome(TrackedOutcome::AccessNotDense); + return true; } // Don't generate a fast path if there have been bounds check failures @@ -8144,7 +8134,8 @@ IonBuilder::getElemTryDense(bool* emitted, MDefinition* obj, MDefinition* index) return true; } - if (!jsop_getelem_dense(obj, index, unboxedType)) + // Emit dense getelem variant. + if (!jsop_getelem_dense(obj, index)) return false; trackOptimizationSuccess(); @@ -8456,7 +8447,7 @@ IonBuilder::getElemTryCache(bool* emitted, MDefinition* obj, MDefinition* index) } bool -IonBuilder::jsop_getelem_dense(MDefinition* obj, MDefinition* index, JSValueType unboxedType) +IonBuilder::jsop_getelem_dense(MDefinition* obj, MDefinition* index) { TemporaryTypeSet* types = bytecodeTypes(pc); @@ -8480,7 +8471,7 @@ IonBuilder::jsop_getelem_dense(MDefinition* obj, MDefinition* index, JSValueType !ElementAccessHasExtraIndexedProperty(constraints(), obj); MIRType knownType = MIRType_Value; - if (unboxedType == JSVAL_TYPE_MAGIC && barrier == BarrierKind::NoBarrier) + if (barrier == BarrierKind::NoBarrier) knownType = GetElemKnownType(needsHoleCheck, types); // Ensure index is an integer. @@ -8489,24 +8480,19 @@ IonBuilder::jsop_getelem_dense(MDefinition* obj, MDefinition* index, JSValueType index = idInt32; // Get the elements vector. - MInstruction* elements = MElements::New(alloc(), obj, unboxedType != JSVAL_TYPE_MAGIC); + MInstruction* elements = MElements::New(alloc(), obj); current->add(elements); // Note: to help GVN, use the original MElements instruction and not // MConvertElementsToDoubles as operand. This is fine because converting // elements to double does not change the initialized length. - MInstruction* initLength; - if (unboxedType != JSVAL_TYPE_MAGIC) - initLength = MUnboxedArrayInitializedLength::New(alloc(), obj); - else - initLength = MInitializedLength::New(alloc(), elements); + MInitializedLength* initLength = MInitializedLength::New(alloc(), elements); current->add(initLength); // If we can load the element as a definite double, make sure to check that // the array has been converted to homogenous doubles first. TemporaryTypeSet* objTypes = obj->resultTypeSet(); bool loadDouble = - unboxedType == JSVAL_TYPE_MAGIC && barrier == BarrierKind::NoBarrier && loopDepth_ && !readOutOfBounds && @@ -8526,18 +8512,13 @@ IonBuilder::jsop_getelem_dense(MDefinition* obj, MDefinition* index, JSValueType // hoisting. index = addBoundsCheck(index, initLength); - if (unboxedType != JSVAL_TYPE_MAGIC) { - load = loadUnboxedValue(elements, 0, index, unboxedType, barrier, types); - } else { - load = MLoadElement::New(alloc(), elements, index, needsHoleCheck, loadDouble); - current->add(load); - } + load = MLoadElement::New(alloc(), 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(alloc(), elements, index, initLength, - unboxedType, needsHoleCheck); + load = MLoadElementHole::New(alloc(), elements, index, initLength, needsHoleCheck); current->add(load); // If maybeUndefined was true, the typeset must have undefined, and @@ -8995,12 +8976,9 @@ IonBuilder::setElemTryDense(bool* emitted, MDefinition* object, { MOZ_ASSERT(*emitted == false); - JSValueType unboxedType = UnboxedArrayElementType(constraints(), object, index); - if (unboxedType == JSVAL_TYPE_MAGIC) { - if (!ElementAccessIsDenseNative(constraints(), object, index)) { - trackOptimizationOutcome(TrackedOutcome::AccessNotDense); - return true; - } + if (!ElementAccessIsDenseNative(constraints(), object, index)) { + trackOptimizationOutcome(TrackedOutcome::AccessNotDense); + return true; } if (PropertyWriteNeedsTypeBarrier(alloc(), constraints(), current, @@ -9034,7 +9012,7 @@ IonBuilder::setElemTryDense(bool* emitted, MDefinition* object, } // Emit dense setelem variant. - if (!jsop_setelem_dense(conversion, SetElem_Normal, object, index, value, unboxedType)) + if (!jsop_setelem_dense(conversion, SetElem_Normal, object, index, value)) return false; trackOptimizationSuccess(); @@ -9119,12 +9097,9 @@ IonBuilder::setElemTryCache(bool* emitted, MDefinition* object, bool IonBuilder::jsop_setelem_dense(TemporaryTypeSet::DoubleConversion conversion, SetElemSafety safety, - MDefinition* obj, MDefinition* id, MDefinition* value, - JSValueType unboxedType) + MDefinition* obj, MDefinition* id, MDefinition* value) { - MIRType elementType = MIRType_None; - if (unboxedType == JSVAL_TYPE_MAGIC) - elementType = DenseNativeElementType(constraints(), obj); + MIRType elementType = DenseNativeElementType(constraints(), obj); bool packed = ElementAccessIsPacked(constraints(), obj); // Writes which are on holes in the object do not have to bail out if they @@ -9143,7 +9118,7 @@ IonBuilder::jsop_setelem_dense(TemporaryTypeSet::DoubleConversion conversion, obj = addMaybeCopyElementsForWrite(obj); // Get the elements vector. - MElements* elements = MElements::New(alloc(), obj, unboxedType != JSVAL_TYPE_MAGIC); + MElements* elements = MElements::New(alloc(), obj); current->add(elements); // Ensure the value is a double, if double conversion might be needed. @@ -9181,23 +9156,20 @@ 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. - MInstruction* store; - MStoreElementCommon *common = nullptr; + MStoreElementCommon* store; if (writeHole && writeOutOfBounds) { MOZ_ASSERT(safety == SetElem_Normal); - MStoreElementHole* ins = MStoreElementHole::New(alloc(), obj, elements, id, newValue, unboxedType); + MStoreElementHole* ins = MStoreElementHole::New(alloc(), obj, elements, id, newValue); store = ins; - common = ins; current->add(ins); current->push(value); + + if (!resumeAfter(ins)) + return false; } else { - MInstruction* initLength; - if (unboxedType != JSVAL_TYPE_MAGIC) - initLength = MUnboxedArrayInitializedLength::New(alloc(), obj); - else - initLength = MInitializedLength::New(alloc(), elements); + MInitializedLength* initLength = MInitializedLength::New(alloc(), elements); current->add(initLength); bool needsHoleCheck; @@ -9208,31 +9180,24 @@ IonBuilder::jsop_setelem_dense(TemporaryTypeSet::DoubleConversion conversion, needsHoleCheck = false; } - if (unboxedType != JSVAL_TYPE_MAGIC) { - store = storeUnboxedValue(obj, elements, 0, id, unboxedType, newValue); - } else { - MStoreElement* ins = MStoreElement::New(alloc(), elements, id, newValue, needsHoleCheck); - store = ins; - common = ins; + MStoreElement* ins = MStoreElement::New(alloc(), elements, id, newValue, needsHoleCheck); + store = ins; - current->add(store); - } + current->add(ins); if (safety == SetElem_Normal) current->push(value); + + if (!resumeAfter(ins)) + return false; } - if (!resumeAfter(store)) - return false; + // Determine whether a write barrier is required. + if (obj->resultTypeSet()->propertyNeedsBarrier(constraints(), JSID_VOID)) + store->setNeedsBarrier(); - if (common) { - // Determine whether a write barrier is required. - if (obj->resultTypeSet()->propertyNeedsBarrier(constraints(), JSID_VOID)) - common->setNeedsBarrier(); - - if (elementType != MIRType_None && packed) - common->setElementType(elementType); - } + if (elementType != MIRType_None && packed) + store->setElementType(elementType); return true; } @@ -9358,16 +9323,6 @@ IonBuilder::jsop_length_fastPath() return true; } - // Compute the length for unboxed array objects. - if (UnboxedArrayElementType(constraints(), obj, nullptr) != JSVAL_TYPE_MAGIC) { - current->pop(); - - MUnboxedArrayLength* length = MUnboxedArrayLength::New(alloc(), obj); - current->add(length); - current->push(length); - return true; - } - // Compute the length for array typed objects. TypedObjectPrediction prediction = typedObjectPrediction(obj); if (!prediction.isUseless()) { @@ -9435,7 +9390,7 @@ IonBuilder::jsop_rest() MNewArray* array = MNewArray::New(alloc(), constraints(), numRest, templateConst, templateObject->group()->initialHeap(constraints()), - NewArray_FullyAllocating, pc); + NewArray_FullyAllocating); current->add(array); if (numRest == 0) { @@ -10561,39 +10516,33 @@ IonBuilder::loadUnboxedProperty(MDefinition* obj, size_t offset, JSValueType unb MInstruction* scaledOffset = MConstant::New(alloc(), Int32Value(scaledOffsetConstant)); current->add(scaledOffset); - return loadUnboxedValue(obj, UnboxedPlainObject::offsetOfData(), - scaledOffset, unboxedType, barrier, types); -} - -MInstruction* -IonBuilder::loadUnboxedValue(MDefinition* elements, size_t elementsOffset, - MDefinition* scaledOffset, JSValueType unboxedType, - BarrierKind barrier, TemporaryTypeSet* types) -{ - MInstruction* load; switch (unboxedType) { case JSVAL_TYPE_BOOLEAN: - load = MLoadUnboxedScalar::New(alloc(), elements, scaledOffset, Scalar::Uint8, - DoesNotRequireMemoryBarrier, elementsOffset); + load = MLoadUnboxedScalar::New(alloc(), obj, scaledOffset, Scalar::Uint8, + DoesNotRequireMemoryBarrier, + UnboxedPlainObject::offsetOfData()); load->setResultType(MIRType_Boolean); break; case JSVAL_TYPE_INT32: - load = MLoadUnboxedScalar::New(alloc(), elements, scaledOffset, Scalar::Int32, - DoesNotRequireMemoryBarrier, elementsOffset); + load = MLoadUnboxedScalar::New(alloc(), obj, scaledOffset, Scalar::Int32, + DoesNotRequireMemoryBarrier, + UnboxedPlainObject::offsetOfData()); load->setResultType(MIRType_Int32); break; case JSVAL_TYPE_DOUBLE: - load = MLoadUnboxedScalar::New(alloc(), elements, scaledOffset, Scalar::Float64, - DoesNotRequireMemoryBarrier, elementsOffset, + load = MLoadUnboxedScalar::New(alloc(), obj, scaledOffset, Scalar::Float64, + DoesNotRequireMemoryBarrier, + UnboxedPlainObject::offsetOfData(), /* canonicalizeDoubles = */ false); load->setResultType(MIRType_Double); break; case JSVAL_TYPE_STRING: - load = MLoadUnboxedString::New(alloc(), elements, scaledOffset, elementsOffset); + load = MLoadUnboxedString::New(alloc(), obj, scaledOffset, + UnboxedPlainObject::offsetOfData()); break; case JSVAL_TYPE_OBJECT: { @@ -10602,8 +10551,8 @@ IonBuilder::loadUnboxedValue(MDefinition* elements, size_t elementsOffset, nullBehavior = MLoadUnboxedObjectOrNull::HandleNull; else nullBehavior = MLoadUnboxedObjectOrNull::NullNotPossible; - load = MLoadUnboxedObjectOrNull::New(alloc(), elements, scaledOffset, nullBehavior, - elementsOffset); + load = MLoadUnboxedObjectOrNull::New(alloc(), obj, scaledOffset, nullBehavior, + UnboxedPlainObject::offsetOfData()); break; } @@ -11486,39 +11435,34 @@ IonBuilder::storeUnboxedProperty(MDefinition* obj, size_t offset, JSValueType un MInstruction* scaledOffset = MConstant::New(alloc(), Int32Value(scaledOffsetConstant)); current->add(scaledOffset); - return storeUnboxedValue(obj, obj, UnboxedPlainObject::offsetOfData(), - scaledOffset, unboxedType, value); -} - -MInstruction* -IonBuilder::storeUnboxedValue(MDefinition* obj, MDefinition* elements, int32_t elementsOffset, - MDefinition* scaledOffset, JSValueType unboxedType, - MDefinition* value) -{ MInstruction* store; switch (unboxedType) { case JSVAL_TYPE_BOOLEAN: - store = MStoreUnboxedScalar::New(alloc(), elements, scaledOffset, value, Scalar::Uint8, - DoesNotRequireMemoryBarrier, elementsOffset); + store = MStoreUnboxedScalar::New(alloc(), obj, scaledOffset, value, Scalar::Uint8, + DoesNotRequireMemoryBarrier, + UnboxedPlainObject::offsetOfData()); break; case JSVAL_TYPE_INT32: - store = MStoreUnboxedScalar::New(alloc(), elements, scaledOffset, value, Scalar::Int32, - DoesNotRequireMemoryBarrier, elementsOffset); + store = MStoreUnboxedScalar::New(alloc(), obj, scaledOffset, value, Scalar::Int32, + DoesNotRequireMemoryBarrier, + UnboxedPlainObject::offsetOfData()); break; case JSVAL_TYPE_DOUBLE: - store = MStoreUnboxedScalar::New(alloc(), elements, scaledOffset, value, Scalar::Float64, - DoesNotRequireMemoryBarrier, elementsOffset); + store = MStoreUnboxedScalar::New(alloc(), obj, scaledOffset, value, Scalar::Float64, + DoesNotRequireMemoryBarrier, + UnboxedPlainObject::offsetOfData()); break; case JSVAL_TYPE_STRING: - store = MStoreUnboxedString::New(alloc(), elements, scaledOffset, value, elementsOffset); + store = MStoreUnboxedString::New(alloc(), obj, scaledOffset, value, + UnboxedPlainObject::offsetOfData()); break; case JSVAL_TYPE_OBJECT: - store = MStoreUnboxedObjectOrNull::New(alloc(), elements, scaledOffset, value, obj, - elementsOffset); + store = MStoreUnboxedObjectOrNull::New(alloc(), obj, scaledOffset, value, obj, + UnboxedPlainObject::offsetOfData()); break; default: diff --git a/js/src/jit/IonBuilder.h b/js/src/jit/IonBuilder.h index 76b9d67fc145..dcfa98f12abb 100644 --- a/js/src/jit/IonBuilder.h +++ b/js/src/jit/IonBuilder.h @@ -656,13 +656,12 @@ class IonBuilder bool jsop_intrinsic(PropertyName* name); bool jsop_bindname(PropertyName* name); bool jsop_getelem(); - bool jsop_getelem_dense(MDefinition* obj, MDefinition* index, JSValueType unboxedType); + bool jsop_getelem_dense(MDefinition* obj, MDefinition* index); bool jsop_getelem_typed(MDefinition* obj, MDefinition* index, ScalarTypeDescr::Type arrayType); bool jsop_setelem(); bool jsop_setelem_dense(TemporaryTypeSet::DoubleConversion conversion, SetElemSafety safety, - MDefinition* object, MDefinition* index, MDefinition* value, - JSValueType unboxedType); + MDefinition* object, MDefinition* index, MDefinition* value); bool jsop_setelem_typed(ScalarTypeDescr::Type arrayType, SetElemSafety safety, MDefinition* object, MDefinition* index, MDefinition* value); @@ -951,16 +950,8 @@ class IonBuilder JSValueType* punboxedType); MInstruction* loadUnboxedProperty(MDefinition* obj, size_t offset, JSValueType unboxedType, BarrierKind barrier, TemporaryTypeSet* types); - MInstruction* loadUnboxedValue(MDefinition* elements, size_t elementsOffset, - MDefinition* scaledOffset, JSValueType unboxedType, - BarrierKind barrier, TemporaryTypeSet* types); MInstruction* storeUnboxedProperty(MDefinition* obj, size_t offset, JSValueType unboxedType, MDefinition* value); - MInstruction* storeUnboxedValue(MDefinition* obj, - MDefinition* elements, int32_t elementsOffset, - MDefinition* scaledOffset, JSValueType unboxedType, - MDefinition* value); - bool checkPreliminaryGroups(MDefinition *obj); bool freezePropTypeSets(TemporaryTypeSet* types, JSObject* foundProto, PropertyName* name); bool canInlinePropertyOpShapes(const BaselineInspector::ReceiverVector& receivers); diff --git a/js/src/jit/LIR-Common.h b/js/src/jit/LIR-Common.h index 34227c2db633..45672b41ac97 100644 --- a/js/src/jit/LIR-Common.h +++ b/js/src/jit/LIR-Common.h @@ -4084,10 +4084,6 @@ class LElements : public LInstructionHelper<1, 1, 0> const LAllocation* object() { return getOperand(0); } - - const MElements* mir() const { - return mir_->toElements(); - } }; // If necessary, convert any int32 elements in a vector into doubles. @@ -4185,48 +4181,6 @@ class LSetInitializedLength : public LInstructionHelper<0, 2, 0> } }; -class LUnboxedArrayLength : public LInstructionHelper<1, 1, 0> -{ - public: - LIR_HEADER(UnboxedArrayLength) - - explicit LUnboxedArrayLength(const LAllocation& object) { - setOperand(0, object); - } - - const LAllocation* object() { - return getOperand(0); - } -}; - -class LUnboxedArrayInitializedLength : public LInstructionHelper<1, 1, 0> -{ - public: - LIR_HEADER(UnboxedArrayInitializedLength) - - explicit LUnboxedArrayInitializedLength(const LAllocation& object) { - setOperand(0, object); - } - - const LAllocation* object() { - return getOperand(0); - } -}; - -class LIncrementUnboxedArrayInitializedLength : public LInstructionHelper<0, 1, 0> -{ - public: - LIR_HEADER(IncrementUnboxedArrayInitializedLength) - - explicit LIncrementUnboxedArrayInitializedLength(const LAllocation& object) { - setOperand(0, object); - } - - const LAllocation* object() { - return getOperand(0); - } -}; - // Load the length from an elements header. class LArrayLength : public LInstructionHelper<1, 1, 0> { @@ -4693,17 +4647,16 @@ class LStoreElementT : public LInstructionHelper<0, 3, 0> }; // Like LStoreElementV, but supports indexes >= initialized length. -class LStoreElementHoleV : public LInstructionHelper<0, 3 + BOX_PIECES, 1> +class LStoreElementHoleV : public LInstructionHelper<0, 3 + BOX_PIECES, 0> { public: LIR_HEADER(StoreElementHoleV) LStoreElementHoleV(const LAllocation& object, const LAllocation& elements, - const LAllocation& index, const LDefinition& temp) { + const LAllocation& index) { setOperand(0, object); setOperand(1, elements); setOperand(2, index); - setTemp(0, temp); } static const size_t Value = 3; @@ -4723,19 +4676,17 @@ class LStoreElementHoleV : public LInstructionHelper<0, 3 + BOX_PIECES, 1> }; // Like LStoreElementT, but supports indexes >= initialized length. -class LStoreElementHoleT : public LInstructionHelper<0, 4, 1> +class LStoreElementHoleT : public LInstructionHelper<0, 4, 0> { public: LIR_HEADER(StoreElementHoleT) LStoreElementHoleT(const LAllocation& object, const LAllocation& elements, - const LAllocation& index, const LAllocation& value, - const LDefinition& temp) { + const LAllocation& index, const LAllocation& value) { setOperand(0, object); setOperand(1, elements); setOperand(2, index); setOperand(3, value); - setTemp(0, temp); } const MStoreElementHole* mir() const { diff --git a/js/src/jit/LOpcodes.h b/js/src/jit/LOpcodes.h index 1d1050547503..9f472dbf4890 100644 --- a/js/src/jit/LOpcodes.h +++ b/js/src/jit/LOpcodes.h @@ -214,9 +214,6 @@ _(PostWriteBarrierV) \ _(InitializedLength) \ _(SetInitializedLength) \ - _(UnboxedArrayLength) \ - _(UnboxedArrayInitializedLength) \ - _(IncrementUnboxedArrayInitializedLength) \ _(BoundsCheck) \ _(BoundsCheckRange) \ _(BoundsCheckLower) \ diff --git a/js/src/jit/Lowering.cpp b/js/src/jit/Lowering.cpp index 290cc0d2fd17..02c704e7af67 100644 --- a/js/src/jit/Lowering.cpp +++ b/js/src/jit/Lowering.cpp @@ -2495,24 +2495,6 @@ LIRGenerator::visitSetInitializedLength(MSetInitializedLength* ins) useRegisterOrConstant(ins->index())), ins); } -void -LIRGenerator::visitUnboxedArrayLength(MUnboxedArrayLength* ins) -{ - define(new(alloc()) LUnboxedArrayLength(useRegisterAtStart(ins->object())), ins); -} - -void -LIRGenerator::visitUnboxedArrayInitializedLength(MUnboxedArrayInitializedLength* ins) -{ - define(new(alloc()) LUnboxedArrayInitializedLength(useRegisterAtStart(ins->object())), ins); -} - -void -LIRGenerator::visitIncrementUnboxedArrayInitializedLength(MIncrementUnboxedArrayInitializedLength* ins) -{ - add(new(alloc()) LIncrementUnboxedArrayInitializedLength(useRegister(ins->object())), ins); -} - void LIRGenerator::visitNot(MNot* ins) { @@ -2755,22 +2737,17 @@ LIRGenerator::visitStoreElementHole(MStoreElementHole* ins) 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()) LStoreElementHoleV(object, elements, index, tempDef); + lir = new(alloc()) LStoreElementHoleV(object, elements, index); useBox(lir, LStoreElementHoleV::Value, ins->value()); break; default: { const LAllocation value = useRegisterOrNonDoubleConstant(ins->value()); - lir = new(alloc()) LStoreElementHoleT(object, elements, index, value, tempDef); + lir = new(alloc()) LStoreElementHoleT(object, elements, index, value); break; } } diff --git a/js/src/jit/Lowering.h b/js/src/jit/Lowering.h index 56f278e4eeaf..16dc3018207d 100644 --- a/js/src/jit/Lowering.h +++ b/js/src/jit/Lowering.h @@ -187,9 +187,6 @@ class LIRGenerator : public LIRGeneratorSpecific void visitTypedObjectDescr(MTypedObjectDescr* ins); void visitInitializedLength(MInitializedLength* ins); void visitSetInitializedLength(MSetInitializedLength* ins); - void visitUnboxedArrayLength(MUnboxedArrayLength* ins); - void visitUnboxedArrayInitializedLength(MUnboxedArrayInitializedLength* ins); - void visitIncrementUnboxedArrayInitializedLength(MIncrementUnboxedArrayInitializedLength* ins); void visitNot(MNot* ins); void visitBoundsCheck(MBoundsCheck* ins); void visitBoundsCheckLower(MBoundsCheckLower* ins); diff --git a/js/src/jit/MCallOptimize.cpp b/js/src/jit/MCallOptimize.cpp index 517e2e5eaf19..3a7f56d2050a 100644 --- a/js/src/jit/MCallOptimize.cpp +++ b/js/src/jit/MCallOptimize.cpp @@ -597,7 +597,7 @@ IonBuilder::inlineArray(CallInfo& callInfo) MNewArray* ins = MNewArray::New(alloc(), constraints(), initLength, templateConst, templateArray->group()->initialHeap(constraints()), - allocating, pc); + allocating); current->add(ins); current->push(ins); @@ -1590,7 +1590,7 @@ IonBuilder::inlineConstantStringSplit(CallInfo& callInfo) MNewArray* ins = MNewArray::New(alloc(), constraints(), initLength, templateConst, templateObject->group()->initialHeap(constraints()), - NewArray_FullyAllocating, pc); + NewArray_FullyAllocating); current->add(ins); current->push(ins); @@ -2135,7 +2135,7 @@ IonBuilder::inlineUnsafeSetDenseArrayElement(CallInfo& callInfo, uint32_t base) TemporaryTypeSet::DoubleConversion conversion = obj->resultTypeSet()->convertDoubleElements(constraints()); - if (!jsop_setelem_dense(conversion, SetElem_Unsafe, obj, id, elem, JSVAL_TYPE_MAGIC)) + if (!jsop_setelem_dense(conversion, SetElem_Unsafe, obj, id, elem)) return false; return true; } diff --git a/js/src/jit/MIR.cpp b/js/src/jit/MIR.cpp index eb98a10c9c3c..da6a4e0651ab 100644 --- a/js/src/jit/MIR.cpp +++ b/js/src/jit/MIR.cpp @@ -4018,17 +4018,17 @@ MArrayState::Copy(TempAllocator& alloc, MArrayState* state) } MNewArray::MNewArray(CompilerConstraintList* constraints, uint32_t count, MConstant* templateConst, - gc::InitialHeap initialHeap, AllocatingBehaviour allocating, jsbytecode* pc) + gc::InitialHeap initialHeap, AllocatingBehaviour allocating) : MUnaryInstruction(templateConst), count_(count), initialHeap_(initialHeap), allocating_(allocating), - convertDoubleElements_(false), - pc_(pc) + convertDoubleElements_(false) { + ArrayObject* obj = templateObject(); setResultType(MIRType_Object); - if (templateObject()) { - TemporaryTypeSet* types = MakeSingletonTypeSet(constraints, templateObject()); + if (!obj->isSingleton()) { + TemporaryTypeSet* types = MakeSingletonTypeSet(constraints, obj); setResultTypeSet(types); if (types->convertDoubleElements(constraints) == TemporaryTypeSet::AlwaysConvertToDoubles) convertDoubleElements_ = true; @@ -4038,25 +4038,16 @@ MNewArray::MNewArray(CompilerConstraintList* constraints, uint32_t count, MConst bool MNewArray::shouldUseVM() const { - if (!templateObject()) - return true; - - // Allocate space using the VMCall when mir hints it needs to get allocated - // immediately, but only when data doesn't fit the available array slots. - if (allocatingBehaviour() == NewArray_Unallocating) - return false; - - if (templateObject()->is()) { - MOZ_ASSERT(templateObject()->as().capacity() >= count()); - return !templateObject()->as().hasInlineElements(); - } - MOZ_ASSERT(count() < NativeObject::NELEMENTS_LIMIT); size_t arraySlots = gc::GetGCKindSlots(templateObject()->asTenured().getAllocKind()) - ObjectElements::VALUES_PER_HEADER; - return count() > arraySlots; + // Allocate space using the VMCall when mir hints it needs to get allocated + // immediately, but only when data doesn't fit the available array slots. + bool allocating = allocatingBehaviour() != NewArray_Unallocating && count() > arraySlots; + + return templateObject()->isSingleton() || allocating; } bool @@ -4705,48 +4696,6 @@ jit::ElementAccessIsDenseNative(CompilerConstraintList* constraints, return clasp && clasp->isNative() && !IsAnyTypedArrayClass(clasp); } -JSValueType -jit::UnboxedArrayElementType(CompilerConstraintList* constraints, MDefinition* obj, - MDefinition* id) -{ - if (obj->mightBeType(MIRType_String)) - return JSVAL_TYPE_MAGIC; - - if (id && id->type() != MIRType_Int32 && id->type() != MIRType_Double) - return JSVAL_TYPE_MAGIC; - - TemporaryTypeSet* types = obj->resultTypeSet(); - if (!types || types->unknownObject()) - return JSVAL_TYPE_MAGIC; - - JSValueType elementType = JSVAL_TYPE_MAGIC; - for (unsigned i = 0; i < types->getObjectCount(); i++) { - TypeSet::ObjectKey* key = types->getObject(i); - if (!key) - continue; - - if (key->unknownProperties() || !key->isGroup()) - return JSVAL_TYPE_MAGIC; - - if (key->clasp() != &UnboxedArrayObject::class_) - return JSVAL_TYPE_MAGIC; - - const UnboxedLayout &layout = key->group()->unboxedLayout(); - - if (layout.nativeGroup()) - return JSVAL_TYPE_MAGIC; - - if (elementType == layout.elementType() || elementType == JSVAL_TYPE_MAGIC) - elementType = layout.elementType(); - else - return JSVAL_TYPE_MAGIC; - - key->watchStateChangeForUnboxedConvertedToNative(constraints); - } - - return elementType; -} - bool jit::ElementAccessIsAnyTypedArray(CompilerConstraintList* constraints, MDefinition* obj, MDefinition* id, diff --git a/js/src/jit/MIR.h b/js/src/jit/MIR.h index cc36a1072bdc..b4fffc5860a9 100644 --- a/js/src/jit/MIR.h +++ b/js/src/jit/MIR.h @@ -2917,29 +2917,25 @@ class MNewArray // Whether values written to this array should be converted to double first. bool convertDoubleElements_; - jsbytecode* pc_; - MNewArray(CompilerConstraintList* constraints, uint32_t count, MConstant* templateConst, - gc::InitialHeap initialHeap, AllocatingBehaviour allocating, jsbytecode* pc); + gc::InitialHeap initialHeap, AllocatingBehaviour allocating); public: INSTRUCTION_HEADER(NewArray) static MNewArray* New(TempAllocator& alloc, CompilerConstraintList* constraints, uint32_t count, MConstant* templateConst, - gc::InitialHeap initialHeap, AllocatingBehaviour allocating, - jsbytecode* pc) + gc::InitialHeap initialHeap, AllocatingBehaviour allocating) { - return new(alloc) MNewArray(constraints, count, templateConst, - initialHeap, allocating, pc); + return new(alloc) MNewArray(constraints, count, templateConst, initialHeap, allocating); } uint32_t count() const { return count_; } - JSObject* templateObject() const { - return getOperand(0)->toConstant()->value().toObjectOrNull(); + ArrayObject* templateObject() const { + return &getOperand(0)->toConstant()->value().toObject().as(); } gc::InitialHeap initialHeap() const { @@ -2950,10 +2946,6 @@ class MNewArray return allocating_; } - jsbytecode* pc() const { - return pc_; - } - bool convertDoubleElements() const { return convertDoubleElements_; } @@ -2976,7 +2968,7 @@ class MNewArray bool canRecoverOnBailout() const override { // The template object can safely be used in the recover instruction // because it can never be mutated by any other function execution. - return templateObject() != nullptr; + return true; } }; @@ -7599,10 +7591,8 @@ class MElements : public MUnaryInstruction, public SingleObjectPolicy::Data { - bool unboxed_; - - explicit MElements(MDefinition* object, bool unboxed) - : MUnaryInstruction(object), unboxed_(unboxed) + explicit MElements(MDefinition* object) + : MUnaryInstruction(object) { setResultType(MIRType_Elements); setMovable(); @@ -7611,19 +7601,15 @@ class MElements public: INSTRUCTION_HEADER(Elements) - static MElements* New(TempAllocator& alloc, MDefinition* object, bool unboxed = false) { - return new(alloc) MElements(object, unboxed); + static MElements* New(TempAllocator& alloc, MDefinition* object) { + return new(alloc) MElements(object); } MDefinition* object() const { return getOperand(0); } - bool unboxed() const { - return unboxed_; - } bool congruentTo(const MDefinition* ins) const override { - return congruentIfOperandsEqual(ins) && - ins->toElements()->unboxed() == unboxed(); + return congruentIfOperandsEqual(ins); } AliasSet getAliasSet() const override { return AliasSet::Load(AliasSet::ObjectFields); @@ -7854,96 +7840,6 @@ class MSetInitializedLength ALLOW_CLONE(MSetInitializedLength) }; -// Load the length from an unboxed array. -class MUnboxedArrayLength - : public MUnaryInstruction, - public SingleObjectPolicy::Data -{ - explicit MUnboxedArrayLength(MDefinition* object) - : MUnaryInstruction(object) - { - setResultType(MIRType_Int32); - setMovable(); - } - - public: - INSTRUCTION_HEADER(UnboxedArrayLength) - - static MUnboxedArrayLength* New(TempAllocator& alloc, MDefinition* object) { - return new(alloc) MUnboxedArrayLength(object); - } - - MDefinition* object() const { - return getOperand(0); - } - bool congruentTo(const MDefinition* ins) const override { - return congruentIfOperandsEqual(ins); - } - AliasSet getAliasSet() const override { - return AliasSet::Load(AliasSet::ObjectFields); - } - - ALLOW_CLONE(MUnboxedArrayLength) -}; - -// Load the initialized length from an unboxed array. -class MUnboxedArrayInitializedLength - : public MUnaryInstruction, - public SingleObjectPolicy::Data -{ - explicit MUnboxedArrayInitializedLength(MDefinition* object) - : MUnaryInstruction(object) - { - setResultType(MIRType_Int32); - setMovable(); - } - - public: - INSTRUCTION_HEADER(UnboxedArrayInitializedLength) - - static MUnboxedArrayInitializedLength* New(TempAllocator& alloc, MDefinition* object) { - return new(alloc) MUnboxedArrayInitializedLength(object); - } - - MDefinition* object() const { - return getOperand(0); - } - bool congruentTo(const MDefinition* ins) const override { - return congruentIfOperandsEqual(ins); - } - AliasSet getAliasSet() const override { - return AliasSet::Load(AliasSet::ObjectFields); - } - - ALLOW_CLONE(MUnboxedArrayInitializedLength) -}; - -// Increment the initialized length of an unboxed array object. -class MIncrementUnboxedArrayInitializedLength - : public MUnaryInstruction, - public SingleObjectPolicy::Data -{ - explicit MIncrementUnboxedArrayInitializedLength(MDefinition* obj) - : MUnaryInstruction(obj) - {} - - public: - INSTRUCTION_HEADER(IncrementUnboxedArrayInitializedLength) - - static MIncrementUnboxedArrayInitializedLength* New(TempAllocator& alloc, MDefinition* obj) { - return new(alloc) MIncrementUnboxedArrayInitializedLength(obj); - } - - MDefinition* object() const { - return getOperand(0); - } - AliasSet getAliasSet() const override { - return AliasSet::Store(AliasSet::ObjectFields); - } - - ALLOW_CLONE(MIncrementUnboxedArrayInitializedLength) -}; - // Load the array length from an elements header. class MArrayLength : public MUnaryInstruction, @@ -8463,23 +8359,18 @@ class MLoadElement ALLOW_CLONE(MLoadElement) }; -// Load a value from the elements vector for a dense native or unboxed array. -// If the index is out-of-bounds, or the indexed slot has a hole, undefined is -// returned instead. +// Load a value from a dense array's element vector. If the index is +// out-of-bounds, or the indexed slot has a hole, undefined is returned +// instead. class MLoadElementHole : public MTernaryInstruction, public SingleObjectPolicy::Data { - // Unboxed element type, JSVAL_TYPE_MAGIC for dense native elements. - JSValueType unboxedType_; - bool needsNegativeIntCheck_; bool needsHoleCheck_; - MLoadElementHole(MDefinition* elements, MDefinition* index, MDefinition* initLength, - JSValueType unboxedType, bool needsHoleCheck) + MLoadElementHole(MDefinition* elements, MDefinition* index, MDefinition* initLength, bool needsHoleCheck) : MTernaryInstruction(elements, index, initLength), - unboxedType_(unboxedType), needsNegativeIntCheck_(true), needsHoleCheck_(needsHoleCheck) { @@ -8500,10 +8391,8 @@ class MLoadElementHole INSTRUCTION_HEADER(LoadElementHole) static MLoadElementHole* New(TempAllocator& alloc, MDefinition* elements, MDefinition* index, - MDefinition* initLength, JSValueType unboxedType, - bool needsHoleCheck) { - return new(alloc) MLoadElementHole(elements, index, initLength, - unboxedType, needsHoleCheck); + MDefinition* initLength, bool needsHoleCheck) { + return new(alloc) MLoadElementHole(elements, index, initLength, needsHoleCheck); } MDefinition* elements() const { @@ -8515,9 +8404,6 @@ class MLoadElementHole MDefinition* initLength() const { return getOperand(2); } - JSValueType unboxedType() const { - return unboxedType_; - } bool needsNegativeIntCheck() const { return needsNegativeIntCheck_; } @@ -8528,8 +8414,6 @@ class MLoadElementHole if (!ins->isLoadElementHole()) return false; const MLoadElementHole* other = ins->toLoadElementHole(); - if (unboxedType() != other->unboxedType()) - return false; if (needsHoleCheck() != other->needsHoleCheck()) return false; if (needsNegativeIntCheck() != other->needsNegativeIntCheck()) @@ -8537,9 +8421,7 @@ class MLoadElementHole return congruentIfOperandsEqual(other); } AliasSet getAliasSet() const override { - return AliasSet::Load(unboxedType() == JSVAL_TYPE_MAGIC - ? AliasSet::Element - : AliasSet::UnboxedElement); + return AliasSet::Load(AliasSet::Element); } void collectRangeInfoPreTrunc() override; @@ -8749,21 +8631,17 @@ class MStoreElement ALLOW_CLONE(MStoreElement) }; -// Like MStoreElement, but supports indexes >= initialized length, and can -// handle unboxed arrays. 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. +// 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 MixPolicy >::Data { - JSValueType unboxedType_; - MStoreElementHole(MDefinition* object, MDefinition* elements, - MDefinition* index, MDefinition* value, JSValueType unboxedType) - : unboxedType_(unboxedType) - { + MDefinition* index, MDefinition* value) { initOperand(0, object); initOperand(1, elements); initOperand(2, index); @@ -8776,8 +8654,8 @@ class MStoreElementHole INSTRUCTION_HEADER(StoreElementHole) static MStoreElementHole* New(TempAllocator& alloc, MDefinition* object, MDefinition* elements, - MDefinition* index, MDefinition* value, JSValueType unboxedType) { - return new(alloc) MStoreElementHole(object, elements, index, value, unboxedType); + MDefinition* index, MDefinition* value) { + return new(alloc) MStoreElementHole(object, elements, index, value); } MDefinition* object() const { @@ -8792,16 +8670,10 @@ class MStoreElementHole MDefinition* value() const { return getOperand(3); } - JSValueType unboxedType() const { - return unboxedType_; - } AliasSet getAliasSet() const override { // StoreElementHole can update the initialized length, the array length // or reallocate obj->elements. - return AliasSet::Store(AliasSet::ObjectFields | - ((unboxedType() == JSVAL_TYPE_MAGIC) - ? AliasSet::Element - : AliasSet::UnboxedElement)); + return AliasSet::Store(AliasSet::Element | AliasSet::ObjectFields); } ALLOW_CLONE(MStoreElementHole) @@ -13348,8 +13220,6 @@ MControlInstruction* MDefinition::toControlInstruction() { bool ElementAccessIsDenseNative(CompilerConstraintList* constraints, MDefinition* obj, MDefinition* id); -JSValueType UnboxedArrayElementType(CompilerConstraintList* constraints, MDefinition* obj, - MDefinition* id); bool ElementAccessIsAnyTypedArray(CompilerConstraintList* constraints, MDefinition* obj, MDefinition* id, Scalar::Type* arrayType); diff --git a/js/src/jit/MOpcodes.h b/js/src/jit/MOpcodes.h index d53de713bf56..b23ed9643d6c 100644 --- a/js/src/jit/MOpcodes.h +++ b/js/src/jit/MOpcodes.h @@ -178,9 +178,6 @@ namespace jit { _(SetTypedObjectOffset) \ _(InitializedLength) \ _(SetInitializedLength) \ - _(UnboxedArrayLength) \ - _(UnboxedArrayInitializedLength) \ - _(IncrementUnboxedArrayInitializedLength) \ _(Not) \ _(BoundsCheck) \ _(BoundsCheckLower) \ diff --git a/js/src/jit/MacroAssembler.cpp b/js/src/jit/MacroAssembler.cpp index f3a61055d50d..4133214de253 100644 --- a/js/src/jit/MacroAssembler.cpp +++ b/js/src/jit/MacroAssembler.cpp @@ -1016,31 +1016,6 @@ template void MacroAssembler::storeUnboxedProperty(BaseIndex address, JSValueType type, ConstantOrRegister value, Label* failure); -void -MacroAssembler::checkUnboxedArrayCapacity(Register obj, const Int32Key& index, Register temp, - Label* failure) -{ - Address initLengthAddr(obj, UnboxedArrayObject::offsetOfCapacityIndexAndInitializedLength()); - Address lengthAddr(obj, UnboxedArrayObject::offsetOfLength()); - - Label capacityIsIndex, done; - load32(initLengthAddr, temp); - branchTest32(Assembler::NonZero, temp, Imm32(UnboxedArrayObject::CapacityMask), &capacityIsIndex); - branchKey(Assembler::BelowOrEqual, lengthAddr, index, failure); - jump(&done); - bind(&capacityIsIndex); - - // Do a partial shift so that we can get an absolute offset from the base - // of CapacityArray to use. - JS_STATIC_ASSERT(sizeof(UnboxedArrayObject::CapacityArray[0]) == 4); - rshiftPtr(Imm32(UnboxedArrayObject::CapacityShift - 2), temp); - and32(Imm32(~0x3), temp); - - addPtr(ImmPtr(&UnboxedArrayObject::CapacityArray), temp); - branchKey(Assembler::BelowOrEqual, Address(temp, 0), index, failure); - bind(&done); -} - // Inlined version of gc::CheckAllocatorState that checks the bare essentials // and bails for anything that cannot be handled with our jit allocators. void @@ -1458,16 +1433,6 @@ MacroAssembler::initGCThing(Register obj, Register temp, JSObject* templateObj, storePtr(ImmWord(0), Address(obj, UnboxedPlainObject::offsetOfExpando())); if (initContents) initUnboxedObjectContents(obj, &templateObj->as()); - } else if (templateObj->is()) { - MOZ_ASSERT(templateObj->as().hasInlineElements()); - int elementsOffset = UnboxedArrayObject::offsetOfInlineElements(); - computeEffectiveAddress(Address(obj, elementsOffset), temp); - storePtr(temp, Address(obj, UnboxedArrayObject::offsetOfElements())); - store32(Imm32(templateObj->as().length()), - Address(obj, UnboxedArrayObject::offsetOfLength())); - uint32_t capacityIndex = templateObj->as().capacityIndex(); - store32(Imm32(capacityIndex << UnboxedArrayObject::CapacityShift), - Address(obj, UnboxedArrayObject::offsetOfCapacityIndexAndInitializedLength())); } else { MOZ_CRASH("Unknown object"); } diff --git a/js/src/jit/MacroAssembler.h b/js/src/jit/MacroAssembler.h index f8623def2bb8..0de0ced9cbdf 100644 --- a/js/src/jit/MacroAssembler.h +++ b/js/src/jit/MacroAssembler.h @@ -718,7 +718,7 @@ class MacroAssembler : public MacroAssemblerSpecific void storeToTypedFloatArray(Scalar::Type arrayType, FloatRegister value, const Address& dest, unsigned numElems = 0); - // Load a property from an UnboxedPlainObject or UnboxedArrayObject. + // Load a property from an UnboxedPlainObject. template void loadUnboxedProperty(T address, JSValueType type, TypedOrValueRegister output); @@ -729,9 +729,6 @@ class MacroAssembler : public MacroAssemblerSpecific void storeUnboxedProperty(T address, JSValueType type, ConstantOrRegister value, Label* failure); - void checkUnboxedArrayCapacity(Register obj, const Int32Key& index, Register temp, - Label* failure); - Register extractString(const Address& address, Register scratch) { return extractObject(address, scratch); } diff --git a/js/src/jit/Recover.cpp b/js/src/jit/Recover.cpp index ce59771e599e..29061c05ba3f 100644 --- a/js/src/jit/Recover.cpp +++ b/js/src/jit/Recover.cpp @@ -1214,6 +1214,10 @@ RNewArray::recover(JSContext* cx, SnapshotIterator& iter) const RootedValue result(cx); RootedObjectGroup group(cx); + // See CodeGenerator::visitNewArrayCallVM + if (!templateObject->isSingleton()) + group = templateObject->group(); + JSObject* resultObject = NewDenseArray(cx, count_, group, allocatingBehaviour_); if (!resultObject) return false; diff --git a/js/src/jit/ScalarReplacement.cpp b/js/src/jit/ScalarReplacement.cpp index d94d8e8e7027..9bbf42ca535c 100644 --- a/js/src/jit/ScalarReplacement.cpp +++ b/js/src/jit/ScalarReplacement.cpp @@ -612,10 +612,6 @@ IsArrayEscaped(MInstruction* ins) return true; } - JSObject* obj = ins->toNewArray()->templateObject(); - if (!obj || obj->is()) - return true; - if (count >= 16) { JitSpewDef(JitSpew_Escape, "Array has too many elements\n", ins); return true; diff --git a/js/src/jit/VMFunctions.cpp b/js/src/jit/VMFunctions.cpp index 6d908201a490..c546908beb41 100644 --- a/js/src/jit/VMFunctions.cpp +++ b/js/src/jit/VMFunctions.cpp @@ -1109,22 +1109,22 @@ Recompile(JSContext* cx) } bool -SetDenseOrUnboxedArrayElement(JSContext* cx, HandleObject obj, int32_t index, - HandleValue value, bool strict) +SetDenseElement(JSContext* cx, HandleNativeObject obj, int32_t index, HandleValue value, + bool strict) { // This function is called from Ion code for StoreElementHole's OOL path. - // In this case we know the object is native or an unboxed array and we can - // use setDenseElement instead of setDenseElementWithType. + // In this case we know the object is native and we can use setDenseElement + // instead of setDenseElementWithType. NativeObject::EnsureDenseResult result = NativeObject::ED_SPARSE; do { - if (index < 0 || obj->is()) + if (index < 0) break; bool isArray = obj->is(); if (isArray && !obj->as().lengthIsWritable()) break; uint32_t idx = uint32_t(index); - result = obj->as().ensureDenseElements(cx, idx, 1); + result = obj->ensureDenseElements(cx, idx, 1); if (result != NativeObject::ED_OK) break; if (isArray) { @@ -1132,7 +1132,7 @@ SetDenseOrUnboxedArrayElement(JSContext* cx, HandleObject obj, int32_t index, if (idx >= arr.length()) arr.setLengthInt32(idx + 1); } - obj->as().setDenseElement(idx, value); + obj->setDenseElement(idx, value); return true; } while (false); diff --git a/js/src/jit/VMFunctions.h b/js/src/jit/VMFunctions.h index 25186b7e2da2..d0c40dbbc542 100644 --- a/js/src/jit/VMFunctions.h +++ b/js/src/jit/VMFunctions.h @@ -746,8 +746,8 @@ JSString* RegExpReplace(JSContext* cx, HandleString string, HandleObject regexp, JSString* StringReplace(JSContext* cx, HandleString string, HandleString pattern, HandleString repl); -bool SetDenseOrUnboxedArrayElement(JSContext* cx, HandleObject obj, int32_t index, - HandleValue value, bool strict); +bool SetDenseElement(JSContext* cx, HandleNativeObject obj, int32_t index, HandleValue value, + bool strict); void AssertValidObjectPtr(JSContext* cx, JSObject* obj); void AssertValidObjectOrNullPtr(JSContext* cx, JSObject* obj); diff --git a/js/src/jsapi.h b/js/src/jsapi.h index 3992a89f4a4c..c148bedff6be 100644 --- a/js/src/jsapi.h +++ b/js/src/jsapi.h @@ -433,13 +433,6 @@ class MOZ_STACK_CLASS AutoValueVector : public AutoVectorRooter MOZ_GUARD_OBJECT_NOTIFIER_INIT; } - explicit AutoValueVector(js::ContextFriendFields* cx - MOZ_GUARD_OBJECT_NOTIFIER_PARAM) - : AutoVectorRooter(cx, VALVECTOR) - { - MOZ_GUARD_OBJECT_NOTIFIER_INIT; - } - MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER }; @@ -1190,7 +1183,6 @@ class JS_PUBLIC_API(RuntimeOptions) { asmJS_(false), nativeRegExp_(false), unboxedObjects_(false), - unboxedArrays_(false), werror_(false), strictMode_(false), extraWarnings_(false), @@ -1240,12 +1232,6 @@ class JS_PUBLIC_API(RuntimeOptions) { return *this; } - bool unboxedArrays() const { return unboxedArrays_; } - RuntimeOptions& setUnboxedArrays(bool flag) { - unboxedArrays_ = flag; - return *this; - } - bool werror() const { return werror_; } RuntimeOptions& setWerror(bool flag) { werror_ = flag; @@ -1292,7 +1278,6 @@ class JS_PUBLIC_API(RuntimeOptions) { bool asmJS_ : 1; bool nativeRegExp_ : 1; bool unboxedObjects_ : 1; - bool unboxedArrays_ : 1; bool werror_ : 1; bool strictMode_ : 1; bool extraWarnings_ : 1; diff --git a/js/src/jsarray.cpp b/js/src/jsarray.cpp index d6057883cd91..66a502b8f353 100644 --- a/js/src/jsarray.cpp +++ b/js/src/jsarray.cpp @@ -2287,13 +2287,8 @@ TryReuseArrayGroup(JSObject* obj, ArrayObject* narr) */ MOZ_ASSERT(ObjectGroup::hasDefaultNewGroup(narr->getProto(), &ArrayObject::class_, narr->group())); - if (obj->is() && - !obj->isSingleton() && - !obj->group()->hasUnanalyzedPreliminaryObjects() && - obj->getProto() == narr->getProto()) - { + if (obj->is() && !obj->isSingleton() && obj->getProto() == narr->getProto()) narr->setGroup(obj->group()); - } } /* diff --git a/js/src/jsgc.h b/js/src/jsgc.h index 38cee1e1f635..1b18b8c6d162 100644 --- a/js/src/jsgc.h +++ b/js/src/jsgc.h @@ -331,12 +331,6 @@ GetGCKindSlots(AllocKind thingKind, const Class* clasp) return nslots; } -static inline size_t -GetGCKindBytes(AllocKind thingKind) -{ - return sizeof(JSObject_Slots0) + GetGCKindSlots(thingKind) * sizeof(Value); -} - // Class to assist in triggering background chunk allocation. This cannot be done // while holding the GC or worker thread state lock due to lock ordering issues. // As a result, the triggering is delayed using this class until neither of the diff --git a/js/src/jsiter.cpp b/js/src/jsiter.cpp index 9a70f22a1802..041958b7f08b 100644 --- a/js/src/jsiter.cpp +++ b/js/src/jsiter.cpp @@ -283,20 +283,8 @@ Snapshot(JSContext* cx, HandleObject pobj_, unsigned flags, AutoIdVector* props) if (!enumerate(cx, pobj, properties)) return false; - RootedId id(cx); for (size_t n = 0; n < properties.length(); n++) { - id = properties[n]; - bool enumerable = true; - - // The enumerate hook does not indicate whether the properties - // it returns are enumerable or not. There is no non-effectful - // way to determine this from the object, so carve out - // exceptions here for places where the property is not - // enumerable. - if (pobj->is() && id == NameToId(cx->names().length)) - enumerable = false; - - if (!Enumerate(cx, pobj, id, enumerable, flags, ht, props)) + if (!Enumerate(cx, pobj, properties[n], true, flags, ht, props)) return false; } diff --git a/js/src/jsobjinlines.h b/js/src/jsobjinlines.h index a9efa5599360..bbbcaf31578c 100644 --- a/js/src/jsobjinlines.h +++ b/js/src/jsobjinlines.h @@ -29,7 +29,7 @@ inline js::Shape* JSObject::maybeShape() const { - if (is() || is()) + if (is()) return nullptr; return *reinterpret_cast(uintptr_t(this) + offsetOfShape()); } @@ -268,8 +268,6 @@ JSObject::create(js::ExclusiveContext* cx, js::gc::AllocKind kind, js::gc::Initi MOZ_ASSERT_IF(group->clasp()->finalize, heap == js::gc::TenuredHeap || (group->clasp()->flags & JSCLASS_FINALIZE_FROM_NURSERY)); - MOZ_ASSERT_IF(group->hasUnanalyzedPreliminaryObjects(), - heap == js::gc::TenuredHeap); // Non-native classes cannot have reserved slots or private data, and the // objects can't have any fixed slots, for compatibility with @@ -771,11 +769,11 @@ ObjectClassIs(HandleObject obj, ESClassValue classValue, JSContext* cx) return Proxy::objectClassIs(obj, classValue, cx); switch (classValue) { - case ESClass_Object: return obj->is() || obj->is(); + case ESClass_Object: return obj->is(); case ESClass_Array: case ESClass_IsArray: // There difference between those is only relevant for proxies. - return obj->is() || obj->is(); + return obj->is(); case ESClass_Number: return obj->is(); case ESClass_String: return obj->is(); case ESClass_Boolean: return obj->is(); @@ -802,7 +800,7 @@ IsObjectWithClass(const Value& v, ESClassValue classValue, JSContext* cx) inline bool IsArray(HandleObject obj, JSContext* cx) { - if (obj->is() || obj->is()) + if (obj->is()) return true; return ObjectClassIs(obj, ESClass_IsArray, cx); diff --git a/js/src/shell/js.cpp b/js/src/shell/js.cpp index 8267c615eb35..4f0e1b4ada69 100644 --- a/js/src/shell/js.cpp +++ b/js/src/shell/js.cpp @@ -143,7 +143,6 @@ static bool enableIon = false; static bool enableAsmJS = false; static bool enableNativeRegExp = false; static bool enableUnboxedObjects = false; -static bool enableUnboxedArrays = false; #ifdef JS_GC_ZEAL static char gZealStr[128]; #endif @@ -5815,14 +5814,12 @@ SetRuntimeOptions(JSRuntime* rt, const OptionParser& op) enableAsmJS = !op.getBoolOption("no-asmjs"); enableNativeRegExp = !op.getBoolOption("no-native-regexp"); enableUnboxedObjects = op.getBoolOption("unboxed-objects"); - enableUnboxedArrays = op.getBoolOption("unboxed-arrays"); JS::RuntimeOptionsRef(rt).setBaseline(enableBaseline) .setIon(enableIon) .setAsmJS(enableAsmJS) .setNativeRegExp(enableNativeRegExp) - .setUnboxedObjects(enableUnboxedObjects) - .setUnboxedArrays(enableUnboxedArrays); + .setUnboxedObjects(enableUnboxedObjects); if (const char* str = op.getStringOption("ion-scalar-replacement")) { if (strcmp(str, "on") == 0) @@ -6027,8 +6024,7 @@ SetWorkerRuntimeOptions(JSRuntime* rt) .setIon(enableIon) .setAsmJS(enableAsmJS) .setNativeRegExp(enableNativeRegExp) - .setUnboxedObjects(enableUnboxedObjects) - .setUnboxedArrays(enableUnboxedArrays); + .setUnboxedObjects(enableUnboxedObjects); rt->setOffthreadIonCompilationEnabled(offthreadCompilation); rt->profilingScripts = enableDisassemblyDumps; @@ -6170,8 +6166,7 @@ main(int argc, char** argv, char** envp) || !op.addBoolOption('\0', "no-ion", "Disable IonMonkey") || !op.addBoolOption('\0', "no-asmjs", "Disable asm.js compilation") || !op.addBoolOption('\0', "no-native-regexp", "Disable native regexp compilation") - || !op.addBoolOption('\0', "unboxed-objects", "Allow creating unboxed plain objects") - || !op.addBoolOption('\0', "unboxed-arrays", "Allow creating unboxed arrays") + || !op.addBoolOption('\0', "unboxed-objects", "Allow creating unboxed objects") || !op.addStringOption('\0', "ion-scalar-replacement", "on/off", "Scalar Replacement (default: on, off to disable)") || !op.addStringOption('\0', "ion-gvn", "[mode]", diff --git a/js/src/vm/ArrayObject-inl.h b/js/src/vm/ArrayObject-inl.h index 25c549c91d14..0ca8aa483722 100644 --- a/js/src/vm/ArrayObject-inl.h +++ b/js/src/vm/ArrayObject-inl.h @@ -38,8 +38,6 @@ ArrayObject::createArrayInternal(ExclusiveContext* cx, gc::AllocKind kind, gc::I MOZ_ASSERT(group->clasp() == shape->getObjectClass()); MOZ_ASSERT(group->clasp() == &ArrayObject::class_); MOZ_ASSERT_IF(group->clasp()->finalize, heap == gc::TenuredHeap); - MOZ_ASSERT_IF(group->hasUnanalyzedPreliminaryObjects(), - heap == js::gc::TenuredHeap); // Arrays can use their fixed slots to store elements, so can't have shapes // which allow named properties to be stored in the fixed slots. diff --git a/js/src/vm/Interpreter-inl.h b/js/src/vm/Interpreter-inl.h index 0111e173a3ea..ca6983cd452a 100644 --- a/js/src/vm/Interpreter-inl.h +++ b/js/src/vm/Interpreter-inl.h @@ -626,7 +626,7 @@ InitArrayElemOperation(JSContext* cx, jsbytecode* pc, HandleObject obj, uint32_t JSOp op = JSOp(*pc); MOZ_ASSERT(op == JSOP_INITELEM_ARRAY || op == JSOP_INITELEM_INC); - MOZ_ASSERT(obj->is() || obj->is()); + MOZ_ASSERT(obj->is()); /* * If val is a hole, do not call DefineElement. diff --git a/js/src/vm/Interpreter.cpp b/js/src/vm/Interpreter.cpp index 4319ac5b5369..b486ca181616 100644 --- a/js/src/vm/Interpreter.cpp +++ b/js/src/vm/Interpreter.cpp @@ -1914,6 +1914,7 @@ CASE(EnableInterruptsPseudoOpcode) /* Various 1-byte no-ops. */ CASE(JSOP_NOP) CASE(JSOP_UNUSED2) +CASE(JSOP_UNUSED126) CASE(JSOP_UNUSED148) CASE(JSOP_BACKPATCH) CASE(JSOP_UNUSED150) @@ -3499,25 +3500,41 @@ CASE(JSOP_NEWINIT) { uint8_t i = GET_UINT8(REGS.pc); MOZ_ASSERT(i == JSProto_Array || i == JSProto_Object); + RootedObject& obj = rootObject0; - JSObject* obj; - if (i == JSProto_Array) - obj = NewArrayOperation(cx, script, REGS.pc, 0); - else + if (i == JSProto_Array) { + NewObjectKind newKind = GenericObject; + if (ObjectGroup::useSingletonForAllocationSite(script, REGS.pc, &ArrayObject::class_)) + newKind = SingletonObject; + obj = NewDenseEmptyArray(cx, NullPtr(), newKind); + if (!obj || !ObjectGroup::setAllocationSiteObjectGroup(cx, script, REGS.pc, obj, + newKind == SingletonObject)) + { + goto error; + } + } else { obj = NewObjectOperation(cx, script, REGS.pc); - - if (!obj) - goto error; + if (!obj) + goto error; + } PUSH_OBJECT(*obj); } END_CASE(JSOP_NEWINIT) CASE(JSOP_NEWARRAY) -CASE(JSOP_SPREADCALLARRAY) { - JSObject* obj = NewArrayOperation(cx, script, REGS.pc, GET_UINT24(REGS.pc)); - if (!obj) + unsigned count = GET_UINT24(REGS.pc); + RootedObject& obj = rootObject0; + NewObjectKind newKind = GenericObject; + if (ObjectGroup::useSingletonForAllocationSite(script, REGS.pc, &ArrayObject::class_)) + newKind = SingletonObject; + obj = NewDenseFullyAllocatedArray(cx, count, NullPtr(), newKind); + if (!obj || !ObjectGroup::setAllocationSiteObjectGroup(cx, script, REGS.pc, obj, + newKind == SingletonObject)) + { goto error; + } + PUSH_OBJECT(*obj); } END_CASE(JSOP_NEWARRAY) @@ -3620,6 +3637,8 @@ CASE(JSOP_INITELEM_ARRAY) RootedObject& obj = rootObject0; obj = ®S.sp[-2].toObject(); + MOZ_ASSERT(obj->is()); + uint32_t index = GET_UINT24(REGS.pc); if (!InitArrayElemOperation(cx, REGS.pc, obj, index, val)) goto error; @@ -4684,7 +4703,10 @@ js::NewObjectOperationWithTemplate(JSContext* cx, HandleObject templateObject) if (templateObject->group()->maybeUnboxedLayout()) { RootedObjectGroup group(cx, templateObject->group()); - return UnboxedPlainObject::create(cx, group, newKind); + JSObject* obj = UnboxedPlainObject::create(cx, group, newKind); + if (!obj) + return nullptr; + return obj; } JSObject* obj = CopyInitializerObject(cx, templateObject.as(), newKind); @@ -4695,68 +4717,6 @@ js::NewObjectOperationWithTemplate(JSContext* cx, HandleObject templateObject) return obj; } -JSObject* -js::NewArrayOperation(JSContext* cx, HandleScript script, jsbytecode* pc, uint32_t length, - NewObjectKind newKind /* = GenericObject */) -{ - MOZ_ASSERT(newKind != SingletonObject); - - RootedObjectGroup group(cx); - if (ObjectGroup::useSingletonForAllocationSite(script, pc, JSProto_Array)) { - newKind = SingletonObject; - } else { - group = ObjectGroup::allocationSiteGroup(cx, script, pc, JSProto_Array); - if (!group) - return nullptr; - if (group->maybePreliminaryObjects()) - group->maybePreliminaryObjects()->maybeAnalyze(cx, group); - - if (group->shouldPreTenure() || group->maybePreliminaryObjects()) - newKind = TenuredObject; - - if (group->maybeUnboxedLayout()) - return UnboxedArrayObject::create(cx, group, length, newKind); - } - - ArrayObject* obj = NewDenseFullyAllocatedArray(cx, length, NullPtr(), newKind); - if (!obj) - return nullptr; - - if (newKind == SingletonObject) { - MOZ_ASSERT(obj->isSingleton()); - } else { - obj->setGroup(group); - - if (PreliminaryObjectArray* preliminaryObjects = group->maybePreliminaryObjects()) - preliminaryObjects->registerNewObject(obj); - } - - return obj; -} - -JSObject* -js::NewArrayOperationWithTemplate(JSContext* cx, HandleObject templateObject) -{ - MOZ_ASSERT(!templateObject->isSingleton()); - - NewObjectKind newKind = templateObject->group()->shouldPreTenure() ? TenuredObject : GenericObject; - - if (templateObject->is()) { - uint32_t length = templateObject->as().length(); - RootedObjectGroup group(cx, templateObject->group()); - return UnboxedArrayObject::create(cx, group, length, newKind); - } - - ArrayObject* obj = NewDenseFullyAllocatedArray(cx, templateObject->as().length(), - NullPtr(), newKind); - if (!obj) - return nullptr; - - MOZ_ASSERT(obj->lastProperty() == templateObject->as().lastProperty()); - obj->setGroup(templateObject->group()); - return obj; -} - void js::ReportUninitializedLexical(JSContext* cx, HandlePropertyName name) { diff --git a/js/src/vm/Interpreter.h b/js/src/vm/Interpreter.h index be085abdbd2e..2cf7db82c070 100644 --- a/js/src/vm/Interpreter.h +++ b/js/src/vm/Interpreter.h @@ -445,13 +445,6 @@ NewObjectOperation(JSContext* cx, HandleScript script, jsbytecode* pc, JSObject* NewObjectOperationWithTemplate(JSContext* cx, HandleObject templateObject); -JSObject* -NewArrayOperation(JSContext* cx, HandleScript script, jsbytecode* pc, uint32_t length, - NewObjectKind newKind = GenericObject); - -JSObject* -NewArrayOperationWithTemplate(JSContext* cx, HandleObject templateObject); - inline bool SetConstOperation(JSContext* cx, HandleObject varobj, HandlePropertyName name, HandleValue rval) { diff --git a/js/src/vm/NativeObject.cpp b/js/src/vm/NativeObject.cpp index 8982ee6bf869..d9b48d83dbdb 100644 --- a/js/src/vm/NativeObject.cpp +++ b/js/src/vm/NativeObject.cpp @@ -329,11 +329,8 @@ NativeObject::setLastPropertyMakeNonNative(Shape* shape) MOZ_ASSERT(shape->compartment() == compartment()); MOZ_ASSERT(shape->slotSpan() == 0); MOZ_ASSERT(shape->numFixedSlots() == 0); - - if (hasDynamicElements()) - js_free(getElementsHeader()); - if (hasDynamicSlots()) - js_free(slots_); + MOZ_ASSERT(!hasDynamicElements()); + MOZ_ASSERT(!hasDynamicSlots()); shape_ = shape; } @@ -714,9 +711,6 @@ ReallocateElements(ExclusiveContext* cx, JSObject* obj, ObjectElements* oldHeade // exceptional resizings will at most triple the capacity, as opposed to the // usual doubling. // -// Note: the structure and behavior of this method follow along with -// UnboxedArrayObject::chooseCapacityIndex. Changes to the allocation strategy -// in one should generally be matched by the other. /* static */ uint32_t NativeObject::goodAllocated(uint32_t reqAllocated, uint32_t length = 0) { diff --git a/js/src/vm/ObjectGroup.cpp b/js/src/vm/ObjectGroup.cpp index 8a0e9c08f2dc..e7918fa8cc6b 100644 --- a/js/src/vm/ObjectGroup.cpp +++ b/js/src/vm/ObjectGroup.cpp @@ -1161,15 +1161,6 @@ ObjectGroup::allocationSiteGroup(JSContext* cx, JSScript* script, jsbytecode* pc } } - if (JSOp(*pc) == JSOP_NEWARRAY) { - PreliminaryObjectArrayWithTemplate* preliminaryObjects = - cx->new_(nullptr); - if (preliminaryObjects) - res->setPreliminaryObjects(preliminaryObjects); - else - cx->recoverFromOutOfMemory(); - } - if (!table->add(p, key, res)) return nullptr; diff --git a/js/src/vm/Opcodes.h b/js/src/vm/Opcodes.h index f9fac122bbff..00fc91d0947e 100644 --- a/js/src/vm/Opcodes.h +++ b/js/src/vm/Opcodes.h @@ -833,8 +833,8 @@ * This opcode takes the kind of initializer (JSProto_Array or * JSProto_Object). * - * This opcode has three extra bytes so it can be exchanged with - * JSOP_NEWOBJECT during emit. + * This opcode has an extra byte so it can be exchanged with JSOP_NEWOBJECT + * during emit. * Category: Literals * Type: Object * Operands: uint8_t kind (, uint24_t extra) @@ -1272,18 +1272,7 @@ * Stack: receiver, obj, propval => obj[propval] */ \ macro(JSOP_GETELEM_SUPER, 125, "getelem-super", NULL, 1, 3, 1, JOF_BYTE |JOF_ELEM|JOF_LEFTASSOC) \ - /* - * Pushes newly created array for a spread call onto the stack. This has - * the same semantics as JSOP_NEWARRAY, but is distinguished to avoid - * using unboxed arrays in spread calls, which would make compiling spread - * calls in baseline more complex. - * - * Category: Literals - * Type: Array - * Operands: uint24_t length - * Stack: => obj - */ \ - macro(JSOP_SPREADCALLARRAY, 126, "spreadcallarray", NULL, 4, 0, 1, JOF_UINT24) \ + macro(JSOP_UNUSED126, 126, "unused126", NULL, 1, 0, 0, JOF_BYTE) \ \ /* * Defines the given function on the current scope. diff --git a/js/src/vm/Runtime-inl.h b/js/src/vm/Runtime-inl.h index e9ad4984e82a..af11943dbad2 100644 --- a/js/src/vm/Runtime-inl.h +++ b/js/src/vm/Runtime-inl.h @@ -52,8 +52,6 @@ NewObjectCache::newObjectFromHit(JSContext* cx, EntryIndex entryIndex, gc::Initi // on the templateObj, which is not a GC thing and can't use runtimeFromAnyThread. ObjectGroup* group = templateObj->group_; - MOZ_ASSERT(!group->hasUnanalyzedPreliminaryObjects()); - if (group->shouldPreTenure()) heap = gc::TenuredHeap; diff --git a/js/src/vm/TypeInference.cpp b/js/src/vm/TypeInference.cpp index 5f5c9c4f8a3e..6097c11110ac 100644 --- a/js/src/vm/TypeInference.cpp +++ b/js/src/vm/TypeInference.cpp @@ -2381,8 +2381,6 @@ TemporaryTypeSet::propertyNeedsBarrier(CompilerConstraintList* constraints, jsid bool js::ClassCanHaveExtraProperties(const Class* clasp) { - if (clasp == &UnboxedPlainObject::class_ || clasp == &UnboxedArrayObject::class_) - return false; return clasp->resolve || clasp->ops.lookupProperty || clasp->ops.getProperty @@ -3339,19 +3337,16 @@ PreliminaryObjectArray::sweep() void PreliminaryObjectArrayWithTemplate::trace(JSTracer* trc) { - if (shape_) - TraceEdge(trc, &shape_, "PreliminaryObjectArrayWithTemplate_shape"); + TraceEdge(trc, &shape_, "PreliminaryObjectArrayWithTemplate_shape"); } /* static */ void PreliminaryObjectArrayWithTemplate::writeBarrierPre(PreliminaryObjectArrayWithTemplate* objects) { - Shape* shape = objects->shape(); - - if (!shape || !shape->runtimeFromAnyThread()->needsIncrementalBarrier()) + if (!objects->shape()->runtimeFromAnyThread()->needsIncrementalBarrier()) return; - JS::Zone* zone = shape->zoneFromAnyThread(); + JS::Zone* zone = objects->shape()->zoneFromAnyThread(); if (zone->needsIncrementalBarrier()) objects->trace(zone->barrierTracer()); } @@ -3411,37 +3406,33 @@ PreliminaryObjectArrayWithTemplate::maybeAnalyze(ExclusiveContext* cx, ObjectGro ScopedJSDeletePtr preliminaryObjects(this); group->detachPreliminaryObjects(); - if (shape()) { - MOZ_ASSERT(shape()->slotSpan() != 0); - MOZ_ASSERT(OnlyHasDataProperties(shape())); + MOZ_ASSERT(shape()->slotSpan() != 0); + MOZ_ASSERT(OnlyHasDataProperties(shape())); - // Make sure all the preliminary objects reflect the properties originally - // in the template object. - for (size_t i = 0; i < PreliminaryObjectArray::COUNT; i++) { - JSObject* objBase = preliminaryObjects->get(i); - if (!objBase) - continue; - PlainObject* obj = &objBase->as(); + // Make sure all the preliminary objects reflect the properties originally + // in the template object. + for (size_t i = 0; i < PreliminaryObjectArray::COUNT; i++) { + JSObject* objBase = preliminaryObjects->get(i); + if (!objBase) + continue; + PlainObject* obj = &objBase->as(); - if (obj->inDictionaryMode() || !OnlyHasDataProperties(obj->lastProperty())) - return; + if (obj->inDictionaryMode() || !OnlyHasDataProperties(obj->lastProperty())) + return; - if (CommonPrefix(obj->lastProperty(), shape()) != shape()) - return; - } + if (CommonPrefix(obj->lastProperty(), shape()) != shape()) + return; } TryConvertToUnboxedLayout(cx, shape(), group, preliminaryObjects); if (group->maybeUnboxedLayout()) return; - if (shape()) { - // We weren't able to use an unboxed layout, but since the preliminary - // still reflect the template object's properties, and all objects in the - // future will be created with those properties, the properties can be - // marked as definite for objects in the group. - group->addDefiniteProperties(cx, shape()); - } + // We weren't able to use an unboxed layout, but since the preliminary + // still reflect the template object's properties, and all objects in the + // future will be created with those properties, the properties can be + // marked as definite for objects in the group. + group->addDefiniteProperties(cx, shape()); } ///////////////////////////////////////////////////////////////////// diff --git a/js/src/vm/UnboxedObject.cpp b/js/src/vm/UnboxedObject.cpp index c649b479e661..2d2865cf9cf9 100644 --- a/js/src/vm/UnboxedObject.cpp +++ b/js/src/vm/UnboxedObject.cpp @@ -261,15 +261,20 @@ UnboxedLayout::makeConstructorCode(JSContext* cx, HandleObjectGroup group) void UnboxedLayout::detachFromCompartment() { - if (isInList()) - remove(); + remove(); } -static inline bool -SetUnboxedValue(ExclusiveContext* cx, JSObject* unboxedObject, jsid id, - uint8_t* p, JSValueType type, const Value& v) +///////////////////////////////////////////////////////////////////// +// UnboxedPlainObject +///////////////////////////////////////////////////////////////////// + +bool +UnboxedPlainObject::setValue(ExclusiveContext* cx, const UnboxedLayout::Property& property, + const Value& v) { - switch (type) { + uint8_t* p = &data_[property.offset]; + + switch (property.type) { case JSVAL_TYPE_BOOLEAN: if (v.isBoolean()) { *p = v.toBoolean(); @@ -304,16 +309,14 @@ SetUnboxedValue(ExclusiveContext* cx, JSObject* unboxedObject, jsid id, // Update property types when writing object properties. Types for // other properties were captured when the unboxed layout was // created. - AddTypePropertyId(cx, unboxedObject, id, v); + AddTypePropertyId(cx, this, NameToId(property.name), v); // Manually trigger post barriers on the whole object. If we treat // the pointer as a HeapPtrObject we will get confused later if the // object is converted to its native representation. JSObject* obj = v.toObjectOrNull(); - if (IsInsideNursery(v.toObjectOrNull()) && !IsInsideNursery(unboxedObject)) { - JSRuntime* rt = cx->asJSContext()->runtime(); - rt->gc.storeBuffer.putWholeCellFromMainThread(unboxedObject); - } + if (IsInsideNursery(v.toObjectOrNull()) && !IsInsideNursery(this)) + cx->asJSContext()->runtime()->gc.storeBuffer.putWholeCellFromMainThread(this); *reinterpret_cast(p) = obj; return true; @@ -325,10 +328,12 @@ SetUnboxedValue(ExclusiveContext* cx, JSObject* unboxedObject, jsid id, } } -static inline Value -GetUnboxedValue(uint8_t* p, JSValueType type) +Value +UnboxedPlainObject::getValue(const UnboxedLayout::Property& property) { - switch (type) { + uint8_t* p = &data_[property.offset]; + + switch (property.type) { case JSVAL_TYPE_BOOLEAN: return BooleanValue(*p != 0); @@ -349,25 +354,6 @@ GetUnboxedValue(uint8_t* p, JSValueType type) } } -///////////////////////////////////////////////////////////////////// -// UnboxedPlainObject -///////////////////////////////////////////////////////////////////// - -bool -UnboxedPlainObject::setValue(ExclusiveContext* cx, const UnboxedLayout::Property& property, - const Value& v) -{ - uint8_t* p = &data_[property.offset]; - return SetUnboxedValue(cx, this, NameToId(property.name), p, property.type, v); -} - -Value -UnboxedPlainObject::getValue(const UnboxedLayout::Property& property) -{ - uint8_t* p = &data_[property.offset]; - return GetUnboxedValue(p, property.type); -} - void UnboxedPlainObject::trace(JSTracer* trc, JSObject* obj) { @@ -433,21 +419,6 @@ UnboxedPlainObject::containsUnboxedOrExpandoProperty(ExclusiveContext* cx, jsid return false; } -static bool -PropagatePropertyTypes(JSContext* cx, jsid id, ObjectGroup* oldGroup, ObjectGroup* newGroup) -{ - HeapTypeSet* typeProperty = oldGroup->maybeGetProperty(id); - TypeSet::TypeList types; - if (!typeProperty->enumerateTypes(&types)) { - ReportOutOfMemory(cx); - return false; - } - MOZ_ASSERT(!types.empty()); - for (size_t j = 0; j < types.length(); j++) - AddTypePropertyId(cx, newGroup, nullptr, id, types[j]); - return true; -} - /* static */ bool UnboxedLayout::makeNativeGroup(JSContext* cx, ObjectGroup* group) { @@ -466,8 +437,6 @@ UnboxedLayout::makeNativeGroup(JSContext* cx, ObjectGroup* group) // group and objects that were allocated using the replacement new group. RootedObjectGroup replacementNewGroup(cx); if (layout.newScript()) { - MOZ_ASSERT(!layout.isArray()); - replacementNewGroup = ObjectGroupCompartment::makeGroup(cx, &PlainObject::class_, proto); if (!replacementNewGroup) return false; @@ -497,24 +466,11 @@ UnboxedLayout::makeNativeGroup(JSContext* cx, ObjectGroup* group) group->clearNewScript(cx, replacementNewGroup); } - const Class* clasp = layout.isArray() ? &ArrayObject::class_ : &PlainObject::class_; - size_t nfixed = layout.isArray() ? 0 : gc::GetGCKindSlots(layout.getAllocKind()); - - if (layout.isArray()) { - // The length shape to use for arrays is cached via a modified initial - // shape for array objects. Create an array now to make sure this entry - // is instantiated. - if (!NewDenseEmptyArray(cx)) - return false; - } - - RootedShape shape(cx, EmptyShape::getInitialShape(cx, clasp, proto, nfixed, 0)); + size_t nfixed = gc::GetGCKindSlots(layout.getAllocKind()); + RootedShape shape(cx, EmptyShape::getInitialShape(cx, &PlainObject::class_, proto, nfixed, 0)); if (!shape) return false; - MOZ_ASSERT_IF(layout.isArray(), !shape->isEmptyShape() && shape->slotSpan() == 0); - - // Add shapes for each property, if this is for a plain object. for (size_t i = 0; i < layout.properties().length(); i++) { const UnboxedLayout::Property& property = layout.properties()[i]; @@ -527,26 +483,26 @@ UnboxedLayout::makeNativeGroup(JSContext* cx, ObjectGroup* group) } ObjectGroup* nativeGroup = - ObjectGroupCompartment::makeGroup(cx, clasp, proto, + ObjectGroupCompartment::makeGroup(cx, &PlainObject::class_, proto, group->flags() & OBJECT_FLAG_DYNAMIC_MASK); if (!nativeGroup) return false; // Propagate all property types from the old group to the new group. - if (layout.isArray()) { - if (!PropagatePropertyTypes(cx, JSID_VOID, group, nativeGroup)) - return false; - } else { - for (size_t i = 0; i < layout.properties().length(); i++) { - const UnboxedLayout::Property& property = layout.properties()[i]; - jsid id = NameToId(property.name); - if (!PropagatePropertyTypes(cx, id, group, nativeGroup)) - return false; + for (size_t i = 0; i < layout.properties().length(); i++) { + const UnboxedLayout::Property& property = layout.properties()[i]; + jsid id = NameToId(property.name); - HeapTypeSet* nativeProperty = nativeGroup->maybeGetProperty(id); - if (nativeProperty->canSetDefinite(i)) - nativeProperty->setDefinite(i); - } + HeapTypeSet* typeProperty = group->maybeGetProperty(id); + TypeSet::TypeList types; + if (!typeProperty->enumerateTypes(&types)) + return false; + MOZ_ASSERT(!types.empty()); + for (size_t j = 0; j < types.length(); j++) + AddTypePropertyId(cx, nativeGroup, nullptr, id, types[j]); + HeapTypeSet* nativeProperty = nativeGroup->maybeGetProperty(id); + if (nativeProperty->canSetDefinite(i)) + nativeProperty->setDefinite(i); } layout.nativeGroup_ = nativeGroup; @@ -863,11 +819,7 @@ UnboxedPlainObject::obj_getOwnPropertyDescriptor(JSContext* cx, HandleObject obj if (UnboxedExpandoObject* expando = obj->as().maybeExpando()) { if (expando->containsShapeOrElement(cx, id)) { RootedObject nexpando(cx, expando); - if (!GetOwnPropertyDescriptor(cx, nexpando, id, desc)) - return false; - if (desc.object() == nexpando) - desc.object().set(obj); - return true; + return GetOwnPropertyDescriptor(cx, nexpando, id, desc); } } @@ -966,548 +918,6 @@ const Class UnboxedPlainObject::class_ = { } }; -///////////////////////////////////////////////////////////////////// -// UnboxedArrayObject -///////////////////////////////////////////////////////////////////// - -/* static */ bool -UnboxedArrayObject::convertToNative(JSContext* cx, JSObject* obj) -{ - const UnboxedLayout& layout = obj->as().layout(); - - if (!layout.nativeGroup()) { - if (!UnboxedLayout::makeNativeGroup(cx, obj->group())) - return false; - } - - size_t length = obj->as().length(); - size_t initlen = obj->as().initializedLength(); - - AutoValueVector values(cx); - for (size_t i = 0; i < initlen; i++) { - if (!values.append(obj->as().getElement(i))) - return false; - } - - obj->setGroup(layout.nativeGroup()); - - ArrayObject* aobj = &obj->as(); - aobj->setLastPropertyMakeNative(cx, layout.nativeShape()); - - // Make sure there is at least one element, so that this array does not - // use emptyObjectElements. - if (!aobj->ensureElements(cx, Max(initlen, 1))) - return false; - - MOZ_ASSERT(!aobj->getDenseInitializedLength()); - aobj->setDenseInitializedLength(initlen); - aobj->initDenseElements(0, values.begin(), initlen); - aobj->setLengthInt32(length); - - return true; -} - -/* static */ UnboxedArrayObject* -UnboxedArrayObject::create(ExclusiveContext* cx, HandleObjectGroup group, uint32_t length, - NewObjectKind newKind) -{ - MOZ_ASSERT(length <= MaximumCapacity); - - MOZ_ASSERT(group->clasp() == &class_); - uint32_t elementSize = UnboxedTypeSize(group->unboxedLayout().elementType()); - uint32_t nbytes = offsetOfInlineElements() + elementSize * length; - - UnboxedArrayObject* res; - if (nbytes <= JSObject::MAX_BYTE_SIZE) { - gc::AllocKind allocKind = gc::GetGCObjectKindForBytes(nbytes); - - res = NewObjectWithGroup(cx, group, allocKind, newKind); - if (!res) - return nullptr; - res->setInlineElements(); - - size_t capacity = (GetGCKindBytes(allocKind) - offsetOfInlineElements()) / elementSize; - res->setCapacityIndex(exactCapacityIndex(capacity)); - } else { - UniquePtr elements( - cx->zone()->pod_malloc(length * elementSize)); - if (!elements) - return nullptr; - - res = NewObjectWithGroup(cx, group, gc::AllocKind::OBJECT0, newKind); - if (!res) - return nullptr; - - res->elements_ = elements.release(); - res->setCapacityIndex(CapacityMatchesLengthIndex); - } - - res->setLength(length); - res->setInitializedLength(0); - return res; -} - -bool -UnboxedArrayObject::setElement(ExclusiveContext* cx, size_t index, const Value& v) -{ - MOZ_ASSERT(index < initializedLength()); - uint8_t* p = elements() + index * elementSize(); - return SetUnboxedValue(cx, this, JSID_VOID, p, elementType(), v); -} - -bool -UnboxedArrayObject::initElement(ExclusiveContext* cx, size_t index, const Value& v) -{ - MOZ_ASSERT(index < initializedLength()); - uint8_t* p = elements() + index * elementSize(); - if (UnboxedTypeNeedsPreBarrier(elementType())) - *reinterpret_cast(p) = nullptr; - return SetUnboxedValue(cx, this, JSID_VOID, p, elementType(), v); -} - -Value -UnboxedArrayObject::getElement(size_t index) -{ - MOZ_ASSERT(index < initializedLength()); - uint8_t* p = elements() + index * elementSize(); - return GetUnboxedValue(p, elementType()); -} - -/* static */ void -UnboxedArrayObject::trace(JSTracer* trc, JSObject* obj) -{ - JSValueType type = obj->as().elementType(); - if (!UnboxedTypeNeedsPreBarrier(type)) - return; - - MOZ_ASSERT(obj->as().elementSize() == sizeof(uintptr_t)); - size_t initlen = obj->as().initializedLength(); - void** elements = reinterpret_cast(obj->as().elements()); - - switch (type) { - case JSVAL_TYPE_OBJECT: - for (size_t i = 0; i < initlen; i++) { - HeapPtrObject* heap = reinterpret_cast(elements + i); - if (*heap) - TraceEdge(trc, heap, "unboxed_object"); - } - break; - - case JSVAL_TYPE_STRING: - for (size_t i = 0; i < initlen; i++) { - HeapPtrString* heap = reinterpret_cast(elements + i); - TraceEdge(trc, heap, "unboxed_string"); - } - break; - - default: - MOZ_CRASH(); - } -} - -/* static */ void -UnboxedArrayObject::objectMoved(JSObject* obj, const JSObject* old) -{ - UnboxedArrayObject& dst = obj->as(); - const UnboxedArrayObject& src = old->as(); - - // Fix up possible inline data pointer. - if (src.hasInlineElements()) - dst.setInlineElements(); -} - -/* static */ void -UnboxedArrayObject::finalize(FreeOp* fop, JSObject* obj) -{ - if (!obj->as().hasInlineElements()) - js_free(obj->as().elements()); -} - -// Possible capacities for unboxed arrays. Some of these capacities might seem -// a little weird, but were chosen to allow the inline data of objects of each -// size to be fully utilized for arrays of the various types on both 32 bit and -// 64 bit platforms. -/* static */ const uint32_t -UnboxedArrayObject::CapacityArray[] = { - UINT32_MAX, // For CapacityMatchesLengthIndex. - 0, 1, 2, 3, 4, 5, 6, 8, 9, 10, 12, 13, 16, 17, 18, 20, 24, 26, 32, 34, 36, 48, 52, 64, 68, - 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768, 65536, 131072, 262144, 524288, - 1048576, 2097152, 3145728, 4194304, 5242880, 6291456, 7340032, 8388608, 9437184, 11534336, - 13631488, 15728640, 17825792, 20971520, 24117248, 27262976, 31457280, 35651584, 40894464, - 46137344, 52428800, 59768832, MaximumCapacity -}; - -static const uint32_t -Pow2CapacityIndexes[] = { - 1, // 1 - 2, // 2 - 4, // 4 - 8, // 8 - 13, // 16 - 19, // 32 - 24, // 64 - 26, // 128 - 27, // 256 - 28, // 512 - 29, // 1024 - 30, // 2048 - 31, // 4096 - 32, // 8192 - 33, // 16384 - 34, // 32768 - 35, // 65536 - 36, // 131072 - 37, // 262144 - 38, // 524288 - 39 // 1048576 -}; - -static const uint32_t MebiCapacityIndex = 39; - -/* static */ uint32_t -UnboxedArrayObject::chooseCapacityIndex(uint32_t capacity, uint32_t length) -{ - // Note: the structure and behavior of this method follow along with - // NativeObject::goodAllocated. Changes to the allocation strategy in one - // should generally be matched by the other. - - // Make sure we have enough space to store all possible values for the capacity index. - MOZ_ASSERT(mozilla::ArrayLength(CapacityArray) - 1 <= CapacityMask >> CapacityShift); - - // The caller should have ensured the capacity is possible for an unboxed array. - MOZ_ASSERT(capacity <= MaximumCapacity); - - static const uint32_t Mebi = 1024 * 1024; - - if (capacity <= Mebi) { - capacity = mozilla::RoundUpPow2(capacity); - - // When the required capacity is close to the array length, then round - // up to the array length itself, as for NativeObject. - if (length >= capacity && capacity > (length / 3) * 2) - return CapacityMatchesLengthIndex; - - if (capacity < MinimumDynamicCapacity) - capacity = MinimumDynamicCapacity; - - uint32_t bit = mozilla::FloorLog2Size(capacity); - MOZ_ASSERT(capacity == uint32_t(1 << bit)); - MOZ_ASSERT(bit <= 20); - MOZ_ASSERT(mozilla::ArrayLength(Pow2CapacityIndexes) == 21); - - uint32_t index = Pow2CapacityIndexes[bit]; - MOZ_ASSERT(CapacityArray[index] == capacity); - - return index; - } - - MOZ_ASSERT(CapacityArray[MebiCapacityIndex] == Mebi); - - for (uint32_t i = MebiCapacityIndex + 1;; i++) { - if (CapacityArray[i] >= capacity) - return i; - } - - MOZ_CRASH("Invalid capacity"); -} - -/* static */ uint32_t -UnboxedArrayObject::exactCapacityIndex(uint32_t capacity) -{ - for (size_t i = CapacityMatchesLengthIndex + 1; i < ArrayLength(CapacityArray); i++) { - if (CapacityArray[i] == capacity) - return i; - } - MOZ_CRASH(); -} - -bool -UnboxedArrayObject::growElements(ExclusiveContext* cx, size_t cap) -{ - // The caller should have checked if this capacity is possible for an - // unboxed array, so the only way this call can fail is from OOM. - MOZ_ASSERT(cap <= MaximumCapacity); - - uint32_t oldCapacity = capacity(); - uint32_t newCapacityIndex = chooseCapacityIndex(cap, length()); - uint32_t newCapacity = computeCapacity(newCapacityIndex, length()); - - MOZ_ASSERT(oldCapacity < cap); - MOZ_ASSERT(cap <= newCapacity); - - // The allocation size computation below cannot have integer overflows. - JS_STATIC_ASSERT(MaximumCapacity < UINT32_MAX / sizeof(double)); - - uint8_t* newElements; - if (hasInlineElements()) { - newElements = cx->zone()->pod_malloc(newCapacity * elementSize()); - if (!newElements) - return false; - js_memcpy(newElements, elements(), initializedLength() * elementSize()); - } else { - newElements = cx->zone()->pod_realloc(elements(), oldCapacity * elementSize(), - newCapacity * elementSize()); - if (!newElements) - return false; - } - - elements_ = newElements; - setCapacityIndex(newCapacityIndex); - - return true; -} - -void -UnboxedArrayObject::shrinkElements(ExclusiveContext* cx, size_t cap) -{ - if (hasInlineElements()) - return; - - uint32_t oldCapacity = capacity(); - uint32_t newCapacityIndex = chooseCapacityIndex(cap, 0); - uint32_t newCapacity = computeCapacity(newCapacityIndex, 0); - - MOZ_ASSERT(cap < oldCapacity); - MOZ_ASSERT(cap <= newCapacity); - - if (newCapacity >= oldCapacity) - return; - - uint8_t* newElements = - cx->zone()->pod_realloc(elements(), oldCapacity * elementSize(), - newCapacity * elementSize()); - if (!newElements) - return; - - elements_ = newElements; - setCapacityIndex(newCapacityIndex); -} - -bool -UnboxedArrayObject::containsProperty(JSContext* cx, jsid id) -{ - if (JSID_IS_INT(id) && uint32_t(JSID_TO_INT(id)) < initializedLength()) - return true; - if (JSID_IS_ATOM(id) && JSID_TO_ATOM(id) == cx->names().length) - return true; - return false; -} - -/* static */ bool -UnboxedArrayObject::obj_lookupProperty(JSContext* cx, HandleObject obj, - HandleId id, MutableHandleObject objp, - MutableHandleShape propp) -{ - if (obj->as().containsProperty(cx, id)) { - MarkNonNativePropertyFound(propp); - objp.set(obj); - return true; - } - - RootedObject proto(cx, obj->getProto()); - if (!proto) { - objp.set(nullptr); - propp.set(nullptr); - return true; - } - - return LookupProperty(cx, proto, id, objp, propp); -} - -/* static */ bool -UnboxedArrayObject::obj_defineProperty(JSContext* cx, HandleObject obj, HandleId id, - Handle desc, - ObjectOpResult& result) -{ - if (JSID_IS_INT(id) && !desc.getter() && !desc.setter() && desc.attributes() == JSPROP_ENUMERATE) { - UnboxedArrayObject* nobj = &obj->as(); - - uint32_t index = JSID_TO_INT(id); - if (index < nobj->initializedLength()) { - if (nobj->setElement(cx, index, desc.value())) - return result.succeed(); - } else if (index == nobj->initializedLength() && index < MaximumCapacity) { - if (nobj->initializedLength() == nobj->capacity()) { - if (!nobj->growElements(cx, index + 1)) - return false; - } - nobj->setInitializedLength(index + 1); - if (nobj->initElement(cx, index, desc.value())) { - if (nobj->length() <= index) - nobj->setLength(index + 1); - return result.succeed(); - } - nobj->setInitializedLength(index); - } - } - - if (!convertToNative(cx, obj)) - return false; - - return DefineProperty(cx, obj, id, desc, result); -} - -/* static */ bool -UnboxedArrayObject::obj_hasProperty(JSContext* cx, HandleObject obj, HandleId id, bool* foundp) -{ - if (obj->as().containsProperty(cx, id)) { - *foundp = true; - return true; - } - - RootedObject proto(cx, obj->getProto()); - if (!proto) { - *foundp = false; - return true; - } - - return HasProperty(cx, proto, id, foundp); -} - -/* static */ bool -UnboxedArrayObject::obj_getProperty(JSContext* cx, HandleObject obj, HandleObject receiver, - HandleId id, MutableHandleValue vp) -{ - if (obj->as().containsProperty(cx, id)) { - if (JSID_IS_INT(id)) - vp.set(obj->as().getElement(JSID_TO_INT(id))); - else - vp.set(Int32Value(obj->as().length())); - return true; - } - - RootedObject proto(cx, obj->getProto()); - if (!proto) { - vp.setUndefined(); - return true; - } - - return GetProperty(cx, proto, receiver, id, vp); -} - -/* static */ bool -UnboxedArrayObject::obj_setProperty(JSContext* cx, HandleObject obj, HandleId id, HandleValue v, - HandleValue receiver, ObjectOpResult& result) -{ - if (obj->as().containsProperty(cx, id)) { - if (receiver.isObject() && obj == &receiver.toObject()) { - if (JSID_IS_INT(id)) { - if (obj->as().setElement(cx, JSID_TO_INT(id), v)) - return result.succeed(); - } else { - uint32_t len; - if (!CanonicalizeArrayLengthValue(cx, v, &len)) - return false; - if (len <= INT32_MAX) { - UnboxedArrayObject* nobj = &obj->as(); - nobj->setLength(len); - if (len < nobj->initializedLength()) { - nobj->setInitializedLength(len); - nobj->shrinkElements(cx, len); - } - } - } - - if (!convertToNative(cx, obj)) - return false; - return SetProperty(cx, obj, id, v, receiver, result); - } - - return SetPropertyByDefining(cx, obj, id, v, receiver, false, result); - } - - return SetPropertyOnProto(cx, obj, id, v, receiver, result); -} - -/* static */ bool -UnboxedArrayObject::obj_getOwnPropertyDescriptor(JSContext* cx, HandleObject obj, HandleId id, - MutableHandle desc) -{ - if (obj->as().containsProperty(cx, id)) { - if (JSID_IS_INT(id)) { - desc.value().set(obj->as().getElement(JSID_TO_INT(id))); - desc.setAttributes(JSPROP_ENUMERATE); - } else { - desc.value().set(Int32Value(obj->as().length())); - desc.setAttributes(JSPROP_PERMANENT); - } - desc.object().set(obj); - return true; - } - - desc.object().set(nullptr); - return true; -} - -/* static */ bool -UnboxedArrayObject::obj_deleteProperty(JSContext* cx, HandleObject obj, HandleId id, - ObjectOpResult& result) -{ - if (!convertToNative(cx, obj)) - return false; - return DeleteProperty(cx, obj, id, result); -} - -/* static */ bool -UnboxedArrayObject::obj_watch(JSContext* cx, HandleObject obj, HandleId id, HandleObject callable) -{ - if (!convertToNative(cx, obj)) - return false; - return WatchProperty(cx, obj, id, callable); -} - -/* static */ bool -UnboxedArrayObject::obj_enumerate(JSContext* cx, HandleObject obj, AutoIdVector& properties) -{ - for (size_t i = 0; i < obj->as().initializedLength(); i++) { - if (!properties.append(INT_TO_JSID(i))) - return false; - } - return properties.append(NameToId(cx->names().length)); -} - -const Class UnboxedArrayObject::class_ = { - "Array", - Class::NON_NATIVE | - JSCLASS_IMPLEMENTS_BARRIERS | - 0 /* FIXME using this flag can severely hurt performance: JSCLASS_BACKGROUND_FINALIZE */, - nullptr, /* addProperty */ - nullptr, /* delProperty */ - nullptr, /* getProperty */ - nullptr, /* setProperty */ - nullptr, /* enumerate */ - nullptr, /* resolve */ - nullptr, /* mayResolve */ - nullptr, /* convert */ - UnboxedArrayObject::finalize, - nullptr, /* call */ - nullptr, /* hasInstance */ - nullptr, /* construct */ - UnboxedArrayObject::trace, - JS_NULL_CLASS_SPEC, - { - nullptr, /* outerObject */ - nullptr, /* innerObject */ - false, /* isWrappedNative */ - nullptr, /* weakmapKeyDelegateOp */ - UnboxedArrayObject::objectMoved - }, - { - UnboxedArrayObject::obj_lookupProperty, - UnboxedArrayObject::obj_defineProperty, - UnboxedArrayObject::obj_hasProperty, - UnboxedArrayObject::obj_getProperty, - UnboxedArrayObject::obj_setProperty, - UnboxedArrayObject::obj_getOwnPropertyDescriptor, - UnboxedArrayObject::obj_deleteProperty, - UnboxedArrayObject::obj_watch, - nullptr, /* No unwatch needed, as watch() converts the object to native */ - nullptr, /* getElements */ - UnboxedArrayObject::obj_enumerate, - nullptr, /* thisObject */ - } -}; - ///////////////////////////////////////////////////////////////////// // API ///////////////////////////////////////////////////////////////////// @@ -1522,20 +932,6 @@ UnboxedTypeIncludes(JSValueType supertype, JSValueType subtype) return false; } -static bool -CombineUnboxedTypes(const Value& value, JSValueType* existing) -{ - JSValueType type = value.isDouble() ? JSVAL_TYPE_DOUBLE : value.extractNonDoubleType(); - - if (*existing == JSVAL_TYPE_MAGIC || *existing == type || UnboxedTypeIncludes(type, *existing)) { - *existing = type; - return true; - } - if (UnboxedTypeIncludes(*existing, type)) - return true; - return false; -} - // Return whether the property names and types in layout are a subset of the // specified vector. static bool @@ -1556,67 +952,83 @@ PropertiesAreSuperset(const UnboxedLayout::PropertyVector& properties, UnboxedLa return true; } -static bool -CombinePlainObjectProperties(PlainObject* obj, Shape* templateShape, - UnboxedLayout::PropertyVector& properties) +bool +js::TryConvertToUnboxedLayout(ExclusiveContext* cx, Shape* templateShape, + ObjectGroup* group, PreliminaryObjectArray* objects) { - // All preliminary objects must have been created with enough space to - // fill in their unboxed data inline. This is ensured either by using - // the largest allocation kind (which limits the maximum size of an - // unboxed object), or by using an allocation kind that covers all - // properties in the template, as the space used by unboxed properties - // is less than or equal to that used by boxed properties. - MOZ_ASSERT(gc::GetGCKindSlots(obj->asTenured().getAllocKind()) >= - Min(NativeObject::MAX_FIXED_SLOTS, templateShape->slotSpan())); + // Unboxed objects are nightly only for now. The getenv() call will be + // removed when they are on by default. See bug 1153266. +#ifdef NIGHTLY_BUILD + if (!getenv("JS_OPTION_USE_UNBOXED_OBJECTS")) { + if (!templateShape->runtimeFromAnyThread()->options().unboxedObjects()) + return true; + } +#else + return true; +#endif - if (obj->lastProperty() != templateShape || obj->hasDynamicElements()) { - // Only use an unboxed representation if all created objects match - // the template shape exactly. + if (templateShape->runtimeFromAnyThread()->isSelfHostingGlobal(cx->global())) + return true; + + if (templateShape->slotSpan() == 0) + return true; + + UnboxedLayout::PropertyVector properties; + if (!properties.appendN(UnboxedLayout::Property(), templateShape->slotSpan())) return false; + + size_t objectCount = 0; + for (size_t i = 0; i < PreliminaryObjectArray::COUNT; i++) { + JSObject* obj = objects->get(i); + if (!obj) + continue; + + objectCount++; + + // All preliminary objects must have been created with enough space to + // fill in their unboxed data inline. This is ensured either by using + // the largest allocation kind (which limits the maximum size of an + // unboxed object), or by using an allocation kind that covers all + // properties in the template, as the space used by unboxed properties + // less than or equal to that used by boxed properties. + MOZ_ASSERT(gc::GetGCKindSlots(obj->asTenured().getAllocKind()) >= + Min(NativeObject::MAX_FIXED_SLOTS, templateShape->slotSpan())); + + if (obj->as().lastProperty() != templateShape || + obj->as().hasDynamicElements()) + { + // Only use an unboxed representation if all created objects match + // the template shape exactly. + return true; + } + + for (size_t i = 0; i < templateShape->slotSpan(); i++) { + Value val = obj->as().getSlot(i); + + JSValueType& existing = properties[i].type; + JSValueType type = val.isDouble() ? JSVAL_TYPE_DOUBLE : val.extractNonDoubleType(); + + if (existing == JSVAL_TYPE_MAGIC || existing == type || UnboxedTypeIncludes(type, existing)) + existing = type; + else if (!UnboxedTypeIncludes(existing, type)) + return true; + } + } + + if (objectCount <= 1) { + // If only one of the objects has been created, it is more likely to + // have new properties added later. + return true; } for (size_t i = 0; i < templateShape->slotSpan(); i++) { - Value val = obj->getSlot(i); - - JSValueType& existing = properties[i].type; - if (!CombineUnboxedTypes(val, &existing)) - return false; + // We can't use an unboxed representation if e.g. all the objects have + // a null value for one of the properties, as we can't decide what type + // it is supposed to have. + if (UnboxedTypeSize(properties[i].type) == 0) + return true; } - return true; -} - -static bool -CombineArrayObjectElements(ExclusiveContext* cx, ArrayObject* obj, JSValueType* elementType) -{ - if (obj->inDictionaryMode() || - obj->lastProperty()->propid() != AtomToId(cx->names().length) || - !obj->lastProperty()->previous()->isEmptyShape() || - !obj->getDenseInitializedLength()) - { - // Only use an unboxed representation if the object has at - // least one element, and no properties. - return false; - } - - for (size_t i = 0; i < obj->getDenseInitializedLength(); i++) { - Value val = obj->getDenseElement(i); - - // For now, unboxed arrays cannot have holes. - if (val.isMagic(JS_ELEMENTS_HOLE)) - return false; - - if (!CombineUnboxedTypes(val, elementType)) - return false; - } - - return true; -} - -static size_t -ComputePlainObjectLayout(ExclusiveContext* cx, Shape* templateShape, - UnboxedLayout::PropertyVector& properties) -{ // Fill in the names for all the object's properties. for (Shape::Range r(templateShape); !r.empty(); r.popFront()) { size_t slot = r.front().slot(); @@ -1676,23 +1088,27 @@ ComputePlainObjectLayout(ExclusiveContext* cx, Shape* templateShape, } } - // The final offset is the amount of data needed by the object. - return offset; -} + // The entire object must be allocatable inline. + if (sizeof(JSObject) + offset > JSObject::MAX_BYTE_SIZE) + return true; + + UniquePtr > layout; + layout.reset(group->zone()->new_(properties, offset)); + if (!layout) + return false; + + cx->compartment()->unboxedLayouts.insertFront(layout.get()); -static bool -SetLayoutTraceList(ExclusiveContext* cx, UnboxedLayout* layout) -{ // Figure out the offsets of any objects or string properties. Vector objectOffsets, stringOffsets; - for (size_t i = 0; i < layout->properties().length(); i++) { - const UnboxedLayout::Property& property = layout->properties()[i]; - MOZ_ASSERT(property.offset != UINT32_MAX); - if (property.type == JSVAL_TYPE_OBJECT) { - if (!objectOffsets.append(property.offset)) + for (size_t i = 0; i < templateShape->slotSpan(); i++) { + MOZ_ASSERT(properties[i].offset != UINT32_MAX); + JSValueType type = properties[i].type; + if (type == JSVAL_TYPE_OBJECT) { + if (!objectOffsets.append(properties[i].offset)) return false; - } else if (property.type == JSVAL_TYPE_STRING) { - if (!stringOffsets.append(property.offset)) + } else if (type == JSVAL_TYPE_STRING) { + if (!stringOffsets.append(properties[i].offset)) return false; } } @@ -1708,234 +1124,64 @@ SetLayoutTraceList(ExclusiveContext* cx, UnboxedLayout* layout) { return false; } - int32_t* traceList = cx->zone()->pod_malloc(entries.length()); + int32_t* traceList = group->zone()->pod_malloc(entries.length()); if (!traceList) return false; PodCopy(traceList, entries.begin(), entries.length()); layout->setTraceList(traceList); } - return true; -} - -static inline Value -NextValue(const AutoValueVector& values, size_t* valueCursor) -{ - return values[(*valueCursor)++]; -} - -static bool -GetValuesFromPreliminaryArrayObject(ArrayObject* obj, AutoValueVector& values) -{ - if (!values.append(Int32Value(obj->length()))) - return false; - if (!values.append(Int32Value(obj->getDenseInitializedLength()))) - return false; - for (size_t i = 0; i < obj->getDenseInitializedLength(); i++) { - if (!values.append(obj->getDenseElement(i))) - return false; - } - return true; -} - -void -UnboxedArrayObject::fillAfterConvert(ExclusiveContext* cx, - const AutoValueVector& values, size_t* valueCursor) -{ - MOZ_ASSERT(CapacityArray[1] == 0); - setCapacityIndex(1); - setInitializedLength(0); - setInlineElements(); - - setLength(NextValue(values, valueCursor).toInt32()); - - int32_t initlen = NextValue(values, valueCursor).toInt32(); - - if (!growElements(cx, initlen)) - CrashAtUnhandlableOOM("UnboxedArrayObject::fillAfterConvert"); - setInitializedLength(initlen); - - for (size_t i = 0; i < size_t(initlen); i++) - JS_ALWAYS_TRUE(initElement(cx, i, NextValue(values, valueCursor))); -} - -static bool -GetValuesFromPreliminaryPlainObject(PlainObject* obj, AutoValueVector& values) -{ - for (size_t i = 0; i < obj->slotSpan(); i++) { - if (!values.append(obj->getSlot(i))) - return false; - } - return true; -} - -void -UnboxedPlainObject::fillAfterConvert(ExclusiveContext* cx, - const AutoValueVector& values, size_t* valueCursor) -{ - initExpando(); - memset(data(), 0, layout().size()); - for (size_t i = 0; i < layout().properties().length(); i++) - JS_ALWAYS_TRUE(setValue(cx, layout().properties()[i], NextValue(values, valueCursor))); -} - -bool -js::TryConvertToUnboxedLayout(ExclusiveContext* cx, Shape* templateShape, - ObjectGroup* group, PreliminaryObjectArray* objects) -{ - // Unboxed objects are nightly only for now. The getenv() call will be - // removed when they are on by default. See bug 1153266. -#ifdef NIGHTLY_BUILD - if (templateShape) { - if (!getenv("JS_OPTION_USE_UNBOXED_OBJECTS")) { - if (!group->runtimeFromAnyThread()->options().unboxedObjects()) - return true; - } - } else { - if (!group->runtimeFromAnyThread()->options().unboxedArrays()) - return true; - } -#else - return true; -#endif - - MOZ_ASSERT_IF(templateShape, !templateShape->getObjectFlags()); - - bool isArray = !templateShape; - - const JS::RuntimeOptions& options = group->runtimeFromAnyThread()->options(); - if (isArray ? !options.unboxedArrays() : !options.unboxedObjects()) - return true; - - if (group->runtimeFromAnyThread()->isSelfHostingGlobal(cx->global())) - return true; - - if (!isArray && templateShape->slotSpan() == 0) - return true; - - UnboxedLayout::PropertyVector properties; - if (!isArray) { - if (!properties.appendN(UnboxedLayout::Property(), templateShape->slotSpan())) - return false; - } - JSValueType elementType = JSVAL_TYPE_MAGIC; - - size_t objectCount = 0; - for (size_t i = 0; i < PreliminaryObjectArray::COUNT; i++) { - JSObject* obj = objects->get(i); - if (!obj) - continue; - - objectCount++; - - if (isArray) { - if (!CombineArrayObjectElements(cx, &obj->as(), &elementType)) - return true; - } else { - if (!CombinePlainObjectProperties(&obj->as(), templateShape, properties)) - return true; - } - } - - size_t layoutSize = 0; - if (isArray) { - // Don't use an unboxed representation if we couldn't determine an - // element type for the objects. - if (UnboxedTypeSize(elementType) == 0) - return true; - } else { - if (objectCount <= 1) { - // If only one of the objects has been created, it is more likely - // to have new properties added later. This heuristic is not used - // for array objects, where we might want an unboxed representation - // even if there is only one large array. - return true; - } - - for (size_t i = 0; i < templateShape->slotSpan(); i++) { - // We can't use an unboxed representation if e.g. all the objects have - // a null value for one of the properties, as we can't decide what type - // it is supposed to have. - if (UnboxedTypeSize(properties[i].type) == 0) - return true; - } - - layoutSize = ComputePlainObjectLayout(cx, templateShape, properties); - - // The entire object must be allocatable inline. - if (sizeof(JSObject) + layoutSize > JSObject::MAX_BYTE_SIZE) - return true; - } - - UniquePtr > layout; - layout.reset(group->zone()->new_()); - if (!layout) - return false; - - if (isArray) { - layout->initArray(elementType); - } else { - if (!layout->initProperties(properties, layoutSize)) - return false; - - // The unboxedLayouts list only tracks layouts for plain objects. - cx->compartment()->unboxedLayouts.insertFront(layout.get()); - - if (!SetLayoutTraceList(cx, layout.get())) - return false; - } - // We've determined that all the preliminary objects can use the new layout - // just constructed, so convert the existing group to use the unboxed class, - // and update the preliminary objects to use the new layout. Do the - // fallible stuff first before modifying any objects. + // just constructed, so convert the existing group to be an + // UnboxedPlainObject rather than a PlainObject, and update the preliminary + // objects to use the new layout. Do the fallible stuff first before + // modifying any objects. // Get an empty shape which we can use for the preliminary objects. - const Class* clasp = isArray ? &UnboxedArrayObject::class_ : &UnboxedPlainObject::class_; - Shape* newShape = EmptyShape::getInitialShape(cx, clasp, group->proto(), 0); + Shape* newShape = EmptyShape::getInitialShape(cx, &UnboxedPlainObject::class_, + group->proto(), + templateShape->getObjectFlags()); if (!newShape) { cx->recoverFromOutOfMemory(); return false; } - // Accumulate a list of all the values in each preliminary object, and + // Accumulate a list of all the properties in each preliminary object, and // update their shapes. - AutoValueVector values(cx); + Vector values; + if (!values.reserve(objectCount * templateShape->slotSpan())) + return false; for (size_t i = 0; i < PreliminaryObjectArray::COUNT; i++) { - JSObject* obj = objects->get(i); - if (!obj) + if (!objects->get(i)) continue; - if (isArray) { - if (!GetValuesFromPreliminaryArrayObject(&obj->as(), values)) - return false; - } else { - if (!GetValuesFromPreliminaryPlainObject(&obj->as(), values)) - return false; - } + RootedNativeObject obj(cx, &objects->get(i)->as()); + for (size_t j = 0; j < templateShape->slotSpan(); j++) + values.infallibleAppend(obj->getSlot(j)); + + // Clear the object to remove any dynamically allocated information. + NativeObject::clear(cx, obj); + + obj->setLastPropertyMakeNonNative(newShape); } if (TypeNewScript* newScript = group->newScript()) layout->setNewScript(newScript); - for (size_t i = 0; i < PreliminaryObjectArray::COUNT; i++) { - if (JSObject* obj = objects->get(i)) - obj->as().setLastPropertyMakeNonNative(newShape); - } - - group->setClasp(clasp); + group->setClasp(&UnboxedPlainObject::class_); group->setUnboxedLayout(layout.get()); size_t valueCursor = 0; for (size_t i = 0; i < PreliminaryObjectArray::COUNT; i++) { - JSObject* obj = objects->get(i); - if (!obj) + if (!objects->get(i)) continue; - - if (isArray) - obj->as().fillAfterConvert(cx, values, &valueCursor); - else - obj->as().fillAfterConvert(cx, values, &valueCursor); + UnboxedPlainObject* obj = &objects->get(i)->as(); + obj->initExpando(); + memset(obj->data(), 0, layout->size()); + for (size_t j = 0; j < templateShape->slotSpan(); j++) { + Value v = values[valueCursor++]; + JS_ALWAYS_TRUE(obj->setValue(cx, properties[j], v)); + } } MOZ_ASSERT(valueCursor == values.length()); diff --git a/js/src/vm/UnboxedObject.h b/js/src/vm/UnboxedObject.h index f353544ef2af..c2de2506cf6a 100644 --- a/js/src/vm/UnboxedObject.h +++ b/js/src/vm/UnboxedObject.h @@ -35,7 +35,7 @@ UnboxedTypeNeedsPreBarrier(JSValueType type) return type == JSVAL_TYPE_STRING || type == JSVAL_TYPE_OBJECT; } -// Class tracking information specific to unboxed objects. +// Class describing the layout of an UnboxedPlainObject. class UnboxedLayout : public mozilla::LinkedListElement { public: @@ -52,14 +52,6 @@ class UnboxedLayout : public mozilla::LinkedListElement typedef Vector PropertyVector; private: - // If objects in this group have ever been converted to native objects, - // these store the corresponding native group and initial shape for such - // objects. Type information for this object is reflected in nativeGroup. - HeapPtrObjectGroup nativeGroup_; - HeapPtrShape nativeShape_; - - // The following members are only used for unboxed plain objects. - // All properties on objects with this layout, in enumeration order. PropertyVector properties_; @@ -73,6 +65,12 @@ class UnboxedLayout : public mozilla::LinkedListElement // structure as the trace list on a TypeDescr. int32_t* traceList_; + // If objects in this group have ever been converted to native objects, + // these store the corresponding native group and initial shape for such + // objects. Type information for this object is reflected in nativeGroup. + HeapPtrObjectGroup nativeGroup_; + HeapPtrShape nativeShape_; + // If nativeGroup is set and this object originally had a TypeNewScript, // this points to the default 'new' group which replaced this one (and // which might itself have been cleared since). This link is only needed to @@ -86,25 +84,13 @@ class UnboxedLayout : public mozilla::LinkedListElement // from an array of values. HeapPtrJitCode constructorCode_; - // The following members are only used for unboxed arrays. - - // The type of array elements. - JSValueType elementType_; - public: - UnboxedLayout() - : nativeGroup_(nullptr), nativeShape_(nullptr), size_(0), newScript_(nullptr), - traceList_(nullptr), replacementNewGroup_(nullptr), constructorCode_(nullptr), - elementType_(JSVAL_TYPE_MAGIC) - {} - - bool initProperties(const PropertyVector& properties, size_t size) { - size_ = size; - return properties_.appendAll(properties); - } - - void initArray(JSValueType elementType) { - elementType_ = elementType; + UnboxedLayout(const PropertyVector& properties, size_t size) + : size_(size), newScript_(nullptr), traceList_(nullptr), + nativeGroup_(nullptr), nativeShape_(nullptr), replacementNewGroup_(nullptr), + constructorCode_(nullptr) + { + properties_.appendAll(properties); } ~UnboxedLayout() { @@ -112,10 +98,6 @@ class UnboxedLayout : public mozilla::LinkedListElement js_free(traceList_); } - bool isArray() { - return elementType_ != JSVAL_TYPE_MAGIC; - } - void detachFromCompartment(); const PropertyVector& properties() const { @@ -170,10 +152,6 @@ class UnboxedLayout : public mozilla::LinkedListElement constructorCode_ = code; } - JSValueType elementType() const { - return elementType_; - } - inline gc::AllocKind getAllocKind() const; void trace(JSTracer* trc); @@ -273,9 +251,6 @@ class UnboxedPlainObject : public JSObject static JSObject* createWithProperties(ExclusiveContext* cx, HandleObjectGroup group, NewObjectKind newKind, IdValuePair* properties); - void fillAfterConvert(ExclusiveContext* cx, - const AutoValueVector& values, size_t* valueCursor); - static void trace(JSTracer* trc, JSObject* object); static size_t offsetOfExpando() { @@ -297,177 +272,9 @@ TryConvertToUnboxedLayout(ExclusiveContext* cx, Shape* templateShape, inline gc::AllocKind UnboxedLayout::getAllocKind() const { - MOZ_ASSERT(size()); return gc::GetGCObjectKindForBytes(UnboxedPlainObject::offsetOfData() + size()); } -// Class for an array object using an unboxed representation. -class UnboxedArrayObject : public JSObject -{ - // Elements pointer for the object. - uint8_t* elements_; - - // The nominal array length. This always fits in an int32_t. - uint32_t length_; - - // Value indicating the allocated capacity and initialized length of the - // array. The top CapacityBits bits are an index into CapacityArray, which - // indicates the elements capacity. The low InitializedLengthBits store the - // initialized length of the array. - uint32_t capacityIndexAndInitializedLength_; - - // If the elements are inline, they will point here. - uint8_t inlineElements_[1]; - - public: - static const uint32_t CapacityBits = 6; - static const uint32_t CapacityShift = 26; - - static const uint32_t CapacityMask = uint32_t(-1) << CapacityShift; - static const uint32_t InitializedLengthMask = (1 << CapacityShift) - 1; - - static const uint32_t MaximumCapacity = InitializedLengthMask; - static const uint32_t MinimumDynamicCapacity = 8; - - static const uint32_t CapacityArray[]; - - // Capacity index which indicates the array's length is also its capacity. - static const uint32_t CapacityMatchesLengthIndex = 0; - - private: - static inline uint32_t computeCapacity(uint32_t index, uint32_t length) { - if (index == CapacityMatchesLengthIndex) - return length; - return CapacityArray[index]; - } - - static uint32_t chooseCapacityIndex(uint32_t capacity, uint32_t length); - static uint32_t exactCapacityIndex(uint32_t capacity); - - public: - static const Class class_; - - static bool obj_lookupProperty(JSContext* cx, HandleObject obj, - HandleId id, MutableHandleObject objp, - MutableHandleShape propp); - - static bool obj_defineProperty(JSContext* cx, HandleObject obj, HandleId id, - Handle desc, - ObjectOpResult& result); - - static bool obj_hasProperty(JSContext* cx, HandleObject obj, HandleId id, bool* foundp); - - static bool obj_getProperty(JSContext* cx, HandleObject obj, HandleObject receiver, - HandleId id, MutableHandleValue vp); - - static bool obj_setProperty(JSContext* cx, HandleObject obj, HandleId id, HandleValue v, - HandleValue receiver, ObjectOpResult& result); - - static bool obj_getOwnPropertyDescriptor(JSContext* cx, HandleObject obj, HandleId id, - MutableHandle desc); - - static bool obj_deleteProperty(JSContext* cx, HandleObject obj, HandleId id, - ObjectOpResult& result); - - static bool obj_enumerate(JSContext* cx, HandleObject obj, AutoIdVector& properties); - static bool obj_watch(JSContext* cx, HandleObject obj, HandleId id, HandleObject callable); - - const UnboxedLayout& layout() const { - return group()->unboxedLayout(); - } - - const UnboxedLayout& layoutDontCheckGeneration() const { - return group()->unboxedLayoutDontCheckGeneration(); - } - - JSValueType elementType() const { - return layoutDontCheckGeneration().elementType(); - } - - uint32_t elementSize() const { - return UnboxedTypeSize(elementType()); - } - - static bool convertToNative(JSContext* cx, JSObject* obj); - static UnboxedArrayObject* create(ExclusiveContext* cx, HandleObjectGroup group, - uint32_t length, NewObjectKind newKind); - - void fillAfterConvert(ExclusiveContext* cx, - const AutoValueVector& values, size_t* valueCursor); - - static void trace(JSTracer* trc, JSObject* object); - static void objectMoved(JSObject* obj, const JSObject* old); - static void finalize(FreeOp* fop, JSObject* obj); - - uint8_t* elements() { - return elements_; - } - - bool hasInlineElements() const { - return elements_ == &inlineElements_[0]; - } - - uint32_t length() const { - return length_; - } - - uint32_t initializedLength() const { - return capacityIndexAndInitializedLength_ & InitializedLengthMask; - } - - uint32_t capacityIndex() const { - return (capacityIndexAndInitializedLength_ & CapacityMask) >> CapacityShift; - } - - uint32_t capacity() const { - return computeCapacity(capacityIndex(), length()); - } - - bool containsProperty(JSContext* cx, jsid id); - - bool setElement(ExclusiveContext* cx, size_t index, const Value& v); - bool initElement(ExclusiveContext* cx, size_t index, const Value& v); - Value getElement(size_t index); - - bool growElements(ExclusiveContext* cx, size_t cap); - void shrinkElements(ExclusiveContext* cx, size_t cap); - - static uint32_t offsetOfElements() { - return offsetof(UnboxedArrayObject, elements_); - } - static uint32_t offsetOfLength() { - return offsetof(UnboxedArrayObject, length_); - } - static uint32_t offsetOfCapacityIndexAndInitializedLength() { - return offsetof(UnboxedArrayObject, capacityIndexAndInitializedLength_); - } - static uint32_t offsetOfInlineElements() { - return offsetof(UnboxedArrayObject, inlineElements_); - } - - private: - void setInlineElements() { - elements_ = &inlineElements_[0]; - } - - void setLength(uint32_t len) { - MOZ_ASSERT(len <= INT32_MAX); - length_ = len; - } - - void setInitializedLength(uint32_t initlen) { - MOZ_ASSERT(initlen <= InitializedLengthMask); - capacityIndexAndInitializedLength_ = - (capacityIndexAndInitializedLength_ & CapacityMask) | initlen; - } - - void setCapacityIndex(uint32_t index) { - MOZ_ASSERT(index <= (CapacityMask >> CapacityShift)); - capacityIndexAndInitializedLength_ = - (index << CapacityShift) | initializedLength(); - } -}; - } // namespace js #endif /* vm_UnboxedObject_h */ diff --git a/js/src/vm/Xdr.h b/js/src/vm/Xdr.h index 8beaf79828a5..8581a0890b73 100644 --- a/js/src/vm/Xdr.h +++ b/js/src/vm/Xdr.h @@ -29,7 +29,7 @@ namespace js { * * https://developer.mozilla.org/en-US/docs/SpiderMonkey/Internals/Bytecode */ -static const uint32_t XDR_BYTECODE_VERSION_SUBTRAHEND = 282; +static const uint32_t XDR_BYTECODE_VERSION_SUBTRAHEND = 281; static const uint32_t XDR_BYTECODE_VERSION = uint32_t(0xb973c0de - XDR_BYTECODE_VERSION_SUBTRAHEND);