In the future this should make it easier to use weak symbols for the allocator shim on platforms that properly support weak symbols. And it would allow reusing the allocator shim code for handling default implementations of the upcoming externally implementable items feature on platforms that don't properly support weak symbols.
181 lines
5.4 KiB
Rust
181 lines
5.4 KiB
Rust
#[cfg(feature = "master")]
|
|
use gccjit::FnAttribute;
|
|
use gccjit::{Context, FunctionType, RValue, ToRValue, Type};
|
|
use rustc_ast::expand::allocator::{
|
|
AllocatorMethod, AllocatorTy, NO_ALLOC_SHIM_IS_UNSTABLE, default_fn_name, global_fn_name,
|
|
};
|
|
use rustc_middle::bug;
|
|
use rustc_middle::ty::TyCtxt;
|
|
use rustc_session::config::OomStrategy;
|
|
use rustc_symbol_mangling::mangle_internal_symbol;
|
|
|
|
use crate::GccContext;
|
|
#[cfg(feature = "master")]
|
|
use crate::base::symbol_visibility_to_gcc;
|
|
|
|
pub(crate) unsafe fn codegen(
|
|
tcx: TyCtxt<'_>,
|
|
mods: &mut GccContext,
|
|
_module_name: &str,
|
|
methods: &[AllocatorMethod],
|
|
) {
|
|
let context = &mods.context;
|
|
let usize = match tcx.sess.target.pointer_width {
|
|
16 => context.new_type::<u16>(),
|
|
32 => context.new_type::<u32>(),
|
|
64 => context.new_type::<u64>(),
|
|
tws => bug!("Unsupported target word size for int: {}", tws),
|
|
};
|
|
let i8 = context.new_type::<i8>();
|
|
let i8p = i8.make_pointer();
|
|
|
|
for method in methods {
|
|
let mut types = Vec::with_capacity(method.inputs.len());
|
|
for input in method.inputs.iter() {
|
|
match input.ty {
|
|
AllocatorTy::Layout => {
|
|
types.push(usize);
|
|
types.push(usize);
|
|
}
|
|
AllocatorTy::Ptr => types.push(i8p),
|
|
AllocatorTy::Usize => types.push(usize),
|
|
|
|
AllocatorTy::Never | AllocatorTy::ResultPtr | AllocatorTy::Unit => {
|
|
panic!("invalid allocator arg")
|
|
}
|
|
}
|
|
}
|
|
let output = match method.output {
|
|
AllocatorTy::ResultPtr => Some(i8p),
|
|
AllocatorTy::Never | 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, context, &from_name, Some(&to_name), &types, output);
|
|
}
|
|
|
|
create_const_value_function(
|
|
tcx,
|
|
context,
|
|
&mangle_internal_symbol(tcx, OomStrategy::SYMBOL),
|
|
i8,
|
|
context.new_rvalue_from_int(i8, tcx.sess.opts.unstable_opts.oom.should_panic() as i32),
|
|
);
|
|
|
|
create_wrapper_function(
|
|
tcx,
|
|
context,
|
|
&mangle_internal_symbol(tcx, NO_ALLOC_SHIM_IS_UNSTABLE),
|
|
None,
|
|
&[],
|
|
None,
|
|
);
|
|
}
|
|
|
|
fn create_const_value_function(
|
|
tcx: TyCtxt<'_>,
|
|
context: &Context<'_>,
|
|
name: &str,
|
|
output: Type<'_>,
|
|
value: RValue<'_>,
|
|
) {
|
|
let func = context.new_function(None, FunctionType::Exported, output, &[], name, false);
|
|
|
|
#[cfg(feature = "master")]
|
|
{
|
|
func.add_attribute(FnAttribute::Visibility(symbol_visibility_to_gcc(
|
|
tcx.sess.default_visibility(),
|
|
)));
|
|
|
|
// FIXME(antoyo): cg_llvm sets AlwaysInline, but AlwaysInline is different in GCC and using
|
|
// it here will causes linking errors when using LTO.
|
|
func.add_attribute(FnAttribute::Inline);
|
|
}
|
|
|
|
if tcx.sess.must_emit_unwind_tables() {
|
|
// TODO(antoyo): emit unwind tables.
|
|
}
|
|
|
|
let block = func.new_block("entry");
|
|
block.end_with_return(None, value);
|
|
}
|
|
|
|
fn create_wrapper_function(
|
|
tcx: TyCtxt<'_>,
|
|
context: &Context<'_>,
|
|
from_name: &str,
|
|
to_name: Option<&str>,
|
|
types: &[Type<'_>],
|
|
output: Option<Type<'_>>,
|
|
) {
|
|
let void = context.new_type::<()>();
|
|
|
|
let args: Vec<_> = types
|
|
.iter()
|
|
.enumerate()
|
|
.map(|(index, typ)| context.new_parameter(None, *typ, format!("param{}", index)))
|
|
.collect();
|
|
let func = context.new_function(
|
|
None,
|
|
FunctionType::Exported,
|
|
output.unwrap_or(void),
|
|
&args,
|
|
from_name,
|
|
false,
|
|
);
|
|
|
|
#[cfg(feature = "master")]
|
|
func.add_attribute(FnAttribute::Visibility(symbol_visibility_to_gcc(
|
|
tcx.sess.default_visibility(),
|
|
)));
|
|
|
|
if tcx.sess.must_emit_unwind_tables() {
|
|
// TODO(antoyo): emit unwind tables.
|
|
}
|
|
|
|
let block = func.new_block("entry");
|
|
|
|
if let Some(to_name) = to_name {
|
|
let args: Vec<_> = types
|
|
.iter()
|
|
.enumerate()
|
|
.map(|(index, typ)| context.new_parameter(None, *typ, format!("param{}", index)))
|
|
.collect();
|
|
let callee = context.new_function(
|
|
None,
|
|
FunctionType::Extern,
|
|
output.unwrap_or(void),
|
|
&args,
|
|
to_name,
|
|
false,
|
|
);
|
|
#[cfg(feature = "master")]
|
|
callee.add_attribute(FnAttribute::Visibility(gccjit::Visibility::Hidden));
|
|
|
|
let args = args
|
|
.iter()
|
|
.enumerate()
|
|
.map(|(i, _)| func.get_param(i as i32).to_rvalue())
|
|
.collect::<Vec<_>>();
|
|
let ret = context.new_call(None, callee, &args);
|
|
//llvm::LLVMSetTailCall(ret, True);
|
|
if output.is_some() {
|
|
block.end_with_return(None, ret);
|
|
} else {
|
|
block.add_eval(None, ret);
|
|
block.end_with_void_return(None);
|
|
}
|
|
} else {
|
|
assert!(output.is_none());
|
|
block.end_with_void_return(None);
|
|
}
|
|
|
|
// TODO(@Commeownist): Check if we need to emit some extra debugging info in certain circumstances
|
|
// as described in https://github.com/rust-lang/rust/commit/77a96ed5646f7c3ee8897693decc4626fe380643
|
|
}
|