Bug 859609 - Inline functions that use the scope chain, and also inline call sites with monomorphic cloned lambdas. r=h4writer

This commit is contained in:
Kannan Vijayan 2013-06-11 15:49:51 -04:00
Родитель 48564710fc
Коммит ef1c8c5079
4 изменённых файлов: 112 добавлений и 55 удалений

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

@ -51,7 +51,8 @@ IonBuilder::IonBuilder(JSContext *cx, TempAllocator *temp, MIRGraph *graph,
failedBoundsCheck_(info->script()->failedBoundsCheck),
failedShapeGuard_(info->script()->failedShapeGuard),
nonStringIteration_(false),
lazyArguments_(NULL)
lazyArguments_(NULL),
inlineCallInfo_(NULL)
{
script_.init(info->script());
pc = info->startPC();
@ -160,9 +161,13 @@ IonBuilder::getSingleCallTarget(types::StackTypeSet *calleeTypes)
bool
IonBuilder::getPolyCallTargets(types::StackTypeSet *calleeTypes,
AutoObjectVector &targets, uint32_t maxTargets)
AutoObjectVector &targets,
uint32_t maxTargets,
bool *gotLambda)
{
JS_ASSERT(targets.length() == 0);
JS_ASSERT(gotLambda);
*gotLambda = false;
if (!calleeTypes)
return true;
@ -179,16 +184,33 @@ IonBuilder::getPolyCallTargets(types::StackTypeSet *calleeTypes,
return false;
for(unsigned i = 0; i < objCount; i++) {
JSObject *obj = calleeTypes->getSingleObject(i);
if (!obj || !obj->isFunction()) {
targets.clear();
return true;
if (obj) {
if (!obj->isFunction()) {
targets.clear();
return true;
}
if (obj->toFunction()->isInterpreted() && !obj->toFunction()->getOrCreateScript(cx))
return false;
DebugOnly<bool> appendOk = targets.append(obj);
JS_ASSERT(appendOk);
} else {
types::TypeObject *typeObj = calleeTypes->getTypeObject(i);
JS_ASSERT(typeObj);
if (!typeObj->isFunction() || !typeObj->interpretedFunction) {
targets.clear();
return true;
}
DebugOnly<bool> appendOk = targets.append(typeObj->interpretedFunction);
JS_ASSERT(appendOk);
*gotLambda = true;
}
if (obj->toFunction()->isInterpreted() && !obj->toFunction()->getOrCreateScript(cx))
return false;
if (!targets.append(obj))
return false;
}
// For now, only inline "singleton" lambda calls
if (*gotLambda && targets.length() > 1)
targets.clear();
return true;
}
@ -209,9 +231,6 @@ IonBuilder::canEnterInlinedFunction(JSFunction *target)
if (!targetScript->compileAndGo)
return false;
if (targetScript->analysis()->usesScopeChain())
return false;
types::TypeObject *targetType = target->getType(cx);
if (!targetType || targetType->unknownProperties())
return false;
@ -220,7 +239,7 @@ IonBuilder::canEnterInlinedFunction(JSFunction *target)
}
bool
IonBuilder::canInlineTarget(JSFunction *target, CallInfo &callInfo)
IonBuilder::canInlineTarget(JSFunction *target)
{
if (!target->isInterpreted()) {
IonSpew(IonSpew_Inlining, "Cannot inline due to non-interpreted");
@ -572,6 +591,8 @@ bool
IonBuilder::buildInline(IonBuilder *callerBuilder, MResumePoint *callerResumePoint,
CallInfo &callInfo)
{
inlineCallInfo_ = &callInfo;
if (!script()->ensureHasBytecodeTypeMap(cx))
return false;
@ -614,23 +635,21 @@ IonBuilder::buildInline(IonBuilder *callerBuilder, MResumePoint *callerResumePoi
if (!current->addPredecessorWithoutPhis(predecessor))
return false;
// Save the actual arguments the caller used to call this inlined call,
// to shortcut operations on "arguments" in the inlined call.
JS_ASSERT(inlinedArguments_.length() == 0);
if (!inlinedArguments_.append(callInfo.argv().begin(), callInfo.argv().end()))
return false;
// Initialize scope chain slot to Undefined. It's set later by |initScopeChain|.
{
MInstruction *scope = MConstant::New(UndefinedValue());
current->add(scope);
current->initSlot(info().scopeChainSlot(), scope);
}
// canEnterInlinedFunction vetoes scripts which use the scope chain or needs an
// arguments object.
JS_ASSERT(!script()->analysis()->usesScopeChain());
MInstruction *scope = MConstant::New(UndefinedValue());
current->add(scope);
current->initSlot(info().scopeChainSlot(), scope);
// Initialize |arguments| slot.
if (info().hasArguments()) {
MInstruction *argsObj = MConstant::New(UndefinedValue());
current->add(argsObj);
current->initSlot(info().argsObjSlot(), argsObj);
}
// Initialize |this| slot.
current->initSlot(info().thisSlot(), callInfo.thisArg());
IonSpew(IonSpew_Inlining, "Initializing %u arg slots", info().nargs());
@ -653,6 +672,10 @@ IonBuilder::buildInline(IonBuilder *callerBuilder, MResumePoint *callerResumePoi
current->initSlot(info().argSlot(i), arg);
}
// Initialize the scope chain now that args are initialized.
if (!initScopeChain(callInfo.fun()))
return false;
IonSpew(IonSpew_Inlining, "Initializing %u local slots", info().nlocals());
// Initialize local variables.
@ -765,7 +788,7 @@ IonBuilder::initParameters()
}
bool
IonBuilder::initScopeChain()
IonBuilder::initScopeChain(MDefinition *callee)
{
MInstruction *scope = NULL;
@ -784,9 +807,11 @@ IonBuilder::initScopeChain()
return abort("non-CNG global scripts are not supported");
if (JSFunction *fun = info().fun()) {
MCallee *callee = MCallee::New();
current->add(callee);
if (!callee) {
MCallee *calleeIns = MCallee::New();
current->add(calleeIns);
callee = calleeIns;
}
scope = MFunctionEnvironment::New(callee);
current->add(scope);
@ -1392,12 +1417,14 @@ IonBuilder::inspectOpcode(JSOp op)
case JSOP_CALLEE:
{
MInstruction *callee;
if (inliningDepth_ == 0)
callee = MCallee::New();
else
callee = MConstant::New(ObjectValue(*info().fun()));
current->add(callee);
MDefinition *callee;
if (inliningDepth_ == 0) {
MInstruction *calleeIns = MCallee::New();
current->add(calleeIns);
callee = calleeIns;
} else {
callee = inlineCallInfo_->fun();
}
current->push(callee);
return true;
}
@ -3556,7 +3583,7 @@ IonBuilder::makeInliningDecision(JSFunction *target, CallInfo &callInfo)
return true;
// Determine whether inlining is possible at callee site
if (!canInlineTarget(target, callInfo))
if (!canInlineTarget(target))
return false;
// Heuristics!
@ -3834,9 +3861,6 @@ IonBuilder::makePolyInlineDispatch(JSContext *cx, CallInfo &callInfo,
IonBuilder::InliningStatus
IonBuilder::inlineSingleCall(CallInfo &callInfo, JSFunction *target)
{
// The inlined target must always be explicitly provided as a constant.
JS_ASSERT(callInfo.fun()->isConstant());
// Expects formals to be popped and wrapped.
if (target->isNative())
return inlineNativeCall(callInfo, target->native());
@ -3848,7 +3872,7 @@ IonBuilder::inlineSingleCall(CallInfo &callInfo, JSFunction *target)
IonBuilder::InliningStatus
IonBuilder::inlineCallsite(AutoObjectVector &targets, AutoObjectVector &originals,
CallInfo &callInfo)
bool lambda, CallInfo &callInfo)
{
if (!inliningEnabled())
return InliningStatus_NotInlined;
@ -3868,11 +3892,20 @@ IonBuilder::inlineCallsite(AutoObjectVector &targets, AutoObjectVector &original
if (!makeInliningDecision(target, callInfo))
return InliningStatus_NotInlined;
// Replace the function with an MConstant.
// Inlining will elminate uses of the original callee, but it needs to
// be preserved in phis if we bail out. Mark the old callee definition as
// folded to ensure this happens.
callInfo.fun()->setFoldedUnchecked();
MConstant *constFun = MConstant::New(ObjectValue(*target));
current->add(constFun);
callInfo.setFun(constFun);
// 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) {
// Replace the function with an MConstant.
MConstant *constFun = MConstant::New(ObjectValue(*target));
current->add(constFun);
callInfo.setFun(constFun);
}
return inlineSingleCall(callInfo, target);
}
@ -4628,7 +4661,7 @@ IonBuilder::jsop_funapplyarguments(uint32_t argc)
// Arguments
Vector<MDefinition *> args(cx);
if (!args.append(inlinedArguments_.begin(), inlinedArguments_.end()))
if (!args.append(inlineCallInfo_->argv().begin(), inlineCallInfo_->argv().end()))
return false;
callInfo.setArgs(&args);
@ -4679,11 +4712,13 @@ IonBuilder::jsop_call(uint32_t argc, bool constructing)
// Acquire known call target if existent.
AutoObjectVector originals(cx);
bool gotLambda;
types::StackTypeSet *calleeTypes = current->peek(calleeDepth)->resultTypeSet();
if (calleeTypes) {
if (!getPolyCallTargets(calleeTypes, originals, 4))
if (!getPolyCallTargets(calleeTypes, originals, 4, &gotLambda))
return false;
}
JS_ASSERT_IF(gotLambda, originals.length() <= 1);
// If any call targets need to be cloned, clone them. Keep track of the
// originals as we need to case on them for poly inline.
@ -4707,8 +4742,11 @@ IonBuilder::jsop_call(uint32_t argc, bool constructing)
if (!callInfo.init(current, argc))
return false;
if (gotLambda && targets.length() > 0)
callInfo.setLambda(true);
// Try inlining
InliningStatus status = inlineCallsite(targets, originals, callInfo);
InliningStatus status = inlineCallsite(targets, originals, gotLambda, callInfo);
if (status == InliningStatus_Inlined)
return true;
if (status == InliningStatus_Error)
@ -6947,7 +6985,7 @@ IonBuilder::jsop_arguments_length()
}
// We are inlining and know the number of arguments the callee pushed
return pushConstant(Int32Value(inlinedArguments_.length()));
return pushConstant(Int32Value(inlineCallInfo_->argv().length()));
}
bool
@ -7010,7 +7048,7 @@ IonBuilder::jsop_rest()
}
// We know the exact number of arguments the callee pushed.
unsigned numActuals = inlinedArguments_.length();
unsigned numActuals = inlineCallInfo_->argv().length();
unsigned numFormals = info().nargs() - 1;
unsigned numRest = numActuals > numFormals ? numActuals - numFormals : 0;
JSObject *templateObject = getNewArrayTemplateObject(numRest);
@ -7043,7 +7081,7 @@ IonBuilder::jsop_rest()
current->add(index);
MInstruction *store;
MDefinition *arg = inlinedArguments_[i];
MDefinition *arg = inlineCallInfo_->argv()[i];
if (elemTypes && !TypeSetIncludes(elemTypes, arg->type(), arg->resultTypeSet())) {
elemTypes->addFreeze(cx);
store = MCallSetElement::New(array, index, arg);

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

@ -219,8 +219,10 @@ class IonBuilder : public MIRGenerator
JSFunction *getSingleCallTarget(types::StackTypeSet *calleeTypes);
bool getPolyCallTargets(types::StackTypeSet *calleeTypes,
AutoObjectVector &targets, uint32_t maxTargets);
bool canInlineTarget(JSFunction *target, CallInfo &callInfo);
AutoObjectVector &targets,
uint32_t maxTargets,
bool *gotLambda);
bool canInlineTarget(JSFunction *target);
void popCfgStack();
DeferredEdge *filterDeadDeferredEdges(DeferredEdge *edge);
@ -311,7 +313,7 @@ class IonBuilder : public MIRGenerator
bool initParameters();
void rewriteParameter(uint32_t slotIdx, MDefinition *param, int32_t argIndex);
void rewriteParameters();
bool initScopeChain();
bool initScopeChain(MDefinition *callee = NULL);
bool initArgumentsObject();
bool pushConstant(const Value &v);
@ -508,7 +510,7 @@ class IonBuilder : public MIRGenerator
// Call functions
InliningStatus inlineCallsite(AutoObjectVector &targets, AutoObjectVector &originals,
CallInfo &callInfo);
bool lambda, CallInfo &callInfo);
bool inlineCalls(CallInfo &callInfo, AutoObjectVector &targets, AutoObjectVector &originals,
Vector<bool> &choiceSet, MGetPropertyCache *maybeCache);
@ -604,7 +606,6 @@ class IonBuilder : public MIRGenerator
BaselineInspector *inspector;
size_t inliningDepth_;
Vector<MDefinition *, 0, IonAllocPolicy> inlinedArguments_;
// Cutoff to disable compilation if excessive time is spent reanalyzing
// loop bodies to compute a fixpoint of the types for loop variables.
@ -625,6 +626,9 @@ class IonBuilder : public MIRGenerator
// If this script can use a lazy arguments object, it will be pre-created
// here.
MInstruction *lazyArguments_;
// If this is an inline builder, the call info for the builder.
const CallInfo *inlineCallInfo_;
};
class CallInfo
@ -634,6 +638,7 @@ class CallInfo
Vector<MDefinition *> args_;
bool constructing_;
bool lambda_;
public:
CallInfo(JSContext *cx, bool constructing)
@ -700,6 +705,10 @@ class CallInfo
return args_;
}
const Vector<MDefinition *> &argv() const {
return args_;
}
MDefinition *getArg(uint32_t i) {
JS_ASSERT(i < argc());
return args_[i];
@ -723,6 +732,13 @@ class CallInfo
return constructing_;
}
bool isLambda() const {
return lambda_;
}
void setLambda(bool lambda) {
lambda_ = lambda;
}
void wrapArgs(MBasicBlock *current) {
thisArg_ = wrap(current, thisArg_);
for (uint32_t i = 0; i < argc(); i++)

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

@ -6180,6 +6180,10 @@ class MFunctionEnvironment
MDefinition *function() const {
return getOperand(0);
}
TypePolicy *typePolicy() {
return this;
}
};
// Loads the current js::ForkJoinSlice*.

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

@ -280,7 +280,6 @@ ScriptAnalysis::analyzeBytecode(JSContext *cx)
case JSOP_SETALIASEDVAR:
case JSOP_LAMBDA:
usesScopeChain_ = true;
isIonInlineable = false;
break;
case JSOP_DEFFUN: