1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
//! Handles codegen of callees as well as other call-related
//! things. Callees are a superset of normal rust values and sometimes
//! have different representations. In particular, top-level fn items
//! and methods are represented as just a fn ptr and not a full
//! closure.

use rustc_codegen_ssa::common;
use rustc_middle::ty::layout::{FnAbiOf, HasTyCtxt};
use rustc_middle::ty::{self, Instance, TypeVisitableExt};
use tracing::debug;

use crate::context::CodegenCx;
use crate::llvm;
use crate::value::Value;

/// Codegens a reference to a fn/method item, monomorphizing and
/// inlining as it goes.
///
/// # Parameters
///
/// - `cx`: the crate context
/// - `instance`: the instance to be instantiated
pub(crate) fn get_fn<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>, instance: Instance<'tcx>) -> &'ll Value {
    let tcx = cx.tcx();

    debug!("get_fn(instance={:?})", instance);

    assert!(!instance.args.has_infer());
    assert!(!instance.args.has_escaping_bound_vars());

    if let Some(&llfn) = cx.instances.borrow().get(&instance) {
        return llfn;
    }

    let sym = tcx.symbol_name(instance).name;
    debug!(
        "get_fn({:?}: {:?}) => {}",
        instance,
        instance.ty(cx.tcx(), ty::ParamEnv::reveal_all()),
        sym
    );

    let fn_abi = cx.fn_abi_of_instance(instance, ty::List::empty());

    let llfn = if let Some(llfn) = cx.get_declared_value(sym) {
        llfn
    } else {
        let instance_def_id = instance.def_id();
        let llfn = if tcx.sess.target.arch == "x86"
            && let Some(dllimport) = crate::common::get_dllimport(tcx, instance_def_id, sym)
        {
            // Fix for https://github.com/rust-lang/rust/issues/104453
            // On x86 Windows, LLVM uses 'L' as the prefix for any private
            // global symbols, so when we create an undecorated function symbol
            // that begins with an 'L' LLVM misinterprets that as a private
            // global symbol that it created and so fails the compilation at a
            // later stage since such a symbol must have a definition.
            //
            // To avoid this, we set the Storage Class to "DllImport" so that
            // LLVM will prefix the name with `__imp_`. Ideally, we'd like the
            // existing logic below to set the Storage Class, but it has an
            // exemption for MinGW for backwards compatibility.
            let llfn = cx.declare_fn(
                &common::i686_decorated_name(
                    dllimport,
                    common::is_mingw_gnu_toolchain(&tcx.sess.target),
                    true,
                ),
                fn_abi,
                Some(instance),
            );
            unsafe {
                llvm::LLVMSetDLLStorageClass(llfn, llvm::DLLStorageClass::DllImport);
            }
            llfn
        } else {
            cx.declare_fn(sym, fn_abi, Some(instance))
        };
        debug!("get_fn: not casting pointer!");

        // Apply an appropriate linkage/visibility value to our item that we
        // just declared.
        //
        // This is sort of subtle. Inside our codegen unit we started off
        // compilation by predefining all our own `MonoItem` instances. That
        // is, everything we're codegenning ourselves is already defined. That
        // means that anything we're actually codegenning in this codegen unit
        // will have hit the above branch in `get_declared_value`. As a result,
        // we're guaranteed here that we're declaring a symbol that won't get
        // defined, or in other words we're referencing a value from another
        // codegen unit or even another crate.
        //
        // So because this is a foreign value we blanket apply an external
        // linkage directive because it's coming from a different object file.
        // The visibility here is where it gets tricky. This symbol could be
        // referencing some foreign crate or foreign library (an `extern`
        // block) in which case we want to leave the default visibility. We may
        // also, though, have multiple codegen units. It could be a
        // monomorphization, in which case its expected visibility depends on
        // whether we are sharing generics or not. The important thing here is
        // that the visibility we apply to the declaration is the same one that
        // has been applied to the definition (wherever that definition may be).
        unsafe {
            llvm::LLVMRustSetLinkage(llfn, llvm::Linkage::ExternalLinkage);

            let is_generic =
                instance.args.non_erasable_generics(tcx, instance.def_id()).next().is_some();

            if is_generic {
                // This is a monomorphization. Its expected visibility depends
                // on whether we are in share-generics mode.

                if cx.tcx.sess.opts.share_generics() {
                    // We are in share_generics mode.

                    if let Some(instance_def_id) = instance_def_id.as_local() {
                        // This is a definition from the current crate. If the
                        // definition is unreachable for downstream crates or
                        // the current crate does not re-export generics, the
                        // definition of the instance will have been declared
                        // as `hidden`.
                        if cx.tcx.is_unreachable_local_definition(instance_def_id)
                            || !cx.tcx.local_crate_exports_generics()
                        {
                            llvm::LLVMRustSetVisibility(llfn, llvm::Visibility::Hidden);
                        }
                    } else {
                        // This is a monomorphization of a generic function
                        // defined in an upstream crate.
                        if instance.upstream_monomorphization(tcx).is_some() {
                            // This is instantiated in another crate. It cannot
                            // be `hidden`.
                        } else {
                            // This is a local instantiation of an upstream definition.
                            // If the current crate does not re-export it
                            // (because it is a C library or an executable), it
                            // will have been declared `hidden`.
                            if !cx.tcx.local_crate_exports_generics() {
                                llvm::LLVMRustSetVisibility(llfn, llvm::Visibility::Hidden);
                            }
                        }
                    }
                } else {
                    // When not sharing generics, all instances are in the same
                    // crate and have hidden visibility
                    llvm::LLVMRustSetVisibility(llfn, llvm::Visibility::Hidden);
                }
            } else {
                // This is a non-generic function
                if cx.tcx.is_codegened_item(instance_def_id) {
                    // This is a function that is instantiated in the local crate

                    if instance_def_id.is_local() {
                        // This is function that is defined in the local crate.
                        // If it is not reachable, it is hidden.
                        if !cx.tcx.is_reachable_non_generic(instance_def_id) {
                            llvm::LLVMRustSetVisibility(llfn, llvm::Visibility::Hidden);
                        }
                    } else {
                        // This is a function from an upstream crate that has
                        // been instantiated here. These are always hidden.
                        llvm::LLVMRustSetVisibility(llfn, llvm::Visibility::Hidden);
                    }
                }
            }

            // MinGW: For backward compatibility we rely on the linker to decide whether it
            // should use dllimport for functions.
            if cx.use_dll_storage_attrs
                && let Some(library) = tcx.native_library(instance_def_id)
                && library.kind.is_dllimport()
                && !matches!(tcx.sess.target.env.as_ref(), "gnu" | "uclibc")
            {
                llvm::LLVMSetDLLStorageClass(llfn, llvm::DLLStorageClass::DllImport);
            }

            if cx.should_assume_dso_local(llfn, true) {
                llvm::LLVMRustSetDSOLocal(llfn, true);
            }
        }

        llfn
    };

    cx.instances.borrow_mut().insert(instance, llfn);

    llfn
}