Bug 861596 - Add Baseline stubs to handle GetProp(length) and GetElem(int32) operations on arguments objects. r=bhackett

This commit is contained in:
Kannan Vijayan 2013-04-18 18:00:23 -04:00
Родитель 6174808648
Коммит 3eeee69bed
6 изменённых файлов: 414 добавлений и 1 удалений

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

@ -3346,6 +3346,18 @@ TypedArrayGetElemStubExists(ICGetElem_Fallback *stub, HandleObject obj)
return false;
}
static bool
ArgumentsGetElemStubExists(ICGetElem_Fallback *stub, ICGetElem_Arguments::Which which)
{
for (ICStubConstIterator iter = stub->beginChainConst(); !iter.atEnd(); iter++) {
if (!iter->isGetElem_Arguments())
continue;
if (iter->toGetElem_Arguments()->which() == which)
return true;
}
return false;
}
static bool TryAttachNativeGetElemStub(JSContext *cx, HandleScript script,
ICGetElem_Fallback *stub, HandleObject obj,
@ -3417,11 +3429,43 @@ TryAttachGetElemStub(JSContext *cx, HandleScript script, ICGetElem_Fallback *stu
return true;
}
if (lhs.isMagic(JS_OPTIMIZED_ARGUMENTS) && rhs.isInt32() &&
!ArgumentsGetElemStubExists(stub, ICGetElem_Arguments::Magic))
{
IonSpew(IonSpew_BaselineIC, " Generating GetElem(MagicArgs[Int32]) stub");
ICGetElem_Arguments::Compiler compiler(cx, stub->fallbackMonitorStub()->firstMonitorStub(),
ICGetElem_Arguments::Magic);
ICStub *argsStub = compiler.getStub(compiler.getStubSpace(script));
if (!argsStub)
return false;
stub->addNewStub(argsStub);
return true;
}
// Otherwise, GetElem is only optimized on objects.
if (!lhs.isObject())
return true;
RootedObject obj(cx, &lhs.toObject());
// Check for ArgumentsObj[int] accesses
if (obj->isArguments() && rhs.isInt32()) {
ICGetElem_Arguments::Which which = ICGetElem_Arguments::Normal;
if (obj->isStrictArguments())
which = ICGetElem_Arguments::Strict;
if (!ArgumentsGetElemStubExists(stub, which)) {
IonSpew(IonSpew_BaselineIC, " Generating GetElem(ArgsObj[Int32]) stub");
ICGetElem_Arguments::Compiler compiler(
cx, stub->fallbackMonitorStub()->firstMonitorStub(), which);
ICStub *argsStub = compiler.getStub(compiler.getStubSpace(script));
if (!argsStub)
return false;
stub->addNewStub(argsStub);
return true;
}
}
if (obj->isNative()) {
// Check for NativeObject[int] dense accesses.
if (rhs.isInt32()) {
@ -3743,6 +3787,138 @@ ICGetElem_TypedArray::Compiler::generateStubCode(MacroAssembler &masm)
return true;
}
//
// GetEelem_Arguments
//
bool
ICGetElem_Arguments::Compiler::generateStubCode(MacroAssembler &masm)
{
Label failure;
if (which_ == ICGetElem_Arguments::Magic) {
// Ensure that this is a magic arguments value.
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 index is an integer.
masm.branchTestInt32(Assembler::NotEqual, R1, &failure);
Register idx = masm.extractInt32(R1, ExtractTemp1);
GeneralRegisterSet regs(availableGeneralRegs(2));
Register scratch = regs.takeAny();
// Load num actual arguments
Address actualArgs(BaselineFrameReg, BaselineFrame::offsetOfNumActualArgs());
masm.loadPtr(actualArgs, scratch);
// Ensure idx < argc
masm.branch32(Assembler::AboveOrEqual, idx, scratch, &failure);
// Load argval
JS_ASSERT(sizeof(Value) == 8);
masm.movePtr(BaselineFrameReg, scratch);
masm.addPtr(Imm32(BaselineFrame::offsetOfArg(0)), scratch);
BaseIndex element(scratch, idx, TimesEight);
masm.loadValue(element, R0);
// Enter type monitor IC to type-check result.
EmitEnterTypeMonitorIC(masm);
masm.bind(&failure);
EmitStubGuardFailure(masm);
return true;
}
JS_ASSERT(which_ == ICGetElem_Arguments::Strict ||
which_ == ICGetElem_Arguments::Normal);
bool isStrict = which_ == ICGetElem_Arguments::Strict;
Class *clasp = isStrict ? &StrictArgumentsObjectClass : &NormalArgumentsObjectClass;
GeneralRegisterSet regs(availableGeneralRegs(2));
Register scratchReg = regs.takeAny();
// 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);
// Guard on index being int32
masm.branchTestInt32(Assembler::NotEqual, R1, &failure);
Register idxReg = masm.extractInt32(R1, ExtractTemp1);
// Get initial ArgsObj length value.
masm.unboxInt32(Address(objReg, ArgumentsObject::getInitialLengthSlotOffset()), scratchReg);
// Test if length has been overridden.
masm.branchTest32(Assembler::NonZero,
scratchReg,
Imm32(ArgumentsObject::LENGTH_OVERRIDDEN_BIT),
&failure);
// Length has not been overridden, ensure that R1 is an integer and is <= length.
masm.rshiftPtr(Imm32(ArgumentsObject::PACKED_BITS_COUNT), scratchReg);
masm.branch32(Assembler::AboveOrEqual, idxReg, scratchReg, &failure);
// Length check succeeded, now check the correct bit. We clobber potential type regs
// now. Inputs will have to be reconstructed if we fail after this point, but that's
// unlikely.
Label failureReconstructInputs;
regs = availableGeneralRegs(0);
if (regs.has(objReg))
regs.take(objReg);
if (regs.has(idxReg))
regs.take(idxReg);
if (regs.has(scratchReg))
regs.take(scratchReg);
Register argData = regs.takeAny();
Register tempReg = regs.takeAny();
// Load ArgumentsData
masm.loadPrivate(Address(objReg, ArgumentsObject::getDataSlotOffset()), argData);
// Load deletedBits bitArray pointer into scratchReg
masm.loadPtr(Address(argData, offsetof(ArgumentsData, deletedBits)), scratchReg);
// In tempReg, calculate index of word containing bit: (idx >> logBitsPerWord)
masm.movePtr(idxReg, tempReg);
masm.rshiftPtr(Imm32(JS_BITS_PER_WORD_LOG2), tempReg);
masm.loadPtr(BaseIndex(scratchReg, tempReg, ScaleFromElemWidth(sizeof(size_t))), scratchReg);
// Don't bother testing specific bit, if any bit is set in the word, fail.
masm.branchPtr(Assembler::NotEqual, scratchReg, ImmWord((size_t)0), &failureReconstructInputs);
// Load the value. use scratchReg and tempReg to form a ValueOperand to load into.
masm.addPtr(Imm32(ArgumentsData::offsetOfArgs()), argData);
regs.add(scratchReg);
regs.add(tempReg);
ValueOperand tempVal = regs.takeAnyValue();
masm.loadValue(BaseIndex(argData, idxReg, ScaleFromElemWidth(sizeof(Value))), tempVal);
// Makesure that this is not a FORWARD_TO_CALL_SLOT magic value.
masm.branchTestMagic(Assembler::Equal, tempVal, &failureReconstructInputs);
// Everything checked out, return value.
masm.moveValue(tempVal, R0);
// Type-check result
EmitEnterTypeMonitorIC(masm);
// Failed, but inputs are deconstructed into object and int, and need to be
// reconstructed into values.
masm.bind(&failureReconstructInputs);
masm.tagValue(JSVAL_TYPE_OBJECT, objReg, R0);
masm.tagValue(JSVAL_TYPE_INT32, idxReg, R1);
masm.bind(&failure);
EmitStubGuardFailure(masm);
return true;
}
//
// SetElem_Fallback
//
@ -4849,6 +5025,18 @@ TryAttachLengthStub(JSContext *cx, HandleScript script, ICGetProp_Fallback *stub
return true;
}
if (val.isMagic(JS_OPTIMIZED_ARGUMENTS) && res.isInt32()) {
IonSpew(IonSpew_BaselineIC, " Generating GetProp(MagicArgs.length) stub");
ICGetProp_ArgumentsLength::Compiler compiler(cx, ICGetProp_ArgumentsLength::Magic);
ICStub *newStub = compiler.getStub(compiler.getStubSpace(script));
if (!newStub)
return false;
*attached = true;
stub->addNewStub(newStub);
return true;
}
if (!val.isObject())
return true;
@ -4878,6 +5066,22 @@ TryAttachLengthStub(JSContext *cx, HandleScript script, ICGetProp_Fallback *stub
return true;
}
if (obj->isArguments() && res.isInt32()) {
IonSpew(IonSpew_BaselineIC, " Generating GetProp(ArgsObj.length %s) stub",
obj->isStrictArguments() ? "Strict" : "Normal");
ICGetProp_ArgumentsLength::Which which = ICGetProp_ArgumentsLength::Normal;
if (obj->isStrictArguments())
which = ICGetProp_ArgumentsLength::Strict;
ICGetProp_ArgumentsLength::Compiler compiler(cx, which);
ICStub *newStub = compiler.getStub(compiler.getStubSpace(script));
if (!newStub)
return false;
*attached = true;
stub->addNewStub(newStub);
return true;
}
return true;
}
@ -5032,9 +5236,18 @@ DoGetPropFallback(JSContext *cx, BaselineFrame *frame, ICGetProp_Fallback *stub,
if (op == JSOP_LENGTH && val.isMagic(JS_OPTIMIZED_ARGUMENTS)) {
// Handle arguments.length access.
if (IsOptimizedArguments(frame, val.address())) {
// TODO: attach optimized stub.
res.setInt32(frame->numActualArgs());
// Monitor result
types::TypeScript::Monitor(cx, script, pc, res);
if (!stub->addMonitorStubForValue(cx, script, res))
return false;
bool attached = false;
if (!TryAttachLengthStub(cx, script, stub, val, res, &attached))
return false;
JS_ASSERT(attached);
return true;
}
}
@ -5567,6 +5780,62 @@ ICGetProp_CallListBaseNative::Compiler::generateStubCode(MacroAssembler &masm)
return true;
}
bool
ICGetProp_ArgumentsLength::Compiler::generateStubCode(MacroAssembler &masm)
{
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);
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;
}
JS_ASSERT(which_ == ICGetProp_ArgumentsLength::Strict ||
which_ == ICGetProp_ArgumentsLength::Normal);
bool isStrict = which_ == ICGetProp_ArgumentsLength::Strict;
Class *clasp = isStrict ? &StrictArgumentsObjectClass : &NormalArgumentsObjectClass;
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.
masm.branchTest32(Assembler::NonZero,
scratchReg,
Imm32(ArgumentsObject::LENGTH_OVERRIDDEN_BIT),
&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);
EmitReturnFromIC(masm);
masm.bind(&failure);
EmitStubGuardFailure(masm);
return true;
}
//
// SetProp_Fallback
//

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

@ -331,6 +331,7 @@ class ICEntry
_(GetElem_String) \
_(GetElem_Dense) \
_(GetElem_TypedArray) \
_(GetElem_Arguments) \
\
_(SetElem_Fallback) \
_(SetElem_Dense) \
@ -364,6 +365,7 @@ class ICEntry
_(GetProp_CallScripted) \
_(GetProp_CallNative) \
_(GetProp_CallListBaseNative)\
_(GetProp_ArgumentsLength) \
\
_(SetProp_Fallback) \
_(SetProp_Native) \
@ -3034,6 +3036,56 @@ class ICGetElem_TypedArray : public ICStub
};
};
class ICGetElem_Arguments : public ICMonitoredStub
{
friend class ICStubSpace;
public:
enum Which { Normal, Strict, Magic };
private:
ICGetElem_Arguments(IonCode *stubCode, ICStub *firstMonitorStub, Which which)
: ICMonitoredStub(ICStub::GetElem_Arguments, stubCode, firstMonitorStub)
{
extra_ = static_cast<uint16_t>(which);
}
public:
static inline ICGetElem_Arguments *New(ICStubSpace *space, IonCode *code,
ICStub *firstMonitorStub, Which which)
{
if (!code)
return NULL;
return space->allocate<ICGetElem_Arguments>(code, firstMonitorStub, which);
}
Which which() const {
return static_cast<Which>(extra_);
}
class Compiler : public ICStubCompiler {
ICStub *firstMonitorStub_;
Which which_;
protected:
bool generateStubCode(MacroAssembler &masm);
virtual int32_t getKey() const {
return static_cast<int32_t>(kind) | (static_cast<int32_t>(which_) << 16);
}
public:
Compiler(JSContext *cx, ICStub *firstMonitorStub, Which which)
: ICStubCompiler(cx, ICStub::GetElem_Arguments),
firstMonitorStub_(firstMonitorStub),
which_(which)
{}
ICStub *getStub(ICStubSpace *space) {
return ICGetElem_Arguments::New(space, getStubCode(), firstMonitorStub_, which_);
}
};
};
// SetElem
// JSOP_SETELEM
// JSOP_INITELEM
@ -4219,6 +4271,47 @@ class ICGetProp_CallListBaseNative : public ICMonitoredStub
};
};
class ICGetProp_ArgumentsLength : public ICStub
{
friend class ICStubSpace;
public:
enum Which { Normal, Strict, Magic };
protected:
ICGetProp_ArgumentsLength(IonCode *stubCode)
: ICStub(ICStub::GetProp_ArgumentsLength, stubCode)
{ }
public:
static inline ICGetProp_ArgumentsLength *New(ICStubSpace *space, IonCode *code)
{
if (!code)
return NULL;
return space->allocate<ICGetProp_ArgumentsLength>(code);
}
class Compiler : public ICStubCompiler {
protected:
Which which_;
bool generateStubCode(MacroAssembler &masm);
virtual int32_t getKey() const {
return static_cast<int32_t>(kind) | (static_cast<int32_t>(which_) << 16);
}
public:
Compiler(JSContext *cx, Which which)
: ICStubCompiler(cx, ICStub::GetProp_ArgumentsLength),
which_(which)
{}
ICStub *getStub(ICStubSpace *space) {
return ICGetProp_ArgumentsLength::New(space, getStubCode());
}
};
};
// SetProp
// JSOP_SETPROP
// JSOP_SETNAME

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

@ -804,6 +804,17 @@ class MacroAssemblerARMCompat : public MacroAssemblerARM
cond = testMagic(cond, t);
ma_b(label, cond);
}
void branchTestMagicValue(Condition cond, const ValueOperand &val, JSWhyMagic why,
Label *label) {
JS_ASSERT(cond == Equal || cond == NotEqual);
// Test for magic
Label notmagic;
Condition testCond = testMagic(cond, val);
ma_b(&notmagic, InvertCondition(testCond));
// Test magic value
branch32(cond, val.payloadReg(), Imm32(static_cast<int32_t>(why)), label);
bind(&notmagic);
}
template<typename T>
void branchTestBooleanTruthy(bool b, const T & t, Label *label) {
Condition c = testBooleanTruthy(b, t);

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

@ -705,6 +705,19 @@ class MacroAssemblerX64 : public MacroAssemblerX86Shared
cond = testMagic(cond, t);
j(cond, label);
}
void branchTestMagicValue(Condition cond, const ValueOperand &val, JSWhyMagic why,
Label *label)
{
JS_ASSERT(cond == Equal || cond == NotEqual);
// Test for magic
Label notmagic;
Condition testCond = testMagic(cond, val);
j(InvertCondition(testCond), &notmagic);
// Test magic value
unboxMagic(val, ScratchReg);
branch32(cond, ScratchReg, Imm32(static_cast<int32_t>(why)), label);
bind(&notmagic);
}
Condition testMagic(Condition cond, const ValueOperand &src) {
splitTag(src, ScratchReg);
return testMagic(cond, ScratchReg);
@ -768,6 +781,10 @@ class MacroAssemblerX64 : public MacroAssemblerX86Shared
unboxBoolean(Operand(src), dest);
}
void unboxMagic(const ValueOperand &src, const Register &dest) {
movl(Operand(src.valueReg()), dest);
}
void unboxDouble(const ValueOperand &src, const FloatRegister &dest) {
movqsd(src.valueReg(), dest);
}

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

@ -607,6 +607,24 @@ class MacroAssemblerX86 : public MacroAssemblerX86Shared
cond = testMagic(cond, t);
j(cond, label);
}
void branchTestMagicValue(Condition cond, const ValueOperand &val, JSWhyMagic why,
Label *label)
{
JS_ASSERT(cond == Equal || cond == NotEqual);
if (cond == Equal) {
// Test for magic
Label notmagic;
Condition testCond = testMagic(Equal, val);
j(InvertCondition(testCond), &notmagic);
// Test magic value
branch32(NotEqual, val.payloadReg(), Imm32(static_cast<int32_t>(why)), label);
bind(&notmagic);
} else {
Condition testCond = testMagic(NotEqual, val);
j(testCond, label);
branch32(NotEqual, val.payloadReg(), Imm32(static_cast<int32_t>(why)), label);
}
}
// Note: this function clobbers the source register.
void boxDouble(const FloatRegister &src, const ValueOperand &dest) {

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

@ -99,9 +99,11 @@ class ArgumentsObject : public JSObject
static const uint32_t DATA_SLOT = 1;
static const uint32_t MAYBE_CALL_SLOT = 2;
public:
static const uint32_t LENGTH_OVERRIDDEN_BIT = 0x1;
static const uint32_t PACKED_BITS_COUNT = 1;
protected:
template <typename CopyArgs>
static ArgumentsObject *create(JSContext *cx, HandleScript script, HandleFunction callee,
unsigned numActuals, CopyArgs &copy);
@ -204,6 +206,9 @@ class ArgumentsObject : public JSObject
static size_t getDataSlotOffset() {
return getFixedSlotOffset(DATA_SLOT);
}
static size_t getInitialLengthSlotOffset() {
return getFixedSlotOffset(INITIAL_LENGTH_SLOT);
}
static void MaybeForwardToCallObject(AbstractFramePtr frame, JSObject *obj, ArgumentsData *data);
#if defined(JS_ION)