mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 1142669 part 4 - Fix some inlining issues and inline scripts with loops. r=h4writer
This commit is contained in:
parent
bf95f782eb
commit
6980cbf56d
@ -203,8 +203,6 @@ namespace JS {
|
||||
"can't inline: type has unknown properties") \
|
||||
_(CantInlineExceededDepth, \
|
||||
"can't inline: exceeded inlining depth") \
|
||||
_(CantInlineBigLoop, \
|
||||
"can't inline: big function with a loop") \
|
||||
_(CantInlineExceededTotalBytecodeLength, \
|
||||
"can't inline: exceeded max total bytecode length") \
|
||||
_(CantInlineBigCaller, \
|
||||
|
@ -65,7 +65,8 @@ BaselineScript::BaselineScript(uint32_t prologueOffset, uint32_t epilogueOffset,
|
||||
traceLoggerScriptEvent_(),
|
||||
#endif
|
||||
postDebugPrologueOffset_(postDebugPrologueOffset),
|
||||
flags_(0)
|
||||
flags_(0),
|
||||
maxInliningDepth_(UINT8_MAX)
|
||||
{ }
|
||||
|
||||
static const unsigned BASELINE_MAX_ARGS_LENGTH = 20000;
|
||||
|
@ -212,6 +212,13 @@ struct BaselineScript
|
||||
// instruction.
|
||||
uint32_t yieldEntriesOffset_;
|
||||
|
||||
// The max inlining depth where we can still inline all functions we inlined
|
||||
// when we Ion-compiled this script. This starts as UINT8_MAX, since we have
|
||||
// no data yet, and won't affect inlining heuristics in that case. The value
|
||||
// is updated when we Ion-compile this script. See makeInliningDecision for
|
||||
// more info.
|
||||
uint8_t maxInliningDepth_;
|
||||
|
||||
public:
|
||||
// Do not call directly, use BaselineScript::New. This is public for cx->new_.
|
||||
BaselineScript(uint32_t prologueOffset, uint32_t epilogueOffset,
|
||||
@ -424,6 +431,17 @@ struct BaselineScript
|
||||
MOZ_ASSERT(bytecodeTypeMapOffset_);
|
||||
return reinterpret_cast<uint32_t *>(reinterpret_cast<uint8_t *>(this) + bytecodeTypeMapOffset_);
|
||||
}
|
||||
|
||||
uint8_t maxInliningDepth() const {
|
||||
return maxInliningDepth_;
|
||||
}
|
||||
void setMaxInliningDepth(uint32_t depth) {
|
||||
MOZ_ASSERT(depth <= UINT8_MAX);
|
||||
maxInliningDepth_ = depth;
|
||||
}
|
||||
void resetMaxInliningDepth() {
|
||||
maxInliningDepth_ = UINT8_MAX;
|
||||
}
|
||||
};
|
||||
static_assert(sizeof(BaselineScript) % sizeof(uintptr_t) == 0,
|
||||
"The data attached to the script must be aligned for fast JIT access.");
|
||||
|
@ -776,6 +776,9 @@ IonBuilder::build()
|
||||
if (!init())
|
||||
return false;
|
||||
|
||||
if (script()->hasBaselineScript())
|
||||
script()->baselineScript()->resetMaxInliningDepth();
|
||||
|
||||
if (!setCurrentAndSpecializePhis(newBlock(pc)))
|
||||
return false;
|
||||
if (!current)
|
||||
@ -4841,56 +4844,6 @@ IonBuilder::makeInliningDecision(JSObject *targetArg, CallInfo &callInfo)
|
||||
// Heuristics!
|
||||
JSScript *targetScript = target->nonLazyScript();
|
||||
|
||||
// Cap the inlining depth.
|
||||
if (js_JitOptions.isSmallFunction(targetScript)) {
|
||||
if (inliningDepth_ >= optimizationInfo().smallFunctionMaxInlineDepth()) {
|
||||
trackOptimizationOutcome(TrackedOutcome::CantInlineExceededDepth);
|
||||
return DontInline(targetScript, "Vetoed: exceeding allowed inline depth");
|
||||
}
|
||||
} else {
|
||||
if (inliningDepth_ >= optimizationInfo().maxInlineDepth()) {
|
||||
trackOptimizationOutcome(TrackedOutcome::CantInlineExceededDepth);
|
||||
return DontInline(targetScript, "Vetoed: exceeding allowed inline depth");
|
||||
}
|
||||
|
||||
if (targetScript->hasLoops()) {
|
||||
// Currently, we are not inlining function which have loops because
|
||||
// the cost inherent to inlining the function overcome the cost
|
||||
// calling it. The reason is not yet clear to everybody, and we
|
||||
// hope that we might be able to remove this restriction in the
|
||||
// future.
|
||||
//
|
||||
// In the mean time, if we have opportunities to optimize the
|
||||
// loop better, then we should try it. Such opportunity might be
|
||||
// suggested by:
|
||||
//
|
||||
// - Constant as argument. Inlining a function called with a
|
||||
// constant, might help GVN, as well as UCE, and potentially
|
||||
// improve bound check removal.
|
||||
//
|
||||
// - Inner function as argument. Inlining a function called
|
||||
// with an inner function might help scalar replacement at
|
||||
// removing the scope chain, and thus using registers within
|
||||
// the loop instead of writting everything back to memory.
|
||||
bool hasOpportunities = false;
|
||||
for (size_t i = 0, e = callInfo.argv().length(); !hasOpportunities && i < e; i++) {
|
||||
MDefinition *arg = callInfo.argv()[i];
|
||||
hasOpportunities = arg->isLambda() || arg->isConstantValue();
|
||||
}
|
||||
|
||||
if (!hasOpportunities) {
|
||||
trackOptimizationOutcome(TrackedOutcome::CantInlineBigLoop);
|
||||
return DontInline(targetScript, "Vetoed: big function that contains a loop");
|
||||
}
|
||||
}
|
||||
|
||||
// Caller must not be excessively large.
|
||||
if (script()->length() >= optimizationInfo().inliningMaxCallerBytecodeLength()) {
|
||||
trackOptimizationOutcome(TrackedOutcome::CantInlineBigCaller);
|
||||
return DontInline(targetScript, "Vetoed: caller excessively large");
|
||||
}
|
||||
}
|
||||
|
||||
// Callee must not be excessively large.
|
||||
// This heuristic also applies to the callsite as a whole.
|
||||
bool offThread = options.offThreadCompilationAvailable();
|
||||
@ -4922,6 +4875,65 @@ IonBuilder::makeInliningDecision(JSObject *targetArg, CallInfo &callInfo)
|
||||
return DontInline(targetScript, "Vetoed: exceeding max total bytecode length");
|
||||
}
|
||||
|
||||
// Cap the inlining depth.
|
||||
|
||||
uint32_t maxInlineDepth;
|
||||
if (js_JitOptions.isSmallFunction(targetScript)) {
|
||||
maxInlineDepth = optimizationInfo().smallFunctionMaxInlineDepth();
|
||||
} else {
|
||||
maxInlineDepth = optimizationInfo().maxInlineDepth();
|
||||
|
||||
// Caller must not be excessively large.
|
||||
if (script()->length() >= optimizationInfo().inliningMaxCallerBytecodeLength()) {
|
||||
trackOptimizationOutcome(TrackedOutcome::CantInlineBigCaller);
|
||||
return DontInline(targetScript, "Vetoed: caller excessively large");
|
||||
}
|
||||
}
|
||||
|
||||
BaselineScript *outerBaseline = outermostBuilder()->script()->baselineScript();
|
||||
if (inliningDepth_ >= maxInlineDepth) {
|
||||
// We hit the depth limit and won't inline this function. Give the
|
||||
// outermost script a max inlining depth of 0, so that it won't be
|
||||
// inlined in other scripts. This heuristic is currently only used
|
||||
// when we're inlining scripts with loops, see the comment below.
|
||||
outerBaseline->setMaxInliningDepth(0);
|
||||
|
||||
trackOptimizationOutcome(TrackedOutcome::CantInlineExceededDepth);
|
||||
return DontInline(targetScript, "Vetoed: exceeding allowed inline depth");
|
||||
}
|
||||
|
||||
// Inlining functions with loops can be complicated. For instance, if we're
|
||||
// close to the inlining depth limit and we inline the function f below, we
|
||||
// can no longer inline the call to g:
|
||||
//
|
||||
// function f() {
|
||||
// while (cond) {
|
||||
// g();
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// If the loop has many iterations, it's more efficient to call f and inline
|
||||
// g in f.
|
||||
//
|
||||
// To avoid this problem, we record a separate max inlining depth for each
|
||||
// script, indicating at which depth we won't be able to inline all functions
|
||||
// we inlined this time. This solves the issue above, because we will only
|
||||
// inline f if it means we can also inline g.
|
||||
if (targetScript->hasLoops() &&
|
||||
inliningDepth_ >= targetScript->baselineScript()->maxInliningDepth())
|
||||
{
|
||||
trackOptimizationOutcome(TrackedOutcome::CantInlineExceededDepth);
|
||||
return DontInline(targetScript, "Vetoed: exceeding allowed script inline depth");
|
||||
}
|
||||
|
||||
// Update the max depth at which we can inline the outer script.
|
||||
MOZ_ASSERT(maxInlineDepth > inliningDepth_);
|
||||
uint32_t scriptInlineDepth = maxInlineDepth - inliningDepth_ - 1;
|
||||
if (scriptInlineDepth < outerBaseline->maxInliningDepth())
|
||||
outerBaseline->setMaxInliningDepth(scriptInlineDepth);
|
||||
|
||||
// End of heuristics, we will inline this function.
|
||||
|
||||
// TI calls ObjectStateChange to trigger invalidation of the caller.
|
||||
TypeSet::ObjectKey *targetKey = TypeSet::ObjectKey::get(target);
|
||||
targetKey->watchStateChangeForInlinedCall(constraints());
|
||||
|
@ -160,10 +160,6 @@ JitOptions::JitOptions()
|
||||
SET_DEFAULT(osrPcMismatchesBeforeRecompile, 6000);
|
||||
|
||||
// The bytecode length limit for small function.
|
||||
//
|
||||
// The default for this was arrived at empirically via benchmarking.
|
||||
// We may want to tune it further after other optimizations have gone
|
||||
// in.
|
||||
SET_DEFAULT(smallFunctionMaxBytecodeLength_, 100);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user