Skip to main content

rustc_codegen_llvm/
mono_item.rs

1use std::borrow::Cow;
2use std::ffi::CString;
3
4use rustc_abi::AddressSpace;
5use rustc_codegen_ssa::traits::*;
6use rustc_hir::attrs::Linkage;
7use rustc_hir::def::DefKind;
8use rustc_hir::def_id::{DefId, LOCAL_CRATE};
9use rustc_middle::bug;
10use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrs;
11use rustc_middle::mir::mono::Visibility;
12use rustc_middle::ty::layout::{FnAbiOf, HasTypingEnv, LayoutOf};
13use rustc_middle::ty::{self, Instance, Ty, TypeVisitableExt};
14use rustc_session::config::CrateType;
15use rustc_target::callconv::{FnAbi, PassMode};
16use rustc_target::spec::{Arch, RelocModel};
17use tracing::debug;
18
19use crate::abi::FnAbiLlvmExt;
20use crate::builder::Builder;
21use crate::context::CodegenCx;
22use crate::errors::SymbolAlreadyDefined;
23use crate::type_of::LayoutLlvmExt;
24use crate::{base, llvm};
25
26impl<'tcx> PreDefineCodegenMethods<'tcx> for CodegenCx<'_, 'tcx> {
27    fn predefine_static(
28        &mut self,
29        def_id: DefId,
30        linkage: Linkage,
31        visibility: Visibility,
32        symbol_name: &str,
33    ) {
34        let instance = Instance::mono(self.tcx, def_id);
35        let DefKind::Static { nested, .. } = self.tcx.def_kind(def_id) else { ::rustc_middle::util::bug::bug_fmt(format_args!("impossible case reached"))bug!() };
36        // Nested statics do not have a type, so pick a dummy type and let `codegen_static` figure
37        // out the llvm type from the actual evaluated initializer.
38        let ty =
39            if nested { self.tcx.types.unit } else { instance.ty(self.tcx, self.typing_env()) };
40        let llty = self.layout_of(ty).llvm_type(self);
41
42        let g = self.define_global(symbol_name, llty).unwrap_or_else(|| {
43            self.sess()
44                .dcx()
45                .emit_fatal(SymbolAlreadyDefined { span: self.tcx.def_span(def_id), symbol_name })
46        });
47
48        llvm::set_linkage(g, base::linkage_to_llvm(linkage));
49        self.set_visibility(g, linkage, visibility);
50
51        self.assume_dso_local(g, false);
52
53        let attrs = self.tcx.codegen_instance_attrs(instance.def);
54        self.add_static_aliases(g, &attrs.foreign_item_symbol_aliases);
55
56        self.instances.borrow_mut().insert(instance, g);
57    }
58
59    fn predefine_fn(
60        &mut self,
61        instance: Instance<'tcx>,
62        linkage: Linkage,
63        visibility: Visibility,
64        symbol_name: &str,
65    ) {
66        if !!instance.args.has_infer() {
    ::core::panicking::panic("assertion failed: !instance.args.has_infer()")
};assert!(!instance.args.has_infer());
67
68        let attrs = self.tcx.codegen_instance_attrs(instance.def);
69
70        let lldecl =
71            self.predefine_without_aliases(instance, &attrs, linkage, visibility, symbol_name);
72        self.add_function_aliases(instance, lldecl, &attrs, &attrs.foreign_item_symbol_aliases);
73
74        self.instances.borrow_mut().insert(instance, lldecl);
75    }
76}
77
78impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> {
79    fn predefine_without_aliases(
80        &self,
81        instance: Instance<'tcx>,
82        attrs: &Cow<'_, CodegenFnAttrs>,
83        linkage: Linkage,
84        visibility: Visibility,
85        symbol_name: &str,
86    ) -> &'ll llvm::Value {
87        let fn_abi: &FnAbi<'tcx, Ty<'tcx>> = self.fn_abi_of_instance(instance, ty::List::empty());
88        let lldecl = self.declare_fn(symbol_name, fn_abi, Some(instance));
89        llvm::set_linkage(lldecl, base::linkage_to_llvm(linkage));
90        base::set_link_section(lldecl, attrs);
91        if (linkage == Linkage::LinkOnceODR || linkage == Linkage::WeakODR)
92            && self.tcx.sess.target.supports_comdat()
93        {
94            llvm::SetUniqueComdat(self.llmod, lldecl);
95        }
96        self.set_visibility(lldecl, linkage, visibility);
97
98        {
    use ::tracing::__macro_support::Callsite as _;
    static __CALLSITE: ::tracing::callsite::DefaultCallsite =
        {
            static META: ::tracing::Metadata<'static> =
                {
                    ::tracing_core::metadata::Metadata::new("event compiler/rustc_codegen_llvm/src/mono_item.rs:98",
                        "rustc_codegen_llvm::mono_item", ::tracing::Level::DEBUG,
                        ::tracing_core::__macro_support::Option::Some("compiler/rustc_codegen_llvm/src/mono_item.rs"),
                        ::tracing_core::__macro_support::Option::Some(98u32),
                        ::tracing_core::__macro_support::Option::Some("rustc_codegen_llvm::mono_item"),
                        ::tracing_core::field::FieldSet::new(&["message"],
                            ::tracing_core::callsite::Identifier(&__CALLSITE)),
                        ::tracing::metadata::Kind::EVENT)
                };
            ::tracing::callsite::DefaultCallsite::new(&META)
        };
    let enabled =
        ::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::DEBUG <=
                    ::tracing::level_filters::LevelFilter::current() &&
            {
                let interest = __CALLSITE.interest();
                !interest.is_never() &&
                    ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                        interest)
            };
    if enabled {
        (|value_set: ::tracing::field::ValueSet|
                    {
                        let meta = __CALLSITE.metadata();
                        ::tracing::Event::dispatch(meta, &value_set);
                        ;
                    })({
                #[allow(unused_imports)]
                use ::tracing::field::{debug, display, Value};
                let mut iter = __CALLSITE.metadata().fields().iter();
                __CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
                                    ::tracing::__macro_support::Option::Some(&format_args!("predefine_fn: instance = {0:?}",
                                                    instance) as &dyn Value))])
            });
    } else { ; }
};debug!("predefine_fn: instance = {:?}", instance);
99
100        self.assume_dso_local(lldecl, false);
101
102        lldecl
103    }
104
105    /// LLVM has the concept of an `alias`.
106    /// We need this for the "externally implementable items" feature,
107    /// though it's generally useful.
108    ///
109    /// On macos, though this might be a more general problem, function symbols
110    /// have a fixed target architecture. This is necessary, since macos binaries
111    /// may contain code for both ARM and x86 macs.
112    ///
113    /// LLVM *can* add attributes for target architecture to function symbols,
114    /// cannot do so for statics, but importantly, also cannot for aliases
115    /// *even* when aliases may refer to a function symbol.
116    ///
117    /// This is not a problem: instead of using LLVM aliases, we can just generate
118    /// a new function symbol (with target architecture!) which effectively comes down to:
119    ///
120    /// ```ignore (illustrative example)
121    /// fn alias_name(...args) {
122    ///     original_name(...args)
123    /// }
124    /// ```
125    ///
126    /// That's also an alias.
127    ///
128    /// This does mean that the alias symbol has a different address than the original symbol
129    /// (assuming no optimizations by LLVM occur). This is unacceptable for statics.
130    /// So for statics we do want to use LLVM aliases, which is fine,
131    /// since for those we don't care about target architecture anyway.
132    ///
133    /// So, this function is for static aliases. See [`add_function_aliases`](Self::add_function_aliases) for the alternative.
134    fn add_static_aliases(&self, aliasee: &llvm::Value, aliases: &[(DefId, Linkage, Visibility)]) {
135        let ty = self.get_type_of_global(aliasee);
136
137        for (alias, linkage, visibility) in aliases {
138            let symbol_name = self.tcx.symbol_name(Instance::mono(self.tcx, *alias));
139            {
    use ::tracing::__macro_support::Callsite as _;
    static __CALLSITE: ::tracing::callsite::DefaultCallsite =
        {
            static META: ::tracing::Metadata<'static> =
                {
                    ::tracing_core::metadata::Metadata::new("event compiler/rustc_codegen_llvm/src/mono_item.rs:139",
                        "rustc_codegen_llvm::mono_item", ::tracing::Level::DEBUG,
                        ::tracing_core::__macro_support::Option::Some("compiler/rustc_codegen_llvm/src/mono_item.rs"),
                        ::tracing_core::__macro_support::Option::Some(139u32),
                        ::tracing_core::__macro_support::Option::Some("rustc_codegen_llvm::mono_item"),
                        ::tracing_core::field::FieldSet::new(&["message"],
                            ::tracing_core::callsite::Identifier(&__CALLSITE)),
                        ::tracing::metadata::Kind::EVENT)
                };
            ::tracing::callsite::DefaultCallsite::new(&META)
        };
    let enabled =
        ::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::DEBUG <=
                    ::tracing::level_filters::LevelFilter::current() &&
            {
                let interest = __CALLSITE.interest();
                !interest.is_never() &&
                    ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                        interest)
            };
    if enabled {
        (|value_set: ::tracing::field::ValueSet|
                    {
                        let meta = __CALLSITE.metadata();
                        ::tracing::Event::dispatch(meta, &value_set);
                        ;
                    })({
                #[allow(unused_imports)]
                use ::tracing::field::{debug, display, Value};
                let mut iter = __CALLSITE.metadata().fields().iter();
                __CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
                                    ::tracing::__macro_support::Option::Some(&format_args!("STATIC ALIAS: {0:?} {1:?} {2:?}",
                                                    alias, linkage, visibility) as &dyn Value))])
            });
    } else { ; }
};tracing::debug!("STATIC ALIAS: {alias:?} {linkage:?} {visibility:?}");
140
141            let lldecl = llvm::add_alias(
142                self.llmod,
143                ty,
144                AddressSpace::ZERO,
145                aliasee,
146                &CString::new(symbol_name.name).unwrap(),
147            );
148
149            llvm::set_visibility(lldecl, base::visibility_to_llvm(*visibility));
150            llvm::set_linkage(lldecl, base::linkage_to_llvm(*linkage));
151        }
152    }
153
154    /// See [`add_static_aliases`](Self::add_static_aliases) for docs.
155    fn add_function_aliases(
156        &self,
157        aliasee_instance: Instance<'tcx>,
158        aliasee: &'ll llvm::Value,
159        attrs: &Cow<'_, CodegenFnAttrs>,
160        aliases: &[(DefId, Linkage, Visibility)],
161    ) {
162        for (alias, linkage, visibility) in aliases {
163            let symbol_name = self.tcx.symbol_name(Instance::mono(self.tcx, *alias));
164            {
    use ::tracing::__macro_support::Callsite as _;
    static __CALLSITE: ::tracing::callsite::DefaultCallsite =
        {
            static META: ::tracing::Metadata<'static> =
                {
                    ::tracing_core::metadata::Metadata::new("event compiler/rustc_codegen_llvm/src/mono_item.rs:164",
                        "rustc_codegen_llvm::mono_item", ::tracing::Level::DEBUG,
                        ::tracing_core::__macro_support::Option::Some("compiler/rustc_codegen_llvm/src/mono_item.rs"),
                        ::tracing_core::__macro_support::Option::Some(164u32),
                        ::tracing_core::__macro_support::Option::Some("rustc_codegen_llvm::mono_item"),
                        ::tracing_core::field::FieldSet::new(&["message"],
                            ::tracing_core::callsite::Identifier(&__CALLSITE)),
                        ::tracing::metadata::Kind::EVENT)
                };
            ::tracing::callsite::DefaultCallsite::new(&META)
        };
    let enabled =
        ::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::DEBUG <=
                    ::tracing::level_filters::LevelFilter::current() &&
            {
                let interest = __CALLSITE.interest();
                !interest.is_never() &&
                    ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                        interest)
            };
    if enabled {
        (|value_set: ::tracing::field::ValueSet|
                    {
                        let meta = __CALLSITE.metadata();
                        ::tracing::Event::dispatch(meta, &value_set);
                        ;
                    })({
                #[allow(unused_imports)]
                use ::tracing::field::{debug, display, Value};
                let mut iter = __CALLSITE.metadata().fields().iter();
                __CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
                                    ::tracing::__macro_support::Option::Some(&format_args!("FUNCTION ALIAS: {0:?} {1:?} {2:?}",
                                                    alias, linkage, visibility) as &dyn Value))])
            });
    } else { ; }
};tracing::debug!("FUNCTION ALIAS: {alias:?} {linkage:?} {visibility:?}");
165
166            // predefine another copy of the original instance
167            // with a new symbol name
168            let alias_lldecl = self.predefine_without_aliases(
169                aliasee_instance,
170                attrs,
171                *linkage,
172                *visibility,
173                symbol_name.name,
174            );
175
176            let fn_abi: &FnAbi<'tcx, Ty<'tcx>> =
177                self.fn_abi_of_instance(aliasee_instance, ty::List::empty());
178
179            // both the alias and the aliasee have the same ty
180            let fn_ty = fn_abi.llvm_type(self);
181            let start_llbb = Builder::append_block(self, alias_lldecl, "start");
182            let mut start_bx = Builder::build(self, start_llbb);
183
184            let num_params = llvm::count_params(alias_lldecl);
185            let mut args = Vec::with_capacity(num_params as usize);
186            for index in 0..num_params {
187                args.push(llvm::get_param(alias_lldecl, index));
188            }
189
190            let call = start_bx.call(
191                fn_ty,
192                Some(attrs),
193                Some(fn_abi),
194                aliasee,
195                &args,
196                None,
197                Some(aliasee_instance),
198            );
199
200            match &fn_abi.ret.mode {
201                PassMode::Ignore | PassMode::Indirect { .. } => start_bx.ret_void(),
202                PassMode::Direct(_) | PassMode::Pair { .. } | PassMode::Cast { .. } => {
203                    start_bx.ret(call)
204                }
205            }
206        }
207    }
208
209    /// A definition or declaration can be assumed to be local to a group of
210    /// libraries that form a single DSO or executable.
211    /// Marks the local as DSO if so.
212    pub(crate) fn assume_dso_local(&self, llval: &llvm::Value, is_declaration: bool) -> bool {
213        let assume = self.should_assume_dso_local(llval, is_declaration);
214        if assume {
215            llvm::set_dso_local(llval);
216        }
217        assume
218    }
219
220    fn set_visibility(&self, lldecl: &llvm::Value, linkage: Linkage, visibility: Visibility) {
221        // If we're compiling the compiler-builtins crate, i.e., the equivalent of
222        // compiler-rt, then we want to implicitly compile everything with hidden
223        // visibility as we're going to link this object all over the place but
224        // don't want the symbols to get exported.
225        if linkage != Linkage::Internal && self.tcx.is_compiler_builtins(LOCAL_CRATE) {
226            llvm::set_visibility(lldecl, llvm::Visibility::Hidden);
227        } else {
228            llvm::set_visibility(lldecl, base::visibility_to_llvm(visibility));
229        }
230    }
231
232    fn should_assume_dso_local(&self, llval: &llvm::Value, is_declaration: bool) -> bool {
233        let linkage = llvm::get_linkage(llval);
234        let visibility = llvm::get_visibility(llval);
235
236        if #[allow(non_exhaustive_omitted_patterns)] match linkage {
    llvm::Linkage::InternalLinkage | llvm::Linkage::PrivateLinkage => true,
    _ => false,
}matches!(linkage, llvm::Linkage::InternalLinkage | llvm::Linkage::PrivateLinkage) {
237            return true;
238        }
239
240        if visibility != llvm::Visibility::Default && linkage != llvm::Linkage::ExternalWeakLinkage
241        {
242            return true;
243        }
244
245        // Symbols from executables can't really be imported any further.
246        let all_exe = self.tcx.crate_types().iter().all(|ty| *ty == CrateType::Executable);
247        let is_declaration_for_linker =
248            is_declaration || linkage == llvm::Linkage::AvailableExternallyLinkage;
249        if all_exe && !is_declaration_for_linker {
250            return true;
251        }
252
253        // PowerPC64 prefers TOC indirection to avoid copy relocations.
254        if self.tcx.sess.target.arch == Arch::PowerPC64 {
255            return false;
256        }
257
258        // Match clang by only supporting COFF and ELF for now.
259        if self.tcx.sess.target.is_like_darwin {
260            return false;
261        }
262
263        // With pie relocation model, calls of functions defined in the translation
264        // unit can use copy relocations.
265        if self.tcx.sess.relocation_model() == RelocModel::Pie && !is_declaration {
266            return true;
267        }
268
269        // Thread-local variables generally don't support copy relocations.
270        let is_thread_local_var = llvm::LLVMIsAGlobalVariable(llval)
271            .is_some_and(|v| llvm::LLVMIsThreadLocal(v).is_true());
272        if is_thread_local_var {
273            return false;
274        }
275
276        // Respect the direct-access-external-data to override default behavior if present.
277        if let Some(direct) = self.tcx.sess.direct_access_external_data() {
278            return direct;
279        }
280
281        // Static relocation model should force copy relocations everywhere.
282        self.tcx.sess.relocation_model() == RelocModel::Static
283    }
284}