rustc_codegen_llvm/
allocator.rs

1use libc::c_uint;
2use rustc_ast::expand::allocator::{
3    ALLOCATOR_METHODS, AllocatorKind, AllocatorTy, NO_ALLOC_SHIM_IS_UNSTABLE,
4    alloc_error_handler_name, default_fn_name, global_fn_name,
5};
6use rustc_codegen_ssa::traits::BaseTypeCodegenMethods as _;
7use rustc_middle::bug;
8use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrFlags, CodegenFnAttrs};
9use rustc_middle::ty::TyCtxt;
10use rustc_session::config::{DebugInfo, OomStrategy};
11use rustc_span::sym;
12use rustc_symbol_mangling::mangle_internal_symbol;
13
14use crate::attributes::llfn_attrs_from_instance;
15use crate::builder::SBuilder;
16use crate::declare::declare_simple_fn;
17use crate::llvm::{self, FALSE, FromGeneric, TRUE, Type, Value};
18use crate::{SimpleCx, attributes, debuginfo};
19
20pub(crate) unsafe fn codegen(
21    tcx: TyCtxt<'_>,
22    cx: SimpleCx<'_>,
23    module_name: &str,
24    kind: AllocatorKind,
25    alloc_error_handler_kind: AllocatorKind,
26) {
27    let usize = match tcx.sess.target.pointer_width {
28        16 => cx.type_i16(),
29        32 => cx.type_i32(),
30        64 => cx.type_i64(),
31        tws => bug!("Unsupported target word size for int: {}", tws),
32    };
33    let i8 = cx.type_i8();
34    let i8p = cx.type_ptr();
35
36    if kind == AllocatorKind::Default {
37        for method in ALLOCATOR_METHODS {
38            let mut args = Vec::with_capacity(method.inputs.len());
39            for input in method.inputs.iter() {
40                match input.ty {
41                    AllocatorTy::Layout => {
42                        args.push(usize); // size
43                        args.push(usize); // align
44                    }
45                    AllocatorTy::Ptr => args.push(i8p),
46                    AllocatorTy::Usize => args.push(usize),
47
48                    AllocatorTy::ResultPtr | AllocatorTy::Unit => panic!("invalid allocator arg"),
49                }
50            }
51            let output = match method.output {
52                AllocatorTy::ResultPtr => Some(i8p),
53                AllocatorTy::Unit => None,
54
55                AllocatorTy::Layout | AllocatorTy::Usize | AllocatorTy::Ptr => {
56                    panic!("invalid allocator output")
57                }
58            };
59
60            let from_name = mangle_internal_symbol(tcx, &global_fn_name(method.name));
61            let to_name = mangle_internal_symbol(tcx, &default_fn_name(method.name));
62
63            let alloc_attr_flag = match method.name {
64                sym::alloc => CodegenFnAttrFlags::ALLOCATOR,
65                sym::dealloc => CodegenFnAttrFlags::DEALLOCATOR,
66                sym::realloc => CodegenFnAttrFlags::REALLOCATOR,
67                sym::alloc_zeroed => CodegenFnAttrFlags::ALLOCATOR_ZEROED,
68                _ => unreachable!("Unknown allocator method!"),
69            };
70
71            let mut attrs = CodegenFnAttrs::new();
72            attrs.flags |= alloc_attr_flag;
73            create_wrapper_function(
74                tcx,
75                &cx,
76                &from_name,
77                Some(&to_name),
78                &args,
79                output,
80                false,
81                &attrs,
82            );
83        }
84    }
85
86    // rust alloc error handler
87    create_wrapper_function(
88        tcx,
89        &cx,
90        &mangle_internal_symbol(tcx, "__rust_alloc_error_handler"),
91        Some(&mangle_internal_symbol(tcx, alloc_error_handler_name(alloc_error_handler_kind))),
92        &[usize, usize], // size, align
93        None,
94        true,
95        &CodegenFnAttrs::new(),
96    );
97
98    unsafe {
99        // __rust_alloc_error_handler_should_panic_v2
100        create_const_value_function(
101            tcx,
102            &cx,
103            &mangle_internal_symbol(tcx, OomStrategy::SYMBOL),
104            &i8,
105            &llvm::LLVMConstInt(i8, tcx.sess.opts.unstable_opts.oom.should_panic() as u64, FALSE),
106        );
107
108        // __rust_no_alloc_shim_is_unstable_v2
109        create_wrapper_function(
110            tcx,
111            &cx,
112            &mangle_internal_symbol(tcx, NO_ALLOC_SHIM_IS_UNSTABLE),
113            None,
114            &[],
115            None,
116            false,
117            &CodegenFnAttrs::new(),
118        );
119    }
120
121    if tcx.sess.opts.debuginfo != DebugInfo::None {
122        let dbg_cx = debuginfo::CodegenUnitDebugContext::new(cx.llmod);
123        debuginfo::metadata::build_compile_unit_di_node(tcx, module_name, &dbg_cx);
124        dbg_cx.finalize(tcx.sess);
125    }
126}
127
128fn create_const_value_function(
129    tcx: TyCtxt<'_>,
130    cx: &SimpleCx<'_>,
131    name: &str,
132    output: &Type,
133    value: &Value,
134) {
135    let ty = cx.type_func(&[], output);
136    let llfn = declare_simple_fn(
137        &cx,
138        name,
139        llvm::CallConv::CCallConv,
140        llvm::UnnamedAddr::Global,
141        llvm::Visibility::from_generic(tcx.sess.default_visibility()),
142        ty,
143    );
144
145    attributes::apply_to_llfn(
146        llfn,
147        llvm::AttributePlace::Function,
148        &[llvm::AttributeKind::AlwaysInline.create_attr(cx.llcx)],
149    );
150
151    let llbb = unsafe { llvm::LLVMAppendBasicBlockInContext(cx.llcx, llfn, c"entry".as_ptr()) };
152    let mut bx = SBuilder::build(&cx, llbb);
153    bx.ret(value);
154}
155
156fn create_wrapper_function(
157    tcx: TyCtxt<'_>,
158    cx: &SimpleCx<'_>,
159    from_name: &str,
160    to_name: Option<&str>,
161    args: &[&Type],
162    output: Option<&Type>,
163    no_return: bool,
164    attrs: &CodegenFnAttrs,
165) {
166    let ty = cx.type_func(args, output.unwrap_or_else(|| cx.type_void()));
167    let llfn = declare_simple_fn(
168        &cx,
169        from_name,
170        llvm::CallConv::CCallConv,
171        llvm::UnnamedAddr::Global,
172        llvm::Visibility::from_generic(tcx.sess.default_visibility()),
173        ty,
174    );
175
176    llfn_attrs_from_instance(cx, tcx, llfn, attrs, None);
177
178    let no_return = if no_return {
179        // -> ! DIFlagNoReturn
180        let no_return = llvm::AttributeKind::NoReturn.create_attr(cx.llcx);
181        attributes::apply_to_llfn(llfn, llvm::AttributePlace::Function, &[no_return]);
182        Some(no_return)
183    } else {
184        None
185    };
186
187    let llbb = unsafe { llvm::LLVMAppendBasicBlockInContext(cx.llcx, llfn, c"entry".as_ptr()) };
188    let mut bx = SBuilder::build(&cx, llbb);
189
190    if let Some(to_name) = to_name {
191        let callee = declare_simple_fn(
192            &cx,
193            to_name,
194            llvm::CallConv::CCallConv,
195            llvm::UnnamedAddr::Global,
196            llvm::Visibility::Hidden,
197            ty,
198        );
199        if let Some(no_return) = no_return {
200            // -> ! DIFlagNoReturn
201            attributes::apply_to_llfn(callee, llvm::AttributePlace::Function, &[no_return]);
202        }
203        llvm::set_visibility(callee, llvm::Visibility::Hidden);
204
205        let args = args
206            .iter()
207            .enumerate()
208            .map(|(i, _)| llvm::get_param(llfn, i as c_uint))
209            .collect::<Vec<_>>();
210        let ret = bx.call(ty, callee, &args, None);
211        llvm::LLVMSetTailCall(ret, TRUE);
212        if output.is_some() {
213            bx.ret(ret);
214        } else {
215            bx.ret_void()
216        }
217    } else {
218        assert!(output.is_none());
219        bx.ret_void()
220    }
221}