Bug 839727 - Minor cleanups around polymorphic inlining. r=djvj

This commit is contained in:
Sean Stangl 2013-02-11 13:16:53 -08:00
Родитель 15c33862f4
Коммит 6da51915ea
5 изменённых файлов: 158 добавлений и 188 удалений

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

@ -370,30 +370,34 @@ CodeGenerator::visitPolyInlineDispatch(LPolyInlineDispatch *lir)
InlinePropertyTable *inlinePropTable = mir->inlinePropertyTable();
if (inlinePropTable) {
// Temporary register is only assigned in the TypeObject case.
Register tempReg = ToRegister(lir->temp());
masm.loadPtr(Address(inputReg, JSObject::offsetOfType()), tempReg);
// Detect functions by TypeObject.
for (size_t i = 0; i < inlinePropTable->numEntries(); i++) {
types::TypeObject *typeObj = inlinePropTable->getTypeObject(i);
JSFunction *func = inlinePropTable->getFunction(i);
LBlock *target = mir->getFunctionBlock(func)->lir();
masm.branchPtr(Assembler::Equal, tempReg, ImmGCPtr(typeObj), target->label());
}
// Jump to fallback block
// Unknown function: jump to fallback block.
LBlock *fallback = mir->fallbackPrepBlock()->lir();
masm.jump(fallback->label());
} else {
for (size_t i = 0; i < mir->numCallees(); i++) {
JSFunction *func = mir->getFunction(i);
LBlock *target = mir->getFunctionBlock(i)->lir();
if (i < mir->numCallees() - 1) {
masm.branchPtr(Assembler::Equal, inputReg, ImmGCPtr(func), target->label());
} else {
// Don't generate guard for final case
masm.jump(target->label());
}
}
return true;
}
// Compare function pointers directly.
for (size_t i = 0; i < mir->numCallees() - 1; i++) {
JSFunction *func = mir->getFunction(i);
LBlock *target = mir->getFunctionBlock(i)->lir();
masm.branchPtr(Assembler::Equal, inputReg, ImmGCPtr(func), target->label());
}
// There's no fallback case, so a final guard isn't necessary.
LBlock *target = mir->getFunctionBlock(mir->numCallees() - 1)->lir();
masm.jump(target->label());
return true;
}

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

@ -2950,49 +2950,56 @@ IonBuilder::inlineScriptedCall(HandleFunction target, CallInfo &callInfo)
return true;
}
MDefinition *
IonBuilder::patchInlinedReturn(CallInfo &callInfo, MBasicBlock *exit, MBasicBlock *bottom)
{
// Replaces the MReturn in the exit block with an MGoto.
MDefinition *rdef = exit->lastIns()->toReturn()->input();
exit->discardLastIns();
// Constructors must be patched by the caller to always return an object.
if (callInfo.constructing()) {
if (rdef->type() == MIRType_Value) {
// Unknown return: dynamically detect objects.
MReturnFromCtor *filter = MReturnFromCtor::New(rdef, callInfo.thisArg());
exit->add(filter);
rdef = filter;
} else if (rdef->type() != MIRType_Object) {
// Known non-object return: force |this|.
rdef = callInfo.thisArg();
}
}
MGoto *replacement = MGoto::New(bottom);
exit->end(replacement);
if (!bottom->addPredecessorWithoutPhis(exit))
return NULL;
return rdef;
}
MDefinition *
IonBuilder::patchInlinedReturns(CallInfo &callInfo, MIRGraphExits &exits, MBasicBlock *bottom)
{
// Replace all MReturns with MGotos, and remember the MDefinition that
// would have been returned.
// Replaces MReturns with MGotos, returning the MDefinition
// representing the return value, or NULL.
JS_ASSERT(exits.length() > 0);
// In the case of a single return, no phi is necessary.
MPhi *phi = NULL;
if (exits.length() > 1) {
phi = MPhi::New(bottom->stackDepth());
phi->initLength(exits.length());
bottom->addPhi(phi);
}
if (exits.length() == 1)
return patchInlinedReturn(callInfo, exits[0], bottom);
// Accumulate multiple returns with a phi.
MPhi *phi = MPhi::New(bottom->stackDepth());
phi->initLength(exits.length());
for (size_t i = 0; i < exits.length(); i++) {
MBasicBlock *exitBlock = exits[i];
MDefinition *rval = exitBlock->lastIns()->toReturn()->getOperand(0);
exitBlock->discardLastIns();
// Inlined constructors return |this| unless overridden by another Object.
if (callInfo.constructing()) {
if (rval->type() == MIRType_Value) {
MReturnFromCtor *filter = MReturnFromCtor::New(rval, callInfo.thisArg());
exitBlock->add(filter);
rval = filter;
} else if (rval->type() != MIRType_Object) {
rval = callInfo.thisArg();
}
}
MGoto *replacement = MGoto::New(bottom);
exitBlock->end(replacement);
if (!bottom->addPredecessorWithoutPhis(exitBlock))
MDefinition *rdef = patchInlinedReturn(callInfo, exits[i], bottom);
if (!rdef)
return NULL;
if (exits.length() == 1)
return rval;
phi->setOperand(i, rval);
phi->setOperand(i, rdef);
}
bottom->addPhi(phi);
return phi;
}
@ -3060,33 +3067,13 @@ IonBuilder::jsop_call_inline(HandleFunction callee, CallInfo &callInfo, MBasicBl
return false;
}
// Replace all MReturns with MGotos, and accumulate them.
MIRGraphExits &exits = *inlineBuilder.graph().exitAccumulator();
// Replace all MReturns with MGotos, and remember the MDefinition that
// would have been returned.
for (MBasicBlock **it = exits.begin(), **end = exits.end(); it != end; ++it) {
MBasicBlock *exitBlock = *it;
MDefinition *rval = exitBlock->lastIns()->toReturn()->getOperand(0);
exitBlock->discardLastIns();
// Inlined constructors return |this| unless overridden by another Object.
if (callInfo.constructing()) {
if (rval->type() == MIRType_Value) {
MReturnFromCtor *filter = MReturnFromCtor::New(rval, callInfo.thisArg());
exitBlock->add(filter);
rval = filter;
} else if (rval->type() != MIRType_Object) {
rval = callInfo.thisArg();
}
}
if (!retvalDefns.append(rval))
MDefinition *rdef = patchInlinedReturn(callInfo, *it, bottom);
if (!rdef)
return false;
MGoto *replacement = MGoto::New(bottom);
exitBlock->end(replacement);
if (!bottom->addPredecessorWithoutPhis(exitBlock))
if (!retvalDefns.append(rdef))
return false;
}
JS_ASSERT(!retvalDefns.empty());
@ -3094,7 +3081,7 @@ IonBuilder::jsop_call_inline(HandleFunction callee, CallInfo &callInfo, MBasicBl
}
bool
IonBuilder::makeInliningDecision(AutoObjectVector &targets, uint32_t argc)
IonBuilder::makeInliningDecision(AutoObjectVector &targets)
{
AssertCanGC();
@ -3169,85 +3156,70 @@ IonBuilder::makeInliningDecision(AutoObjectVector &targets, uint32_t argc)
}
static bool
ValidateInlineableGetPropertyCache(MGetPropertyCache *getPropCache, MDefinition *thisDefn,
size_t maxUseCount)
CanInlineGetPropertyCache(MGetPropertyCache *cache, MDefinition *thisDef)
{
JS_ASSERT(getPropCache->object()->type() == MIRType_Object);
if (getPropCache->useCount() > maxUseCount)
JS_ASSERT(cache->object()->type() == MIRType_Object);
if (cache->object() != thisDef)
return false;
// Ensure that the input to the GetPropertyCache is the thisDefn for this function.
if (getPropCache->object() != thisDefn)
InlinePropertyTable *table = cache->inlinePropertyTable();
if (!table)
return false;
InlinePropertyTable *propTable = getPropCache->inlinePropertyTable();
if (!propTable || propTable->numEntries() == 0)
if (table->numEntries() == 0)
return false;
return true;
}
MGetPropertyCache *
IonBuilder::checkInlineableGetPropertyCache(uint32_t argc)
IonBuilder::getInlineableGetPropertyCache(CallInfo &callInfo)
{
// Stack state:
// ..., Func, This, Arg1, ..., ArgC
// Note: PassArgs have already been eliminated.
JS_ASSERT(current->stackDepth() >= argc + 2);
// Ensure that This is object-typed.
int thisDefnDepth = -((int) argc + 1);
MDefinition *thisDefn = current->peek(thisDefnDepth);
if (thisDefn->type() != MIRType_Object)
if (callInfo.constructing())
return NULL;
// Ensure that Func is defined by a GetPropertyCache that is then TypeBarriered and then
// infallibly Unboxed to an object.
int funcDefnDepth = -((int) argc + 2);
MDefinition *funcDefn = current->peek(funcDefnDepth);
if (funcDefn->type() != MIRType_Object)
MDefinition *thisDef = callInfo.thisArg();
if (thisDef->type() != MIRType_Object)
return NULL;
// If it's a constant, then ignore it since there's nothing to optimize: any potential
// GetProp that led to the funcDefn has already been optimized away.
if (funcDefn->isConstant())
MDefinition *funcDef = callInfo.fun();
if (funcDef->type() != MIRType_Object)
return NULL;
// Match patterns:
// 1. MGetPropertyCache
// 2. MUnbox[MIRType_Object, Infallible] <- MTypeBarrier <- MGetPropertyCache
// If it's a GetPropertyCache, return it immediately, but make sure its not used anywhere
// else (because otherwise we wouldn't be able to move it).
if (funcDefn->isGetPropertyCache()) {
MGetPropertyCache *getPropCache = funcDefn->toGetPropertyCache();
if (!ValidateInlineableGetPropertyCache(getPropCache, thisDefn, 0))
// MGetPropertyCache with no uses may be optimized away.
if (funcDef->isGetPropertyCache()) {
MGetPropertyCache *cache = funcDef->toGetPropertyCache();
if (cache->useCount() > 0)
return NULL;
return getPropCache;
if (!CanInlineGetPropertyCache(cache, thisDef))
return NULL;
return cache;
}
// Check for MUnbox[MIRType_Object, Infallible] <- MTypeBarrier <- MGetPropertyCache
if (!funcDefn->isUnbox() || funcDefn->toUnbox()->useCount() > 0)
return NULL;
// Optimize away the following common pattern:
// MUnbox[MIRType_Object, Infallible] <- MTypeBarrier <- MGetPropertyCache
if (funcDef->isUnbox()) {
MUnbox *unbox = funcDef->toUnbox();
if (unbox->mode() != MUnbox::Infallible)
return NULL;
if (unbox->useCount() > 0)
return NULL;
if (!unbox->input()->isTypeBarrier())
return NULL;
MUnbox *unbox = current->peek(funcDefnDepth)->toUnbox();
if (unbox->mode() != MUnbox::Infallible || !unbox->input()->isTypeBarrier())
return NULL;
MTypeBarrier *barrier = unbox->input()->toTypeBarrier();
if (barrier->useCount() != 1)
return NULL;
if (!barrier->input()->isGetPropertyCache())
return NULL;
MTypeBarrier *typeBarrier = unbox->input()->toTypeBarrier();
if (typeBarrier->useCount() != 1 || !typeBarrier->input()->isGetPropertyCache())
return NULL;
MGetPropertyCache *cache = barrier->input()->toGetPropertyCache();
if (cache->useCount() > 1)
return NULL;
if (!CanInlineGetPropertyCache(cache, thisDef))
return NULL;
return cache;
}
MGetPropertyCache *getPropCache = typeBarrier->input()->toGetPropertyCache();
JS_ASSERT(getPropCache->object()->type() == MIRType_Object);
if (!ValidateInlineableGetPropertyCache(getPropCache, thisDefn, 1))
return NULL;
return getPropCache;
return NULL;
}
MPolyInlineDispatch *
@ -3378,9 +3350,7 @@ IonBuilder::inlineScriptedCalls(AutoObjectVector &targets, AutoObjectVector &ori
callInfo.unwrapArgs();
callInfo.pushFormals(current);
#ifdef DEBUG
uint32_t origStackDepth = current->stackDepth();
#endif
DebugOnly<uint32_t> origStackDepth = current->stackDepth();
IonSpew(IonSpew_Inlining, "Inlining %d targets", (int) targets.length());
JS_ASSERT(targets.length() > 0);
@ -3388,29 +3358,14 @@ IonBuilder::inlineScriptedCalls(AutoObjectVector &targets, AutoObjectVector &ori
// |top| jumps into the callee subgraph -- save it for later use.
MBasicBlock *top = current;
// Check if the input is a GetPropertyCache that can be eliminated via guards on
// the |this| object's typeguards.
MGetPropertyCache *getPropCache = NULL;
if (!callInfo.constructing()) {
getPropCache = checkInlineableGetPropertyCache(callInfo.argc());
if (getPropCache) {
InlinePropertyTable *inlinePropTable = getPropCache->inlinePropertyTable();
// checkInlineableGetPropertyCache should have verified this.
JS_ASSERT(inlinePropTable != NULL);
int numCases = inlinePropTable->numEntries();
IonSpew(IonSpew_Inlining, "Got inlineable property cache with %d cases", numCases);
inlinePropTable->trimToAndMaybePatchTargets(targets, originals);
// Trim the cases based on those that match the targets at this call site.
IonSpew(IonSpew_Inlining, "%d inlineable cases left after trimming to %d targets",
(int) inlinePropTable->numEntries(),
(int) targets.length());
if (inlinePropTable->numEntries() == 0)
getPropCache = NULL;
}
// Check for a function MGetPropertyCache that may be eliminated via guards
// on the |this| object's typeguards.
MGetPropertyCache *getPropCache = getInlineableGetPropertyCache(callInfo);
if (getPropCache) {
InlinePropertyTable *table = getPropCache->inlinePropertyTable();
table->trimToAndMaybePatchTargets(targets, originals);
if (table->numEntries() == 0)
getPropCache = NULL;
}
// Do the inline build. Return value definitions are stored in retvalDefns.
@ -4020,7 +3975,7 @@ IonBuilder::jsop_funapplyarguments(uint32_t argc)
AutoObjectVector targets(cx);
targets.append(target);
if (makeInliningDecision(targets, argc))
if (makeInliningDecision(targets))
return inlineScriptedCall(target, callInfo);
}
@ -4075,7 +4030,7 @@ IonBuilder::jsop_call(uint32_t argc, bool constructing)
}
// Inline scriped call(s).
if (inliningEnabled() && targets.length() > 0 && makeInliningDecision(targets, argc))
if (inliningEnabled() && targets.length() > 0 && makeInliningDecision(targets))
return inlineScriptedCalls(targets, originals, callInfo);
// No inline, just make the call.

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

@ -439,7 +439,7 @@ class IonBuilder : public MIRGenerator
bool inlineScriptedCalls(AutoObjectVector &targets, AutoObjectVector &originals,
CallInfo &callInfo);
bool inlineScriptedCall(HandleFunction target, CallInfo &callInfo);
bool makeInliningDecision(AutoObjectVector &targets, uint32_t argc);
bool makeInliningDecision(AutoObjectVector &targets);
bool anyFunctionIsCloneAtCallsite(types::StackTypeSet *funTypes);
MDefinition *makeCallsiteClone(HandleFunction target, MDefinition *fun);
@ -450,6 +450,7 @@ class IonBuilder : public MIRGenerator
bool makeCall(HandleFunction target, CallInfo &callInfo,
types::StackTypeSet *calleeTypes, bool cloneAtCallsite);
MDefinition *patchInlinedReturn(CallInfo &callInfo, MBasicBlock *exit, MBasicBlock *bottom);
MDefinition *patchInlinedReturns(CallInfo &callInfo, MIRGraphExits &exits, MBasicBlock *bottom);
inline bool TestCommonPropFunc(JSContext *cx, types::StackTypeSet *types,
@ -460,7 +461,7 @@ class IonBuilder : public MIRGenerator
bool annotateGetPropertyCache(JSContext *cx, MDefinition *obj, MGetPropertyCache *getPropCache,
types::StackTypeSet *objTypes, types::StackTypeSet *pushedTypes);
MGetPropertyCache *checkInlineableGetPropertyCache(uint32_t argc);
MGetPropertyCache *getInlineableGetPropertyCache(CallInfo &callInfo);
MPolyInlineDispatch *
makePolyInlineDispatch(JSContext *cx, CallInfo &callInfo,

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

@ -1909,3 +1909,33 @@ MLoadSlot::mightAlias(MDefinition *store)
return false;
return true;
}
void
InlinePropertyTable::trimToAndMaybePatchTargets(AutoObjectVector &targets,
AutoObjectVector &originals)
{
IonSpew(IonSpew_Inlining, "Got inlineable property cache with %d cases",
(int)numEntries());
size_t i = 0;
while (i < numEntries()) {
bool foundFunc = false;
// Compare using originals, but if we find a matching function,
// patch it to the target, which might be a clone.
for (size_t j = 0; j < originals.length(); j++) {
if (entries_[i]->func == originals[j]) {
if (entries_[i]->func != targets[j])
entries_[i] = new Entry(entries_[i]->typeObj, targets[j]->toFunction());
foundFunc = true;
break;
}
}
if (!foundFunc)
entries_.erase(&(entries_[i]));
else
i++;
}
IonSpew(IonSpew_Inlining, "%d inlineable cases left after trimming to %d targets",
(int)numEntries(), (int)targets.length());
}

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

@ -1051,6 +1051,9 @@ class MReturn
return new MReturn(ins);
}
MDefinition *input() const {
return getOperand(0);
}
TypePolicy *typePolicy() {
return this;
}
@ -4610,7 +4613,7 @@ class MStoreFixedSlot
: MBinaryInstruction(obj, rval),
needsBarrier_(barrier),
slot_(slot)
{}
{ }
public:
INSTRUCTION_HEADER(StoreFixedSlot)
@ -4654,22 +4657,18 @@ class InlinePropertyTable : public TempObject
CompilerRootFunction func;
Entry(types::TypeObject *typeObj, JSFunction *func)
: typeObj(typeObj),
func(func)
{
}
: typeObj(typeObj), func(func)
{ }
};
jsbytecode *pc_;
MResumePoint *priorResumePoint_;
Vector<Entry *, 4, IonAllocPolicy> entries_;
public:
InlinePropertyTable(jsbytecode *pc)
: pc_(pc),
priorResumePoint_(NULL),
entries_()
{
}
: pc_(pc), priorResumePoint_(NULL), entries_()
{ }
void setPriorResumePoint(MResumePoint *resumePoint) {
JS_ASSERT(priorResumePoint_ == NULL);
@ -4702,26 +4701,7 @@ class InlinePropertyTable : public TempObject
return entries_[i]->func;
}
void trimToAndMaybePatchTargets(AutoObjectVector &targets, AutoObjectVector &originals) {
size_t i = 0;
while (i < numEntries()) {
bool foundFunc = false;
// Compare using originals, but if we find a matching function,
// patch it to the target, which might be a clone.
for (size_t j = 0; j < originals.length(); j++) {
if (entries_[i]->func == originals[j]) {
if (entries_[i]->func != targets[j])
entries_[i] = new Entry(entries_[i]->typeObj, targets[j]->toFunction());
foundFunc = true;
break;
}
}
if (!foundFunc)
entries_.erase(&(entries_[i]));
else
i++;
}
}
void trimToAndMaybePatchTargets(AutoObjectVector &targets, AutoObjectVector &originals);
};
class MGetPropertyCache