Skip to content

Commit 8f5c3f1

Browse files
committed
Auto merge of #32557 - dotdash:issue-32518, r=nikomatsakis
Use weak_odr linkage when reusing definitions across codegen units When reuing a definition across codegen units, we obviously cannot use internal linkage, but using external linkage means that we can end up with multiple conflicting definitions of a single symbol across multiple crates. Since the definitions should all be equal semantically, we can use weak_odr linkage to resolve the situation. Fixes #32518 r? @nikomatsakis
2 parents 0c07a3c + 22f4587 commit 8f5c3f1

File tree

8 files changed

+152
-17
lines changed

8 files changed

+152
-17
lines changed

src/librustc_llvm/lib.rs

+21
Original file line numberDiff line numberDiff line change
@@ -2125,6 +2125,9 @@ extern {
21252125
pub fn LLVMRustFreeOperandBundleDef(Bundle: OperandBundleDefRef);
21262126

21272127
pub fn LLVMRustPositionBuilderAtStart(B: BuilderRef, BB: BasicBlockRef);
2128+
2129+
pub fn LLVMRustSetComdat(M: ModuleRef, V: ValueRef, Name: *const c_char);
2130+
pub fn LLVMRustUnsetComdat(V: ValueRef);
21282131
}
21292132

21302133
// LLVM requires symbols from this library, but apparently they're not printed
@@ -2149,6 +2152,24 @@ pub fn SetLinkage(global: ValueRef, link: Linkage) {
21492152
}
21502153
}
21512154

2155+
// Externally visible symbols that might appear in multiple translation units need to appear in
2156+
// their own comdat section so that the duplicates can be discarded at link time. This can for
2157+
// example happen for generics when using multiple codegen units. This function simply uses the
2158+
// value's name as the comdat value to make sure that it is in a 1-to-1 relationship to the
2159+
// function.
2160+
// For more details on COMDAT sections see e.g. http://www.airs.com/blog/archives/52
2161+
pub fn SetUniqueComdat(llmod: ModuleRef, val: ValueRef) {
2162+
unsafe {
2163+
LLVMRustSetComdat(llmod, val, LLVMGetValueName(val));
2164+
}
2165+
}
2166+
2167+
pub fn UnsetComdat(val: ValueRef) {
2168+
unsafe {
2169+
LLVMRustUnsetComdat(val);
2170+
}
2171+
}
2172+
21522173
pub fn SetDLLStorageClass(global: ValueRef, class: DLLStorageClassTypes) {
21532174
unsafe {
21542175
LLVMRustSetDLLStorageClass(global, class);

src/librustc_trans/base.rs

+24-12
Original file line numberDiff line numberDiff line change
@@ -2215,18 +2215,27 @@ pub fn update_linkage(ccx: &CrateContext,
22152215
}
22162216
}
22172217

2218-
match id {
2219-
Some(id) if ccx.reachable().contains(&id) => {
2218+
let (is_reachable, is_generic) = if let Some(id) = id {
2219+
(ccx.reachable().contains(&id), false)
2220+
} else {
2221+
(false, true)
2222+
};
2223+
2224+
// We need external linkage for items reachable from other translation units, this include
2225+
// other codegen units in case of parallel compilations.
2226+
if is_reachable || ccx.sess().opts.cg.codegen_units > 1 {
2227+
if is_generic {
2228+
// This only happens with multiple codegen units, in which case we need to use weak_odr
2229+
// linkage because other crates might expose the same symbol. We cannot use
2230+
// linkonce_odr here because the symbol might then get dropped before the other codegen
2231+
// units get to link it.
2232+
llvm::SetUniqueComdat(ccx.llmod(), llval);
2233+
llvm::SetLinkage(llval, llvm::WeakODRLinkage);
2234+
} else {
22202235
llvm::SetLinkage(llval, llvm::ExternalLinkage);
2221-
},
2222-
_ => {
2223-
// `id` does not refer to an item in `ccx.reachable`.
2224-
if ccx.sess().opts.cg.codegen_units > 1 {
2225-
llvm::SetLinkage(llval, llvm::ExternalLinkage);
2226-
} else {
2227-
llvm::SetLinkage(llval, llvm::InternalLinkage);
2228-
}
2229-
},
2236+
}
2237+
} else {
2238+
llvm::SetLinkage(llval, llvm::InternalLinkage);
22302239
}
22312240
}
22322241

@@ -2547,8 +2556,10 @@ fn internalize_symbols(cx: &SharedCrateContext, reachable: &HashSet<&str>) {
25472556
// then give it internal linkage.
25482557
for ccx in cx.iter() {
25492558
for val in iter_globals(ccx.llmod()).chain(iter_functions(ccx.llmod())) {
2559+
let linkage = llvm::LLVMGetLinkage(val);
25502560
// We only care about external definitions.
2551-
if !(llvm::LLVMGetLinkage(val) == llvm::ExternalLinkage as c_uint &&
2561+
if !((linkage == llvm::ExternalLinkage as c_uint ||
2562+
linkage == llvm::WeakODRLinkage as c_uint) &&
25522563
llvm::LLVMIsDeclaration(val) == 0) {
25532564
continue;
25542565
}
@@ -2560,6 +2571,7 @@ fn internalize_symbols(cx: &SharedCrateContext, reachable: &HashSet<&str>) {
25602571
!reachable.contains(str::from_utf8(&name).unwrap()) {
25612572
llvm::SetLinkage(val, llvm::InternalLinkage);
25622573
llvm::SetDLLStorageClass(val, llvm::DefaultStorageClass);
2574+
llvm::UnsetComdat(val);
25632575
}
25642576
}
25652577
}

src/librustc_trans/monomorphize.rs

+6-5
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,6 @@ pub fn monomorphic_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
123123
ref attrs, node: hir::ImplItemKind::Method(
124124
hir::MethodSig { ref decl, .. }, ref body), ..
125125
}) => {
126-
base::update_linkage(ccx, lldecl, None, base::OriginalTranslation);
127126
attributes::from_fn_attrs(ccx, attrs, lldecl);
128127

129128
let is_first = !ccx.available_monomorphizations().borrow()
@@ -133,12 +132,14 @@ pub fn monomorphic_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
133132
}
134133

135134
let trans_everywhere = attr::requests_inline(attrs);
136-
if trans_everywhere && !is_first {
137-
llvm::SetLinkage(lldecl, llvm::AvailableExternallyLinkage);
138-
}
139-
140135
if trans_everywhere || is_first {
136+
let origin = if is_first { base::OriginalTranslation } else { base::InlinedCopy };
137+
base::update_linkage(ccx, lldecl, None, origin);
141138
trans_fn(ccx, decl, body, lldecl, psubsts, fn_node_id);
139+
} else {
140+
// We marked the value as using internal linkage earlier, but that is illegal for
141+
// declarations, so switch back to external linkage.
142+
llvm::SetLinkage(lldecl, llvm::ExternalLinkage);
142143
}
143144
}
144145

src/rustllvm/RustWrapper.cpp

+13
Original file line numberDiff line numberDiff line change
@@ -1189,3 +1189,16 @@ extern "C" void LLVMRustPositionBuilderAtStart(LLVMBuilderRef B, LLVMBasicBlockR
11891189
auto point = unwrap(BB)->getFirstInsertionPt();
11901190
unwrap(B)->SetInsertPoint(unwrap(BB), point);
11911191
}
1192+
1193+
extern "C" void LLVMRustSetComdat(LLVMModuleRef M, LLVMValueRef V, const char *Name) {
1194+
Triple TargetTriple(unwrap(M)->getTargetTriple());
1195+
GlobalObject *GV = unwrap<GlobalObject>(V);
1196+
if (!TargetTriple.isOSBinFormatMachO()) {
1197+
GV->setComdat(unwrap(M)->getOrInsertComdat(Name));
1198+
}
1199+
}
1200+
1201+
extern "C" void LLVMRustUnsetComdat(LLVMValueRef V) {
1202+
GlobalObject *GV = unwrap<GlobalObject>(V);
1203+
GV->setComdat(nullptr);
1204+
}

src/test/auxiliary/cgu_test.rs

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// no-prefer-dynamic
12+
// compile-flags: --crate-type=lib
13+
14+
pub fn id<T>(t: T) -> T {
15+
t
16+
}

src/test/auxiliary/cgu_test_a.rs

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// no-prefer-dynamic
12+
// compile-flags: -Ccodegen-units=2 --crate-type=lib
13+
14+
extern crate cgu_test;
15+
16+
pub mod a {
17+
pub fn a() {
18+
::cgu_test::id(0);
19+
}
20+
}
21+
pub mod b {
22+
pub fn a() {
23+
::cgu_test::id(0);
24+
}
25+
}

src/test/auxiliary/cgu_test_b.rs

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// no-prefer-dynamic
12+
// compile-flags: -Ccodegen-units=2 --crate-type=lib
13+
14+
extern crate cgu_test;
15+
16+
pub mod a {
17+
pub fn a() {
18+
::cgu_test::id(0);
19+
}
20+
}
21+
pub mod b {
22+
pub fn a() {
23+
::cgu_test::id(0);
24+
}
25+
}

src/test/run-pass/issue-32518.rs

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// no-prefer-dynamic
12+
// aux-build:cgu_test.rs
13+
// aux-build:cgu_test_a.rs
14+
// aux-build:cgu_test_b.rs
15+
16+
extern crate cgu_test_a;
17+
extern crate cgu_test_b;
18+
19+
fn main() {
20+
cgu_test_a::a::a();
21+
cgu_test_b::a::a();
22+
}

0 commit comments

Comments
 (0)