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::ty::TyCtxt;
9use rustc_session::config::{DebugInfo, OomStrategy};
10
11use crate::builder::SBuilder;
12use crate::declare::declare_simple_fn;
13use crate::llvm::{self, False, True, Type};
14use crate::{SimpleCx, attributes, debuginfo};
15
16pub(crate) unsafe fn codegen(
17    tcx: TyCtxt<'_>,
18    cx: SimpleCx<'_>,
19    module_name: &str,
20    kind: AllocatorKind,
21    alloc_error_handler_kind: AllocatorKind,
22) {
23    let usize = match tcx.sess.target.pointer_width {
24        16 => cx.type_i16(),
25        32 => cx.type_i32(),
26        64 => cx.type_i64(),
27        tws => bug!("Unsupported target word size for int: {}", tws),
28    };
29    let i8 = cx.type_i8();
30    let i8p = cx.type_ptr();
31
32    if kind == AllocatorKind::Default {
33        for method in ALLOCATOR_METHODS {
34            let mut args = Vec::with_capacity(method.inputs.len());
35            for input in method.inputs.iter() {
36                match input.ty {
37                    AllocatorTy::Layout => {
38                        args.push(usize); // size
39                        args.push(usize); // align
40                    }
41                    AllocatorTy::Ptr => args.push(i8p),
42                    AllocatorTy::Usize => args.push(usize),
43
44                    AllocatorTy::ResultPtr | AllocatorTy::Unit => panic!("invalid allocator arg"),
45                }
46            }
47            let output = match method.output {
48                AllocatorTy::ResultPtr => Some(i8p),
49                AllocatorTy::Unit => None,
50
51                AllocatorTy::Layout | AllocatorTy::Usize | AllocatorTy::Ptr => {
52                    panic!("invalid allocator output")
53                }
54            };
55
56            let from_name = global_fn_name(method.name);
57            let to_name = default_fn_name(method.name);
58
59            create_wrapper_function(tcx, &cx, &from_name, &to_name, &args, output, false);
60        }
61    }
62
63    // rust alloc error handler
64    create_wrapper_function(
65        tcx,
66        &cx,
67        "__rust_alloc_error_handler",
68        alloc_error_handler_name(alloc_error_handler_kind),
69        &[usize, usize], // size, align
70        None,
71        true,
72    );
73
74    unsafe {
75        // __rust_alloc_error_handler_should_panic
76        let name = OomStrategy::SYMBOL;
77        let ll_g = cx.declare_global(name, i8);
78        llvm::set_visibility(ll_g, llvm::Visibility::from_generic(tcx.sess.default_visibility()));
79        let val = tcx.sess.opts.unstable_opts.oom.should_panic();
80        let llval = llvm::LLVMConstInt(i8, val as u64, False);
81        llvm::set_initializer(ll_g, llval);
82
83        let name = NO_ALLOC_SHIM_IS_UNSTABLE;
84        let ll_g = cx.declare_global(name, i8);
85        llvm::set_visibility(ll_g, llvm::Visibility::from_generic(tcx.sess.default_visibility()));
86        let llval = llvm::LLVMConstInt(i8, 0, False);
87        llvm::set_initializer(ll_g, llval);
88    }
89
90    if tcx.sess.opts.debuginfo != DebugInfo::None {
91        let dbg_cx = debuginfo::CodegenUnitDebugContext::new(cx.llmod);
92        debuginfo::metadata::build_compile_unit_di_node(tcx, module_name, &dbg_cx);
93        dbg_cx.finalize(tcx.sess);
94    }
95}
96
97fn create_wrapper_function(
98    tcx: TyCtxt<'_>,
99    cx: &SimpleCx<'_>,
100    from_name: &str,
101    to_name: &str,
102    args: &[&Type],
103    output: Option<&Type>,
104    no_return: bool,
105) {
106    let ty = cx.type_func(args, output.unwrap_or_else(|| cx.type_void()));
107    let llfn = declare_simple_fn(
108        &cx,
109        from_name,
110        llvm::CallConv::CCallConv,
111        llvm::UnnamedAddr::Global,
112        llvm::Visibility::from_generic(tcx.sess.default_visibility()),
113        ty,
114    );
115    let no_return = if no_return {
116        // -> ! DIFlagNoReturn
117        let no_return = llvm::AttributeKind::NoReturn.create_attr(cx.llcx);
118        attributes::apply_to_llfn(llfn, llvm::AttributePlace::Function, &[no_return]);
119        Some(no_return)
120    } else {
121        None
122    };
123
124    if tcx.sess.must_emit_unwind_tables() {
125        let uwtable =
126            attributes::uwtable_attr(cx.llcx, tcx.sess.opts.unstable_opts.use_sync_unwind);
127        attributes::apply_to_llfn(llfn, llvm::AttributePlace::Function, &[uwtable]);
128    }
129
130    let callee = declare_simple_fn(
131        &cx,
132        to_name,
133        llvm::CallConv::CCallConv,
134        llvm::UnnamedAddr::Global,
135        llvm::Visibility::Hidden,
136        ty,
137    );
138    if let Some(no_return) = no_return {
139        // -> ! DIFlagNoReturn
140        attributes::apply_to_llfn(callee, llvm::AttributePlace::Function, &[no_return]);
141    }
142    llvm::set_visibility(callee, llvm::Visibility::Hidden);
143
144    let llbb = unsafe { llvm::LLVMAppendBasicBlockInContext(cx.llcx, llfn, c"entry".as_ptr()) };
145
146    let mut bx = SBuilder::build(&cx, llbb);
147    let args = args
148        .iter()
149        .enumerate()
150        .map(|(i, _)| llvm::get_param(llfn, i as c_uint))
151        .collect::<Vec<_>>();
152    let ret = bx.call(ty, callee, &args, None);
153    llvm::LLVMSetTailCall(ret, True);
154    if output.is_some() {
155        bx.ret(ret);
156    } else {
157        bx.ret_void()
158    }
159}