mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 768270 - Inline constructors. r=dvander
This commit is contained in:
parent
3dac2376bd
commit
db0466b390
@ -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)
|
||||
{
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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,
|
||||
|
@ -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>
|
||||
{
|
||||
|
@ -68,6 +68,7 @@
|
||||
_(CallConstructor) \
|
||||
_(StackArg) \
|
||||
_(CreateThis) \
|
||||
_(ReturnFromCtor) \
|
||||
_(BitNotI) \
|
||||
_(BitNotV) \
|
||||
_(BitOpI) \
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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);
|
||||
|
@ -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.
|
||||
//
|
||||
|
@ -57,6 +57,7 @@ namespace ion {
|
||||
_(Phi) \
|
||||
_(OsrValue) \
|
||||
_(OsrScopeChain) \
|
||||
_(ReturnFromCtor) \
|
||||
_(CheckOverRecursed) \
|
||||
_(RecompileCheck) \
|
||||
_(DefVar) \
|
||||
|
@ -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;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user