use std::cell::{Cell, RefCell}; use gccjit::{ Block, Context, CType, Function, FunctionType, LValue, RValue, Struct, Type, }; use rustc_codegen_ssa::base::wants_msvc_seh; use rustc_codegen_ssa::traits::{ BackendTypes, MiscMethods, }; use rustc_data_structures::base_n; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_middle::bug; use rustc_middle::mir::mono::CodegenUnit; use rustc_middle::ty::{self, Instance, ParamEnv, PolyExistentialTraitRef, Ty, TyCtxt}; use rustc_middle::ty::layout::{HasParamEnv, HasTyCtxt, LayoutError, TyAndLayout}; use rustc_session::Session; use rustc_span::{Span, Symbol, DUMMY_SP}; use rustc_target::abi::{HasDataLayout, LayoutOf, PointeeInfo, Size, TargetDataLayout, VariantIdx}; use rustc_target::spec::{HasTargetSpec, Target, TlsModel}; use crate::callee::get_fn; use crate::declare::mangle_name; #[derive(Clone)] pub struct FuncSig<'gcc> { pub params: Vec>, pub return_type: Type<'gcc>, } pub struct CodegenCx<'gcc, 'tcx> { pub check_overflow: bool, pub codegen_unit: &'tcx CodegenUnit<'tcx>, pub context: &'gcc Context<'gcc>, // TODO: First set it to a dummy block to avoid using Option? pub current_block: RefCell>>, pub current_func: RefCell>>, pub normal_function_addresses: RefCell>>, /// The function where globals are initialized. pub global_init_func: Function<'gcc>, pub global_init_block: Block<'gcc>, pub functions: RefCell>>, pub tls_model: gccjit::TlsModel, pub bool_type: Type<'gcc>, pub i8_type: Type<'gcc>, pub i16_type: Type<'gcc>, pub i32_type: Type<'gcc>, pub i64_type: Type<'gcc>, pub i128_type: Type<'gcc>, pub isize_type: Type<'gcc>, pub u8_type: Type<'gcc>, pub u16_type: Type<'gcc>, pub u32_type: Type<'gcc>, pub u64_type: Type<'gcc>, pub u128_type: Type<'gcc>, pub usize_type: Type<'gcc>, pub int_type: Type<'gcc>, pub uint_type: Type<'gcc>, pub long_type: Type<'gcc>, pub ulong_type: Type<'gcc>, pub ulonglong_type: Type<'gcc>, pub sizet_type: Type<'gcc>, pub float_type: Type<'gcc>, pub double_type: Type<'gcc>, pub linkage: Cell, pub scalar_types: RefCell, Type<'gcc>>>, pub types: RefCell, Option), Type<'gcc>>>, pub tcx: TyCtxt<'tcx>, pub struct_types: RefCell>, Type<'gcc>>>, pub types_with_fields_to_set: RefCell, (Struct<'gcc>, TyAndLayout<'tcx>)>>, /// Cache instances of monomorphic and polymorphic items pub instances: RefCell, RValue<'gcc>>>, /// Cache generated vtables pub vtables: RefCell, Option>), RValue<'gcc>>>, /// Cache of emitted const globals (value -> global) pub const_globals: RefCell, RValue<'gcc>>>, pub init_argv_var: RefCell, pub argv_initialized: Cell, /// Cache of constant strings, pub const_cstr_cache: RefCell>>, /// Cache of globals. pub globals: RefCell>>, // TODO: remove global_names. pub global_names: RefCell, String>>, /// A counter that is used for generating local symbol names local_gen_sym_counter: Cell, pub global_gen_sym_counter: Cell, eh_personality: Cell>>, pub pointee_infos: RefCell, Size), Option>>, /// NOTE: a hack is used because the rustc API is not suitable to libgccjit and as such, /// `const_undef()` returns struct as pointer so that they can later be assigned a value. /// As such, this set remembers which of these pointers were returned by this function so that /// they can be derefered later. /// FIXME: fix the rustc API to avoid having this hack. pub structs_as_pointer: RefCell>>, /// Store the pointer of different types for safety. /// When casting the values back to their original types, check that they are indeed that type /// with these sets. /// FIXME: remove when the API supports more types. #[cfg(debug_assertions)] lvalues: RefCell>>, } impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> { pub fn new(context: &'gcc Context<'gcc>, codegen_unit: &'tcx CodegenUnit<'tcx>, tcx: TyCtxt<'tcx>) -> Self { let check_overflow = tcx.sess.overflow_checks(); // TODO: fix this mess. libgccjit seems to return random type when using new_int_type(). //let isize_type = context.new_int_type((tcx.data_layout.pointer_size.bits() / 8) as i32, true); let isize_type = context.new_c_type(CType::LongLong); //let usize_type = context.new_int_type((tcx.data_layout.pointer_size.bits() / 8) as i32, false); let usize_type = context.new_c_type(CType::ULongLong); let bool_type = context.new_type::(); let i8_type = context.new_type::(); let i16_type = context.new_type::(); let i32_type = context.new_type::(); let i64_type = context.new_c_type(CType::LongLong); let i128_type = context.new_c_type(CType::Int128t).get_aligned(8); // TODO: should this be hard-coded? let u8_type = context.new_type::(); let u16_type = context.new_type::(); let u32_type = context.new_type::(); let u64_type = context.new_c_type(CType::ULongLong); let u128_type = context.new_c_type(CType::UInt128t).get_aligned(8); // TODO: should this be hard-coded? let tls_model = to_gcc_tls_mode(tcx.sess.tls_model()); let float_type = context.new_type::(); let double_type = context.new_type::(); let int_type = context.new_c_type(CType::Int); let uint_type = context.new_c_type(CType::UInt); let long_type = context.new_c_type(CType::Long); let ulong_type = context.new_c_type(CType::ULong); let ulonglong_type = context.new_c_type(CType::ULongLong); let sizet_type = context.new_c_type(CType::SizeT); assert_eq!(isize_type, i64_type); assert_eq!(usize_type, u64_type); let mut functions = FxHashMap::default(); let builtins = [ "__builtin_unreachable", "abort", "__builtin_expect", "__builtin_add_overflow", "__builtin_mul_overflow", "__builtin_saddll_overflow", /*"__builtin_sadd_overflow",*/ "__builtin_smulll_overflow", /*"__builtin_smul_overflow",*/ "__builtin_ssubll_overflow", /*"__builtin_ssub_overflow",*/ "__builtin_sub_overflow", "__builtin_uaddll_overflow", "__builtin_uadd_overflow", "__builtin_umulll_overflow", "__builtin_umul_overflow", "__builtin_usubll_overflow", "__builtin_usub_overflow", "sqrtf", "sqrt", "__builtin_powif", "__builtin_powi", "sinf", "sin", "cosf", "cos", "powf", "pow", "expf", "exp", "exp2f", "exp2", "logf", "log", "log10f", "log10", "log2f", "log2", "fmaf", "fma", "fabsf", "fabs", "fminf", "fmin", "fmaxf", "fmax", "copysignf", "copysign", "floorf", "floor", "ceilf", "ceil", "truncf", "trunc", "rintf", "rint", "nearbyintf", "nearbyint", "roundf", "round", "__builtin_expect_with_probability", ]; for builtin in builtins.iter() { functions.insert(builtin.to_string(), context.get_builtin_function(builtin)); } let global_init_func = context.new_function(None, FunctionType::Exported, context.new_type::<()>(), &[], &format!("__gccGlobalInit{}", unit_name(&codegen_unit)), false); let global_init_block = global_init_func.new_block("initial"); Self { check_overflow, codegen_unit, context, current_block: RefCell::new(None), current_func: RefCell::new(None), normal_function_addresses: Default::default(), functions: RefCell::new(functions), global_init_func, global_init_block, tls_model, bool_type, i8_type, i16_type, i32_type, i64_type, i128_type, isize_type, usize_type, u8_type, u16_type, u32_type, u64_type, u128_type, int_type, uint_type, long_type, ulong_type, ulonglong_type, sizet_type, float_type, double_type, linkage: Cell::new(FunctionType::Internal), #[cfg(debug_assertions)] lvalues: Default::default(), instances: Default::default(), vtables: Default::default(), const_globals: Default::default(), init_argv_var: RefCell::new(String::new()), argv_initialized: Cell::new(false), const_cstr_cache: Default::default(), global_names: Default::default(), globals: Default::default(), scalar_types: Default::default(), types: Default::default(), tcx, struct_types: Default::default(), types_with_fields_to_set: Default::default(), local_gen_sym_counter: Cell::new(0), global_gen_sym_counter: Cell::new(0), eh_personality: Cell::new(None), pointee_infos: Default::default(), structs_as_pointer: Default::default(), } } pub fn lvalue_to_rvalue(&self, value: LValue<'gcc>) -> RValue<'gcc> { #[cfg(debug_assertions)] self.lvalues.borrow_mut().insert(value); unsafe { std::mem::transmute(value) } } pub fn rvalue_as_function(&self, value: RValue<'gcc>) -> Function<'gcc> { let function: Function<'gcc> = unsafe { std::mem::transmute(value) }; debug_assert!(self.functions.borrow().values().find(|value| **value == function).is_some(), "{:?} ({:?}) is not a function", value, value.get_type()); function } pub fn rvalue_as_lvalue(&self, value: RValue<'gcc>) -> LValue<'gcc> { let lvalue: LValue<'gcc> = unsafe { std::mem::transmute(value) }; //debug_assert!(self.lvalues.borrow().contains(&lvalue), "{:?} is not an lvalue", value); lvalue } pub fn sess(&self) -> &Session { &self.tcx.sess } } impl<'gcc, 'tcx> BackendTypes for CodegenCx<'gcc, 'tcx> { type Value = RValue<'gcc>; type Function = RValue<'gcc>; type BasicBlock = Block<'gcc>; type Type = Type<'gcc>; type Funclet = (); // TODO type DIScope = (); // TODO type DILocation = (); // TODO type DIVariable = (); // TODO } impl<'gcc, 'tcx> MiscMethods<'tcx> for CodegenCx<'gcc, 'tcx> { fn vtables(&self) -> &RefCell, Option>), RValue<'gcc>>> { &self.vtables } fn get_fn(&self, instance: Instance<'tcx>) -> RValue<'gcc> { let func = get_fn(self, instance); *self.current_func.borrow_mut() = Some(self.rvalue_as_function(func)); func } fn get_fn_addr(&self, instance: Instance<'tcx>) -> RValue<'gcc> { //let symbol = self.tcx.symbol_name(instance).name; let func = get_fn(self, instance); let func = self.rvalue_as_function(func); let ptr = func.get_address(None); // TODO: don't do this twice: i.e. in declare_fn and here. //let fn_abi = FnAbi::of_instance(self, instance, &[]); //let (return_type, params, _) = fn_abi.gcc_type(self); // FIXME: the rustc API seems to call get_fn_addr() when not needed (e.g. for FFI). //let pointer_type = ptr.get_type(); self.normal_function_addresses.borrow_mut().insert(ptr); ptr } fn eh_personality(&self) -> RValue<'gcc> { // The exception handling personality function. // // If our compilation unit has the `eh_personality` lang item somewhere // within it, then we just need to codegen that. Otherwise, we're // building an rlib which will depend on some upstream implementation of // this function, so we just codegen a generic reference to it. We don't // specify any of the types for the function, we just make it a symbol // that LLVM can later use. // // Note that MSVC is a little special here in that we don't use the // `eh_personality` lang item at all. Currently LLVM has support for // both Dwarf and SEH unwind mechanisms for MSVC targets and uses the // *name of the personality function* to decide what kind of unwind side // tables/landing pads to emit. It looks like Dwarf is used by default, // injecting a dependency on the `_Unwind_Resume` symbol for resuming // an "exception", but for MSVC we want to force SEH. This means that we // can't actually have the personality function be our standard // `rust_eh_personality` function, but rather we wired it up to the // CRT's custom personality function, which forces LLVM to consider // landing pads as "landing pads for SEH". if let Some(llpersonality) = self.eh_personality.get() { return llpersonality; } let tcx = self.tcx; let llfn = match tcx.lang_items().eh_personality() { Some(def_id) if !wants_msvc_seh(self.sess()) => self.get_fn_addr( ty::Instance::resolve( tcx, ty::ParamEnv::reveal_all(), def_id, tcx.intern_substs(&[]), ) .unwrap().unwrap(), ), _ => { let _name = if wants_msvc_seh(self.sess()) { "__CxxFrameHandler3" } else { "rust_eh_personality" }; //let func = self.declare_func(name, self.type_i32(), &[], true); // FIXME: this hack should not be needed. That will probably be removed when // unwinding support is added. self.context.new_rvalue_from_int(self.int_type, 0) } }; //attributes::apply_target_cpu_attr(self, llfn); self.eh_personality.set(Some(llfn)); llfn } fn sess(&self) -> &Session { &self.tcx.sess } fn check_overflow(&self) -> bool { self.check_overflow } fn codegen_unit(&self) -> &'tcx CodegenUnit<'tcx> { self.codegen_unit } fn used_statics(&self) -> &RefCell>> { unimplemented!(); //&self.used_statics } fn set_frame_pointer_type(&self, _llfn: RValue<'gcc>) { // TODO //attributes::set_frame_pointer_type(self, llfn) } fn apply_target_cpu_attr(&self, _llfn: RValue<'gcc>) { // TODO //attributes::apply_target_cpu_attr(self, llfn) } fn create_used_variable(&self) { unimplemented!(); /*let name = const_cstr!("llvm.used"); let section = const_cstr!("llvm.metadata"); let array = self.const_array(&self.type_ptr_to(self.type_i8()), &*self.used_statics.borrow()); unsafe { let g = llvm::LLVMAddGlobal(self.llmod, self.val_ty(array), name.as_ptr()); llvm::LLVMSetInitializer(g, array); llvm::LLVMRustSetLinkage(g, llvm::Linkage::AppendingLinkage); llvm::LLVMSetSection(g, section.as_ptr()); }*/ } fn declare_c_main(&self, fn_type: Self::Type) -> Option { if self.get_declared_value("main").is_none() { Some(self.declare_cfn("main", fn_type)) } else { // If the symbol already exists, it is an error: for example, the user wrote // #[no_mangle] extern "C" fn main(..) {..} // instead of #[start] None } } } impl<'gcc, 'tcx> HasTyCtxt<'tcx> for CodegenCx<'gcc, 'tcx> { fn tcx(&self) -> TyCtxt<'tcx> { self.tcx } } impl<'gcc, 'tcx> HasDataLayout for CodegenCx<'gcc, 'tcx> { fn data_layout(&self) -> &TargetDataLayout { &self.tcx.data_layout } } impl<'gcc, 'tcx> HasTargetSpec for CodegenCx<'gcc, 'tcx> { fn target_spec(&self) -> &Target { &self.tcx.sess.target } } impl<'gcc, 'tcx> LayoutOf for CodegenCx<'gcc, 'tcx> { type Ty = Ty<'tcx>; type TyAndLayout = TyAndLayout<'tcx>; fn layout_of(&self, ty: Ty<'tcx>) -> Self::TyAndLayout { self.spanned_layout_of(ty, DUMMY_SP) } fn spanned_layout_of(&self, ty: Ty<'tcx>, span: Span) -> Self::TyAndLayout { self.tcx.layout_of(ParamEnv::reveal_all().and(ty)).unwrap_or_else(|e| { if let LayoutError::SizeOverflow(_) = e { self.sess().span_fatal(span, &e.to_string()) } else { bug!("failed to get layout for `{}`: {}", ty, e) } }) } } impl<'tcx, 'gcc> HasParamEnv<'tcx> for CodegenCx<'gcc, 'tcx> { fn param_env(&self) -> ParamEnv<'tcx> { ParamEnv::reveal_all() } } impl<'b, 'tcx> CodegenCx<'b, 'tcx> { /// Generates a new symbol name with the given prefix. This symbol name must /// only be used for definitions with `internal` or `private` linkage. pub fn generate_local_symbol_name(&self, prefix: &str) -> String { let idx = self.local_gen_sym_counter.get(); self.local_gen_sym_counter.set(idx + 1); // Include a '.' character, so there can be no accidental conflicts with // user defined names let mut name = String::with_capacity(prefix.len() + 6); name.push_str(prefix); name.push_str("."); base_n::push_str(idx as u128, base_n::ALPHANUMERIC_ONLY, &mut name); name } } pub fn unit_name<'tcx>(codegen_unit: &CodegenUnit<'tcx>) -> String { let name = &codegen_unit.name().to_string(); mangle_name(&name.replace('-', "_")) } fn to_gcc_tls_mode(tls_model: TlsModel) -> gccjit::TlsModel { match tls_model { TlsModel::GeneralDynamic => gccjit::TlsModel::GlobalDynamic, TlsModel::LocalDynamic => gccjit::TlsModel::LocalDynamic, TlsModel::InitialExec => gccjit::TlsModel::InitialExec, TlsModel::LocalExec => gccjit::TlsModel::LocalExec, } }