Bug 1116855 - Add JIT optimizations for unboxed objects, r=jandem.

This commit is contained in:
Brian Hackett 2015-02-01 09:50:04 -07:00
Родитель 51def7fb13
Коммит e702b57de9
25 изменённых файлов: 1531 добавлений и 251 удалений

Просмотреть файл

@ -363,6 +363,11 @@ ICStub::trace(JSTracer *trc)
}
break;
}
case ICStub::GetProp_Unboxed: {
ICGetProp_Unboxed *propStub = toGetProp_Unboxed();
MarkTypeObject(trc, &propStub->type(), "baseline-getprop-unboxed-stub-type");
break;
}
case ICStub::GetProp_TypedObject: {
ICGetProp_TypedObject *propStub = toGetProp_TypedObject();
MarkShape(trc, &propStub->shape(), "baseline-getprop-typedobject-stub-shape");
@ -437,6 +442,11 @@ ICStub::trace(JSTracer *trc)
}
break;
}
case ICStub::SetProp_Unboxed: {
ICSetProp_Unboxed *propStub = toSetProp_Unboxed();
MarkTypeObject(trc, &propStub->type(), "baseline-setprop-unboxed-stub-type");
break;
}
case ICStub::SetProp_TypedObject: {
ICSetProp_TypedObject *propStub = toSetProp_TypedObject();
MarkShape(trc, &propStub->shape(), "baseline-setprop-typedobject-stub-shape");
@ -1410,8 +1420,9 @@ DoTypeUpdateFallback(JSContext *cx, BaselineFrame *frame, ICUpdatedStub *stub, H
break;
}
case ICStub::SetProp_Native:
case ICStub::SetProp_NativeAdd: {
MOZ_ASSERT(obj->isNative());
case ICStub::SetProp_NativeAdd:
case ICStub::SetProp_Unboxed: {
MOZ_ASSERT(obj->isNative() || obj->is<UnboxedPlainObject>());
jsbytecode *pc = stub->getChainFallback()->icEntry()->pc(script);
if (*pc == JSOP_SETALIASEDVAR || *pc == JSOP_INITALIASEDLEXICAL)
id = NameToId(ScopeCoordinateName(cx->runtime()->scopeCoordinateNameCache, script, pc));
@ -6598,6 +6609,40 @@ TryAttachNativeGetPropStub(JSContext *cx, HandleScript script, jsbytecode *pc,
return true;
}
static bool
TryAttachUnboxedGetPropStub(JSContext *cx, HandleScript script,
ICGetProp_Fallback *stub, HandlePropertyName name, HandleValue val,
bool *attached)
{
MOZ_ASSERT(!*attached);
if (!cx->runtime()->jitSupportsFloatingPoint)
return true;
if (!val.isObject() || !val.toObject().is<UnboxedPlainObject>())
return true;
Rooted<UnboxedPlainObject *> obj(cx, &val.toObject().as<UnboxedPlainObject>());
const UnboxedLayout::Property *property = obj->layout().lookup(name);
if (!property)
return true;
ICStub *monitorStub = stub->fallbackMonitorStub()->firstMonitorStub();
ICGetProp_Unboxed::Compiler compiler(cx, monitorStub, obj->type(),
property->offset + UnboxedPlainObject::offsetOfData(),
property->type);
ICStub *newStub = compiler.getStub(compiler.getStubSpace(script));
if (!newStub)
return false;
stub->addNewStub(newStub);
StripPreliminaryObjectStubs(cx, stub);
*attached = true;
return true;
}
static bool
TryAttachTypedObjectGetPropStub(JSContext *cx, HandleScript script,
ICGetProp_Fallback *stub, HandlePropertyName name, HandleValue val,
@ -6845,6 +6890,11 @@ DoGetPropFallback(JSContext *cx, BaselineFrame *frame, ICGetProp_Fallback *stub_
if (attached)
return true;
if (!TryAttachUnboxedGetPropStub(cx, script, stub, name, val, &attached))
return false;
if (attached)
return true;
if (!TryAttachTypedObjectGetPropStub(cx, script, stub, name, val, &attached))
return false;
if (attached)
@ -7755,6 +7805,39 @@ ICGetProp_Generic::Compiler::generateStubCode(MacroAssembler &masm)
return true;
}
bool
ICGetProp_Unboxed::Compiler::generateStubCode(MacroAssembler &masm)
{
Label failure;
GeneralRegisterSet regs(availableGeneralRegs(1));
Register scratch = regs.takeAnyExcluding(BaselineTailCallReg);
// Object and type guard.
masm.branchTestObject(Assembler::NotEqual, R0, &failure);
Register object = masm.extractObject(R0, ExtractTemp0);
masm.loadPtr(Address(BaselineStubReg, ICGetProp_Unboxed::offsetOfType()), scratch);
masm.branchPtr(Assembler::NotEqual, Address(object, JSObject::offsetOfType()), scratch,
&failure);
// Get the address being read from.
masm.load32(Address(BaselineStubReg, ICGetProp_Unboxed::offsetOfFieldOffset()), scratch);
masm.loadUnboxedProperty(BaseIndex(object, scratch, TimesOne), fieldType_, TypedOrValueRegister(R0));
// Only monitor the result if its type might change.
if (fieldType_ == JSVAL_TYPE_OBJECT)
EmitEnterTypeMonitorIC(masm);
else
EmitReturnFromIC(masm);
masm.bind(&failure);
EmitStubGuardFailure(masm);
return true;
}
bool
ICGetProp_TypedObject::Compiler::generateStubCode(MacroAssembler &masm)
{
@ -8009,6 +8092,40 @@ TryAttachSetAccessorPropStub(JSContext *cx, HandleScript script, jsbytecode *pc,
return true;
}
static bool
TryAttachUnboxedSetPropStub(JSContext *cx, HandleScript script,
ICSetProp_Fallback *stub, HandleId id,
HandleObject obj, HandleValue rhs, bool *attached)
{
MOZ_ASSERT(!*attached);
if (!cx->runtime()->jitSupportsFloatingPoint)
return true;
if (!obj->is<UnboxedPlainObject>())
return true;
const UnboxedLayout::Property *property = obj->as<UnboxedPlainObject>().layout().lookup(id);
if (!property)
return true;
ICSetProp_Unboxed::Compiler compiler(cx, obj->type(),
property->offset + UnboxedPlainObject::offsetOfData(),
property->type);
ICUpdatedStub *newStub = compiler.getStub(compiler.getStubSpace(script));
if (!newStub)
return false;
if (compiler.needsUpdateStubs() && !newStub->addUpdateStubForValue(cx, script, obj, id, rhs))
return false;
stub->addNewStub(newStub);
StripPreliminaryObjectStubs(cx, stub);
*attached = true;
return true;
}
static bool
TryAttachTypedObjectSetPropStub(JSContext *cx, HandleScript script,
ICSetProp_Fallback *stub, HandleId id,
@ -8152,6 +8269,15 @@ DoSetPropFallback(JSContext *cx, BaselineFrame *frame, ICSetProp_Fallback *stub_
if (attached)
return true;
if (!attached &&
lhs.isObject() &&
!TryAttachUnboxedSetPropStub(cx, script, stub, id, obj, rhs, &attached))
{
return false;
}
if (attached)
return true;
if (!attached &&
lhs.isObject() &&
!TryAttachTypedObjectSetPropStub(cx, script, stub, id, obj, rhs, &attached))
@ -8436,6 +8562,75 @@ ICSetPropNativeAddCompiler::generateStubCode(MacroAssembler &masm)
return true;
}
bool
ICSetProp_Unboxed::Compiler::generateStubCode(MacroAssembler &masm)
{
Label failure;
// Guard input is an object.
masm.branchTestObject(Assembler::NotEqual, R0, &failure);
GeneralRegisterSet regs(availableGeneralRegs(2));
Register scratch = regs.takeAny();
// Unbox and type guard.
Register object = masm.extractObject(R0, ExtractTemp0);
masm.loadPtr(Address(BaselineStubReg, ICSetProp_Unboxed::offsetOfType()), scratch);
masm.branchPtr(Assembler::NotEqual, Address(object, JSObject::offsetOfType()), scratch,
&failure);
if (needsUpdateStubs()) {
// Stow both R0 and R1 (object and value).
masm.push(object);
masm.push(BaselineStubReg);
EmitStowICValues(masm, 2);
// Move RHS into R0 for TypeUpdate check.
masm.moveValue(R1, R0);
// Call the type update stub.
if (!callTypeUpdateIC(masm, sizeof(Value)))
return false;
// Unstow R0 and R1 (object and key)
EmitUnstowICValues(masm, 2);
masm.pop(BaselineStubReg);
masm.pop(object);
// Trigger post barriers here on the values being written. Fields which
// objects can be written to also need update stubs.
GeneralRegisterSet saveRegs;
saveRegs.add(R0);
saveRegs.add(R1);
saveRegs.addUnchecked(object);
saveRegs.add(BaselineStubReg);
emitPostWriteBarrierSlot(masm, object, R1, scratch, saveRegs);
}
// Compute the address being written to.
masm.load32(Address(BaselineStubReg, ICSetProp_Unboxed::offsetOfFieldOffset()), scratch);
BaseIndex address(object, scratch, TimesOne);
if (fieldType_ == JSVAL_TYPE_OBJECT)
EmitPreBarrier(masm, address, MIRType_Object);
else if (fieldType_ == JSVAL_TYPE_STRING)
EmitPreBarrier(masm, address, MIRType_String);
else
MOZ_ASSERT(!UnboxedTypeNeedsPreBarrier(fieldType_));
masm.storeUnboxedProperty(address, fieldType_,
ConstantOrRegister(TypedOrValueRegister(R1)), &failure);
// The RHS has to be in R0.
masm.moveValue(R1, R0);
EmitReturnFromIC(masm);
masm.bind(&failure);
EmitStubGuardFailure(masm);
return true;
}
bool
ICSetProp_TypedObject::Compiler::generateStubCode(MacroAssembler &masm)
{
@ -9066,27 +9261,26 @@ TryAttachCallStub(JSContext *cx, ICCall_Fallback *stub, HandleScript script, jsb
// Remember the template object associated with any script being called
// as a constructor, for later use during Ion compilation.
RootedPlainObject templateObject(cx);
RootedObject templateObject(cx);
if (constructing) {
JSObject *thisObject = CreateThisForFunction(cx, fun, MaybeSingletonObject);
if (!thisObject)
return false;
if (thisObject->is<PlainObject>()) {
templateObject = &thisObject->as<PlainObject>();
if (thisObject->is<PlainObject>() || thisObject->is<UnboxedPlainObject>()) {
templateObject = thisObject;
// If we are calling a constructor for which the new script
// properties analysis has not been performed yet, don't attach a
// stub. After the analysis is performed, CreateThisForFunction may
// start returning objects with a different type, and the Ion
// compiler might get confused.
if (templateObject->type()->newScript() &&
!templateObject->type()->newScript()->analyzed())
{
types::TypeNewScript *newScript = templateObject->type()->newScript();
if (newScript && !newScript->analyzed()) {
// Clear the object just created from the preliminary objects
// on the TypeNewScript, as it will not be used or filled in by
// running code.
templateObject->type()->newScript()->unregisterNewObject(templateObject);
newScript->unregisterNewObject(&templateObject->as<PlainObject>());
return true;
}
}
@ -11713,7 +11907,7 @@ ICSetProp_CallNative::Clone(JSContext *cx, ICStubSpace *space, ICStub *,
}
ICCall_Scripted::ICCall_Scripted(JitCode *stubCode, ICStub *firstMonitorStub,
HandleScript calleeScript, HandleNativeObject templateObject,
HandleScript calleeScript, HandleObject templateObject,
uint32_t pcOffset)
: ICMonitoredStub(ICStub::Call_Scripted, stubCode, firstMonitorStub),
calleeScript_(calleeScript),
@ -11726,7 +11920,7 @@ ICCall_Scripted::Clone(JSContext *cx, ICStubSpace *space, ICStub *firstMonitorSt
ICCall_Scripted &other)
{
RootedScript calleeScript(cx, other.calleeScript_);
RootedNativeObject templateObject(cx, other.templateObject_);
RootedObject templateObject(cx, other.templateObject_);
return New(space, other.jitCode(), firstMonitorStub, calleeScript, templateObject,
other.pcOffset_);
}

Просмотреть файл

@ -430,6 +430,7 @@ class ICEntry
_(GetProp_Native) \
_(GetProp_NativeDoesNotExist) \
_(GetProp_NativePrototype) \
_(GetProp_Unboxed) \
_(GetProp_TypedObject) \
_(GetProp_CallScripted) \
_(GetProp_CallNative) \
@ -444,6 +445,7 @@ class ICEntry
_(SetProp_Fallback) \
_(SetProp_Native) \
_(SetProp_NativeAdd) \
_(SetProp_Unboxed) \
_(SetProp_TypedObject) \
_(SetProp_CallScripted) \
_(SetProp_CallNative) \
@ -4523,6 +4525,72 @@ class ICGetPropNativeDoesNotExistCompiler : public ICStubCompiler
ICStub *getStub(ICStubSpace *space);
};
class ICGetProp_Unboxed : public ICMonitoredStub
{
friend class ICStubSpace;
HeapPtrTypeObject type_;
uint32_t fieldOffset_;
ICGetProp_Unboxed(JitCode *stubCode, ICStub *firstMonitorStub, HandleTypeObject type,
uint32_t fieldOffset)
: ICMonitoredStub(ICStub::GetProp_Unboxed, stubCode, firstMonitorStub),
type_(type), fieldOffset_(fieldOffset)
{
(void) fieldOffset_; // Silence clang warning
}
public:
static inline ICGetProp_Unboxed *New(ICStubSpace *space, JitCode *code,
ICStub *firstMonitorStub, HandleTypeObject shape,
uint32_t fieldOffset)
{
if (!code)
return nullptr;
return space->allocate<ICGetProp_Unboxed>(code, firstMonitorStub, shape, fieldOffset);
}
HeapPtrTypeObject &type() {
return type_;
}
static size_t offsetOfType() {
return offsetof(ICGetProp_Unboxed, type_);
}
static size_t offsetOfFieldOffset() {
return offsetof(ICGetProp_Unboxed, fieldOffset_);
}
class Compiler : public ICStubCompiler {
protected:
ICStub *firstMonitorStub_;
RootedTypeObject type_;
uint32_t fieldOffset_;
JSValueType fieldType_;
bool generateStubCode(MacroAssembler &masm);
virtual int32_t getKey() const {
return static_cast<int32_t>(kind) | (static_cast<int32_t>(fieldType_)) << 16;
}
public:
Compiler(JSContext *cx, ICStub *firstMonitorStub,
types::TypeObject *type, uint32_t fieldOffset, JSValueType fieldType)
: ICStubCompiler(cx, ICStub::GetProp_Unboxed),
firstMonitorStub_(firstMonitorStub),
type_(cx, type),
fieldOffset_(fieldOffset),
fieldType_(fieldType)
{}
ICStub *getStub(ICStubSpace *space) {
return ICGetProp_Unboxed::New(space, getStubCode(), firstMonitorStub_,
type_, fieldOffset_);
}
};
};
static uint32_t
SimpleTypeDescrKey(SimpleTypeDescr *descr)
{
@ -5419,6 +5487,77 @@ class ICSetPropNativeAddCompiler : public ICStubCompiler
ICUpdatedStub *getStub(ICStubSpace *space);
};
class ICSetProp_Unboxed : public ICUpdatedStub
{
friend class ICStubSpace;
HeapPtrTypeObject type_;
uint32_t fieldOffset_;
ICSetProp_Unboxed(JitCode *stubCode, HandleTypeObject type, uint32_t fieldOffset)
: ICUpdatedStub(ICStub::SetProp_Unboxed, stubCode),
type_(type),
fieldOffset_(fieldOffset)
{
(void) fieldOffset_; // Silence clang warning
}
public:
static inline ICSetProp_Unboxed *New(ICStubSpace *space, JitCode *code,
HandleTypeObject type, uint32_t fieldOffset)
{
if (!code)
return nullptr;
return space->allocate<ICSetProp_Unboxed>(code, type, fieldOffset);
}
HeapPtrTypeObject &type() {
return type_;
}
static size_t offsetOfType() {
return offsetof(ICSetProp_Unboxed, type_);
}
static size_t offsetOfFieldOffset() {
return offsetof(ICSetProp_Unboxed, fieldOffset_);
}
class Compiler : public ICStubCompiler {
protected:
RootedTypeObject type_;
uint32_t fieldOffset_;
JSValueType fieldType_;
bool generateStubCode(MacroAssembler &masm);
virtual int32_t getKey() const {
return static_cast<int32_t>(kind) |
(static_cast<int32_t>(fieldType_) << 16);
}
public:
Compiler(JSContext *cx, types::TypeObject *type, uint32_t fieldOffset,
JSValueType fieldType)
: ICStubCompiler(cx, ICStub::SetProp_Unboxed),
type_(cx, type),
fieldOffset_(fieldOffset),
fieldType_(fieldType)
{}
ICUpdatedStub *getStub(ICStubSpace *space) {
ICUpdatedStub *stub = ICSetProp_Unboxed::New(space, getStubCode(),
type_, fieldOffset_);
if (!stub || !stub->initUpdatingChain(cx, space))
return nullptr;
return stub;
}
bool needsUpdateStubs() {
return fieldType_ == JSVAL_TYPE_OBJECT;
}
};
};
class ICSetProp_TypedObject : public ICUpdatedStub
{
friend class ICStubSpace;
@ -5799,17 +5938,17 @@ class ICCall_Scripted : public ICMonitoredStub
protected:
HeapPtrScript calleeScript_;
HeapPtrNativeObject templateObject_;
HeapPtrObject templateObject_;
uint32_t pcOffset_;
ICCall_Scripted(JitCode *stubCode, ICStub *firstMonitorStub,
HandleScript calleeScript, HandleNativeObject templateObject,
HandleScript calleeScript, HandleObject templateObject,
uint32_t pcOffset);
public:
static inline ICCall_Scripted *New(
ICStubSpace *space, JitCode *code, ICStub *firstMonitorStub,
HandleScript calleeScript, HandleNativeObject templateObject,
HandleScript calleeScript, HandleObject templateObject,
uint32_t pcOffset)
{
if (!code)
@ -5824,7 +5963,7 @@ class ICCall_Scripted : public ICMonitoredStub
HeapPtrScript &calleeScript() {
return calleeScript_;
}
HeapPtrNativeObject &templateObject() {
HeapPtrObject &templateObject() {
return templateObject_;
}
@ -5872,7 +6011,7 @@ class ICCallScriptedCompiler : public ICCallStubCompiler {
bool isConstructing_;
bool isSpread_;
RootedScript calleeScript_;
RootedNativeObject templateObject_;
RootedObject templateObject_;
uint32_t pcOffset_;
bool generateStubCode(MacroAssembler &masm);
@ -5883,7 +6022,7 @@ class ICCallScriptedCompiler : public ICCallStubCompiler {
public:
ICCallScriptedCompiler(JSContext *cx, ICStub *firstMonitorStub,
HandleScript calleeScript, HandleNativeObject templateObject,
HandleScript calleeScript, HandleObject templateObject,
bool isConstructing, bool isSpread, uint32_t pcOffset)
: ICCallStubCompiler(cx, ICStub::Call_Scripted),
firstMonitorStub_(firstMonitorStub),

Просмотреть файл

@ -80,12 +80,15 @@ SetElemICInspector::sawTypedArrayWrite() const
}
bool
BaselineInspector::maybeShapesForPropertyOp(jsbytecode *pc, ShapeVector &shapes)
BaselineInspector::maybeInfoForPropertyOp(jsbytecode *pc,
ShapeVector &nativeShapes,
TypeObjectVector &unboxedTypes)
{
// Return a list of shapes seen by the baseline IC for the current op.
// An empty list indicates no shapes are known, or there was an uncacheable
// access.
MOZ_ASSERT(shapes.empty());
// Return lists of native shapes and unboxed objects seen by the baseline
// IC for the current op. Empty lists indicate no shapes/types are known,
// or there was an uncacheable access.
MOZ_ASSERT(nativeShapes.empty());
MOZ_ASSERT(unboxedTypes.empty());
if (!hasBaselineScript())
return true;
@ -95,43 +98,66 @@ BaselineInspector::maybeShapesForPropertyOp(jsbytecode *pc, ShapeVector &shapes)
ICStub *stub = entry.firstStub();
while (stub->next()) {
Shape *shape;
Shape *shape = nullptr;
types::TypeObject *type = nullptr;
if (stub->isGetProp_Native()) {
shape = stub->toGetProp_Native()->shape();
} else if (stub->isSetProp_Native()) {
shape = stub->toSetProp_Native()->shape();
} else if (stub->isGetProp_Unboxed()) {
type = stub->toGetProp_Unboxed()->type();
} else if (stub->isSetProp_Unboxed()) {
type = stub->toSetProp_Unboxed()->type();
} else {
shapes.clear();
nativeShapes.clear();
unboxedTypes.clear();
return true;
}
// Don't add the same shape twice (this can happen if there are multiple
// SetProp_Native stubs with different TypeObject's).
// Don't add the same shape/type twice (this can happen if there are
// multiple SetProp_Native stubs with different TypeObject's).
if (shape) {
bool found = false;
for (size_t i = 0; i < shapes.length(); i++) {
if (shapes[i] == shape) {
for (size_t i = 0; i < nativeShapes.length(); i++) {
if (nativeShapes[i] == shape) {
found = true;
break;
}
}
if (!found && !shapes.append(shape))
if (!found && !nativeShapes.append(shape))
return false;
} else {
bool found = false;
for (size_t i = 0; i < unboxedTypes.length(); i++) {
if (unboxedTypes[i] == type) {
found = true;
break;
}
}
if (!found && !unboxedTypes.append(type))
return false;
}
stub = stub->next();
}
if (stub->isGetProp_Fallback()) {
if (stub->toGetProp_Fallback()->hadUnoptimizableAccess())
shapes.clear();
if (stub->toGetProp_Fallback()->hadUnoptimizableAccess()) {
nativeShapes.clear();
unboxedTypes.clear();
}
} else {
if (stub->toSetProp_Fallback()->hadUnoptimizableAccess())
shapes.clear();
if (stub->toSetProp_Fallback()->hadUnoptimizableAccess()) {
nativeShapes.clear();
unboxedTypes.clear();
}
}
// Don't inline if there are more than 5 shapes.
if (shapes.length() > 5)
shapes.clear();
// Don't inline if there are more than 5 shapes/types.
if (nativeShapes.length() + unboxedTypes.length() > 5) {
nativeShapes.clear();
unboxedTypes.clear();
}
return true;
}
@ -413,7 +439,7 @@ BaselineInspector::hasSeenDoubleResult(jsbytecode *pc)
return false;
}
NativeObject *
JSObject *
BaselineInspector::getTemplateObject(jsbytecode *pc)
{
if (!hasBaselineScript())
@ -429,7 +455,7 @@ BaselineInspector::getTemplateObject(jsbytecode *pc)
case ICStub::Rest_Fallback:
return stub->toRest_Fallback()->templateObject();
case ICStub::Call_Scripted:
if (NativeObject *obj = stub->toCall_Scripted()->templateObject())
if (JSObject *obj = stub->toCall_Scripted()->templateObject())
return obj;
break;
default:

Просмотреть файл

@ -93,7 +93,8 @@ class BaselineInspector
public:
typedef Vector<Shape *, 4, JitAllocPolicy> ShapeVector;
bool maybeShapesForPropertyOp(jsbytecode *pc, ShapeVector &shapes);
typedef Vector<types::TypeObject *, 4, JitAllocPolicy> TypeObjectVector;
bool maybeInfoForPropertyOp(jsbytecode *pc, ShapeVector &nativeShapes, TypeObjectVector &unboxedTypes);
SetElemICInspector setElemICInspector(jsbytecode *pc) {
return makeICInspector<SetElemICInspector>(pc, ICStub::SetElem_Fallback);
@ -109,7 +110,7 @@ class BaselineInspector
bool hasSeenDoubleResult(jsbytecode *pc);
bool hasSeenNonStringIterMore(jsbytecode *pc);
NativeObject *getTemplateObject(jsbytecode *pc);
JSObject *getTemplateObject(jsbytecode *pc);
JSObject *getTemplateObjectForNative(jsbytecode *pc, Native native);
JSObject *getTemplateObjectForClassHook(jsbytecode *pc, const Class *clasp);

Просмотреть файл

@ -2233,21 +2233,49 @@ CodeGenerator::emitGetPropertyPolymorphic(LInstruction *ins, Register obj, Regis
const TypedOrValueRegister &output)
{
MGetPropertyPolymorphic *mir = ins->mirRaw()->toGetPropertyPolymorphic();
MOZ_ASSERT(mir->numShapes() > 1);
masm.loadObjShape(obj, scratch);
size_t total = mir->numUnboxedTypes() + mir->numShapes();
MOZ_ASSERT(total > 1);
bool typeInScratch = mir->numUnboxedTypes() > 1;
bool shapeInScratch = mir->numShapes() > 1;
Label done;
for (size_t i = 0; i < mir->numShapes(); i++) {
for (size_t i = 0; i < total; i++) {
bool unboxedType = i < mir->numUnboxedTypes();
ImmGCPtr comparePtr = unboxedType
? ImmGCPtr(mir->unboxedType(i))
: ImmGCPtr(mir->objShape(i - mir->numUnboxedTypes()));
Address addr(obj, unboxedType ? JSObject::offsetOfType() : JSObject::offsetOfShape());
if ((i == 0 && typeInScratch) || (i == mir->numUnboxedTypes() && shapeInScratch))
masm.loadPtr(addr, scratch);
bool inScratch = unboxedType ? typeInScratch : shapeInScratch;
Label next;
if (i == mir->numShapes() - 1) {
bailoutCmpPtr(Assembler::NotEqual, scratch, ImmGCPtr(mir->objShape(i)),
ins->snapshot());
if (i == total - 1) {
if (inScratch)
bailoutCmpPtr(Assembler::NotEqual, scratch, comparePtr, ins->snapshot());
else
bailoutCmpPtr(Assembler::NotEqual, addr, comparePtr, ins->snapshot());
} else {
masm.branchPtr(Assembler::NotEqual, scratch, ImmGCPtr(mir->objShape(i)), &next);
if (inScratch)
masm.branchPtr(Assembler::NotEqual, scratch, comparePtr, &next);
else
masm.branchPtr(Assembler::NotEqual, addr, comparePtr, &next);
}
Shape *shape = mir->shape(i);
if (unboxedType) {
const UnboxedLayout::Property *property =
mir->unboxedType(i)->unboxedLayout().lookup(mir->name());
Address propertyAddr(obj, UnboxedPlainObject::offsetOfData() + property->offset);
masm.loadUnboxedProperty(propertyAddr, property->type, output);
} else {
Shape *shape = mir->shape(i - mir->numUnboxedTypes());
if (shape->slot() < shape->numFixedSlots()) {
// Fixed slot.
masm.loadTypedOrValue(Address(obj, NativeObject::getFixedSlotOffset(shape->slot())),
@ -2258,8 +2286,9 @@ CodeGenerator::emitGetPropertyPolymorphic(LInstruction *ins, Register obj, Regis
masm.loadPtr(Address(obj, NativeObject::offsetOfSlots()), scratch);
masm.loadTypedOrValue(Address(scratch, offset), output);
}
}
if (i != mir->numShapes() - 1)
if (i != total - 1)
masm.jump(&done);
masm.bind(&next);
}
@ -2291,21 +2320,55 @@ CodeGenerator::emitSetPropertyPolymorphic(LInstruction *ins, Register obj, Regis
const ConstantOrRegister &value)
{
MSetPropertyPolymorphic *mir = ins->mirRaw()->toSetPropertyPolymorphic();
MOZ_ASSERT(mir->numShapes() > 1);
masm.loadObjShape(obj, scratch);
size_t total = mir->numUnboxedTypes() + mir->numShapes();
MOZ_ASSERT(total > 1);
bool typeInScratch = mir->numUnboxedTypes() > 1;
bool shapeInScratch = mir->numShapes() > 1;
Label done;
for (size_t i = 0; i < mir->numShapes(); i++) {
for (size_t i = 0; i < total; i++) {
bool unboxedType = i < mir->numUnboxedTypes();
ImmGCPtr comparePtr = unboxedType
? ImmGCPtr(mir->unboxedType(i))
: ImmGCPtr(mir->objShape(i - mir->numUnboxedTypes()));
Address addr(obj, unboxedType ? JSObject::offsetOfType() : JSObject::offsetOfShape());
if ((i == 0 && typeInScratch) || (i == mir->numUnboxedTypes() && shapeInScratch))
masm.loadPtr(addr, scratch);
bool inScratch = unboxedType ? typeInScratch : shapeInScratch;
Label next;
if (i == mir->numShapes() - 1) {
bailoutCmpPtr(Assembler::NotEqual, scratch, ImmGCPtr(mir->objShape(i)),
ins->snapshot());
if (i == total - 1) {
if (inScratch)
bailoutCmpPtr(Assembler::NotEqual, scratch, comparePtr, ins->snapshot());
else
bailoutCmpPtr(Assembler::NotEqual, addr, comparePtr, ins->snapshot());
} else {
masm.branchPtr(Assembler::NotEqual, scratch, ImmGCPtr(mir->objShape(i)), &next);
if (inScratch)
masm.branchPtr(Assembler::NotEqual, scratch, comparePtr, &next);
else
masm.branchPtr(Assembler::NotEqual, addr, comparePtr, &next);
}
Shape *shape = mir->shape(i);
if (unboxedType) {
const UnboxedLayout::Property *property =
mir->unboxedType(i)->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));
masm.storeUnboxedProperty(propertyAddr, property->type, value, nullptr);
} else {
Shape *shape = mir->shape(i - mir->numUnboxedTypes());
if (shape->slot() < shape->numFixedSlots()) {
// Fixed slot.
Address addr(obj, NativeObject::getFixedSlotOffset(shape->slot()));
@ -2320,8 +2383,9 @@ CodeGenerator::emitSetPropertyPolymorphic(LInstruction *ins, Register obj, Regis
emitPreBarrier(addr);
masm.storeConstantOrRegister(value, addr);
}
}
if (i != mir->numShapes() - 1)
if (i != total - 1)
masm.jump(&done);
masm.bind(&next);
}
@ -4573,7 +4637,7 @@ static const VMFunction NewGCObjectInfo =
void
CodeGenerator::visitCreateThisWithTemplate(LCreateThisWithTemplate *lir)
{
PlainObject *templateObject = lir->mir()->templateObject();
JSObject *templateObject = lir->mir()->templateObject();
gc::AllocKind allocKind = templateObject->asTenured().getAllocKind();
gc::InitialHeap initialHeap = lir->mir()->initialHeap();
const js::Class *clasp = templateObject->type()->clasp();
@ -4591,7 +4655,8 @@ CodeGenerator::visitCreateThisWithTemplate(LCreateThisWithTemplate *lir)
// Initialize based on the templateObject.
masm.bind(ool->rejoin());
bool initFixedSlots = ShouldInitFixedSlots(lir, templateObject);
bool initFixedSlots = !templateObject->is<PlainObject>() ||
ShouldInitFixedSlots(lir, &templateObject->as<PlainObject>());
masm.initGCThing(objReg, tempReg, templateObject, initFixedSlots);
}
@ -8373,8 +8438,8 @@ CodeGenerator::visitLoadUnboxedPointerT(LLoadUnboxedPointerT *lir)
bool bailOnNull;
int32_t offsetAdjustment;
if (lir->mir()->isLoadUnboxedObjectOrNull()) {
MOZ_ASSERT(lir->mir()->toLoadUnboxedObjectOrNull()->bailOnNull());
bailOnNull = true;
bailOnNull = lir->mir()->toLoadUnboxedObjectOrNull()->nullBehavior() !=
MLoadUnboxedObjectOrNull::NullNotPossible;
offsetAdjustment = lir->mir()->toLoadUnboxedObjectOrNull()->offsetAdjustment();
} else if (lir->mir()->isLoadUnboxedString()) {
bailOnNull = false;
@ -8411,11 +8476,13 @@ CodeGenerator::visitLoadTypedArrayElement(LLoadTypedArrayElement *lir)
Label fail;
if (lir->index()->isConstant()) {
Address source(elements, ToInt32(lir->index()) * width + lir->mir()->offsetAdjustment());
masm.loadFromTypedArray(arrayType, source, out, temp, &fail);
masm.loadFromTypedArray(arrayType, source, out, temp, &fail,
lir->mir()->canonicalizeDoubles());
} else {
BaseIndex source(elements, ToRegister(lir->index()), ScaleFromElemWidth(width),
lir->mir()->offsetAdjustment());
masm.loadFromTypedArray(arrayType, source, out, temp, &fail);
masm.loadFromTypedArray(arrayType, source, out, temp, &fail,
lir->mir()->canonicalizeDoubles());
}
if (fail.used())

Просмотреть файл

@ -5514,7 +5514,9 @@ IonBuilder::createThisScriptedSingleton(JSFunction *target, MDefinition *callee)
return nullptr;
JSObject *templateObject = inspector->getTemplateObject(pc);
if (!templateObject || !templateObject->is<PlainObject>())
if (!templateObject)
return nullptr;
if (!templateObject->is<PlainObject>() && !templateObject->is<UnboxedPlainObject>())
return nullptr;
if (!templateObject->hasTenuredProto() || templateObject->getProto() != proto)
return nullptr;
@ -6181,7 +6183,7 @@ IonBuilder::jsop_compare(JSOp op)
bool
IonBuilder::jsop_newarray(uint32_t count)
{
NativeObject *templateObject = inspector->getTemplateObject(pc);
JSObject *templateObject = inspector->getTemplateObject(pc);
if (!templateObject) {
if (info().analysisMode() == Analysis_ArgumentsUsage) {
MUnknownValue *unknown = MUnknownValue::New(alloc());
@ -6218,9 +6220,9 @@ IonBuilder::jsop_newarray(uint32_t count)
ins->resultTypeSet()->convertDoubleElements(constraints());
if (conversion == types::TemporaryTypeSet::AlwaysConvertToDoubles)
templateObject->setShouldConvertDoubleElements();
templateObject->as<ArrayObject>().setShouldConvertDoubleElements();
else
templateObject->clearShouldConvertDoubleElements();
templateObject->as<ArrayObject>().clearShouldConvertDoubleElements();
return true;
}
@ -6811,7 +6813,8 @@ IonBuilder::maybeInsertResume()
static bool
ClassHasEffectlessLookup(const Class *clasp, PropertyName *name)
{
return clasp->isNative() && !clasp->ops.lookupGeneric;
return (clasp == &UnboxedPlainObject::class_) ||
(clasp->isNative() && !clasp->ops.lookupGeneric);
}
static bool
@ -7741,9 +7744,13 @@ IonBuilder::pushReferenceLoadFromTypedObject(MDefinition *typedObj,
// there is no other barrier needed we include the null bailout with
// MLoadUnboxedObjectOrNull, which avoids the need to box the result
// for a type barrier instruction.
bool bailOnNull = barrier == BarrierKind::NoBarrier &&
!observedTypes->hasType(types::Type::NullType());
load = MLoadUnboxedObjectOrNull::New(alloc(), elements, scaledOffset, bailOnNull, adjustment);
MLoadUnboxedObjectOrNull::NullBehavior nullBehavior;
if (barrier == BarrierKind::NoBarrier && !observedTypes->hasType(types::Type::NullType()))
nullBehavior = MLoadUnboxedObjectOrNull::BailOnNull;
else
nullBehavior = MLoadUnboxedObjectOrNull::HandleNull;
load = MLoadUnboxedObjectOrNull::New(alloc(), elements, scaledOffset, nullBehavior,
adjustment);
break;
}
case ReferenceTypeDescr::TYPE_STRING: {
@ -9244,7 +9251,7 @@ IonBuilder::getDefiniteSlot(types::TemporaryTypeSet *types, PropertyName *name)
if (slot == UINT32_MAX) {
slot = propertySlot;
} else if (slot != propertySlot) {
trackOptimizationOutcome(TrackedOutcome::NotFixedSlot);
trackOptimizationOutcome(TrackedOutcome::InconsistentFixedSlot);
return UINT32_MAX;
}
}
@ -9252,6 +9259,58 @@ IonBuilder::getDefiniteSlot(types::TemporaryTypeSet *types, PropertyName *name)
return slot;
}
uint32_t
IonBuilder::getUnboxedOffset(types::TemporaryTypeSet *types, PropertyName *name, JSValueType *punboxedType)
{
if (!types || types->unknownObject()) {
trackOptimizationOutcome(TrackedOutcome::NoTypeInfo);
return UINT32_MAX;
}
uint32_t offset = UINT32_MAX;
for (size_t i = 0; i < types->getObjectCount(); i++) {
types::TypeObjectKey *type = types->getObject(i);
if (!type)
continue;
if (type->unknownProperties()) {
trackOptimizationOutcome(TrackedOutcome::UnknownProperties);
return UINT32_MAX;
}
if (type->singleton()) {
trackOptimizationOutcome(TrackedOutcome::Singleton);
return UINT32_MAX;
}
UnboxedLayout *layout = type->asTypeObject()->maybeUnboxedLayout();
if (!layout) {
trackOptimizationOutcome(TrackedOutcome::NotUnboxed);
return UINT32_MAX;
}
const UnboxedLayout::Property *property = layout->lookup(name);
if (!property) {
trackOptimizationOutcome(TrackedOutcome::StructNoField);
return UINT32_MAX;
}
if (offset == UINT32_MAX) {
offset = property->offset;
*punboxedType = property->type;
} else if (offset != property->offset) {
trackOptimizationOutcome(TrackedOutcome::InconsistentFieldOffset);
return UINT32_MAX;
} else if (*punboxedType != property->type) {
trackOptimizationOutcome(TrackedOutcome::InconsistentFieldType);
return UINT32_MAX;
}
}
return offset;
}
bool
IonBuilder::jsop_runonce()
{
@ -9679,6 +9738,11 @@ IonBuilder::jsop_getprop(PropertyName *name)
if (!getPropTryDefiniteSlot(&emitted, obj, name, barrier, types) || emitted)
return emitted;
// Try to emit loads from unboxed objects.
trackOptimizationAttempt(TrackedStrategy::GetProp_Unboxed);
if (!getPropTryUnboxed(&emitted, obj, name, barrier, types) || emitted)
return emitted;
// Try to inline a common property getter, or make a call.
trackOptimizationAttempt(TrackedStrategy::GetProp_CommonGetter);
if (!getPropTryCommonGetter(&emitted, obj, name, types) || emitted)
@ -10007,6 +10071,92 @@ IonBuilder::getPropTryDefiniteSlot(bool *emitted, MDefinition *obj, PropertyName
return true;
}
MInstruction *
IonBuilder::loadUnboxedProperty(MDefinition *obj, size_t offset, JSValueType unboxedType,
BarrierKind barrier, types::TemporaryTypeSet *types)
{
size_t scaledOffsetConstant = offset / UnboxedTypeSize(unboxedType);
MInstruction *scaledOffset = MConstant::New(alloc(), Int32Value(scaledOffsetConstant));
current->add(scaledOffset);
MInstruction *load;
switch (unboxedType) {
case JSVAL_TYPE_BOOLEAN:
load = MLoadTypedArrayElement::New(alloc(), obj, scaledOffset, Scalar::Uint8,
DoesNotRequireMemoryBarrier,
UnboxedPlainObject::offsetOfData());
load->setResultType(MIRType_Boolean);
break;
case JSVAL_TYPE_INT32:
load = MLoadTypedArrayElement::New(alloc(), obj, scaledOffset, Scalar::Int32,
DoesNotRequireMemoryBarrier,
UnboxedPlainObject::offsetOfData());
load->setResultType(MIRType_Int32);
break;
case JSVAL_TYPE_DOUBLE:
load = MLoadTypedArrayElement::New(alloc(), obj, scaledOffset, Scalar::Float64,
DoesNotRequireMemoryBarrier,
UnboxedPlainObject::offsetOfData(),
/* canonicalizeDoubles = */ false);
load->setResultType(MIRType_Double);
break;
case JSVAL_TYPE_STRING:
load = MLoadUnboxedString::New(alloc(), obj, scaledOffset,
UnboxedPlainObject::offsetOfData());
break;
case JSVAL_TYPE_OBJECT: {
MLoadUnboxedObjectOrNull::NullBehavior nullBehavior;
if (types->hasType(types::Type::NullType()) || barrier != BarrierKind::NoBarrier)
nullBehavior = MLoadUnboxedObjectOrNull::HandleNull;
else
nullBehavior = MLoadUnboxedObjectOrNull::NullNotPossible;
load = MLoadUnboxedObjectOrNull::New(alloc(), obj, scaledOffset, nullBehavior,
UnboxedPlainObject::offsetOfData());
break;
}
default:
MOZ_CRASH();
}
current->add(load);
return load;
}
bool
IonBuilder::getPropTryUnboxed(bool *emitted, MDefinition *obj, PropertyName *name,
BarrierKind barrier, types::TemporaryTypeSet *types)
{
MOZ_ASSERT(*emitted == false);
JSValueType unboxedType;
uint32_t offset = getUnboxedOffset(obj->resultTypeSet(), name, &unboxedType);
if (offset == UINT32_MAX)
return true;
if (obj->type() != MIRType_Object) {
MGuardObject *guard = MGuardObject::New(alloc(), obj);
current->add(guard);
obj = guard;
}
if (unboxedType != JSVAL_TYPE_OBJECT)
barrier = BarrierKind::NoBarrier;
MInstruction *load = loadUnboxedProperty(obj, offset, unboxedType, barrier, types);
current->push(load);
if (!pushTypeBarrier(load, types, barrier))
return false;
*emitted = true;
return true;
}
bool
IonBuilder::getPropTryCommonGetter(bool *emitted, MDefinition *obj, PropertyName *name,
types::TemporaryTypeSet *types)
@ -10135,17 +10285,25 @@ IonBuilder::getPropTryCommonGetter(bool *emitted, MDefinition *obj, PropertyName
return true;
}
static bool
CanInlinePropertyOpShapes(const BaselineInspector::ShapeVector &shapes)
bool
IonBuilder::canInlinePropertyOpShapes(const BaselineInspector::ShapeVector &nativeShapes,
const BaselineInspector::TypeObjectVector &unboxedTypes)
{
for (size_t i = 0; i < shapes.length(); i++) {
if (nativeShapes.empty() && unboxedTypes.empty()) {
trackOptimizationOutcome(TrackedOutcome::NoShapeInfo);
return false;
}
for (size_t i = 0; i < nativeShapes.length(); i++) {
// We inline the property access as long as the shape is not in
// dictionary mode. We cannot be sure that the shape is still a
// lastProperty, and calling Shape::search() on dictionary mode
// shapes that aren't lastProperty is invalid.
if (shapes[i]->inDictionary())
if (nativeShapes[i]->inDictionary()) {
trackOptimizationOutcome(TrackedOutcome::InDictionaryMode);
return false;
}
}
return true;
}
@ -10154,7 +10312,6 @@ static bool
GetPropertyShapes(jsid id, const BaselineInspector::ShapeVector &shapes,
BaselineInspector::ShapeVector &propShapes, bool *sameSlot)
{
MOZ_ASSERT(shapes.length() > 1);
MOZ_ASSERT(propShapes.empty());
if (!propShapes.reserve(shapes.length()))
@ -10190,30 +10347,24 @@ IonBuilder::getPropTryInlineAccess(bool *emitted, MDefinition *obj, PropertyName
return true;
}
BaselineInspector::ShapeVector shapes(alloc());
if (!inspector->maybeShapesForPropertyOp(pc, shapes))
BaselineInspector::ShapeVector nativeShapes(alloc());
BaselineInspector::TypeObjectVector unboxedTypes(alloc());
if (!inspector->maybeInfoForPropertyOp(pc, nativeShapes, unboxedTypes))
return false;
if (shapes.empty()) {
trackOptimizationOutcome(TrackedOutcome::NoShapeInfo);
if (!canInlinePropertyOpShapes(nativeShapes, unboxedTypes))
return true;
}
if (!CanInlinePropertyOpShapes(shapes)) {
trackOptimizationOutcome(TrackedOutcome::InDictionaryMode);
return true;
}
MIRType rvalType = types->getKnownMIRType();
if (barrier != BarrierKind::NoBarrier || IsNullOrUndefined(rvalType))
rvalType = MIRType_Value;
if (shapes.length() == 1) {
if (nativeShapes.length() == 1 && unboxedTypes.empty()) {
// In the monomorphic case, use separate ShapeGuard and LoadSlot
// instructions.
spew("Inlining monomorphic GETPROP");
Shape *objShape = shapes[0];
Shape *objShape = nativeShapes[0];
obj = addShapeGuard(obj, objShape, Bailout_ShapeGuard);
Shape *shape = objShape->searchLinear(NameToId(name));
@ -10227,15 +10378,39 @@ IonBuilder::getPropTryInlineAccess(bool *emitted, MDefinition *obj, PropertyName
return true;
}
MOZ_ASSERT(shapes.length() > 1);
if (nativeShapes.empty() && unboxedTypes.length() == 1) {
spew("Inlining monomorphic unboxed GETPROP");
types::TypeObject *unboxedType = unboxedTypes[0];
// Failures in this type guard should be treated the same as a shape guard failure.
obj = MGuardObjectType::New(alloc(), obj, unboxedType, /* bailOnEquality = */ false,
Bailout_ShapeGuard);
current->add(obj->toInstruction());
if (failedShapeGuard_)
obj->toGuardObjectType()->setNotMovable();
const UnboxedLayout::Property *property = unboxedType->unboxedLayout().lookup(name);
MInstruction *load = loadUnboxedProperty(obj, property->offset, property->type, barrier, types);
current->push(load);
if (!pushTypeBarrier(load, types, barrier))
return false;
*emitted = true;
return true;
}
MOZ_ASSERT(nativeShapes.length() + unboxedTypes.length() > 1);
spew("Inlining polymorphic GETPROP");
BaselineInspector::ShapeVector propShapes(alloc());
bool sameSlot;
if (!GetPropertyShapes(NameToId(name), shapes, propShapes, &sameSlot))
if (!GetPropertyShapes(NameToId(name), nativeShapes, propShapes, &sameSlot))
return false;
if (sameSlot) {
if (sameSlot && unboxedTypes.empty()) {
MGuardShapePolymorphic *guard = MGuardShapePolymorphic::New(alloc(), obj);
current->add(guard);
obj = guard;
@ -10243,8 +10418,8 @@ IonBuilder::getPropTryInlineAccess(bool *emitted, MDefinition *obj, PropertyName
if (failedShapeGuard_)
guard->setNotMovable();
for (size_t i = 0; i < shapes.length(); i++) {
if (!guard->addShape(shapes[i]))
for (size_t i = 0; i < nativeShapes.length(); i++) {
if (!guard->addShape(nativeShapes[i]))
return false;
}
@ -10260,8 +10435,13 @@ IonBuilder::getPropTryInlineAccess(bool *emitted, MDefinition *obj, PropertyName
current->add(load);
current->push(load);
for (size_t i = 0; i < shapes.length(); i++) {
if (!load->addShape(shapes[i], propShapes[i]))
for (size_t i = 0; i < nativeShapes.length(); i++) {
if (!load->addShape(nativeShapes[i], propShapes[i]))
return false;
}
for (size_t i = 0; i < unboxedTypes.length(); i++) {
if (!load->addUnboxedType(unboxedTypes[i]))
return false;
}
@ -10447,10 +10627,6 @@ IonBuilder::jsop_setprop(PropertyName *name)
return resumeAfter(ins);
}
// Add post barrier if needed.
if (NeedsPostBarrier(info(), value))
current->add(MPostWriteBarrier::New(alloc(), obj, value));
// Try to inline a common property setter, or make a call.
trackOptimizationAttempt(TrackedStrategy::SetProp_CommonSetter);
if (!setPropTryCommonSetter(&emitted, obj, name, value) || emitted)
@ -10465,6 +10641,16 @@ IonBuilder::jsop_setprop(PropertyName *name)
bool barrier = PropertyWriteNeedsTypeBarrier(alloc(), constraints(), current, &obj, name, &value,
/* canModify = */ true);
// Try to emit stores to unboxed objects.
trackOptimizationAttempt(TrackedStrategy::SetProp_Unboxed);
if (!setPropTryUnboxed(&emitted, obj, name, value, barrier, objTypes) || emitted)
return emitted;
// Add post barrier if needed. The instructions above manage any post
// barriers they need directly.
if (NeedsPostBarrier(info(), value))
current->add(MPostWriteBarrier::New(alloc(), obj, value));
// Try to emit store from definite slots.
trackOptimizationAttempt(TrackedStrategy::SetProp_DefiniteSlot);
if (!setPropTryDefiniteSlot(&emitted, obj, name, value, barrier, objTypes) || emitted)
@ -10750,6 +10936,86 @@ IonBuilder::setPropTryDefiniteSlot(bool *emitted, MDefinition *obj,
return true;
}
MInstruction *
IonBuilder::storeUnboxedProperty(MDefinition *obj, size_t offset, JSValueType unboxedType,
MDefinition *value)
{
size_t scaledOffsetConstant = offset / UnboxedTypeSize(unboxedType);
MInstruction *scaledOffset = MConstant::New(alloc(), Int32Value(scaledOffsetConstant));
current->add(scaledOffset);
MInstruction *store;
switch (unboxedType) {
case JSVAL_TYPE_BOOLEAN:
store = MStoreTypedArrayElement::New(alloc(), obj, scaledOffset, value, Scalar::Uint8,
DoesNotRequireMemoryBarrier,
UnboxedPlainObject::offsetOfData());
break;
case JSVAL_TYPE_INT32:
store = MStoreTypedArrayElement::New(alloc(), obj, scaledOffset, value, Scalar::Int32,
DoesNotRequireMemoryBarrier,
UnboxedPlainObject::offsetOfData());
break;
case JSVAL_TYPE_DOUBLE:
store = MStoreTypedArrayElement::New(alloc(), obj, scaledOffset, value, Scalar::Float64,
DoesNotRequireMemoryBarrier,
UnboxedPlainObject::offsetOfData());
break;
case JSVAL_TYPE_STRING:
store = MStoreUnboxedString::New(alloc(), obj, scaledOffset, value,
UnboxedPlainObject::offsetOfData());
break;
case JSVAL_TYPE_OBJECT:
store = MStoreUnboxedObjectOrNull::New(alloc(), obj, scaledOffset, value, obj,
UnboxedPlainObject::offsetOfData());
break;
default:
MOZ_CRASH();
}
current->add(store);
return store;
}
bool
IonBuilder::setPropTryUnboxed(bool *emitted, MDefinition *obj,
PropertyName *name, MDefinition *value,
bool barrier, types::TemporaryTypeSet *objTypes)
{
MOZ_ASSERT(*emitted == false);
if (barrier) {
trackOptimizationOutcome(TrackedOutcome::NeedsTypeBarrier);
return true;
}
JSValueType unboxedType;
uint32_t offset = getUnboxedOffset(obj->resultTypeSet(), name, &unboxedType);
if (offset == UINT32_MAX)
return true;
if (obj->type() != MIRType_Object) {
MGuardObject *guard = MGuardObject::New(alloc(), obj);
current->add(guard);
obj = guard;
}
MInstruction *store = storeUnboxedProperty(obj, offset, unboxedType, value);
current->push(value);
if (!resumeAfter(store))
return false;
*emitted = true;
return true;
}
bool
IonBuilder::setPropTryInlineAccess(bool *emitted, MDefinition *obj,
PropertyName *name, MDefinition *value,
@ -10762,28 +11028,22 @@ IonBuilder::setPropTryInlineAccess(bool *emitted, MDefinition *obj,
return true;
}
BaselineInspector::ShapeVector shapes(alloc());
if (!inspector->maybeShapesForPropertyOp(pc, shapes))
BaselineInspector::ShapeVector nativeShapes(alloc());
BaselineInspector::TypeObjectVector unboxedTypes(alloc());
if (!inspector->maybeInfoForPropertyOp(pc, nativeShapes, unboxedTypes))
return false;
if (shapes.empty()) {
trackOptimizationOutcome(TrackedOutcome::NoShapeInfo);
if (!canInlinePropertyOpShapes(nativeShapes, unboxedTypes))
return true;
}
if (!CanInlinePropertyOpShapes(shapes)) {
trackOptimizationOutcome(TrackedOutcome::InDictionaryMode);
return true;
}
if (shapes.length() == 1) {
if (nativeShapes.length() == 1 && unboxedTypes.empty()) {
spew("Inlining monomorphic SETPROP");
// The Baseline IC was monomorphic, so we inline the property access as
// long as the shape is not in dictionary mode. We cannot be sure
// that the shape is still a lastProperty, and calling Shape::search
// on dictionary mode shapes that aren't lastProperty is invalid.
Shape *objShape = shapes[0];
Shape *objShape = nativeShapes[0];
obj = addShapeGuard(obj, objShape, Bailout_ShapeGuard);
Shape *shape = objShape->searchLinear(NameToId(name));
@ -10798,15 +11058,37 @@ IonBuilder::setPropTryInlineAccess(bool *emitted, MDefinition *obj,
return true;
}
MOZ_ASSERT(shapes.length() > 1);
if (nativeShapes.empty() && unboxedTypes.length() == 1) {
spew("Inlining monomorphic unboxed SETPROP");
types::TypeObject *unboxedType = unboxedTypes[0];
// Failures in this type guard should be treated the same as a shape guard failure.
obj = MGuardObjectType::New(alloc(), obj, unboxedType, /* bailOnEquality = */ false,
Bailout_ShapeGuard);
current->add(obj->toInstruction());
if (failedShapeGuard_)
obj->toGuardObjectType()->setNotMovable();
const UnboxedLayout::Property *property = unboxedType->unboxedLayout().lookup(name);
storeUnboxedProperty(obj, property->offset, property->type, value);
current->push(value);
*emitted = true;
return true;
}
MOZ_ASSERT(nativeShapes.length() + unboxedTypes.length() > 1);
spew("Inlining polymorphic SETPROP");
BaselineInspector::ShapeVector propShapes(alloc());
bool sameSlot;
if (!GetPropertyShapes(NameToId(name), shapes, propShapes, &sameSlot))
if (!GetPropertyShapes(NameToId(name), nativeShapes, propShapes, &sameSlot))
return false;
if (sameSlot) {
if (sameSlot && unboxedTypes.empty()) {
MGuardShapePolymorphic *guard = MGuardShapePolymorphic::New(alloc(), obj);
current->add(guard);
obj = guard;
@ -10814,8 +11096,8 @@ IonBuilder::setPropTryInlineAccess(bool *emitted, MDefinition *obj,
if (failedShapeGuard_)
guard->setNotMovable();
for (size_t i = 0; i < shapes.length(); i++) {
if (!guard->addShape(shapes[i]))
for (size_t i = 0; i < nativeShapes.length(); i++) {
if (!guard->addShape(nativeShapes[i]))
return false;
}
@ -10828,18 +11110,23 @@ IonBuilder::setPropTryInlineAccess(bool *emitted, MDefinition *obj,
return true;
}
MSetPropertyPolymorphic *ins = MSetPropertyPolymorphic::New(alloc(), obj, value);
MSetPropertyPolymorphic *ins = MSetPropertyPolymorphic::New(alloc(), obj, value, name);
current->add(ins);
current->push(value);
for (size_t i = 0; i < shapes.length(); i++) {
Shape *objShape = shapes[i];
for (size_t i = 0; i < nativeShapes.length(); i++) {
Shape *objShape = nativeShapes[i];
Shape *shape = objShape->searchLinear(NameToId(name));
MOZ_ASSERT(shape);
if (!ins->addShape(objShape, shape))
return false;
}
for (size_t i = 0; i < unboxedTypes.length(); i++) {
if (!ins->addUnboxedType(unboxedTypes[i]))
return false;
}
if (objTypes->propertyNeedsBarrier(constraints(), NameToId(name)))
ins->setNeedsBarrier();

Просмотреть файл

@ -12,6 +12,7 @@
#include "mozilla/LinkedList.h"
#include "jit/BaselineInspector.h"
#include "jit/BytecodeAnalysis.h"
#include "jit/IonAnalysis.h"
#include "jit/IonOptimizationLevels.h"
@ -25,7 +26,6 @@ namespace jit {
class CodeGenerator;
class CallInfo;
class BaselineInspector;
class BaselineFrameInspector;
// Records information about a baseline frame for compilation that is stable
@ -423,6 +423,8 @@ class IonBuilder
types::TemporaryTypeSet *types);
bool getPropTryDefiniteSlot(bool *emitted, MDefinition *obj, PropertyName *name,
BarrierKind barrier, types::TemporaryTypeSet *types);
bool getPropTryUnboxed(bool *emitted, MDefinition *obj, PropertyName *name,
BarrierKind barrier, types::TemporaryTypeSet *types);
bool getPropTryCommonGetter(bool *emitted, MDefinition *obj, PropertyName *name,
types::TemporaryTypeSet *types);
bool getPropTryInlineAccess(bool *emitted, MDefinition *obj, PropertyName *name,
@ -453,6 +455,9 @@ class IonBuilder
bool setPropTryDefiniteSlot(bool *emitted, MDefinition *obj,
PropertyName *name, MDefinition *value,
bool barrier, types::TemporaryTypeSet *objTypes);
bool setPropTryUnboxed(bool *emitted, MDefinition *obj,
PropertyName *name, MDefinition *value,
bool barrier, types::TemporaryTypeSet *objTypes);
bool setPropTryInlineAccess(bool *emitted, MDefinition *obj,
PropertyName *name, MDefinition *value,
bool barrier, types::TemporaryTypeSet *objTypes);
@ -878,8 +883,16 @@ class IonBuilder
bool testSingletonPropertyTypes(MDefinition *obj, JSObject *singleton, PropertyName *name,
bool *testObject, bool *testString);
uint32_t getDefiniteSlot(types::TemporaryTypeSet *types, PropertyName *name);
uint32_t getUnboxedOffset(types::TemporaryTypeSet *types, PropertyName *name,
JSValueType *punboxedType);
MInstruction *loadUnboxedProperty(MDefinition *obj, size_t offset, JSValueType unboxedType,
BarrierKind barrier, types::TemporaryTypeSet *types);
MInstruction *storeUnboxedProperty(MDefinition *obj, size_t offset, JSValueType unboxedType,
MDefinition *value);
bool freezePropTypeSets(types::TemporaryTypeSet *types,
JSObject *foundProto, PropertyName *name);
bool canInlinePropertyOpShapes(const BaselineInspector::ShapeVector &nativeShapes,
const BaselineInspector::TypeObjectVector &unboxedTypes);
types::TemporaryTypeSet *bytecodeTypes(jsbytecode *pc);

Просмотреть файл

@ -769,7 +769,6 @@ GenerateReadSlot(JSContext *cx, IonScript *ion, MacroAssembler &masm,
Shape *shape, Register object, TypedOrValueRegister output,
Label *failures = nullptr)
{
MOZ_ASSERT(obj->isNative());
// If there's a single jump to |failures|, we can patch the shape guard
// jump directly. Otherwise, jump to the end of the stub, so there's a
// common point to patch.
@ -781,11 +780,18 @@ GenerateReadSlot(JSContext *cx, IonScript *ion, MacroAssembler &masm,
if (multipleFailureJumps && !failures)
failures = &failures_;
// Guard on the shape of the object.
// Guard on the shape or type of the object, depending on whether it is native.
if (obj->isNative()) {
attacher.branchNextStubOrLabel(masm, Assembler::NotEqual,
Address(object, JSObject::offsetOfShape()),
ImmGCPtr(obj->lastProperty()),
failures);
} else {
attacher.branchNextStubOrLabel(masm, Assembler::NotEqual,
Address(object, JSObject::offsetOfType()),
ImmGCPtr(obj->type()),
failures);
}
// If we need a scratch register, use either an output register or the
// object register. After this point, we cannot jump directly to
@ -876,6 +882,24 @@ GenerateReadSlot(JSContext *cx, IonScript *ion, MacroAssembler &masm,
}
static void
GenerateReadUnboxed(JSContext *cx, IonScript *ion, MacroAssembler &masm,
IonCache::StubAttacher &attacher, JSObject *obj,
const UnboxedLayout::Property *property,
Register object, TypedOrValueRegister output)
{
// Guard on the type of the object.
attacher.branchNextStub(masm, Assembler::NotEqual,
Address(object, JSObject::offsetOfType()),
ImmGCPtr(obj->type()));
Address address(object, UnboxedPlainObject::offsetOfData() + property->offset);
masm.loadUnboxedProperty(address, property->type, output);
attacher.jumpRejoin(masm);
}
static bool
EmitGetterCall(JSContext *cx, MacroAssembler &masm,
IonCache::StubAttacher &attacher, JSObject *obj,
@ -1170,7 +1194,7 @@ CanAttachNativeGetProp(typename GetPropCache::Context cx, const GetPropCache &ca
MutableHandleNativeObject holder, MutableHandleShape shape,
bool skipArrayLen = false)
{
if (!obj || !obj->isNative())
if (!obj)
return GetPropertyIC::CanAttachNone;
// The lookup needs to be universally pure, otherwise we risk calling hooks out
@ -1305,6 +1329,30 @@ GetPropertyIC::tryAttachNative(JSContext *cx, HandleScript outerScript, IonScrip
return linkAndAttachStub(cx, masm, attacher, ion, attachKind);
}
bool
GetPropertyIC::tryAttachUnboxed(JSContext *cx, HandleScript outerScript, IonScript *ion,
HandleObject obj, HandlePropertyName name,
void *returnAddr, bool *emitted)
{
MOZ_ASSERT(canAttachStub());
MOZ_ASSERT(!*emitted);
MOZ_ASSERT(outerScript->ionScript() == ion);
if (!obj->is<UnboxedPlainObject>())
return true;
const UnboxedLayout::Property *property = obj->as<UnboxedPlainObject>().layout().lookup(name);
if (!property)
return true;
*emitted = true;
MacroAssembler masm(cx, ion, outerScript, profilerLeavePc_);
RepatchStubAppender attacher(*this);
GenerateReadUnboxed(cx, ion, masm, attacher, obj, property, object(), output());
return linkAndAttachStub(cx, masm, attacher, ion, "read unboxed");
}
bool
GetPropertyIC::tryAttachTypedArrayLength(JSContext *cx, HandleScript outerScript, IonScript *ion,
HandleObject obj, HandlePropertyName name, bool *emitted)
@ -1729,9 +1777,15 @@ GetPropertyIC::tryAttachStub(JSContext *cx, HandleScript outerScript, IonScript
if (!*emitted && !tryAttachNative(cx, outerScript, ion, obj, name, returnAddr, emitted))
return false;
if (!*emitted && !tryAttachUnboxed(cx, outerScript, ion, obj, name, returnAddr, emitted))
return false;
if (!*emitted && !tryAttachTypedArrayLength(cx, outerScript, ion, obj, name, emitted))
return false;
if (!*emitted)
JitSpew(JitSpew_IonIC, "Failed to attach GETPROP cache");
return true;
}
@ -1836,6 +1890,26 @@ IonCache::destroy()
{
}
// Jump to failure if a value being written is not a property for obj/id.
// This might clobber |object|.
static void
CheckTypeSetForWrite(MacroAssembler &masm, JSObject *obj, jsid id,
Register object, ConstantOrRegister value, Label *failure)
{
TypedOrValueRegister valReg = value.reg();
types::TypeObject *type = obj->type();
if (type->unknownProperties())
return;
types::HeapTypeSet *propTypes = type->maybeGetProperty(id);
MOZ_ASSERT(propTypes);
// guardTypeSet can read from type sets without triggering read barriers.
types::TypeSet::readBarrier(propTypes);
Register scratch = object;
masm.guardTypeSet(valReg, propTypes, BarrierKind::TypeSet, scratch, failure);
}
static void
GenerateSetSlot(JSContext *cx, MacroAssembler &masm, IonCache::StubAttacher &attacher,
NativeObject *obj, Shape *shape, Register object, ConstantOrRegister value,
@ -1861,18 +1935,8 @@ GenerateSetSlot(JSContext *cx, MacroAssembler &masm, IonCache::StubAttacher &att
ImmGCPtr(type), &failures);
if (checkTypeset) {
TypedOrValueRegister valReg = value.reg();
types::HeapTypeSet *propTypes = type->maybeGetProperty(shape->propid());
MOZ_ASSERT(propTypes);
MOZ_ASSERT(!propTypes->unknown());
// guardTypeSet can read from type sets without triggering read barriers.
types::TypeSet::readBarrier(propTypes);
Register scratchReg = object;
masm.push(scratchReg);
masm.guardTypeSet(valReg, propTypes, BarrierKind::TypeSet, scratchReg, &barrierFailure);
masm.push(object);
CheckTypeSetForWrite(masm, obj, shape->propid(), object, value, &barrierFailure);
masm.pop(object);
}
}
@ -2420,17 +2484,7 @@ GenerateAddSlot(JSContext *cx, MacroAssembler &masm, IonCache::StubAttacher &att
// Guard that the incoming value is in the type set for the property
// if a type barrier is required.
if (checkTypeset) {
TypedOrValueRegister valReg = value.reg();
types::TypeObject *type = obj->type();
types::HeapTypeSet *propTypes = type->maybeGetProperty(obj->lastProperty()->propid());
MOZ_ASSERT(propTypes);
MOZ_ASSERT(!propTypes->unknown());
// guardTypeSet can read from type sets without triggering read barriers.
types::TypeSet::readBarrier(propTypes);
Register scratchReg = object;
masm.guardTypeSet(valReg, propTypes, BarrierKind::TypeSet, scratchReg, &failuresPopObject);
CheckTypeSetForWrite(masm, obj, obj->lastProperty()->propid(), object, value, &failuresPopObject);
masm.loadPtr(Address(StackPointer, 0), object);
}
@ -2684,6 +2738,85 @@ CanAttachNativeSetProp(JSContext *cx, HandleObject obj, HandleId id, ConstantOrR
return SetPropertyIC::CanAttachNone;
}
static void
GenerateSetUnboxed(JSContext *cx, MacroAssembler &masm, IonCache::StubAttacher &attacher,
JSObject *obj, jsid id, uint32_t unboxedOffset, JSValueType unboxedType,
Register object, ConstantOrRegister value, bool checkTypeset)
{
Label failure, failurePopObject;
// Guard on the type of the object.
masm.branchPtr(Assembler::NotEqual,
Address(object, JSObject::offsetOfType()),
ImmGCPtr(obj->type()), &failure);
if (checkTypeset) {
masm.push(object);
CheckTypeSetForWrite(masm, obj, id, object, value, &failurePopObject);
masm.pop(object);
}
Address address(object, UnboxedPlainObject::offsetOfData() + unboxedOffset);
if (cx->zone()->needsIncrementalBarrier()) {
if (unboxedType == JSVAL_TYPE_OBJECT)
masm.callPreBarrier(address, MIRType_Object);
else if (unboxedType == JSVAL_TYPE_STRING)
masm.callPreBarrier(address, MIRType_String);
else
MOZ_ASSERT(!UnboxedTypeNeedsPreBarrier(unboxedType));
}
// If the unboxed object's type has known properties, then instances have
// never been converted to native objects and the type set check performed
// above ensures the value being written can be stored in the unboxed
// object.
Label *storeFailure = obj->type()->unknownProperties() ? &failure : nullptr;
masm.storeUnboxedProperty(address, unboxedType, value, storeFailure);
attacher.jumpRejoin(masm);
masm.bind(&failurePopObject);
masm.pop(object);
masm.bind(&failure);
attacher.jumpNextStub(masm);
}
bool
SetPropertyIC::attachSetUnboxed(JSContext *cx, HandleScript outerScript, IonScript *ion,
HandleObject obj, HandleId id,
uint32_t unboxedOffset, JSValueType unboxedType,
bool checkTypeset)
{
MacroAssembler masm(cx, ion, outerScript, profilerLeavePc_);
RepatchStubAppender attacher(*this);
GenerateSetUnboxed(cx, masm, attacher, obj, id, unboxedOffset, unboxedType,
object(), value(), needsTypeBarrier());
return linkAndAttachStub(cx, masm, attacher, ion, "set_unboxed");
}
static bool
CanAttachSetUnboxed(JSContext *cx, HandleObject obj, HandleId id, ConstantOrRegister val,
bool needsTypeBarrier, bool *checkTypeset,
uint32_t *unboxedOffset, JSValueType *unboxedType)
{
if (!obj->is<UnboxedPlainObject>())
return false;
const UnboxedLayout::Property *property = obj->as<UnboxedPlainObject>().layout().lookup(id);
if (property) {
if (needsTypeBarrier && !CanInlineSetPropTypeCheck(obj, id, val, checkTypeset))
return false;
*unboxedOffset = property->offset;
*unboxedType = property->type;
return true;
}
return false;
}
bool
SetPropertyIC::update(JSContext *cx, size_t cacheIndex, HandleObject obj,
HandleValue value)
@ -2748,6 +2881,21 @@ SetPropertyIC::update(JSContext *cx, size_t cacheIndex, HandleObject obj,
return false;
addedSetterStub = true;
}
checkTypeset = false;
uint32_t unboxedOffset;
JSValueType unboxedType;
if (!addedSetterStub && CanAttachSetUnboxed(cx, obj, id, cache.value(),
cache.needsTypeBarrier(),
&checkTypeset, &unboxedOffset, &unboxedType))
{
if (!cache.attachSetUnboxed(cx, script, ion, obj, id, unboxedOffset, unboxedType,
checkTypeset))
{
return false;
}
addedSetterStub = true;
}
}
uint32_t oldSlots = obj->is<NativeObject>() ? obj->as<NativeObject>().numDynamicSlots() : 0;
@ -2767,8 +2915,12 @@ SetPropertyIC::update(JSContext *cx, size_t cacheIndex, HandleObject obj,
RootedNativeObject nobj(cx, &obj->as<NativeObject>());
if (!cache.attachAddSlot(cx, script, ion, nobj, oldShape, oldType, checkTypeset))
return false;
addedSetterStub = true;
}
if (!addedSetterStub)
JitSpew(JitSpew_IonIC, "Failed to attach SETPROP cache");
return true;
}

Просмотреть файл

@ -669,6 +669,10 @@ class GetPropertyIC : public RepatchIonCache
HandleObject obj, HandlePropertyName name,
void *returnAddr, bool *emitted);
bool tryAttachUnboxed(JSContext *cx, HandleScript outerScript, IonScript *ion,
HandleObject obj, HandlePropertyName name,
void *returnAddr, bool *emitted);
bool tryAttachTypedArrayLength(JSContext *cx, HandleScript outerScript, IonScript *ion,
HandleObject obj, HandlePropertyName name, bool *emitted);
@ -747,6 +751,11 @@ class SetPropertyIC : public RepatchIonCache
HandleNativeObject obj, HandleShape oldShape, HandleTypeObject oldType,
bool checkTypeset);
bool attachSetUnboxed(JSContext *cx, HandleScript outerScript, IonScript *ion,
HandleObject obj, HandleId id,
uint32_t unboxedOffset, JSValueType unboxedType,
bool checkTypeset);
bool attachGenericProxy(JSContext *cx, HandleScript outerScript, IonScript *ion,
void *returnAddr);

Просмотреть файл

@ -2619,12 +2619,12 @@ LIRGenerator::visitLoadUnboxedObjectOrNull(MLoadUnboxedObjectOrNull *ins)
if (ins->type() == MIRType_Object) {
LLoadUnboxedPointerT *lir = new(alloc()) LLoadUnboxedPointerT(useRegister(ins->elements()),
useRegisterOrConstant(ins->index()));
if (ins->bailOnNull())
if (ins->nullBehavior() == MLoadUnboxedObjectOrNull::BailOnNull)
assignSnapshot(lir, Bailout_TypeBarrierO);
define(lir, ins);
} else {
MOZ_ASSERT(ins->type() == MIRType_Value);
MOZ_ASSERT(!ins->bailOnNull());
MOZ_ASSERT(ins->nullBehavior() != MLoadUnboxedObjectOrNull::BailOnNull);
LLoadUnboxedPointerV *lir = new(alloc()) LLoadUnboxedPointerV(useRegister(ins->elements()),
useRegisterOrConstant(ins->index()));
@ -2848,7 +2848,7 @@ LIRGenerator::visitLoadTypedArrayElement(MLoadTypedArrayElement *ins)
const LUse elements = useRegister(ins->elements());
const LAllocation index = useRegisterOrConstant(ins->index());
MOZ_ASSERT(IsNumberType(ins->type()));
MOZ_ASSERT(IsNumberType(ins->type()) || ins->type() == MIRType_Boolean);
// We need a temp register for Uint32Array with known double result.
LDefinition tempDef = LDefinition::BogusTemp();

Просмотреть файл

@ -3647,8 +3647,9 @@ MNewObject::shouldUseVM() const
bool
MCreateThisWithTemplate::canRecoverOnBailout() const
{
MOZ_ASSERT(!templateObject()->denseElementsAreCopyOnWrite());
MOZ_ASSERT(!templateObject()->is<ArrayObject>());
MOZ_ASSERT(templateObject()->is<PlainObject>() || templateObject()->is<UnboxedPlainObject>());
MOZ_ASSERT_IF(templateObject()->is<PlainObject>(),
!templateObject()->as<PlainObject>().denseElementsAreCopyOnWrite());
return true;
}
@ -3661,7 +3662,7 @@ MObjectState::MObjectState(MDefinition *obj)
if (obj->isNewObject())
templateObject = obj->toNewObject()->templateObject();
else if (obj->isCreateThisWithTemplate())
templateObject = obj->toCreateThisWithTemplate()->templateObject();
templateObject = &obj->toCreateThisWithTemplate()->templateObject()->as<PlainObject>();
else
templateObject = obj->toNewCallObject()->templateObject();
numSlots_ = templateObject->slotSpan();
@ -4727,10 +4728,12 @@ AddTypeGuard(TempAllocator &alloc, MBasicBlock *current, MDefinition *obj,
{
MInstruction *guard;
if (type->isTypeObject())
guard = MGuardObjectType::New(alloc, obj, type->asTypeObject(), bailOnEquality);
else
if (type->isTypeObject()) {
guard = MGuardObjectType::New(alloc, obj, type->asTypeObject(), bailOnEquality,
Bailout_ObjectIdentityOrTypeGuard);
} else {
guard = MGuardObjectIdentity::New(alloc, obj, type->asSingleObject(), bailOnEquality);
}
current->add(guard);

Просмотреть файл

@ -4273,8 +4273,8 @@ class MCreateThisWithTemplate
}
// Template for |this|, provided by TI.
PlainObject *templateObject() const {
return &getOperand(0)->toConstant()->value().toObject().as<PlainObject>();
JSObject *templateObject() const {
return &getOperand(0)->toConstant()->value().toObject();
}
gc::InitialHeap initialHeap() const {
@ -8059,21 +8059,29 @@ class MLoadUnboxedObjectOrNull
: public MBinaryInstruction,
public SingleObjectPolicy::Data
{
bool bailOnNull_;
public:
enum NullBehavior {
HandleNull,
BailOnNull,
NullNotPossible
};
private:
NullBehavior nullBehavior_;
int32_t offsetAdjustment_;
MLoadUnboxedObjectOrNull(MDefinition *elements, MDefinition *index,
bool bailOnNull, int32_t offsetAdjustment)
NullBehavior nullBehavior, int32_t offsetAdjustment)
: MBinaryInstruction(elements, index),
bailOnNull_(bailOnNull),
nullBehavior_(nullBehavior),
offsetAdjustment_(offsetAdjustment)
{
if (bailOnNull) {
if (nullBehavior == BailOnNull) {
// Don't eliminate loads which bail out on a null pointer, for the
// same reason as MLoadElement.
setGuard();
}
setResultType(bailOnNull ? MIRType_Object : MIRType_Value);
setResultType(nullBehavior == HandleNull ? MIRType_Value : MIRType_Object);
setMovable();
MOZ_ASSERT(IsValidElementsType(elements, offsetAdjustment));
MOZ_ASSERT(index->type() == MIRType_Int32);
@ -8084,8 +8092,9 @@ class MLoadUnboxedObjectOrNull
static MLoadUnboxedObjectOrNull *New(TempAllocator &alloc,
MDefinition *elements, MDefinition *index,
bool bailOnNull, int32_t offsetAdjustment) {
return new(alloc) MLoadUnboxedObjectOrNull(elements, index, bailOnNull, offsetAdjustment);
NullBehavior nullBehavior, int32_t offsetAdjustment) {
return new(alloc) MLoadUnboxedObjectOrNull(elements, index, nullBehavior,
offsetAdjustment);
}
MDefinition *elements() const {
@ -8094,20 +8103,20 @@ class MLoadUnboxedObjectOrNull
MDefinition *index() const {
return getOperand(1);
}
bool bailOnNull() const {
return bailOnNull_;
NullBehavior nullBehavior() const {
return nullBehavior_;
}
int32_t offsetAdjustment() const {
return offsetAdjustment_;
}
bool fallible() const {
return bailOnNull();
return nullBehavior() == BailOnNull;
}
bool congruentTo(const MDefinition *ins) const MOZ_OVERRIDE {
if (!ins->isLoadUnboxedObjectOrNull())
return false;
const MLoadUnboxedObjectOrNull *other = ins->toLoadUnboxedObjectOrNull();
if (bailOnNull() != other->bailOnNull())
if (nullBehavior() != other->nullBehavior())
return false;
if (offsetAdjustment() != other->offsetAdjustment())
return false;
@ -8140,7 +8149,8 @@ class MLoadUnboxedString
INSTRUCTION_HEADER(LoadUnboxedString)
static MLoadUnboxedString *New(TempAllocator &alloc,
MDefinition *elements, MDefinition *index, int32_t offsetAdjustment) {
MDefinition *elements, MDefinition *index,
int32_t offsetAdjustment = 0) {
return new(alloc) MLoadUnboxedString(elements, index, offsetAdjustment);
}
@ -8331,7 +8341,7 @@ class MStoreUnboxedObjectOrNull
static MStoreUnboxedObjectOrNull *New(TempAllocator &alloc,
MDefinition *elements, MDefinition *index,
MDefinition *value, MDefinition *typedObj,
int32_t offsetAdjustment) {
int32_t offsetAdjustment = 0) {
return new(alloc) MStoreUnboxedObjectOrNull(elements, index, value, typedObj,
offsetAdjustment);
}
@ -8386,7 +8396,7 @@ class MStoreUnboxedString
static MStoreUnboxedString *New(TempAllocator &alloc,
MDefinition *elements, MDefinition *index,
MDefinition *value, int32_t offsetAdjustment) {
MDefinition *value, int32_t offsetAdjustment = 0) {
return new(alloc) MStoreUnboxedString(elements, index, value, offsetAdjustment);
}
MDefinition *elements() const {
@ -8581,14 +8591,16 @@ class MLoadTypedArrayElement
Scalar::Type arrayType_;
bool requiresBarrier_;
int32_t offsetAdjustment_;
bool canonicalizeDoubles_;
MLoadTypedArrayElement(MDefinition *elements, MDefinition *index,
Scalar::Type arrayType, MemoryBarrierRequirement requiresBarrier,
int32_t offsetAdjustment)
int32_t offsetAdjustment, bool canonicalizeDoubles)
: MBinaryInstruction(elements, index),
arrayType_(arrayType),
requiresBarrier_(requiresBarrier == DoesRequireMemoryBarrier),
offsetAdjustment_(offsetAdjustment)
offsetAdjustment_(offsetAdjustment),
canonicalizeDoubles_(canonicalizeDoubles)
{
setResultType(MIRType_Value);
if (requiresBarrier_)
@ -8606,10 +8618,12 @@ class MLoadTypedArrayElement
static MLoadTypedArrayElement *New(TempAllocator &alloc, MDefinition *elements, MDefinition *index,
Scalar::Type arrayType,
MemoryBarrierRequirement requiresBarrier=DoesNotRequireMemoryBarrier,
int32_t offsetAdjustment = 0)
int32_t offsetAdjustment = 0,
bool canonicalizeDoubles = true)
{
return new(alloc) MLoadTypedArrayElement(elements, index, arrayType,
requiresBarrier, offsetAdjustment);
requiresBarrier, offsetAdjustment,
canonicalizeDoubles);
}
Scalar::Type arrayType() const {
@ -8622,6 +8636,9 @@ class MLoadTypedArrayElement
bool requiresMemoryBarrier() const {
return requiresBarrier_;
}
bool canonicalizeDoubles() const {
return canonicalizeDoubles_;
}
MDefinition *elements() const {
return getOperand(0);
}
@ -8649,6 +8666,8 @@ class MLoadTypedArrayElement
return false;
if (offsetAdjustment() != other->offsetAdjustment())
return false;
if (canonicalizeDoubles() != other->canonicalizeDoubles())
return false;
return congruentIfOperandsEqual(other);
}
@ -9340,8 +9359,8 @@ class MGetPropertyCache
bool updateForReplacement(MDefinition *ins) MOZ_OVERRIDE;
};
// Emit code to load a value from an object's slots if its shape matches
// one of the shapes observed by the baseline IC, else bails out.
// Emit code to load a value from an object if its shape/type matches one of
// the shapes/types observed by the baseline IC, else bails out.
class MGetPropertyPolymorphic
: public MUnaryInstruction,
public SingleObjectPolicy::Data
@ -9354,12 +9373,14 @@ class MGetPropertyPolymorphic
Shape *shape;
};
Vector<Entry, 4, JitAllocPolicy> shapes_;
Vector<Entry, 4, JitAllocPolicy> nativeShapes_;
Vector<types::TypeObject *, 4, JitAllocPolicy> unboxedTypes_;
AlwaysTenuredPropertyName name_;
MGetPropertyPolymorphic(TempAllocator &alloc, MDefinition *obj, PropertyName *name)
: MUnaryInstruction(obj),
shapes_(alloc),
nativeShapes_(alloc),
unboxedTypes_(alloc),
name_(name)
{
setGuard();
@ -9367,10 +9388,6 @@ class MGetPropertyPolymorphic
setResultType(MIRType_Value);
}
PropertyName *name() const {
return name_;
}
public:
INSTRUCTION_HEADER(GetPropertyPolymorphic)
@ -9390,22 +9407,35 @@ class MGetPropertyPolymorphic
Entry entry;
entry.objShape = objShape;
entry.shape = shape;
return shapes_.append(entry);
return nativeShapes_.append(entry);
}
bool addUnboxedType(types::TypeObject *type) {
return unboxedTypes_.append(type);
}
size_t numShapes() const {
return shapes_.length();
return nativeShapes_.length();
}
Shape *objShape(size_t i) const {
return shapes_[i].objShape;
return nativeShapes_[i].objShape;
}
Shape *shape(size_t i) const {
return shapes_[i].shape;
return nativeShapes_[i].shape;
}
size_t numUnboxedTypes() const {
return unboxedTypes_.length();
}
types::TypeObject *unboxedType(size_t i) const {
return unboxedTypes_[i];
}
PropertyName *name() const {
return name_;
}
MDefinition *obj() const {
return getOperand(0);
}
AliasSet getAliasSet() const MOZ_OVERRIDE {
return AliasSet::Load(AliasSet::ObjectFields | AliasSet::FixedSlot | AliasSet::DynamicSlot);
return AliasSet::Load(AliasSet::ObjectFields | AliasSet::FixedSlot | AliasSet::DynamicSlot |
(unboxedTypes_.empty() ? 0 : (AliasSet::TypedArrayElement | AliasSet::Element)));
}
bool mightAlias(const MDefinition *store) const MOZ_OVERRIDE;
@ -9425,12 +9455,17 @@ class MSetPropertyPolymorphic
Shape *shape;
};
Vector<Entry, 4, JitAllocPolicy> shapes_;
Vector<Entry, 4, JitAllocPolicy> nativeShapes_;
Vector<types::TypeObject *, 4, JitAllocPolicy> unboxedTypes_;
AlwaysTenuredPropertyName name_;
bool needsBarrier_;
MSetPropertyPolymorphic(TempAllocator &alloc, MDefinition *obj, MDefinition *value)
MSetPropertyPolymorphic(TempAllocator &alloc, MDefinition *obj, MDefinition *value,
PropertyName *name)
: MBinaryInstruction(obj, value),
shapes_(alloc),
nativeShapes_(alloc),
unboxedTypes_(alloc),
name_(name),
needsBarrier_(false)
{
}
@ -9438,24 +9473,37 @@ class MSetPropertyPolymorphic
public:
INSTRUCTION_HEADER(SetPropertyPolymorphic)
static MSetPropertyPolymorphic *New(TempAllocator &alloc, MDefinition *obj, MDefinition *value) {
return new(alloc) MSetPropertyPolymorphic(alloc, obj, value);
static MSetPropertyPolymorphic *New(TempAllocator &alloc, MDefinition *obj, MDefinition *value,
PropertyName *name) {
return new(alloc) MSetPropertyPolymorphic(alloc, obj, value, name);
}
bool addShape(Shape *objShape, Shape *shape) {
Entry entry;
entry.objShape = objShape;
entry.shape = shape;
return shapes_.append(entry);
return nativeShapes_.append(entry);
}
bool addUnboxedType(types::TypeObject *type) {
return unboxedTypes_.append(type);
}
size_t numShapes() const {
return shapes_.length();
return nativeShapes_.length();
}
Shape *objShape(size_t i) const {
return shapes_[i].objShape;
return nativeShapes_[i].objShape;
}
Shape *shape(size_t i) const {
return shapes_[i].shape;
return nativeShapes_[i].shape;
}
size_t numUnboxedTypes() const {
return unboxedTypes_.length();
}
types::TypeObject *unboxedType(size_t i) const {
return unboxedTypes_[i];
}
PropertyName *name() const {
return name_;
}
MDefinition *obj() const {
return getOperand(0);
@ -9470,7 +9518,8 @@ class MSetPropertyPolymorphic
needsBarrier_ = true;
}
AliasSet getAliasSet() const MOZ_OVERRIDE {
return AliasSet::Store(AliasSet::ObjectFields | AliasSet::FixedSlot | AliasSet::DynamicSlot);
return AliasSet::Store(AliasSet::ObjectFields | AliasSet::FixedSlot | AliasSet::DynamicSlot |
(unboxedTypes_.empty() ? 0 : (AliasSet::TypedArrayElement | AliasSet::Element)));
}
};
@ -9805,11 +9854,14 @@ class MGuardObjectType
{
AlwaysTenured<types::TypeObject *> typeObject_;
bool bailOnEquality_;
BailoutKind bailoutKind_;
MGuardObjectType(MDefinition *obj, types::TypeObject *typeObject, bool bailOnEquality)
MGuardObjectType(MDefinition *obj, types::TypeObject *typeObject, bool bailOnEquality,
BailoutKind bailoutKind)
: MUnaryInstruction(obj),
typeObject_(typeObject),
bailOnEquality_(bailOnEquality)
bailOnEquality_(bailOnEquality),
bailoutKind_(bailoutKind)
{
setGuard();
setMovable();
@ -9820,8 +9872,8 @@ class MGuardObjectType
INSTRUCTION_HEADER(GuardObjectType)
static MGuardObjectType *New(TempAllocator &alloc, MDefinition *obj, types::TypeObject *typeObject,
bool bailOnEquality) {
return new(alloc) MGuardObjectType(obj, typeObject, bailOnEquality);
bool bailOnEquality, BailoutKind bailoutKind) {
return new(alloc) MGuardObjectType(obj, typeObject, bailOnEquality, bailoutKind);
}
MDefinition *obj() const {
@ -9833,6 +9885,9 @@ class MGuardObjectType
bool bailOnEquality() const {
return bailOnEquality_;
}
BailoutKind bailoutKind() const {
return bailoutKind_;
}
bool congruentTo(const MDefinition *ins) const MOZ_OVERRIDE {
if (!ins->isGuardObjectType())
return false;
@ -9840,6 +9895,8 @@ class MGuardObjectType
return false;
if (bailOnEquality() != ins->toGuardObjectType()->bailOnEquality())
return false;
if (bailoutKind() != ins->toGuardObjectType()->bailoutKind())
return false;
return congruentIfOperandsEqual(ins);
}
AliasSet getAliasSet() const MOZ_OVERRIDE {

Просмотреть файл

@ -306,7 +306,7 @@ MacroAssembler::storeToTypedFloatArray(Scalar::Type arrayType, FloatRegister val
template<typename T>
void
MacroAssembler::loadFromTypedArray(Scalar::Type arrayType, const T &src, AnyRegister dest, Register temp,
Label *fail)
Label *fail, bool canonicalizeDoubles)
{
switch (arrayType) {
case Scalar::Int8:
@ -344,6 +344,7 @@ MacroAssembler::loadFromTypedArray(Scalar::Type arrayType, const T &src, AnyRegi
break;
case Scalar::Float64:
loadDouble(src, dest.fpu());
if (canonicalizeDoubles)
canonicalizeDouble(dest.fpu());
break;
default:
@ -352,9 +353,9 @@ MacroAssembler::loadFromTypedArray(Scalar::Type arrayType, const T &src, AnyRegi
}
template void MacroAssembler::loadFromTypedArray(Scalar::Type arrayType, const Address &src, AnyRegister dest,
Register temp, Label *fail);
Register temp, Label *fail, bool canonicalizeDoubles);
template void MacroAssembler::loadFromTypedArray(Scalar::Type arrayType, const BaseIndex &src, AnyRegister dest,
Register temp, Label *fail);
Register temp, Label *fail, bool canonicalizeDoubles);
template<typename T>
void
@ -621,6 +622,214 @@ MacroAssembler::atomicBinopToTypedIntArray(AtomicOp op, Scalar::Type arrayType,
const Register &value, const BaseIndex &mem,
Register temp1, Register temp2, AnyRegister output);
template <typename T>
void
MacroAssembler::loadUnboxedProperty(T address, JSValueType type, TypedOrValueRegister output)
{
switch (type) {
case JSVAL_TYPE_BOOLEAN:
case JSVAL_TYPE_INT32:
case JSVAL_TYPE_STRING: {
Register outReg;
if (output.hasValue()) {
outReg = output.valueReg().scratchReg();
} else {
MOZ_ASSERT(output.type() == MIRTypeFromValueType(type));
outReg = output.typedReg().gpr();
}
switch (type) {
case JSVAL_TYPE_BOOLEAN:
load8ZeroExtend(address, outReg);
break;
case JSVAL_TYPE_INT32:
load32(address, outReg);
break;
case JSVAL_TYPE_STRING:
loadPtr(address, outReg);
break;
default:
MOZ_CRASH();
}
if (output.hasValue())
tagValue(type, outReg, output.valueReg());
break;
}
case JSVAL_TYPE_OBJECT:
if (output.hasValue()) {
Register scratch = output.valueReg().scratchReg();
loadPtr(address, scratch);
Label notNull, done;
branchPtr(Assembler::NotEqual, scratch, ImmWord(0), &notNull);
moveValue(NullValue(), output.valueReg());
jump(&done);
bind(&notNull);
tagValue(JSVAL_TYPE_OBJECT, scratch, output.valueReg());
bind(&done);
} else {
// Reading null can't be possible here, as otherwise the result
// would be a value (either because null has been read before or
// because there is a barrier).
Register reg = output.typedReg().gpr();
loadPtr(address, reg);
#ifdef DEBUG
Label ok;
branchTestPtr(Assembler::NonZero, reg, reg, &ok);
assumeUnreachable("Null not possible");
bind(&ok);
#endif
}
break;
case JSVAL_TYPE_DOUBLE:
// Note: doubles in unboxed objects are not accessed through other
// views and do not need canonicalization.
if (output.hasValue())
loadValue(address, output.valueReg());
else
loadDouble(address, output.typedReg().fpu());
break;
default:
MOZ_CRASH();
}
}
template void
MacroAssembler::loadUnboxedProperty(Address address, JSValueType type,
TypedOrValueRegister output);
template void
MacroAssembler::loadUnboxedProperty(BaseIndex address, JSValueType type,
TypedOrValueRegister output);
template <typename T>
void
MacroAssembler::storeUnboxedProperty(T address, JSValueType type,
ConstantOrRegister value, Label *failure)
{
switch (type) {
case JSVAL_TYPE_BOOLEAN:
if (value.constant()) {
if (value.value().isBoolean())
store8(Imm32(value.value().toBoolean()), address);
else
jump(failure);
} else if (value.reg().hasTyped()) {
if (value.reg().type() == MIRType_Boolean)
store8(value.reg().typedReg().gpr(), address);
else
jump(failure);
} else {
if (failure)
branchTestBoolean(Assembler::NotEqual, value.reg().valueReg(), failure);
storeUnboxedPayload(value.reg().valueReg(), address, /* width = */ 1);
}
break;
case JSVAL_TYPE_INT32:
if (value.constant()) {
if (value.value().isInt32())
store32(Imm32(value.value().toInt32()), address);
else
jump(failure);
} else if (value.reg().hasTyped()) {
if (value.reg().type() == MIRType_Int32)
store32(value.reg().typedReg().gpr(), address);
else
jump(failure);
} else {
if (failure)
branchTestInt32(Assembler::NotEqual, value.reg().valueReg(), failure);
storeUnboxedPayload(value.reg().valueReg(), address, /* width = */ 4);
}
break;
case JSVAL_TYPE_DOUBLE:
if (value.constant()) {
if (value.value().isNumber()) {
loadConstantDouble(value.value().toNumber(), ScratchDoubleReg);
storeDouble(ScratchDoubleReg, address);
} else {
jump(failure);
}
} else if (value.reg().hasTyped()) {
if (value.reg().type() == MIRType_Int32) {
convertInt32ToDouble(value.reg().typedReg().gpr(), ScratchDoubleReg);
storeDouble(ScratchDoubleReg, address);
} else if (value.reg().type() == MIRType_Double) {
storeDouble(value.reg().typedReg().fpu(), address);
} else {
jump(failure);
}
} else {
if (failure)
branchTestNumber(Assembler::NotEqual, value.reg().valueReg(), failure);
unboxValue(value.reg().valueReg(), AnyRegister(ScratchDoubleReg));
storeDouble(ScratchDoubleReg, address);
}
break;
case JSVAL_TYPE_OBJECT:
if (value.constant()) {
if (value.value().isObjectOrNull())
storePtr(ImmGCPtr(value.value().toObjectOrNull()), address);
else
jump(failure);
} else if (value.reg().hasTyped()) {
MOZ_ASSERT(value.reg().type() != MIRType_Null);
if (value.reg().type() == MIRType_Object)
storePtr(value.reg().typedReg().gpr(), address);
else
jump(failure);
} else {
if (failure) {
Label ok;
branchTestNull(Assembler::Equal, value.reg().valueReg(), &ok);
branchTestObject(Assembler::NotEqual, value.reg().valueReg(), failure);
bind(&ok);
}
storeUnboxedPayload(value.reg().valueReg(), address, /* width = */ sizeof(uintptr_t));
}
break;
case JSVAL_TYPE_STRING:
if (value.constant()) {
if (value.value().isString())
storePtr(ImmGCPtr(value.value().toString()), address);
else
jump(failure);
} else if (value.reg().hasTyped()) {
if (value.reg().type() == MIRType_String)
storePtr(value.reg().typedReg().gpr(), address);
else
jump(failure);
} else {
if (failure)
branchTestString(Assembler::NotEqual, value.reg().valueReg(), failure);
storeUnboxedPayload(value.reg().valueReg(), address, /* width = */ sizeof(uintptr_t));
}
break;
default:
MOZ_CRASH();
}
}
template void
MacroAssembler::storeUnboxedProperty(Address address, JSValueType type,
ConstantOrRegister value, Label *failure);
template void
MacroAssembler::storeUnboxedProperty(BaseIndex address, JSValueType type,
ConstantOrRegister value, Label *failure);
// Inlined version of gc::CheckAllocatorState that checks the bare essentials
// and bails for anything that cannot be handled with our jit allocators.
void
@ -780,22 +989,22 @@ MacroAssembler::allocateObject(Register result, Register slots, gc::AllocKind al
callFreeStub(slots);
jump(fail);
bind(&success);
breakpoint();
}
void
MacroAssembler::newGCThing(Register result, Register temp, NativeObject *templateObj,
MacroAssembler::newGCThing(Register result, Register temp, JSObject *templateObj,
gc::InitialHeap initialHeap, Label *fail)
{
// This method does not initialize the object: if external slots get
// allocated into |temp|, there is no easy way for us to ensure the caller
// frees them. Instead just assert this case does not happen.
MOZ_ASSERT(!templateObj->numDynamicSlots());
MOZ_ASSERT_IF(templateObj->isNative(), !templateObj->as<NativeObject>().numDynamicSlots());
gc::AllocKind allocKind = templateObj->asTenured().getAllocKind();
MOZ_ASSERT(allocKind >= gc::FINALIZE_OBJECT0 && allocKind <= gc::FINALIZE_OBJECT_LAST);
allocateObject(result, temp, allocKind, templateObj->numDynamicSlots(), initialHeap, fail);
allocateObject(result, temp, allocKind, 0, initialHeap, fail);
}
void
@ -1030,18 +1239,37 @@ MacroAssembler::initGCThing(Register obj, Register slots, JSObject *templateObj,
}
}
} else if (templateObj->is<InlineTypedObject>()) {
InlineTypedObject *ntemplate = &templateObj->as<InlineTypedObject>();
size_t nbytes = templateObj->as<InlineTypedObject>().size();
const uint8_t *memory = templateObj->as<InlineTypedObject>().inlineTypedMem();
// Memcpy the contents of the template object to the new object.
size_t nbytes = ntemplate->size();
size_t offset = 0;
while (nbytes) {
uintptr_t value = *(uintptr_t *)(ntemplate->inlineTypedMem() + offset);
uintptr_t value = *(uintptr_t *)(memory + offset);
storePtr(ImmWord(value),
Address(obj, InlineTypedObject::offsetOfDataStart() + offset));
nbytes = (nbytes < sizeof(uintptr_t)) ? 0 : nbytes - sizeof(uintptr_t);
offset += sizeof(uintptr_t);
}
} else if (templateObj->is<UnboxedPlainObject>()) {
const UnboxedLayout &layout = templateObj->as<UnboxedPlainObject>().layout();
// Initialize reference fields of the object, per UnboxedPlainObject::create.
if (const int32_t *list = layout.traceList()) {
while (*list != -1) {
storePtr(ImmGCPtr(GetJitContext()->runtime->names().empty),
Address(obj, UnboxedPlainObject::offsetOfData() + *list));
list++;
}
list++;
while (*list != -1) {
storePtr(ImmWord(0),
Address(obj, UnboxedPlainObject::offsetOfData() + *list));
list++;
}
// Unboxed objects don't have Values to initialize.
MOZ_ASSERT(*(list + 1) == -1);
}
} else {
MOZ_CRASH("Unknown object");
}

Просмотреть файл

@ -694,7 +694,8 @@ class MacroAssembler : public MacroAssemblerSpecific
}
template<typename T>
void loadFromTypedArray(Scalar::Type arrayType, const T &src, AnyRegister dest, Register temp, Label *fail);
void loadFromTypedArray(Scalar::Type arrayType, const T &src, AnyRegister dest, Register temp, Label *fail,
bool canonicalizeDoubles = true);
template<typename T>
void loadFromTypedArray(Scalar::Type arrayType, const T &src, const ValueOperand &dest, bool allowDouble,
@ -732,6 +733,17 @@ class MacroAssembler : public MacroAssemblerSpecific
void storeToTypedFloatArray(Scalar::Type arrayType, FloatRegister value, const BaseIndex &dest);
void storeToTypedFloatArray(Scalar::Type arrayType, FloatRegister value, const Address &dest);
// Load a property from an UnboxedPlainObject.
template <typename T>
void loadUnboxedProperty(T address, JSValueType type, TypedOrValueRegister output);
// Store a property to an UnboxedPlainObject, without triggering barriers.
// If failure is null, the value definitely has a type suitable for storing
// in the property.
template <typename T>
void storeUnboxedProperty(T address, JSValueType type,
ConstantOrRegister value, Label *failure);
Register extractString(const Address &address, Register scratch) {
return extractObject(address, scratch);
}
@ -804,7 +816,7 @@ class MacroAssembler : public MacroAssemblerSpecific
void createGCObject(Register result, Register temp, JSObject *templateObj,
gc::InitialHeap initialHeap, Label *fail, bool initFixedSlots = true);
void newGCThing(Register result, Register temp, NativeObject *templateObj,
void newGCThing(Register result, Register temp, JSObject *templateObj,
gc::InitialHeap initialHeap, Label *fail);
void initGCThing(Register obj, Register temp, JSObject *templateObj,
bool initFixedSlots = true);

Просмотреть файл

@ -32,6 +32,8 @@ namespace jit {
"getprop TypedObject") \
_(GetProp_DefiniteSlot, \
"getprop definite slot") \
_(GetProp_Unboxed, \
"getprop unboxed object") \
_(GetProp_CommonGetter, \
"getprop common getter") \
_(GetProp_InlineAccess, \
@ -47,6 +49,8 @@ namespace jit {
"setprop TypedObject") \
_(SetProp_DefiniteSlot, \
"setprop definite slot") \
_(SetProp_Unboxed, \
"setprop unboxed object") \
_(SetProp_InlineAccess, \
"setprop inline access") \
\
@ -108,12 +112,20 @@ namespace jit {
"is not singleton") \
_(NotFixedSlot, \
"property not in fixed slot") \
_(InconsistentFixedSlot, \
"property not in a consistent fixed slot") \
_(NotObject, \
"not definitely an object") \
_(NotStruct, \
"not definitely a TypedObject struct") \
_(NotUnboxed, \
"not definitely an unboxed object") \
_(StructNoField, \
"struct doesn't definitely have field") \
_(InconsistentFieldType, \
"unboxed property does not consistent type") \
_(InconsistentFieldOffset, \
"unboxed property does not consistent offset") \
_(NeedsTypeBarrier, \
"needs type barrier") \
_(InDictionaryMode, \

Просмотреть файл

@ -112,6 +112,10 @@ IsObjectEscaped(MInstruction *ins, JSObject *objDefault = nullptr)
else
obj = objDefault;
// Don't optimize unboxed objects, which aren't handled by MObjectState.
if (obj->is<UnboxedPlainObject>())
return true;
// Check if the object is escaped. If the object is not the first argument
// of either a known Store / Load, then we consider it as escaped. This is a
// cheap and conservative escape analysis.

Просмотреть файл

@ -393,7 +393,7 @@ LIRGeneratorARM::visitGuardObjectType(MGuardObjectType *ins)
LDefinition tempObj = temp(LDefinition::OBJECT);
LGuardObjectType *guard = new(alloc()) LGuardObjectType(useRegister(ins->obj()), tempObj);
assignSnapshot(guard, Bailout_ObjectIdentityOrTypeGuard);
assignSnapshot(guard, ins->bailoutKind());
add(guard, ins);
redefine(ins, ins->obj());
}

Просмотреть файл

@ -2685,6 +2685,20 @@ MacroAssemblerARMCompat::cmpPtr(const Address &lhs, ImmPtr rhs)
cmpPtr(lhs, ImmWord(uintptr_t(rhs.value)));
}
void
MacroAssemblerARMCompat::cmpPtr(const Address &lhs, ImmGCPtr rhs)
{
loadPtr(lhs, secondScratchReg_);
ma_cmp(secondScratchReg_, rhs);
}
void
MacroAssemblerARMCompat::cmpPtr(const Address &lhs, Imm32 rhs)
{
loadPtr(lhs, secondScratchReg_);
ma_cmp(secondScratchReg_, rhs);
}
void
MacroAssemblerARMCompat::setStackArg(Register reg, uint32_t arg)
{

Просмотреть файл

@ -1129,6 +1129,19 @@ class MacroAssemblerARMCompat : public MacroAssemblerARM
void storeUnboxedValue(ConstantOrRegister value, MIRType valueType, const T &dest,
MIRType slotType);
template <typename T>
void storeUnboxedPayload(ValueOperand value, T address, size_t nbytes) {
switch (nbytes) {
case 4:
storePtr(value.payloadReg(), address);
return;
case 1:
store8(value.payloadReg(), address);
return;
default: MOZ_CRASH("Bad payload width");
}
}
void moveValue(const Value &val, const ValueOperand &dest);
void moveValue(const ValueOperand &src, const ValueOperand &dest) {
@ -1622,14 +1635,16 @@ class MacroAssemblerARMCompat : public MacroAssemblerARM
void cmp32(const Operand &lhs, Imm32 rhs);
void cmp32(const Operand &lhs, Register rhs);
void cmpPtr(Register lhs, Register rhs);
void cmpPtr(Register lhs, ImmWord rhs);
void cmpPtr(Register lhs, ImmPtr rhs);
void cmpPtr(Register lhs, Register rhs);
void cmpPtr(Register lhs, ImmGCPtr rhs);
void cmpPtr(Register lhs, Imm32 rhs);
void cmpPtr(const Address &lhs, Register rhs);
void cmpPtr(const Address &lhs, ImmWord rhs);
void cmpPtr(const Address &lhs, ImmPtr rhs);
void cmpPtr(const Address &lhs, ImmGCPtr rhs);
void cmpPtr(const Address &lhs, Imm32 rhs);
void subPtr(Imm32 imm, const Register dest);
void subPtr(const Address &addr, const Register dest);

Просмотреть файл

@ -384,7 +384,7 @@ LIRGeneratorMIPS::visitGuardObjectType(MGuardObjectType *ins)
LDefinition tempObj = temp(LDefinition::OBJECT);
LGuardObjectType *guard = new(alloc()) LGuardObjectType(useRegister(ins->obj()), tempObj);
assignSnapshot(guard, Bailout_ObjectIdentityOrTypeGuard);
assignSnapshot(guard, ins->bailoutKind());
add(guard, ins);
redefine(ins, ins->obj());
}

Просмотреть файл

@ -869,6 +869,19 @@ public:
void storeUnboxedValue(ConstantOrRegister value, MIRType valueType, const T &dest,
MIRType slotType);
template <typename T>
void storeUnboxedPayload(ValueOperand value, T address, size_t nbytes) {
switch (nbytes) {
case 4:
storePtr(value.payloadReg(), address);
return;
case 1:
store8(value.payloadReg(), address);
return;
default: MOZ_CRASH("Bad payload width");
}
}
void moveValue(const Value &val, const ValueOperand &dest);
void moveValue(const ValueOperand &src, const ValueOperand &dest) {

Просмотреть файл

@ -400,6 +400,7 @@ class MacroAssemblerNone : public Assembler
template <typename T> void loadUnboxedValue(T, MIRType, AnyRegister) { MOZ_CRASH(); }
template <typename T> void storeUnboxedValue(ConstantOrRegister, MIRType, T, MIRType) { MOZ_CRASH(); }
template <typename T> void storeUnboxedPayload(ValueOperand value, T, size_t) { MOZ_CRASH(); }
void rshiftPtr(Imm32, Register) { MOZ_CRASH(); }
void rshiftPtrArithmetic(Imm32, Register) { MOZ_CRASH(); }

Просмотреть файл

@ -49,7 +49,7 @@ LIRGeneratorX86Shared::visitGuardObjectType(MGuardObjectType *ins)
MOZ_ASSERT(ins->obj()->type() == MIRType_Object);
LGuardObjectType *guard = new(alloc()) LGuardObjectType(useRegisterAtStart(ins->obj()));
assignSnapshot(guard, Bailout_ObjectIdentityOrTypeGuard);
assignSnapshot(guard, ins->bailoutKind());
add(guard, ins);
redefine(ins, ins->obj());
}

Просмотреть файл

@ -1342,6 +1342,23 @@ class MacroAssemblerX64 : public MacroAssemblerX86Shared
template <typename T>
void storeUnboxedValue(ConstantOrRegister value, MIRType valueType, const T &dest, MIRType slotType);
template <typename T>
void storeUnboxedPayload(ValueOperand value, T address, size_t nbytes) {
switch (nbytes) {
case 8:
unboxNonDouble(value, ScratchReg);
storePtr(ScratchReg, address);
return;
case 4:
store32(value.valueReg(), address);
return;
case 1:
store8(value.valueReg(), address);
return;
default: MOZ_CRASH("Bad payload width");
}
}
void loadInstructionPointerAfterCall(Register dest) {
loadPtr(Address(StackPointer, 0x0), dest);
}

Просмотреть файл

@ -550,6 +550,9 @@ class MacroAssemblerX86 : public MacroAssemblerX86Shared
void cmpPtr(const Address &lhs, const ImmPtr rhs) {
cmpPtr(lhs, ImmWord(uintptr_t(rhs.value)));
}
void cmpPtr(const Address &lhs, const ImmGCPtr rhs) {
cmpPtr(Operand(lhs), rhs);
}
void cmpPtr(Register lhs, Register rhs) {
cmp32(lhs, rhs);
}
@ -1065,6 +1068,19 @@ class MacroAssemblerX86 : public MacroAssemblerX86Shared
void storeUnboxedValue(ConstantOrRegister value, MIRType valueType, const T &dest,
MIRType slotType);
template <typename T>
void storeUnboxedPayload(ValueOperand value, T address, size_t nbytes) {
switch (nbytes) {
case 4:
storePtr(value.payloadReg(), address);
return;
case 1:
store8(value.payloadReg(), address);
return;
default: MOZ_CRASH("Bad payload width");
}
}
void rshiftPtr(Imm32 imm, Register dest) {
shrl(imm, dest);
}