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 }; 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::>(); 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() } }