Bug 921120 - Enable Ion-compilation of JSOP_SETARG for functions which use magic arguments. r=nbp

This commit is contained in:
Kannan Vijayan 2013-09-30 10:24:30 -04:00
parent a2e77150b6
commit 44f1bc368f
15 changed files with 274 additions and 69 deletions

View File

@ -166,6 +166,10 @@ BytecodeAnalysis::init(JSContext *cx)
hasTryFinally_ = true;
break;
case JSOP_SETARG:
hasSetArg_ = true;
break;
default:
break;
}

View File

@ -42,6 +42,7 @@ class BytecodeAnalysis
bool usesScopeChain_;
bool hasTryFinally_;
bool hasSetArg_;
public:
explicit BytecodeAnalysis(JSScript *script);
@ -59,13 +60,17 @@ class BytecodeAnalysis
return nullptr;
}
bool usesScopeChain() {
bool usesScopeChain() const {
return usesScopeChain_;
}
bool hasTryFinally() {
bool hasTryFinally() const {
return hasTryFinally_;
}
bool hasSetArg() const {
return hasSetArg_;
}
};

View File

@ -5455,7 +5455,7 @@ CodeGenerator::visitArgumentsLength(LArgumentsLength *lir)
}
bool
CodeGenerator::visitGetArgument(LGetArgument *lir)
CodeGenerator::visitGetFrameArgument(LGetFrameArgument *lir)
{
ValueOperand result = GetValueOutput(lir);
const LAllocation *index = lir->index();
@ -5473,6 +5473,45 @@ CodeGenerator::visitGetArgument(LGetArgument *lir)
return true;
}
bool
CodeGenerator::visitSetFrameArgumentT(LSetFrameArgumentT *lir)
{
size_t argOffset = frameSize() + IonJSFrameLayout::offsetOfActualArgs() +
(sizeof(Value) * lir->mir()->argno());
MIRType type = lir->mir()->value()->type();
if (type == MIRType_Double) {
// Store doubles directly.
FloatRegister input = ToFloatRegister(lir->input());
masm.storeDouble(input, Address(StackPointer, argOffset));
} else {
Register input = ToRegister(lir->input());
masm.storeValue(ValueTypeFromMIRType(type), input, Address(StackPointer, argOffset));
}
return true;
}
bool
CodeGenerator:: visitSetFrameArgumentC(LSetFrameArgumentC *lir)
{
size_t argOffset = frameSize() + IonJSFrameLayout::offsetOfActualArgs() +
(sizeof(Value) * lir->mir()->argno());
masm.storeValue(lir->val(), Address(StackPointer, argOffset));
return true;
}
bool
CodeGenerator:: visitSetFrameArgumentV(LSetFrameArgumentV *lir)
{
const ValueOperand val = ToValue(lir, LSetFrameArgumentV::Input);
size_t argOffset = frameSize() + IonJSFrameLayout::offsetOfActualArgs() +
(sizeof(Value) * lir->mir()->argno());
masm.storeValue(val, Address(StackPointer, argOffset));
return true;
}
typedef bool (*RunOnceScriptPrologueFn)(JSContext *, HandleScript);
static const VMFunction RunOnceScriptPrologueInfo =
FunctionInfo<RunOnceScriptPrologueFn>(js::RunOnceScriptPrologue);

View File

@ -237,7 +237,10 @@ class CodeGenerator : public CodeGeneratorSpecific
bool visitIteratorMore(LIteratorMore *lir);
bool visitIteratorEnd(LIteratorEnd *lir);
bool visitArgumentsLength(LArgumentsLength *lir);
bool visitGetArgument(LGetArgument *lir);
bool visitGetFrameArgument(LGetFrameArgument *lir);
bool visitSetFrameArgumentT(LSetFrameArgumentT *lir);
bool visitSetFrameArgumentC(LSetFrameArgumentC *lir);
bool visitSetFrameArgumentV(LSetFrameArgumentV *lir);
bool visitRunOncePrologue(LRunOncePrologue *lir);
bool emitRest(LInstruction *lir, Register array, Register numActuals,
Register temp0, Register temp1, unsigned numFormals,

View File

@ -238,6 +238,9 @@ class CompileInfo
bool hasArguments() const {
return script()->argumentsHasVarBinding();
}
bool argumentsAliasesFormals() const {
return script()->argumentsAliasesFormals();
}
bool needsArgsObj() const {
return script()->needsArgsObj();
}

View File

@ -1388,49 +1388,7 @@ IonBuilder::inspectOpcode(JSOp op)
return true;
case JSOP_SETARG:
// To handle this case, we should spill the arguments to the space where
// actual arguments are stored. The tricky part is that if we add a MIR
// to wrap the spilling action, we don't want the spilling to be
// captured by the GETARG and by the resume point, only by
// MGetArgument.
if (info().argsObjAliasesFormals()) {
current->add(MSetArgumentsObjectArg::New(current->argumentsObject(), GET_SLOTNO(pc),
current->peek(-1)));
} else {
// TODO: if hasArguments() is true, and the script has a JSOP_SETARG, then
// convert all arg accesses to go through the arguments object.
if (info().hasArguments())
return abort("NYI: arguments & setarg.");
int32_t arg = GET_SLOTNO(pc);
// If this assignment is at the start of the function and is coercing
// the original value for the argument which was passed in, loosen
// the type information for that original argument if it is currently
// empty due to originally executing in the interpreter.
MDefinition *value = current->peek(-1);
if (graph().numBlocks() == 1 &&
(value->isBitOr() || value->isBitAnd() || value->isMul() /* for JSOP_POS */))
{
for (size_t i = 0; i < value->numOperands(); i++) {
MDefinition *op = value->getOperand(i);
if (op->isParameter() &&
op->toParameter()->index() == arg &&
op->resultTypeSet() &&
op->resultTypeSet()->empty())
{
types::TypeSet *argTypes = types::TypeScript::ArgTypes(script(), arg);
// Update both the original and cloned type set.
argTypes->addType(cx, types::Type::UnknownType());
op->resultTypeSet()->addType(cx, types::Type::UnknownType());
}
}
}
current->setArg(arg);
}
return true;
return jsop_setarg(GET_SLOTNO(pc));
case JSOP_GETLOCAL:
case JSOP_CALLLOCAL:
@ -3379,7 +3337,7 @@ IonBuilder::jsop_try()
return abort("Has try-finally");
// Try-catch within inline frames is not yet supported.
if (callerBuilder_)
if (isInlineBuilder())
return abort("try-catch within inline frame");
graph().setHasTryBlock();
@ -3486,7 +3444,7 @@ IonBuilder::ControlStatus
IonBuilder::processThrow()
{
// JSOP_THROW can't be compiled within inlined frames.
if (callerBuilder_)
if (isInlineBuilder())
return ControlStatus_Abort;
MDefinition *def = current->pop();
@ -6741,7 +6699,7 @@ IonBuilder::getElemTryArguments(bool *emitted, MDefinition *obj, MDefinition *in
if (obj->type() != MIRType_Magic)
return true;
// Emit GetArgument.
// Emit GetFrameArgument.
JS_ASSERT(!info().argsObjAliasesFormals());
@ -6761,7 +6719,7 @@ IonBuilder::getElemTryArguments(bool *emitted, MDefinition *obj, MDefinition *in
index = addBoundsCheck(index, length);
// Load the argument from the actual arguments.
MGetArgument *load = MGetArgument::New(index);
MGetFrameArgument *load = MGetFrameArgument::New(index, analysis_.hasSetArg());
current->add(load);
current->push(load);
@ -9082,6 +9040,67 @@ IonBuilder::jsop_lambda(JSFunction *fun)
return resumeAfter(ins);
}
bool
IonBuilder::jsop_setarg(uint32_t arg)
{
// To handle this case, we should spill the arguments to the space where
// actual arguments are stored. The tricky part is that if we add a MIR
// to wrap the spilling action, we don't want the spilling to be
// captured by the GETARG and by the resume point, only by
// MGetFrameArgument.
JS_ASSERT(analysis_.hasSetArg());
MDefinition *val = current->peek(-1);
// If an arguments object is in use, and it aliases formals, then all SETARGs
// must go through the arguments object.
if (info().argsObjAliasesFormals()) {
current->add(MSetArgumentsObjectArg::New(current->argumentsObject(), GET_SLOTNO(pc), val));
return true;
}
// Otherwise, if a magic arguments is in use, and it aliases formals, and there exist
// arguments[...] GETELEM expressions in the script, then SetFrameArgument must be used.
// If no arguments[...] GETELEM expressions are in the script, and an argsobj is not
// required, then it means that any aliased argument set can never be observed, and
// the frame does not actually need to be updated with the new arg value.
if (info().argumentsAliasesFormals()) {
// Try-catch within inline frames is not yet supported.
if (isInlineBuilder())
return abort("JSOP_SETARG with magic arguments in inlined function.");
MSetFrameArgument *store = MSetFrameArgument::New(arg, val);
current->add(store);
current->setArg(arg);
return true;
}
// If this assignment is at the start of the function and is coercing
// the original value for the argument which was passed in, loosen
// the type information for that original argument if it is currently
// empty due to originally executing in the interpreter.
if (graph().numBlocks() == 1 &&
(val->isBitOr() || val->isBitAnd() || val->isMul() /* for JSOP_POS */))
{
for (size_t i = 0; i < val->numOperands(); i++) {
MDefinition *op = val->getOperand(i);
if (op->isParameter() &&
op->toParameter()->index() == (int32_t)arg &&
op->resultTypeSet() &&
op->resultTypeSet()->empty())
{
types::TypeSet *argTypes = types::TypeScript::ArgTypes(script(), arg);
// Update both the original and cloned type set.
argTypes->addType(cx, types::Type::UnknownType());
op->resultTypeSet()->addType(cx, types::Type::UnknownType());
}
}
}
current->setArg(arg);
return true;
}
bool
IonBuilder::jsop_defvar(uint32_t index)
{

View File

@ -447,6 +447,7 @@ class IonBuilder : public MIRGenerator
bool jsop_binary(JSOp op, MDefinition *left, MDefinition *right);
bool jsop_pos();
bool jsop_neg();
bool jsop_setarg(uint32_t arg);
bool jsop_defvar(uint32_t index);
bool jsop_deffun(uint32_t index);
bool jsop_notearg();
@ -673,6 +674,10 @@ class IonBuilder : public MIRGenerator
TypeRepresentationSetHash *getOrCreateReprSetHash(); // fallible
bool isInlineBuilder() const {
return callerBuilder_ != NULL;
}
private:
bool init();

View File

@ -4552,13 +4552,13 @@ class LArgumentsLength : public LInstructionHelper<1, 0, 0>
};
// Load a value from the actual arguments.
class LGetArgument : public LInstructionHelper<BOX_PIECES, 1, 0>
class LGetFrameArgument : public LInstructionHelper<BOX_PIECES, 1, 0>
{
public:
LIR_HEADER(GetArgument)
LIR_HEADER(GetFrameArgument)
BOX_OUTPUT_ACCESSORS()
LGetArgument(const LAllocation &index) {
LGetFrameArgument(const LAllocation &index) {
setOperand(0, index);
}
const LAllocation *index() {
@ -4566,6 +4566,58 @@ class LGetArgument : public LInstructionHelper<BOX_PIECES, 1, 0>
}
};
// Load a value from the actual arguments.
class LSetFrameArgumentT : public LInstructionHelper<0, 1, 0>
{
public:
LIR_HEADER(SetFrameArgumentT)
LSetFrameArgumentT(const LAllocation &input) {
setOperand(0, input);
}
MSetFrameArgument *mir() const {
return mir_->toSetFrameArgument();
}
const LAllocation *input() {
return getOperand(0);
}
};
// Load a value from the actual arguments.
class LSetFrameArgumentC : public LInstructionHelper<0, 0, 0>
{
Value val_;
public:
LIR_HEADER(SetFrameArgumentC)
LSetFrameArgumentC(const Value &val) {
val_ = val;
}
MSetFrameArgument *mir() const {
return mir_->toSetFrameArgument();
}
const Value &val() const {
return val_;
}
};
// Load a value from the actual arguments.
class LSetFrameArgumentV : public LInstructionHelper<0, BOX_PIECES, 0>
{
public:
LIR_HEADER(SetFrameArgumentV)
BOX_OUTPUT_ACCESSORS()
LSetFrameArgumentV() {}
static const size_t Input = 0;
MSetFrameArgument *mir() const {
return mir_->toSetFrameArgument();
}
};
class LRunOncePrologue : public LCallInstructionHelper<0, 0, 0>
{
public:

View File

@ -227,7 +227,10 @@
_(TypedObjectElements) \
_(StringLength) \
_(ArgumentsLength) \
_(GetArgument) \
_(GetFrameArgument) \
_(SetFrameArgumentT) \
_(SetFrameArgumentC) \
_(SetFrameArgumentV) \
_(RunOncePrologue) \
_(Rest) \
_(RestPar) \

View File

@ -2914,12 +2914,34 @@ LIRGenerator::visitArgumentsLength(MArgumentsLength *ins)
}
bool
LIRGenerator::visitGetArgument(MGetArgument *ins)
LIRGenerator::visitGetFrameArgument(MGetFrameArgument *ins)
{
LGetArgument *lir = new LGetArgument(useRegisterOrConstant(ins->index()));
LGetFrameArgument *lir = new LGetFrameArgument(useRegisterOrConstant(ins->index()));
return defineBox(lir, ins);
}
bool
LIRGenerator::visitSetFrameArgument(MSetFrameArgument *ins)
{
MDefinition *input = ins->input();
if (input->type() == MIRType_Value) {
LSetFrameArgumentV *lir = new LSetFrameArgumentV();
if (!useBox(lir, LSetFrameArgumentV::Input, input))
return false;
return add(lir, ins);
}
if (input->type() == MIRType_Undefined || input->type() == MIRType_Null) {
Value val = input->type() == MIRType_Undefined ? UndefinedValue() : NullValue();
LSetFrameArgumentC *lir = new LSetFrameArgumentC(val);
return add(lir, ins);
}
LSetFrameArgumentT *lir = new LSetFrameArgumentT(useRegister(input));
return add(lir, ins);
}
bool
LIRGenerator::visitRunOncePrologue(MRunOncePrologue *ins)
{

View File

@ -231,7 +231,8 @@ class LIRGenerator : public LIRGeneratorSpecific
bool visitIteratorEnd(MIteratorEnd *ins);
bool visitStringLength(MStringLength *ins);
bool visitArgumentsLength(MArgumentsLength *ins);
bool visitGetArgument(MGetArgument *ins);
bool visitGetFrameArgument(MGetFrameArgument *ins);
bool visitSetFrameArgument(MSetFrameArgument *ins);
bool visitRunOncePrologue(MRunOncePrologue *ins);
bool visitRest(MRest *ins);
bool visitRestPar(MRestPar *ins);

View File

@ -218,12 +218,13 @@ class AliasSet {
FixedSlot = 1 << 3, // A member of obj->fixedSlots().
TypedArrayElement = 1 << 4, // A typed array element.
DOMProperty = 1 << 5, // A DOM property
AsmJSGlobalVar = 1 << 6, // An asm.js global var
AsmJSHeap = 1 << 7, // An asm.js heap load
FrameArgument = 1 << 6, // An argument kept on the stack frame
AsmJSGlobalVar = 1 << 7, // An asm.js global var
AsmJSHeap = 1 << 8, // An asm.js heap load
Last = AsmJSHeap,
Any = Last | (Last - 1),
NumCategories = 8,
NumCategories = 9,
// Indicates load or store.
Store_ = 1 << 31
@ -7743,22 +7744,25 @@ class MArgumentsLength : public MNullaryInstruction
};
// This MIR instruction is used to get an argument from the actual arguments.
class MGetArgument
class MGetFrameArgument
: public MUnaryInstruction,
public IntPolicy<0>
{
MGetArgument(MDefinition *idx)
: MUnaryInstruction(idx)
bool scriptHasSetArg_;
MGetFrameArgument(MDefinition *idx, bool scriptHasSetArg)
: MUnaryInstruction(idx),
scriptHasSetArg_(scriptHasSetArg)
{
setResultType(MIRType_Value);
setMovable();
}
public:
INSTRUCTION_HEADER(GetArgument)
INSTRUCTION_HEADER(GetFrameArgument)
static MGetArgument *New(MDefinition *idx) {
return new MGetArgument(idx);
static MGetFrameArgument *New(MDefinition *idx, bool scriptHasSetArg) {
return new MGetFrameArgument(idx, scriptHasSetArg);
}
MDefinition *index() const {
@ -7772,10 +7776,50 @@ class MGetArgument
return congruentIfOperandsEqual(ins);
}
AliasSet getAliasSet() const {
// If the script doesn't have any JSOP_SETARG ops, then this instruction is never
// aliased.
if (scriptHasSetArg_)
return AliasSet::Load(AliasSet::FrameArgument);
return AliasSet::None();
}
};
// This MIR instruction is used to set an argument value in the frame.
class MSetFrameArgument
: public MUnaryInstruction
{
uint32_t argno_;
MSetFrameArgument(uint32_t argno, MDefinition *value)
: MUnaryInstruction(value),
argno_(argno)
{
setMovable();
}
public:
INSTRUCTION_HEADER(SetFrameArgument)
static MSetFrameArgument *New(uint32_t argno, MDefinition *value) {
return new MSetFrameArgument(argno, value);
}
uint32_t argno() const {
return argno_;
}
MDefinition *value() const {
return getOperand(0);
}
bool congruentTo(MDefinition *ins) const {
return false;
}
AliasSet getAliasSet() const {
return AliasSet::Store(AliasSet::FrameArgument);
}
};
class MRestCommon
{
unsigned numFormals_;

View File

@ -165,7 +165,8 @@ namespace jit {
_(IteratorEnd) \
_(StringLength) \
_(ArgumentsLength) \
_(GetArgument) \
_(GetFrameArgument) \
_(SetFrameArgument) \
_(RunOncePrologue) \
_(Rest) \
_(Floor) \

View File

@ -248,7 +248,8 @@ class ParallelSafetyVisitor : public MInstructionVisitor
UNSAFE_OP(IteratorEnd)
SAFE_OP(StringLength)
SAFE_OP(ArgumentsLength)
SAFE_OP(GetArgument)
SAFE_OP(GetFrameArgument)
UNSAFE_OP(SetFrameArgument)
UNSAFE_OP(RunOncePrologue)
CUSTOM_OP(Rest)
SAFE_OP(RestPar)

View File

@ -663,6 +663,9 @@ class JSScript : public js::gc::BarrieredCell<JSScript>
bool argumentsHasVarBinding() const { return argsHasVarBinding_; }
jsbytecode *argumentsBytecode() const { JS_ASSERT(code[0] == JSOP_ARGUMENTS); return code; }
void setArgumentsHasVarBinding();
bool argumentsAliasesFormals() const {
return argumentsHasVarBinding() && !strict;
}
js::GeneratorKind generatorKind() const {
return js::GeneratorKindFromBits(generatorKindBits_);