mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 1146597 - Add unboxed arrays for JSOP_NEWARRAY arrays, and shell option for using them, r=jandem.
This commit is contained in:
parent
a83483431e
commit
cfd7d0bcfc
@ -6430,7 +6430,7 @@ BytecodeEmitter::emitCallOrNew(ParseNode* pn)
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (!emitArray(pn2->pn_next, argc))
|
||||
if (!emitArray(pn2->pn_next, argc, JSOP_SPREADCALLARRAY))
|
||||
return false;
|
||||
}
|
||||
emittingForInit = oldEmittingForInit;
|
||||
@ -6897,7 +6897,7 @@ BytecodeEmitter::emitSpread()
|
||||
}
|
||||
|
||||
bool
|
||||
BytecodeEmitter::emitArray(ParseNode* pn, uint32_t count)
|
||||
BytecodeEmitter::emitArray(ParseNode* pn, uint32_t count, JSOp op)
|
||||
{
|
||||
/*
|
||||
* Emit code for [a, b, c] that is equivalent to constructing a new
|
||||
@ -6907,6 +6907,7 @@ BytecodeEmitter::emitArray(ParseNode* pn, uint32_t count)
|
||||
* 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) {
|
||||
@ -6915,9 +6916,9 @@ BytecodeEmitter::emitArray(ParseNode* pn, uint32_t count)
|
||||
}
|
||||
|
||||
ptrdiff_t off;
|
||||
if (!emitN(JSOP_NEWARRAY, 3, &off)) // ARRAY
|
||||
if (!emitN(op, 3, &off)) // ARRAY
|
||||
return false;
|
||||
checkTypeSet(JSOP_NEWARRAY);
|
||||
checkTypeSet(op);
|
||||
jsbytecode* pc = code(off);
|
||||
|
||||
// For arrays with spread, this is a very pessimistic allocation, the
|
||||
@ -7575,7 +7576,7 @@ BytecodeEmitter::emitTree(ParseNode* pn)
|
||||
}
|
||||
}
|
||||
|
||||
ok = emitArray(pn->pn_head, pn->pn_count);
|
||||
ok = emitArray(pn->pn_head, pn->pn_count, JSOP_NEWARRAY);
|
||||
break;
|
||||
|
||||
case PNK_ARRAYCOMP:
|
||||
|
@ -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);
|
||||
bool emitArray(ParseNode* pn, uint32_t count, JSOp op);
|
||||
bool emitArrayComp(ParseNode* pn);
|
||||
|
||||
bool emitInternedObjectOp(uint32_t index, JSOp op);
|
||||
|
@ -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"] <= 20)
|
||||
setJitCompilerOption("ion.warmup.trigger", 20);
|
||||
if (getJitCompilerOptions()["ion.warmup.trigger"] <= 100)
|
||||
setJitCompilerOption("ion.warmup.trigger", 100);
|
||||
|
||||
// This function is used to force a bailout when it is inlined, and to recover
|
||||
// the frame which is inlining this function.
|
||||
|
@ -1740,23 +1740,15 @@ 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, group in R1.
|
||||
// Pass length in R0.
|
||||
masm.move32(Imm32(length), R0.scratchReg());
|
||||
masm.movePtr(ImmGCPtr(group), R1.scratchReg());
|
||||
|
||||
ArrayObject* templateObject = NewDenseUnallocatedArray(cx, length, NullPtr(), TenuredObject);
|
||||
if (!templateObject)
|
||||
ObjectGroup* group = ObjectGroup::allocationSiteGroup(cx, script, pc, JSProto_Array);
|
||||
if (!group)
|
||||
return false;
|
||||
templateObject->setGroup(group);
|
||||
|
||||
ICNewArray_Fallback::Compiler stubCompiler(cx, templateObject);
|
||||
ICNewArray_Fallback::Compiler stubCompiler(cx, group);
|
||||
if (!emitOpIC(stubCompiler.getStub(&stubSpace_)))
|
||||
return false;
|
||||
|
||||
@ -1764,6 +1756,12 @@ 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<NewArrayCopyOnWriteFn>(js::NewDenseCopyOnWriteArray);
|
||||
@ -1829,24 +1827,15 @@ BaselineCompiler::emit_JSOP_NEWINIT()
|
||||
frame.syncStack(0);
|
||||
JSProtoKey key = JSProtoKey(GET_UINT8(pc));
|
||||
|
||||
RootedObjectGroup group(cx);
|
||||
if (!ObjectGroup::useSingletonForAllocationSite(script, pc, key)) {
|
||||
group = ObjectGroup::allocationSiteGroup(cx, script, pc, key);
|
||||
if (key == JSProto_Array) {
|
||||
// Pass length in R0.
|
||||
masm.move32(Imm32(0), R0.scratchReg());
|
||||
|
||||
ObjectGroup* group = ObjectGroup::allocationSiteGroup(cx, script, pc, JSProto_Array);
|
||||
if (!group)
|
||||
return false;
|
||||
}
|
||||
|
||||
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);
|
||||
ICNewArray_Fallback::Compiler stubCompiler(cx, group);
|
||||
if (!emitOpIC(stubCompiler.getStub(&stubSpace_)))
|
||||
return false;
|
||||
} else {
|
||||
|
@ -93,6 +93,7 @@ namespace jit {
|
||||
_(JSOP_BITNOT) \
|
||||
_(JSOP_NEG) \
|
||||
_(JSOP_NEWARRAY) \
|
||||
_(JSOP_SPREADCALLARRAY) \
|
||||
_(JSOP_NEWARRAY_COPYONWRITE) \
|
||||
_(JSOP_INITELEM_ARRAY) \
|
||||
_(JSOP_NEWOBJECT) \
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -397,12 +397,13 @@ class ICEntry
|
||||
_(GetElem_NativePrototypeCallScripted) \
|
||||
_(GetElem_String) \
|
||||
_(GetElem_Dense) \
|
||||
_(GetElem_UnboxedArray) \
|
||||
_(GetElem_TypedArray) \
|
||||
_(GetElem_Arguments) \
|
||||
\
|
||||
_(SetElem_Fallback) \
|
||||
_(SetElem_Dense) \
|
||||
_(SetElem_DenseAdd) \
|
||||
_(SetElem_DenseOrUnboxedArray) \
|
||||
_(SetElem_DenseOrUnboxedArrayAdd) \
|
||||
_(SetElem_TypedArray) \
|
||||
\
|
||||
_(In_Fallback) \
|
||||
@ -428,6 +429,7 @@ class ICEntry
|
||||
\
|
||||
_(GetProp_Fallback) \
|
||||
_(GetProp_ArrayLength) \
|
||||
_(GetProp_UnboxedArrayLength) \
|
||||
_(GetProp_Primitive) \
|
||||
_(GetProp_StringLength) \
|
||||
_(GetProp_Native) \
|
||||
@ -1737,31 +1739,45 @@ class ICNewArray_Fallback : public ICFallbackStub
|
||||
{
|
||||
friend class ICStubSpace;
|
||||
|
||||
HeapPtrArrayObject templateObject_;
|
||||
HeapPtrObject templateObject_;
|
||||
|
||||
ICNewArray_Fallback(JitCode* stubCode, ArrayObject* templateObject)
|
||||
: ICFallbackStub(ICStub::NewArray_Fallback, stubCode), templateObject_(templateObject)
|
||||
// The group used for objects created here is always available, even if the
|
||||
// template object itself is not.
|
||||
HeapPtrObjectGroup templateGroup_;
|
||||
|
||||
ICNewArray_Fallback(JitCode* stubCode, ObjectGroup* templateGroup)
|
||||
: ICFallbackStub(ICStub::NewArray_Fallback, stubCode),
|
||||
templateObject_(nullptr), templateGroup_(templateGroup)
|
||||
{}
|
||||
|
||||
public:
|
||||
class Compiler : public ICStubCompiler {
|
||||
RootedArrayObject templateObject;
|
||||
RootedObjectGroup templateGroup;
|
||||
bool generateStubCode(MacroAssembler& masm);
|
||||
|
||||
public:
|
||||
Compiler(JSContext* cx, ArrayObject* templateObject)
|
||||
Compiler(JSContext* cx, ObjectGroup* templateGroup)
|
||||
: ICStubCompiler(cx, ICStub::NewArray_Fallback),
|
||||
templateObject(cx, templateObject)
|
||||
templateGroup(cx, templateGroup)
|
||||
{}
|
||||
|
||||
ICStub* getStub(ICStubSpace* space) {
|
||||
return ICStub::New<ICNewArray_Fallback>(space, getStubCode(), templateObject);
|
||||
return ICStub::New<ICNewArray_Fallback>(space, getStubCode(), templateGroup);
|
||||
}
|
||||
};
|
||||
|
||||
HeapPtrArrayObject& templateObject() {
|
||||
HeapPtrObject& templateObject() {
|
||||
return templateObject_;
|
||||
}
|
||||
|
||||
void setTemplateObject(JSObject* obj) {
|
||||
MOZ_ASSERT(obj->group() == templateGroup());
|
||||
templateObject_ = obj;
|
||||
}
|
||||
|
||||
HeapPtrObjectGroup& templateGroup() {
|
||||
return templateGroup_;
|
||||
}
|
||||
};
|
||||
|
||||
class ICNewObject_Fallback : public ICFallbackStub
|
||||
@ -3026,6 +3042,54 @@ 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<int32_t>(kind) |
|
||||
(static_cast<int32_t>(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<ICGetElem_UnboxedArray>(space, getStubCode(), firstMonitorStub_,
|
||||
group_);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
// Enum for stubs handling a combination of typed arrays and typed objects.
|
||||
enum TypedThingLayout {
|
||||
Layout_TypedArray,
|
||||
@ -3183,21 +3247,21 @@ class ICSetElem_Fallback : public ICFallbackStub
|
||||
};
|
||||
};
|
||||
|
||||
class ICSetElem_Dense : public ICUpdatedStub
|
||||
class ICSetElem_DenseOrUnboxedArray : public ICUpdatedStub
|
||||
{
|
||||
friend class ICStubSpace;
|
||||
|
||||
HeapPtrShape shape_;
|
||||
HeapPtrShape shape_; // null for unboxed arrays
|
||||
HeapPtrObjectGroup group_;
|
||||
|
||||
ICSetElem_Dense(JitCode* stubCode, Shape* shape, ObjectGroup* group);
|
||||
ICSetElem_DenseOrUnboxedArray(JitCode* stubCode, Shape* shape, ObjectGroup* group);
|
||||
|
||||
public:
|
||||
static size_t offsetOfShape() {
|
||||
return offsetof(ICSetElem_Dense, shape_);
|
||||
return offsetof(ICSetElem_DenseOrUnboxedArray, shape_);
|
||||
}
|
||||
static size_t offsetOfGroup() {
|
||||
return offsetof(ICSetElem_Dense, group_);
|
||||
return offsetof(ICSetElem_DenseOrUnboxedArray, group_);
|
||||
}
|
||||
|
||||
HeapPtrShape& shape() {
|
||||
@ -3209,33 +3273,41 @@ class ICSetElem_Dense : public ICUpdatedStub
|
||||
|
||||
class Compiler : public ICStubCompiler {
|
||||
RootedShape shape_;
|
||||
|
||||
// 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_;
|
||||
RootedObjectGroup group_;
|
||||
JSValueType unboxedType_;
|
||||
|
||||
bool generateStubCode(MacroAssembler& masm);
|
||||
|
||||
public:
|
||||
virtual int32_t getKey() const {
|
||||
return static_cast<int32_t>(kind) |
|
||||
(static_cast<int32_t>(unboxedType_) << 16);
|
||||
}
|
||||
|
||||
Compiler(JSContext* cx, Shape* shape, HandleObjectGroup group)
|
||||
: ICStubCompiler(cx, ICStub::SetElem_Dense),
|
||||
: ICStubCompiler(cx, ICStub::SetElem_DenseOrUnboxedArray),
|
||||
shape_(cx, shape),
|
||||
group_(group)
|
||||
group_(cx, group),
|
||||
unboxedType_(shape ? JSVAL_TYPE_MAGIC : group->unboxedLayout().elementType())
|
||||
{}
|
||||
|
||||
ICUpdatedStub* getStub(ICStubSpace* space) {
|
||||
ICSetElem_Dense* stub = ICStub::New<ICSetElem_Dense>(space, getStubCode(), shape_, group_);
|
||||
ICSetElem_DenseOrUnboxedArray* stub =
|
||||
ICStub::New<ICSetElem_DenseOrUnboxedArray>(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 <size_t ProtoChainDepth> class ICSetElem_DenseAddImpl;
|
||||
template <size_t ProtoChainDepth> class ICSetElem_DenseOrUnboxedArrayAddImpl;
|
||||
|
||||
class ICSetElem_DenseAdd : public ICUpdatedStub
|
||||
class ICSetElem_DenseOrUnboxedArrayAdd : public ICUpdatedStub
|
||||
{
|
||||
friend class ICStubSpace;
|
||||
|
||||
@ -3245,11 +3317,11 @@ class ICSetElem_DenseAdd : public ICUpdatedStub
|
||||
protected:
|
||||
HeapPtrObjectGroup group_;
|
||||
|
||||
ICSetElem_DenseAdd(JitCode* stubCode, ObjectGroup* group, size_t protoChainDepth);
|
||||
ICSetElem_DenseOrUnboxedArrayAdd(JitCode* stubCode, ObjectGroup* group, size_t protoChainDepth);
|
||||
|
||||
public:
|
||||
static size_t offsetOfGroup() {
|
||||
return offsetof(ICSetElem_DenseAdd, group_);
|
||||
return offsetof(ICSetElem_DenseOrUnboxedArrayAdd, group_);
|
||||
}
|
||||
|
||||
HeapPtrObjectGroup& group() {
|
||||
@ -3261,28 +3333,29 @@ class ICSetElem_DenseAdd : public ICUpdatedStub
|
||||
}
|
||||
|
||||
template <size_t ProtoChainDepth>
|
||||
ICSetElem_DenseAddImpl<ProtoChainDepth>* toImplUnchecked() {
|
||||
return static_cast<ICSetElem_DenseAddImpl<ProtoChainDepth>*>(this);
|
||||
ICSetElem_DenseOrUnboxedArrayAddImpl<ProtoChainDepth>* toImplUnchecked() {
|
||||
return static_cast<ICSetElem_DenseOrUnboxedArrayAddImpl<ProtoChainDepth>*>(this);
|
||||
}
|
||||
|
||||
template <size_t ProtoChainDepth>
|
||||
ICSetElem_DenseAddImpl<ProtoChainDepth>* toImpl() {
|
||||
ICSetElem_DenseOrUnboxedArrayAddImpl<ProtoChainDepth>* toImpl() {
|
||||
MOZ_ASSERT(ProtoChainDepth == protoChainDepth());
|
||||
return toImplUnchecked<ProtoChainDepth>();
|
||||
}
|
||||
};
|
||||
|
||||
template <size_t ProtoChainDepth>
|
||||
class ICSetElem_DenseAddImpl : public ICSetElem_DenseAdd
|
||||
class ICSetElem_DenseOrUnboxedArrayAddImpl : public ICSetElem_DenseOrUnboxedArrayAdd
|
||||
{
|
||||
friend class ICStubSpace;
|
||||
|
||||
// Note: for unboxed arrays, the first shape is null.
|
||||
static const size_t NumShapes = ProtoChainDepth + 1;
|
||||
mozilla::Array<HeapPtrShape, NumShapes> shapes_;
|
||||
|
||||
ICSetElem_DenseAddImpl(JitCode* stubCode, ObjectGroup* group,
|
||||
const AutoShapeVector* shapes)
|
||||
: ICSetElem_DenseAdd(stubCode, group, ProtoChainDepth)
|
||||
ICSetElem_DenseOrUnboxedArrayAddImpl(JitCode* stubCode, ObjectGroup* group,
|
||||
const AutoShapeVector* shapes)
|
||||
: ICSetElem_DenseOrUnboxedArrayAdd(stubCode, group, ProtoChainDepth)
|
||||
{
|
||||
MOZ_ASSERT(shapes->length() == NumShapes);
|
||||
for (size_t i = 0; i < NumShapes; i++)
|
||||
@ -3291,40 +3364,52 @@ class ICSetElem_DenseAddImpl : public ICSetElem_DenseAdd
|
||||
|
||||
public:
|
||||
void traceShapes(JSTracer* trc) {
|
||||
for (size_t i = 0; i < NumShapes; i++)
|
||||
TraceEdge(trc, &shapes_[i], "baseline-setelem-denseadd-stub-shape");
|
||||
for (size_t i = 0; i < NumShapes; i++) {
|
||||
if (shapes_[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_DenseAddImpl, shapes_) + idx * sizeof(HeapPtrShape);
|
||||
return offsetof(ICSetElem_DenseOrUnboxedArrayAddImpl, shapes_) + idx * sizeof(HeapPtrShape);
|
||||
}
|
||||
};
|
||||
|
||||
class ICSetElemDenseAddCompiler : public ICStubCompiler {
|
||||
class ICSetElemDenseOrUnboxedArrayAddCompiler : public ICStubCompiler {
|
||||
RootedObject obj_;
|
||||
size_t protoChainDepth_;
|
||||
JSValueType unboxedType_;
|
||||
|
||||
bool generateStubCode(MacroAssembler& masm);
|
||||
|
||||
protected:
|
||||
virtual int32_t getKey() const {
|
||||
return static_cast<int32_t>(kind) | (static_cast<int32_t>(protoChainDepth_) << 16);
|
||||
return static_cast<int32_t>(kind) |
|
||||
(static_cast<int32_t>(protoChainDepth_) << 16) |
|
||||
(static_cast<int32_t>(unboxedType_) << 19);
|
||||
}
|
||||
|
||||
public:
|
||||
ICSetElemDenseAddCompiler(JSContext* cx, HandleObject obj, size_t protoChainDepth)
|
||||
: ICStubCompiler(cx, ICStub::SetElem_DenseAdd),
|
||||
ICSetElemDenseOrUnboxedArrayAddCompiler(JSContext* cx, HandleObject obj, size_t protoChainDepth)
|
||||
: ICStubCompiler(cx, ICStub::SetElem_DenseOrUnboxedArrayAdd),
|
||||
obj_(cx, obj),
|
||||
protoChainDepth_(protoChainDepth)
|
||||
protoChainDepth_(protoChainDepth),
|
||||
unboxedType_(obj->is<UnboxedArrayObject>()
|
||||
? obj->as<UnboxedArrayObject>().elementType()
|
||||
: JSVAL_TYPE_MAGIC)
|
||||
{}
|
||||
|
||||
template <size_t ProtoChainDepth>
|
||||
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.
|
||||
@ -3976,6 +4061,30 @@ 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<ICGetProp_UnboxedArrayLength>(space, getStubCode());
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
// Stub for accessing a property on a primitive's prototype.
|
||||
class ICGetProp_Primitive : public ICMonitoredStub
|
||||
{
|
||||
|
@ -21,9 +21,9 @@ SetElemICInspector::sawOOBDenseWrite() const
|
||||
if (!icEntry_)
|
||||
return false;
|
||||
|
||||
// Check for a SetElem_DenseAdd stub.
|
||||
// Check for an element adding stub.
|
||||
for (ICStub* stub = icEntry_->firstStub(); stub; stub = stub->next()) {
|
||||
if (stub->isSetElem_DenseAdd())
|
||||
if (stub->isSetElem_DenseOrUnboxedArrayAdd())
|
||||
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_DenseAdd() || stub->isSetElem_Dense())
|
||||
if (stub->isSetElem_DenseOrUnboxedArrayAdd() || stub->isSetElem_DenseOrUnboxedArray())
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@ -463,6 +463,25 @@ BaselineInspector::getTemplateObject(jsbytecode* pc)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ObjectGroup*
|
||||
BaselineInspector::getTemplateObjectGroup(jsbytecode* pc)
|
||||
{
|
||||
if (!hasBaselineScript())
|
||||
return nullptr;
|
||||
|
||||
const ICEntry& entry = icEntryFromPC(pc);
|
||||
for (ICStub* stub = entry.firstStub(); stub; stub = stub->next()) {
|
||||
switch (stub->kind()) {
|
||||
case ICStub::NewArray_Fallback:
|
||||
return stub->toNewArray_Fallback()->templateGroup();
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
JSFunction*
|
||||
BaselineInspector::getSingleCallee(jsbytecode* pc)
|
||||
{
|
||||
|
@ -117,6 +117,10 @@ class BaselineInspector
|
||||
JSObject* getTemplateObjectForNative(jsbytecode* pc, Native native);
|
||||
JSObject* getTemplateObjectForClassHook(jsbytecode* pc, const Class* clasp);
|
||||
|
||||
// Sometimes the group a template object will have is known, even if the
|
||||
// object itself isn't.
|
||||
ObjectGroup* getTemplateObjectGroup(jsbytecode* pc);
|
||||
|
||||
JSFunction* getSingleCallee(jsbytecode* pc);
|
||||
|
||||
DeclEnvObject* templateDeclEnvObject();
|
||||
|
@ -2339,6 +2339,18 @@ CodeGenerator::visitGetPropertyPolymorphicT(LGetPropertyPolymorphicT* ins)
|
||||
emitGetPropertyPolymorphic(ins, obj, temp, output);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
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)
|
||||
@ -2377,13 +2389,7 @@ CodeGenerator::emitSetPropertyPolymorphic(LInstruction* ins, Register obj, Regis
|
||||
receiver.group->unboxedLayout().lookup(mir->name());
|
||||
Address propertyAddr(obj, UnboxedPlainObject::offsetOfData() + property->offset);
|
||||
|
||||
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));
|
||||
|
||||
EmitUnboxedPreBarrier(masm, propertyAddr, property->type);
|
||||
masm.storeUnboxedProperty(propertyAddr, property->type, value, nullptr);
|
||||
}
|
||||
|
||||
@ -2425,7 +2431,9 @@ CodeGenerator::visitSetPropertyPolymorphicT(LSetPropertyPolymorphicT* ins)
|
||||
void
|
||||
CodeGenerator::visitElements(LElements* lir)
|
||||
{
|
||||
Address elements(ToRegister(lir->object()), NativeObject::offsetOfElements());
|
||||
Address elements(ToRegister(lir->object()),
|
||||
lir->mir()->unboxed() ? UnboxedArrayObject::offsetOfElements()
|
||||
: NativeObject::offsetOfElements());
|
||||
masm.loadPtr(elements, ToRegister(lir->output()));
|
||||
}
|
||||
|
||||
@ -4056,6 +4064,11 @@ class OutOfLineNewArray : public OutOfLineCodeBase<CodeGenerator>
|
||||
}
|
||||
};
|
||||
|
||||
typedef JSObject* (*NewArrayOperationFn)(JSContext*, HandleScript, jsbytecode*, uint32_t,
|
||||
NewObjectKind);
|
||||
static const VMFunction NewArrayOperationInfo =
|
||||
FunctionInfo<NewArrayOperationFn>(NewArrayOperation);
|
||||
|
||||
typedef ArrayObject* (*NewDenseArrayFn)(ExclusiveContext*, uint32_t, HandleObjectGroup,
|
||||
AllocatingBehaviour, bool);
|
||||
static const VMFunction NewDenseArrayInfo = FunctionInfo<NewDenseArrayFn>(NewDenseArray);
|
||||
@ -4069,15 +4082,22 @@ CodeGenerator::visitNewArrayCallVM(LNewArray* lir)
|
||||
saveLive(lir);
|
||||
|
||||
JSObject* templateObject = lir->mir()->templateObject();
|
||||
ObjectGroup* group =
|
||||
templateObject->isSingleton() ? nullptr : templateObject->group();
|
||||
|
||||
pushArg(Imm32(lir->mir()->convertDoubleElements()));
|
||||
pushArg(Imm32(lir->mir()->allocatingBehaviour()));
|
||||
pushArg(ImmGCPtr(group));
|
||||
pushArg(Imm32(lir->mir()->count()));
|
||||
if (templateObject && !templateObject->is<UnboxedArrayObject>()) {
|
||||
pushArg(Imm32(lir->mir()->convertDoubleElements()));
|
||||
pushArg(Imm32(lir->mir()->allocatingBehaviour()));
|
||||
pushArg(ImmGCPtr(templateObject->group()));
|
||||
pushArg(Imm32(lir->mir()->count()));
|
||||
|
||||
callVM(NewDenseArrayInfo, lir);
|
||||
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);
|
||||
}
|
||||
|
||||
if (ReturnReg != objReg)
|
||||
masm.movePtr(ReturnReg, objReg);
|
||||
@ -4147,7 +4167,7 @@ CodeGenerator::visitNewArray(LNewArray* lir)
|
||||
{
|
||||
Register objReg = ToRegister(lir->output());
|
||||
Register tempReg = ToRegister(lir->temp());
|
||||
ArrayObject* templateObject = lir->mir()->templateObject();
|
||||
JSObject* templateObject = lir->mir()->templateObject();
|
||||
DebugOnly<uint32_t> count = lir->mir()->count();
|
||||
|
||||
MOZ_ASSERT(count < NativeObject::NELEMENTS_LIMIT);
|
||||
@ -6276,6 +6296,30 @@ 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)
|
||||
{
|
||||
@ -6480,18 +6524,21 @@ 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;
|
||||
if (value->isConstant())
|
||||
v = ConstantOrRegister(*value->toConstant());
|
||||
else
|
||||
v = TypedOrValueRegister(valueType, ToAnyRegister(value));
|
||||
|
||||
ConstantOrRegister v = ToConstantOrRegister(value, valueType);
|
||||
if (index->isConstant()) {
|
||||
Address dest(elements, ToInt32(index) * sizeof(js::Value) + offsetAdjustment);
|
||||
masm.storeUnboxedValue(v, valueType, dest, elementType);
|
||||
@ -6548,19 +6595,45 @@ 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();
|
||||
|
||||
// OOL path if index >= initializedLength.
|
||||
Address initLength(elements, ObjectElements::offsetOfInitializedLength());
|
||||
masm.branchKey(Assembler::BelowOrEqual, initLength, ToInt32Key(index), ool->entry());
|
||||
JSValueType unboxedType = lir->mir()->unboxedType();
|
||||
if (unboxedType == JSVAL_TYPE_MAGIC) {
|
||||
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);
|
||||
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->rejoin());
|
||||
}
|
||||
@ -6571,29 +6644,54 @@ 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);
|
||||
|
||||
// OOL path if index >= initializedLength.
|
||||
Address initLength(elements, ObjectElements::offsetOfInitializedLength());
|
||||
masm.branchKey(Assembler::BelowOrEqual, initLength, ToInt32Key(index), ool->entry());
|
||||
JSValueType unboxedType = lir->mir()->unboxedType();
|
||||
if (unboxedType == JSVAL_TYPE_MAGIC) {
|
||||
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 (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->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->rejoin());
|
||||
}
|
||||
|
||||
typedef bool (*SetDenseElementFn)(JSContext*, HandleNativeObject, int32_t, HandleValue,
|
||||
bool strict);
|
||||
static const VMFunction SetDenseElementInfo = FunctionInfo<SetDenseElementFn>(SetDenseElement);
|
||||
typedef bool (*SetDenseOrUnboxedArrayElementFn)(JSContext*, HandleObject, int32_t,
|
||||
HandleValue, bool strict);
|
||||
static const VMFunction SetDenseOrUnboxedArrayElementInfo =
|
||||
FunctionInfo<SetDenseOrUnboxedArrayElementFn>(SetDenseOrUnboxedArrayElement);
|
||||
|
||||
void
|
||||
CodeGenerator::visitOutOfLineStoreElementHole(OutOfLineStoreElementHole* ool)
|
||||
@ -6603,6 +6701,8 @@ CodeGenerator::visitOutOfLineStoreElementHole(OutOfLineStoreElementHole* ool)
|
||||
const LAllocation* index;
|
||||
MIRType valueType;
|
||||
ConstantOrRegister value;
|
||||
JSValueType unboxedType;
|
||||
LDefinition *temp = nullptr;
|
||||
|
||||
if (ins->isStoreElementHoleV()) {
|
||||
LStoreElementHoleV* store = ins->toStoreElementHoleV();
|
||||
@ -6611,6 +6711,8 @@ 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());
|
||||
@ -6621,6 +6723,8 @@ 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.
|
||||
@ -6629,33 +6733,55 @@ CodeGenerator::visitOutOfLineStoreElementHole(OutOfLineStoreElementHole* ool)
|
||||
Label callStub;
|
||||
#ifdef JS_CODEGEN_MIPS
|
||||
// Had to reimplement for MIPS because there are no flags.
|
||||
Address initLength(elements, ObjectElements::offsetOfInitializedLength());
|
||||
masm.branchKey(Assembler::NotEqual, initLength, ToInt32Key(index), &callStub);
|
||||
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);
|
||||
}
|
||||
#else
|
||||
masm.j(Assembler::NotEqual, &callStub);
|
||||
#endif
|
||||
|
||||
Int32Key key = ToInt32Key(index);
|
||||
|
||||
// Check array capacity.
|
||||
masm.branchKey(Assembler::BelowOrEqual, Address(elements, ObjectElements::offsetOfCapacity()),
|
||||
key, &callStub);
|
||||
if (unboxedType == JSVAL_TYPE_MAGIC) {
|
||||
// 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);
|
||||
masm.bumpKey(&key, -1);
|
||||
} else {
|
||||
// Check array capacity.
|
||||
masm.checkUnboxedArrayCapacity(object, key, ToRegister(temp), &callStub);
|
||||
|
||||
if (ins->isStoreElementHoleT() && valueType != MIRType_Double) {
|
||||
// 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) {
|
||||
// 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.
|
||||
@ -6677,7 +6803,7 @@ CodeGenerator::visitOutOfLineStoreElementHole(OutOfLineStoreElementHole* ool)
|
||||
else
|
||||
pushArg(ToRegister(index));
|
||||
pushArg(object);
|
||||
callVM(SetDenseElementInfo, ins);
|
||||
callVM(SetDenseOrUnboxedArrayElementInfo, ins);
|
||||
|
||||
restoreLive(ins);
|
||||
masm.jump(ool->rejoin());
|
||||
@ -8580,13 +8706,27 @@ 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);
|
||||
NativeObject::elementsSizeMustNotOverflow();
|
||||
masm.loadValue(Address(elements, ToInt32(lir->index()) * sizeof(Value)), out);
|
||||
} else {
|
||||
else
|
||||
masm.branch32(Assembler::BelowOrEqual, initLength, ToRegister(lir->index()), &undefined);
|
||||
masm.loadValue(BaseObjectElementIndex(elements, ToRegister(lir->index())), out);
|
||||
|
||||
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);
|
||||
}
|
||||
} 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);
|
||||
}
|
||||
}
|
||||
|
||||
// If a hole check is needed, and the value wasn't a hole, we're done.
|
||||
|
@ -188,6 +188,9 @@ 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);
|
||||
|
@ -6477,37 +6477,29 @@ bool
|
||||
IonBuilder::jsop_newarray(uint32_t count)
|
||||
{
|
||||
JSObject* templateObject = inspector->getTemplateObject(pc);
|
||||
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");
|
||||
}
|
||||
gc::InitialHeap heap;
|
||||
MConstant* templateConst;
|
||||
|
||||
MOZ_ASSERT(templateObject->is<ArrayObject>());
|
||||
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");
|
||||
if (templateObject) {
|
||||
heap = templateObject->group()->initialHeap(constraints());
|
||||
templateConst = MConstant::NewConstraintlessObject(alloc(), templateObject);
|
||||
} else {
|
||||
heap = gc::DefaultHeap;
|
||||
templateConst = MConstant::New(alloc(), NullValue());
|
||||
}
|
||||
|
||||
MConstant* templateConst = MConstant::NewConstraintlessObject(alloc(), templateObject);
|
||||
current->add(templateConst);
|
||||
|
||||
MNewArray* ins = MNewArray::New(alloc(), constraints(), count, templateConst,
|
||||
templateObject->group()->initialHeap(constraints()),
|
||||
NewArray_FullyAllocating);
|
||||
heap, NewArray_FullyAllocating, pc);
|
||||
current->add(ins);
|
||||
current->push(ins);
|
||||
|
||||
ObjectGroup* templateGroup = inspector->getTemplateObjectGroup(pc);
|
||||
if (templateGroup) {
|
||||
TemporaryTypeSet* types = MakeSingletonTypeSet(constraints(), templateGroup);
|
||||
ins->setResultTypeSet(types);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -6581,10 +6573,23 @@ IonBuilder::jsop_initelem_array()
|
||||
// intializer, and that arrays are marked as non-packed when writing holes
|
||||
// to them during initialization.
|
||||
bool needStub = false;
|
||||
if (obj->isUnknownValue() || shouldAbortOnPreliminaryGroups(obj)) {
|
||||
JSValueType unboxedType = JSVAL_TYPE_MAGIC;
|
||||
if (shouldAbortOnPreliminaryGroups(obj)) {
|
||||
needStub = true;
|
||||
} else if (!obj->resultTypeSet() ||
|
||||
obj->resultTypeSet()->unknownObject() ||
|
||||
obj->resultTypeSet()->getObjectCount() != 1)
|
||||
{
|
||||
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;
|
||||
@ -6597,9 +6602,6 @@ 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);
|
||||
@ -6610,29 +6612,44 @@ IonBuilder::jsop_initelem_array()
|
||||
current->add(id);
|
||||
|
||||
// Get the elements vector.
|
||||
MElements* elements = MElements::New(alloc(), obj);
|
||||
MElements* elements = MElements::New(alloc(), obj, unboxedType != JSVAL_TYPE_MAGIC);
|
||||
current->add(elements);
|
||||
|
||||
if (obj->toNewArray()->convertDoubleElements()) {
|
||||
MInstruction* valueDouble = MToDouble::New(alloc(), value);
|
||||
current->add(valueDouble);
|
||||
value = valueDouble;
|
||||
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;
|
||||
}
|
||||
|
||||
// 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;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
@ -8115,9 +8132,12 @@ IonBuilder::getElemTryDense(bool* emitted, MDefinition* obj, MDefinition* index)
|
||||
{
|
||||
MOZ_ASSERT(*emitted == false);
|
||||
|
||||
if (!ElementAccessIsDenseNative(constraints(), obj, index)) {
|
||||
trackOptimizationOutcome(TrackedOutcome::AccessNotDense);
|
||||
return true;
|
||||
JSValueType unboxedType = UnboxedArrayElementType(constraints(), obj, index);
|
||||
if (unboxedType == JSVAL_TYPE_MAGIC) {
|
||||
if (!ElementAccessIsDenseNative(constraints(), obj, index)) {
|
||||
trackOptimizationOutcome(TrackedOutcome::AccessNotDense);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Don't generate a fast path if there have been bounds check failures
|
||||
@ -8134,8 +8154,7 @@ IonBuilder::getElemTryDense(bool* emitted, MDefinition* obj, MDefinition* index)
|
||||
return true;
|
||||
}
|
||||
|
||||
// Emit dense getelem variant.
|
||||
if (!jsop_getelem_dense(obj, index))
|
||||
if (!jsop_getelem_dense(obj, index, unboxedType))
|
||||
return false;
|
||||
|
||||
trackOptimizationSuccess();
|
||||
@ -8447,7 +8466,7 @@ IonBuilder::getElemTryCache(bool* emitted, MDefinition* obj, MDefinition* index)
|
||||
}
|
||||
|
||||
bool
|
||||
IonBuilder::jsop_getelem_dense(MDefinition* obj, MDefinition* index)
|
||||
IonBuilder::jsop_getelem_dense(MDefinition* obj, MDefinition* index, JSValueType unboxedType)
|
||||
{
|
||||
TemporaryTypeSet* types = bytecodeTypes(pc);
|
||||
|
||||
@ -8471,7 +8490,7 @@ IonBuilder::jsop_getelem_dense(MDefinition* obj, MDefinition* index)
|
||||
!ElementAccessHasExtraIndexedProperty(constraints(), obj);
|
||||
|
||||
MIRType knownType = MIRType_Value;
|
||||
if (barrier == BarrierKind::NoBarrier)
|
||||
if (unboxedType == JSVAL_TYPE_MAGIC && barrier == BarrierKind::NoBarrier)
|
||||
knownType = GetElemKnownType(needsHoleCheck, types);
|
||||
|
||||
// Ensure index is an integer.
|
||||
@ -8480,19 +8499,24 @@ IonBuilder::jsop_getelem_dense(MDefinition* obj, MDefinition* index)
|
||||
index = idInt32;
|
||||
|
||||
// Get the elements vector.
|
||||
MInstruction* elements = MElements::New(alloc(), obj);
|
||||
MInstruction* elements = MElements::New(alloc(), obj, unboxedType != JSVAL_TYPE_MAGIC);
|
||||
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.
|
||||
MInitializedLength* initLength = MInitializedLength::New(alloc(), elements);
|
||||
MInstruction* initLength;
|
||||
if (unboxedType != JSVAL_TYPE_MAGIC)
|
||||
initLength = MUnboxedArrayInitializedLength::New(alloc(), obj);
|
||||
else
|
||||
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 &&
|
||||
@ -8512,13 +8536,18 @@ IonBuilder::jsop_getelem_dense(MDefinition* obj, MDefinition* index)
|
||||
// hoisting.
|
||||
index = addBoundsCheck(index, initLength);
|
||||
|
||||
load = MLoadElement::New(alloc(), elements, index, needsHoleCheck, loadDouble);
|
||||
current->add(load);
|
||||
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);
|
||||
}
|
||||
} 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, needsHoleCheck);
|
||||
load = MLoadElementHole::New(alloc(), elements, index, initLength,
|
||||
unboxedType, needsHoleCheck);
|
||||
current->add(load);
|
||||
|
||||
// If maybeUndefined was true, the typeset must have undefined, and
|
||||
@ -8976,9 +9005,12 @@ IonBuilder::setElemTryDense(bool* emitted, MDefinition* object,
|
||||
{
|
||||
MOZ_ASSERT(*emitted == false);
|
||||
|
||||
if (!ElementAccessIsDenseNative(constraints(), object, index)) {
|
||||
trackOptimizationOutcome(TrackedOutcome::AccessNotDense);
|
||||
return true;
|
||||
JSValueType unboxedType = UnboxedArrayElementType(constraints(), object, index);
|
||||
if (unboxedType == JSVAL_TYPE_MAGIC) {
|
||||
if (!ElementAccessIsDenseNative(constraints(), object, index)) {
|
||||
trackOptimizationOutcome(TrackedOutcome::AccessNotDense);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (PropertyWriteNeedsTypeBarrier(alloc(), constraints(), current,
|
||||
@ -9012,7 +9044,7 @@ IonBuilder::setElemTryDense(bool* emitted, MDefinition* object,
|
||||
}
|
||||
|
||||
// Emit dense setelem variant.
|
||||
if (!jsop_setelem_dense(conversion, SetElem_Normal, object, index, value))
|
||||
if (!jsop_setelem_dense(conversion, SetElem_Normal, object, index, value, unboxedType))
|
||||
return false;
|
||||
|
||||
trackOptimizationSuccess();
|
||||
@ -9097,9 +9129,12 @@ IonBuilder::setElemTryCache(bool* emitted, MDefinition* object,
|
||||
bool
|
||||
IonBuilder::jsop_setelem_dense(TemporaryTypeSet::DoubleConversion conversion,
|
||||
SetElemSafety safety,
|
||||
MDefinition* obj, MDefinition* id, MDefinition* value)
|
||||
MDefinition* obj, MDefinition* id, MDefinition* value,
|
||||
JSValueType unboxedType)
|
||||
{
|
||||
MIRType elementType = DenseNativeElementType(constraints(), obj);
|
||||
MIRType elementType = MIRType_None;
|
||||
if (unboxedType == JSVAL_TYPE_MAGIC)
|
||||
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
|
||||
@ -9118,7 +9153,7 @@ IonBuilder::jsop_setelem_dense(TemporaryTypeSet::DoubleConversion conversion,
|
||||
obj = addMaybeCopyElementsForWrite(obj);
|
||||
|
||||
// Get the elements vector.
|
||||
MElements* elements = MElements::New(alloc(), obj);
|
||||
MElements* elements = MElements::New(alloc(), obj, unboxedType != JSVAL_TYPE_MAGIC);
|
||||
current->add(elements);
|
||||
|
||||
// Ensure the value is a double, if double conversion might be needed.
|
||||
@ -9156,20 +9191,23 @@ 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.
|
||||
MStoreElementCommon* store;
|
||||
MInstruction* store;
|
||||
MStoreElementCommon *common = nullptr;
|
||||
if (writeHole && writeOutOfBounds) {
|
||||
MOZ_ASSERT(safety == SetElem_Normal);
|
||||
|
||||
MStoreElementHole* ins = MStoreElementHole::New(alloc(), obj, elements, id, newValue);
|
||||
MStoreElementHole* ins = MStoreElementHole::New(alloc(), obj, elements, id, newValue, unboxedType);
|
||||
store = ins;
|
||||
common = ins;
|
||||
|
||||
current->add(ins);
|
||||
current->push(value);
|
||||
|
||||
if (!resumeAfter(ins))
|
||||
return false;
|
||||
} else {
|
||||
MInitializedLength* initLength = MInitializedLength::New(alloc(), elements);
|
||||
MInstruction* initLength;
|
||||
if (unboxedType != JSVAL_TYPE_MAGIC)
|
||||
initLength = MUnboxedArrayInitializedLength::New(alloc(), obj);
|
||||
else
|
||||
initLength = MInitializedLength::New(alloc(), elements);
|
||||
current->add(initLength);
|
||||
|
||||
bool needsHoleCheck;
|
||||
@ -9180,24 +9218,31 @@ IonBuilder::jsop_setelem_dense(TemporaryTypeSet::DoubleConversion conversion,
|
||||
needsHoleCheck = false;
|
||||
}
|
||||
|
||||
MStoreElement* ins = MStoreElement::New(alloc(), elements, id, newValue, needsHoleCheck);
|
||||
store = ins;
|
||||
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;
|
||||
|
||||
current->add(ins);
|
||||
current->add(store);
|
||||
}
|
||||
|
||||
if (safety == SetElem_Normal)
|
||||
current->push(value);
|
||||
|
||||
if (!resumeAfter(ins))
|
||||
return false;
|
||||
}
|
||||
|
||||
// Determine whether a write barrier is required.
|
||||
if (obj->resultTypeSet()->propertyNeedsBarrier(constraints(), JSID_VOID))
|
||||
store->setNeedsBarrier();
|
||||
if (!resumeAfter(store))
|
||||
return false;
|
||||
|
||||
if (elementType != MIRType_None && packed)
|
||||
store->setElementType(elementType);
|
||||
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);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -9323,6 +9368,16 @@ 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()) {
|
||||
@ -9390,7 +9445,7 @@ IonBuilder::jsop_rest()
|
||||
|
||||
MNewArray* array = MNewArray::New(alloc(), constraints(), numRest, templateConst,
|
||||
templateObject->group()->initialHeap(constraints()),
|
||||
NewArray_FullyAllocating);
|
||||
NewArray_FullyAllocating, pc);
|
||||
current->add(array);
|
||||
|
||||
if (numRest == 0) {
|
||||
@ -10516,33 +10571,39 @@ 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(), obj, scaledOffset, Scalar::Uint8,
|
||||
DoesNotRequireMemoryBarrier,
|
||||
UnboxedPlainObject::offsetOfData());
|
||||
load = MLoadUnboxedScalar::New(alloc(), elements, scaledOffset, Scalar::Uint8,
|
||||
DoesNotRequireMemoryBarrier, elementsOffset);
|
||||
load->setResultType(MIRType_Boolean);
|
||||
break;
|
||||
|
||||
case JSVAL_TYPE_INT32:
|
||||
load = MLoadUnboxedScalar::New(alloc(), obj, scaledOffset, Scalar::Int32,
|
||||
DoesNotRequireMemoryBarrier,
|
||||
UnboxedPlainObject::offsetOfData());
|
||||
load = MLoadUnboxedScalar::New(alloc(), elements, scaledOffset, Scalar::Int32,
|
||||
DoesNotRequireMemoryBarrier, elementsOffset);
|
||||
load->setResultType(MIRType_Int32);
|
||||
break;
|
||||
|
||||
case JSVAL_TYPE_DOUBLE:
|
||||
load = MLoadUnboxedScalar::New(alloc(), obj, scaledOffset, Scalar::Float64,
|
||||
DoesNotRequireMemoryBarrier,
|
||||
UnboxedPlainObject::offsetOfData(),
|
||||
load = MLoadUnboxedScalar::New(alloc(), elements, scaledOffset, Scalar::Float64,
|
||||
DoesNotRequireMemoryBarrier, elementsOffset,
|
||||
/* canonicalizeDoubles = */ false);
|
||||
load->setResultType(MIRType_Double);
|
||||
break;
|
||||
|
||||
case JSVAL_TYPE_STRING:
|
||||
load = MLoadUnboxedString::New(alloc(), obj, scaledOffset,
|
||||
UnboxedPlainObject::offsetOfData());
|
||||
load = MLoadUnboxedString::New(alloc(), elements, scaledOffset, elementsOffset);
|
||||
break;
|
||||
|
||||
case JSVAL_TYPE_OBJECT: {
|
||||
@ -10551,8 +10612,8 @@ IonBuilder::loadUnboxedProperty(MDefinition* obj, size_t offset, JSValueType unb
|
||||
nullBehavior = MLoadUnboxedObjectOrNull::HandleNull;
|
||||
else
|
||||
nullBehavior = MLoadUnboxedObjectOrNull::NullNotPossible;
|
||||
load = MLoadUnboxedObjectOrNull::New(alloc(), obj, scaledOffset, nullBehavior,
|
||||
UnboxedPlainObject::offsetOfData());
|
||||
load = MLoadUnboxedObjectOrNull::New(alloc(), elements, scaledOffset, nullBehavior,
|
||||
elementsOffset);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -11435,34 +11496,39 @@ 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(), obj, scaledOffset, value, Scalar::Uint8,
|
||||
DoesNotRequireMemoryBarrier,
|
||||
UnboxedPlainObject::offsetOfData());
|
||||
store = MStoreUnboxedScalar::New(alloc(), elements, scaledOffset, value, Scalar::Uint8,
|
||||
DoesNotRequireMemoryBarrier, elementsOffset);
|
||||
break;
|
||||
|
||||
case JSVAL_TYPE_INT32:
|
||||
store = MStoreUnboxedScalar::New(alloc(), obj, scaledOffset, value, Scalar::Int32,
|
||||
DoesNotRequireMemoryBarrier,
|
||||
UnboxedPlainObject::offsetOfData());
|
||||
store = MStoreUnboxedScalar::New(alloc(), elements, scaledOffset, value, Scalar::Int32,
|
||||
DoesNotRequireMemoryBarrier, elementsOffset);
|
||||
break;
|
||||
|
||||
case JSVAL_TYPE_DOUBLE:
|
||||
store = MStoreUnboxedScalar::New(alloc(), obj, scaledOffset, value, Scalar::Float64,
|
||||
DoesNotRequireMemoryBarrier,
|
||||
UnboxedPlainObject::offsetOfData());
|
||||
store = MStoreUnboxedScalar::New(alloc(), elements, scaledOffset, value, Scalar::Float64,
|
||||
DoesNotRequireMemoryBarrier, elementsOffset);
|
||||
break;
|
||||
|
||||
case JSVAL_TYPE_STRING:
|
||||
store = MStoreUnboxedString::New(alloc(), obj, scaledOffset, value,
|
||||
UnboxedPlainObject::offsetOfData());
|
||||
store = MStoreUnboxedString::New(alloc(), elements, scaledOffset, value, elementsOffset);
|
||||
break;
|
||||
|
||||
case JSVAL_TYPE_OBJECT:
|
||||
store = MStoreUnboxedObjectOrNull::New(alloc(), obj, scaledOffset, value, obj,
|
||||
UnboxedPlainObject::offsetOfData());
|
||||
store = MStoreUnboxedObjectOrNull::New(alloc(), elements, scaledOffset, value, obj,
|
||||
elementsOffset);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
@ -656,12 +656,13 @@ class IonBuilder
|
||||
bool jsop_intrinsic(PropertyName* name);
|
||||
bool jsop_bindname(PropertyName* name);
|
||||
bool jsop_getelem();
|
||||
bool jsop_getelem_dense(MDefinition* obj, MDefinition* index);
|
||||
bool jsop_getelem_dense(MDefinition* obj, MDefinition* index, JSValueType unboxedType);
|
||||
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);
|
||||
MDefinition* object, MDefinition* index, MDefinition* value,
|
||||
JSValueType unboxedType);
|
||||
bool jsop_setelem_typed(ScalarTypeDescr::Type arrayType,
|
||||
SetElemSafety safety,
|
||||
MDefinition* object, MDefinition* index, MDefinition* value);
|
||||
@ -950,8 +951,16 @@ 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);
|
||||
|
@ -4084,6 +4084,10 @@ 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.
|
||||
@ -4181,6 +4185,48 @@ 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>
|
||||
{
|
||||
@ -4647,16 +4693,17 @@ class LStoreElementT : public LInstructionHelper<0, 3, 0>
|
||||
};
|
||||
|
||||
// Like LStoreElementV, but supports indexes >= initialized length.
|
||||
class LStoreElementHoleV : public LInstructionHelper<0, 3 + BOX_PIECES, 0>
|
||||
class LStoreElementHoleV : public LInstructionHelper<0, 3 + BOX_PIECES, 1>
|
||||
{
|
||||
public:
|
||||
LIR_HEADER(StoreElementHoleV)
|
||||
|
||||
LStoreElementHoleV(const LAllocation& object, const LAllocation& elements,
|
||||
const LAllocation& index) {
|
||||
const LAllocation& index, const LDefinition& temp) {
|
||||
setOperand(0, object);
|
||||
setOperand(1, elements);
|
||||
setOperand(2, index);
|
||||
setTemp(0, temp);
|
||||
}
|
||||
|
||||
static const size_t Value = 3;
|
||||
@ -4676,17 +4723,19 @@ class LStoreElementHoleV : public LInstructionHelper<0, 3 + BOX_PIECES, 0>
|
||||
};
|
||||
|
||||
// Like LStoreElementT, but supports indexes >= initialized length.
|
||||
class LStoreElementHoleT : public LInstructionHelper<0, 4, 0>
|
||||
class LStoreElementHoleT : public LInstructionHelper<0, 4, 1>
|
||||
{
|
||||
public:
|
||||
LIR_HEADER(StoreElementHoleT)
|
||||
|
||||
LStoreElementHoleT(const LAllocation& object, const LAllocation& elements,
|
||||
const LAllocation& index, const LAllocation& value) {
|
||||
const LAllocation& index, const LAllocation& value,
|
||||
const LDefinition& temp) {
|
||||
setOperand(0, object);
|
||||
setOperand(1, elements);
|
||||
setOperand(2, index);
|
||||
setOperand(3, value);
|
||||
setTemp(0, temp);
|
||||
}
|
||||
|
||||
const MStoreElementHole* mir() const {
|
||||
|
@ -214,6 +214,9 @@
|
||||
_(PostWriteBarrierV) \
|
||||
_(InitializedLength) \
|
||||
_(SetInitializedLength) \
|
||||
_(UnboxedArrayLength) \
|
||||
_(UnboxedArrayInitializedLength) \
|
||||
_(IncrementUnboxedArrayInitializedLength) \
|
||||
_(BoundsCheck) \
|
||||
_(BoundsCheckRange) \
|
||||
_(BoundsCheckLower) \
|
||||
|
@ -2495,6 +2495,24 @@ 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)
|
||||
{
|
||||
@ -2737,17 +2755,22 @@ 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);
|
||||
lir = new(alloc()) LStoreElementHoleV(object, elements, index, tempDef);
|
||||
useBox(lir, LStoreElementHoleV::Value, ins->value());
|
||||
break;
|
||||
|
||||
default:
|
||||
{
|
||||
const LAllocation value = useRegisterOrNonDoubleConstant(ins->value());
|
||||
lir = new(alloc()) LStoreElementHoleT(object, elements, index, value);
|
||||
lir = new(alloc()) LStoreElementHoleT(object, elements, index, value, tempDef);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -187,6 +187,9 @@ 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);
|
||||
|
@ -597,7 +597,7 @@ IonBuilder::inlineArray(CallInfo& callInfo)
|
||||
|
||||
MNewArray* ins = MNewArray::New(alloc(), constraints(), initLength, templateConst,
|
||||
templateArray->group()->initialHeap(constraints()),
|
||||
allocating);
|
||||
allocating, pc);
|
||||
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);
|
||||
NewArray_FullyAllocating, pc);
|
||||
|
||||
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))
|
||||
if (!jsop_setelem_dense(conversion, SetElem_Unsafe, obj, id, elem, JSVAL_TYPE_MAGIC))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
@ -667,19 +667,30 @@ MConstant::NewConstraintlessObject(TempAllocator& alloc, JSObject* v)
|
||||
return new(alloc) MConstant(v);
|
||||
}
|
||||
|
||||
TemporaryTypeSet*
|
||||
jit::MakeSingletonTypeSet(CompilerConstraintList* constraints, JSObject* obj)
|
||||
static TemporaryTypeSet*
|
||||
MakeSingletonTypeSetFromKey(CompilerConstraintList* constraints, TypeSet::ObjectKey* key)
|
||||
{
|
||||
// Invalidate when this object's ObjectGroup gets unknown properties. This
|
||||
// happens for instance when we mutate an object's __proto__, in this case
|
||||
// we want to invalidate and mark this TypeSet as containing AnyObject
|
||||
// (because mutating __proto__ will change an object's ObjectGroup).
|
||||
MOZ_ASSERT(constraints);
|
||||
TypeSet::ObjectKey* key = TypeSet::ObjectKey::get(obj);
|
||||
key->hasStableClassAndProto(constraints);
|
||||
|
||||
LifoAlloc* alloc = GetJitContext()->temp->lifoAlloc();
|
||||
return alloc->new_<TemporaryTypeSet>(alloc, TypeSet::ObjectType(obj));
|
||||
return alloc->new_<TemporaryTypeSet>(alloc, TypeSet::ObjectType(key));
|
||||
}
|
||||
|
||||
TemporaryTypeSet*
|
||||
jit::MakeSingletonTypeSet(CompilerConstraintList* constraints, JSObject* obj)
|
||||
{
|
||||
return MakeSingletonTypeSetFromKey(constraints, TypeSet::ObjectKey::get(obj));
|
||||
}
|
||||
|
||||
TemporaryTypeSet*
|
||||
jit::MakeSingletonTypeSet(CompilerConstraintList* constraints, ObjectGroup* obj)
|
||||
{
|
||||
return MakeSingletonTypeSetFromKey(constraints, TypeSet::ObjectKey::get(obj));
|
||||
}
|
||||
|
||||
static TemporaryTypeSet*
|
||||
@ -4018,17 +4029,17 @@ MArrayState::Copy(TempAllocator& alloc, MArrayState* state)
|
||||
}
|
||||
|
||||
MNewArray::MNewArray(CompilerConstraintList* constraints, uint32_t count, MConstant* templateConst,
|
||||
gc::InitialHeap initialHeap, AllocatingBehaviour allocating)
|
||||
gc::InitialHeap initialHeap, AllocatingBehaviour allocating, jsbytecode* pc)
|
||||
: MUnaryInstruction(templateConst),
|
||||
count_(count),
|
||||
initialHeap_(initialHeap),
|
||||
allocating_(allocating),
|
||||
convertDoubleElements_(false)
|
||||
convertDoubleElements_(false),
|
||||
pc_(pc)
|
||||
{
|
||||
ArrayObject* obj = templateObject();
|
||||
setResultType(MIRType_Object);
|
||||
if (!obj->isSingleton()) {
|
||||
TemporaryTypeSet* types = MakeSingletonTypeSet(constraints, obj);
|
||||
if (templateObject()) {
|
||||
TemporaryTypeSet* types = MakeSingletonTypeSet(constraints, templateObject());
|
||||
setResultTypeSet(types);
|
||||
if (types->convertDoubleElements(constraints) == TemporaryTypeSet::AlwaysConvertToDoubles)
|
||||
convertDoubleElements_ = true;
|
||||
@ -4038,16 +4049,25 @@ 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<UnboxedArrayObject>()) {
|
||||
MOZ_ASSERT(templateObject()->as<UnboxedArrayObject>().capacity() >= count());
|
||||
return !templateObject()->as<UnboxedArrayObject>().hasInlineElements();
|
||||
}
|
||||
|
||||
MOZ_ASSERT(count() < NativeObject::NELEMENTS_LIMIT);
|
||||
|
||||
size_t arraySlots =
|
||||
gc::GetGCKindSlots(templateObject()->asTenured().getAllocKind()) - ObjectElements::VALUES_PER_HEADER;
|
||||
|
||||
// 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;
|
||||
return count() > arraySlots;
|
||||
}
|
||||
|
||||
bool
|
||||
@ -4696,6 +4716,48 @@ 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,
|
||||
|
185
js/src/jit/MIR.h
185
js/src/jit/MIR.h
@ -2853,6 +2853,9 @@ class MThrow
|
||||
TemporaryTypeSet*
|
||||
MakeSingletonTypeSet(CompilerConstraintList* constraints, JSObject* obj);
|
||||
|
||||
TemporaryTypeSet*
|
||||
MakeSingletonTypeSet(CompilerConstraintList* constraints, ObjectGroup* obj);
|
||||
|
||||
bool
|
||||
MergeTypes(MIRType* ptype, TemporaryTypeSet** ptypeSet,
|
||||
MIRType newType, TemporaryTypeSet* newTypeSet);
|
||||
@ -2917,25 +2920,29 @@ 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);
|
||||
gc::InitialHeap initialHeap, AllocatingBehaviour allocating, jsbytecode* pc);
|
||||
|
||||
public:
|
||||
INSTRUCTION_HEADER(NewArray)
|
||||
|
||||
static MNewArray* New(TempAllocator& alloc, CompilerConstraintList* constraints,
|
||||
uint32_t count, MConstant* templateConst,
|
||||
gc::InitialHeap initialHeap, AllocatingBehaviour allocating)
|
||||
gc::InitialHeap initialHeap, AllocatingBehaviour allocating,
|
||||
jsbytecode* pc)
|
||||
{
|
||||
return new(alloc) MNewArray(constraints, count, templateConst, initialHeap, allocating);
|
||||
return new(alloc) MNewArray(constraints, count, templateConst,
|
||||
initialHeap, allocating, pc);
|
||||
}
|
||||
|
||||
uint32_t count() const {
|
||||
return count_;
|
||||
}
|
||||
|
||||
ArrayObject* templateObject() const {
|
||||
return &getOperand(0)->toConstant()->value().toObject().as<ArrayObject>();
|
||||
JSObject* templateObject() const {
|
||||
return getOperand(0)->toConstant()->value().toObjectOrNull();
|
||||
}
|
||||
|
||||
gc::InitialHeap initialHeap() const {
|
||||
@ -2946,6 +2953,10 @@ class MNewArray
|
||||
return allocating_;
|
||||
}
|
||||
|
||||
jsbytecode* pc() const {
|
||||
return pc_;
|
||||
}
|
||||
|
||||
bool convertDoubleElements() const {
|
||||
return convertDoubleElements_;
|
||||
}
|
||||
@ -2968,7 +2979,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 true;
|
||||
return templateObject() != nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
@ -7591,8 +7602,10 @@ class MElements
|
||||
: public MUnaryInstruction,
|
||||
public SingleObjectPolicy::Data
|
||||
{
|
||||
explicit MElements(MDefinition* object)
|
||||
: MUnaryInstruction(object)
|
||||
bool unboxed_;
|
||||
|
||||
explicit MElements(MDefinition* object, bool unboxed)
|
||||
: MUnaryInstruction(object), unboxed_(unboxed)
|
||||
{
|
||||
setResultType(MIRType_Elements);
|
||||
setMovable();
|
||||
@ -7601,15 +7614,19 @@ class MElements
|
||||
public:
|
||||
INSTRUCTION_HEADER(Elements)
|
||||
|
||||
static MElements* New(TempAllocator& alloc, MDefinition* object) {
|
||||
return new(alloc) MElements(object);
|
||||
static MElements* New(TempAllocator& alloc, MDefinition* object, bool unboxed = false) {
|
||||
return new(alloc) MElements(object, unboxed);
|
||||
}
|
||||
|
||||
MDefinition* object() const {
|
||||
return getOperand(0);
|
||||
}
|
||||
bool unboxed() const {
|
||||
return unboxed_;
|
||||
}
|
||||
bool congruentTo(const MDefinition* ins) const override {
|
||||
return congruentIfOperandsEqual(ins);
|
||||
return congruentIfOperandsEqual(ins) &&
|
||||
ins->toElements()->unboxed() == unboxed();
|
||||
}
|
||||
AliasSet getAliasSet() const override {
|
||||
return AliasSet::Load(AliasSet::ObjectFields);
|
||||
@ -7840,6 +7857,96 @@ 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,
|
||||
@ -8359,18 +8466,23 @@ class MLoadElement
|
||||
ALLOW_CLONE(MLoadElement)
|
||||
};
|
||||
|
||||
// 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.
|
||||
// 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.
|
||||
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, bool needsHoleCheck)
|
||||
MLoadElementHole(MDefinition* elements, MDefinition* index, MDefinition* initLength,
|
||||
JSValueType unboxedType, bool needsHoleCheck)
|
||||
: MTernaryInstruction(elements, index, initLength),
|
||||
unboxedType_(unboxedType),
|
||||
needsNegativeIntCheck_(true),
|
||||
needsHoleCheck_(needsHoleCheck)
|
||||
{
|
||||
@ -8391,8 +8503,10 @@ class MLoadElementHole
|
||||
INSTRUCTION_HEADER(LoadElementHole)
|
||||
|
||||
static MLoadElementHole* New(TempAllocator& alloc, MDefinition* elements, MDefinition* index,
|
||||
MDefinition* initLength, bool needsHoleCheck) {
|
||||
return new(alloc) MLoadElementHole(elements, index, initLength, needsHoleCheck);
|
||||
MDefinition* initLength, JSValueType unboxedType,
|
||||
bool needsHoleCheck) {
|
||||
return new(alloc) MLoadElementHole(elements, index, initLength,
|
||||
unboxedType, needsHoleCheck);
|
||||
}
|
||||
|
||||
MDefinition* elements() const {
|
||||
@ -8404,6 +8518,9 @@ class MLoadElementHole
|
||||
MDefinition* initLength() const {
|
||||
return getOperand(2);
|
||||
}
|
||||
JSValueType unboxedType() const {
|
||||
return unboxedType_;
|
||||
}
|
||||
bool needsNegativeIntCheck() const {
|
||||
return needsNegativeIntCheck_;
|
||||
}
|
||||
@ -8414,6 +8531,8 @@ 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())
|
||||
@ -8421,7 +8540,9 @@ class MLoadElementHole
|
||||
return congruentIfOperandsEqual(other);
|
||||
}
|
||||
AliasSet getAliasSet() const override {
|
||||
return AliasSet::Load(AliasSet::Element);
|
||||
return AliasSet::Load(unboxedType() == JSVAL_TYPE_MAGIC
|
||||
? AliasSet::Element
|
||||
: AliasSet::UnboxedElement);
|
||||
}
|
||||
void collectRangeInfoPreTrunc() override;
|
||||
|
||||
@ -8631,17 +8752,21 @@ class MStoreElement
|
||||
ALLOW_CLONE(MStoreElement)
|
||||
};
|
||||
|
||||
// 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.
|
||||
// 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.
|
||||
class MStoreElementHole
|
||||
: public MAryInstruction<4>,
|
||||
public MStoreElementCommon,
|
||||
public MixPolicy<SingleObjectPolicy, NoFloatPolicy<3> >::Data
|
||||
{
|
||||
JSValueType unboxedType_;
|
||||
|
||||
MStoreElementHole(MDefinition* object, MDefinition* elements,
|
||||
MDefinition* index, MDefinition* value) {
|
||||
MDefinition* index, MDefinition* value, JSValueType unboxedType)
|
||||
: unboxedType_(unboxedType)
|
||||
{
|
||||
initOperand(0, object);
|
||||
initOperand(1, elements);
|
||||
initOperand(2, index);
|
||||
@ -8654,8 +8779,8 @@ class MStoreElementHole
|
||||
INSTRUCTION_HEADER(StoreElementHole)
|
||||
|
||||
static MStoreElementHole* New(TempAllocator& alloc, MDefinition* object, MDefinition* elements,
|
||||
MDefinition* index, MDefinition* value) {
|
||||
return new(alloc) MStoreElementHole(object, elements, index, value);
|
||||
MDefinition* index, MDefinition* value, JSValueType unboxedType) {
|
||||
return new(alloc) MStoreElementHole(object, elements, index, value, unboxedType);
|
||||
}
|
||||
|
||||
MDefinition* object() const {
|
||||
@ -8670,10 +8795,16 @@ 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::Element | AliasSet::ObjectFields);
|
||||
return AliasSet::Store(AliasSet::ObjectFields |
|
||||
((unboxedType() == JSVAL_TYPE_MAGIC)
|
||||
? AliasSet::Element
|
||||
: AliasSet::UnboxedElement));
|
||||
}
|
||||
|
||||
ALLOW_CLONE(MStoreElementHole)
|
||||
@ -13220,6 +13351,8 @@ 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);
|
||||
|
@ -178,6 +178,9 @@ namespace jit {
|
||||
_(SetTypedObjectOffset) \
|
||||
_(InitializedLength) \
|
||||
_(SetInitializedLength) \
|
||||
_(UnboxedArrayLength) \
|
||||
_(UnboxedArrayInitializedLength) \
|
||||
_(IncrementUnboxedArrayInitializedLength) \
|
||||
_(Not) \
|
||||
_(BoundsCheck) \
|
||||
_(BoundsCheckLower) \
|
||||
|
@ -1016,6 +1016,31 @@ 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
|
||||
@ -1433,6 +1458,16 @@ MacroAssembler::initGCThing(Register obj, Register temp, JSObject* templateObj,
|
||||
storePtr(ImmWord(0), Address(obj, UnboxedPlainObject::offsetOfExpando()));
|
||||
if (initContents)
|
||||
initUnboxedObjectContents(obj, &templateObj->as<UnboxedPlainObject>());
|
||||
} else if (templateObj->is<UnboxedArrayObject>()) {
|
||||
MOZ_ASSERT(templateObj->as<UnboxedArrayObject>().hasInlineElements());
|
||||
int elementsOffset = UnboxedArrayObject::offsetOfInlineElements();
|
||||
computeEffectiveAddress(Address(obj, elementsOffset), temp);
|
||||
storePtr(temp, Address(obj, UnboxedArrayObject::offsetOfElements()));
|
||||
store32(Imm32(templateObj->as<UnboxedArrayObject>().length()),
|
||||
Address(obj, UnboxedArrayObject::offsetOfLength()));
|
||||
uint32_t capacityIndex = templateObj->as<UnboxedArrayObject>().capacityIndex();
|
||||
store32(Imm32(capacityIndex << UnboxedArrayObject::CapacityShift),
|
||||
Address(obj, UnboxedArrayObject::offsetOfCapacityIndexAndInitializedLength()));
|
||||
} else {
|
||||
MOZ_CRASH("Unknown object");
|
||||
}
|
||||
|
@ -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.
|
||||
// Load a property from an UnboxedPlainObject or UnboxedArrayObject.
|
||||
template <typename T>
|
||||
void loadUnboxedProperty(T address, JSValueType type, TypedOrValueRegister output);
|
||||
|
||||
@ -729,6 +729,9 @@ 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);
|
||||
}
|
||||
|
@ -1214,10 +1214,6 @@ 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;
|
||||
|
@ -612,6 +612,10 @@ IsArrayEscaped(MInstruction* ins)
|
||||
return true;
|
||||
}
|
||||
|
||||
JSObject* obj = ins->toNewArray()->templateObject();
|
||||
if (!obj || obj->is<UnboxedArrayObject>())
|
||||
return true;
|
||||
|
||||
if (count >= 16) {
|
||||
JitSpewDef(JitSpew_Escape, "Array has too many elements\n", ins);
|
||||
return true;
|
||||
|
@ -1109,22 +1109,22 @@ Recompile(JSContext* cx)
|
||||
}
|
||||
|
||||
bool
|
||||
SetDenseElement(JSContext* cx, HandleNativeObject obj, int32_t index, HandleValue value,
|
||||
bool strict)
|
||||
SetDenseOrUnboxedArrayElement(JSContext* cx, HandleObject 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 and we can use setDenseElement
|
||||
// instead of setDenseElementWithType.
|
||||
// In this case we know the object is native or an unboxed array and we can
|
||||
// use setDenseElement instead of setDenseElementWithType.
|
||||
|
||||
NativeObject::EnsureDenseResult result = NativeObject::ED_SPARSE;
|
||||
do {
|
||||
if (index < 0)
|
||||
if (index < 0 || obj->is<UnboxedArrayObject>())
|
||||
break;
|
||||
bool isArray = obj->is<ArrayObject>();
|
||||
if (isArray && !obj->as<ArrayObject>().lengthIsWritable())
|
||||
break;
|
||||
uint32_t idx = uint32_t(index);
|
||||
result = obj->ensureDenseElements(cx, idx, 1);
|
||||
result = obj->as<NativeObject>().ensureDenseElements(cx, idx, 1);
|
||||
if (result != NativeObject::ED_OK)
|
||||
break;
|
||||
if (isArray) {
|
||||
@ -1132,7 +1132,7 @@ SetDenseElement(JSContext* cx, HandleNativeObject obj, int32_t index, HandleValu
|
||||
if (idx >= arr.length())
|
||||
arr.setLengthInt32(idx + 1);
|
||||
}
|
||||
obj->setDenseElement(idx, value);
|
||||
obj->as<NativeObject>().setDenseElement(idx, value);
|
||||
return true;
|
||||
} while (false);
|
||||
|
||||
@ -1140,6 +1140,15 @@ SetDenseElement(JSContext* cx, HandleNativeObject obj, int32_t index, HandleValu
|
||||
return false;
|
||||
MOZ_ASSERT(result == NativeObject::ED_SPARSE);
|
||||
|
||||
if (index >= 0 && obj->is<UnboxedArrayObject>()) {
|
||||
UnboxedArrayObject* nobj = &obj->as<UnboxedArrayObject>();
|
||||
if (uint32_t(index) == nobj->initializedLength() &&
|
||||
uint32_t(index) < UnboxedArrayObject::MaximumCapacity)
|
||||
{
|
||||
return nobj->appendElementNoTypeChange(cx, index, value);
|
||||
}
|
||||
}
|
||||
|
||||
RootedValue indexVal(cx, Int32Value(index));
|
||||
return SetObjectElement(cx, obj, indexVal, value, strict);
|
||||
}
|
||||
|
@ -746,8 +746,8 @@ JSString* RegExpReplace(JSContext* cx, HandleString string, HandleObject regexp,
|
||||
JSString* StringReplace(JSContext* cx, HandleString string, HandleString pattern,
|
||||
HandleString repl);
|
||||
|
||||
bool SetDenseElement(JSContext* cx, HandleNativeObject obj, int32_t index, HandleValue value,
|
||||
bool strict);
|
||||
bool SetDenseOrUnboxedArrayElement(JSContext* cx, HandleObject obj, int32_t index,
|
||||
HandleValue value, bool strict);
|
||||
|
||||
void AssertValidObjectPtr(JSContext* cx, JSObject* obj);
|
||||
void AssertValidObjectOrNullPtr(JSContext* cx, JSObject* obj);
|
||||
|
@ -534,8 +534,13 @@ MacroAssemblerX64::branchValueIsNurseryObject(Condition cond, ValueOperand value
|
||||
{
|
||||
MOZ_ASSERT(cond == Assembler::Equal || cond == Assembler::NotEqual);
|
||||
|
||||
// 'Value' representing the start of the nursery tagged as a JSObject
|
||||
const Nursery& nursery = GetJitContext()->runtime->gcNursery();
|
||||
|
||||
// Avoid creating a bogus ObjectValue below.
|
||||
if (!nursery.exists())
|
||||
return;
|
||||
|
||||
// 'Value' representing the start of the nursery tagged as a JSObject
|
||||
Value start = ObjectValue(*reinterpret_cast<JSObject*>(nursery.start()));
|
||||
|
||||
movePtr(ImmWord(-ptrdiff_t(start.asRawBits())), ScratchReg);
|
||||
|
@ -1138,6 +1138,7 @@ class JS_PUBLIC_API(RuntimeOptions) {
|
||||
asmJS_(true),
|
||||
nativeRegExp_(true),
|
||||
unboxedObjects_(false), // Not enabled by default yet
|
||||
unboxedArrays_(false), // Ditto
|
||||
werror_(false),
|
||||
strictMode_(false),
|
||||
extraWarnings_(false),
|
||||
@ -1187,6 +1188,12 @@ 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;
|
||||
@ -1233,6 +1240,7 @@ 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;
|
||||
|
@ -2287,8 +2287,13 @@ TryReuseArrayGroup(JSObject* obj, ArrayObject* narr)
|
||||
*/
|
||||
MOZ_ASSERT(ObjectGroup::hasDefaultNewGroup(narr->getProto(), &ArrayObject::class_, narr->group()));
|
||||
|
||||
if (obj->is<ArrayObject>() && !obj->isSingleton() && obj->getProto() == narr->getProto())
|
||||
if (obj->is<ArrayObject>() &&
|
||||
!obj->isSingleton() &&
|
||||
!obj->group()->hasUnanalyzedPreliminaryObjects() &&
|
||||
obj->getProto() == narr->getProto())
|
||||
{
|
||||
narr->setGroup(obj->group());
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -331,6 +331,12 @@ 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
|
||||
|
@ -283,8 +283,20 @@ 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++) {
|
||||
if (!Enumerate(cx, pobj, properties[n], true, flags, ht, props))
|
||||
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<UnboxedArrayObject>() && id == NameToId(cx->names().length))
|
||||
enumerable = false;
|
||||
|
||||
if (!Enumerate(cx, pobj, id, enumerable, flags, ht, props))
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -29,7 +29,7 @@
|
||||
inline js::Shape*
|
||||
JSObject::maybeShape() const
|
||||
{
|
||||
if (is<js::UnboxedPlainObject>())
|
||||
if (is<js::UnboxedPlainObject>() || is<js::UnboxedArrayObject>())
|
||||
return nullptr;
|
||||
return *reinterpret_cast<js::Shape**>(uintptr_t(this) + offsetOfShape());
|
||||
}
|
||||
@ -268,6 +268,8 @@ 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
|
||||
@ -758,11 +760,11 @@ ObjectClassIs(HandleObject obj, ESClassValue classValue, JSContext* cx)
|
||||
return Proxy::objectClassIs(obj, classValue, cx);
|
||||
|
||||
switch (classValue) {
|
||||
case ESClass_Object: return obj->is<PlainObject>();
|
||||
case ESClass_Object: return obj->is<PlainObject>() || obj->is<UnboxedPlainObject>();
|
||||
case ESClass_Array:
|
||||
case ESClass_IsArray:
|
||||
// There difference between those is only relevant for proxies.
|
||||
return obj->is<ArrayObject>();
|
||||
return obj->is<ArrayObject>() || obj->is<UnboxedArrayObject>();
|
||||
case ESClass_Number: return obj->is<NumberObject>();
|
||||
case ESClass_String: return obj->is<StringObject>();
|
||||
case ESClass_Boolean: return obj->is<BooleanObject>();
|
||||
@ -789,7 +791,7 @@ IsObjectWithClass(const Value& v, ESClassValue classValue, JSContext* cx)
|
||||
inline bool
|
||||
IsArray(HandleObject obj, JSContext* cx)
|
||||
{
|
||||
if (obj->is<ArrayObject>())
|
||||
if (obj->is<ArrayObject>() || obj->is<UnboxedArrayObject>())
|
||||
return true;
|
||||
|
||||
return ObjectClassIs(obj, ESClass_IsArray, cx);
|
||||
|
@ -143,6 +143,7 @@ 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
|
||||
@ -5814,12 +5815,14 @@ 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);
|
||||
.setUnboxedObjects(enableUnboxedObjects)
|
||||
.setUnboxedArrays(enableUnboxedArrays);
|
||||
|
||||
if (const char* str = op.getStringOption("ion-scalar-replacement")) {
|
||||
if (strcmp(str, "on") == 0)
|
||||
@ -6024,7 +6027,8 @@ SetWorkerRuntimeOptions(JSRuntime* rt)
|
||||
.setIon(enableIon)
|
||||
.setAsmJS(enableAsmJS)
|
||||
.setNativeRegExp(enableNativeRegExp)
|
||||
.setUnboxedObjects(enableUnboxedObjects);
|
||||
.setUnboxedObjects(enableUnboxedObjects)
|
||||
.setUnboxedArrays(enableUnboxedArrays);
|
||||
rt->setOffthreadIonCompilationEnabled(offthreadCompilation);
|
||||
rt->profilingScripts = enableDisassemblyDumps;
|
||||
|
||||
@ -6166,7 +6170,8 @@ 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 objects")
|
||||
|| !op.addBoolOption('\0', "unboxed-objects", "Allow creating unboxed plain objects")
|
||||
|| !op.addBoolOption('\0', "unboxed-arrays", "Allow creating unboxed arrays")
|
||||
|| !op.addStringOption('\0', "ion-scalar-replacement", "on/off",
|
||||
"Scalar Replacement (default: on, off to disable)")
|
||||
|| !op.addStringOption('\0', "ion-gvn", "[mode]",
|
||||
|
@ -38,6 +38,8 @@ 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.
|
||||
|
@ -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<ArrayObject>());
|
||||
MOZ_ASSERT(obj->is<ArrayObject>() || obj->is<UnboxedArrayObject>());
|
||||
|
||||
/*
|
||||
* If val is a hole, do not call DefineElement.
|
||||
|
@ -1914,7 +1914,6 @@ 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)
|
||||
@ -3500,41 +3499,25 @@ CASE(JSOP_NEWINIT)
|
||||
{
|
||||
uint8_t i = GET_UINT8(REGS.pc);
|
||||
MOZ_ASSERT(i == JSProto_Array || i == JSProto_Object);
|
||||
RootedObject& obj = rootObject0;
|
||||
|
||||
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 {
|
||||
JSObject* obj;
|
||||
if (i == JSProto_Array)
|
||||
obj = NewArrayOperation(cx, script, REGS.pc, 0);
|
||||
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)
|
||||
{
|
||||
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))
|
||||
{
|
||||
JSObject* obj = NewArrayOperation(cx, script, REGS.pc, GET_UINT24(REGS.pc));
|
||||
if (!obj)
|
||||
goto error;
|
||||
}
|
||||
|
||||
PUSH_OBJECT(*obj);
|
||||
}
|
||||
END_CASE(JSOP_NEWARRAY)
|
||||
@ -3637,8 +3620,6 @@ CASE(JSOP_INITELEM_ARRAY)
|
||||
RootedObject& obj = rootObject0;
|
||||
obj = ®S.sp[-2].toObject();
|
||||
|
||||
MOZ_ASSERT(obj->is<ArrayObject>());
|
||||
|
||||
uint32_t index = GET_UINT24(REGS.pc);
|
||||
if (!InitArrayElemOperation(cx, REGS.pc, obj, index, val))
|
||||
goto error;
|
||||
@ -4703,10 +4684,7 @@ js::NewObjectOperationWithTemplate(JSContext* cx, HandleObject templateObject)
|
||||
|
||||
if (templateObject->group()->maybeUnboxedLayout()) {
|
||||
RootedObjectGroup group(cx, templateObject->group());
|
||||
JSObject* obj = UnboxedPlainObject::create(cx, group, newKind);
|
||||
if (!obj)
|
||||
return nullptr;
|
||||
return obj;
|
||||
return UnboxedPlainObject::create(cx, group, newKind);
|
||||
}
|
||||
|
||||
JSObject* obj = CopyInitializerObject(cx, templateObject.as<PlainObject>(), newKind);
|
||||
@ -4717,6 +4695,68 @@ 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<UnboxedArrayObject>()) {
|
||||
uint32_t length = templateObject->as<UnboxedArrayObject>().length();
|
||||
RootedObjectGroup group(cx, templateObject->group());
|
||||
return UnboxedArrayObject::create(cx, group, length, newKind);
|
||||
}
|
||||
|
||||
ArrayObject* obj = NewDenseFullyAllocatedArray(cx, templateObject->as<ArrayObject>().length(),
|
||||
NullPtr(), newKind);
|
||||
if (!obj)
|
||||
return nullptr;
|
||||
|
||||
MOZ_ASSERT(obj->lastProperty() == templateObject->as<ArrayObject>().lastProperty());
|
||||
obj->setGroup(templateObject->group());
|
||||
return obj;
|
||||
}
|
||||
|
||||
void
|
||||
js::ReportUninitializedLexical(JSContext* cx, HandlePropertyName name)
|
||||
{
|
||||
|
@ -445,6 +445,13 @@ 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)
|
||||
{
|
||||
|
@ -328,8 +328,11 @@ NativeObject::setLastPropertyMakeNonNative(Shape* shape)
|
||||
MOZ_ASSERT(shape->compartment() == compartment());
|
||||
MOZ_ASSERT(shape->slotSpan() == 0);
|
||||
MOZ_ASSERT(shape->numFixedSlots() == 0);
|
||||
MOZ_ASSERT(!hasDynamicElements());
|
||||
MOZ_ASSERT(!hasDynamicSlots());
|
||||
|
||||
if (hasDynamicElements())
|
||||
js_free(getElementsHeader());
|
||||
if (hasDynamicSlots())
|
||||
js_free(slots_);
|
||||
|
||||
shape_ = shape;
|
||||
}
|
||||
@ -710,6 +713,9 @@ 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)
|
||||
{
|
||||
|
@ -1168,6 +1168,15 @@ ObjectGroup::allocationSiteGroup(JSContext* cx, JSScript* script, jsbytecode* pc
|
||||
}
|
||||
}
|
||||
|
||||
if (JSOp(*pc) == JSOP_NEWARRAY && cx->runtime()->options().unboxedArrays()) {
|
||||
PreliminaryObjectArrayWithTemplate* preliminaryObjects =
|
||||
cx->new_<PreliminaryObjectArrayWithTemplate>(nullptr);
|
||||
if (preliminaryObjects)
|
||||
res->setPreliminaryObjects(preliminaryObjects);
|
||||
else
|
||||
cx->recoverFromOutOfMemory();
|
||||
}
|
||||
|
||||
if (!table->add(p, key, res))
|
||||
return nullptr;
|
||||
|
||||
|
@ -833,8 +833,8 @@
|
||||
* This opcode takes the kind of initializer (JSProto_Array or
|
||||
* JSProto_Object).
|
||||
*
|
||||
* This opcode has an extra byte so it can be exchanged with JSOP_NEWOBJECT
|
||||
* during emit.
|
||||
* This opcode has three extra bytes so it can be exchanged with
|
||||
* JSOP_NEWOBJECT during emit.
|
||||
* Category: Literals
|
||||
* Type: Object
|
||||
* Operands: uint8_t kind (, uint24_t extra)
|
||||
@ -1272,7 +1272,18 @@
|
||||
* Stack: receiver, obj, propval => obj[propval]
|
||||
*/ \
|
||||
macro(JSOP_GETELEM_SUPER, 125, "getelem-super", NULL, 1, 3, 1, JOF_BYTE |JOF_ELEM|JOF_LEFTASSOC) \
|
||||
macro(JSOP_UNUSED126, 126, "unused126", NULL, 1, 0, 0, JOF_BYTE) \
|
||||
/*
|
||||
* 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) \
|
||||
\
|
||||
/*
|
||||
* Defines the given function on the current scope.
|
||||
|
@ -52,6 +52,8 @@ 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;
|
||||
|
||||
|
@ -643,6 +643,9 @@ ConstraintTypeSet::addType(ExclusiveContext* cxArg, Type type)
|
||||
void
|
||||
TypeSet::print(FILE* fp)
|
||||
{
|
||||
if (!fp)
|
||||
fp = stderr;
|
||||
|
||||
if (flags & TYPE_FLAG_NON_DATA_PROPERTY)
|
||||
fprintf(fp, " [non-data]");
|
||||
|
||||
@ -2381,6 +2384,8 @@ 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
|
||||
@ -3337,16 +3342,19 @@ PreliminaryObjectArray::sweep()
|
||||
void
|
||||
PreliminaryObjectArrayWithTemplate::trace(JSTracer* trc)
|
||||
{
|
||||
TraceEdge(trc, &shape_, "PreliminaryObjectArrayWithTemplate_shape");
|
||||
if (shape_)
|
||||
TraceEdge(trc, &shape_, "PreliminaryObjectArrayWithTemplate_shape");
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
PreliminaryObjectArrayWithTemplate::writeBarrierPre(PreliminaryObjectArrayWithTemplate* objects)
|
||||
{
|
||||
if (!objects->shape()->runtimeFromAnyThread()->needsIncrementalBarrier())
|
||||
Shape* shape = objects->shape();
|
||||
|
||||
if (!shape || !shape->runtimeFromAnyThread()->needsIncrementalBarrier())
|
||||
return;
|
||||
|
||||
JS::Zone* zone = objects->shape()->zoneFromAnyThread();
|
||||
JS::Zone* zone = shape->zoneFromAnyThread();
|
||||
if (zone->needsIncrementalBarrier())
|
||||
objects->trace(zone->barrierTracer());
|
||||
}
|
||||
@ -3406,33 +3414,37 @@ PreliminaryObjectArrayWithTemplate::maybeAnalyze(ExclusiveContext* cx, ObjectGro
|
||||
ScopedJSDeletePtr<PreliminaryObjectArrayWithTemplate> preliminaryObjects(this);
|
||||
group->detachPreliminaryObjects();
|
||||
|
||||
MOZ_ASSERT(shape()->slotSpan() != 0);
|
||||
MOZ_ASSERT(OnlyHasDataProperties(shape()));
|
||||
if (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<PlainObject>();
|
||||
// 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<PlainObject>();
|
||||
|
||||
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;
|
||||
|
||||
// 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());
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -35,7 +35,7 @@ UnboxedTypeNeedsPreBarrier(JSValueType type)
|
||||
return type == JSVAL_TYPE_STRING || type == JSVAL_TYPE_OBJECT;
|
||||
}
|
||||
|
||||
// Class describing the layout of an UnboxedPlainObject.
|
||||
// Class tracking information specific to unboxed objects.
|
||||
class UnboxedLayout : public mozilla::LinkedListElement<UnboxedLayout>
|
||||
{
|
||||
public:
|
||||
@ -52,6 +52,14 @@ class UnboxedLayout : public mozilla::LinkedListElement<UnboxedLayout>
|
||||
typedef Vector<Property, 0, SystemAllocPolicy> 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_;
|
||||
|
||||
@ -65,12 +73,6 @@ class UnboxedLayout : public mozilla::LinkedListElement<UnboxedLayout>
|
||||
// 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
|
||||
@ -84,13 +86,25 @@ class UnboxedLayout : public mozilla::LinkedListElement<UnboxedLayout>
|
||||
// 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(const PropertyVector& properties, size_t size)
|
||||
: size_(size), newScript_(nullptr), traceList_(nullptr),
|
||||
nativeGroup_(nullptr), nativeShape_(nullptr), replacementNewGroup_(nullptr),
|
||||
constructorCode_(nullptr)
|
||||
{
|
||||
properties_.appendAll(properties);
|
||||
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() {
|
||||
@ -98,6 +112,10 @@ class UnboxedLayout : public mozilla::LinkedListElement<UnboxedLayout>
|
||||
js_free(traceList_);
|
||||
}
|
||||
|
||||
bool isArray() {
|
||||
return elementType_ != JSVAL_TYPE_MAGIC;
|
||||
}
|
||||
|
||||
void detachFromCompartment();
|
||||
|
||||
const PropertyVector& properties() const {
|
||||
@ -152,6 +170,10 @@ class UnboxedLayout : public mozilla::LinkedListElement<UnboxedLayout>
|
||||
constructorCode_ = code;
|
||||
}
|
||||
|
||||
JSValueType elementType() const {
|
||||
return elementType_;
|
||||
}
|
||||
|
||||
inline gc::AllocKind getAllocKind() const;
|
||||
|
||||
void trace(JSTracer* trc);
|
||||
@ -251,6 +273,9 @@ 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() {
|
||||
@ -272,9 +297,179 @@ 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<JSPropertyDescriptor> 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<JSPropertyDescriptor> 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 appendElementNoTypeChange(ExclusiveContext* cx, size_t index, const Value& v);
|
||||
|
||||
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 */
|
||||
|
@ -29,7 +29,7 @@ namespace js {
|
||||
*
|
||||
* https://developer.mozilla.org/en-US/docs/SpiderMonkey/Internals/Bytecode
|
||||
*/
|
||||
static const uint32_t XDR_BYTECODE_VERSION_SUBTRAHEND = 281;
|
||||
static const uint32_t XDR_BYTECODE_VERSION_SUBTRAHEND = 282;
|
||||
static const uint32_t XDR_BYTECODE_VERSION =
|
||||
uint32_t(0xb973c0de - XDR_BYTECODE_VERSION_SUBTRAHEND);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user