Skip to content

Commit 73f8adc

Browse files
committed
make separate compilation respect #[inline] attributes
Adjust the handling of `#[inline]` items so that they get translated into every compilation unit that uses them. This is necessary to preserve the semantics of `#[inline(always)]`. Crate-local `#[inline]` functions and statics are blindly translated into every compilation unit. Cross-crate inlined items and monomorphizations of `#[inline]` functions are translated the first time a reference is seen in each compilation unit. When using multiple compilation units, inlined items are given `available_externally` linkage whenever possible to avoid duplicating object code.
1 parent edc5cdc commit 73f8adc

File tree

6 files changed

+190
-58
lines changed

6 files changed

+190
-58
lines changed

src/librustc/middle/trans/base.rs

Lines changed: 74 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -2125,12 +2125,43 @@ impl<'a> Visitor<()> for TransItemVisitor<'a> {
21252125
}
21262126
}
21272127

2128+
/// Enum describing the origin of an LLVM `Value`, for linkage purposes.
2129+
pub enum ValueOrigin {
2130+
/// The LLVM `Value` is in this context because the corresponding item was
2131+
/// assigned to the current compilation unit.
2132+
OriginalTranslation,
2133+
/// The `Value`'s corresponding item was assigned to some other compilation
2134+
/// unit, but the `Value` was translated in this context anyway because the
2135+
/// item is marked `#[inline]`.
2136+
InlinedCopy,
2137+
}
2138+
21282139
/// Set the appropriate linkage for an LLVM `ValueRef` (function or global).
21292140
/// If the `llval` is the direct translation of a specific Rust item, `id`
21302141
/// should be set to the `NodeId` of that item. (This mapping should be
21312142
/// 1-to-1, so monomorphizations and drop/visit glue should have `id` set to
2132-
/// `None`.)
2133-
pub fn update_linkage(ccx: &CrateContext, llval: ValueRef, id: Option<ast::NodeId>) {
2143+
/// `None`.) `llval_origin` indicates whether `llval` is the translation of an
2144+
/// item assigned to `ccx`'s compilation unit or an inlined copy of an item
2145+
/// assigned to a different compilation unit.
2146+
pub fn update_linkage(ccx: &CrateContext,
2147+
llval: ValueRef,
2148+
id: Option<ast::NodeId>,
2149+
llval_origin: ValueOrigin) {
2150+
match llval_origin {
2151+
InlinedCopy => {
2152+
// `llval` is a translation of an item defined in a separate
2153+
// compilation unit. This only makes sense if there are at least
2154+
// two compilation units.
2155+
assert!(ccx.sess().opts.cg.codegen_units > 1);
2156+
// `llval` is a copy of something defined elsewhere, so use
2157+
// `AvailableExternallyLinkage` to avoid duplicating code in the
2158+
// output.
2159+
llvm::SetLinkage(llval, llvm::AvailableExternallyLinkage);
2160+
return;
2161+
},
2162+
OriginalTranslation => {},
2163+
}
2164+
21342165
match id {
21352166
Some(id) if ccx.reachable().contains(&id) => {
21362167
llvm::SetLinkage(llval, llvm::ExternalLinkage);
@@ -2149,29 +2180,41 @@ pub fn update_linkage(ccx: &CrateContext, llval: ValueRef, id: Option<ast::NodeI
21492180
pub fn trans_item(ccx: &CrateContext, item: &ast::Item) {
21502181
let _icx = push_ctxt("trans_item");
21512182

2183+
let from_external = ccx.external_srcs().borrow().contains_key(&item.id);
2184+
21522185
match item.node {
21532186
ast::ItemFn(ref decl, _fn_style, abi, ref generics, ref body) => {
21542187
if !generics.is_type_parameterized() {
2155-
let llfn = get_item_val(ccx, item.id);
2156-
if abi != Rust {
2157-
foreign::trans_rust_fn_with_foreign_abi(ccx,
2158-
&**decl,
2159-
&**body,
2160-
item.attrs.as_slice(),
2161-
llfn,
2162-
&param_substs::empty(),
2163-
item.id,
2164-
None);
2165-
} else {
2166-
trans_fn(ccx,
2167-
&**decl,
2168-
&**body,
2169-
llfn,
2170-
&param_substs::empty(),
2171-
item.id,
2172-
item.attrs.as_slice());
2188+
let trans_everywhere = attr::requests_inline(item.attrs.as_slice());
2189+
// Ignore `trans_everywhere` for cross-crate inlined items
2190+
// (`from_external`). `trans_item` will be called once for each
2191+
// compilation unit that references the item, so it will still get
2192+
// translated everywhere it's needed.
2193+
for (ref ccx, is_origin) in ccx.maybe_iter(!from_external && trans_everywhere) {
2194+
let llfn = get_item_val(ccx, item.id);
2195+
if abi != Rust {
2196+
foreign::trans_rust_fn_with_foreign_abi(ccx,
2197+
&**decl,
2198+
&**body,
2199+
item.attrs.as_slice(),
2200+
llfn,
2201+
&param_substs::empty(),
2202+
item.id,
2203+
None);
2204+
} else {
2205+
trans_fn(ccx,
2206+
&**decl,
2207+
&**body,
2208+
llfn,
2209+
&param_substs::empty(),
2210+
item.id,
2211+
item.attrs.as_slice());
2212+
}
2213+
update_linkage(ccx,
2214+
llfn,
2215+
Some(item.id),
2216+
if is_origin { OriginalTranslation } else { InlinedCopy });
21732217
}
2174-
update_linkage(ccx, llfn, Some(item.id));
21752218
}
21762219

21772220
// Be sure to travel more than just one layer deep to catch nested
@@ -2196,10 +2239,17 @@ pub fn trans_item(ccx: &CrateContext, item: &ast::Item) {
21962239
// Recurse on the expression to catch items in blocks
21972240
let mut v = TransItemVisitor{ ccx: ccx };
21982241
v.visit_expr(&**expr, ());
2199-
consts::trans_const(ccx, m, item.id);
22002242

2201-
let g = get_item_val(ccx, item.id);
2202-
update_linkage(ccx, g, Some(item.id));
2243+
let trans_everywhere = attr::requests_inline(item.attrs.as_slice());
2244+
for (ref ccx, is_origin) in ccx.maybe_iter(!from_external && trans_everywhere) {
2245+
consts::trans_const(ccx, m, item.id);
2246+
2247+
let g = get_item_val(ccx, item.id);
2248+
update_linkage(ccx,
2249+
g,
2250+
Some(item.id),
2251+
if is_origin { OriginalTranslation } else { InlinedCopy });
2252+
}
22032253

22042254
// Do static_assert checking. It can't really be done much earlier
22052255
// because we need to get the value of the bool out of LLVM

src/librustc/middle/trans/context.rs

Lines changed: 56 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,9 @@ pub struct LocalCrateContext {
155155
pub struct CrateContext<'a> {
156156
shared: &'a SharedCrateContext,
157157
local: &'a LocalCrateContext,
158+
/// The index of `local` in `shared.local_ccxs`. This is used in
159+
/// `maybe_iter(true)` to identify the original `LocalCrateContext`.
160+
index: uint,
158161
}
159162

160163
pub struct CrateContextIterator<'a> {
@@ -174,10 +177,41 @@ impl<'a> Iterator<CrateContext<'a>> for CrateContextIterator<'a> {
174177
Some(CrateContext {
175178
shared: self.shared,
176179
local: &self.shared.local_ccxs[index],
180+
index: index,
177181
})
178182
}
179183
}
180184

185+
/// The iterator produced by `CrateContext::maybe_iter`.
186+
pub struct CrateContextMaybeIterator<'a> {
187+
shared: &'a SharedCrateContext,
188+
index: uint,
189+
single: bool,
190+
origin: uint,
191+
}
192+
193+
impl<'a> Iterator<(CrateContext<'a>, bool)> for CrateContextMaybeIterator<'a> {
194+
fn next(&mut self) -> Option<(CrateContext<'a>, bool)> {
195+
if self.index >= self.shared.local_ccxs.len() {
196+
return None;
197+
}
198+
199+
let index = self.index;
200+
self.index += 1;
201+
if self.single {
202+
self.index = self.shared.local_ccxs.len();
203+
}
204+
205+
let ccx = CrateContext {
206+
shared: self.shared,
207+
local: &self.shared.local_ccxs[index],
208+
index: index,
209+
};
210+
Some((ccx, index == self.origin))
211+
}
212+
}
213+
214+
181215
unsafe fn create_context_and_module(sess: &Session, mod_name: &str) -> (ContextRef, ModuleRef) {
182216
let llcx = llvm::LLVMContextCreate();
183217
let llmod = mod_name.with_c_str(|buf| {
@@ -270,18 +304,21 @@ impl SharedCrateContext {
270304
CrateContext {
271305
shared: self,
272306
local: &self.local_ccxs[index],
307+
index: index,
273308
}
274309
}
275310

276311
fn get_smallest_ccx<'a>(&'a self) -> CrateContext<'a> {
277-
let local_ccx =
312+
let (local_ccx, index) =
278313
self.local_ccxs
279314
.iter()
280-
.min_by(|&local_ccx| local_ccx.n_llvm_insns.get())
315+
.zip(range(0, self.local_ccxs.len()))
316+
.min_by(|&(local_ccx, _idx)| local_ccx.n_llvm_insns.get())
281317
.unwrap();
282318
CrateContext {
283319
shared: self,
284320
local: local_ccx,
321+
index: index,
285322
}
286323
}
287324

@@ -426,6 +463,7 @@ impl LocalCrateContext {
426463
CrateContext {
427464
shared: shared,
428465
local: self,
466+
index: -1 as uint,
429467
}
430468
}
431469
}
@@ -446,6 +484,22 @@ impl<'b> CrateContext<'b> {
446484
self.shared.get_smallest_ccx()
447485
}
448486

487+
/// Either iterate over only `self`, or iterate over all `CrateContext`s in
488+
/// the `SharedCrateContext`. The iterator produces `(ccx, is_origin)`
489+
/// pairs, where `is_origin` is `true` if `ccx` is `self` and `false`
490+
/// otherwise. This method is useful for avoiding code duplication in
491+
/// cases where it may or may not be necessary to translate code into every
492+
/// context.
493+
pub fn maybe_iter(&self, iter_all: bool) -> CrateContextMaybeIterator<'b> {
494+
CrateContextMaybeIterator {
495+
shared: self.shared,
496+
index: if iter_all { 0 } else { self.index },
497+
single: !iter_all,
498+
origin: self.index,
499+
}
500+
}
501+
502+
449503
pub fn tcx<'a>(&'a self) -> &'a ty::ctxt {
450504
&self.shared.tcx
451505
}

src/librustc/middle/trans/glue.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -669,7 +669,7 @@ fn make_generic_glue(ccx: &CrateContext,
669669

670670
let bcx = init_function(&fcx, false, ty::mk_nil());
671671

672-
update_linkage(ccx, llfn, None);
672+
update_linkage(ccx, llfn, None, OriginalTranslation);
673673

674674
ccx.stats().n_glues_created.set(ccx.stats().n_glues_created.get() + 1u);
675675
// All glue functions take values passed *by alias*; this is a

src/librustc/middle/trans/meth.rs

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ use util::ppaux::Repr;
3838
use std::c_str::ToCStr;
3939
use syntax::abi::{Rust, RustCall};
4040
use syntax::parse::token;
41-
use syntax::{ast, ast_map, visit};
41+
use syntax::{ast, ast_map, attr, visit};
4242
use syntax::ast_util::PostExpansionMethod;
4343

4444
// drop_glue pointer, size, align.
@@ -77,15 +77,21 @@ pub fn trans_impl(ccx: &CrateContext,
7777
match *impl_item {
7878
ast::MethodImplItem(method) => {
7979
if method.pe_generics().ty_params.len() == 0u {
80-
let llfn = get_item_val(ccx, method.id);
81-
trans_fn(ccx,
82-
&*method.pe_fn_decl(),
83-
&*method.pe_body(),
84-
llfn,
85-
&param_substs::empty(),
86-
method.id,
87-
[]);
88-
update_linkage(ccx, llfn, Some(method.id));
80+
let trans_everywhere = attr::requests_inline(method.attrs.as_slice());
81+
for (ref ccx, is_origin) in ccx.maybe_iter(trans_everywhere) {
82+
let llfn = get_item_val(ccx, method.id);
83+
trans_fn(ccx,
84+
&*method.pe_fn_decl(),
85+
&*method.pe_body(),
86+
llfn,
87+
&param_substs::empty(),
88+
method.id,
89+
[]);
90+
update_linkage(ccx,
91+
llfn,
92+
Some(method.id),
93+
if is_origin { OriginalTranslation } else { InlinedCopy });
94+
}
8995
}
9096
let mut v = TransItemVisitor {
9197
ccx: ccx,

src/librustc/middle/trans/monomorphize.rs

Lines changed: 34 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
use back::link::exported_name;
1212
use driver::session;
1313
use llvm::ValueRef;
14+
use llvm;
1415
use middle::subst;
1516
use middle::subst::Subst;
1617
use middle::trans::base::{set_llvm_fn_attrs, set_inline_hint};
@@ -27,6 +28,7 @@ use syntax::abi;
2728
use syntax::ast;
2829
use syntax::ast_map;
2930
use syntax::ast_util::{local_def, PostExpansionMethod};
31+
use syntax::attr;
3032
use std::hash::{sip, Hash};
3133

3234
pub fn monomorphic_fn(ccx: &CrateContext,
@@ -150,6 +152,25 @@ pub fn monomorphic_fn(ccx: &CrateContext,
150152
ccx.monomorphized().borrow_mut().insert(hash_id.take().unwrap(), lldecl);
151153
lldecl
152154
};
155+
let setup_lldecl = |lldecl, attrs: &[ast::Attribute]| {
156+
base::update_linkage(ccx, lldecl, None, base::OriginalTranslation);
157+
set_llvm_fn_attrs(attrs, lldecl);
158+
159+
let is_first = !ccx.available_monomorphizations().borrow().contains(&s);
160+
if is_first {
161+
ccx.available_monomorphizations().borrow_mut().insert(s.clone());
162+
}
163+
164+
let trans_everywhere = attr::requests_inline(attrs);
165+
if trans_everywhere && !is_first {
166+
llvm::SetLinkage(lldecl, llvm::AvailableExternallyLinkage);
167+
}
168+
169+
// If `true`, then `lldecl` should be given a function body.
170+
// Otherwise, it should be left as a declaration of an external
171+
// function, with no definition in the current compilation unit.
172+
trans_everywhere || is_first
173+
};
153174

154175
let lldecl = match map_node {
155176
ast_map::NodeItem(i) => {
@@ -159,11 +180,8 @@ pub fn monomorphic_fn(ccx: &CrateContext,
159180
..
160181
} => {
161182
let d = mk_lldecl(abi);
162-
base::update_linkage(ccx, d, None);
163-
set_llvm_fn_attrs(i.attrs.as_slice(), d);
164-
165-
if !ccx.available_monomorphizations().borrow().contains(&s) {
166-
ccx.available_monomorphizations().borrow_mut().insert(s.clone());
183+
let needs_body = setup_lldecl(d, i.attrs.as_slice());
184+
if needs_body {
167185
if abi != abi::Rust {
168186
foreign::trans_rust_fn_with_foreign_abi(
169187
ccx, &**decl, &**body, [], d, &psubsts, fn_id.node,
@@ -205,17 +223,15 @@ pub fn monomorphic_fn(ccx: &CrateContext,
205223
match *ii {
206224
ast::MethodImplItem(mth) => {
207225
let d = mk_lldecl(abi::Rust);
208-
base::update_linkage(ccx, d, None);
209-
set_llvm_fn_attrs(mth.attrs.as_slice(), d);
210-
if !ccx.available_monomorphizations().borrow().contains(&s) {
211-
ccx.available_monomorphizations().borrow_mut().insert(s.clone());
212-
trans_fn(ccx,
213-
&*mth.pe_fn_decl(),
214-
&*mth.pe_body(),
215-
d,
216-
&psubsts,
217-
mth.id,
218-
[]);
226+
let needs_body = setup_lldecl(d, mth.attrs.as_slice());
227+
if needs_body {
228+
trans_fn(ccx,
229+
&*mth.pe_fn_decl(),
230+
&*mth.pe_body(),
231+
d,
232+
&psubsts,
233+
mth.id,
234+
[]);
219235
}
220236
d
221237
}
@@ -225,10 +241,8 @@ pub fn monomorphic_fn(ccx: &CrateContext,
225241
match *method {
226242
ast::ProvidedMethod(mth) => {
227243
let d = mk_lldecl(abi::Rust);
228-
base::update_linkage(ccx, d, None);
229-
set_llvm_fn_attrs(mth.attrs.as_slice(), d);
230-
if !ccx.available_monomorphizations().borrow().contains(&s) {
231-
ccx.available_monomorphizations().borrow_mut().insert(s.clone());
244+
let needs_body = setup_lldecl(d, mth.attrs.as_slice());
245+
if needs_body {
232246
trans_fn(ccx, &*mth.pe_fn_decl(), &*mth.pe_body(), d,
233247
&psubsts, mth.id, []);
234248
}

0 commit comments

Comments
 (0)