зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1116855
- Add JIT optimizations for unboxed objects, r=jandem.
This commit is contained in:
Родитель
51def7fb13
Коммит
e702b57de9
|
@ -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);
|
||||
|
||||
|
|
151
js/src/jit/MIR.h
151
js/src/jit/MIR.h
|
@ -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), ¬Null);
|
||||
|
||||
moveValue(NullValue(), output.valueReg());
|
||||
jump(&done);
|
||||
|
||||
bind(¬Null);
|
||||
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);
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче