Bug 1258105 - Port object length stubs to CacheIR. r=efaust

This commit is contained in:
Jan de Mooij 2016-03-26 13:22:12 +01:00
parent 4acbe0650b
commit a5da57cddc
7 changed files with 183 additions and 195 deletions

View File

@ -742,6 +742,37 @@ BaselineCacheIRCompiler::emitGuardProto()
return true;
}
bool
BaselineCacheIRCompiler::emitGuardClass()
{
Register obj = allocator.useRegister(masm, reader.objOperandId());
AutoScratchRegister scratch(allocator, masm);
FailurePath* failure;
if (!addFailurePath(&failure))
return false;
const Class* clasp = nullptr;
switch (reader.guardClassKind()) {
case GuardClassKind::Array:
clasp = &ArrayObject::class_;
break;
case GuardClassKind::UnboxedArray:
clasp = &UnboxedArrayObject::class_;
break;
case GuardClassKind::MappedArguments:
clasp = &MappedArgumentsObject::class_;
break;
case GuardClassKind::UnmappedArguments:
clasp = &UnmappedArgumentsObject::class_;
break;
}
MOZ_ASSERT(clasp);
masm.branchTestObjClass(Assembler::NotEqual, obj, scratch, clasp, failure->label());
return true;
}
bool
BaselineCacheIRCompiler::emitGuardNoUnboxedExpando()
{
@ -810,6 +841,69 @@ BaselineCacheIRCompiler::emitLoadUndefinedResult()
return true;
}
bool
BaselineCacheIRCompiler::emitLoadInt32ArrayLengthResult()
{
Register obj = allocator.useRegister(masm, reader.objOperandId());
AutoScratchRegister scratch(allocator, masm);
FailurePath* failure;
if (!addFailurePath(&failure))
return false;
masm.loadPtr(Address(obj, NativeObject::offsetOfElements()), scratch);
masm.load32(Address(scratch, ObjectElements::offsetOfLength()), scratch);
// Guard length fits in an int32.
masm.branchTest32(Assembler::Signed, scratch, scratch, failure->label());
masm.tagValue(JSVAL_TYPE_INT32, scratch, R0);
// The int32 type was monitored when attaching the stub, so we can
// just return.
emitReturnFromIC();
return true;
}
bool
BaselineCacheIRCompiler::emitLoadUnboxedArrayLengthResult()
{
Register obj = allocator.useRegister(masm, reader.objOperandId());
masm.load32(Address(obj, UnboxedArrayObject::offsetOfLength()), R0.scratchReg());
masm.tagValue(JSVAL_TYPE_INT32, R0.scratchReg(), R0);
// The int32 type was monitored when attaching the stub, so we can
// just return.
emitReturnFromIC();
return true;
}
bool
BaselineCacheIRCompiler::emitLoadArgumentsObjectLengthResult()
{
Register obj = allocator.useRegister(masm, reader.objOperandId());
AutoScratchRegister scratch(allocator, masm);
FailurePath* failure;
if (!addFailurePath(&failure))
return false;
// Get initial length value.
masm.unboxInt32(Address(obj, ArgumentsObject::getInitialLengthSlotOffset()), scratch);
// Test if length has been overridden.
masm.branchTest32(Assembler::NonZero,
scratch,
Imm32(ArgumentsObject::LENGTH_OVERRIDDEN_BIT),
failure->label());
// Shift out arguments length and return it. No need to type monitor
// because this stub always returns int32.
masm.rshiftPtr(Imm32(ArgumentsObject::PACKED_BITS_COUNT), scratch);
masm.tagValue(JSVAL_TYPE_INT32, scratch, R0);
emitReturnFromIC();
return true;
}
bool
BaselineCacheIRCompiler::emitLoadObject()
{
@ -1008,7 +1102,8 @@ jit::AttachBaselineCacheIRStub(JSContext* cx, const CacheIRWriter& writer, Cache
return nullptr;
CacheIRStubKey key(stubInfo);
jitCompartment->putCacheIRStubCode(lookup, key, code);
if (!jitCompartment->putCacheIRStubCode(lookup, key, code))
return nullptr;
}
// We got our shared stub code and stub info. Time to allocate and attach a

View File

@ -805,8 +805,6 @@ BaselineInspector::expectedPropertyAccessInputType(jsbytecode* pc)
// Either an object or magic arguments.
return MIRType_Value;
case ICStub::GetProp_ArrayLength:
case ICStub::GetProp_UnboxedArrayLength:
case ICStub::GetProp_Unboxed:
case ICStub::GetProp_TypedObject:
case ICStub::GetProp_CallScripted:

View File

@ -56,6 +56,8 @@ GetPropIRGenerator::tryAttachStub(Maybe<CacheIRWriter>& writer)
RootedObject obj(cx_, &val_.toObject());
ObjOperandId objId = writer->guardIsObject(valId);
if (!emitted_ && !tryAttachObjectLength(*writer, obj, objId))
return false;
if (!emitted_ && !tryAttachNative(*writer, obj, objId))
return false;
if (!emitted_ && !tryAttachUnboxedExpando(*writer, obj, objId))
@ -274,3 +276,45 @@ GetPropIRGenerator::tryAttachUnboxedExpando(CacheIRWriter& writer, HandleObject
EmitReadSlotResult(writer, obj, obj, shape, objId);
return true;
}
bool
GetPropIRGenerator::tryAttachObjectLength(CacheIRWriter& writer, HandleObject obj, ObjOperandId objId)
{
MOZ_ASSERT(!emitted_);
if (name_ != cx_->names().length)
return true;
if (obj->is<ArrayObject>()) {
// Make sure int32 is added to the TypeSet before we attach a stub, so
// the stub can return int32 values without monitoring the result.
if (obj->as<ArrayObject>().length() > INT32_MAX)
return true;
writer.guardClass(objId, GuardClassKind::Array);
writer.loadInt32ArrayLengthResult(objId);
emitted_ = true;
return true;
}
if (obj->is<UnboxedArrayObject>()) {
writer.guardClass(objId, GuardClassKind::UnboxedArray);
writer.loadUnboxedArrayLengthResult(objId);
emitted_ = true;
return true;
}
if (obj->is<ArgumentsObject>() && !obj->as<ArgumentsObject>().hasOverriddenLength()) {
if (obj->is<MappedArgumentsObject>()) {
writer.guardClass(objId, GuardClassKind::MappedArguments);
} else {
MOZ_ASSERT(obj->is<UnmappedArgumentsObject>());
writer.guardClass(objId, GuardClassKind::UnmappedArguments);
}
writer.loadArgumentsObjectLengthResult(objId);
emitted_ = true;
return true;
}
return true;
}

View File

@ -82,6 +82,7 @@ class ObjOperandId : public OperandId
_(GuardShape) \
_(GuardGroup) \
_(GuardProto) \
_(GuardClass) \
_(GuardNoUnboxedExpando) \
_(GuardAndLoadUnboxedExpando) \
_(LoadObject) \
@ -89,6 +90,9 @@ class ObjOperandId : public OperandId
_(LoadUnboxedExpando) \
_(LoadFixedSlotResult) \
_(LoadDynamicSlotResult) \
_(LoadInt32ArrayLengthResult) \
_(LoadUnboxedArrayLengthResult) \
_(LoadArgumentsObjectLengthResult) \
_(LoadUndefinedResult)
enum class CacheOp {
@ -114,6 +118,16 @@ struct StubField {
{}
};
// We use this enum as GuardClass operand, instead of storing Class* pointers
// in the IR, to keep the IR compact and the same size on all platforms.
enum class GuardClassKind
{
Array,
UnboxedArray,
MappedArguments,
UnmappedArguments,
};
// Class to record CacheIR + some additional metadata for code generation.
class MOZ_RAII CacheIRWriter
{
@ -240,6 +254,11 @@ class MOZ_RAII CacheIRWriter
writeOpWithOperandId(CacheOp::GuardProto, obj);
addStubWord(uintptr_t(proto), StubField::GCType::JSObject);
}
void guardClass(ObjOperandId obj, GuardClassKind kind) {
MOZ_ASSERT(uint32_t(kind) <= UINT8_MAX);
writeOpWithOperandId(CacheOp::GuardClass, obj);
buffer_.writeByte(uint32_t(kind));
}
void guardNoUnboxedExpando(ObjOperandId obj) {
writeOpWithOperandId(CacheOp::GuardNoUnboxedExpando, obj);
}
@ -280,6 +299,15 @@ class MOZ_RAII CacheIRWriter
writeOpWithOperandId(CacheOp::LoadDynamicSlotResult, obj);
addStubWord(offset, StubField::GCType::NoGCThing);
}
void loadInt32ArrayLengthResult(ObjOperandId obj) {
writeOpWithOperandId(CacheOp::LoadInt32ArrayLengthResult, obj);
}
void loadUnboxedArrayLengthResult(ObjOperandId obj) {
writeOpWithOperandId(CacheOp::LoadUnboxedArrayLengthResult, obj);
}
void loadArgumentsObjectLengthResult(ObjOperandId obj) {
writeOpWithOperandId(CacheOp::LoadArgumentsObjectLengthResult, obj);
}
};
class CacheIRStubInfo;
@ -313,9 +341,9 @@ class MOZ_RAII CacheIRReader
ObjOperandId objOperandId() {
return ObjOperandId(buffer_.readByte());
}
uint32_t stubOffset() {
return buffer_.readByte();
}
uint32_t stubOffset() { return buffer_.readByte(); }
GuardClassKind guardClassKind() { return GuardClassKind(buffer_.readByte()); }
bool matchOp(CacheOp op) {
const uint8_t* pos = buffer_.currentPosition();
@ -356,6 +384,7 @@ class MOZ_RAII GetPropIRGenerator
bool tryAttachNative(CacheIRWriter& writer, HandleObject obj, ObjOperandId objId);
bool tryAttachUnboxedExpando(CacheIRWriter& writer, HandleObject obj, ObjOperandId objId);
bool tryAttachObjectLength(CacheIRWriter& writer, HandleObject obj, ObjOperandId objId);
GetPropIRGenerator(const GetPropIRGenerator&) = delete;
GetPropIRGenerator& operator=(const GetPropIRGenerator&) = delete;

View File

@ -2117,51 +2117,6 @@ TryAttachLengthStub(JSContext* cx, SharedStubInfo* info,
return true;
}
if (!val.isObject())
return true;
RootedObject obj(cx, &val.toObject());
if (obj->is<ArrayObject>() && res.isInt32()) {
JitSpew(JitSpew_BaselineIC, " Generating GetProp(Array.length) stub");
ICGetProp_ArrayLength::Compiler compiler(cx, info->engine());
ICStub* newStub = compiler.getStub(compiler.getStubSpace(info->outerScript(cx)));
if (!newStub)
return false;
*attached = true;
stub->addNewStub(newStub);
return true;
}
if (obj->is<UnboxedArrayObject>() && res.isInt32()) {
JitSpew(JitSpew_BaselineIC, " Generating GetProp(UnboxedArray.length) stub");
ICGetProp_UnboxedArrayLength::Compiler compiler(cx, info->engine());
ICStub* newStub = compiler.getStub(compiler.getStubSpace(info->outerScript(cx)));
if (!newStub)
return false;
*attached = true;
stub->addNewStub(newStub);
return true;
}
if (obj->is<ArgumentsObject>() && res.isInt32()) {
JitSpew(JitSpew_BaselineIC, " Generating GetProp(ArgsObj.length %s) stub",
obj->is<MappedArgumentsObject>() ? "Mapped" : "Unmapped");
ICGetProp_ArgumentsLength::Which which = ICGetProp_ArgumentsLength::Mapped;
if (obj->is<UnmappedArgumentsObject>())
which = ICGetProp_ArgumentsLength::Unmapped;
ICGetProp_ArgumentsLength::Compiler compiler(cx, info->engine(), which);
ICStub* newStub = compiler.getStub(compiler.getStubSpace(info->outerScript(cx)));
if (!newStub)
return false;
*attached = true;
stub->addNewStub(newStub);
return true;
}
return true;
}
@ -3040,58 +2995,6 @@ ICGetProp_Fallback::Compiler::postGenerateStubCode(MacroAssembler& masm, Handle<
}
}
bool
ICGetProp_ArrayLength::Compiler::generateStubCode(MacroAssembler& masm)
{
Label failure;
masm.branchTestObject(Assembler::NotEqual, R0, &failure);
Register scratch = R1.scratchReg();
// Unbox R0 and guard it's an array.
Register obj = masm.extractObject(R0, ExtractTemp0);
masm.branchTestObjClass(Assembler::NotEqual, obj, scratch, &ArrayObject::class_, &failure);
// Load obj->elements->length.
masm.loadPtr(Address(obj, NativeObject::offsetOfElements()), scratch);
masm.load32(Address(scratch, ObjectElements::offsetOfLength()), scratch);
// Guard length fits in an int32.
masm.branchTest32(Assembler::Signed, scratch, scratch, &failure);
masm.tagValue(JSVAL_TYPE_INT32, scratch, R0);
EmitReturnFromIC(masm);
// Failure case - jump to next stub
masm.bind(&failure);
EmitStubGuardFailure(masm);
return true;
}
bool
ICGetProp_UnboxedArrayLength::Compiler::generateStubCode(MacroAssembler& masm)
{
Label failure;
masm.branchTestObject(Assembler::NotEqual, R0, &failure);
Register scratch = R1.scratchReg();
// Unbox R0 and guard it's an unboxed array.
Register obj = masm.extractObject(R0, ExtractTemp0);
masm.branchTestObjClass(Assembler::NotEqual, obj, scratch, &UnboxedArrayObject::class_, &failure);
// Load obj->length.
masm.load32(Address(obj, UnboxedArrayObject::offsetOfLength()), scratch);
masm.tagValue(JSVAL_TYPE_INT32, scratch, R0);
EmitReturnFromIC(masm);
// Failure case - jump to next stub
masm.bind(&failure);
EmitStubGuardFailure(masm);
return true;
}
bool
ICGetProp_StringLength::Compiler::generateStubCode(MacroAssembler& masm)
{
@ -3799,53 +3702,22 @@ ICGetProp_DOMProxyShadowed::Compiler::generateStubCode(MacroAssembler& masm)
bool
ICGetProp_ArgumentsLength::Compiler::generateStubCode(MacroAssembler& masm)
{
MOZ_ASSERT(which_ == ICGetProp_ArgumentsLength::Magic);
Label failure;
if (which_ == ICGetProp_ArgumentsLength::Magic) {
// Ensure that this is lazy arguments.
masm.branchTestMagicValue(Assembler::NotEqual, R0, JS_OPTIMIZED_ARGUMENTS, &failure);
// Ensure that frame has not loaded different arguments object since.
masm.branchTest32(Assembler::NonZero,
Address(BaselineFrameReg, BaselineFrame::reverseOffsetOfFlags()),
Imm32(BaselineFrame::HAS_ARGS_OBJ),
&failure);
// Ensure that this is lazy arguments.
masm.branchTestMagicValue(Assembler::NotEqual, R0, JS_OPTIMIZED_ARGUMENTS, &failure);
Address actualArgs(BaselineFrameReg, BaselineFrame::offsetOfNumActualArgs());
masm.loadPtr(actualArgs, R0.scratchReg());
masm.tagValue(JSVAL_TYPE_INT32, R0.scratchReg(), R0);
EmitReturnFromIC(masm);
masm.bind(&failure);
EmitStubGuardFailure(masm);
return true;
}
MOZ_ASSERT(which_ == ICGetProp_ArgumentsLength::Mapped ||
which_ == ICGetProp_ArgumentsLength::Unmapped);
const Class* clasp = (which_ == ICGetProp_ArgumentsLength::Mapped)
? &MappedArgumentsObject::class_
: &UnmappedArgumentsObject::class_;
Register scratchReg = R1.scratchReg();
// Guard on input being an arguments object.
masm.branchTestObject(Assembler::NotEqual, R0, &failure);
Register objReg = masm.extractObject(R0, ExtractTemp0);
masm.branchTestObjClass(Assembler::NotEqual, objReg, scratchReg, clasp, &failure);
// Get initial length value.
masm.unboxInt32(Address(objReg, ArgumentsObject::getInitialLengthSlotOffset()), scratchReg);
// Test if length has been overridden.
// Ensure that frame has not loaded different arguments object since.
masm.branchTest32(Assembler::NonZero,
scratchReg,
Imm32(ArgumentsObject::LENGTH_OVERRIDDEN_BIT),
Address(BaselineFrameReg, BaselineFrame::reverseOffsetOfFlags()),
Imm32(BaselineFrame::HAS_ARGS_OBJ),
&failure);
// Nope, shift out arguments length and return it.
// No need to type monitor because this stub always returns Int32.
masm.rshiftPtr(Imm32(ArgumentsObject::PACKED_BITS_COUNT), scratchReg);
masm.tagValue(JSVAL_TYPE_INT32, scratchReg, R0);
Address actualArgs(BaselineFrameReg, BaselineFrame::offsetOfNumActualArgs());
masm.loadPtr(actualArgs, R0.scratchReg());
masm.tagValue(JSVAL_TYPE_INT32, R0.scratchReg(), R0);
EmitReturnFromIC(masm);
masm.bind(&failure);

View File

@ -2373,54 +2373,6 @@ class ICGetProp_Generic : public ICMonitoredStub
};
};
// Stub for accessing a dense array's length.
class ICGetProp_ArrayLength : public ICStub
{
friend class ICStubSpace;
explicit ICGetProp_ArrayLength(JitCode* stubCode)
: ICStub(GetProp_ArrayLength, stubCode)
{}
public:
class Compiler : public ICStubCompiler {
bool generateStubCode(MacroAssembler& masm);
public:
explicit Compiler(JSContext* cx, Engine engine)
: ICStubCompiler(cx, ICStub::GetProp_ArrayLength, engine)
{}
ICStub* getStub(ICStubSpace* space) {
return newStub<ICGetProp_ArrayLength>(space, getStubCode());
}
};
};
// Stub for accessing an unboxed array's length.
class ICGetProp_UnboxedArrayLength : public ICStub
{
friend class ICStubSpace;
explicit ICGetProp_UnboxedArrayLength(JitCode* stubCode)
: ICStub(GetProp_UnboxedArrayLength, stubCode)
{}
public:
class Compiler : public ICStubCompiler {
bool generateStubCode(MacroAssembler& masm);
public:
explicit Compiler(JSContext* cx, Engine engine)
: ICStubCompiler(cx, ICStub::GetProp_UnboxedArrayLength, engine)
{}
ICStub* getStub(ICStubSpace* space) {
return newStub<ICGetProp_UnboxedArrayLength>(space, getStubCode());
}
};
};
// Stub for accessing a property on a primitive's prototype.
class ICGetProp_Primitive : public ICMonitoredStub
{
@ -3228,7 +3180,7 @@ class ICGetProp_ArgumentsLength : public ICStub
{
friend class ICStubSpace;
public:
enum Which { Mapped, Unmapped, Magic };
enum Which { Magic };
protected:
explicit ICGetProp_ArgumentsLength(JitCode* stubCode)

View File

@ -35,8 +35,6 @@ namespace jit {
_(Compare_Int32WithBoolean) \
\
_(GetProp_Fallback) \
_(GetProp_ArrayLength) \
_(GetProp_UnboxedArrayLength) \
_(GetProp_Primitive) \
_(GetProp_StringLength) \
_(GetProp_Unboxed) \