diff --git a/src/librustc/middle/trans/base.rs b/src/librustc/middle/trans/base.rs index c6cc3092c11ec..0c26d13e383c0 100644 --- a/src/librustc/middle/trans/base.rs +++ b/src/librustc/middle/trans/base.rs @@ -503,8 +503,40 @@ pub fn set_inline_hint(f: ValueRef) { lib::llvm::SetFunctionAttribute(f, lib::llvm::InlineHintAttribute) } +pub fn set_llvm_linkage_attr(attrs: &[ast::Attribute], ll: ValueRef) { + use lib::llvm::*; + use syntax::attr::*; + + // Convert to the LLVM linkage values. + let linkage = match find_linkage_attr(attrs) { + LinkageDefault => { return; }, + LinkageExternal => ExternalLinkage, + LinkageAvailableExternally => AvailableExternallyLinkage, + LinkageLinkOnceAny => LinkOnceAnyLinkage, + LinkageLinkOnceODR => LinkOnceODRLinkage, + LinkageLinkOnceODRAutoHide => LinkOnceODRAutoHideLinkage, + LinkageWeakAny => WeakAnyLinkage, + LinkageWeakODR => WeakODRLinkage, + LinkageAppending => AppendingLinkage, + LinkageInternal => InternalLinkage, + LinkagePrivate => PrivateLinkage, + LinkageDLLImport => DLLImportLinkage, + LinkageDLLExport => DLLExportLinkage, + LinkageExternalWeak => ExternalWeakLinkage, + LinkageGhost => GhostLinkage, + LinkageCommon => CommonLinkage, + LinkageLinkerPrivate => LinkerPrivateLinkage, + LinkageLinkerPrivateWeak => LinkerPrivateWeakLinkage + }; + lib::llvm::SetLinkage(ll, linkage); +} + pub fn set_llvm_fn_attrs(attrs: &[ast::Attribute], llfn: ValueRef) { use syntax::attr::*; + + // Change the function linkage if requested. + set_llvm_linkage_attr(attrs, llfn); + // Set the inline hint if there is one match find_inline_attr(attrs) { InlineHint => set_inline_hint(llfn), @@ -2645,9 +2677,7 @@ pub fn get_item_val(ccx: @mut CrateContext, id: ast::NodeId) -> ValueRef { let ty = type_of(ccx, ty); llvm::LLVMAddGlobal(ccx.llmod, ty.to_ref(), buf) }; - if attr::contains_name(ni.attrs, "weak_linkage") { - lib::llvm::SetLinkage(g, lib::llvm::ExternalWeakLinkage); - } + set_llvm_linkage_attr(ni.attrs, g); g } } diff --git a/src/libstd/rt/crate_map.rs b/src/libstd/rt/crate_map.rs index 96a0069e85156..8fce402072dab 100644 --- a/src/libstd/rt/crate_map.rs +++ b/src/libstd/rt/crate_map.rs @@ -34,7 +34,8 @@ pub struct CrateMap<'self> { #[cfg(not(windows))] pub fn get_crate_map() -> Option<&'static CrateMap<'static>> { extern { - #[weak_linkage] + #[weak_linkage] // NOTE: remove after next snapshot + #[linkage(external_weak)] #[link_name = "_rust_crate_map_toplevel"] static CRATE_MAP: CrateMap<'static>; } diff --git a/src/libsyntax/attr.rs b/src/libsyntax/attr.rs index 47b31a4f76d5f..4752dd9617753 100644 --- a/src/libsyntax/attr.rs +++ b/src/libsyntax/attr.rs @@ -265,6 +265,62 @@ pub fn find_inline_attr(attrs: &[Attribute]) -> InlineAttr { } } +#[deriving(Eq)] +pub enum LinkageAttr { + LinkageDefault, + LinkageExternal, + LinkageAvailableExternally, + LinkageLinkOnceAny, + LinkageLinkOnceODR, + LinkageLinkOnceODRAutoHide, + LinkageWeakAny, + LinkageWeakODR, + LinkageAppending, + LinkageInternal, + LinkagePrivate, + LinkageDLLImport, + LinkageDLLExport, + LinkageExternalWeak, + LinkageGhost, + LinkageCommon, + LinkageLinkerPrivate, + LinkageLinkerPrivateWeak +} + +/// Find and translate #[linkage(name)] in the list of attrs. +pub fn find_linkage_attr(attrs: &[Attribute]) -> LinkageAttr { + do attrs.iter().fold(LinkageDefault) |la,attr| { + match attr.node.value.node { + MetaList(n, ref items) if "linkage" == n => { + do items.iter().fold(la) |la,item| { + let linkage : &str = item.name(); + match linkage { + "external" => LinkageExternal, + "available_externally" => LinkageAvailableExternally, + "link_once_any" => LinkageLinkOnceAny, + "link_once_odr" => LinkageLinkOnceODR, + "link_once_odr_auto_hide" => LinkageLinkOnceODRAutoHide, + "weak_any" => LinkageWeakAny, + "weak_odr" => LinkageWeakODR, + "appending" => LinkageAppending, + "internal" => LinkageInternal, + "private" => LinkagePrivate, + "dll_import" => LinkageDLLImport, + "dll_export" => LinkageDLLExport, + "external_weak" => LinkageExternalWeak, + "ghost" => LinkageGhost, + "common" => LinkageCommon, + "linker_private" => LinkageLinkerPrivate, + "linker_private_weak" => LinkageLinkerPrivateWeak, + _ => la + } + } + } + _ => la + } + } +} + /// Tests if any `cfg(...)` meta items in `metas` match `cfg`. e.g. /// /// test_cfg(`[foo="a", bar]`, `[cfg(foo), cfg(bar)]`) == true diff --git a/src/test/auxiliary/extern_foreign.rs b/src/test/auxiliary/extern_foreign.rs new file mode 100644 index 0000000000000..82a4ef8bdd901 --- /dev/null +++ b/src/test/auxiliary/extern_foreign.rs @@ -0,0 +1,21 @@ +// Copyright 2013 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. + +#[link(name="extern_foreign", vers="0.0")]; + +extern "C" { + fn foreign(); +} + +#[fixed_stack_segment] +pub unsafe fn doer() { + foreign(); +} + diff --git a/src/test/run-pass/attr-linkage-external-foreign.rs b/src/test/run-pass/attr-linkage-external-foreign.rs new file mode 100644 index 0000000000000..7ffb732e6acd5 --- /dev/null +++ b/src/test/run-pass/attr-linkage-external-foreign.rs @@ -0,0 +1,23 @@ +// Copyright 2013 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. + +// aux-build:extern_foreign.rs + +extern mod extern_foreign; + +#[linkage(external)] +#[no_mangle] +pub extern "C" fn foreign() { +} + +pub fn main() { + unsafe { extern_foreign::doer() }; +} + diff --git a/src/test/run-pass/attr-linkage-external.rs b/src/test/run-pass/attr-linkage-external.rs new file mode 100644 index 0000000000000..5ae57aa864b02 --- /dev/null +++ b/src/test/run-pass/attr-linkage-external.rs @@ -0,0 +1,34 @@ +// Copyright 2012 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. + +#[linkage(external)] +#[inline(always)] // Force LLVM to consider the function for removal. +#[no_mangle] +fn foobar() {} + +// The following tests call foobar directly via inline assembly, using bl on ARM and call on x86. +// This is done so that LLVM's decisions aren't affected by the reference to foobar, and without +// #[linkage(external)], LLVM fails with the error "undefined reference to `foobar'". + +#[cfg(target_arch = "arm")] +pub fn main() { + foobar(); + unsafe { asm!("bl foobar"); } +} + +#[cfg(target_arch = "x86")] +#[cfg(target_arch = "x86_64")] +pub fn main() { + foobar(); + unsafe { asm!("call foobar"); } +} + +#[cfg(not(target_arch = "arm"), not(target_arch = "x86"), not(target_arch = "x86_64"))] +pub fn main() {}