Auto merge of #116505 - saethlin:infer-inline, r=cjgillot

Automatically enable cross-crate inlining for small functions

This is basically reviving https://github.com/rust-lang/rust/pull/70550

The `#[inline]` attribute can have a significant impact on code generation or runtime performance (because it enables inlining between CGUs where it would normally not happen) and also on compile-time performance (because it enables MIR inlining). But it has to be added manually, which is awkward.

This PR factors whether a DefId is cross-crate inlinable into a query, and replaces all uses of `CodegenFnAttrs::requests_inline` with this new query. The new query incorporates all the other logic that is used to determine whether a Def should be treated as cross-crate-inlinable, and as a last step inspects the function's optimized_mir to determine if it should be treated as cross-crate-inlinable.

The heuristic implemented here is deliberately conservative; we only infer inlinability for functions whose optimized_mir does not contain any calls or asserts. I plan to study adjusting the cost model later, but for now the compile time implications of this change are so significant that I think this very crude heuristic is well worth landing.
This commit is contained in:
bors 2023-10-18 02:00:44 +00:00
commit 5d5edf0248
68 changed files with 457 additions and 350 deletions

View File

@ -770,6 +770,7 @@ fn test_unstable_options_tracking_hash() {
);
tracked!(codegen_backend, Some("abc".to_string()));
tracked!(crate_attr, vec!["abc".to_string()]);
tracked!(cross_crate_inline_threshold, Some(200));
tracked!(debug_info_for_profiling, true);
tracked!(debug_macros, true);
tracked!(dep_info_omit_d_target, true);

View File

@ -1273,6 +1273,10 @@ impl<'a, 'tcx> CrateMetadataRef<'a> {
self.root.tables.optimized_mir.get(self, id).is_some()
}
fn cross_crate_inlinable(self, id: DefIndex) -> bool {
self.root.tables.cross_crate_inlinable.get(self, id).unwrap_or(false)
}
fn get_fn_has_self_parameter(self, id: DefIndex, sess: &'a Session) -> bool {
self.root
.tables

View File

@ -287,6 +287,7 @@ provide! { tcx, def_id, other, cdata,
item_attrs => { tcx.arena.alloc_from_iter(cdata.get_item_attrs(def_id.index, tcx.sess)) }
is_mir_available => { cdata.is_item_mir_available(def_id.index) }
is_ctfe_mir_available => { cdata.is_ctfe_mir_available(def_id.index) }
cross_crate_inlinable => { cdata.cross_crate_inlinable(def_id.index) }
dylib_dependency_formats => { cdata.get_dylib_dependency_formats(tcx) }
is_private_dep => {

View File

@ -1046,7 +1046,7 @@ fn should_encode_mir(
|| (tcx.sess.opts.output_types.should_codegen()
&& reachable_set.contains(&def_id)
&& (generics.requires_monomorphization(tcx)
|| tcx.codegen_fn_attrs(def_id).requests_inline()));
|| tcx.cross_crate_inlinable(def_id)));
// The function has a `const` modifier or is in a `#[const_trait]`.
let is_const_fn = tcx.is_const_fn_raw(def_id.to_def_id())
|| tcx.is_const_default_method(def_id.to_def_id());
@ -1615,6 +1615,9 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
debug!("EntryBuilder::encode_mir({:?})", def_id);
if encode_opt {
record!(self.tables.optimized_mir[def_id.to_def_id()] <- tcx.optimized_mir(def_id));
self.tables
.cross_crate_inlinable
.set(def_id.to_def_id().index, Some(self.tcx.cross_crate_inlinable(def_id)));
record!(self.tables.closure_saved_names_of_captured_variables[def_id.to_def_id()]
<- tcx.closure_saved_names_of_captured_variables(def_id));

View File

@ -427,6 +427,7 @@ define_tables! {
object_lifetime_default: Table<DefIndex, LazyValue<ObjectLifetimeDefault>>,
optimized_mir: Table<DefIndex, LazyValue<mir::Body<'static>>>,
mir_for_ctfe: Table<DefIndex, LazyValue<mir::Body<'static>>>,
cross_crate_inlinable: Table<DefIndex, bool>,
closure_saved_names_of_captured_variables: Table<DefIndex, LazyValue<IndexVec<FieldIdx, Symbol>>>,
mir_generator_witnesses: Table<DefIndex, LazyValue<mir::GeneratorLayout<'static>>>,
promoted_mir: Table<DefIndex, LazyValue<IndexVec<mir::Promoted, mir::Body<'static>>>>,

View File

@ -299,6 +299,30 @@ impl FixedSizeEncoding for bool {
}
}
impl FixedSizeEncoding for Option<bool> {
type ByteArray = [u8; 1];
#[inline]
fn from_bytes(b: &[u8; 1]) -> Self {
match b[0] {
0 => Some(false),
1 => Some(true),
2 => None,
_ => unreachable!(),
}
}
#[inline]
fn write_to_bytes(self, b: &mut [u8; 1]) {
debug_assert!(!self.is_default());
b[0] = match self {
Some(false) => 0,
Some(true) => 1,
None => 2,
};
}
}
impl FixedSizeEncoding for UnusedGenericParams {
type ByteArray = [u8; 4];

View File

@ -126,14 +126,6 @@ impl CodegenFnAttrs {
}
}
/// Returns `true` if `#[inline]` or `#[inline(always)]` is present.
pub fn requests_inline(&self) -> bool {
match self.inline {
InlineAttr::Hint | InlineAttr::Always => true,
InlineAttr::None | InlineAttr::Never => false,
}
}
/// Returns `true` if it looks like this symbol needs to be exported, for example:
///
/// * `#[no_mangle]` is present

View File

@ -2202,6 +2202,11 @@ rustc_queries! {
query generics_require_sized_self(def_id: DefId) -> bool {
desc { "check whether the item has a `where Self: Sized` bound" }
}
query cross_crate_inlinable(def_id: DefId) -> bool {
desc { "whether the item should be made inlinable across crates" }
separate_provide_extern
}
}
rustc_query_append! { define_callbacks! }

View File

@ -245,16 +245,15 @@ impl<'tcx> InstanceDef<'tcx> {
// drops of `Option::None` before LTO. We also respect the intent of
// `#[inline]` on `Drop::drop` implementations.
return ty.ty_adt_def().map_or(true, |adt_def| {
adt_def.destructor(tcx).map_or_else(
|| adt_def.is_enum(),
|dtor| tcx.codegen_fn_attrs(dtor.did).requests_inline(),
)
adt_def
.destructor(tcx)
.map_or_else(|| adt_def.is_enum(), |dtor| tcx.cross_crate_inlinable(dtor.did))
});
}
if let ty::InstanceDef::ThreadLocalShim(..) = *self {
return false;
}
tcx.codegen_fn_attrs(self.def_id()).requests_inline()
tcx.cross_crate_inlinable(self.def_id())
}
pub fn requires_caller_location(&self, tcx: TyCtxt<'_>) -> bool {

View File

@ -0,0 +1,119 @@
use rustc_attr::InlineAttr;
use rustc_hir::def::DefKind;
use rustc_hir::def_id::LocalDefId;
use rustc_middle::mir::visit::Visitor;
use rustc_middle::mir::*;
use rustc_middle::query::Providers;
use rustc_middle::ty::TyCtxt;
use rustc_session::config::OptLevel;
pub fn provide(providers: &mut Providers) {
providers.cross_crate_inlinable = cross_crate_inlinable;
}
fn cross_crate_inlinable(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {
let codegen_fn_attrs = tcx.codegen_fn_attrs(def_id);
// If this has an extern indicator, then this function is globally shared and thus will not
// generate cgu-internal copies which would make it cross-crate inlinable.
if codegen_fn_attrs.contains_extern_indicator() {
return false;
}
// Obey source annotations first; this is important because it means we can use
// #[inline(never)] to force code generation.
match codegen_fn_attrs.inline {
InlineAttr::Never => return false,
InlineAttr::Hint | InlineAttr::Always => return true,
_ => {}
}
// This just reproduces the logic from Instance::requires_inline.
match tcx.def_kind(def_id) {
DefKind::Ctor(..) | DefKind::Closure => return true,
DefKind::Fn | DefKind::AssocFn => {}
_ => return false,
}
// Don't do any inference when incremental compilation is enabled; the additional inlining that
// inference permits also creates more work for small edits.
if tcx.sess.opts.incremental.is_some() {
return false;
}
// Don't do any inference unless optimizations are enabled.
if matches!(tcx.sess.opts.optimize, OptLevel::No) {
return false;
}
if !tcx.is_mir_available(def_id) {
return false;
}
let mir = tcx.optimized_mir(def_id);
let mut checker =
CostChecker { tcx, callee_body: mir, calls: 0, statements: 0, landing_pads: 0, resumes: 0 };
checker.visit_body(mir);
checker.calls == 0
&& checker.resumes == 0
&& checker.landing_pads == 0
&& checker.statements
<= tcx.sess.opts.unstable_opts.cross_crate_inline_threshold.unwrap_or(100)
}
struct CostChecker<'b, 'tcx> {
tcx: TyCtxt<'tcx>,
callee_body: &'b Body<'tcx>,
calls: usize,
statements: usize,
landing_pads: usize,
resumes: usize,
}
impl<'tcx> Visitor<'tcx> for CostChecker<'_, 'tcx> {
fn visit_statement(&mut self, statement: &Statement<'tcx>, _: Location) {
// Don't count StorageLive/StorageDead in the inlining cost.
match statement.kind {
StatementKind::StorageLive(_)
| StatementKind::StorageDead(_)
| StatementKind::Deinit(_)
| StatementKind::Nop => {}
_ => self.statements += 1,
}
}
fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, _: Location) {
let tcx = self.tcx;
match terminator.kind {
TerminatorKind::Drop { ref place, unwind, .. } => {
let ty = place.ty(self.callee_body, tcx).ty;
if !ty.is_trivially_pure_clone_copy() {
self.calls += 1;
if let UnwindAction::Cleanup(_) = unwind {
self.landing_pads += 1;
}
}
}
TerminatorKind::Call { unwind, .. } => {
self.calls += 1;
if let UnwindAction::Cleanup(_) = unwind {
self.landing_pads += 1;
}
}
TerminatorKind::Assert { unwind, .. } => {
self.calls += 1;
if let UnwindAction::Cleanup(_) = unwind {
self.landing_pads += 1;
}
}
TerminatorKind::UnwindResume => self.resumes += 1,
TerminatorKind::InlineAsm { unwind, .. } => {
self.statements += 1;
if let UnwindAction::Cleanup(_) = unwind {
self.landing_pads += 1;
}
}
TerminatorKind::Return => {}
_ => self.statements += 1,
}
}
}

View File

@ -169,8 +169,11 @@ impl<'tcx> Inliner<'tcx> {
caller_body: &mut Body<'tcx>,
callsite: &CallSite<'tcx>,
) -> Result<std::ops::Range<BasicBlock>, &'static str> {
self.check_mir_is_available(caller_body, &callsite.callee)?;
let callee_attrs = self.tcx.codegen_fn_attrs(callsite.callee.def_id());
self.check_codegen_attributes(callsite, callee_attrs)?;
let cross_crate_inlinable = self.tcx.cross_crate_inlinable(callsite.callee.def_id());
self.check_codegen_attributes(callsite, callee_attrs, cross_crate_inlinable)?;
let terminator = caller_body[callsite.block].terminator.as_ref().unwrap();
let TerminatorKind::Call { args, destination, .. } = &terminator.kind else { bug!() };
@ -183,9 +186,8 @@ impl<'tcx> Inliner<'tcx> {
}
}
self.check_mir_is_available(caller_body, &callsite.callee)?;
let callee_body = try_instance_mir(self.tcx, callsite.callee.def)?;
self.check_mir_body(callsite, callee_body, callee_attrs)?;
self.check_mir_body(callsite, callee_body, callee_attrs, cross_crate_inlinable)?;
if !self.tcx.consider_optimizing(|| {
format!("Inline {:?} into {:?}", callsite.callee, caller_body.source)
@ -401,6 +403,7 @@ impl<'tcx> Inliner<'tcx> {
&self,
callsite: &CallSite<'tcx>,
callee_attrs: &CodegenFnAttrs,
cross_crate_inlinable: bool,
) -> Result<(), &'static str> {
if let InlineAttr::Never = callee_attrs.inline {
return Err("never inline hint");
@ -414,7 +417,7 @@ impl<'tcx> Inliner<'tcx> {
.non_erasable_generics(self.tcx, callsite.callee.def_id())
.next()
.is_some();
if !is_generic && !callee_attrs.requests_inline() {
if !is_generic && !cross_crate_inlinable {
return Err("not exported");
}
@ -456,10 +459,11 @@ impl<'tcx> Inliner<'tcx> {
callsite: &CallSite<'tcx>,
callee_body: &Body<'tcx>,
callee_attrs: &CodegenFnAttrs,
cross_crate_inlinable: bool,
) -> Result<(), &'static str> {
let tcx = self.tcx;
let mut threshold = if callee_attrs.requests_inline() {
let mut threshold = 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)

View File

@ -62,6 +62,7 @@ mod const_prop;
mod const_prop_lint;
mod copy_prop;
mod coverage;
mod cross_crate_inline;
mod ctfe_limit;
mod dataflow_const_prop;
mod dead_store_elimination;
@ -123,6 +124,7 @@ pub fn provide(providers: &mut Providers) {
coverage::query::provide(providers);
ffi_unwind_calls::provide(providers);
shim::provide(providers);
cross_crate_inline::provide(providers);
*providers = Providers {
mir_keys,
mir_const,

View File

@ -18,43 +18,10 @@ use rustc_middle::ty::{self, TyCtxt};
use rustc_session::config::CrateType;
use rustc_target::spec::abi::Abi;
// Returns true if the given item must be inlined because it may be
// monomorphized or it was marked with `#[inline]`. This will only return
// true for functions.
fn item_might_be_inlined(tcx: TyCtxt<'_>, item: &hir::Item<'_>, attrs: &CodegenFnAttrs) -> bool {
if attrs.requests_inline() {
return true;
}
match item.kind {
hir::ItemKind::Fn(ref sig, ..) if sig.header.is_const() => true,
hir::ItemKind::Impl { .. } | hir::ItemKind::Fn(..) => {
let generics = tcx.generics_of(item.owner_id);
generics.requires_monomorphization(tcx)
}
_ => false,
}
}
fn method_might_be_inlined(
tcx: TyCtxt<'_>,
impl_item: &hir::ImplItem<'_>,
impl_src: LocalDefId,
) -> bool {
let codegen_fn_attrs = tcx.codegen_fn_attrs(impl_item.hir_id().owner.to_def_id());
let generics = tcx.generics_of(impl_item.owner_id);
if codegen_fn_attrs.requests_inline() || generics.requires_monomorphization(tcx) {
return true;
}
if let hir::ImplItemKind::Fn(method_sig, _) = &impl_item.kind {
if method_sig.header.is_const() {
return true;
}
}
match tcx.hir().find_by_def_id(impl_src) {
Some(Node::Item(item)) => item_might_be_inlined(tcx, &item, codegen_fn_attrs),
Some(..) | None => span_bug!(impl_item.span, "impl did is not an item"),
}
fn item_might_be_inlined(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
tcx.generics_of(def_id).requires_monomorphization(tcx)
|| tcx.cross_crate_inlinable(def_id)
|| tcx.is_const_fn(def_id)
}
// Information needed while computing reachability.
@ -150,9 +117,7 @@ impl<'tcx> ReachableContext<'tcx> {
match self.tcx.hir().find_by_def_id(def_id) {
Some(Node::Item(item)) => match item.kind {
hir::ItemKind::Fn(..) => {
item_might_be_inlined(self.tcx, &item, self.tcx.codegen_fn_attrs(def_id))
}
hir::ItemKind::Fn(..) => item_might_be_inlined(self.tcx, def_id.into()),
_ => false,
},
Some(Node::TraitItem(trait_method)) => match trait_method.kind {
@ -164,9 +129,7 @@ impl<'tcx> ReachableContext<'tcx> {
Some(Node::ImplItem(impl_item)) => match impl_item.kind {
hir::ImplItemKind::Const(..) => true,
hir::ImplItemKind::Fn(..) => {
let hir_id = self.tcx.hir().local_def_id_to_hir_id(def_id);
let impl_did = self.tcx.hir().get_parent_item(hir_id);
method_might_be_inlined(self.tcx, impl_item, impl_did.def_id)
item_might_be_inlined(self.tcx, impl_item.hir_id().owner.to_def_id())
}
hir::ImplItemKind::Type(_) => false,
},
@ -226,11 +189,7 @@ impl<'tcx> ReachableContext<'tcx> {
Node::Item(item) => {
match item.kind {
hir::ItemKind::Fn(.., body) => {
if item_might_be_inlined(
self.tcx,
&item,
self.tcx.codegen_fn_attrs(item.owner_id),
) {
if item_might_be_inlined(self.tcx, item.owner_id.into()) {
self.visit_nested_body(body);
}
}
@ -279,8 +238,7 @@ impl<'tcx> ReachableContext<'tcx> {
self.visit_nested_body(body);
}
hir::ImplItemKind::Fn(_, body) => {
let impl_def_id = self.tcx.local_parent(search_item);
if method_might_be_inlined(self.tcx, impl_item, impl_def_id) {
if item_might_be_inlined(self.tcx, impl_item.hir_id().owner.to_def_id()) {
self.visit_nested_body(body)
}
}

View File

@ -1452,6 +1452,8 @@ options! {
"combine CGUs into a single one"),
crate_attr: Vec<String> = (Vec::new(), parse_string_push, [TRACKED],
"inject the given attribute in the crate"),
cross_crate_inline_threshold: Option<usize> = (None, parse_opt_number, [TRACKED],
"threshold to allow cross crate inlining of functions"),
debug_info_for_profiling: bool = (false, parse_bool, [TRACKED],
"emit discriminators and other data necessary for AutoFDO"),
debug_macros: bool = (false, parse_bool, [TRACKED],

View File

@ -530,6 +530,7 @@ fn alloc_guard(alloc_size: usize) -> Result<(), TryReserveError> {
// ensure that the code generation related to these panics is minimal as there's
// only one location which panics rather than a bunch throughout the module.
#[cfg(not(no_global_oom_handling))]
#[inline(never)]
fn capacity_overflow() -> ! {
panic!("capacity overflow");
}

View File

@ -155,6 +155,7 @@ fn lang_start_internal(
}
#[cfg(not(test))]
#[inline(never)]
#[lang = "start"]
fn lang_start<T: crate::process::Termination + 'static>(
main: fn() -> T,

View File

@ -1,6 +1,7 @@
// assembly-output: emit-asm
// compile-flags: --target armv7-unknown-linux-gnueabihf
// compile-flags: -C target-feature=+neon
// compile-flags: -C opt-level=0
// needs-llvm-components: arm
#![feature(no_core, lang_items, rustc_attrs, repr_simd)]

View File

@ -22,6 +22,7 @@ pub unsafe fn sse41_blend_nofeature(x: __m128, y: __m128) -> __m128 {
f(x, y)
}
#[no_mangle]
#[target_feature(enable = "sse4.1")]
pub fn sse41_blend_noinline(x: __m128, y: __m128) -> __m128 {
let f = {

View File

@ -1,6 +1,6 @@
// Makes sure that `-Z dwarf-version=5` causes `rustc` to emit DWARF version 5.
// assembly-output: emit-asm
// compile-flags: -g --target x86_64-unknown-linux-gnu -Z dwarf-version=5
// compile-flags: -g --target x86_64-unknown-linux-gnu -Z dwarf-version=5 -Copt-level=0
// needs-llvm-components: x86
#![feature(no_core, lang_items)]

View File

@ -6,15 +6,18 @@ pub unsafe fn f() {
//~ MONO_ITEM static f::S @@ asm_sym-cgu.0[External]
static S: usize = 1;
//~ MONO_ITEM fn f::fun @@ asm_sym-cgu.0[External]
#[inline(never)]
fn fun() {}
core::arch::asm!("/* {0} {1} */", sym S, sym fun);
}
//~ MONO_ITEM fn g @@ asm_sym-cgu.0[External]
#[inline(never)]
pub unsafe fn g() {
//~ MONO_ITEM static g::S @@ asm_sym-cgu.0[Internal]
static S: usize = 2;
//~ MONO_ITEM fn g::fun @@ asm_sym-cgu.0[Internal]
#[inline(never)]
fn fun() {}
core::arch::asm!("/* {0} {1} */", sym S, sym fun);
}

View File

@ -1,3 +1,5 @@
// compile-flags: -Copt-level=0
#![crate_type = "lib"]
pub trait Trait : Sized {

View File

@ -1,4 +1,4 @@
// compile-flags:-Zprint-mono-items=eager -Zinline-mir=no
// compile-flags:-Zprint-mono-items=eager -Zinline-mir=no -Copt-level=0
#![deny(dead_code)]
#![feature(start)]

View File

@ -1,4 +1,4 @@
// compile-flags:-Zprint-mono-items=eager
// compile-flags:-Zprint-mono-items=eager -Copt-level=0
#![deny(dead_code)]
#![feature(start)]

View File

@ -4,6 +4,7 @@
#![crate_type = "rlib"]
//~ MONO_ITEM fn foo @@ unreferenced_const_fn-cgu.0[External]
#[inline(never)]
pub const fn foo(x: u32) -> u32 {
x + 0xf00
}

View File

@ -1,5 +1,5 @@
// FIXME(nagisa): remove the flags below once all targets support `asm!`.
// compile-flags: --target x86_64-unknown-linux-gnu
// compile-flags: --target x86_64-unknown-linux-gnu -Copt-level=0
// needs-llvm-components: x86
// Verify we sanitize the special tokens for the LLVM inline-assembly, ensuring people won't

View File

@ -3,7 +3,7 @@
// extensions rely on the field having this name.
// ignore-tidy-linelength
// compile-flags: -C debuginfo=2 --edition=2018
// compile-flags: -C debuginfo=2 --edition=2018 -Copt-level=0
#![crate_type = "lib"]

View File

@ -1,8 +1,8 @@
// Verifies that linkage name is omitted when it is
// the same as variable / function name.
//
// compile-flags: -C no-prepopulate-passes
// compile-flags: -C debuginfo=2
// compile-flags: -C no-prepopulate-passes -Copt-level=0
// compile-flags: -C debuginfo=2 -Copt-level=0
#![crate_type = "lib"]
pub mod xyz {

View File

@ -1,5 +1,5 @@
// revisions: WINDOWS ANDROID
// compile-flags: -C panic=abort
// compile-flags: -C panic=abort -Copt-level=0
// [WINDOWS] compile-flags: --target=x86_64-pc-windows-msvc
// [WINDOWS] needs-llvm-components: x86
// [ANDROID] compile-flags: --target=armv7-linux-androideabi

View File

@ -7,10 +7,12 @@
struct SomeUniqueName;
impl Drop for SomeUniqueName {
#[inline(never)]
fn drop(&mut self) {
}
}
#[inline(never)]
pub fn possibly_unwinding() {
}

View File

@ -1,4 +1,4 @@
// compile-flags: -C no-prepopulate-passes -C force-frame-pointers=y
// compile-flags: -C no-prepopulate-passes -C force-frame-pointers=y -Copt-level=0
#![crate_type="lib"]

View File

@ -1,4 +1,4 @@
// compile-flags: -C no-prepopulate-passes -C force-unwind-tables=y
// compile-flags: -C no-prepopulate-passes -C force-unwind-tables=y -Copt-level=0
#![crate_type="lib"]

View File

@ -6,6 +6,7 @@
#![crate_type = "lib"]
#[inline(never)]
pub fn outer_function(x: usize, y: usize) -> usize {
inner_function(x, y) + 1
}
@ -13,8 +14,8 @@ pub fn outer_function(x: usize, y: usize) -> usize {
#[inline]
fn inner_function(aaaa: usize, bbbb: usize) -> usize {
// CHECK: !DILocalVariable(name: "aaaa", arg: 1
// CHECK-SAME: line: 14
// CHECK-SAME: line: 15
// CHECK: !DILocalVariable(name: "bbbb", arg: 2
// CHECK-SAME: line: 14
// CHECK-SAME: line: 15
aaaa + bbbb
}

View File

@ -1,5 +1,5 @@
//
// compile-flags: -Z instrument-mcount
// compile-flags: -Z instrument-mcount -Copt-level=0
#![crate_type = "lib"]

View File

@ -1,7 +1,7 @@
// Checks that `-Z instrument-xray` produces expected instrumentation.
//
// needs-xray
// compile-flags: -Z instrument-xray=always
// compile-flags: -Z instrument-xray=always -Copt-level=0
#![crate_type = "lib"]

View File

@ -1,9 +1,9 @@
// Checks that `-Z instrument-xray` options can be specified multiple times.
//
// needs-xray
// compile-flags: -Z instrument-xray=skip-exit
// compile-flags: -Z instrument-xray=instruction-threshold=123
// compile-flags: -Z instrument-xray=instruction-threshold=456
// compile-flags: -Z instrument-xray=skip-exit -Copt-level=0
// compile-flags: -Z instrument-xray=instruction-threshold=123 -Copt-level=0
// compile-flags: -Z instrument-xray=instruction-threshold=456 -Copt-level=0
#![crate_type = "lib"]

View File

@ -1,8 +1,8 @@
// Checks that the last `-Z instrument-xray` option wins.
//
// needs-xray
// compile-flags: -Z instrument-xray=always
// compile-flags: -Z instrument-xray=never
// compile-flags: -Z instrument-xray=always -Copt-level=0
// compile-flags: -Z instrument-xray=never -Copt-level=0
#![crate_type = "lib"]

View File

@ -1,4 +1,4 @@
// compile-flags: -C panic=unwind -C no-prepopulate-passes
// compile-flags: -C panic=unwind -C no-prepopulate-passes -Copt-level=0
#![crate_type = "lib"]

View File

@ -9,10 +9,12 @@
struct S;
impl Drop for S {
#[inline(never)]
fn drop(&mut self) {
}
}
#[inline(never)]
fn might_unwind() {
}

View File

@ -1,7 +1,7 @@
// Verifies that user-defined CFI encoding for types are emitted.
//
// needs-sanitizer-cfi
// compile-flags: -Clto -Cno-prepopulate-passes -Ctarget-feature=-crt-static -Zsanitizer=cfi
// compile-flags: -Clto -Cno-prepopulate-passes -Ctarget-feature=-crt-static -Zsanitizer=cfi -Copt-level=0
#![crate_type="lib"]
#![feature(cfi_encoding, extern_types)]

View File

@ -1,7 +1,7 @@
// Verifies that type metadata identifiers for functions are emitted correctly.
//
// needs-sanitizer-cfi
// compile-flags: -Clto -Cno-prepopulate-passes -Ctarget-feature=-crt-static -Zsanitizer=cfi
// compile-flags: -Clto -Cno-prepopulate-passes -Ctarget-feature=-crt-static -Zsanitizer=cfi -Copt-level=0
#![crate_type="lib"]
#![allow(dead_code)]

View File

@ -1,7 +1,7 @@
// Verifies that pointer types are generalized.
//
// needs-sanitizer-cfi
// compile-flags: -Clto -Cno-prepopulate-passes -Ctarget-feature=-crt-static -Zsanitizer=cfi -Zsanitizer-cfi-generalize-pointers
// compile-flags: -Clto -Cno-prepopulate-passes -Ctarget-feature=-crt-static -Zsanitizer=cfi -Zsanitizer-cfi-generalize-pointers -Copt-level=0
#![crate_type="lib"]

View File

@ -1,7 +1,7 @@
// Verifies that integer types are normalized.
//
// needs-sanitizer-cfi
// compile-flags: -Clto -Cno-prepopulate-passes -Ctarget-feature=-crt-static -Zsanitizer=cfi -Zsanitizer-cfi-normalize-integers
// compile-flags: -Clto -Cno-prepopulate-passes -Ctarget-feature=-crt-static -Zsanitizer=cfi -Zsanitizer-cfi-normalize-integers -Copt-level=0
#![crate_type="lib"]

View File

@ -1,6 +1,6 @@
// Verifies that `-Zsanitizer=kernel-address` emits sanitizer instrumentation.
// compile-flags: -Zsanitizer=kernel-address
// compile-flags: -Zsanitizer=kernel-address -Copt-level=0
// revisions: aarch64 riscv64imac riscv64gc x86_64
//[aarch64] compile-flags: --target aarch64-unknown-none
//[aarch64] needs-llvm-components: aarch64

View File

@ -2,7 +2,7 @@
// applied when enabling the memtag sanitizer.
//
// needs-sanitizer-memtag
// compile-flags: -Zsanitizer=memtag -Ctarget-feature=+mte
// compile-flags: -Zsanitizer=memtag -Ctarget-feature=+mte -Copt-level=0
#![crate_type = "lib"]

View File

@ -2,7 +2,7 @@
// selectively disable sanitizer instrumentation.
//
// needs-sanitizer-address
// compile-flags: -Zsanitizer=address -Ctarget-feature=-crt-static
// compile-flags: -Zsanitizer=address -Ctarget-feature=-crt-static -Copt-level=0
#![crate_type="lib"]
#![feature(no_sanitize)]

View File

@ -1,7 +1,7 @@
// This tests that the safestack attribute is applied when enabling the safe-stack sanitizer.
//
// needs-sanitizer-safestack
// compile-flags: -Zsanitizer=safestack
// compile-flags: -Zsanitizer=safestack -Copt-level=0
#![crate_type = "lib"]

View File

@ -1,4 +1,4 @@
// compile-flags: -g -Z src-hash-algorithm=md5
// compile-flags: -g -Z src-hash-algorithm=md5 -Copt-level=0
#![crate_type = "lib"]

View File

@ -1,4 +1,4 @@
// compile-flags: -g -Z src-hash-algorithm=sha1
// compile-flags: -g -Z src-hash-algorithm=sha1 -Copt-level=0
#![crate_type = "lib"]

View File

@ -1,4 +1,4 @@
// compile-flags: -g -Z src-hash-algorithm=sha256
// compile-flags: -g -Z src-hash-algorithm=sha256 -Copt-level=0
#![crate_type = "lib"]

View File

@ -15,7 +15,8 @@ pub extern "C" fn exported() {
// CHECK-LABEL: ; target_cpu_on_functions::not_exported
// CHECK-NEXT: ; Function Attrs:
// CHECK-NEXT: define {{.*}}() {{.*}} #0
// CHECK-NEXT: define {{.*}}() {{.*}} #1
#[inline(never)]
fn not_exported() {}
// CHECK: attributes #0 = {{.*}} "target-cpu"="{{.*}}"

View File

@ -7,16 +7,16 @@
// are targeting older LLVM versions. Once the min supported version
// is LLVM-14 we can remove the optional regex matching for this feature.
// [ENABLE_SVE] compile-flags: -C target-feature=+sve
// [ENABLE_SVE] compile-flags: -C target-feature=+sve -Copt-level=0
// ENABLE_SVE: attributes #0 = { {{.*}} "target-features"="{{((\+outline-atomics,?)|(\+v8a,?)?|(\+sve,?)|(\+neon,?))*}}" }
// [DISABLE_SVE] compile-flags: -C target-feature=-sve
// [DISABLE_SVE] compile-flags: -C target-feature=-sve -Copt-level=0
// DISABLE_SVE: attributes #0 = { {{.*}} "target-features"="{{((\+outline-atomics,?)|(\+v8a,?)?|(-sve,?)|(\+neon,?))*}}" }
// [DISABLE_NEON] compile-flags: -C target-feature=-neon
// [DISABLE_NEON] compile-flags: -C target-feature=-neon -Copt-level=0
// DISABLE_NEON: attributes #0 = { {{.*}} "target-features"="{{((\+outline-atomics,?)|(\+v8a,?)?|(-fp-armv8,?)|(-neon,?))*}}" }
// [ENABLE_NEON] compile-flags: -C target-feature=+neon
// [ENABLE_NEON] compile-flags: -C target-feature=+neon -Copt-level=0
// ENABLE_NEON: attributes #0 = { {{.*}} "target-features"="{{((\+outline-atomics,?)|(\+v8a,?)?|(\+fp-armv8,?)|(\+neon,?))*}}" }

View File

@ -3,7 +3,7 @@
// no-prefer-dynamic
//
// compile-flags: -C no-prepopulate-passes -C panic=abort -C linker-plugin-lto -Cpasses=name-anon-globals -Z tune-cpu=generic
// compile-flags: -C no-prepopulate-passes -C panic=abort -C linker-plugin-lto -Cpasses=name-anon-globals -Z tune-cpu=generic -Copt-level=0
#![crate_type = "staticlib"]

View File

@ -1,45 +1,62 @@
Function name: <try_error_result::Thing1>::get_thing_2
Raw bytes (28): 0x[01, 01, 02, 01, 05, 05, 02, 04, 01, 28, 05, 01, 18, 05, 02, 0d, 00, 14, 02, 02, 0d, 00, 1a, 07, 02, 05, 00, 06]
Raw bytes (63): 0x[01, 01, 02, 01, 05, 05, 02, 0b, 01, 28, 05, 01, 18, 05, 02, 0d, 00, 14, 00, 00, 0d, 00, 14, 00, 00, 0d, 00, 14, 00, 00, 0d, 00, 14, 00, 00, 0d, 00, 14, 00, 00, 0d, 00, 14, 02, 02, 0d, 00, 1a, 00, 00, 0d, 00, 1a, 00, 00, 0d, 00, 1a, 07, 02, 05, 00, 06]
Number of files: 1
- file 0 => global file 1
Number of expressions: 2
- expression 0 operands: lhs = Counter(0), rhs = Counter(1)
- expression 1 operands: lhs = Counter(1), rhs = Expression(0, Sub)
Number of file 0 mappings: 4
Number of file 0 mappings: 11
- Code(Counter(0)) at (prev + 40, 5) to (start + 1, 24)
- Code(Counter(1)) at (prev + 2, 13) to (start + 0, 20)
- Code(Zero) at (prev + 0, 13) to (start + 0, 20)
- Code(Zero) at (prev + 0, 13) to (start + 0, 20)
- Code(Zero) at (prev + 0, 13) to (start + 0, 20)
- Code(Zero) at (prev + 0, 13) to (start + 0, 20)
- Code(Zero) at (prev + 0, 13) to (start + 0, 20)
- Code(Expression(0, Sub)) at (prev + 2, 13) to (start + 0, 26)
= (c0 - c1)
- Code(Zero) at (prev + 0, 13) to (start + 0, 26)
- Code(Zero) at (prev + 0, 13) to (start + 0, 26)
- Code(Expression(1, Add)) at (prev + 2, 5) to (start + 0, 6)
= (c1 + (c0 - c1))
Function name: <try_error_result::Thing2>::call
Raw bytes (28): 0x[01, 01, 02, 01, 05, 05, 02, 04, 01, 33, 05, 01, 18, 05, 02, 0d, 00, 14, 02, 02, 0d, 00, 13, 07, 02, 05, 00, 06]
Raw bytes (63): 0x[01, 01, 02, 01, 05, 05, 02, 0b, 01, 33, 05, 01, 18, 05, 02, 0d, 00, 14, 00, 00, 0d, 00, 14, 00, 00, 0d, 00, 14, 00, 00, 0d, 00, 14, 00, 00, 0d, 00, 14, 02, 02, 0d, 00, 13, 00, 00, 0d, 00, 13, 00, 00, 0d, 00, 13, 00, 00, 0d, 00, 13, 07, 02, 05, 00, 06]
Number of files: 1
- file 0 => global file 1
Number of expressions: 2
- expression 0 operands: lhs = Counter(0), rhs = Counter(1)
- expression 1 operands: lhs = Counter(1), rhs = Expression(0, Sub)
Number of file 0 mappings: 4
Number of file 0 mappings: 11
- Code(Counter(0)) at (prev + 51, 5) to (start + 1, 24)
- Code(Counter(1)) at (prev + 2, 13) to (start + 0, 20)
- Code(Zero) at (prev + 0, 13) to (start + 0, 20)
- Code(Zero) at (prev + 0, 13) to (start + 0, 20)
- Code(Zero) at (prev + 0, 13) to (start + 0, 20)
- Code(Zero) at (prev + 0, 13) to (start + 0, 20)
- Code(Expression(0, Sub)) at (prev + 2, 13) to (start + 0, 19)
= (c0 - c1)
- Code(Zero) at (prev + 0, 13) to (start + 0, 19)
- Code(Zero) at (prev + 0, 13) to (start + 0, 19)
- Code(Zero) at (prev + 0, 13) to (start + 0, 19)
- Code(Expression(1, Add)) at (prev + 2, 5) to (start + 0, 6)
= (c1 + (c0 - c1))
Function name: try_error_result::call
Raw bytes (28): 0x[01, 01, 02, 01, 05, 05, 02, 04, 01, 04, 01, 01, 14, 05, 02, 09, 00, 10, 02, 02, 09, 00, 0f, 07, 02, 01, 00, 02]
Raw bytes (43): 0x[01, 01, 02, 01, 05, 05, 02, 07, 01, 04, 01, 01, 14, 05, 02, 09, 00, 10, 00, 00, 09, 00, 10, 00, 00, 09, 00, 10, 02, 02, 09, 00, 0f, 00, 00, 09, 00, 0f, 07, 02, 01, 00, 02]
Number of files: 1
- file 0 => global file 1
Number of expressions: 2
- expression 0 operands: lhs = Counter(0), rhs = Counter(1)
- expression 1 operands: lhs = Counter(1), rhs = Expression(0, Sub)
Number of file 0 mappings: 4
Number of file 0 mappings: 7
- Code(Counter(0)) at (prev + 4, 1) to (start + 1, 20)
- Code(Counter(1)) at (prev + 2, 9) to (start + 0, 16)
- Code(Zero) at (prev + 0, 9) to (start + 0, 16)
- Code(Zero) at (prev + 0, 9) to (start + 0, 16)
- Code(Expression(0, Sub)) at (prev + 2, 9) to (start + 0, 15)
= (c0 - c1)
- Code(Zero) at (prev + 0, 9) to (start + 0, 15)
- Code(Expression(1, Add)) at (prev + 2, 1) to (start + 0, 2)
= (c1 + (c0 - c1))

View File

@ -4,29 +4,20 @@
fn main() -> () {
let mut _0: ();
let _1: main::Un;
let mut _2: u32;
let mut _3: u32;
scope 1 {
debug un => _1;
scope 2 {
}
scope 3 (inlined std::mem::drop::<u32>) {
debug _x => _3;
scope 4 (inlined std::mem::drop::<u32>) {
debug _x => const 1_u32;
}
}
scope 3 (inlined val) {
}
bb0: {
StorageLive(_1);
StorageLive(_2);
_2 = val() -> [return: bb1, unwind unreachable];
}
bb1: {
_1 = Un { us: move _2 };
StorageDead(_2);
StorageLive(_3);
_3 = (_1.0: u32);
StorageDead(_3);
_1 = Un { us: const 1_u32 };
StorageDead(_1);
return;
}

View File

@ -4,29 +4,20 @@
fn main() -> () {
let mut _0: ();
let _1: main::Un;
let mut _2: u32;
let mut _3: u32;
scope 1 {
debug un => _1;
scope 2 {
}
scope 3 (inlined std::mem::drop::<u32>) {
debug _x => _3;
scope 4 (inlined std::mem::drop::<u32>) {
debug _x => const 1_u32;
}
}
scope 3 (inlined val) {
}
bb0: {
StorageLive(_1);
StorageLive(_2);
_2 = val() -> [return: bb1, unwind continue];
}
bb1: {
_1 = Un { us: move _2 };
StorageDead(_2);
StorageLive(_3);
_3 = (_1.0: u32);
StorageDead(_3);
_1 = Un { us: const 1_u32 };
StorageDead(_1);
return;
}

View File

@ -7,104 +7,104 @@
debug upper => _3;
let mut _0: std::result::Result<(), std::fmt::Error>;
let _4: bool;
let mut _5: &std::fmt::Formatter<'_>;
let mut _7: std::option::Option<usize>;
let mut _8: &std::fmt::Formatter<'_>;
let mut _9: isize;
let mut _11: &mut std::fmt::Formatter<'_>;
let mut _12: &T;
let mut _13: core::num::flt2dec::Sign;
let mut _14: u32;
let mut _15: u32;
let mut _16: usize;
let mut _17: bool;
let mut _18: &mut std::fmt::Formatter<'_>;
let mut _19: &T;
let mut _20: core::num::flt2dec::Sign;
let mut _21: bool;
let mut _6: std::option::Option<usize>;
let mut _7: isize;
let mut _9: &mut std::fmt::Formatter<'_>;
let mut _10: &T;
let mut _11: core::num::flt2dec::Sign;
let mut _12: u32;
let mut _13: u32;
let mut _14: usize;
let mut _15: bool;
let mut _16: &mut std::fmt::Formatter<'_>;
let mut _17: &T;
let mut _18: core::num::flt2dec::Sign;
let mut _19: bool;
scope 1 {
debug force_sign => _4;
let _6: core::num::flt2dec::Sign;
let _5: core::num::flt2dec::Sign;
scope 2 {
debug sign => _6;
debug sign => _5;
scope 3 {
debug precision => _10;
let _10: usize;
debug precision => _8;
let _8: usize;
scope 5 (inlined Formatter::<'_>::precision) {
debug self => _1;
}
}
}
}
scope 4 (inlined Formatter::<'_>::sign_plus) {
debug self => _1;
let mut _20: u32;
let mut _21: u32;
}
bb0: {
StorageLive(_4);
StorageLive(_20);
StorageLive(_21);
_21 = ((*_1).0: u32);
_20 = BitAnd(move _21, const 1_u32);
StorageDead(_21);
_4 = Ne(move _20, const 0_u32);
StorageDead(_20);
StorageLive(_5);
_5 = &(*_1);
_4 = Formatter::<'_>::sign_plus(move _5) -> [return: bb1, unwind unreachable];
switchInt(_4) -> [0: bb2, otherwise: bb1];
}
bb1: {
StorageDead(_5);
StorageLive(_6);
switchInt(_4) -> [0: bb3, otherwise: bb2];
- _5 = MinusPlus;
+ _5 = const MinusPlus;
goto -> bb3;
}
bb2: {
- _6 = MinusPlus;
+ _6 = const MinusPlus;
goto -> bb4;
- _5 = Minus;
+ _5 = const Minus;
goto -> bb3;
}
bb3: {
- _6 = Minus;
+ _6 = const Minus;
goto -> bb4;
StorageLive(_6);
_6 = ((*_1).4: std::option::Option<usize>);
_7 = discriminant(_6);
switchInt(move _7) -> [1: bb4, otherwise: bb6];
}
bb4: {
StorageLive(_7);
StorageLive(_8);
_8 = &(*_1);
_7 = Formatter::<'_>::precision(move _8) -> [return: bb5, unwind unreachable];
_8 = ((_6 as Some).0: usize);
StorageLive(_11);
_11 = _5;
StorageLive(_12);
StorageLive(_13);
_13 = _8 as u32 (IntToInt);
_12 = Add(move _13, const 1_u32);
StorageDead(_13);
_0 = float_to_exponential_common_exact::<T>(_1, _2, move _11, move _12, _3) -> [return: bb5, unwind unreachable];
}
bb5: {
StorageDead(_8);
_9 = discriminant(_7);
switchInt(move _9) -> [1: bb6, otherwise: bb8];
StorageDead(_12);
StorageDead(_11);
goto -> bb8;
}
bb6: {
_10 = ((_7 as Some).0: usize);
StorageLive(_13);
_13 = _6;
StorageLive(_14);
StorageLive(_15);
_15 = _10 as u32 (IntToInt);
_14 = Add(move _15, const 1_u32);
StorageDead(_15);
_0 = float_to_exponential_common_exact::<T>(_1, _2, move _13, move _14, _3) -> [return: bb7, unwind unreachable];
StorageLive(_18);
_18 = _5;
_0 = float_to_exponential_common_shortest::<T>(_1, _2, move _18, _3) -> [return: bb7, unwind unreachable];
}
bb7: {
StorageDead(_14);
StorageDead(_13);
goto -> bb10;
StorageDead(_18);
goto -> bb8;
}
bb8: {
StorageLive(_20);
_20 = _6;
_0 = float_to_exponential_common_shortest::<T>(_1, _2, move _20, _3) -> [return: bb9, unwind unreachable];
}
bb9: {
StorageDead(_20);
goto -> bb10;
}
bb10: {
StorageDead(_6);
StorageDead(_5);
StorageDead(_4);
StorageDead(_7);
StorageDead(_6);
return;
}
}

View File

@ -7,104 +7,104 @@
debug upper => _3;
let mut _0: std::result::Result<(), std::fmt::Error>;
let _4: bool;
let mut _5: &std::fmt::Formatter<'_>;
let mut _7: std::option::Option<usize>;
let mut _8: &std::fmt::Formatter<'_>;
let mut _9: isize;
let mut _11: &mut std::fmt::Formatter<'_>;
let mut _12: &T;
let mut _13: core::num::flt2dec::Sign;
let mut _14: u32;
let mut _15: u32;
let mut _16: usize;
let mut _17: bool;
let mut _18: &mut std::fmt::Formatter<'_>;
let mut _19: &T;
let mut _20: core::num::flt2dec::Sign;
let mut _21: bool;
let mut _6: std::option::Option<usize>;
let mut _7: isize;
let mut _9: &mut std::fmt::Formatter<'_>;
let mut _10: &T;
let mut _11: core::num::flt2dec::Sign;
let mut _12: u32;
let mut _13: u32;
let mut _14: usize;
let mut _15: bool;
let mut _16: &mut std::fmt::Formatter<'_>;
let mut _17: &T;
let mut _18: core::num::flt2dec::Sign;
let mut _19: bool;
scope 1 {
debug force_sign => _4;
let _6: core::num::flt2dec::Sign;
let _5: core::num::flt2dec::Sign;
scope 2 {
debug sign => _6;
debug sign => _5;
scope 3 {
debug precision => _10;
let _10: usize;
debug precision => _8;
let _8: usize;
scope 5 (inlined Formatter::<'_>::precision) {
debug self => _1;
}
}
}
}
scope 4 (inlined Formatter::<'_>::sign_plus) {
debug self => _1;
let mut _20: u32;
let mut _21: u32;
}
bb0: {
StorageLive(_4);
StorageLive(_20);
StorageLive(_21);
_21 = ((*_1).0: u32);
_20 = BitAnd(move _21, const 1_u32);
StorageDead(_21);
_4 = Ne(move _20, const 0_u32);
StorageDead(_20);
StorageLive(_5);
_5 = &(*_1);
_4 = Formatter::<'_>::sign_plus(move _5) -> [return: bb1, unwind continue];
switchInt(_4) -> [0: bb2, otherwise: bb1];
}
bb1: {
StorageDead(_5);
StorageLive(_6);
switchInt(_4) -> [0: bb3, otherwise: bb2];
- _5 = MinusPlus;
+ _5 = const MinusPlus;
goto -> bb3;
}
bb2: {
- _6 = MinusPlus;
+ _6 = const MinusPlus;
goto -> bb4;
- _5 = Minus;
+ _5 = const Minus;
goto -> bb3;
}
bb3: {
- _6 = Minus;
+ _6 = const Minus;
goto -> bb4;
StorageLive(_6);
_6 = ((*_1).4: std::option::Option<usize>);
_7 = discriminant(_6);
switchInt(move _7) -> [1: bb4, otherwise: bb6];
}
bb4: {
StorageLive(_7);
StorageLive(_8);
_8 = &(*_1);
_7 = Formatter::<'_>::precision(move _8) -> [return: bb5, unwind continue];
_8 = ((_6 as Some).0: usize);
StorageLive(_11);
_11 = _5;
StorageLive(_12);
StorageLive(_13);
_13 = _8 as u32 (IntToInt);
_12 = Add(move _13, const 1_u32);
StorageDead(_13);
_0 = float_to_exponential_common_exact::<T>(_1, _2, move _11, move _12, _3) -> [return: bb5, unwind continue];
}
bb5: {
StorageDead(_8);
_9 = discriminant(_7);
switchInt(move _9) -> [1: bb6, otherwise: bb8];
StorageDead(_12);
StorageDead(_11);
goto -> bb8;
}
bb6: {
_10 = ((_7 as Some).0: usize);
StorageLive(_13);
_13 = _6;
StorageLive(_14);
StorageLive(_15);
_15 = _10 as u32 (IntToInt);
_14 = Add(move _15, const 1_u32);
StorageDead(_15);
_0 = float_to_exponential_common_exact::<T>(_1, _2, move _13, move _14, _3) -> [return: bb7, unwind continue];
StorageLive(_18);
_18 = _5;
_0 = float_to_exponential_common_shortest::<T>(_1, _2, move _18, _3) -> [return: bb7, unwind continue];
}
bb7: {
StorageDead(_14);
StorageDead(_13);
goto -> bb10;
StorageDead(_18);
goto -> bb8;
}
bb8: {
StorageLive(_20);
_20 = _6;
_0 = float_to_exponential_common_shortest::<T>(_1, _2, move _20, _3) -> [return: bb9, unwind continue];
}
bb9: {
StorageDead(_20);
goto -> bb10;
}
bb10: {
StorageDead(_6);
StorageDead(_5);
StorageDead(_4);
StorageDead(_7);
StorageDead(_6);
return;
}
}

View File

@ -3,6 +3,7 @@ pub fn outer() -> usize {
inner()
}
#[inline(never)]
fn index() -> usize {
loop {}
}

View File

@ -8,31 +8,37 @@
let mut _3: &fn() {foo};
let _4: fn() {foo};
let mut _5: ();
+ scope 1 (inlined hide_foo) {
+ }
bb0: {
StorageLive(_2);
StorageLive(_3);
StorageLive(_4);
_4 = hide_foo() -> [return: bb1, unwind unreachable];
}
bb1: {
- _4 = hide_foo() -> [return: bb1, unwind unreachable];
- }
-
- bb1: {
_3 = &_4;
StorageLive(_5);
_5 = ();
_2 = <fn() {foo} as Fn<()>>::call(move _3, move _5) -> [return: bb2, unwind unreachable];
- _2 = <fn() {foo} as Fn<()>>::call(move _3, move _5) -> [return: bb2, unwind unreachable];
+ _2 = <fn() {foo} as Fn<()>>::call(move _3, move _5) -> [return: bb1, unwind unreachable];
}
bb2: {
- bb2: {
+ bb1: {
StorageDead(_5);
StorageDead(_3);
StorageDead(_4);
StorageDead(_2);
_0 = const ();
drop(_1) -> [return: bb3, unwind unreachable];
- drop(_1) -> [return: bb3, unwind unreachable];
+ drop(_1) -> [return: bb2, unwind unreachable];
}
bb3: {
- bb3: {
+ bb2: {
return;
}
}

View File

@ -8,39 +8,48 @@
let mut _3: &fn() {foo};
let _4: fn() {foo};
let mut _5: ();
+ scope 1 (inlined hide_foo) {
+ }
bb0: {
StorageLive(_2);
StorageLive(_3);
StorageLive(_4);
_4 = hide_foo() -> [return: bb1, unwind: bb4];
}
bb1: {
- _4 = hide_foo() -> [return: bb1, unwind: bb4];
- }
-
- bb1: {
_3 = &_4;
StorageLive(_5);
_5 = ();
_2 = <fn() {foo} as Fn<()>>::call(move _3, move _5) -> [return: bb2, unwind: bb4];
- _2 = <fn() {foo} as Fn<()>>::call(move _3, move _5) -> [return: bb2, unwind: bb4];
+ _2 = <fn() {foo} as Fn<()>>::call(move _3, move _5) -> [return: bb1, unwind: bb3];
}
bb2: {
- bb2: {
+ bb1: {
StorageDead(_5);
StorageDead(_3);
StorageDead(_4);
StorageDead(_2);
_0 = const ();
drop(_1) -> [return: bb3, unwind: bb5];
- drop(_1) -> [return: bb3, unwind: bb5];
+ drop(_1) -> [return: bb2, unwind: bb4];
}
bb3: {
- bb3: {
+ bb2: {
return;
}
bb4 (cleanup): {
drop(_1) -> [return: bb5, unwind terminate(cleanup)];
- bb4 (cleanup): {
- drop(_1) -> [return: bb5, unwind terminate(cleanup)];
+ bb3 (cleanup): {
+ drop(_1) -> [return: bb4, unwind terminate(cleanup)];
}
bb5 (cleanup): {
- bb5 (cleanup): {
+ bb4 (cleanup): {
resume;
}
}

View File

@ -4,17 +4,14 @@ fn outer(_1: u8) -> u8 {
debug v => _1; // in scope 0 at $DIR/spans.rs:9:14: 9:15
let mut _0: u8; // return place in scope 0 at $DIR/spans.rs:9:24: 9:26
let mut _2: &u8; // in scope 0 at $DIR/spans.rs:10:11: 10:13
scope 1 (inlined inner) { // at $DIR/spans.rs:10:5: 10:14
debug x => _2; // in scope 1 at $DIR/spans.rs:13:14: 13:15
}
bb0: {
StorageLive(_2); // scope 0 at $DIR/spans.rs:10:11: 10:13
_2 = &_1; // scope 0 at $DIR/spans.rs:10:11: 10:13
_0 = inner(move _2) -> [return: bb1, unwind unreachable]; // scope 0 at $DIR/spans.rs:10:5: 10:14
// mir::ConstOperand
// + span: $DIR/spans.rs:10:5: 10:10
// + const_: Const { ty: for<'a> fn(&'a u8) -> u8 {inner}, val: Value(inner) }
}
bb1: {
_0 = _1; // scope 1 at $DIR/spans.rs:14:5: 14:7
StorageDead(_2); // scope 0 at $DIR/spans.rs:10:13: 10:14
return; // scope 0 at $DIR/spans.rs:11:2: 11:2
}

View File

@ -4,17 +4,14 @@ fn outer(_1: u8) -> u8 {
debug v => _1; // in scope 0 at $DIR/spans.rs:9:14: 9:15
let mut _0: u8; // return place in scope 0 at $DIR/spans.rs:9:24: 9:26
let mut _2: &u8; // in scope 0 at $DIR/spans.rs:10:11: 10:13
scope 1 (inlined inner) { // at $DIR/spans.rs:10:5: 10:14
debug x => _2; // in scope 1 at $DIR/spans.rs:13:14: 13:15
}
bb0: {
StorageLive(_2); // scope 0 at $DIR/spans.rs:10:11: 10:13
_2 = &_1; // scope 0 at $DIR/spans.rs:10:11: 10:13
_0 = inner(move _2) -> [return: bb1, unwind continue]; // scope 0 at $DIR/spans.rs:10:5: 10:14
// mir::ConstOperand
// + span: $DIR/spans.rs:10:5: 10:10
// + const_: Const { ty: for<'a> fn(&'a u8) -> u8 {inner}, val: Value(inner) }
}
bb1: {
_0 = _1; // scope 1 at $DIR/spans.rs:14:5: 14:7
StorageDead(_2); // scope 0 at $DIR/spans.rs:10:13: 10:14
return; // scope 0 at $DIR/spans.rs:11:2: 11:2
}

View File

@ -3,44 +3,26 @@
fn while_loop(_1: bool) -> () {
debug c => _1;
let mut _0: ();
let mut _2: bool;
let mut _3: bool;
scope 1 (inlined get_bool) {
debug c => _1;
}
scope 2 (inlined get_bool) {
debug c => _1;
}
bb0: {
goto -> bb1;
}
bb1: {
StorageLive(_2);
_2 = get_bool(_1) -> [return: bb2, unwind unreachable];
switchInt(_1) -> [0: bb3, otherwise: bb2];
}
bb2: {
switchInt(move _2) -> [0: bb7, otherwise: bb3];
switchInt(_1) -> [0: bb1, otherwise: bb3];
}
bb3: {
StorageLive(_3);
_3 = get_bool(_1) -> [return: bb4, unwind unreachable];
}
bb4: {
switchInt(move _3) -> [0: bb5, otherwise: bb6];
}
bb5: {
StorageDead(_3);
StorageDead(_2);
goto -> bb1;
}
bb6: {
StorageDead(_3);
goto -> bb7;
}
bb7: {
StorageDead(_2);
return;
}
}

View File

@ -3,44 +3,26 @@
fn while_loop(_1: bool) -> () {
debug c => _1;
let mut _0: ();
let mut _2: bool;
let mut _3: bool;
scope 1 (inlined get_bool) {
debug c => _1;
}
scope 2 (inlined get_bool) {
debug c => _1;
}
bb0: {
goto -> bb1;
}
bb1: {
StorageLive(_2);
_2 = get_bool(_1) -> [return: bb2, unwind continue];
switchInt(_1) -> [0: bb3, otherwise: bb2];
}
bb2: {
switchInt(move _2) -> [0: bb7, otherwise: bb3];
switchInt(_1) -> [0: bb1, otherwise: bb3];
}
bb3: {
StorageLive(_3);
_3 = get_bool(_1) -> [return: bb4, unwind continue];
}
bb4: {
switchInt(move _3) -> [0: bb5, otherwise: bb6];
}
bb5: {
StorageDead(_3);
StorageDead(_2);
goto -> bb1;
}
bb6: {
StorageDead(_3);
goto -> bb7;
}
bb7: {
StorageDead(_2);
return;
}
}

View File

@ -1,3 +1,4 @@
#![crate_type = "lib"]
#[inline(never)]
pub fn foo() {}

View File

@ -2,6 +2,7 @@
use std::arch::asm;
#[deny(unreachable_code)]
#[inline(never)]
pub fn exit(n: usize) -> i32 {
unsafe {
// Pretend this asm is an exit() syscall.

View File

@ -5,6 +5,7 @@ use std::arch::asm;
use std::intrinsics;
#[allow(unreachable_code)]
#[inline(never)]
pub fn exit(n: usize) -> i32 {
unsafe {
// Pretend this asm is an exit() syscall.

View File

@ -1,3 +1,3 @@
thread 'main' panicked at library/alloc/src/raw_vec.rs:534:5:
thread 'main' panicked at library/alloc/src/raw_vec.rs:535:5:
capacity overflow
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace