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
This commit is contained in:
commit
8f5c3f1fcf
src
librustc_llvm
librustc_trans
rustllvm
test
@ -2125,6 +2125,9 @@ extern {
|
||||
pub fn LLVMRustFreeOperandBundleDef(Bundle: OperandBundleDefRef);
|
||||
|
||||
pub fn LLVMRustPositionBuilderAtStart(B: BuilderRef, BB: BasicBlockRef);
|
||||
|
||||
pub fn LLVMRustSetComdat(M: ModuleRef, V: ValueRef, Name: *const c_char);
|
||||
pub fn LLVMRustUnsetComdat(V: ValueRef);
|
||||
}
|
||||
|
||||
// LLVM requires symbols from this library, but apparently they're not printed
|
||||
@ -2149,6 +2152,24 @@ pub fn SetLinkage(global: ValueRef, link: Linkage) {
|
||||
}
|
||||
}
|
||||
|
||||
// Externally visible symbols that might appear in multiple translation units need to appear in
|
||||
// their own comdat section so that the duplicates can be discarded at link time. This can for
|
||||
// example happen for generics when using multiple codegen units. This function simply uses the
|
||||
// value's name as the comdat value to make sure that it is in a 1-to-1 relationship to the
|
||||
// function.
|
||||
// For more details on COMDAT sections see e.g. http://www.airs.com/blog/archives/52
|
||||
pub fn SetUniqueComdat(llmod: ModuleRef, val: ValueRef) {
|
||||
unsafe {
|
||||
LLVMRustSetComdat(llmod, val, LLVMGetValueName(val));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn UnsetComdat(val: ValueRef) {
|
||||
unsafe {
|
||||
LLVMRustUnsetComdat(val);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn SetDLLStorageClass(global: ValueRef, class: DLLStorageClassTypes) {
|
||||
unsafe {
|
||||
LLVMRustSetDLLStorageClass(global, class);
|
||||
|
@ -2215,18 +2215,27 @@ pub fn update_linkage(ccx: &CrateContext,
|
||||
}
|
||||
}
|
||||
|
||||
match id {
|
||||
Some(id) if ccx.reachable().contains(&id) => {
|
||||
let (is_reachable, is_generic) = if let Some(id) = id {
|
||||
(ccx.reachable().contains(&id), false)
|
||||
} else {
|
||||
(false, true)
|
||||
};
|
||||
|
||||
// We need external linkage for items reachable from other translation units, this include
|
||||
// other codegen units in case of parallel compilations.
|
||||
if is_reachable || ccx.sess().opts.cg.codegen_units > 1 {
|
||||
if is_generic {
|
||||
// This only happens with multiple codegen units, in which case we need to use weak_odr
|
||||
// linkage because other crates might expose the same symbol. We cannot use
|
||||
// linkonce_odr here because the symbol might then get dropped before the other codegen
|
||||
// units get to link it.
|
||||
llvm::SetUniqueComdat(ccx.llmod(), llval);
|
||||
llvm::SetLinkage(llval, llvm::WeakODRLinkage);
|
||||
} else {
|
||||
llvm::SetLinkage(llval, llvm::ExternalLinkage);
|
||||
},
|
||||
_ => {
|
||||
// `id` does not refer to an item in `ccx.reachable`.
|
||||
if ccx.sess().opts.cg.codegen_units > 1 {
|
||||
llvm::SetLinkage(llval, llvm::ExternalLinkage);
|
||||
} else {
|
||||
llvm::SetLinkage(llval, llvm::InternalLinkage);
|
||||
}
|
||||
},
|
||||
}
|
||||
} else {
|
||||
llvm::SetLinkage(llval, llvm::InternalLinkage);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2547,8 +2556,10 @@ fn internalize_symbols(cx: &SharedCrateContext, reachable: &HashSet<&str>) {
|
||||
// then give it internal linkage.
|
||||
for ccx in cx.iter() {
|
||||
for val in iter_globals(ccx.llmod()).chain(iter_functions(ccx.llmod())) {
|
||||
let linkage = llvm::LLVMGetLinkage(val);
|
||||
// We only care about external definitions.
|
||||
if !(llvm::LLVMGetLinkage(val) == llvm::ExternalLinkage as c_uint &&
|
||||
if !((linkage == llvm::ExternalLinkage as c_uint ||
|
||||
linkage == llvm::WeakODRLinkage as c_uint) &&
|
||||
llvm::LLVMIsDeclaration(val) == 0) {
|
||||
continue;
|
||||
}
|
||||
@ -2560,6 +2571,7 @@ fn internalize_symbols(cx: &SharedCrateContext, reachable: &HashSet<&str>) {
|
||||
!reachable.contains(str::from_utf8(&name).unwrap()) {
|
||||
llvm::SetLinkage(val, llvm::InternalLinkage);
|
||||
llvm::SetDLLStorageClass(val, llvm::DefaultStorageClass);
|
||||
llvm::UnsetComdat(val);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -123,7 +123,6 @@ pub fn monomorphic_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
|
||||
ref attrs, node: hir::ImplItemKind::Method(
|
||||
hir::MethodSig { ref decl, .. }, ref body), ..
|
||||
}) => {
|
||||
base::update_linkage(ccx, lldecl, None, base::OriginalTranslation);
|
||||
attributes::from_fn_attrs(ccx, attrs, lldecl);
|
||||
|
||||
let is_first = !ccx.available_monomorphizations().borrow()
|
||||
@ -133,12 +132,14 @@ pub fn monomorphic_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
|
||||
}
|
||||
|
||||
let trans_everywhere = attr::requests_inline(attrs);
|
||||
if trans_everywhere && !is_first {
|
||||
llvm::SetLinkage(lldecl, llvm::AvailableExternallyLinkage);
|
||||
}
|
||||
|
||||
if trans_everywhere || is_first {
|
||||
let origin = if is_first { base::OriginalTranslation } else { base::InlinedCopy };
|
||||
base::update_linkage(ccx, lldecl, None, origin);
|
||||
trans_fn(ccx, decl, body, lldecl, psubsts, fn_node_id);
|
||||
} else {
|
||||
// We marked the value as using internal linkage earlier, but that is illegal for
|
||||
// declarations, so switch back to external linkage.
|
||||
llvm::SetLinkage(lldecl, llvm::ExternalLinkage);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1189,3 +1189,16 @@ extern "C" void LLVMRustPositionBuilderAtStart(LLVMBuilderRef B, LLVMBasicBlockR
|
||||
auto point = unwrap(BB)->getFirstInsertionPt();
|
||||
unwrap(B)->SetInsertPoint(unwrap(BB), point);
|
||||
}
|
||||
|
||||
extern "C" void LLVMRustSetComdat(LLVMModuleRef M, LLVMValueRef V, const char *Name) {
|
||||
Triple TargetTriple(unwrap(M)->getTargetTriple());
|
||||
GlobalObject *GV = unwrap<GlobalObject>(V);
|
||||
if (!TargetTriple.isOSBinFormatMachO()) {
|
||||
GV->setComdat(unwrap(M)->getOrInsertComdat(Name));
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" void LLVMRustUnsetComdat(LLVMValueRef V) {
|
||||
GlobalObject *GV = unwrap<GlobalObject>(V);
|
||||
GV->setComdat(nullptr);
|
||||
}
|
||||
|
16
src/test/auxiliary/cgu_test.rs
Normal file
16
src/test/auxiliary/cgu_test.rs
Normal file
@ -0,0 +1,16 @@
|
||||
// Copyright 2016 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 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// no-prefer-dynamic
|
||||
// compile-flags: --crate-type=lib
|
||||
|
||||
pub fn id<T>(t: T) -> T {
|
||||
t
|
||||
}
|
25
src/test/auxiliary/cgu_test_a.rs
Normal file
25
src/test/auxiliary/cgu_test_a.rs
Normal file
@ -0,0 +1,25 @@
|
||||
// Copyright 2016 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 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// no-prefer-dynamic
|
||||
// compile-flags: -Ccodegen-units=2 --crate-type=lib
|
||||
|
||||
extern crate cgu_test;
|
||||
|
||||
pub mod a {
|
||||
pub fn a() {
|
||||
::cgu_test::id(0);
|
||||
}
|
||||
}
|
||||
pub mod b {
|
||||
pub fn a() {
|
||||
::cgu_test::id(0);
|
||||
}
|
||||
}
|
25
src/test/auxiliary/cgu_test_b.rs
Normal file
25
src/test/auxiliary/cgu_test_b.rs
Normal file
@ -0,0 +1,25 @@
|
||||
// Copyright 2016 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 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// no-prefer-dynamic
|
||||
// compile-flags: -Ccodegen-units=2 --crate-type=lib
|
||||
|
||||
extern crate cgu_test;
|
||||
|
||||
pub mod a {
|
||||
pub fn a() {
|
||||
::cgu_test::id(0);
|
||||
}
|
||||
}
|
||||
pub mod b {
|
||||
pub fn a() {
|
||||
::cgu_test::id(0);
|
||||
}
|
||||
}
|
22
src/test/run-pass/issue-32518.rs
Normal file
22
src/test/run-pass/issue-32518.rs
Normal file
@ -0,0 +1,22 @@
|
||||
// Copyright 2016 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 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// no-prefer-dynamic
|
||||
// aux-build:cgu_test.rs
|
||||
// aux-build:cgu_test_a.rs
|
||||
// aux-build:cgu_test_b.rs
|
||||
|
||||
extern crate cgu_test_a;
|
||||
extern crate cgu_test_b;
|
||||
|
||||
fn main() {
|
||||
cgu_test_a::a::a();
|
||||
cgu_test_b::a::a();
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user