Bug 900849 - Make polymorphic inlining work with non-singletons. r=djvj

This commit is contained in:
Jan de Mooij 2015-01-17 13:04:04 +01:00
Родитель 9bcffcb8a6
Коммит 6e185bd06d
7 изменённых файлов: 79 добавлений и 33 удалений

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

@ -722,9 +722,14 @@ CodeGenerator::visitFunctionDispatch(LFunctionDispatch *lir)
// Compare function pointers, except for the last case.
for (size_t i = 0; i < casesWithFallback - 1; i++) {
MOZ_ASSERT(i < mir->numCases());
JSFunction *func = mir->getCase(i);
LBlock *target = skipTrivialBlocks(mir->getCaseBlock(i))->lir();
masm.branchPtr(Assembler::Equal, input, ImmGCPtr(func), target->label());
if (types::TypeObject *funcType = mir->getCaseTypeObject(i)) {
masm.branchPtr(Assembler::Equal, Address(input, JSObject::offsetOfType()),
ImmGCPtr(funcType), target->label());
} else {
JSFunction *func = mir->getCase(i);
masm.branchPtr(Assembler::Equal, input, ImmGCPtr(func), target->label());
}
}
// Jump to the last case.

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

@ -271,11 +271,9 @@ IonBuilder::getSingleCallTarget(types::TemporaryTypeSet *calleeTypes)
bool
IonBuilder::getPolyCallTargets(types::TemporaryTypeSet *calleeTypes, bool constructing,
ObjectVector &targets, uint32_t maxTargets, bool *gotLambda)
ObjectVector &targets, uint32_t maxTargets)
{
MOZ_ASSERT(targets.empty());
MOZ_ASSERT(gotLambda);
*gotLambda = false;
if (!calleeTypes)
return true;
@ -290,9 +288,11 @@ IonBuilder::getPolyCallTargets(types::TemporaryTypeSet *calleeTypes, bool constr
if (!targets.reserve(objCount))
return false;
for(unsigned i = 0; i < objCount; i++) {
for (unsigned i = 0; i < objCount; i++) {
JSObject *obj = calleeTypes->getSingleObject(i);
if (!obj) {
if (obj) {
MOZ_ASSERT(obj->hasSingletonType());
} else {
types::TypeObject *typeObj = calleeTypes->getTypeObject(i);
if (!typeObj)
continue;
@ -303,7 +303,7 @@ IonBuilder::getPolyCallTargets(types::TemporaryTypeSet *calleeTypes, bool constr
return true;
}
*gotLambda = true;
MOZ_ASSERT(!obj->hasSingletonType());
}
// Don't optimize if the callee is not callable or constructable per
@ -317,10 +317,6 @@ IonBuilder::getPolyCallTargets(types::TemporaryTypeSet *calleeTypes, bool constr
targets.infallibleAppend(obj);
}
// For now, only inline "singleton" lambda calls
if (*gotLambda && targets.length() > 1)
targets.clear();
return true;
}
@ -4833,7 +4829,7 @@ IonBuilder::inlineSingleCall(CallInfo &callInfo, JSObject *targetArg)
IonBuilder::InliningStatus
IonBuilder::inlineCallsite(const ObjectVector &targets, ObjectVector &originals,
bool lambda, CallInfo &callInfo)
CallInfo &callInfo)
{
if (targets.empty())
return InliningStatus_NotInlined;
@ -4868,7 +4864,7 @@ IonBuilder::inlineCallsite(const ObjectVector &targets, ObjectVector &originals,
// If the callee is not going to be a lambda (which may vary across
// different invocations), then the callee definition can be replaced by a
// constant.
if (!lambda) {
if (target->hasSingletonType()) {
// Replace the function with an MConstant.
MConstant *constFun = constant(ObjectValue(*target));
callInfo.setFun(constFun);
@ -5110,12 +5106,19 @@ IonBuilder::inlineCalls(CallInfo &callInfo, const ObjectVector &targets,
if (!inlineBlock)
return false;
// Create a function MConstant to use in the entry ResumePoint.
MConstant *funcDef = MConstant::New(alloc(), ObjectValue(*target), constraints());
// Create a function MConstant to use in the entry ResumePoint. If we
// can't use a constant, add a no-op MPolyInlineGuard, to prevent
// hoisting scope chain gets above the dispatch instruction.
MInstruction *funcDef;
if (target->hasSingletonType())
funcDef = MConstant::New(alloc(), ObjectValue(*target), constraints());
else
funcDef = MPolyInlineGuard::New(alloc(), callInfo.fun());
funcDef->setImplicitlyUsedUnchecked();
dispatchBlock->add(funcDef);
// Use the MConstant in the inline resume point and on stack.
// Use the inlined callee in the inline resume point and on stack.
int funIndex = inlineBlock->entryResumePoint()->stackDepth() - callInfo.numFormals();
inlineBlock->entryResumePoint()->replaceOperand(funIndex, funcDef);
inlineBlock->rewriteSlot(funIndex, funcDef);
@ -5160,7 +5163,8 @@ IonBuilder::inlineCalls(CallInfo &callInfo, const ObjectVector &targets,
//
// Note that guarding is on the original function pointer even
// if there is a clone, since cloning occurs at the callsite.
dispatch->addCase(original, inlineBlock);
types::TypeObject *funType = original->hasSingletonType() ? nullptr : original->type();
dispatch->addCase(original, funType, inlineBlock);
MDefinition *retVal = inlineReturnBlock->peek(-1);
retPhi->addInput(retVal);
@ -5211,8 +5215,11 @@ IonBuilder::inlineCalls(CallInfo &callInfo, const ObjectVector &targets,
MOZ_ASSERT(!remaining);
if (targets[i]->is<JSFunction>()) {
remaining = &targets[i]->as<JSFunction>();
clonedAtCallsite = targets[i] != originals[i];
JSFunction *target = &targets[i]->as<JSFunction>();
if (target->hasSingletonType()) {
remaining = target;
clonedAtCallsite = target != originals[i];
}
}
break;
}
@ -5641,13 +5648,9 @@ IonBuilder::jsop_call(uint32_t argc, bool constructing)
// Acquire known call target if existent.
ObjectVector originals(alloc());
bool gotLambda = false;
types::TemporaryTypeSet *calleeTypes = current->peek(calleeDepth)->resultTypeSet();
if (calleeTypes) {
if (!getPolyCallTargets(calleeTypes, constructing, originals, 4, &gotLambda))
return false;
}
MOZ_ASSERT_IF(gotLambda, originals.length() <= 1);
if (calleeTypes && !getPolyCallTargets(calleeTypes, constructing, originals, 4))
return false;
// If any call targets need to be cloned, look for existing clones to use.
// Keep track of the originals as we need to case on them for poly inline.
@ -5676,7 +5679,7 @@ IonBuilder::jsop_call(uint32_t argc, bool constructing)
return false;
// Try inlining
InliningStatus status = inlineCallsite(targets, originals, gotLambda, callInfo);
InliningStatus status = inlineCallsite(targets, originals, callInfo);
if (status == InliningStatus_Inlined)
return true;
if (status == InliningStatus_Error)

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

@ -238,7 +238,7 @@ class IonBuilder
JSFunction *getSingleCallTarget(types::TemporaryTypeSet *calleeTypes);
bool getPolyCallTargets(types::TemporaryTypeSet *calleeTypes, bool constructing,
ObjectVector &targets, uint32_t maxTargets, bool *gotLambda);
ObjectVector &targets, uint32_t maxTargets);
void popCfgStack();
DeferredEdge *filterDeadDeferredEdges(DeferredEdge *edge);
@ -817,7 +817,7 @@ class IonBuilder
// Call functions
InliningStatus inlineCallsite(const ObjectVector &targets, ObjectVector &originals,
bool lambda, CallInfo &callInfo);
CallInfo &callInfo);
bool inlineCalls(CallInfo &callInfo, const ObjectVector &targets, ObjectVector &originals,
BoolVector &choiceSet, MGetPropertyCache *maybeCache);

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

@ -3159,6 +3159,13 @@ LIRGenerator::visitGuardString(MGuardString *ins)
redefine(ins, ins->input());
}
void
LIRGenerator::visitPolyInlineGuard(MPolyInlineGuard *ins)
{
MOZ_ASSERT(ins->input()->type() == MIRType_Object);
redefine(ins, ins->input());
}
void
LIRGenerator::visitGuardShapePolymorphic(MGuardShapePolymorphic *ins)
{

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

@ -220,6 +220,7 @@ class LIRGenerator : public LIRGeneratorSpecific
void visitGuardObject(MGuardObject *ins);
void visitGuardString(MGuardString *ins);
void visitGuardShapePolymorphic(MGuardShapePolymorphic *ins);
void visitPolyInlineGuard(MPolyInlineGuard *ins);
void visitAssertRange(MAssertRange *ins);
void visitCallGetProperty(MCallGetProperty *ins);
void visitDeleteProperty(MDeleteProperty *ins);

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

@ -4120,6 +4120,28 @@ class MGuardString
}
};
class MPolyInlineGuard
: public MUnaryInstruction,
public SingleObjectPolicy::Data
{
explicit MPolyInlineGuard(MDefinition *ins)
: MUnaryInstruction(ins)
{
setGuard();
setResultType(MIRType_Object);
}
public:
INSTRUCTION_HEADER(PolyInlineGuard)
static MPolyInlineGuard *New(TempAllocator &alloc, MDefinition *ins) {
return new(alloc) MPolyInlineGuard(ins);
}
AliasSet getAliasSet() const MOZ_OVERRIDE {
return AliasSet::None();
}
};
class MAssertRange
: public MUnaryInstruction,
public NoTypePolicy::Data
@ -9384,10 +9406,14 @@ class MDispatchInstruction
// Map from JSFunction* -> MBasicBlock.
struct Entry {
JSFunction *func;
// If |func| has a singleton type, |funcType| is null. Otherwise,
// |funcType| holds the TypeObject for |func|, and dispatch guards
// on the type instead of directly on the function.
types::TypeObject *funcType;
MBasicBlock *block;
Entry(JSFunction *func, MBasicBlock *block)
: func(func), block(block)
Entry(JSFunction *func, types::TypeObject *funcType, MBasicBlock *block)
: func(func), funcType(funcType), block(block)
{ }
};
Vector<Entry, 4, JitAllocPolicy> map_;
@ -9455,8 +9481,8 @@ class MDispatchInstruction
}
public:
void addCase(JSFunction *func, MBasicBlock *block) {
map_.append(Entry(func, block));
void addCase(JSFunction *func, types::TypeObject *funcType, MBasicBlock *block) {
map_.append(Entry(func, funcType, block));
}
uint32_t numCases() const {
return map_.length();
@ -9464,6 +9490,9 @@ class MDispatchInstruction
JSFunction *getCase(uint32_t i) const {
return map_[i].func;
}
types::TypeObject *getCaseTypeObject(uint32_t i) const {
return map_[i].funcType;
}
MBasicBlock *getCaseBlock(uint32_t i) const {
return map_[i].block;
}

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

@ -102,6 +102,7 @@ namespace jit {
_(Unbox) \
_(GuardObject) \
_(GuardString) \
_(PolyInlineGuard) \
_(AssertRange) \
_(ToDouble) \
_(ToFloat32) \