зеркало из https://github.com/mozilla/gecko-dev.git
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:
Родитель
48564710fc
Коммит
ef1c8c5079
|
@ -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:
|
||||
|
|
Загрузка…
Ссылка в новой задаче