Bug 1146597 - Add unboxed arrays for JSOP_NEWARRAY arrays, and shell option for using them, r=jandem.

This commit is contained in:
Brian Hackett 2015-04-29 17:14:28 -07:00
parent f7d03175b2
commit 95eab242ee
44 changed files with 2762 additions and 832 deletions

View File

@ -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:

View File

@ -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);

View File

@ -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.

View File

@ -1740,23 +1740,11 @@ 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)
return false;
templateObject->setGroup(group);
ICNewArray_Fallback::Compiler stubCompiler(cx, templateObject);
ICNewArray_Fallback::Compiler stubCompiler(cx);
if (!emitOpIC(stubCompiler.getStub(&stubSpace_)))
return false;
@ -1764,6 +1752,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 +1823,11 @@ 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 (!group)
return false;
}
if (key == JSProto_Array) {
// Pass length in R0, group in R1.
// Pass length in R0.
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);
if (!emitOpIC(stubCompiler.getStub(&stubSpace_)))
return false;
} else {

View File

@ -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

View File

@ -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,33 @@ 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)
explicit ICNewArray_Fallback(JitCode* stubCode)
: ICFallbackStub(ICStub::NewArray_Fallback, stubCode), templateObject_(nullptr)
{}
public:
class Compiler : public ICStubCompiler {
RootedArrayObject templateObject;
bool generateStubCode(MacroAssembler& masm);
public:
Compiler(JSContext* cx, ArrayObject* templateObject)
: ICStubCompiler(cx, ICStub::NewArray_Fallback),
templateObject(cx, templateObject)
explicit Compiler(JSContext* cx)
: ICStubCompiler(cx, ICStub::NewArray_Fallback)
{}
ICStub* getStub(ICStubSpace* space) {
return ICStub::New<ICNewArray_Fallback>(space, getStubCode(), templateObject);
return ICStub::New<ICNewArray_Fallback>(space, getStubCode());
}
};
HeapPtrArrayObject& templateObject() {
HeapPtrObject& templateObject() {
return templateObject_;
}
void setTemplateObject(JSObject* obj) {
templateObject_ = obj;
}
};
class ICNewObject_Fallback : public ICFallbackStub
@ -3026,6 +3030,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 +3235,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 +3261,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 +3305,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 +3321,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 +3352,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 +4049,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
{

View File

@ -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;

View File

@ -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.

View File

@ -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);

View File

@ -6477,35 +6477,20 @@ 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);
return true;
@ -6581,10 +6566,20 @@ 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()->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 +6592,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 +6602,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 +8122,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 +8144,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 +8456,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 +8480,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 +8489,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 +8526,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 +8995,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 +9034,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 +9119,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 +9143,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 +9181,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 +9208,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 +9358,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 +9435,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 +10561,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 +10602,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 +11486,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:

View File

@ -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);

View File

@ -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 {

View File

@ -214,6 +214,9 @@
_(PostWriteBarrierV) \
_(InitializedLength) \
_(SetInitializedLength) \
_(UnboxedArrayLength) \
_(UnboxedArrayInitializedLength) \
_(IncrementUnboxedArrayInitializedLength) \
_(BoundsCheck) \
_(BoundsCheckRange) \
_(BoundsCheckLower) \

View File

@ -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;
}
}

View File

@ -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);

View File

@ -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;
}

View File

@ -4018,17 +4018,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 +4038,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 +4705,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,

View File

@ -2917,25 +2917,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 +2950,10 @@ class MNewArray
return allocating_;
}
jsbytecode* pc() const {
return pc_;
}
bool convertDoubleElements() const {
return convertDoubleElements_;
}
@ -2968,7 +2976,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 +7599,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 +7611,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 +7854,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 +8463,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 +8500,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 +8515,9 @@ class MLoadElementHole
MDefinition* initLength() const {
return getOperand(2);
}
JSValueType unboxedType() const {
return unboxedType_;
}
bool needsNegativeIntCheck() const {
return needsNegativeIntCheck_;
}
@ -8414,6 +8528,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 +8537,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 +8749,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 +8776,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 +8792,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 +13348,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);

View File

@ -178,6 +178,9 @@ namespace jit {
_(SetTypedObjectOffset) \
_(InitializedLength) \
_(SetInitializedLength) \
_(UnboxedArrayLength) \
_(UnboxedArrayInitializedLength) \
_(IncrementUnboxedArrayInitializedLength) \
_(Not) \
_(BoundsCheck) \
_(BoundsCheckLower) \

View File

@ -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");
}

View File

@ -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);
}

View File

@ -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;

View File

@ -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;

View File

@ -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);

View File

@ -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);

View File

@ -433,6 +433,13 @@ class MOZ_STACK_CLASS AutoValueVector : public AutoVectorRooter<Value>
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
}
explicit AutoValueVector(js::ContextFriendFields* cx
MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
: AutoVectorRooter<Value>(cx, VALVECTOR)
{
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
}
MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
};
@ -1183,6 +1190,7 @@ class JS_PUBLIC_API(RuntimeOptions) {
asmJS_(false),
nativeRegExp_(false),
unboxedObjects_(false),
unboxedArrays_(false),
werror_(false),
strictMode_(false),
extraWarnings_(false),
@ -1232,6 +1240,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;
@ -1278,6 +1292,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;

View File

@ -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());
}
}
/*

View File

@ -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

View File

@ -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;
}

View File

@ -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
@ -769,11 +771,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>();
@ -800,7 +802,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);

View File

@ -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]",

View File

@ -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.

View File

@ -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.

View File

@ -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 = &REGS.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)
{

View File

@ -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)
{

View File

@ -329,8 +329,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;
}
@ -711,6 +714,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)
{

View File

@ -1161,6 +1161,15 @@ ObjectGroup::allocationSiteGroup(JSContext* cx, JSScript* script, jsbytecode* pc
}
}
if (JSOp(*pc) == JSOP_NEWARRAY) {
PreliminaryObjectArrayWithTemplate* preliminaryObjects =
cx->new_<PreliminaryObjectArrayWithTemplate>(nullptr);
if (preliminaryObjects)
res->setPreliminaryObjects(preliminaryObjects);
else
cx->recoverFromOutOfMemory();
}
if (!table->add(p, key, res))
return nullptr;

View File

@ -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.

View File

@ -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;

View File

@ -2381,6 +2381,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 +3339,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 +3411,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

View File

@ -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,177 @@ 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 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 */

View File

@ -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);