rustc_codegen_llvm/
mono_item.rs

1use std::ffi::CString;
2
3use rustc_abi::AddressSpace;
4use rustc_codegen_ssa::traits::*;
5use rustc_hir::attrs::Linkage;
6use rustc_hir::def::DefKind;
7use rustc_hir::def_id::{DefId, LOCAL_CRATE};
8use rustc_middle::bug;
9use rustc_middle::mir::mono::Visibility;
10use rustc_middle::ty::layout::{FnAbiOf, HasTypingEnv, LayoutOf};
11use rustc_middle::ty::{self, Instance, TypeVisitableExt};
12use rustc_session::config::CrateType;
13use rustc_span::Symbol;
14use rustc_target::spec::{Arch, RelocModel};
15use tracing::debug;
16
17use crate::context::CodegenCx;
18use crate::errors::SymbolAlreadyDefined;
19use crate::type_of::LayoutLlvmExt;
20use crate::{base, llvm};
21
22impl<'tcx> PreDefineCodegenMethods<'tcx> for CodegenCx<'_, 'tcx> {
23    fn predefine_static(
24        &mut self,
25        def_id: DefId,
26        linkage: Linkage,
27        visibility: Visibility,
28        symbol_name: &str,
29    ) {
30        let instance = Instance::mono(self.tcx, def_id);
31        let DefKind::Static { nested, .. } = self.tcx.def_kind(def_id) else { bug!() };
32        // Nested statics do not have a type, so pick a dummy type and let `codegen_static` figure
33        // out the llvm type from the actual evaluated initializer.
34        let ty =
35            if nested { self.tcx.types.unit } else { instance.ty(self.tcx, self.typing_env()) };
36        let llty = self.layout_of(ty).llvm_type(self);
37
38        let g = self.define_global(symbol_name, llty).unwrap_or_else(|| {
39            self.sess()
40                .dcx()
41                .emit_fatal(SymbolAlreadyDefined { span: self.tcx.def_span(def_id), symbol_name })
42        });
43
44        llvm::set_linkage(g, base::linkage_to_llvm(linkage));
45        llvm::set_visibility(g, base::visibility_to_llvm(visibility));
46        self.assume_dso_local(g, false);
47
48        let attrs = self.tcx.codegen_instance_attrs(instance.def);
49        self.add_aliases(g, &attrs.foreign_item_symbol_aliases);
50
51        self.instances.borrow_mut().insert(instance, g);
52    }
53
54    fn predefine_fn(
55        &mut self,
56        instance: Instance<'tcx>,
57        linkage: Linkage,
58        visibility: Visibility,
59        symbol_name: &str,
60    ) {
61        assert!(!instance.args.has_infer());
62
63        let fn_abi = self.fn_abi_of_instance(instance, ty::List::empty());
64        let lldecl = self.declare_fn(symbol_name, fn_abi, Some(instance));
65        llvm::set_linkage(lldecl, base::linkage_to_llvm(linkage));
66        let attrs = self.tcx.codegen_instance_attrs(instance.def);
67        base::set_link_section(lldecl, &attrs);
68        if (linkage == Linkage::LinkOnceODR || linkage == Linkage::WeakODR)
69            && self.tcx.sess.target.supports_comdat()
70        {
71            llvm::SetUniqueComdat(self.llmod, lldecl);
72        }
73
74        // If we're compiling the compiler-builtins crate, e.g., the equivalent of
75        // compiler-rt, then we want to implicitly compile everything with hidden
76        // visibility as we're going to link this object all over the place but
77        // don't want the symbols to get exported.
78        if linkage != Linkage::Internal && self.tcx.is_compiler_builtins(LOCAL_CRATE) {
79            llvm::set_visibility(lldecl, llvm::Visibility::Hidden);
80        } else {
81            llvm::set_visibility(lldecl, base::visibility_to_llvm(visibility));
82        }
83
84        debug!("predefine_fn: instance = {:?}", instance);
85
86        self.assume_dso_local(lldecl, false);
87
88        self.add_aliases(lldecl, &attrs.foreign_item_symbol_aliases);
89
90        self.instances.borrow_mut().insert(instance, lldecl);
91    }
92}
93
94impl CodegenCx<'_, '_> {
95    fn add_aliases(&self, aliasee: &llvm::Value, aliases: &[(Symbol, Linkage, Visibility)]) {
96        let ty = self.get_type_of_global(aliasee);
97
98        for (alias, linkage, visibility) in aliases {
99            tracing::debug!("ALIAS: {alias:?} {linkage:?} {visibility:?}");
100            let lldecl = llvm::add_alias(
101                self.llmod,
102                ty,
103                AddressSpace::ZERO,
104                aliasee,
105                &CString::new(alias.as_str()).unwrap(),
106            );
107
108            llvm::set_visibility(lldecl, base::visibility_to_llvm(*visibility));
109            llvm::set_linkage(lldecl, base::linkage_to_llvm(*linkage));
110        }
111    }
112
113    /// Whether a definition or declaration can be assumed to be local to a group of
114    /// libraries that form a single DSO or executable.
115    /// Marks the local as DSO if so.
116    pub(crate) fn assume_dso_local(&self, llval: &llvm::Value, is_declaration: bool) -> bool {
117        let assume = self.should_assume_dso_local(llval, is_declaration);
118        if assume {
119            llvm::set_dso_local(llval);
120        }
121        assume
122    }
123
124    fn should_assume_dso_local(&self, llval: &llvm::Value, is_declaration: bool) -> bool {
125        let linkage = llvm::get_linkage(llval);
126        let visibility = llvm::get_visibility(llval);
127
128        if matches!(linkage, llvm::Linkage::InternalLinkage | llvm::Linkage::PrivateLinkage) {
129            return true;
130        }
131
132        if visibility != llvm::Visibility::Default && linkage != llvm::Linkage::ExternalWeakLinkage
133        {
134            return true;
135        }
136
137        // Symbols from executables can't really be imported any further.
138        let all_exe = self.tcx.crate_types().iter().all(|ty| *ty == CrateType::Executable);
139        let is_declaration_for_linker =
140            is_declaration || linkage == llvm::Linkage::AvailableExternallyLinkage;
141        if all_exe && !is_declaration_for_linker {
142            return true;
143        }
144
145        // PowerPC64 prefers TOC indirection to avoid copy relocations.
146        if matches!(self.tcx.sess.target.arch, Arch::PowerPC64 | Arch::PowerPC64LE) {
147            return false;
148        }
149
150        // Match clang by only supporting COFF and ELF for now.
151        if self.tcx.sess.target.is_like_darwin {
152            return false;
153        }
154
155        // With pie relocation model calls of functions defined in the translation
156        // unit can use copy relocations.
157        if self.tcx.sess.relocation_model() == RelocModel::Pie && !is_declaration {
158            return true;
159        }
160
161        // Thread-local variables generally don't support copy relocations.
162        let is_thread_local_var = llvm::LLVMIsAGlobalVariable(llval)
163            .is_some_and(|v| llvm::LLVMIsThreadLocal(v).is_true());
164        if is_thread_local_var {
165            return false;
166        }
167
168        // Respect the direct-access-external-data to override default behavior if present.
169        if let Some(direct) = self.tcx.sess.direct_access_external_data() {
170            return direct;
171        }
172
173        // Static relocation model should force copy relocations everywhere.
174        self.tcx.sess.relocation_model() == RelocModel::Static
175    }
176}