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); args.push(usize); }
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 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], None,
94 true,
95 &CodegenFnAttrs::new(),
96 );
97
98 unsafe {
99 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 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 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 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}