diff --git a/src/librustc_trans/trans_item.rs b/src/librustc_trans/trans_item.rs index e40b1617d0a01..060f02ee23e0a 100644 --- a/src/librustc_trans/trans_item.rs +++ b/src/librustc_trans/trans_item.rs @@ -31,7 +31,7 @@ use rustc::traits; use rustc::ty::{self, Ty, TyCtxt, TypeFoldable}; use rustc::ty::subst::{Subst, Substs}; use syntax::ast; -use syntax::attr; +use syntax::attr::{self, InlineAttr}; use syntax_pos::Span; use syntax_pos::symbol::Symbol; use type_of; @@ -175,16 +175,32 @@ pub trait TransItemExt<'a, 'tcx>: fmt::Debug { match *self.as_trans_item() { TransItem::Fn(ref instance) => { - if self.explicit_linkage(tcx).is_none() && - common::requests_inline(tcx, instance) + // If this function isn't inlined or otherwise has explicit + // linkage, then we'll be creating a globally shared version. + if self.explicit_linkage(tcx).is_some() || + !common::requests_inline(tcx, instance) { - if inline_in_all_cgus { - InstantiationMode::LocalCopy - } else { + return InstantiationMode::GloballyShared { may_conflict: false } + } + + // At this point we don't have explicit linkage and we're an + // inlined function. If we're inlining into all CGUs then we'll + // be creating a local copy per CGU + if inline_in_all_cgus { + return InstantiationMode::LocalCopy + } + + // Finally, if this is `#[inline(always)]` we're sure to respect + // that with an inline copy per CGU, but otherwise we'll be + // creating one copy of this `#[inline]` function which may + // conflict with upstream crates as it could be an exported + // symbol. + let attrs = instance.def.attrs(tcx); + match attr::find_inline_attr(Some(tcx.sess.diagnostic()), &attrs) { + InlineAttr::Always => InstantiationMode::LocalCopy, + _ => { InstantiationMode::GloballyShared { may_conflict: true } } - } else { - InstantiationMode::GloballyShared { may_conflict: false } } } TransItem::Static(..) => { diff --git a/src/test/codegen-units/partitioning/local-inlining-but-not-all.rs b/src/test/codegen-units/partitioning/local-inlining-but-not-all.rs index ccc8f03a40ffe..84464a627beb1 100644 --- a/src/test/codegen-units/partitioning/local-inlining-but-not-all.rs +++ b/src/test/codegen-units/partitioning/local-inlining-but-not-all.rs @@ -20,7 +20,7 @@ mod inline { //~ TRANS_ITEM fn local_inlining_but_not_all::inline[0]::inlined_function[0] @@ local_inlining_but_not_all-inline[External] - #[inline(always)] + #[inline] pub fn inlined_function() { diff --git a/src/test/run-make/inline-always-many-cgu/Makefile b/src/test/run-make/inline-always-many-cgu/Makefile new file mode 100644 index 0000000000000..edf88a6327cd2 --- /dev/null +++ b/src/test/run-make/inline-always-many-cgu/Makefile @@ -0,0 +1,8 @@ +-include ../tools.mk + +all: + $(RUSTC) foo.rs --emit llvm-ir -C codegen-units=2 + if grep -w call $(TMPDIR)/*.ll; then \ + echo "found call instruction when one wasn't expected"; \ + exit 1; \ + fi diff --git a/src/test/run-make/inline-always-many-cgu/foo.rs b/src/test/run-make/inline-always-many-cgu/foo.rs new file mode 100644 index 0000000000000..539dcdfa9b30b --- /dev/null +++ b/src/test/run-make/inline-always-many-cgu/foo.rs @@ -0,0 +1,25 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![crate_type = "lib"] + +pub mod a { + #[inline(always)] + pub fn foo() { + } + + pub fn bar() { + } +} + +#[no_mangle] +pub fn bar() { + a::foo(); +}