rust/compiler/rustc_codegen_llvm/src/consts.rs

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

596 lines
27 KiB
Rust
Raw Normal View History

2019-02-17 18:58:58 +00:00
use crate::base;
2019-12-24 22:38:22 +00:00
use crate::common::CodegenCx;
use crate::debuginfo;
use crate::llvm::{self, True};
2022-02-20 16:37:22 +00:00
use crate::llvm_util;
2019-02-17 18:58:58 +00:00
use crate::type_::Type;
use crate::type_of::LayoutLlvmExt;
use crate::value::Value;
2021-02-13 11:17:15 +00:00
use cstr::cstr;
use libc::c_uint;
2020-03-29 15:19:48 +00:00
use rustc_codegen_ssa::traits::*;
use rustc_hir::def_id::DefId;
2020-03-29 14:41:09 +00:00
use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrFlags, CodegenFnAttrs};
2020-03-29 15:19:48 +00:00
use rustc_middle::mir::interpret::{
read_target_uint, Allocation, ConstAllocation, ErrorHandled, GlobalAlloc, InitChunk, Pointer,
Scalar as InterpScalar,
2020-03-29 15:19:48 +00:00
};
2020-03-29 14:41:09 +00:00
use rustc_middle::mir::mono::MonoItem;
use rustc_middle::ty::layout::LayoutOf;
2020-03-29 14:41:09 +00:00
use rustc_middle::ty::{self, Instance, Ty};
use rustc_middle::{bug, span_bug};
use rustc_target::abi::{
AddressSpace, Align, HasDataLayout, Primitive, Scalar, Size, WrappingRange,
};
use std::ops::Range;
2020-08-05 11:35:53 +00:00
use tracing::debug;
pub fn const_alloc_to_llvm<'ll>(cx: &CodegenCx<'ll, '_>, alloc: ConstAllocation<'_>) -> &'ll Value {
let alloc = alloc.inner();
let mut llvals = Vec::with_capacity(alloc.relocations().len() + 1);
let dl = cx.data_layout();
let pointer_size = dl.pointer_size.bytes() as usize;
// Note: this function may call `inspect_with_uninit_and_ptr_outside_interpreter`,
2021-08-10 05:26:33 +00:00
// so `range` must be within the bounds of `alloc` and not contain or overlap a relocation.
fn append_chunks_of_init_and_uninit_bytes<'ll, 'a, 'b>(
llvals: &mut Vec<&'ll Value>,
cx: &'a CodegenCx<'ll, 'b>,
alloc: &'a Allocation,
range: Range<usize>,
) {
let chunks = alloc
.init_mask()
.range_as_init_chunks(Size::from_bytes(range.start), Size::from_bytes(range.end));
let chunk_to_llval = move |chunk| match chunk {
InitChunk::Init(range) => {
let range = (range.start.bytes() as usize)..(range.end.bytes() as usize);
let bytes = alloc.inspect_with_uninit_and_ptr_outside_interpreter(range);
cx.const_bytes(bytes)
}
InitChunk::Uninit(range) => {
let len = range.end.bytes() - range.start.bytes();
cx.const_undef(cx.type_array(cx.type_i8(), len))
}
};
// Generating partially-uninit consts is limited to small numbers of chunks,
// to avoid the cost of generating large complex const expressions.
// For example, `[(u32, u8); 1024 * 1024]` contains uninit padding in each element,
// and would result in `{ [5 x i8] zeroinitializer, [3 x i8] undef, ...repeat 1M times... }`.
2022-02-20 16:37:22 +00:00
let max = if llvm_util::get_version() < (14, 0, 0) {
// Generating partially-uninit consts inhibits optimizations in LLVM < 14.
// See https://github.com/rust-lang/rust/issues/84565.
1
} else {
cx.sess().opts.debugging_opts.uninit_const_chunk_threshold
};
let allow_uninit_chunks = chunks.clone().take(max.saturating_add(1)).count() <= max;
2021-04-27 04:15:41 +00:00
if allow_uninit_chunks {
2021-04-27 04:15:41 +00:00
llvals.extend(chunks.map(chunk_to_llval));
} else {
// If this allocation contains any uninit bytes, codegen as if it was initialized
// (using some arbitrary value for uninit bytes).
let bytes = alloc.inspect_with_uninit_and_ptr_outside_interpreter(range);
llvals.push(cx.const_bytes(bytes));
}
}
let mut next_offset = 0;
for &(offset, alloc_id) in alloc.relocations().iter() {
let offset = offset.bytes();
assert_eq!(offset as usize as u64, offset);
let offset = offset as usize;
if offset > next_offset {
// This `inspect` is okay since we have checked that it is not within a relocation, it
// is within the bounds of the allocation, and it doesn't affect interpreter execution
// (we inspect the result after interpreter execution).
append_chunks_of_init_and_uninit_bytes(&mut llvals, cx, alloc, next_offset..offset);
}
let ptr_offset = read_target_uint(
dl.endian,
// This `inspect` is okay since it is within the bounds of the allocation, it doesn't
// affect interpreter execution (we inspect the result after interpreter execution),
// and we properly interpret the relocation as a relocation pointer offset.
alloc.inspect_with_uninit_and_ptr_outside_interpreter(offset..(offset + pointer_size)),
2019-12-24 22:38:22 +00:00
)
.expect("const_alloc_to_llvm: could not read relocation pointer")
as u64;
let address_space = match cx.tcx.global_alloc(alloc_id) {
GlobalAlloc::Function(..) => cx.data_layout().instruction_address_space,
GlobalAlloc::Static(..) | GlobalAlloc::Memory(..) => AddressSpace::DATA,
};
llvals.push(cx.scalar_to_backend(
InterpScalar::from_pointer(
Pointer::new(alloc_id, Size::from_bytes(ptr_offset)),
&cx.tcx,
),
Scalar::Initialized {
value: Primitive::Pointer,
valid_range: WrappingRange::full(dl.pointer_size),
},
cx.type_i8p_ext(address_space),
));
next_offset = offset + pointer_size;
}
if alloc.len() >= next_offset {
let range = next_offset..alloc.len();
// This `inspect` is okay since we have check that it is after all relocations, it is
// within the bounds of the allocation, and it doesn't affect interpreter execution (we
// inspect the result after interpreter execution).
append_chunks_of_init_and_uninit_bytes(&mut llvals, cx, alloc, range);
}
cx.const_struct(&llvals, true)
}
pub fn codegen_static_initializer<'ll, 'tcx>(
cx: &CodegenCx<'ll, 'tcx>,
def_id: DefId,
) -> Result<(&'ll Value, ConstAllocation<'tcx>), ErrorHandled> {
let alloc = cx.tcx.eval_static_initializer(def_id)?;
Ok((const_alloc_to_llvm(cx, alloc), alloc))
}
fn set_global_alignment<'ll>(cx: &CodegenCx<'ll, '_>, gv: &'ll Value, mut align: Align) {
// The target may require greater alignment for globals than the type does.
// Note: GCC and Clang also allow `__attribute__((aligned))` on variables,
// which can force it to be smaller. Rust doesn't support this yet.
if let Some(min) = cx.sess().target.min_global_align {
match Align::from_bits(min) {
Ok(min) => align = align.max(min),
Err(err) => {
2018-01-05 05:04:08 +00:00
cx.sess().err(&format!("invalid minimum global alignment: {}", err));
}
}
}
unsafe {
llvm::LLVMSetAlignment(gv, align.bytes() as u32);
}
}
fn check_and_apply_linkage<'ll, 'tcx>(
cx: &CodegenCx<'ll, 'tcx>,
attrs: &CodegenFnAttrs,
ty: Ty<'tcx>,
sym: &str,
2020-10-23 11:57:28 +00:00
span_def_id: DefId,
) -> &'ll Value {
let llty = cx.layout_of(ty).llvm_type(cx);
if let Some(linkage) = attrs.linkage {
debug!("get_static: sym={} linkage={:?}", sym, linkage);
// If this is a static with a linkage specified, then we need to handle
// it a little specially. The typesystem prevents things like &T and
// extern "C" fn() from being non-null, so we can't just declare a
// static and call it a day. Some linkages (like weak) will make it such
// that the static actually has a null value.
2020-08-02 22:49:11 +00:00
let llty2 = if let ty::RawPtr(ref mt) = ty.kind() {
2018-10-08 14:58:26 +00:00
cx.layout_of(mt.ty).llvm_type(cx)
} else {
cx.sess().span_fatal(
2020-10-23 11:57:28 +00:00
cx.tcx.def_span(span_def_id),
2019-12-24 22:38:22 +00:00
"must have type `*const T` or `*mut T` due to `#[linkage]` attribute",
)
};
unsafe {
// Declare a symbol `foo` with the desired linkage.
2021-09-30 17:38:50 +00:00
let g1 = cx.declare_global(sym, llty2);
llvm::LLVMRustSetLinkage(g1, base::linkage_to_llvm(linkage));
// Declare an internal global `extern_with_linkage_foo` which
// is initialized with the address of `foo`. If `foo` is
// discarded during linking (for example, if `foo` has weak
// linkage and there are no definitions), then
// `extern_with_linkage_foo` will instead be initialized to
// zero.
let mut real_name = "_rust_extern_with_linkage_".to_string();
2021-09-30 17:38:50 +00:00
real_name.push_str(sym);
2019-12-24 22:38:22 +00:00
let g2 = cx.define_global(&real_name, llty).unwrap_or_else(|| {
2020-10-23 11:57:28 +00:00
cx.sess().span_fatal(
cx.tcx.def_span(span_def_id),
&format!("symbol `{}` is already defined", &sym),
)
});
llvm::LLVMRustSetLinkage(g2, llvm::Linkage::InternalLinkage);
llvm::LLVMSetInitializer(g2, g1);
g2
}
} else {
// Generate an external declaration.
// FIXME(nagisa): investigate whether it can be changed into define_global
2021-09-30 17:38:50 +00:00
cx.declare_global(sym, llty)
}
}
pub fn ptrcast<'ll>(val: &'ll Value, ty: &'ll Type) -> &'ll Value {
2019-12-24 22:38:22 +00:00
unsafe { llvm::LLVMConstPointerCast(val, ty) }
2018-09-10 14:28:47 +00:00
}
impl<'ll> CodegenCx<'ll, '_> {
2018-11-24 16:45:05 +00:00
crate fn const_bitcast(&self, val: &'ll Value, ty: &'ll Type) -> &'ll Value {
2019-12-24 22:38:22 +00:00
unsafe { llvm::LLVMConstBitCast(val, ty) }
2018-09-10 14:28:47 +00:00
}
2018-11-24 16:11:59 +00:00
2018-11-24 16:30:48 +00:00
crate fn static_addr_of_mut(
2018-09-10 14:28:47 +00:00
&self,
cv: &'ll Value,
align: Align,
2018-09-10 14:28:47 +00:00
kind: Option<&str>,
) -> &'ll Value {
unsafe {
let gv = match kind {
2018-11-07 10:08:41 +00:00
Some(kind) if !self.tcx.sess.fewer_names() => {
let name = self.generate_local_symbol_name(kind);
2021-12-03 02:06:36 +00:00
let gv = self.define_global(&name, self.val_ty(cv)).unwrap_or_else(|| {
2019-12-24 22:38:22 +00:00
bug!("symbol `{}` is already defined", name);
2018-09-10 14:28:47 +00:00
});
llvm::LLVMRustSetLinkage(gv, llvm::Linkage::PrivateLinkage);
gv
2019-12-24 22:38:22 +00:00
}
_ => self.define_private_global(self.val_ty(cv)),
2018-09-10 14:28:47 +00:00
};
llvm::LLVMSetInitializer(gv, cv);
2021-09-30 17:38:50 +00:00
set_global_alignment(self, gv, align);
llvm::SetUnnamedAddress(gv, llvm::UnnamedAddr::Global);
2018-09-10 14:28:47 +00:00
gv
}
}
crate fn get_static(&self, def_id: DefId) -> &'ll Value {
2018-09-10 14:28:47 +00:00
let instance = Instance::mono(self.tcx, def_id);
2018-11-07 10:08:41 +00:00
if let Some(&g) = self.instances.borrow().get(&instance) {
2018-09-10 14:28:47 +00:00
return g;
}
2019-12-24 22:38:22 +00:00
let defined_in_current_codegen_unit =
self.codegen_unit.items().contains_key(&MonoItem::Static(def_id));
assert!(
!defined_in_current_codegen_unit,
"consts::get_static() should always hit the cache for \
2018-09-10 14:28:47 +00:00
statics defined in the same CGU, but did not for `{:?}`",
2019-12-24 22:38:22 +00:00
def_id
);
2018-09-10 14:28:47 +00:00
let ty = instance.ty(self.tcx, ty::ParamEnv::reveal_all());
let sym = self.tcx.symbol_name(instance).name;
2021-02-02 14:38:51 +00:00
let fn_attrs = self.tcx.codegen_fn_attrs(def_id);
2018-09-10 14:28:47 +00:00
2021-02-02 14:38:51 +00:00
debug!("get_static: sym={} instance={:?} fn_attrs={:?}", sym, instance, fn_attrs);
2018-09-10 14:28:47 +00:00
2021-02-04 10:17:01 +00:00
let g = if def_id.is_local() && !self.tcx.is_foreign_item(def_id) {
2018-09-13 12:58:19 +00:00
let llty = self.layout_of(ty).llvm_type(self);
2021-02-02 14:38:51 +00:00
if let Some(g) = self.get_declared_value(sym) {
if self.val_ty(g) != self.type_ptr_to(llty) {
span_bug!(self.tcx.def_span(def_id), "Conflicting types for static");
2018-09-10 14:28:47 +00:00
}
2021-02-02 14:38:51 +00:00
}
2018-09-10 14:28:47 +00:00
2021-02-02 14:38:51 +00:00
let g = self.declare_global(sym, llty);
2018-09-10 14:28:47 +00:00
2021-02-02 14:38:51 +00:00
if !self.tcx.is_reachable_non_generic(def_id) {
unsafe {
llvm::LLVMRustSetVisibility(g, llvm::Visibility::Hidden);
2018-09-10 14:28:47 +00:00
}
}
g
} else {
2021-09-30 17:38:50 +00:00
check_and_apply_linkage(self, fn_attrs, ty, sym, def_id)
2021-02-02 14:38:51 +00:00
};
2018-09-10 14:28:47 +00:00
2021-02-02 14:38:51 +00:00
// Thread-local statics in some other crate need to *always* be linked
// against in a thread-local fashion, so we need to be sure to apply the
// thread-local attribute locally if it was present remotely. If we
// don't do this then linker errors can be generated where the linker
// complains that one object files has a thread local version of the
// symbol and another one doesn't.
if fn_attrs.flags.contains(CodegenFnAttrFlags::THREAD_LOCAL) {
llvm::set_thread_local_mode(g, self.tls_model);
}
2018-09-10 14:28:47 +00:00
2021-02-04 10:17:01 +00:00
if !def_id.is_local() {
2019-12-24 22:38:22 +00:00
let needs_dll_storage_attr = self.use_dll_storage_attrs && !self.tcx.is_foreign_item(def_id) &&
2018-09-10 14:28:47 +00:00
// ThinLTO can't handle this workaround in all cases, so we don't
// emit the attrs. Instead we make them unnecessary by disallowing
2019-02-01 14:15:43 +00:00
// dynamic linking when linker plugin based LTO is enabled.
!self.tcx.sess.opts.cg.linker_plugin_lto.enabled();
2018-09-10 14:28:47 +00:00
// If this assertion triggers, there's something wrong with commandline
// argument validation.
2019-12-24 22:38:22 +00:00
debug_assert!(
!(self.tcx.sess.opts.cg.linker_plugin_lto.enabled()
&& self.tcx.sess.target.is_like_windows
2019-12-24 22:38:22 +00:00
&& self.tcx.sess.opts.cg.prefer_dynamic)
);
2018-09-10 14:28:47 +00:00
if needs_dll_storage_attr {
// This item is external but not foreign, i.e., it originates from an external Rust
2018-09-10 14:28:47 +00:00
// crate. Since we don't know whether this crate will be linked dynamically or
// statically in the final application, we always mark such symbols as 'dllimport'.
// If final linkage happens to be static, we rely on compiler-emitted __imp_ stubs
// to make things work.
//
// However, in some scenarios we defer emission of statics to downstream
// crates, so there are cases where a static with an upstream DefId
// is actually present in the current crate. We can find out via the
// is_codegened_item query.
2018-11-07 10:08:41 +00:00
if !self.tcx.is_codegened_item(def_id) {
2018-09-10 14:28:47 +00:00
unsafe {
llvm::LLVMSetDLLStorageClass(g, llvm::DLLStorageClass::DllImport);
}
}
}
2021-02-02 14:38:51 +00:00
}
2018-09-10 14:28:47 +00:00
if self.use_dll_storage_attrs && self.tcx.is_dllimport_foreign_item(def_id) {
// For foreign (native) libs we know the exact storage type to use.
unsafe {
llvm::LLVMSetDLLStorageClass(g, llvm::DLLStorageClass::DllImport);
rustc: Add `const` globals to the language This change is an implementation of [RFC 69][rfc] which adds a third kind of global to the language, `const`. This global is most similar to what the old `static` was, and if you're unsure about what to use then you should use a `const`. The semantics of these three kinds of globals are: * A `const` does not represent a memory location, but only a value. Constants are translated as rvalues, which means that their values are directly inlined at usage location (similar to a #define in C/C++). Constant values are, well, constant, and can not be modified. Any "modification" is actually a modification to a local value on the stack rather than the actual constant itself. Almost all values are allowed inside constants, whether they have interior mutability or not. There are a few minor restrictions listed in the RFC, but they should in general not come up too often. * A `static` now always represents a memory location (unconditionally). Any references to the same `static` are actually a reference to the same memory location. Only values whose types ascribe to `Sync` are allowed in a `static`. This restriction is in place because many threads may access a `static` concurrently. Lifting this restriction (and allowing unsafe access) is a future extension not implemented at this time. * A `static mut` continues to always represent a memory location. All references to a `static mut` continue to be `unsafe`. This is a large breaking change, and many programs will need to be updated accordingly. A summary of the breaking changes is: * Statics may no longer be used in patterns. Statics now always represent a memory location, which can sometimes be modified. To fix code, repurpose the matched-on-`static` to a `const`. static FOO: uint = 4; match n { FOO => { /* ... */ } _ => { /* ... */ } } change this code to: const FOO: uint = 4; match n { FOO => { /* ... */ } _ => { /* ... */ } } * Statics may no longer refer to other statics by value. Due to statics being able to change at runtime, allowing them to reference one another could possibly lead to confusing semantics. If you are in this situation, use a constant initializer instead. Note, however, that statics may reference other statics by address, however. * Statics may no longer be used in constant expressions, such as array lengths. This is due to the same restrictions as listed above. Use a `const` instead. [breaking-change] [rfc]: https://github.com/rust-lang/rfcs/pull/246
2014-10-06 15:17:01 +00:00
}
2013-06-22 01:46:34 +00:00
}
unsafe {
if self.should_assume_dso_local(g, true) {
2021-05-14 01:47:41 +00:00
llvm::LLVMRustSetDSOLocal(g, true);
}
}
2018-11-07 10:08:41 +00:00
self.instances.borrow_mut().insert(instance, g);
2018-09-10 14:28:47 +00:00
g
}
}
impl<'ll> StaticMethods for CodegenCx<'ll, '_> {
2019-12-24 22:38:22 +00:00
fn static_addr_of(&self, cv: &'ll Value, align: Align, kind: Option<&str>) -> &'ll Value {
if let Some(&gv) = self.const_globals.borrow().get(&cv) {
unsafe {
// Upgrade the alignment in cases where the same constant is used with different
// alignment requirements
let llalign = align.bytes() as u32;
if llalign > llvm::LLVMGetAlignment(gv) {
llvm::LLVMSetAlignment(gv, llalign);
}
}
return gv;
}
let gv = self.static_addr_of_mut(cv, align, kind);
unsafe {
llvm::LLVMSetGlobalConstant(gv, True);
}
self.const_globals.borrow_mut().insert(cv, gv);
gv
}
2018-09-10 14:28:47 +00:00
2019-12-24 22:38:22 +00:00
fn codegen_static(&self, def_id: DefId, is_mutable: bool) {
2018-09-10 14:28:47 +00:00
unsafe {
2018-11-07 10:08:41 +00:00
let attrs = self.tcx.codegen_fn_attrs(def_id);
2018-09-10 14:28:47 +00:00
2022-02-18 23:48:49 +00:00
let Ok((v, alloc)) = codegen_static_initializer(self, def_id) else {
2018-09-10 14:28:47 +00:00
// Error has already been reported
2022-02-18 23:48:49 +00:00
return;
2018-09-10 14:28:47 +00:00
};
let alloc = alloc.inner();
2018-09-10 14:28:47 +00:00
2018-11-07 10:08:41 +00:00
let g = self.get_static(def_id);
2018-09-10 14:28:47 +00:00
// boolean SSA values are i1, but they have to be stored in i8 slots,
// otherwise some LLVM optimization passes don't work as expected
let mut val_llty = self.val_ty(v);
let v = if val_llty == self.type_i1() {
val_llty = self.type_i8();
llvm::LLVMConstZExt(v, val_llty)
} else {
v
};
let instance = Instance::mono(self.tcx, def_id);
let ty = instance.ty(self.tcx, ty::ParamEnv::reveal_all());
2018-09-13 12:58:19 +00:00
let llty = self.layout_of(ty).llvm_type(self);
2018-09-10 14:28:47 +00:00
let g = if val_llty == llty {
g
} else {
// If we created the global with the wrong type,
// correct the type.
let name = llvm::get_value_name(g).to_vec();
llvm::set_value_name(g, b"");
2018-09-10 14:28:47 +00:00
let linkage = llvm::LLVMRustGetLinkage(g);
let visibility = llvm::LLVMRustGetVisibility(g);
let new_g = llvm::LLVMRustGetOrInsertGlobal(
2019-12-24 22:38:22 +00:00
self.llmod,
name.as_ptr().cast(),
name.len(),
val_llty,
);
2018-09-10 14:28:47 +00:00
llvm::LLVMRustSetLinkage(new_g, linkage);
llvm::LLVMRustSetVisibility(new_g, visibility);
// The old global has had its name removed but is returned by
// get_static since it is in the instance cache. Provide an
// alternative lookup that points to the new global so that
// global_asm! can compute the correct mangled symbol name
// for the global.
self.renamed_statics.borrow_mut().insert(def_id, new_g);
2018-09-10 14:28:47 +00:00
// To avoid breaking any invariants, we leave around the old
// global for the moment; we'll replace all references to it
// with the new global later. (See base::codegen_backend.)
2018-11-07 10:08:41 +00:00
self.statics_to_rauw.borrow_mut().push((g, new_g));
2018-09-10 14:28:47 +00:00
new_g
};
2021-09-30 17:38:50 +00:00
set_global_alignment(self, g, self.align_of(ty));
2018-09-10 14:28:47 +00:00
llvm::LLVMSetInitializer(g, v);
if self.should_assume_dso_local(g, true) {
2021-05-14 01:47:41 +00:00
llvm::LLVMRustSetDSOLocal(g, true);
}
2018-09-10 14:28:47 +00:00
// As an optimization, all shared statics which do not have interior
// mutability are placed into read-only memory.
2020-10-27 01:02:48 +00:00
if !is_mutable && self.type_is_freeze(ty) {
llvm::LLVMSetGlobalConstant(g, llvm::True);
}
debuginfo::build_global_var_di_node(self, def_id, g);
2018-09-10 14:28:47 +00:00
if attrs.flags.contains(CodegenFnAttrFlags::THREAD_LOCAL) {
llvm::set_thread_local_mode(g, self.tls_model);
// Do not allow LLVM to change the alignment of a TLS on macOS.
//
// By default a global's alignment can be freely increased.
// This allows LLVM to generate more performant instructions
// e.g., using load-aligned into a SIMD register.
2018-09-10 14:28:47 +00:00
//
// However, on macOS 10.10 or below, the dynamic linker does not
// respect any alignment given on the TLS (radar 24221680).
// This will violate the alignment assumption, and causing segfault at runtime.
//
// This bug is very easy to trigger. In `println!` and `panic!`,
// the `LOCAL_STDOUT`/`LOCAL_STDERR` handles are stored in a TLS,
// which the values would be `mem::replace`d on initialization.
// The implementation of `mem::replace` will use SIMD
// whenever the size is 32 bytes or higher. LLVM notices SIMD is used
// and tries to align `LOCAL_STDOUT`/`LOCAL_STDERR` to a 32-byte boundary,
// which macOS's dyld disregarded and causing crashes
// (see issues #51794, #51758, #50867, #48866 and #44056).
//
// To workaround the bug, we trick LLVM into not increasing
// the global's alignment by explicitly assigning a section to it
// (equivalent to automatically generating a `#[link_section]` attribute).
// See the comment in the `GlobalValue::canIncreaseAlignment()` function
// of `lib/IR/Globals.cpp` for why this works.
//
// When the alignment is not increased, the optimized `mem::replace`
// will use load-unaligned instructions instead, and thus avoiding the crash.
//
// We could remove this hack whenever we decide to drop macOS 10.10 support.
if self.tcx.sess.target.is_like_osx {
// The `inspect` method is okay here because we checked relocations, and
// because we are doing this access to inspect the final interpreter state
// (not as part of the interpreter execution).
//
// FIXME: This check requires that the (arbitrary) value of undefined bytes
// happens to be zero. Instead, we should only check the value of defined bytes
// and set all undefined bytes to zero if this allocation is headed for the
// BSS.
let all_bytes_are_zero = alloc.relocations().is_empty()
&& alloc
.inspect_with_uninit_and_ptr_outside_interpreter(0..alloc.len())
.iter()
.all(|&byte| byte == 0);
let sect_name = if all_bytes_are_zero {
2021-02-13 11:17:15 +00:00
cstr!("__DATA,__thread_bss")
2018-09-10 14:28:47 +00:00
} else {
2021-02-13 11:17:15 +00:00
cstr!("__DATA,__thread_data")
2018-09-10 14:28:47 +00:00
};
llvm::LLVMSetSection(g, sect_name.as_ptr());
}
}
2018-09-10 14:28:47 +00:00
// Wasm statics with custom link sections get special treatment as they
// go into custom sections of the wasm executable.
if self.tcx.sess.target.is_like_wasm {
2018-09-10 14:28:47 +00:00
if let Some(section) = attrs.link_section {
let section = llvm::LLVMMDStringInContext(
2018-11-07 10:08:41 +00:00
self.llcx,
section.as_str().as_ptr().cast(),
2018-09-10 14:28:47 +00:00
section.as_str().len() as c_uint,
);
assert!(alloc.relocations().is_empty());
// The `inspect` method is okay here because we checked relocations, and
// because we are doing this access to inspect the final interpreter state (not
// as part of the interpreter execution).
2019-12-24 22:38:22 +00:00
let bytes =
alloc.inspect_with_uninit_and_ptr_outside_interpreter(0..alloc.len());
2018-09-10 14:28:47 +00:00
let alloc = llvm::LLVMMDStringInContext(
2018-11-07 10:08:41 +00:00
self.llcx,
bytes.as_ptr().cast(),
bytes.len() as c_uint,
2018-09-10 14:28:47 +00:00
);
let data = [section, alloc];
2018-11-07 10:08:41 +00:00
let meta = llvm::LLVMMDNodeInContext(self.llcx, data.as_ptr(), 2);
2018-09-10 14:28:47 +00:00
llvm::LLVMAddNamedMetadataOperand(
2018-11-07 10:08:41 +00:00
self.llmod,
"wasm.custom_sections\0".as_ptr().cast(),
2018-09-10 14:28:47 +00:00
meta,
);
}
} else {
2021-09-30 17:38:50 +00:00
base::set_link_section(g, attrs);
}
2018-09-10 14:28:47 +00:00
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, which means we are allowed use `llvm.compiler.used` instead of
// `llvm.used` here.
//
// Additionally, https://reviews.llvm.org/D97448 in LLVM 13 started emitting unique
// sections with SHF_GNU_RETAIN flag for llvm.used symbols, which may trigger bugs
// in the handling of `.init_array` (the static constructor list) in versions of
// the gold linker (prior to the one released with binutils 2.36).
//
// However, unconditional use of `llvm.compiler.used` caused a nontrivial amount of
// ecosystem breakage, especially on Mach-O targets. To resolve this, we compile it
// as llvm.compiler.used on ELF targets and llvm.used elsewhere, which should be
// equivalent to how we compiled `#[used]` before LLVM 13, as `llvm.used` and
// `llvm.compiler.used` were treated the same on ELF targets prior in earlier LLVM
// versions (additionally, it seems to be how Clang handles `__attribute__((used))`,
// perhaps for similar compatibility-motivated reasons).
//
// See https://github.com/rust-lang/rust/issues/47384#issuecomment-1019080146 and
// following comments for some discussion of this.
//
// The final wrinkle is it's not really clear how to tell if we're going to output
// ELF, so it's been approximated as "not like wasm, osx, or windows", which is
// not exactly correct, but is pretty close and hopefully handles all the platforms
// platforms where old versions of `ld.gold` are likely to show up.
//
// All this is subject to change in the future. Which is a good thing, because this
// probably should be firmed up somehow!
let seems_like_elf = !(self.tcx.sess.target.is_like_osx
|| self.tcx.sess.target.is_like_windows
|| self.tcx.sess.target.is_like_wasm);
if seems_like_elf {
self.add_compiler_used_global(g);
} else {
self.add_used_global(g);
}
2018-09-10 14:28:47 +00:00
}
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);
}
add an #[used] attribute similar to GCC's __attribute((used))__. This attribute prevents LLVM from optimizing away a non-exported symbol, within a compilation unit (object file), when there are no references to it. This is better explained with an example: ``` #[used] static LIVE: i32 = 0; static REFERENCED: i32 = 0; static DEAD: i32 = 0; fn internal() {} pub fn exported() -> &'static i32 { &REFERENCED } ``` Without optimizations, LLVM pretty much preserves all the static variables and functions within the compilation unit. ``` $ rustc --crate-type=lib --emit=obj symbols.rs && nm -C symbols.o 0000000000000000 t drop::h1be0f8f27a2ba94a 0000000000000000 r symbols::REFERENCED::hb3bdfd46050bc84c 0000000000000000 r symbols::DEAD::hc2ea8f9bd06f380b 0000000000000000 r symbols::LIVE::h0970cf9889edb56e 0000000000000000 T symbols::exported::h6f096c2b1fc292b2 0000000000000000 t symbols::internal::h0ac1aadbc1e3a494 ``` With optimizations, LLVM will drop dead code. Here `internal` is dropped because it's not a exported function/symbol (i.e. not `pub`lic). `DEAD` is dropped for the same reason. `REFERENCED` is preserved, even though it's not exported, because it's referenced by the `exported` function. Finally, `LIVE` survives because of the `#[used]` attribute even though it's not exported or referenced. ``` $ rustc --crate-type=lib -C opt-level=3 --emit=obj symbols.rs && nm -C symbols.o 0000000000000000 r symbols::REFERENCED::hb3bdfd46050bc84c 0000000000000000 r symbols::LIVE::h0970cf9889edb56e 0000000000000000 T symbols::exported::h6f096c2b1fc292b2 ``` Note that the linker knows nothing about `#[used]` and will drop `LIVE` because no other object references to it. ``` $ echo 'fn main() {}' >> symbols.rs $ rustc symbols.rs && nm -C symbols | grep LIVE ``` At this time, `#[used]` only works on `static` variables.
2017-02-20 19:42:47 +00:00
}
}
/// Add a global value to a list to be stored in the `llvm.used` variable, an array of i8*.
fn add_used_global(&self, global: &'ll Value) {
let cast = unsafe { llvm::LLVMConstPointerCast(global, self.type_i8p()) };
self.used_statics.borrow_mut().push(cast);
}
/// Add a global value to a list to be stored in the `llvm.compiler.used` variable,
/// an array of i8*.
fn add_compiler_used_global(&self, global: &'ll Value) {
let cast = unsafe { llvm::LLVMConstPointerCast(global, self.type_i8p()) };
self.compiler_used_statics.borrow_mut().push(cast);
}
}