Bug 1100173, bug 1102510 - Add baseline caches for typed object scalar element accesses and scalar/reference property accesses, r=jandem.

This commit is contained in:
Brian Hackett 2014-11-26 08:20:14 -07:00
parent 67bcbecc1c
commit ee3a4f5001
6 changed files with 745 additions and 82 deletions

View File

@ -154,6 +154,10 @@ class TypedProto : public NativeObject
}
inline type::Kind kind() const;
static int32_t offsetOfTypeDescr() {
return getFixedSlotOffset(JS_TYPROTO_SLOT_DESCR);
}
};
class TypeDescr : public NativeObject
@ -432,6 +436,10 @@ class ArrayTypeDescr : public ComplexTypeDescr
int32_t length() const {
return getReservedSlot(JS_DESCR_SLOT_ARRAY_LENGTH).toInt32();
}
static int32_t offsetOfLength() {
return getFixedSlotOffset(JS_DESCR_SLOT_ARRAY_LENGTH);
}
};
/*

View File

@ -366,6 +366,11 @@ ICStub::trace(JSTracer *trc)
}
break;
}
case ICStub::GetProp_TypedObject: {
ICGetProp_TypedObject *propStub = toGetProp_TypedObject();
MarkShape(trc, &propStub->shape(), "baseline-getprop-typedobject-stub-shape");
break;
}
case ICStub::GetProp_CallDOMProxyNative:
case ICStub::GetProp_CallDOMProxyWithGenerationNative: {
ICGetPropCallDOMProxyNativeStub *propStub;
@ -435,6 +440,12 @@ ICStub::trace(JSTracer *trc)
}
break;
}
case ICStub::SetProp_TypedObject: {
ICSetProp_TypedObject *propStub = toSetProp_TypedObject();
MarkShape(trc, &propStub->shape(), "baseline-setprop-typedobject-stub-shape");
MarkTypeObject(trc, &propStub->type(), "baseline-setprop-typedobject-stub-type");
break;
}
case ICStub::SetProp_CallScripted: {
ICSetProp_CallScripted *callStub = toSetProp_CallScripted();
MarkShape(trc, &callStub->shape(), "baseline-setpropcallscripted-stub-shape");
@ -1545,6 +1556,25 @@ DoTypeUpdateFallback(JSContext *cx, BaselineFrame *frame, ICUpdatedStub *stub, H
types::AddTypePropertyId(cx, obj, id, value);
break;
}
case ICStub::SetProp_TypedObject: {
MOZ_ASSERT(obj->is<TypedObject>());
jsbytecode *pc = stub->getChainFallback()->icEntry()->pc(script);
id = NameToId(script->getName(pc));
if (stub->toSetProp_TypedObject()->isObjectReference()) {
// Ignore all values being written except plain objects. Null
// is included implicitly in type information for this property,
// and non-object non-null values will cause the stub to fail to
// match shortly and we will end up doing the assignment in the VM.
if (value.isObject())
types::AddTypePropertyId(cx, obj, id, value);
} else {
// Ignore undefined values, which are included implicitly in type
// information for this property.
if (!value.isUndefined())
types::AddTypePropertyId(cx, obj, id, value);
}
break;
}
default:
MOZ_CRASH("Invalid stub");
}
@ -3893,9 +3923,35 @@ static bool TryAttachNativeGetElemStub(JSContext *cx, HandleScript script, jsbyt
}
static bool
TypedArrayRequiresFloatingPoint(HandleObject obj)
IsPrimitiveArrayTypedObject(JSObject *obj)
{
uint32_t type = AnyTypedArrayType(obj);
if (!obj->is<TypedObject>())
return false;
TypeDescr &descr = obj->as<TypedObject>().typeDescr();
return descr.is<ArrayTypeDescr>() &&
descr.as<ArrayTypeDescr>().elementType().is<ScalarTypeDescr>();
}
static Scalar::Type
PrimitiveArrayTypedObjectType(JSObject *obj)
{
MOZ_ASSERT(IsPrimitiveArrayTypedObject(obj));
TypeDescr &descr = obj->as<TypedObject>().typeDescr();
return descr.as<ArrayTypeDescr>().elementType().as<ScalarTypeDescr>().type();
}
static Scalar::Type
TypedThingElementType(JSObject *obj)
{
return IsAnyTypedArray(obj)
? AnyTypedArrayType(obj)
: PrimitiveArrayTypedObjectType(obj);
}
static bool
TypedThingRequiresFloatingPoint(JSObject *obj)
{
Scalar::Type type = TypedThingElementType(obj);
return type == Scalar::Uint32 ||
type == Scalar::Float32 ||
type == Scalar::Float64;
@ -3987,8 +4043,10 @@ TryAttachGetElemStub(JSContext *cx, JSScript *script, jsbytecode *pc, ICGetElem_
}
}
// Check for TypedArray[int] => Number accesses.
if (IsAnyTypedArray(obj.get()) && rhs.isNumber() && res.isNumber() &&
// Check for TypedArray[int] => Number and TypedObject[int] => Number accesses.
if ((IsAnyTypedArray(obj.get()) || IsPrimitiveArrayTypedObject(obj)) &&
rhs.isNumber() &&
res.isNumber() &&
!TypedArrayGetElemStubExists(stub, obj))
{
// Don't attach CALLELEM stubs for accesses on typed array expected to yield numbers.
@ -3998,15 +4056,19 @@ TryAttachGetElemStub(JSContext *cx, JSScript *script, jsbytecode *pc, ICGetElem_
#endif
if (!cx->runtime()->jitSupportsFloatingPoint &&
(TypedArrayRequiresFloatingPoint(obj) || rhs.isDouble()))
(TypedThingRequiresFloatingPoint(obj) || rhs.isDouble()))
{
return true;
}
// Don't attach typed object stubs if they might be neutered, as the
// stub will always bail out.
if (IsPrimitiveArrayTypedObject(obj) && cx->compartment()->neuteredTypedObjects)
return true;
JitSpew(JitSpew_BaselineIC, " Generating GetElem(TypedArray[Int32]) stub");
ICGetElem_TypedArray::Compiler compiler(cx,
AnyTypedArrayShape(obj.get()),
AnyTypedArrayType(obj.get()));
ICGetElem_TypedArray::Compiler compiler(cx, obj->lastProperty(),
TypedThingElementType(obj));
ICStub *typedArrayStub = compiler.getStub(compiler.getStubSpace(script));
if (!typedArrayStub)
return false;
@ -4614,10 +4676,60 @@ ICGetElem_Dense::Compiler::generateStubCode(MacroAssembler &masm)
// GetElem_TypedArray
//
static void
LoadTypedThingLength(MacroAssembler &masm, TypedThingLayout layout, Register obj, Register result)
{
switch (layout) {
case Layout_TypedArray:
masm.unboxInt32(Address(obj, TypedArrayLayout::lengthOffset()), result);
break;
case Layout_OutlineTypedObject:
case Layout_InlineTypedObject:
masm.loadObjProto(obj, result);
masm.unboxObject(Address(result, TypedProto::offsetOfTypeDescr()), result);
masm.unboxInt32(Address(result, ArrayTypeDescr::offsetOfLength()), result);
break;
default:
MOZ_CRASH();
}
}
static void
LoadTypedThingData(MacroAssembler &masm, TypedThingLayout layout, Register obj, Register result)
{
switch (layout) {
case Layout_TypedArray:
masm.loadPtr(Address(obj, TypedArrayLayout::dataOffset()), result);
break;
case Layout_OutlineTypedObject:
masm.loadPtr(Address(obj, OutlineTypedObject::offsetOfData()), result);
break;
case Layout_InlineTypedObject:
masm.computeEffectiveAddress(Address(obj, InlineTypedObject::offsetOfDataStart()), result);
break;
default:
MOZ_CRASH();
}
}
static void
CheckForNeuteredTypedObject(JSContext *cx, MacroAssembler &masm, Label *failure)
{
// All stubs which manipulate typed objects need to check the compartment
// wide flag indicating whether the objects are neutered, and bail out in
// this case.
int32_t *address = &cx->compartment()->neuteredTypedObjects;
masm.branch32(Assembler::NotEqual, AbsoluteAddress(address), Imm32(0), failure);
}
bool
ICGetElem_TypedArray::Compiler::generateStubCode(MacroAssembler &masm)
{
Label failure;
if (layout_ != Layout_TypedArray)
CheckForNeuteredTypedObject(cx, masm, &failure);
masm.branchTestObject(Assembler::NotEqual, R0, &failure);
GeneralRegisterSet regs(availableGeneralRegs(2));
@ -4650,11 +4762,11 @@ ICGetElem_TypedArray::Compiler::generateStubCode(MacroAssembler &masm)
Register key = masm.extractInt32(R1, ExtractTemp1);
// Bounds check.
masm.unboxInt32(Address(obj, TypedArrayLayout::lengthOffset()), scratchReg);
LoadTypedThingLength(masm, layout_, obj, scratchReg);
masm.branch32(Assembler::BelowOrEqual, scratchReg, key, &failure);
// Load the elements vector.
masm.loadPtr(Address(obj, TypedArrayLayout::dataOffset()), scratchReg);
LoadTypedThingData(masm, layout_, obj, scratchReg);
// Load the value.
BaseIndex source(scratchReg, key, ScaleFromElemWidth(Scalar::byteSize(type_)));
@ -5112,31 +5224,45 @@ DoSetElemFallback(JSContext *cx, BaselineFrame *frame, ICSetElem_Fallback *stub_
return true;
}
if (IsAnyTypedArray(obj.get()) && index.isNumber() && rhs.isNumber()) {
if ((IsAnyTypedArray(obj.get()) || IsPrimitiveArrayTypedObject(obj)) &&
index.isNumber() &&
rhs.isNumber())
{
if (!cx->runtime()->jitSupportsFloatingPoint &&
(TypedArrayRequiresFloatingPoint(obj) || index.isDouble()))
(TypedThingRequiresFloatingPoint(obj) || index.isDouble()))
{
return true;
}
uint32_t len = AnyTypedArrayLength(obj.get());
bool expectOutOfBounds;
double idx = index.toNumber();
bool expectOutOfBounds = (idx < 0 || idx >= double(len));
if (IsAnyTypedArray(obj)) {
expectOutOfBounds = (idx < 0 || idx >= double(AnyTypedArrayLength(obj)));
} else {
// Typed objects throw on out of bounds accesses. Don't attach
// a stub in this case.
if (idx < 0 || idx >= double(obj->as<TypedObject>().length()))
return true;
expectOutOfBounds = false;
// Don't attach stubs if typed objects in the compartment might be
// neutered, as the stub will always bail out.
if (cx->compartment()->neuteredTypedObjects)
return true;
}
if (!TypedArraySetElemStubExists(stub, obj, expectOutOfBounds)) {
// Remove any existing TypedArraySetElemStub that doesn't handle out-of-bounds
if (expectOutOfBounds)
RemoveExistingTypedArraySetElemStub(cx, stub, obj);
Shape *shape = obj->lastProperty();
Scalar::Type type = TypedThingElementType(obj);
JitSpew(JitSpew_BaselineIC,
" Generating SetElem_TypedArray stub (shape=%p, type=%u, oob=%s)",
AnyTypedArrayShape(obj.get()),
AnyTypedArrayType(obj.get()),
expectOutOfBounds ? "yes" : "no");
ICSetElem_TypedArray::Compiler compiler(cx,
AnyTypedArrayShape(obj.get()),
AnyTypedArrayType(obj.get()),
expectOutOfBounds);
shape, type, expectOutOfBounds ? "yes" : "no");
ICSetElem_TypedArray::Compiler compiler(cx, shape, type, expectOutOfBounds);
ICStub *typedArrayStub = compiler.getStub(compiler.getStubSpace(script));
if (!typedArrayStub)
return false;
@ -5520,10 +5646,81 @@ ICSetElemDenseAddCompiler::generateStubCode(MacroAssembler &masm)
// SetElem_TypedArray
//
// Write an arbitrary value to a typed array or typed object address at dest.
// If the value could not be converted to the appropriate format, jump to
// failure or failureModifiedScratch.
template <typename T>
static void
StoreToTypedArray(JSContext *cx, MacroAssembler &masm, Scalar::Type type, Address value, T dest,
Register scratch, Label *failure, Label *failureModifiedScratch)
{
Label done;
if (type == Scalar::Float32 || type == Scalar::Float64) {
masm.ensureDouble(value, FloatReg0, failure);
if (type == Scalar::Float32) {
masm.convertDoubleToFloat32(FloatReg0, ScratchFloat32Reg);
masm.storeToTypedFloatArray(type, ScratchFloat32Reg, dest);
} else {
masm.storeToTypedFloatArray(type, FloatReg0, dest);
}
} else if (type == Scalar::Uint8Clamped) {
Label notInt32;
masm.branchTestInt32(Assembler::NotEqual, value, &notInt32);
masm.unboxInt32(value, scratch);
masm.clampIntToUint8(scratch);
Label clamped;
masm.bind(&clamped);
masm.storeToTypedIntArray(type, scratch, dest);
masm.jump(&done);
// If the value is a double, clamp to uint8 and jump back.
// Else, jump to failure.
masm.bind(&notInt32);
if (cx->runtime()->jitSupportsFloatingPoint) {
masm.branchTestDouble(Assembler::NotEqual, value, failure);
masm.unboxDouble(value, FloatReg0);
masm.clampDoubleToUint8(FloatReg0, scratch);
masm.jump(&clamped);
} else {
masm.jump(failure);
}
} else {
Label notInt32;
masm.branchTestInt32(Assembler::NotEqual, value, &notInt32);
masm.unboxInt32(value, scratch);
Label isInt32;
masm.bind(&isInt32);
masm.storeToTypedIntArray(type, scratch, dest);
masm.jump(&done);
// If the value is a double, truncate and jump back.
// Else, jump to failure.
masm.bind(&notInt32);
if (cx->runtime()->jitSupportsFloatingPoint) {
masm.branchTestDouble(Assembler::NotEqual, value, failure);
masm.unboxDouble(value, FloatReg0);
masm.branchTruncateDouble(FloatReg0, scratch, failureModifiedScratch);
masm.jump(&isInt32);
} else {
masm.jump(failure);
}
}
if (done.bound())
masm.bind(&done);
}
bool
ICSetElem_TypedArray::Compiler::generateStubCode(MacroAssembler &masm)
{
Label failure;
if (layout_ != Layout_TypedArray)
CheckForNeuteredTypedObject(cx, masm, &failure);
masm.branchTestObject(Assembler::NotEqual, R0, &failure);
GeneralRegisterSet regs(availableGeneralRegs(2));
@ -5557,12 +5754,12 @@ ICSetElem_TypedArray::Compiler::generateStubCode(MacroAssembler &masm)
// Bounds check.
Label oobWrite;
masm.unboxInt32(Address(obj, TypedArrayLayout::lengthOffset()), scratchReg);
LoadTypedThingLength(masm, layout_, obj, scratchReg);
masm.branch32(Assembler::BelowOrEqual, scratchReg, key,
expectOutOfBounds_ ? &oobWrite : &failure);
// Load the elements vector.
masm.loadPtr(Address(obj, TypedArrayLayout::dataOffset()), scratchReg);
LoadTypedThingData(masm, layout_, obj, scratchReg);
BaseIndex dest(scratchReg, key, ScaleFromElemWidth(Scalar::byteSize(type_)));
Address value(BaselineStackReg, ICStackValueOffset);
@ -5575,64 +5772,15 @@ ICSetElem_TypedArray::Compiler::generateStubCode(MacroAssembler &masm)
regs.take(scratchReg);
Register secondScratch = regs.takeAny();
if (type_ == Scalar::Float32 || type_ == Scalar::Float64) {
masm.ensureDouble(value, FloatReg0, &failure);
if (type_ == Scalar::Float32)
{
masm.convertDoubleToFloat32(FloatReg0, ScratchFloat32Reg);
masm.storeToTypedFloatArray(type_, ScratchFloat32Reg, dest);
} else {
masm.storeToTypedFloatArray(type_, FloatReg0, dest);
}
EmitReturnFromIC(masm);
} else if (type_ == Scalar::Uint8Clamped) {
Label notInt32;
masm.branchTestInt32(Assembler::NotEqual, value, &notInt32);
masm.unboxInt32(value, secondScratch);
masm.clampIntToUint8(secondScratch);
Label clamped;
masm.bind(&clamped);
masm.storeToTypedIntArray(type_, secondScratch, dest);
EmitReturnFromIC(masm);
// If the value is a double, clamp to uint8 and jump back.
// Else, jump to failure.
masm.bind(&notInt32);
if (cx->runtime()->jitSupportsFloatingPoint) {
masm.branchTestDouble(Assembler::NotEqual, value, &failure);
masm.unboxDouble(value, FloatReg0);
masm.clampDoubleToUint8(FloatReg0, secondScratch);
masm.jump(&clamped);
} else {
masm.jump(&failure);
}
} else {
Label notInt32;
masm.branchTestInt32(Assembler::NotEqual, value, &notInt32);
masm.unboxInt32(value, secondScratch);
Label isInt32;
masm.bind(&isInt32);
masm.storeToTypedIntArray(type_, secondScratch, dest);
EmitReturnFromIC(masm);
// If the value is a double, truncate and jump back.
// Else, jump to failure.
Label failureRestoreRegs;
masm.bind(&notInt32);
if (cx->runtime()->jitSupportsFloatingPoint) {
masm.branchTestDouble(Assembler::NotEqual, value, &failure);
masm.unboxDouble(value, FloatReg0);
masm.branchTruncateDouble(FloatReg0, secondScratch, &failureRestoreRegs);
masm.jump(&isInt32);
} else {
masm.jump(&failure);
}
Label failureModifiedSecondScratch;
StoreToTypedArray(cx, masm, type_, value, dest,
secondScratch, &failure, &failureModifiedSecondScratch);
EmitReturnFromIC(masm);
if (failureModifiedSecondScratch.used()) {
// Writing to secondScratch may have clobbered R0 or R1, restore them
// first.
masm.bind(&failureRestoreRegs);
masm.bind(&failureModifiedSecondScratch);
masm.tagValue(JSVAL_TYPE_OBJECT, obj, R0);
masm.tagValue(JSVAL_TYPE_INT32, key, R1);
}
@ -5642,6 +5790,7 @@ ICSetElem_TypedArray::Compiler::generateStubCode(MacroAssembler &masm)
EmitStubGuardFailure(masm);
if (expectOutOfBounds_) {
MOZ_ASSERT(layout_ == Layout_TypedArray);
masm.bind(&oobWrite);
EmitReturnFromIC(masm);
}
@ -6603,6 +6752,46 @@ TryAttachNativeGetPropStub(JSContext *cx, HandleScript script, jsbytecode *pc,
return true;
}
static bool
TryAttachTypedObjectGetPropStub(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<TypedObject>())
return true;
Rooted<TypedObject *> obj(cx, &val.toObject().as<TypedObject>());
if (!obj->typeDescr().is<StructTypeDescr>())
return true;
Rooted<StructTypeDescr *> structDescr(cx, &obj->typeDescr().as<StructTypeDescr>());
size_t fieldIndex;
if (!structDescr->fieldIndex(NameToId(name), &fieldIndex))
return true;
Rooted<TypeDescr *> fieldDescr(cx, &structDescr->fieldDescr(fieldIndex));
if (!fieldDescr->is<SimpleTypeDescr>())
return true;
uint32_t fieldOffset = structDescr->fieldOffset(fieldIndex);
ICStub *monitorStub = stub->fallbackMonitorStub()->firstMonitorStub();
ICGetProp_TypedObject::Compiler compiler(cx, monitorStub, obj->lastProperty(),
fieldOffset, &fieldDescr->as<SimpleTypeDescr>());
ICStub *newStub = compiler.getStub(compiler.getStubSpace(script));
if (!newStub)
return false;
stub->addNewStub(newStub);
*attached = true;
return true;
}
static bool
TryAttachPrimitiveGetPropStub(JSContext *cx, HandleScript script, jsbytecode *pc,
ICGetProp_Fallback *stub, HandlePropertyName name, HandleValue val,
@ -6810,6 +6999,11 @@ DoGetPropFallback(JSContext *cx, BaselineFrame *frame, ICGetProp_Fallback *stub_
if (attached)
return true;
if (!TryAttachTypedObjectGetPropStub(cx, script, stub, name, val, &attached))
return false;
if (attached)
return true;
if (val.isString() || val.isNumber() || val.isBoolean()) {
if (!TryAttachPrimitiveGetPropStub(cx, script, pc, stub, name, val, res, &attached))
return false;
@ -7705,6 +7899,82 @@ ICGetProp_Generic::Compiler::generateStubCode(MacroAssembler &masm)
return true;
}
bool
ICGetProp_TypedObject::Compiler::generateStubCode(MacroAssembler &masm)
{
Label failure;
CheckForNeuteredTypedObject(cx, masm, &failure);
GeneralRegisterSet regs(availableGeneralRegs(1));
Register scratch1 = regs.takeAnyExcluding(BaselineTailCallReg);
Register scratch2 = regs.takeAnyExcluding(BaselineTailCallReg);
// Object and shape guard.
masm.branchTestObject(Assembler::NotEqual, R0, &failure);
Register object = masm.extractObject(R0, ExtractTemp0);
masm.loadPtr(Address(BaselineStubReg, ICGetProp_TypedObject::offsetOfShape()), scratch1);
masm.branchTestObjShape(Assembler::NotEqual, object, scratch1, &failure);
// Get the object's data pointer.
LoadTypedThingData(masm, layout_, object, scratch1);
// Get the address being written to.
masm.load32(Address(BaselineStubReg, ICGetProp_TypedObject::offsetOfFieldOffset()), scratch2);
masm.addPtr(scratch2, scratch1);
// Only monitor the result if the type produced by this stub might vary.
bool monitorLoad;
if (fieldDescr_->is<ScalarTypeDescr>()) {
Scalar::Type type = fieldDescr_->as<ScalarTypeDescr>().type();
monitorLoad = type == Scalar::Uint32;
masm.loadFromTypedArray(type, Address(scratch1, 0), R0, /* allowDouble = */ true,
scratch2, nullptr);
} else {
ReferenceTypeDescr::Type type = fieldDescr_->as<ReferenceTypeDescr>().type();
monitorLoad = type != ReferenceTypeDescr::TYPE_STRING;
switch (type) {
case ReferenceTypeDescr::TYPE_ANY:
masm.loadValue(Address(scratch1, 0), R0);
break;
case ReferenceTypeDescr::TYPE_OBJECT: {
Label notNull, done;
masm.loadPtr(Address(scratch1, 0), scratch1);
masm.branchTestPtr(Assembler::NonZero, scratch1, scratch1, &notNull);
masm.moveValue(NullValue(), R0);
masm.jump(&done);
masm.bind(&notNull);
masm.tagValue(JSVAL_TYPE_OBJECT, scratch1, R0);
masm.bind(&done);
break;
}
case ReferenceTypeDescr::TYPE_STRING:
masm.loadPtr(Address(scratch1, 0), scratch1);
masm.tagValue(JSVAL_TYPE_STRING, scratch1, R0);
break;
default:
MOZ_CRASH();
}
}
if (monitorLoad)
EmitEnterTypeMonitorIC(masm);
else
EmitReturnFromIC(masm);
masm.bind(&failure);
EmitStubGuardFailure(masm);
return true;
}
void
BaselineScript::noteAccessedGetter(uint32_t pcOffset)
{
@ -7883,6 +8153,48 @@ TryAttachSetAccessorPropStub(JSContext *cx, HandleScript script, jsbytecode *pc,
return true;
}
static bool
TryAttachTypedObjectSetPropStub(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<TypedObject>())
return true;
if (!obj->as<TypedObject>().typeDescr().is<StructTypeDescr>())
return true;
Rooted<StructTypeDescr *> structDescr(cx);
structDescr = &obj->as<TypedObject>().typeDescr().as<StructTypeDescr>();
size_t fieldIndex;
if (!structDescr->fieldIndex(id, &fieldIndex))
return true;
Rooted<TypeDescr *> fieldDescr(cx, &structDescr->fieldDescr(fieldIndex));
if (!fieldDescr->is<SimpleTypeDescr>())
return true;
uint32_t fieldOffset = structDescr->fieldOffset(fieldIndex);
ICSetProp_TypedObject::Compiler compiler(cx, obj->lastProperty(), obj->type(), fieldOffset,
&fieldDescr->as<SimpleTypeDescr>());
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);
*attached = true;
return true;
}
static bool
DoSetPropFallback(JSContext *cx, BaselineFrame *frame, ICSetProp_Fallback *stub_,
HandleValue lhs, HandleValue rhs, MutableHandleValue res)
@ -7977,6 +8289,15 @@ DoSetPropFallback(JSContext *cx, BaselineFrame *frame, ICSetProp_Fallback *stub_
if (attached)
return true;
if (!attached &&
lhs.isObject() &&
!TryAttachTypedObjectSetPropStub(cx, script, stub, id, obj, rhs, &attached))
{
return false;
}
if (attached)
return true;
MOZ_ASSERT(!attached);
if (!isTemporarilyUnoptimizable)
stub->noteUnoptimizableAccess();
@ -8256,6 +8577,128 @@ ICSetPropNativeAddCompiler::generateStubCode(MacroAssembler &masm)
return true;
}
bool
ICSetProp_TypedObject::Compiler::generateStubCode(MacroAssembler &masm)
{
Label failure;
CheckForNeuteredTypedObject(cx, masm, &failure);
// Guard input is an object.
masm.branchTestObject(Assembler::NotEqual, R0, &failure);
GeneralRegisterSet regs(availableGeneralRegs(2));
Register scratch = regs.takeAny();
// Unbox and shape guard.
Register object = masm.extractObject(R0, ExtractTemp0);
masm.loadPtr(Address(BaselineStubReg, ICSetProp_TypedObject::offsetOfShape()), scratch);
masm.branchTestObjShape(Assembler::NotEqual, object, scratch, &failure);
// Guard that the type object matches.
masm.loadPtr(Address(BaselineStubReg, ICSetProp_TypedObject::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. Descriptors
// which can write objects also need update stubs.
GeneralRegisterSet saveRegs;
saveRegs.add(R0);
saveRegs.add(R1);
saveRegs.addUnchecked(object);
saveRegs.add(BaselineStubReg);
emitPostWriteBarrierSlot(masm, object, R1, scratch, saveRegs);
}
// Save the rhs on the stack so we can get a second scratch register.
Label failurePopRHS;
masm.pushValue(R1);
regs = availableGeneralRegs(1);
regs.takeUnchecked(object);
regs.take(scratch);
Register secondScratch = regs.takeAny();
// Get the object's data pointer.
LoadTypedThingData(masm, layout_, object, scratch);
// Compute the address being written to.
masm.load32(Address(BaselineStubReg, ICSetProp_TypedObject::offsetOfFieldOffset()), secondScratch);
masm.addPtr(secondScratch, scratch);
Address dest(scratch, 0);
Address value(BaselineStackReg, 0);
if (fieldDescr_->is<ScalarTypeDescr>()) {
Scalar::Type type = fieldDescr_->as<ScalarTypeDescr>().type();
StoreToTypedArray(cx, masm, type, value, dest,
secondScratch, &failurePopRHS, &failurePopRHS);
masm.popValue(R1);
EmitReturnFromIC(masm);
} else {
ReferenceTypeDescr::Type type = fieldDescr_->as<ReferenceTypeDescr>().type();
masm.popValue(R1);
switch (type) {
case ReferenceTypeDescr::TYPE_ANY:
EmitPreBarrier(masm, dest, MIRType_Value);
masm.storeValue(R1, dest);
break;
case ReferenceTypeDescr::TYPE_OBJECT: {
EmitPreBarrier(masm, dest, MIRType_Object);
Label notObject;
masm.branchTestObject(Assembler::NotEqual, R1, &notObject);
Register rhsObject = masm.extractObject(R1, ExtractTemp0);
masm.storePtr(rhsObject, dest);
EmitReturnFromIC(masm);
masm.bind(&notObject);
masm.branchTestNull(Assembler::NotEqual, R1, &failure);
masm.storePtr(ImmWord(0), dest);
break;
}
case ReferenceTypeDescr::TYPE_STRING: {
EmitPreBarrier(masm, dest, MIRType_String);
masm.branchTestString(Assembler::NotEqual, R1, &failure);
Register rhsString = masm.extractString(R1, ExtractTemp0);
masm.storePtr(rhsString, dest);
break;
}
default:
MOZ_CRASH();
}
EmitReturnFromIC(masm);
}
masm.bind(&failurePopRHS);
masm.popValue(R1);
masm.bind(&failure);
EmitStubGuardFailure(masm);
return true;
}
bool
ICSetProp_CallScripted::Compiler::generateStubCode(MacroAssembler &masm)
{

View File

@ -14,9 +14,11 @@
#include "jsgc.h"
#include "jsopcode.h"
#include "builtin/TypedObject.h"
#include "jit/BaselineJIT.h"
#include "jit/BaselineRegisters.h"
#include "vm/ArrayObject.h"
#include "vm/TypedArrayCommon.h"
namespace js {
@ -421,6 +423,7 @@ class ICEntry
_(GetProp_Native) \
_(GetProp_NativeDoesNotExist) \
_(GetProp_NativePrototype) \
_(GetProp_TypedObject) \
_(GetProp_CallScripted) \
_(GetProp_CallNative) \
_(GetProp_CallNativePrototype)\
@ -434,6 +437,7 @@ class ICEntry
_(SetProp_Fallback) \
_(SetProp_Native) \
_(SetProp_NativeAdd) \
_(SetProp_TypedObject) \
_(SetProp_CallScripted) \
_(SetProp_CallNative) \
\
@ -3406,6 +3410,26 @@ class ICGetElem_Dense : public ICMonitoredStub
};
};
// Enum for stubs handling a combination of typed arrays and typed objects.
enum TypedThingLayout {
Layout_TypedArray,
Layout_OutlineTypedObject,
Layout_InlineTypedObject
};
static inline TypedThingLayout
GetTypedThingLayout(const Class *clasp)
{
if (IsAnyTypedArrayClass(clasp))
return Layout_TypedArray;
if (IsOutlineTypedObjectClass(clasp))
return Layout_OutlineTypedObject;
if (IsInlineTypedObjectClass(clasp))
return Layout_InlineTypedObject;
MOZ_CRASH("Bad object class");
}
// Accesses scalar elements of a typed array or typed object.
class ICGetElem_TypedArray : public ICStub
{
friend class ICStubSpace;
@ -3435,19 +3459,23 @@ class ICGetElem_TypedArray : public ICStub
class Compiler : public ICStubCompiler {
RootedShape shape_;
Scalar::Type type_;
TypedThingLayout layout_;
protected:
bool generateStubCode(MacroAssembler &masm);
virtual int32_t getKey() const {
return static_cast<int32_t>(kind) | (static_cast<int32_t>(type_) << 16);
return static_cast<int32_t>(kind) |
(static_cast<int32_t>(type_) << 16) |
(static_cast<int32_t>(layout_) << 24);
}
public:
Compiler(JSContext *cx, Shape *shape, Scalar::Type type)
: ICStubCompiler(cx, ICStub::GetElem_TypedArray),
shape_(cx, shape),
type_(type)
type_(type),
layout_(GetTypedThingLayout(shape->getObjectClass()))
{}
ICStub *getStub(ICStubSpace *space) {
@ -3721,6 +3749,7 @@ class ICSetElemDenseAddCompiler : public ICStubCompiler {
ICUpdatedStub *getStub(ICStubSpace *space);
};
// Accesses scalar elements of a typed array or typed object.
class ICSetElem_TypedArray : public ICStub
{
friend class ICStubSpace;
@ -3760,14 +3789,17 @@ class ICSetElem_TypedArray : public ICStub
class Compiler : public ICStubCompiler {
RootedShape shape_;
Scalar::Type type_;
TypedThingLayout layout_;
bool expectOutOfBounds_;
protected:
bool generateStubCode(MacroAssembler &masm);
virtual int32_t getKey() const {
return static_cast<int32_t>(kind) | (static_cast<int32_t>(type_) << 16) |
(static_cast<int32_t>(expectOutOfBounds_) << 24);
return static_cast<int32_t>(kind) |
(static_cast<int32_t>(type_) << 16) |
(static_cast<int32_t>(layout_) << 24) |
(static_cast<int32_t>(expectOutOfBounds_) << 28);
}
public:
@ -3775,6 +3807,7 @@ class ICSetElem_TypedArray : public ICStub
: ICStubCompiler(cx, ICStub::SetElem_TypedArray),
shape_(cx, shape),
type_(type),
layout_(GetTypedThingLayout(shape->getObjectClass())),
expectOutOfBounds_(expectOutOfBounds)
{}
@ -4571,6 +4604,84 @@ class ICGetPropNativeDoesNotExistCompiler : public ICStubCompiler
ICStub *getStub(ICStubSpace *space);
};
static uint32_t
SimpleTypeDescrKey(SimpleTypeDescr *descr)
{
if (descr->is<ScalarTypeDescr>())
return uint32_t(descr->as<ScalarTypeDescr>().type()) << 1;
return (uint32_t(descr->as<ReferenceTypeDescr>().type()) << 1) | 1;
}
class ICGetProp_TypedObject : public ICMonitoredStub
{
friend class ICStubSpace;
HeapPtrShape shape_;
uint32_t fieldOffset_;
ICGetProp_TypedObject(JitCode *stubCode, ICStub *firstMonitorStub, HandleShape shape,
uint32_t fieldOffset)
: ICMonitoredStub(ICStub::GetProp_TypedObject, stubCode, firstMonitorStub),
shape_(shape), fieldOffset_(fieldOffset)
{
(void) fieldOffset_; // Silence clang warning
}
public:
static inline ICGetProp_TypedObject *New(ICStubSpace *space, JitCode *code,
ICStub *firstMonitorStub, HandleShape shape,
uint32_t fieldOffset)
{
if (!code)
return nullptr;
return space->allocate<ICGetProp_TypedObject>(code, firstMonitorStub, shape, fieldOffset);
}
HeapPtrShape &shape() {
return shape_;
}
static size_t offsetOfShape() {
return offsetof(ICGetProp_TypedObject, shape_);
}
static size_t offsetOfFieldOffset() {
return offsetof(ICGetProp_TypedObject, fieldOffset_);
}
class Compiler : public ICStubCompiler {
protected:
ICStub *firstMonitorStub_;
RootedShape shape_;
uint32_t fieldOffset_;
TypedThingLayout layout_;
Rooted<SimpleTypeDescr *> fieldDescr_;
bool generateStubCode(MacroAssembler &masm);
virtual int32_t getKey() const {
return static_cast<int32_t>(kind) |
(static_cast<int32_t>(SimpleTypeDescrKey(fieldDescr_)) << 16) |
(static_cast<int32_t>(layout_) << 24);
}
public:
Compiler(JSContext *cx, ICStub *firstMonitorStub,
Shape *shape, uint32_t fieldOffset, SimpleTypeDescr *fieldDescr)
: ICStubCompiler(cx, ICStub::GetProp_TypedObject),
firstMonitorStub_(firstMonitorStub),
shape_(cx, shape),
fieldOffset_(fieldOffset),
layout_(GetTypedThingLayout(shape->getObjectClass())),
fieldDescr_(cx, fieldDescr)
{}
ICStub *getStub(ICStubSpace *space) {
return ICGetProp_TypedObject::New(space, getStubCode(), firstMonitorStub_,
shape_, fieldOffset_);
}
};
};
class ICGetPropCallGetter : public ICMonitoredStub
{
friend class ICStubSpace;
@ -5413,6 +5524,102 @@ class ICSetPropNativeAddCompiler : public ICStubCompiler
ICUpdatedStub *getStub(ICStubSpace *space);
};
class ICSetProp_TypedObject : public ICUpdatedStub
{
friend class ICStubSpace;
HeapPtrShape shape_;
HeapPtrTypeObject type_;
uint32_t fieldOffset_;
bool isObjectReference_;
ICSetProp_TypedObject(JitCode *stubCode, HandleShape shape, HandleTypeObject type,
uint32_t fieldOffset, bool isObjectReference)
: ICUpdatedStub(ICStub::SetProp_TypedObject, stubCode),
shape_(shape),
type_(type),
fieldOffset_(fieldOffset),
isObjectReference_(isObjectReference)
{
(void) fieldOffset_; // Silence clang warning
}
public:
static inline ICSetProp_TypedObject *New(ICStubSpace *space, JitCode *code,
HandleShape shape, HandleTypeObject type,
uint32_t fieldOffset, bool isObjectReference)
{
if (!code)
return nullptr;
return space->allocate<ICSetProp_TypedObject>(code, shape, type,
fieldOffset, isObjectReference);
}
HeapPtrShape &shape() {
return shape_;
}
HeapPtrTypeObject &type() {
return type_;
}
bool isObjectReference() {
return isObjectReference_;
}
static size_t offsetOfShape() {
return offsetof(ICSetProp_TypedObject, shape_);
}
static size_t offsetOfType() {
return offsetof(ICSetProp_TypedObject, type_);
}
static size_t offsetOfFieldOffset() {
return offsetof(ICSetProp_TypedObject, fieldOffset_);
}
class Compiler : public ICStubCompiler {
protected:
RootedShape shape_;
RootedTypeObject type_;
uint32_t fieldOffset_;
TypedThingLayout layout_;
Rooted<SimpleTypeDescr *> fieldDescr_;
bool generateStubCode(MacroAssembler &masm);
virtual int32_t getKey() const {
return static_cast<int32_t>(kind) |
(static_cast<int32_t>(SimpleTypeDescrKey(fieldDescr_)) << 16) |
(static_cast<int32_t>(layout_) << 24);
}
public:
Compiler(JSContext *cx, Shape *shape, types::TypeObject *type, uint32_t fieldOffset,
SimpleTypeDescr *fieldDescr)
: ICStubCompiler(cx, ICStub::SetProp_TypedObject),
shape_(cx, shape),
type_(cx, type),
fieldOffset_(fieldOffset),
layout_(GetTypedThingLayout(shape->getObjectClass())),
fieldDescr_(cx, fieldDescr)
{}
ICUpdatedStub *getStub(ICStubSpace *space) {
bool isObjectReference =
fieldDescr_->is<ReferenceTypeDescr>() &&
fieldDescr_->as<ReferenceTypeDescr>().type() == ReferenceTypeDescr::TYPE_OBJECT;
ICUpdatedStub *stub = ICSetProp_TypedObject::New(space, getStubCode(), shape_, type_,
fieldOffset_, isObjectReference);
if (!stub || !stub->initUpdatingChain(cx, space))
return nullptr;
return stub;
}
bool needsUpdateStubs() {
return fieldDescr_->is<ReferenceTypeDescr>() &&
fieldDescr_->as<ReferenceTypeDescr>().type() != ReferenceTypeDescr::TYPE_STRING;
}
};
};
// Base stub for calling a setters on a native object.
class ICSetPropCallSetter : public ICStub
{

View File

@ -56,6 +56,7 @@ JSCompartment::JSCompartment(Zone *zone, const JS::CompartmentOptions &options =
lastAnimationTime(0),
regExps(runtime_),
globalWriteBarriered(false),
neuteredTypedObjects(0),
propertyTree(thisForCtor()),
selfHostingScriptSource(nullptr),
lazyArrayBuffers(nullptr),

View File

@ -241,6 +241,9 @@ struct JSCompartment
*/
bool globalWriteBarriered;
// Non-zero if any typed objects in this compartment might be neutered.
int32_t neuteredTypedObjects;
public:
void addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf,
size_t *tiAllocationSiteTables,

View File

@ -529,6 +529,7 @@ ArrayBufferObject::neuter(JSContext *cx, Handle<ArrayBufferObject*> buffer,
if (!cx->global()->getType(cx))
CrashAtUnhandlableOOM("ArrayBufferObject::neuter");
types::MarkTypeObjectFlags(cx, cx->global(), types::OBJECT_FLAG_TYPED_OBJECT_NEUTERED);
cx->compartment()->neuteredTypedObjects = 1;
}
// Neuter all views on the buffer, clear out the list of views and the