use crate::attributes; use libc::c_uint; use rustc_ast::expand::allocator::{AllocatorKind, AllocatorTy, ALLOCATOR_METHODS}; use rustc_middle::bug; use rustc_middle::ty::TyCtxt; use rustc_session::config::{DebugInfo, OomStrategy}; use rustc_span::symbol::sym; use crate::debuginfo; use crate::llvm::{self, False, True}; use crate::ModuleLlvm; pub(crate) unsafe fn codegen( tcx: TyCtxt<'_>, module_llvm: &mut ModuleLlvm, module_name: &str, kind: AllocatorKind, has_alloc_error_handler: bool, ) { let llcx = &*module_llvm.llcx; let llmod = module_llvm.llmod(); let usize = match tcx.sess.target.pointer_width { 16 => llvm::LLVMInt16TypeInContext(llcx), 32 => llvm::LLVMInt32TypeInContext(llcx), 64 => llvm::LLVMInt64TypeInContext(llcx), tws => bug!("Unsupported target word size for int: {}", tws), }; let i8 = llvm::LLVMInt8TypeInContext(llcx); let i8p = llvm::LLVMPointerType(i8, 0); let void = llvm::LLVMVoidTypeInContext(llcx); for method in ALLOCATOR_METHODS { let mut args = Vec::with_capacity(method.inputs.len()); for ty in method.inputs.iter() { match *ty { AllocatorTy::Layout => { args.push(usize); // size args.push(usize); // align } AllocatorTy::Ptr => args.push(i8p), AllocatorTy::Usize => args.push(usize), AllocatorTy::ResultPtr | AllocatorTy::Unit => panic!("invalid allocator arg"), } } let output = match method.output { AllocatorTy::ResultPtr => Some(i8p), AllocatorTy::Unit => None, AllocatorTy::Layout | AllocatorTy::Usize | AllocatorTy::Ptr => { panic!("invalid allocator output") } }; let ty = llvm::LLVMFunctionType( output.unwrap_or(void), args.as_ptr(), args.len() as c_uint, False, ); let name = format!("__rust_{}", method.name); let llfn = llvm::LLVMRustGetOrInsertFunction(llmod, name.as_ptr().cast(), name.len(), ty); if tcx.sess.target.default_hidden_visibility { llvm::LLVMRustSetVisibility(llfn, llvm::Visibility::Hidden); } if tcx.sess.must_emit_unwind_tables() { let uwtable = attributes::uwtable_attr(llcx); attributes::apply_to_llfn(llfn, llvm::AttributePlace::Function, &[uwtable]); } let callee = kind.fn_name(method.name); let callee = llvm::LLVMRustGetOrInsertFunction(llmod, callee.as_ptr().cast(), callee.len(), ty); llvm::LLVMRustSetVisibility(callee, llvm::Visibility::Hidden); let llbb = llvm::LLVMAppendBasicBlockInContext(llcx, llfn, "entry\0".as_ptr().cast()); let llbuilder = llvm::LLVMCreateBuilderInContext(llcx); llvm::LLVMPositionBuilderAtEnd(llbuilder, llbb); let args = args .iter() .enumerate() .map(|(i, _)| llvm::LLVMGetParam(llfn, i as c_uint)) .collect::>(); let ret = llvm::LLVMRustBuildCall( llbuilder, ty, callee, args.as_ptr(), args.len() as c_uint, None, ); llvm::LLVMSetTailCall(ret, True); if output.is_some() { llvm::LLVMBuildRet(llbuilder, ret); } else { llvm::LLVMBuildRetVoid(llbuilder); } llvm::LLVMDisposeBuilder(llbuilder); } // rust alloc error handler let args = [usize, usize]; // size, align let ty = llvm::LLVMFunctionType(void, args.as_ptr(), args.len() as c_uint, False); let name = "__rust_alloc_error_handler"; let llfn = llvm::LLVMRustGetOrInsertFunction(llmod, name.as_ptr().cast(), name.len(), ty); // -> ! DIFlagNoReturn let no_return = llvm::AttributeKind::NoReturn.create_attr(llcx); attributes::apply_to_llfn(llfn, llvm::AttributePlace::Function, &[no_return]); if tcx.sess.target.default_hidden_visibility { llvm::LLVMRustSetVisibility(llfn, llvm::Visibility::Hidden); } if tcx.sess.must_emit_unwind_tables() { let uwtable = attributes::uwtable_attr(llcx); attributes::apply_to_llfn(llfn, llvm::AttributePlace::Function, &[uwtable]); } let kind = if has_alloc_error_handler { AllocatorKind::Global } else { AllocatorKind::Default }; let callee = kind.fn_name(sym::oom); let callee = llvm::LLVMRustGetOrInsertFunction(llmod, callee.as_ptr().cast(), callee.len(), ty); // -> ! DIFlagNoReturn attributes::apply_to_llfn(callee, llvm::AttributePlace::Function, &[no_return]); llvm::LLVMRustSetVisibility(callee, llvm::Visibility::Hidden); let llbb = llvm::LLVMAppendBasicBlockInContext(llcx, llfn, "entry\0".as_ptr().cast()); let llbuilder = llvm::LLVMCreateBuilderInContext(llcx); llvm::LLVMPositionBuilderAtEnd(llbuilder, llbb); let args = args .iter() .enumerate() .map(|(i, _)| llvm::LLVMGetParam(llfn, i as c_uint)) .collect::>(); let ret = llvm::LLVMRustBuildCall(llbuilder, ty, callee, args.as_ptr(), args.len() as c_uint, None); llvm::LLVMSetTailCall(ret, True); llvm::LLVMBuildRetVoid(llbuilder); llvm::LLVMDisposeBuilder(llbuilder); // __rust_alloc_error_handler_should_panic let name = OomStrategy::SYMBOL; let ll_g = llvm::LLVMRustGetOrInsertGlobal(llmod, name.as_ptr().cast(), name.len(), i8); if tcx.sess.target.default_hidden_visibility { llvm::LLVMRustSetVisibility(ll_g, llvm::Visibility::Hidden); } let val = tcx.sess.opts.unstable_opts.oom.should_panic(); let llval = llvm::LLVMConstInt(i8, val as u64, False); llvm::LLVMSetInitializer(ll_g, llval); if tcx.sess.opts.debuginfo != DebugInfo::None { let dbg_cx = debuginfo::CodegenUnitDebugContext::new(llmod); debuginfo::metadata::build_compile_unit_di_node(tcx, module_name, &dbg_cx); dbg_cx.finalize(tcx.sess); } }