mirror of
				https://github.com/rust-lang/rust.git
				synced 2025-10-30 20:44:34 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			200 lines
		
	
	
		
			6.2 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
			
		
		
	
	
			200 lines
		
	
	
		
			6.2 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
| use libc::c_uint;
 | |
| use rustc_ast::expand::allocator::{
 | |
|     ALLOCATOR_METHODS, AllocatorKind, AllocatorTy, NO_ALLOC_SHIM_IS_UNSTABLE,
 | |
|     alloc_error_handler_name, default_fn_name, global_fn_name,
 | |
| };
 | |
| use rustc_codegen_ssa::traits::BaseTypeCodegenMethods as _;
 | |
| use rustc_middle::bug;
 | |
| use rustc_middle::ty::TyCtxt;
 | |
| use rustc_session::config::{DebugInfo, OomStrategy};
 | |
| use rustc_symbol_mangling::mangle_internal_symbol;
 | |
| 
 | |
| use crate::builder::SBuilder;
 | |
| use crate::declare::declare_simple_fn;
 | |
| use crate::llvm::{self, False, True, Type, Value};
 | |
| use crate::{SimpleCx, attributes, debuginfo};
 | |
| 
 | |
| pub(crate) unsafe fn codegen(
 | |
|     tcx: TyCtxt<'_>,
 | |
|     cx: SimpleCx<'_>,
 | |
|     module_name: &str,
 | |
|     kind: AllocatorKind,
 | |
|     alloc_error_handler_kind: AllocatorKind,
 | |
| ) {
 | |
|     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 i8 = cx.type_i8();
 | |
|     let i8p = cx.type_ptr();
 | |
| 
 | |
|     if kind == AllocatorKind::Default {
 | |
|         for method in ALLOCATOR_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::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 from_name = mangle_internal_symbol(tcx, &global_fn_name(method.name));
 | |
|             let to_name = mangle_internal_symbol(tcx, &default_fn_name(method.name));
 | |
| 
 | |
|             create_wrapper_function(tcx, &cx, &from_name, Some(&to_name), &args, output, false);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     // rust alloc error handler
 | |
|     create_wrapper_function(
 | |
|         tcx,
 | |
|         &cx,
 | |
|         &mangle_internal_symbol(tcx, "__rust_alloc_error_handler"),
 | |
|         Some(&mangle_internal_symbol(tcx, alloc_error_handler_name(alloc_error_handler_kind))),
 | |
|         &[usize, usize], // size, align
 | |
|         None,
 | |
|         true,
 | |
|     );
 | |
| 
 | |
|     unsafe {
 | |
|         // __rust_alloc_error_handler_should_panic_v2
 | |
|         create_const_value_function(
 | |
|             tcx,
 | |
|             &cx,
 | |
|             &mangle_internal_symbol(tcx, OomStrategy::SYMBOL),
 | |
|             &i8,
 | |
|             &llvm::LLVMConstInt(i8, tcx.sess.opts.unstable_opts.oom.should_panic() as u64, False),
 | |
|         );
 | |
| 
 | |
|         // __rust_no_alloc_shim_is_unstable_v2
 | |
|         create_wrapper_function(
 | |
|             tcx,
 | |
|             &cx,
 | |
|             &mangle_internal_symbol(tcx, NO_ALLOC_SHIM_IS_UNSTABLE),
 | |
|             None,
 | |
|             &[],
 | |
|             None,
 | |
|             false,
 | |
|         );
 | |
|     }
 | |
| 
 | |
|     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_const_value_function(
 | |
|     tcx: TyCtxt<'_>,
 | |
|     cx: &SimpleCx<'_>,
 | |
|     name: &str,
 | |
|     output: &Type,
 | |
|     value: &Value,
 | |
| ) {
 | |
|     let ty = cx.type_func(&[], output);
 | |
|     let llfn = declare_simple_fn(
 | |
|         &cx,
 | |
|         name,
 | |
|         llvm::CallConv::CCallConv,
 | |
|         llvm::UnnamedAddr::Global,
 | |
|         llvm::Visibility::from_generic(tcx.sess.default_visibility()),
 | |
|         ty,
 | |
|     );
 | |
| 
 | |
|     attributes::apply_to_llfn(
 | |
|         llfn,
 | |
|         llvm::AttributePlace::Function,
 | |
|         &[llvm::AttributeKind::AlwaysInline.create_attr(cx.llcx)],
 | |
|     );
 | |
| 
 | |
|     let llbb = unsafe { llvm::LLVMAppendBasicBlockInContext(cx.llcx, llfn, c"entry".as_ptr()) };
 | |
|     let mut bx = SBuilder::build(&cx, llbb);
 | |
|     bx.ret(value);
 | |
| }
 | |
| 
 | |
| fn create_wrapper_function(
 | |
|     tcx: TyCtxt<'_>,
 | |
|     cx: &SimpleCx<'_>,
 | |
|     from_name: &str,
 | |
|     to_name: Option<&str>,
 | |
|     args: &[&Type],
 | |
|     output: Option<&Type>,
 | |
|     no_return: bool,
 | |
| ) {
 | |
|     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,
 | |
|     );
 | |
|     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.must_emit_unwind_tables() {
 | |
|         let uwtable =
 | |
|             attributes::uwtable_attr(cx.llcx, tcx.sess.opts.unstable_opts.use_sync_unwind);
 | |
|         attributes::apply_to_llfn(llfn, llvm::AttributePlace::Function, &[uwtable]);
 | |
|     }
 | |
| 
 | |
|     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()
 | |
|     }
 | |
| }
 | 
