| use libc::c_uint; | |
| use rustc_ast::expand::allocator::{ | |
| AllocatorMethod, AllocatorTy, NO_ALLOC_SHIM_IS_UNSTABLE, SpecialAllocatorMethod, | |
| default_fn_name, global_fn_name, | |
| }; | |
| use rustc_codegen_ssa::traits::BaseTypeCodegenMethods as _; | |
| use rustc_middle::bug; | |
| use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrFlags, CodegenFnAttrs}; | |
| use rustc_middle::ty::TyCtxt; | |
| use rustc_session::config::DebugInfo; | |
| use rustc_symbol_mangling::mangle_internal_symbol; | |
| use crate::attributes::llfn_attrs_from_instance; | |
| use crate::builder::SBuilder; | |
| use crate::declare::declare_simple_fn; | |
| use crate::llvm::{self, FromGeneric, TRUE, Type}; | |
| use crate::{SimpleCx, attributes, debuginfo}; | |
| pub(crate) unsafe fn codegen( | |
| tcx: TyCtxt<'_>, | |
| cx: SimpleCx<'_>, | |
| module_name: &str, | |
| methods: &[AllocatorMethod], | |
| ) { | |
| let usize = match tcx.sess.target.pointer_width { | |
| 16 => cx.type_i16(), | |
| 32 => cx.type_i32(), | |
| 64 => cx.type_i64(), | |
| tws => bug!("Unsupported target word size for int: {}", tws), | |
| }; | |
| let i8p = cx.type_ptr(); | |
| for method in methods { | |
| let mut args = Vec::with_capacity(method.inputs.len()); | |
| for input in method.inputs.iter() { | |
| match input.ty { | |
| AllocatorTy::Layout => { | |
| args.push(usize); // size | |
| args.push(usize); // align | |
| } | |
| AllocatorTy::Ptr => args.push(i8p), | |
| AllocatorTy::Usize => args.push(usize), | |
| AllocatorTy::Never | AllocatorTy::ResultPtr | AllocatorTy::Unit => { | |
| panic!("invalid allocator arg") | |
| } | |
| } | |
| } | |
| let mut no_return = false; | |
| let output = match method.output { | |
| AllocatorTy::ResultPtr => Some(i8p), | |
| AllocatorTy::Unit => None, | |
| AllocatorTy::Never => { | |
| no_return = true; | |
| None | |
| } | |
| AllocatorTy::Layout | AllocatorTy::Usize | AllocatorTy::Ptr => { | |
| panic!("invalid allocator output") | |
| } | |
| }; | |
| let from_name = mangle_internal_symbol(tcx, &global_fn_name(method.name)); | |
| let to_name = mangle_internal_symbol(tcx, &default_fn_name(method.name)); | |
| let alloc_attr_flag = match method.special { | |
| Some(SpecialAllocatorMethod::Alloc) => CodegenFnAttrFlags::ALLOCATOR, | |
| Some(SpecialAllocatorMethod::Dealloc) => CodegenFnAttrFlags::DEALLOCATOR, | |
| Some(SpecialAllocatorMethod::Realloc) => CodegenFnAttrFlags::REALLOCATOR, | |
| Some(SpecialAllocatorMethod::AllocZeroed) => CodegenFnAttrFlags::ALLOCATOR_ZEROED, | |
| None => CodegenFnAttrFlags::empty(), | |
| }; | |
| let mut attrs = CodegenFnAttrs::new(); | |
| attrs.flags |= alloc_attr_flag; | |
| create_wrapper_function( | |
| tcx, | |
| &cx, | |
| &from_name, | |
| Some(&to_name), | |
| &args, | |
| output, | |
| no_return, | |
| &attrs, | |
| ); | |
| } | |
| // __rust_no_alloc_shim_is_unstable_v2 | |
| create_wrapper_function( | |
| tcx, | |
| &cx, | |
| &mangle_internal_symbol(tcx, NO_ALLOC_SHIM_IS_UNSTABLE), | |
| None, | |
| &[], | |
| None, | |
| false, | |
| &CodegenFnAttrs::new(), | |
| ); | |
| if tcx.sess.opts.debuginfo != DebugInfo::None { | |
| let dbg_cx = debuginfo::CodegenUnitDebugContext::new(cx.llmod); | |
| debuginfo::metadata::build_compile_unit_di_node(tcx, module_name, &dbg_cx); | |
| dbg_cx.finalize(tcx.sess); | |
| } | |
| } | |
| fn create_wrapper_function( | |
| tcx: TyCtxt<'_>, | |
| cx: &SimpleCx<'_>, | |
| from_name: &str, | |
| to_name: Option<&str>, | |
| args: &[&Type], | |
| output: Option<&Type>, | |
| no_return: bool, | |
| attrs: &CodegenFnAttrs, | |
| ) { | |
| let ty = cx.type_func(args, output.unwrap_or_else(|| cx.type_void())); | |
| let llfn = declare_simple_fn( | |
| &cx, | |
| from_name, | |
| llvm::CallConv::CCallConv, | |
| llvm::UnnamedAddr::Global, | |
| llvm::Visibility::from_generic(tcx.sess.default_visibility()), | |
| ty, | |
| ); | |
| llfn_attrs_from_instance(cx, tcx, llfn, attrs, None); | |
| let no_return = if no_return { | |
| // -> ! DIFlagNoReturn | |
| let no_return = llvm::AttributeKind::NoReturn.create_attr(cx.llcx); | |
| attributes::apply_to_llfn(llfn, llvm::AttributePlace::Function, &[no_return]); | |
| Some(no_return) | |
| } else { | |
| None | |
| }; | |
| if tcx.sess.target.is_like_gpu { | |
| // Conservatively apply convergent to all functions in case they may call | |
| // a convergent function. Rely on LLVM to optimize away the unnecessary | |
| // convergent attributes. | |
| attributes::apply_to_llfn( | |
| llfn, | |
| llvm::AttributePlace::Function, | |
| &[llvm::AttributeKind::Convergent.create_attr(cx.llcx)], | |
| ); | |
| } | |
| let llbb = unsafe { llvm::LLVMAppendBasicBlockInContext(cx.llcx, llfn, c"entry".as_ptr()) }; | |
| let mut bx = SBuilder::build(&cx, llbb); | |
| if let Some(to_name) = to_name { | |
| let callee = declare_simple_fn( | |
| &cx, | |
| to_name, | |
| llvm::CallConv::CCallConv, | |
| llvm::UnnamedAddr::Global, | |
| llvm::Visibility::Hidden, | |
| ty, | |
| ); | |
| if let Some(no_return) = no_return { | |
| // -> ! DIFlagNoReturn | |
| attributes::apply_to_llfn(callee, llvm::AttributePlace::Function, &[no_return]); | |
| } | |
| llvm::set_visibility(callee, llvm::Visibility::Hidden); | |
| let args = args | |
| .iter() | |
| .enumerate() | |
| .map(|(i, _)| llvm::get_param(llfn, i as c_uint)) | |
| .collect::<Vec<_>>(); | |
| let ret = bx.call(ty, callee, &args, None); | |
| llvm::LLVMSetTailCall(ret, TRUE); | |
| if output.is_some() { | |
| bx.ret(ret); | |
| } else { | |
| bx.ret_void() | |
| } | |
| } else { | |
| assert!(output.is_none()); | |
| bx.ret_void() | |
| } | |
| } |