Bug 768270 - Inline constructors. r=dvander

This commit is contained in:
Sean Stangl 2012-06-28 18:58:51 -07:00
parent 3dac2376bd
commit db0466b390
11 changed files with 162 additions and 33 deletions

View File

@ -1245,6 +1245,31 @@ CodeGenerator::visitOutOfLineCreateThis(OutOfLineCreateThis *ool)
return true;
}
bool
CodeGenerator::visitReturnFromCtor(LReturnFromCtor *lir)
{
ValueOperand value = ToValue(lir, LReturnFromCtor::ValueIndex);
Register obj = ToRegister(lir->getObject());
Register output = ToRegister(lir->output());
Label valueIsObject, end;
masm.branchTestObject(Assembler::Equal, value, &valueIsObject);
// Value is not an object. Return that other object.
masm.movePtr(obj, output);
masm.jump(&end);
// Value is an object. Return unbox(Value).
masm.bind(&valueIsObject);
Register payload = masm.extractObject(value, output);
if (payload != output)
masm.movePtr(payload, output);
masm.bind(&end);
return true;
}
bool
CodeGenerator::visitArrayLength(LArrayLength *lir)
{

View File

@ -120,6 +120,7 @@ class CodeGenerator : public CodeGeneratorSpecific
bool visitCreateThisVMCall(LCreateThis *lir);
bool visitCreateThis(LCreateThis *lir);
bool visitOutOfLineCreateThis(OutOfLineCreateThis *ool);
bool visitReturnFromCtor(LReturnFromCtor *lir);
bool visitArrayLength(LArrayLength *lir);
bool visitTypedArrayLength(LTypedArrayLength *lir);
bool visitTypedArrayElements(LTypedArrayElements *lir);

View File

@ -362,30 +362,36 @@ bool
IonBuilder::buildInline(IonBuilder *callerBuilder, MResumePoint *callerResumePoint,
MDefinition *thisDefn, MDefinitionVector &argv)
{
IonSpew(IonSpew_MIR, "Inlining script %s:%d (%p)",
script->filename, script->lineno, (void *)script);
callerBuilder_ = callerBuilder;
callerResumePoint_ = callerResumePoint;
// Generate single entrance block.
current = newBlock(pc);
if (!current)
return false;
IonSpew(IonSpew_MIR, "Inlining script %s:%d (%p)",
script->filename, script->lineno, (void *) script);
current->setCallerResumePoint(callerResumePoint);
callerBuilder_ = callerBuilder;
callerResumePoint_ = callerResumePoint;
// Connect the entrance block to the last block in the caller's graph.
MBasicBlock *predecessor = callerResumePoint->block();
predecessor->end(MGoto::New(current));
if (!current->addPredecessorWithoutPhis(predecessor))
return false;
JS_ASSERT(predecessor->numSuccessors() == 1);
JS_ASSERT(current->numPredecessors() == 1);
current->setCallerResumePoint(callerResumePoint);
// Fill in any missing arguments with undefined.
// Explicitly pass Undefined for missing arguments.
const size_t numActualArgs = argv.length() - 1;
const size_t nargs = info().nargs();
IonSpew(IonSpew_MIR, "Inlining with %s of arguments.",
(numActualArgs == nargs) ? "right number": ((numActualArgs < nargs) ? "underflow" : "overflow"));
if (numActualArgs < nargs) {
for (size_t i = 0, missing = nargs - numActualArgs; i < missing; ++i) {
const size_t missing = nargs - numActualArgs;
for (size_t i = 0; i < missing; i++) {
MConstant *undef = MConstant::New(UndefinedValue());
current->add(undef);
if (!argv.append(undef))
@ -393,8 +399,7 @@ IonBuilder::buildInline(IonBuilder *callerBuilder, MResumePoint *callerResumePoi
}
}
// The oracle ensures that the inlined script does not use the scope chain, so we
// just initialize its slot to |undefined|.
// The Oracle ensures that the inlined script does not use the scope chain.
JS_ASSERT(!script->analysis()->usesScopeChain());
MInstruction *scope = MConstant::New(UndefinedValue());
current->add(scope);
@ -425,7 +430,7 @@ IonBuilder::buildInline(IonBuilder *callerBuilder, MResumePoint *callerResumePoi
IonSpew(IonSpew_Inlining, "Inline entry block MResumePoint %p, %u operands",
(void *) current->entryResumePoint(), current->entryResumePoint()->numOperands());
// Note: +2 for the scope chain and |this|.
// +2 for the scope chain and |this|.
JS_ASSERT(current->entryResumePoint()->numOperands() == nargs + info().nlocals() + 2);
return traverseBytecode();
@ -2724,7 +2729,8 @@ IonBuilder::jsop_notearg()
}
bool
IonBuilder::jsop_call_inline(JSFunction *callee, uint32 argc, IonBuilder &inlineBuilder)
IonBuilder::jsop_call_inline(IonBuilder &inlineBuilder, HandleFunction callee,
uint32 argc, bool constructing)
{
#ifdef DEBUG
uint32 origStackDepth = current->stackDepth();
@ -2740,8 +2746,8 @@ IonBuilder::jsop_call_inline(JSFunction *callee, uint32 argc, IonBuilder &inline
// This resume point collects outer variables only. It is used to recover
// the stack state before the current bytecode.
MResumePoint *inlineResumePoint = MResumePoint::New(top, pc, callerResumePoint_,
MResumePoint::Outer);
MResumePoint *inlineResumePoint =
MResumePoint::New(top, pc, callerResumePoint_, MResumePoint::Outer);
if (!inlineResumePoint)
return false;
@ -2752,8 +2758,6 @@ IonBuilder::jsop_call_inline(JSFunction *callee, uint32 argc, IonBuilder &inline
// Note that we leave the callee on the simulated stack for the
// duration of the call.
MDefinitionVector argv;
// Arguments are popped right-to-left so we have to fill |args| backwards.
if (!discardCallArgs(argc, argv, top))
return false;
@ -2764,7 +2768,15 @@ IonBuilder::jsop_call_inline(JSFunction *callee, uint32 argc, IonBuilder &inline
current->pop();
current->push(constFun);
MDefinition *thisDefn = argv[0];
// Create |this| on the caller-side for inlined constructors.
MDefinition *thisDefn = NULL;
if (constructing) {
thisDefn = createThis(callee, constFun);
if (!thisDefn)
return false;
} else {
thisDefn = argv[0];
}
// Build the graph.
if (!inlineBuilder.buildInline(this, inlineResumePoint, thisDefn, argv))
@ -2778,16 +2790,29 @@ IonBuilder::jsop_call_inline(JSFunction *callee, uint32 argc, IonBuilder &inline
MBasicBlock *bottom = newBlock(NULL, postCall);
bottom->setCallerResumePoint(callerResumePoint_);
// Link graph exits to |bottom| via MGotos, replacing MReturns.
// Replace all MReturns with MGotos, and remember the MDefinition that
// would have been returned.
Vector<MDefinition *, 8, IonAllocPolicy> retvalDefns;
for (MBasicBlock **it = exits.begin(), **end = exits.end(); it != end; ++it) {
MBasicBlock *exitBlock = *it;
JS_ASSERT(exitBlock->lastIns()->op() == MDefinition::Op_Return);
if (!retvalDefns.append(exitBlock->lastIns()->toReturn()->getOperand(0)))
MDefinition *rval = exitBlock->lastIns()->toReturn()->getOperand(0);
exitBlock->discardLastIns();
// Inlined constructors return |this| unless overridden by another Object.
if (constructing) {
if (rval->type() == MIRType_Value) {
MReturnFromCtor *filter = MReturnFromCtor::New(rval, thisDefn);
rval->block()->add(filter);
rval = filter;
} else if (rval->type() != MIRType_Object) {
rval = thisDefn;
}
}
if (!retvalDefns.append(rval))
return false;
exitBlock->discardLastIns();
MGoto *replacement = MGoto::New(bottom);
exitBlock->end(replacement);
if (!bottom->addPredecessorWithoutPhis(exitBlock))
@ -2859,7 +2884,7 @@ static bool IsSmallFunction(JSFunction *target) {
}
bool
IonBuilder::makeInliningDecision(JSFunction *target)
IonBuilder::makeInliningDecision(HandleFunction target)
{
static const size_t INLINING_LIMIT = 3;
@ -2898,7 +2923,7 @@ IonBuilder::makeInliningDecision(JSFunction *target)
}
bool
IonBuilder::inlineScriptedCall(JSFunction *target, uint32 argc)
IonBuilder::inlineScriptedCall(HandleFunction target, uint32 argc, bool constructing)
{
IonSpew(IonSpew_Inlining, "Recursively building");
@ -2920,7 +2945,7 @@ IonBuilder::inlineScriptedCall(JSFunction *target, uint32 argc)
IonBuilder inlineBuilder(cx, scopeChain, temp(), graph(), &oracle,
*info, inliningDepth + 1, loopDepth_);
return jsop_call_inline(target, argc, inlineBuilder);
return jsop_call_inline(inlineBuilder, target, argc, constructing);
}
void
@ -3158,8 +3183,8 @@ IonBuilder::jsop_call_fun_barrier(HandleFunction target, uint32 argc,
}
}
if (!constructing && makeInliningDecision(target))
return inlineScriptedCall(target, argc);
if (makeInliningDecision(target))
return inlineScriptedCall(target, argc, constructing);
}
return makeCallBarrier(target, argc, constructing, types, barrier);

View File

@ -197,8 +197,8 @@ class IonBuilder : public MIRGenerator
TypeOracle *oracle, CompileInfo &info, size_t inliningDepth = 0, uint32 loopDepth = 0);
bool build();
bool buildInline(IonBuilder *callerBuilder, MResumePoint *callerResumePoint, MDefinition *thisDefn,
MDefinitionVector &args);
bool buildInline(IonBuilder *callerBuilder, MResumePoint *callerResumePoint,
MDefinition *thisDefn, MDefinitionVector &args);
private:
bool traverseBytecode();
@ -405,9 +405,10 @@ class IonBuilder : public MIRGenerator
InliningStatus inlineNativeCall(JSNative native, uint32 argc, bool constructing);
bool jsop_call_inline(JSFunction *callee, uint32 argc, IonBuilder &inlineBuilder);
bool inlineScriptedCall(JSFunction *target, uint32 argc);
bool makeInliningDecision(JSFunction *target);
bool jsop_call_inline(IonBuilder &inlineBuilder, HandleFunction callee,
uint32 argc, bool constructing);
bool inlineScriptedCall(HandleFunction target, uint32 argc, bool constructing);
bool makeInliningDecision(HandleFunction target);
bool jsop_call_fun_barrier(HandleFunction target, uint32 argc,
bool constructing,

View File

@ -431,6 +431,30 @@ class LCreateThis : public LInstructionHelper<1, 2, 0>
}
};
// If the Value is an Object, return unbox(Value).
// Otherwise, return the other Object.
class LReturnFromCtor : public LInstructionHelper<1, BOX_PIECES + 1, 0>
{
public:
LIR_HEADER(ReturnFromCtor);
LReturnFromCtor(const LAllocation &object)
{
// Value set by useBox() during lowering.
setOperand(LReturnFromCtor::ObjectIndex, object);
}
const LAllocation *getObject() {
return getOperand(LReturnFromCtor::ObjectIndex);
}
const LDefinition *output() {
return getDef(0);
}
static const size_t ValueIndex = 0;
static const size_t ObjectIndex = BOX_PIECES;
};
// Writes an argument for a function call to the frame's argument vector.
class LStackArg : public LInstructionHelper<0, BOX_PIECES, 0>
{

View File

@ -68,6 +68,7 @@
_(CallConstructor) \
_(StackArg) \
_(CreateThis) \
_(ReturnFromCtor) \
_(BitNotI) \
_(BitNotV) \
_(BitOpI) \

View File

@ -231,6 +231,16 @@ LIRGenerator::visitCreateThis(MCreateThis *ins)
return define(lir, ins) && assignSafepoint(lir, ins);
}
bool
LIRGenerator::visitReturnFromCtor(MReturnFromCtor *ins)
{
LReturnFromCtor *lir = new LReturnFromCtor(useRegister(ins->getObject()));
if (!useBox(lir, LReturnFromCtor::ValueIndex, ins->getValue()))
return false;
return define(lir, ins);
}
bool
LIRGenerator::visitCall(MCall *call)
{

View File

@ -121,6 +121,7 @@ class LIRGenerator : public LIRGeneratorSpecific
bool visitPrepareCall(MPrepareCall *ins);
bool visitPassArg(MPassArg *arg);
bool visitCreateThis(MCreateThis *ins);
bool visitReturnFromCtor(MReturnFromCtor *ins);
bool visitCall(MCall *call);
bool visitTest(MTest *test);
bool visitCompare(MCompare *comp);

View File

@ -1521,6 +1521,42 @@ class MCreateThis
}
};
// Given a MIRType_Value A and a MIRType_Object B:
// If the Value may be safely unboxed to an Object, return Object(A).
// Otherwise, return B.
// Used to implement return behavior for inlined constructors.
class MReturnFromCtor
: public MAryInstruction<2>,
public MixPolicy<BoxPolicy<0>, ObjectPolicy<1> >
{
MReturnFromCtor(MDefinition *value, MDefinition *object) {
initOperand(0, value);
initOperand(1, object);
setResultType(MIRType_Object);
}
public:
INSTRUCTION_HEADER(ReturnFromCtor);
static MReturnFromCtor *New(MDefinition *value, MDefinition *object)
{
return new MReturnFromCtor(value, object);
}
MDefinition *getValue() const {
return getOperand(0);
}
MDefinition *getObject() const {
return getOperand(1);
}
AliasSet getAliasSet() const {
return AliasSet::None();
}
TypePolicy *typePolicy() {
return this;
}
};
// Passes an MDefinition to an MCall. Must occur between an MPrepareCall and
// MCall. Boxes the input and stores it to the correct location on stack.
//

View File

@ -57,6 +57,7 @@ namespace ion {
_(Phi) \
_(OsrValue) \
_(OsrScopeChain) \
_(ReturnFromCtor) \
_(CheckOverRecursed) \
_(RecompileCheck) \
_(DefVar) \

View File

@ -159,16 +159,20 @@ TypeIdString(jsid id)
#endif
}
/* Assert code to know which PCs are reasonable to be considering inlining on */
/* Assert code to know which PCs are reasonable to be considering inlining on. */
inline bool
IsInlinableCall(jsbytecode *pc)
{
JSOp op = JSOp(*pc);
// CALL, FUNCALL, FUNAPPLY (Standard callsites)
// NEW (IonMonkey-only callsite)
// GETPROP, CALLPROP, and LENGTH. (Inlined Getters)
// SETPROP, SETNAME, SETGNAME (Inlined Setters)
return op == JSOP_CALL || op == JSOP_FUNCALL || op == JSOP_FUNAPPLY ||
#ifdef JS_ION
op == JSOP_NEW ||
#endif
op == JSOP_GETPROP || op == JSOP_CALLPROP || op == JSOP_LENGTH ||
op == JSOP_SETPROP || op == JSOP_SETGNAME || op == JSOP_SETNAME;