From 03733ca65a09a9b54b2c2a674e4145b165bd1cab Mon Sep 17 00:00:00 2001 From: cynecx Date: Mon, 22 Nov 2021 13:14:54 +0100 Subject: [PATCH] `#[used(linker)]` attribute (https://github.com/dtolnay/linkme/issues/41) --- compiler/rustc_codegen_gcc/src/consts.rs | 2 +- compiler/rustc_codegen_llvm/src/consts.rs | 9 +++++ compiler/rustc_feature/src/active.rs | 2 + compiler/rustc_feature/src/builtin_attrs.rs | 2 +- .../src/middle/codegen_fn_attrs.rs | 2 + compiler/rustc_passes/src/dead.rs | 5 ++- compiler/rustc_span/src/symbol.rs | 3 ++ compiler/rustc_typeck/src/collect.rs | 37 ++++++++++++++++++- 8 files changed, 58 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_codegen_gcc/src/consts.rs b/compiler/rustc_codegen_gcc/src/consts.rs index ba4589bd810..ddc2b88191b 100644 --- a/compiler/rustc_codegen_gcc/src/consts.rs +++ b/compiler/rustc_codegen_gcc/src/consts.rs @@ -144,7 +144,7 @@ fn codegen_static(&self, def_id: DefId, is_mutable: bool) { // TODO(antoyo): set link section. } - if attrs.flags.contains(CodegenFnAttrFlags::USED) { + if attrs.flags.contains(CodegenFnAttrFlags::USED) || attrs.flags.contains(CodegenFnAttrFlags::USED_LINKER) { self.add_used_global(global.to_rvalue()); } } diff --git a/compiler/rustc_codegen_llvm/src/consts.rs b/compiler/rustc_codegen_llvm/src/consts.rs index d43c7c60651..6707de93352 100644 --- a/compiler/rustc_codegen_llvm/src/consts.rs +++ b/compiler/rustc_codegen_llvm/src/consts.rs @@ -522,6 +522,9 @@ fn codegen_static(&self, def_id: DefId, is_mutable: bool) { } if attrs.flags.contains(CodegenFnAttrFlags::USED) { + // `USED` and `USED_LINKER` can't be used together. + assert!(!attrs.flags.contains(CodegenFnAttrFlags::USED_LINKER)); + // The semantics of #[used] in Rust only require the symbol to make it into the // object file. It is explicitly allowed for the linker to strip the symbol if it // is dead. As such, use llvm.compiler.used instead of llvm.used. @@ -530,6 +533,12 @@ fn codegen_static(&self, def_id: DefId, is_mutable: bool) { // in some versions of the gold linker. self.add_compiler_used_global(g); } + if attrs.flags.contains(CodegenFnAttrFlags::USED_LINKER) { + // `USED` and `USED_LINKER` can't be used together. + assert!(!attrs.flags.contains(CodegenFnAttrFlags::USED)); + + self.add_used_global(g); + } } } diff --git a/compiler/rustc_feature/src/active.rs b/compiler/rustc_feature/src/active.rs index bfe2459dc8d..d958a14afab 100644 --- a/compiler/rustc_feature/src/active.rs +++ b/compiler/rustc_feature/src/active.rs @@ -533,6 +533,8 @@ pub fn set(&self, features: &mut Features, span: Span) { /// /// NOTE: A limited form of `union U { ... }` was accepted in 1.19.0. (active, untagged_unions, "1.13.0", Some(55149), None), + /// Allows using the `#[used(retain)]` attribute. + (active, used_with_arg, "1.60.0", None, None), /// Allows `extern "wasm" fn` (active, wasm_abi, "1.53.0", Some(83788), None), // !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index 3933746c319..739e5450438 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -324,7 +324,7 @@ pub struct BuiltinAttribute { ungated!(export_name, Normal, template!(NameValueStr: "name"), FutureWarnPreceding), ungated!(link_section, Normal, template!(NameValueStr: "name"), FutureWarnPreceding), ungated!(no_mangle, Normal, template!(Word), WarnFollowing), - ungated!(used, Normal, template!(Word), WarnFollowing), + ungated!(used, Normal, template!(Word, List: "used"), WarnFollowing), // Limits: ungated!(recursion_limit, CrateLevel, template!(NameValueStr: "N"), FutureWarnFollowing), diff --git a/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs b/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs index b054d21adaa..54eb2dc9e28 100644 --- a/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs +++ b/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs @@ -89,6 +89,8 @@ pub struct CodegenFnAttrFlags: u32 { /// the MIR `InstrumentCoverage` pass and not added to the coverage map /// during codegen. const NO_COVERAGE = 1 << 15; + /// `#[used(linker)]`: indicates that LLVM nor the linker can eliminate this function. + const USED_LINKER = 1 << 16; } } diff --git a/compiler/rustc_passes/src/dead.rs b/compiler/rustc_passes/src/dead.rs index e7e56711b33..dc3ce1afa33 100644 --- a/compiler/rustc_passes/src/dead.rs +++ b/compiler/rustc_passes/src/dead.rs @@ -466,7 +466,10 @@ fn has_allow_dead_code_or_lang_attr(tcx: TyCtxt<'_>, id: hir::HirId) -> bool { // #[used], #[no_mangle], #[export_name], etc also keeps the item alive // forcefully, e.g., for placing it in a specific section. - if cg_attrs.contains_extern_indicator() || cg_attrs.flags.contains(CodegenFnAttrFlags::USED) { + if cg_attrs.contains_extern_indicator() + || cg_attrs.flags.contains(CodegenFnAttrFlags::USED) + || cg_attrs.flags.contains(CodegenFnAttrFlags::USED_LINKER) + { return true; } diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 18627cd2c09..4a1fd476b8c 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -449,6 +449,7 @@ compare_exchange_weak, compile_error, compile_error_macro, + compiler, compiler_builtins, compiler_fence, concat, @@ -815,6 +816,7 @@ link_ordinal, link_section, linkage, + linker, lint_reasons, literal, load, @@ -1458,6 +1460,7 @@ use_extern_macros, use_nested_groups, used, + used_with_arg, usize, v1, va_arg, diff --git a/compiler/rustc_typeck/src/collect.rs b/compiler/rustc_typeck/src/collect.rs index cf519a9ab32..4a3c477021c 100644 --- a/compiler/rustc_typeck/src/collect.rs +++ b/compiler/rustc_typeck/src/collect.rs @@ -2849,7 +2849,42 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, id: DefId) -> CodegenFnAttrs { } else if attr.has_name(sym::rustc_std_internal_symbol) { codegen_fn_attrs.flags |= CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL; } else if attr.has_name(sym::used) { - codegen_fn_attrs.flags |= CodegenFnAttrFlags::USED; + let inner = attr.meta_item_list(); + match inner.as_deref() { + Some([item]) if item.has_name(sym::linker) => { + if !tcx.features().used_with_arg { + feature_err( + &tcx.sess.parse_sess, + sym::used_with_arg, + attr.span, + "`#[used(linker)]` is currently unstable", + ) + .emit(); + } + codegen_fn_attrs.flags |= CodegenFnAttrFlags::USED_LINKER; + } + Some([item]) if item.has_name(sym::compiler) => { + if !tcx.features().used_with_arg { + feature_err( + &tcx.sess.parse_sess, + sym::used_with_arg, + attr.span, + "`#[used(compiler)]` is currently unstable", + ) + .emit(); + } + codegen_fn_attrs.flags |= CodegenFnAttrFlags::USED; + } + Some(_) => { + tcx.sess + .struct_span_err( + attr.span, + "expected `used`, `used(compiler)` or `used(linker)`", + ) + .emit(); + } + None => codegen_fn_attrs.flags |= CodegenFnAttrFlags::USED, + } } else if attr.has_name(sym::cmse_nonsecure_entry) { if !matches!(tcx.fn_sig(id).abi(), abi::Abi::C { .. }) { struct_span_err!(