Avoid MIR bloat in inlining

In 126578 we ended up with more binary size increases than expected.

This change attempts to avoid inlining large things into small things, to avoid that kind of increase, in cases when top-down inlining will still be able to do that inlining later.
This commit is contained in:
Scott McMurray 2024-06-29 00:48:23 -07:00
parent c3774be741
commit 23c8ed14c9
18 changed files with 387 additions and 847 deletions

View file

@ -85,13 +85,18 @@ fn inline<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) -> bool {
}
let param_env = tcx.param_env_reveal_all_normalized(def_id);
let codegen_fn_attrs = tcx.codegen_fn_attrs(def_id);
let mut this = Inliner {
tcx,
param_env,
codegen_fn_attrs: tcx.codegen_fn_attrs(def_id),
codegen_fn_attrs,
history: Vec::new(),
changed: false,
caller_is_inline_forwarder: matches!(
codegen_fn_attrs.inline,
InlineAttr::Hint | InlineAttr::Always
) && body_is_forwarder(body),
};
let blocks = START_BLOCK..body.basic_blocks.next_index();
this.process_blocks(body, blocks);
@ -111,6 +116,9 @@ struct Inliner<'tcx> {
history: Vec<DefId>,
/// Indicates that the caller body has been modified.
changed: bool,
/// Indicates that the caller is #[inline] and just calls another function,
/// and thus we can inline less into it as it'll be inlined itself.
caller_is_inline_forwarder: bool,
}
impl<'tcx> Inliner<'tcx> {
@ -485,7 +493,9 @@ impl<'tcx> Inliner<'tcx> {
) -> Result<(), &'static str> {
let tcx = self.tcx;
let mut threshold = if cross_crate_inlinable {
let mut threshold = if self.caller_is_inline_forwarder {
self.tcx.sess.opts.unstable_opts.inline_mir_forwarder_threshold.unwrap_or(30)
} else if cross_crate_inlinable {
self.tcx.sess.opts.unstable_opts.inline_mir_hint_threshold.unwrap_or(100)
} else {
self.tcx.sess.opts.unstable_opts.inline_mir_threshold.unwrap_or(50)
@ -504,6 +514,8 @@ impl<'tcx> Inliner<'tcx> {
let mut checker =
CostChecker::new(self.tcx, self.param_env, Some(callsite.callee), callee_body);
checker.add_function_level_costs();
// Traverse the MIR manually so we can account for the effects of inlining on the CFG.
let mut work_list = vec![START_BLOCK];
let mut visited = BitSet::new_empty(callee_body.basic_blocks.len());
@ -1091,3 +1103,37 @@ fn try_instance_mir<'tcx>(
}
Ok(tcx.instance_mir(instance))
}
fn body_is_forwarder(body: &Body<'_>) -> bool {
let TerminatorKind::Call { target, .. } = body.basic_blocks[START_BLOCK].terminator().kind
else {
return false;
};
if let Some(target) = target {
let TerminatorKind::Return = body.basic_blocks[target].terminator().kind else {
return false;
};
}
let max_blocks = if !body.is_polymorphic {
2
} else if target.is_none() {
3
} else {
4
};
if body.basic_blocks.len() > max_blocks {
return false;
}
body.basic_blocks.iter_enumerated().all(|(bb, bb_data)| {
bb == START_BLOCK
|| matches!(
bb_data.terminator().kind,
TerminatorKind::Return
| TerminatorKind::Drop { .. }
| TerminatorKind::UnwindResume
| TerminatorKind::UnwindTerminate(_)
)
})
}