Auto merge of #39987 - japaric:used, r=arielb1
#[used] attribute (For an explanation of what this feature does, read the commit message) I'd like to propose landing this as an experimental feature (experimental as in: no clear stabilization path -- like `asm!`, `#[linkage]`) as it's low maintenance (I think) and relevant to the "Usage in resource-constrained environments" exploration area. The main use case I see is running code before `main`. This could be used, for instance, to cheaply initialize an allocator before `main` where the alternative is to use `lazy_static` to initialize the allocator on its first use which it's more expensive (atomics) and doesn't work on ARM Cortex-M0 microcontrollers (no `AtomicUsize` on that platform) Here's a `std` example of that: ``` rust unsafe extern "C" fn before_main_1() { println!("Hello"); } unsafe extern "C" fn before_main_2() { println!("World"); } #[link_section = ".init_arary"] #[used] static INIT_ARRAY: [unsafe extern "C" fn(); 2] = [before_main_1, before_main_2]; fn main() { println!("Goodbye"); } ``` ``` $ rustc -C lto -C opt-level=3 before_main.rs $ ./before_main Hello World Goodbye ``` In general, this pattern could be used to let *dependencies* run code before `main` (which sounds like it could go very wrong in some cases). There are probably other use cases; I hope that the people I have cc-ed can comment on those. Note that I'm personally unsure if the above pattern is something we want to promote / allow and that's why I'm proposing this feature as experimental. If this leads to more footguns than benefits then we can just axe the feature. cc @nikomatsakis ^ I know you have some thoughts on having a process for experimental features though I'm fine with writing an RFC before landing this. - `dead_code` lint will have to be updated to special case `#[used]` symbols. - Should we extend `#[used]` to work on non-generic functions? cc rust-lang/rfcs#1002 cc rust-lang/rfcs#1459 cc @dpc @JinShil
This commit is contained in:
commit
b9c5197d48
@ -205,6 +205,7 @@
|
||||
- [unwind_attributes](unwind-attributes.md)
|
||||
- [update_panic_count](update-panic-count.md)
|
||||
- [use_extern_macros](use-extern-macros.md)
|
||||
- [used](used.md)
|
||||
- [utf8_error_error_len](utf8-error-error-len.md)
|
||||
- [vec_remove_item](vec-remove-item.md)
|
||||
- [windows_c](windows-c.md)
|
||||
|
153
src/doc/unstable-book/src/used.md
Normal file
153
src/doc/unstable-book/src/used.md
Normal file
@ -0,0 +1,153 @@
|
||||
# `used`
|
||||
|
||||
The tracking issue for this feature
|
||||
is: [40289](https://github.com/rust-lang/rust/issues/40289).
|
||||
|
||||
------------------------
|
||||
|
||||
The `#[used]` attribute can be applied to `static` variables to prevent the Rust
|
||||
compiler from optimizing them away even if they appear to be unused by the crate
|
||||
(appear to be "dead code").
|
||||
|
||||
``` rust
|
||||
#![feature(used)]
|
||||
|
||||
#[used]
|
||||
static FOO: i32 = 1;
|
||||
|
||||
static BAR: i32 = 2;
|
||||
|
||||
fn main() {}
|
||||
```
|
||||
|
||||
If you compile this program into an object file, you'll see that `FOO` makes it
|
||||
to the object file but `BAR` doesn't. Neither static variable is used by the
|
||||
program.
|
||||
|
||||
``` text
|
||||
$ rustc -C opt-level=3 --emit=obj used.rs
|
||||
|
||||
$ nm -C used.o
|
||||
0000000000000000 T main
|
||||
U std::rt::lang_start
|
||||
0000000000000000 r used::FOO
|
||||
0000000000000000 t used::main
|
||||
```
|
||||
|
||||
Note that the *linker* knows nothing about the `#[used]` attribute and will
|
||||
remove `#[used]` symbols if they are not referenced by other parts of the
|
||||
program:
|
||||
|
||||
``` text
|
||||
$ rustc -C opt-level=3 used.rs
|
||||
|
||||
$ nm -C used | grep FOO
|
||||
```
|
||||
|
||||
"This doesn't sound too useful then!" you may think but keep reading.
|
||||
|
||||
To preserve the symbols all the way to the final binary, you'll need the
|
||||
cooperation of the linker. Here's one example:
|
||||
|
||||
The ELF standard defines two special sections, `.init_array` and
|
||||
`.pre_init_array`, that may contain function pointers which will be executed
|
||||
*before* the `main` function is invoked. The linker will preserve symbols placed
|
||||
in these sections (at least when linking programs that target the `*-*-linux-*`
|
||||
targets).
|
||||
|
||||
``` rust,ignore
|
||||
#![feature(used)]
|
||||
|
||||
extern "C" fn before_main() {
|
||||
println!("Hello, world!");
|
||||
}
|
||||
|
||||
#[link_section = ".init_array"]
|
||||
#[used]
|
||||
static INIT_ARRAY: [extern "C" fn(); 1] = [before_main];
|
||||
|
||||
fn main() {}
|
||||
```
|
||||
|
||||
So, `#[used]` and `#[link_section]` can be combined to obtain "life before
|
||||
main".
|
||||
|
||||
``` text
|
||||
$ rustc -C opt-level=3 before-main.rs
|
||||
|
||||
$ ./before-main
|
||||
Hello, world!
|
||||
```
|
||||
|
||||
Another example: ARM Cortex-M microcontrollers need their reset handler, a
|
||||
pointer to the function that will executed right after the microcontroller is
|
||||
turned on, to be placed near the start of their FLASH memory to boot properly.
|
||||
|
||||
This condition can be met using `#[used]` and `#[link_section]` plus a linker
|
||||
script.
|
||||
|
||||
``` rust,ignore
|
||||
#![feature(lang_items)]
|
||||
#![feature(used)]
|
||||
#![no_main]
|
||||
#![no_std]
|
||||
|
||||
extern "C" fn reset_handler() -> ! {
|
||||
loop {}
|
||||
}
|
||||
|
||||
#[link_section = ".reset_handler"]
|
||||
#[used]
|
||||
static RESET_HANDLER: extern "C" fn() -> ! = reset_handler;
|
||||
|
||||
#[lang = "panic_fmt"]
|
||||
fn panic_fmt() {}
|
||||
```
|
||||
|
||||
``` text
|
||||
MEMORY
|
||||
{
|
||||
FLASH : ORIGIN = 0x08000000, LENGTH = 128K
|
||||
RAM : ORIGIN = 0x20000000, LENGTH = 20K
|
||||
}
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
.text ORIGIN(FLASH) :
|
||||
{
|
||||
/* Vector table */
|
||||
LONG(ORIGIN(RAM) + LENGTH(RAM)); /* initial SP value */
|
||||
KEEP(*(.reset_handler));
|
||||
|
||||
/* Omitted: The rest of the vector table */
|
||||
|
||||
*(.text.*);
|
||||
} > FLASH
|
||||
|
||||
/DISCARD/ :
|
||||
{
|
||||
/* Unused unwinding stuff */
|
||||
*(.ARM.exidx.*)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
``` text
|
||||
$ xargo rustc --target thumbv7m-none-eabi --release -- \
|
||||
-C link-arg=-Tlink.x -C link-arg=-nostartfiles
|
||||
|
||||
$ arm-none-eabi-objdump -Cd target/thumbv7m-none-eabi/release/app
|
||||
./target/thumbv7m-none-eabi/release/app: file format elf32-littlearm
|
||||
|
||||
|
||||
Disassembly of section .text:
|
||||
|
||||
08000000 <app::RESET_HANDLER-0x4>:
|
||||
8000000: 20005000 .word 0x20005000
|
||||
|
||||
08000004 <app::RESET_HANDLER>:
|
||||
8000004: 08000009 ....
|
||||
|
||||
08000008 <app::reset_handler>:
|
||||
8000008: e7fe b.n 8000008 <app::reset_handler>
|
||||
```
|
@ -50,7 +50,7 @@ use builder::Builder;
|
||||
use callee;
|
||||
use common::{C_bool, C_bytes_in_context, C_i32, C_uint};
|
||||
use collector::{self, TransItemCollectionMode};
|
||||
use common::{C_struct_in_context, C_u64, C_undef};
|
||||
use common::{C_struct_in_context, C_u64, C_undef, C_array};
|
||||
use common::CrateContext;
|
||||
use common::{type_is_zero_size, val_ty};
|
||||
use common;
|
||||
@ -1187,6 +1187,23 @@ pub fn trans_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
||||
}
|
||||
}
|
||||
|
||||
// Create the llvm.used variable
|
||||
// This variable has type [N x i8*] and is stored in the llvm.metadata section
|
||||
if !ccx.used_statics().borrow().is_empty() {
|
||||
let name = CString::new("llvm.used").unwrap();
|
||||
let section = CString::new("llvm.metadata").unwrap();
|
||||
let array = C_array(Type::i8(&ccx).ptr_to(), &*ccx.used_statics().borrow());
|
||||
|
||||
unsafe {
|
||||
let g = llvm::LLVMAddGlobal(ccx.llmod(),
|
||||
val_ty(array).to_ref(),
|
||||
name.as_ptr());
|
||||
llvm::LLVMSetInitializer(g, array);
|
||||
llvm::LLVMRustSetLinkage(g, llvm::Linkage::AppendingLinkage);
|
||||
llvm::LLVMSetSection(g, section.as_ptr());
|
||||
}
|
||||
}
|
||||
|
||||
// Finalize debuginfo
|
||||
if ccx.sess().opts.debuginfo != NoDebugInfo {
|
||||
debuginfo::finalize(&ccx);
|
||||
|
@ -276,6 +276,12 @@ pub fn trans_static<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
|
||||
|
||||
base::set_link_section(ccx, g, attrs);
|
||||
|
||||
if attr::contains_name(attrs, "used") {
|
||||
// This static will be stored in the llvm.used variable which is an array of i8*
|
||||
let cast = llvm::LLVMConstPointerCast(g, Type::i8p(ccx).to_ref());
|
||||
ccx.used_statics().borrow_mut().push(cast);
|
||||
}
|
||||
|
||||
Ok(g)
|
||||
}
|
||||
}
|
||||
|
@ -132,6 +132,10 @@ pub struct LocalCrateContext<'tcx> {
|
||||
/// to constants.)
|
||||
statics_to_rauw: RefCell<Vec<(ValueRef, ValueRef)>>,
|
||||
|
||||
/// Statics that will be placed in the llvm.used variable
|
||||
/// See http://llvm.org/docs/LangRef.html#the-llvm-used-global-variable for details
|
||||
used_statics: RefCell<Vec<ValueRef>>,
|
||||
|
||||
lltypes: RefCell<FxHashMap<Ty<'tcx>, Type>>,
|
||||
llsizingtypes: RefCell<FxHashMap<Ty<'tcx>, Type>>,
|
||||
type_hashcodes: RefCell<FxHashMap<Ty<'tcx>, String>>,
|
||||
@ -587,6 +591,7 @@ impl<'tcx> LocalCrateContext<'tcx> {
|
||||
impl_method_cache: RefCell::new(FxHashMap()),
|
||||
closure_bare_wrapper_cache: RefCell::new(FxHashMap()),
|
||||
statics_to_rauw: RefCell::new(Vec::new()),
|
||||
used_statics: RefCell::new(Vec::new()),
|
||||
lltypes: RefCell::new(FxHashMap()),
|
||||
llsizingtypes: RefCell::new(FxHashMap()),
|
||||
type_hashcodes: RefCell::new(FxHashMap()),
|
||||
@ -754,6 +759,10 @@ impl<'b, 'tcx> CrateContext<'b, 'tcx> {
|
||||
&self.local().statics_to_rauw
|
||||
}
|
||||
|
||||
pub fn used_statics<'a>(&'a self) -> &'a RefCell<Vec<ValueRef>> {
|
||||
&self.local().used_statics
|
||||
}
|
||||
|
||||
pub fn lltypes<'a>(&'a self) -> &'a RefCell<FxHashMap<Ty<'tcx>, Type>> {
|
||||
&self.local().lltypes
|
||||
}
|
||||
|
@ -334,11 +334,15 @@ declare_features! (
|
||||
// `extern "x86-interrupt" fn()`
|
||||
(active, abi_x86_interrupt, "1.17.0", Some(40180)),
|
||||
|
||||
|
||||
// Allows the `catch {...}` expression
|
||||
(active, catch_expr, "1.17.0", Some(31436)),
|
||||
|
||||
// See rust-lang/rfcs#1414. Allows code like `let x: &'static u32 = &42` to work.
|
||||
(active, rvalue_static_promotion, "1.15.1", Some(38865)),
|
||||
|
||||
// Used to preserve symbols (see llvm.used)
|
||||
(active, used, "1.18.0", Some(40289)),
|
||||
);
|
||||
|
||||
declare_features! (
|
||||
@ -746,6 +750,10 @@ pub const BUILTIN_ATTRIBUTES: &'static [(&'static str, AttributeType, AttributeG
|
||||
"unwind_attributes",
|
||||
"#[unwind] is experimental",
|
||||
cfg_fn!(unwind_attributes))),
|
||||
("used", Whitelisted, Gated(
|
||||
Stability::Unstable, "used",
|
||||
"the `#[used]` attribute is an experimental feature",
|
||||
cfg_fn!(used))),
|
||||
|
||||
// used in resolve
|
||||
("prelude_import", Whitelisted, Gated(Stability::Unstable,
|
||||
|
15
src/test/compile-fail/feature-gate-used.rs
Normal file
15
src/test/compile-fail/feature-gate-used.rs
Normal file
@ -0,0 +1,15 @@
|
||||
// Copyright 2017 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.
|
||||
|
||||
#[used]
|
||||
fn foo() {}
|
||||
//~^^ ERROR the `#[used]` attribute is an experimental feature
|
||||
|
||||
fn main() {}
|
11
src/test/run-make/used/Makefile
Normal file
11
src/test/run-make/used/Makefile
Normal file
@ -0,0 +1,11 @@
|
||||
-include ../tools.mk
|
||||
|
||||
ifdef IS_WINDOWS
|
||||
# Do nothing on MSVC.
|
||||
all:
|
||||
exit 0
|
||||
else
|
||||
all:
|
||||
$(RUSTC) -C opt-level=3 --emit=obj used.rs
|
||||
nm $(TMPDIR)/used.o | grep FOO
|
||||
endif
|
17
src/test/run-make/used/used.rs
Normal file
17
src/test/run-make/used/used.rs
Normal file
@ -0,0 +1,17 @@
|
||||
// Copyright 2017 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.
|
||||
|
||||
#![crate_type = "lib"]
|
||||
#![feature(used)]
|
||||
|
||||
#[used]
|
||||
static FOO: u32 = 0;
|
||||
|
||||
static BAR: u32 = 0;
|
Loading…
x
Reference in New Issue
Block a user