Skip to main content

rustc_codegen_llvm/
declare.rs

1//! Declare various LLVM values.
2//!
3//! Prefer using functions and methods from this module rather than calling LLVM
4//! functions directly. These functions do some additional work to ensure we do
5//! the right thing given the preconceptions of codegen.
6//!
7//! Some useful guidelines:
8//!
9//! * Use declare_* family of methods if you are declaring, but are not
10//!   interested in defining the Value they return.
11//! * Use define_* family of methods when you might be defining the Value.
12//! * When in doubt, define.
13
14use std::borrow::Borrow;
15
16use itertools::Itertools;
17use rustc_abi::AddressSpace;
18use rustc_codegen_ssa::traits::{MiscCodegenMethods, TypeMembershipCodegenMethods};
19use rustc_data_structures::fx::FxIndexSet;
20use rustc_middle::ty::{Instance, Ty};
21use rustc_sanitizers::{cfi, kcfi};
22use rustc_target::callconv::FnAbi;
23use smallvec::SmallVec;
24use tracing::debug;
25
26use crate::abi::FnAbiLlvmExt;
27use crate::attributes;
28use crate::common::AsCCharPtr;
29use crate::context::{CodegenCx, GenericCx, SCx, SimpleCx};
30use crate::llvm::AttributePlace::Function;
31use crate::llvm::{self, FromGeneric, Type, Value, Visibility};
32
33/// Declare a function with a SimpleCx.
34///
35/// If there’s a value with the same name already declared, the function will
36/// update the declaration and return existing Value instead.
37pub(crate) fn declare_simple_fn<'ll>(
38    cx: &SimpleCx<'ll>,
39    name: &str,
40    callconv: llvm::CallConv,
41    unnamed: llvm::UnnamedAddr,
42    visibility: llvm::Visibility,
43    ty: &'ll Type,
44) -> &'ll Value {
45    {
    use ::tracing::__macro_support::Callsite as _;
    static __CALLSITE: ::tracing::callsite::DefaultCallsite =
        {
            static META: ::tracing::Metadata<'static> =
                {
                    ::tracing_core::metadata::Metadata::new("event /rustc-dev/db8f076d2619ce2585b0380dda06e8da25a40da4/compiler/rustc_codegen_llvm/src/declare.rs:45",
                        "rustc_codegen_llvm::declare", ::tracing::Level::DEBUG,
                        ::tracing_core::__macro_support::Option::Some("/rustc-dev/db8f076d2619ce2585b0380dda06e8da25a40da4/compiler/rustc_codegen_llvm/src/declare.rs"),
                        ::tracing_core::__macro_support::Option::Some(45u32),
                        ::tracing_core::__macro_support::Option::Some("rustc_codegen_llvm::declare"),
                        ::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};
                __CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&format_args!("declare_simple_fn(name={0:?}, ty={1:?})",
                                                    name, ty) as &dyn ::tracing::field::Value))])
            });
    } else { ; }
};debug!("declare_simple_fn(name={:?}, ty={:?})", name, ty);
46    let llfn =
47        unsafe { llvm::LLVMGetOrInsertFunction(cx.llmod, name.as_c_char_ptr(), name.len(), ty) };
48
49    llvm::SetFunctionCallConv(llfn, callconv);
50    llvm::set_unnamed_address(llfn, unnamed);
51    llvm::set_visibility(llfn, visibility);
52
53    llfn
54}
55
56/// Declare a function.
57///
58/// If there’s a value with the same name already declared, the function will
59/// update the declaration and return existing Value instead.
60pub(crate) fn declare_raw_fn<'ll, 'tcx>(
61    cx: &CodegenCx<'ll, 'tcx>,
62    name: &str,
63    callconv: llvm::CallConv,
64    unnamed: llvm::UnnamedAddr,
65    visibility: llvm::Visibility,
66    ty: &'ll Type,
67) -> &'ll Value {
68    {
    use ::tracing::__macro_support::Callsite as _;
    static __CALLSITE: ::tracing::callsite::DefaultCallsite =
        {
            static META: ::tracing::Metadata<'static> =
                {
                    ::tracing_core::metadata::Metadata::new("event /rustc-dev/db8f076d2619ce2585b0380dda06e8da25a40da4/compiler/rustc_codegen_llvm/src/declare.rs:68",
                        "rustc_codegen_llvm::declare", ::tracing::Level::DEBUG,
                        ::tracing_core::__macro_support::Option::Some("/rustc-dev/db8f076d2619ce2585b0380dda06e8da25a40da4/compiler/rustc_codegen_llvm/src/declare.rs"),
                        ::tracing_core::__macro_support::Option::Some(68u32),
                        ::tracing_core::__macro_support::Option::Some("rustc_codegen_llvm::declare"),
                        ::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};
                __CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&format_args!("declare_raw_fn(name={0:?}, ty={1:?})",
                                                    name, ty) as &dyn ::tracing::field::Value))])
            });
    } else { ; }
};debug!("declare_raw_fn(name={:?}, ty={:?})", name, ty);
69    let llfn = declare_simple_fn(cx, name, callconv, unnamed, visibility, ty);
70
71    let mut attrs = SmallVec::<[_; 4]>::new();
72
73    if cx.sess().target.is_like_gpu {
74        // Conservatively apply convergent to all functions in case they may call
75        // a convergent function. Rely on LLVM to optimize away the unnecessary
76        // convergent attributes.
77        attrs.push(llvm::AttributeKind::Convergent.create_attr(cx.llcx));
78    }
79
80    if cx.tcx.sess.opts.cg.no_redzone.unwrap_or(cx.tcx.sess.target.disable_redzone) {
81        attrs.push(llvm::AttributeKind::NoRedZone.create_attr(cx.llcx));
82    }
83
84    attrs.extend(attributes::non_lazy_bind_attr(cx, cx.tcx.sess));
85
86    attributes::apply_to_llfn(llfn, Function, &attrs);
87
88    llfn
89}
90
91impl<'ll, CX: Borrow<SCx<'ll>>> GenericCx<'ll, CX> {
92    /// Declare a global value.
93    ///
94    /// If there’s a value with the same name already declared, the function will
95    /// return its Value instead.
96    pub(crate) fn declare_global(&self, name: &str, ty: &'ll Type) -> &'ll Value {
97        {
    use ::tracing::__macro_support::Callsite as _;
    static __CALLSITE: ::tracing::callsite::DefaultCallsite =
        {
            static META: ::tracing::Metadata<'static> =
                {
                    ::tracing_core::metadata::Metadata::new("event /rustc-dev/db8f076d2619ce2585b0380dda06e8da25a40da4/compiler/rustc_codegen_llvm/src/declare.rs:97",
                        "rustc_codegen_llvm::declare", ::tracing::Level::DEBUG,
                        ::tracing_core::__macro_support::Option::Some("/rustc-dev/db8f076d2619ce2585b0380dda06e8da25a40da4/compiler/rustc_codegen_llvm/src/declare.rs"),
                        ::tracing_core::__macro_support::Option::Some(97u32),
                        ::tracing_core::__macro_support::Option::Some("rustc_codegen_llvm::declare"),
                        ::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};
                __CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&format_args!("declare_global(name={0:?})",
                                                    name) as &dyn ::tracing::field::Value))])
            });
    } else { ; }
};debug!("declare_global(name={:?})", name);
98        unsafe {
99            llvm::LLVMRustGetOrInsertGlobal(
100                (**self).borrow().llmod,
101                name.as_c_char_ptr(),
102                name.len(),
103                ty,
104            )
105        }
106    }
107
108    /// Declare a global value in a specific address space.
109    ///
110    /// If there’s a value with the same name already declared, the function will
111    /// return its Value instead.
112    pub(crate) fn declare_global_in_addrspace(
113        &self,
114        name: &str,
115        ty: &'ll Type,
116        addr_space: AddressSpace,
117    ) -> &'ll Value {
118        {
    use ::tracing::__macro_support::Callsite as _;
    static __CALLSITE: ::tracing::callsite::DefaultCallsite =
        {
            static META: ::tracing::Metadata<'static> =
                {
                    ::tracing_core::metadata::Metadata::new("event /rustc-dev/db8f076d2619ce2585b0380dda06e8da25a40da4/compiler/rustc_codegen_llvm/src/declare.rs:118",
                        "rustc_codegen_llvm::declare", ::tracing::Level::DEBUG,
                        ::tracing_core::__macro_support::Option::Some("/rustc-dev/db8f076d2619ce2585b0380dda06e8da25a40da4/compiler/rustc_codegen_llvm/src/declare.rs"),
                        ::tracing_core::__macro_support::Option::Some(118u32),
                        ::tracing_core::__macro_support::Option::Some("rustc_codegen_llvm::declare"),
                        ::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};
                __CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&format_args!("declare_global(name={0:?}, addrspace={1:?})",
                                                    name, addr_space) as &dyn ::tracing::field::Value))])
            });
    } else { ; }
};debug!("declare_global(name={name:?}, addrspace={addr_space:?})");
119        unsafe {
120            llvm::LLVMRustGetOrInsertGlobalInAddrspace(
121                (**self).borrow().llmod,
122                name.as_c_char_ptr(),
123                name.len(),
124                ty,
125                addr_space.0,
126            )
127        }
128    }
129}
130
131impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> {
132    /// Declare a C ABI function.
133    ///
134    /// Only use this for foreign function ABIs and glue. For Rust functions use
135    /// `declare_fn` instead.
136    ///
137    /// If there’s a value with the same name already declared, the function will
138    /// update the declaration and return existing Value instead.
139    pub(crate) fn declare_cfn(
140        &self,
141        name: &str,
142        unnamed: llvm::UnnamedAddr,
143        fn_type: &'ll Type,
144    ) -> &'ll Value {
145        // Visibility should always be default for declarations, otherwise the linker may report an
146        // error.
147        declare_raw_fn(self, name, llvm::CCallConv, unnamed, Visibility::Default, fn_type)
148    }
149
150    /// Declare an entry Function
151    ///
152    /// The ABI of this function can change depending on the target (although for now the same as
153    /// `declare_cfn`)
154    ///
155    /// If there’s a value with the same name already declared, the function will
156    /// update the declaration and return existing Value instead.
157    pub(crate) fn declare_entry_fn(
158        &self,
159        name: &str,
160        callconv: llvm::CallConv,
161        unnamed: llvm::UnnamedAddr,
162        fn_type: &'ll Type,
163    ) -> &'ll Value {
164        let visibility = Visibility::from_generic(self.tcx.sess.default_visibility());
165        declare_raw_fn(self, name, callconv, unnamed, visibility, fn_type)
166    }
167
168    /// Declare a Rust function.
169    ///
170    /// If there’s a value with the same name already declared, the function will
171    /// update the declaration and return existing Value instead.
172    pub(crate) fn declare_fn(
173        &self,
174        name: &str,
175        fn_abi: &FnAbi<'tcx, Ty<'tcx>>,
176        instance: Option<Instance<'tcx>>,
177    ) -> &'ll Value {
178        {
    use ::tracing::__macro_support::Callsite as _;
    static __CALLSITE: ::tracing::callsite::DefaultCallsite =
        {
            static META: ::tracing::Metadata<'static> =
                {
                    ::tracing_core::metadata::Metadata::new("event /rustc-dev/db8f076d2619ce2585b0380dda06e8da25a40da4/compiler/rustc_codegen_llvm/src/declare.rs:178",
                        "rustc_codegen_llvm::declare", ::tracing::Level::DEBUG,
                        ::tracing_core::__macro_support::Option::Some("/rustc-dev/db8f076d2619ce2585b0380dda06e8da25a40da4/compiler/rustc_codegen_llvm/src/declare.rs"),
                        ::tracing_core::__macro_support::Option::Some(178u32),
                        ::tracing_core::__macro_support::Option::Some("rustc_codegen_llvm::declare"),
                        ::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};
                __CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&format_args!("declare_rust_fn(name={0:?}, fn_abi={1:?})",
                                                    name, fn_abi) as &dyn ::tracing::field::Value))])
            });
    } else { ; }
};debug!("declare_rust_fn(name={:?}, fn_abi={:?})", name, fn_abi);
179
180        // Function addresses in Rust are never significant, allowing functions to
181        // be merged.
182        let llfn = declare_raw_fn(
183            self,
184            name,
185            fn_abi.llvm_cconv(self),
186            llvm::UnnamedAddr::Global,
187            llvm::Visibility::Default,
188            fn_abi.llvm_type(self),
189        );
190        fn_abi.apply_attrs_llfn(self, llfn, instance);
191
192        if self.tcx.sess.is_sanitizer_cfi_enabled() {
193            if let Some(instance) = instance {
194                let mut typeids = FxIndexSet::default();
195                for options in [
196                    cfi::TypeIdOptions::GENERALIZE_POINTERS,
197                    cfi::TypeIdOptions::NORMALIZE_INTEGERS,
198                    cfi::TypeIdOptions::USE_CONCRETE_SELF,
199                ]
200                .into_iter()
201                .powerset()
202                .map(cfi::TypeIdOptions::from_iter)
203                {
204                    let typeid = cfi::typeid_for_instance(self.tcx, instance, options);
205                    if typeids.insert(typeid.clone()) {
206                        self.add_type_metadata(llfn, typeid.as_bytes());
207                    }
208                }
209            } else {
210                for options in [
211                    cfi::TypeIdOptions::GENERALIZE_POINTERS,
212                    cfi::TypeIdOptions::NORMALIZE_INTEGERS,
213                ]
214                .into_iter()
215                .powerset()
216                .map(cfi::TypeIdOptions::from_iter)
217                {
218                    let typeid = cfi::typeid_for_fnabi(self.tcx, fn_abi, options);
219                    self.add_type_metadata(llfn, typeid.as_bytes());
220                }
221            }
222        }
223
224        if self.tcx.sess.is_sanitizer_kcfi_enabled() {
225            // LLVM KCFI does not support multiple !kcfi_type attachments
226            let mut options = kcfi::TypeIdOptions::empty();
227            if self.tcx.sess.is_sanitizer_cfi_generalize_pointers_enabled() {
228                options.insert(kcfi::TypeIdOptions::GENERALIZE_POINTERS);
229            }
230            if self.tcx.sess.is_sanitizer_cfi_normalize_integers_enabled() {
231                options.insert(kcfi::TypeIdOptions::NORMALIZE_INTEGERS);
232            }
233
234            let kcfi_typeid = if let Some(instance) = instance {
235                kcfi::typeid_for_instance(self.tcx, instance, options)
236            } else {
237                kcfi::typeid_for_fnabi(self.tcx, fn_abi, options)
238            };
239            self.set_kcfi_type_metadata(llfn, kcfi_typeid);
240        }
241
242        llfn
243    }
244}
245
246impl<'ll, CX: Borrow<SCx<'ll>>> GenericCx<'ll, CX> {
247    /// Declare a global with an intention to define it.
248    ///
249    /// Use this function when you intend to define a global. This function will
250    /// return `None` if the name already has a definition associated with it. In that
251    /// case an error should be reported to the user, because it usually happens due
252    /// to user’s fault (e.g., misuse of `#[no_mangle]` or `#[export_name]` attributes).
253    pub(crate) fn define_global(&self, name: &str, ty: &'ll Type) -> Option<&'ll Value> {
254        if self.get_defined_value(name).is_some() {
255            None
256        } else {
257            Some(self.declare_global(name, ty))
258        }
259    }
260
261    /// Gets declared value by name.
262    pub(crate) fn get_declared_value(&self, name: &str) -> Option<&'ll Value> {
263        {
    use ::tracing::__macro_support::Callsite as _;
    static __CALLSITE: ::tracing::callsite::DefaultCallsite =
        {
            static META: ::tracing::Metadata<'static> =
                {
                    ::tracing_core::metadata::Metadata::new("event /rustc-dev/db8f076d2619ce2585b0380dda06e8da25a40da4/compiler/rustc_codegen_llvm/src/declare.rs:263",
                        "rustc_codegen_llvm::declare", ::tracing::Level::DEBUG,
                        ::tracing_core::__macro_support::Option::Some("/rustc-dev/db8f076d2619ce2585b0380dda06e8da25a40da4/compiler/rustc_codegen_llvm/src/declare.rs"),
                        ::tracing_core::__macro_support::Option::Some(263u32),
                        ::tracing_core::__macro_support::Option::Some("rustc_codegen_llvm::declare"),
                        ::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};
                __CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&format_args!("get_declared_value(name={0:?})",
                                                    name) as &dyn ::tracing::field::Value))])
            });
    } else { ; }
};debug!("get_declared_value(name={:?})", name);
264        unsafe { llvm::LLVMRustGetNamedValue(self.llmod(), name.as_c_char_ptr(), name.len()) }
265    }
266
267    /// Gets defined or externally defined (AvailableExternally linkage) value by
268    /// name.
269    pub(crate) fn get_defined_value(&self, name: &str) -> Option<&'ll Value> {
270        self.get_declared_value(name).and_then(|val| {
271            let declaration = llvm::is_declaration(val);
272            if !declaration { Some(val) } else { None }
273        })
274    }
275}