use rustc_middle::mir::*;
use rustc_middle::ty::{self, Instance, InstanceDef, ParamEnv, Ty, TyCtxt};
use rustc_session::config::OptLevel;
-use rustc_span::def_id::DefId;
use rustc_span::{hygiene::ExpnKind, ExpnData, LocalExpnId, Span};
use rustc_target::abi::VariantIdx;
use rustc_target::spec::abi::Abi;
let param_env = tcx.param_env_reveal_all_normalized(def_id);
- let mut this = Inliner {
- tcx,
- param_env,
- codegen_fn_attrs: tcx.codegen_fn_attrs(def_id),
- history: Vec::new(),
- changed: false,
- };
+ let mut this =
+ Inliner { tcx, param_env, codegen_fn_attrs: tcx.codegen_fn_attrs(def_id), changed: false };
let blocks = BasicBlock::new(0)..body.basic_blocks.next_index();
this.process_blocks(body, blocks);
this.changed
param_env: ParamEnv<'tcx>,
/// Caller codegen attributes.
codegen_fn_attrs: &'tcx CodegenFnAttrs,
- /// Stack of inlined instances.
- /// We only check the `DefId` and not the substs because we want to
- /// avoid inlining cases of polymorphic recursion.
- /// The number of `DefId`s is finite, so checking history is enough
- /// to ensure that we do not loop endlessly while inlining.
- history: Vec<DefId>,
/// Indicates that the caller body has been modified.
changed: bool,
}
debug!("not-inlined {} [{}]", callsite.callee, reason);
continue;
}
- Ok(new_blocks) => {
+ Ok(_) => {
debug!("inlined {}", callsite.callee);
self.changed = true;
- self.history.push(callsite.callee.def_id());
- self.process_blocks(caller_body, new_blocks);
- self.history.pop();
+ // We could process the blocks returned by `try_inlining` here. However, that
+ // leads to exponential compile times due to the top-down nature of this kind
+ // of inlining.
}
}
}
return None;
}
- if self.history.contains(&callee.def_id()) {
- return None;
- }
-
let fn_sig = self.tcx.bound_fn_sig(def_id).subst(self.tcx, substs);
return Some(CallSite {
return Err("C variadic");
}
- if callee_attrs.flags.contains(CodegenFnAttrFlags::NAKED) {
- return Err("naked");
- }
-
if callee_attrs.flags.contains(CodegenFnAttrFlags::COLD) {
return Err("cold");
}
return Err("incompatible sanitizer set");
}
- if callee_attrs.instruction_set != self.codegen_fn_attrs.instruction_set {
+ // Two functions are compatible if the callee has no attribute (meaning
+ // that it's codegen agnostic), or sets an attribute that is identical
+ // to this function's attribute.
+ if callee_attrs.instruction_set.is_some()
+ && callee_attrs.instruction_set != self.codegen_fn_attrs.instruction_set
+ {
return Err("incompatible instruction set");
}
if ty.needs_drop(tcx, self.param_env) && let Some(unwind) = unwind {
work_list.push(unwind);
}
+ } else if callee_attrs.instruction_set != self.codegen_fn_attrs.instruction_set
+ && matches!(term.kind, TerminatorKind::InlineAsm { .. })
+ {
+ // During the attribute checking stage we allow a callee with no
+ // instruction_set assigned to count as compatible with a function that does
+ // assign one. However, during this stage we require an exact match when any
+ // inline-asm is detected. LLVM will still possibly do an inline later on
+ // if the no-attribute function ends up with the same instruction set anyway.
+ return Err("Cannot move inline-asm across instruction sets");
} else {
work_list.extend(term.successors())
}