trans: use sizing_type_of for interacting with potentially incomplete types.

This commit is contained in:
Eduard Burtescu 2016-02-25 15:35:47 +02:00
parent d492d09f31
commit 1d7c9bd137
3 changed files with 71 additions and 59 deletions

View File

@ -61,6 +61,21 @@ pub enum ArgKind {
pub struct ArgType {
pub kind: ArgKind,
/// Original LLVM type
pub original_ty: Type,
/// Sizing LLVM type (pointers are opaque).
/// Unlike original_ty, this is guaranteed to be complete.
///
/// For example, while we're computing the function pointer type in
/// `struct Foo(fn(Foo));`, `original_ty` is still LLVM's `%Foo = {}`.
/// The field type will likely end up being `void(%Foo)*`, but we cannot
/// use `%Foo` to compute properties (e.g. size and alignment) of `Foo`,
/// until `%Foo` is completed by having all of its field types inserted,
/// so `ty` holds the "sizing type" of `Foo`, which replaces all pointers
/// with opaque ones, resulting in `{i8*}` for `Foo`.
/// ABI-specific logic can then look at the size, alignment and fields of
/// `{i8*}` in order to determine how the argument will be passed.
/// Only later will `original_ty` aka `%Foo` be used in the LLVM function
/// pointer type, without ever having introspected it.
pub ty: Type,
/// Coerced LLVM Type
pub cast: Option<Type>,
@ -71,9 +86,10 @@ pub struct ArgType {
}
impl ArgType {
fn new(ty: Type) -> ArgType {
fn new(original_ty: Type, ty: Type) -> ArgType {
ArgType {
kind: Direct,
original_ty: original_ty,
ty: ty,
cast: None,
pad: None,
@ -90,14 +106,6 @@ impl ArgType {
}
}
fn c_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, ty: Ty<'tcx>) -> Type {
if ty.is_bool() {
Type::i1(cx)
} else {
type_of::type_of(cx, ty)
}
}
/// Metadata describing how the arguments to a native function
/// should be passed in order to respect the native ABI.
///
@ -147,13 +155,6 @@ impl FnType {
Aapcs => llvm::CCallConv,
};
let rty = match sig.output {
ty::FnConverging(ret_ty) if !return_type_is_void(ccx, ret_ty) => {
c_type_of(ccx, ret_ty)
}
_ => Type::void(ccx)
};
let mut inputs = &sig.inputs[..];
let extra_args = if abi == RustCall {
assert!(!sig.variadic && extra_args.is_empty());
@ -173,30 +174,45 @@ impl FnType {
extra_args
};
let arg_of = |ty: Ty<'tcx>| {
if ty.is_bool() {
let llty = Type::i1(ccx);
let mut arg = ArgType::new(llty, llty);
arg.attr = Some(llvm::Attribute::ZExt);
arg
} else {
ArgType::new(type_of::type_of(ccx, ty),
type_of::sizing_type_of(ccx, ty))
}
};
let ret = match sig.output {
ty::FnConverging(ret_ty) if !return_type_is_void(ccx, ret_ty) => {
arg_of(ret_ty)
}
_ => ArgType::new(Type::void(ccx), Type::void(ccx))
};
let mut args = Vec::with_capacity(inputs.len() + extra_args.len());
for ty in inputs.iter().chain(extra_args.iter()) {
let llty = c_type_of(ccx, ty);
let arg = arg_of(ty);
if type_is_fat_ptr(ccx.tcx(), ty) {
args.extend(llty.field_types().into_iter().map(ArgType::new));
let original = arg.original_ty.field_types();
let sizing = arg.ty.field_types();
args.extend(original.into_iter().zip(sizing)
.map(|(o, s)| ArgType::new(o, s)));
} else {
args.push(ArgType::new(llty));
args.push(arg);
}
}
let mut fty = FnType {
args: args,
ret: ArgType::new(rty),
ret: ret,
variadic: sig.variadic,
cconv: cconv
};
// Add ZExt attributes to i1 arguments and returns.
for arg in Some(&mut fty.ret).into_iter().chain(&mut fty.args) {
if arg.ty == Type::i1(ccx) {
arg.attr = Some(llvm::Attribute::ZExt);
}
}
if abi == Rust || abi == RustCall {
let fixup = |arg: &mut ArgType| {
if !arg.ty.is_aggregate() {
@ -213,13 +229,12 @@ impl FnType {
arg.cast = Some(Type::ix(ccx, size * 8));
}
};
if let ty::FnConverging(ret_ty) = sig.output {
if fty.ret.ty != Type::void(ccx) {
// Fat pointers are returned by-value.
if !type_is_fat_ptr(ccx.tcx(), ret_ty) &&
fty.ret.ty != Type::void(ccx) {
if !type_is_fat_ptr(ccx.tcx(), sig.output.unwrap()) {
fixup(&mut fty.ret);
}
};
}
for arg in &mut fty.args {
fixup(arg);
}
@ -256,10 +271,10 @@ impl FnType {
let mut llargument_tys = Vec::new();
let llreturn_ty = if self.ret.is_indirect() {
llargument_tys.push(self.ret.ty.ptr_to());
llargument_tys.push(self.ret.original_ty.ptr_to());
Type::void(ccx)
} else {
self.ret.cast.unwrap_or(self.ret.ty)
self.ret.cast.unwrap_or(self.ret.original_ty)
};
for arg in &self.args {
@ -272,9 +287,9 @@ impl FnType {
}
let llarg_ty = if arg.is_indirect() {
arg.ty.ptr_to()
arg.original_ty.ptr_to()
} else {
arg.cast.unwrap_or(arg.ty)
arg.cast.unwrap_or(arg.original_ty)
};
llargument_tys.push(llarg_ty);

View File

@ -203,7 +203,7 @@ pub fn trans_native_call<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
i,
Value(llarg_rust),
rust_indirect,
arg_ty.ty);
arg_ty);
// Ensure that we always have the Rust value indirectly,
// because it makes bitcasting easier.
@ -261,12 +261,9 @@ pub fn trans_native_call<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
// type to match because some ABIs will use a different type than
// the Rust type. e.g., a {u32,u32} struct could be returned as
// u64.
if fn_type.ret.ty != Type::void(ccx) && !fn_type.ret.is_indirect() {
let llrust_ret_ty = fn_type.ret.ty;
let llforeign_ret_ty = match fn_type.ret.cast {
Some(ty) => ty,
None => fn_type.ret.ty
};
let llrust_ret_ty = fn_type.ret.original_ty;
if llrust_ret_ty != Type::void(ccx) && !fn_type.ret.is_indirect() {
let llforeign_ret_ty = fn_type.ret.cast.unwrap_or(llrust_ret_ty);
debug!("llretptr={:?}", Value(llretptr));
debug!("llforeign_retval={:?}", Value(llforeign_retval));
@ -625,12 +622,9 @@ pub fn trans_rust_fn_with_foreign_abi<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
None, Some(attributes));
// Get the return value where the foreign fn expects it.
let llforeign_ret_ty = match fn_ty.ret.cast {
Some(ty) => ty,
None => fn_ty.ret.ty
};
let llforeign_ret_ty = fn_ty.ret.cast.unwrap_or(fn_ty.ret.original_ty);
match foreign_outptr {
None if fn_ty.ret.ty == Type::void(ccx) => {
None if llforeign_ret_ty == Type::void(ccx) => {
// Function returns `()` or `bot`, which in Rust is the LLVM
// type "{}" but in foreign ABIs is "Void".
builder.ret_void();

View File

@ -108,7 +108,7 @@ pub fn sizing_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, t: Ty<'tcx>) -> Typ
let llsizingty = match t.sty {
_ if !type_is_sized(cx.tcx(), t) => {
Type::struct_(cx, &[Type::i8p(cx), Type::i8p(cx)], false)
Type::struct_(cx, &[Type::i8p(cx), unsized_info_ty(cx, t)], false)
}
ty::TyBool => Type::bool(cx),
@ -123,7 +123,7 @@ pub fn sizing_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, t: Ty<'tcx>) -> Typ
if type_is_sized(cx.tcx(), ty) {
Type::i8p(cx)
} else {
Type::struct_(cx, &[Type::i8p(cx), Type::i8p(cx)], false)
Type::struct_(cx, &[Type::i8p(cx), unsized_info_ty(cx, ty)], false)
}
}
@ -177,6 +177,18 @@ pub fn sizing_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, t: Ty<'tcx>) -> Typ
llsizingty
}
fn unsized_info_ty<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, ty: Ty<'tcx>) -> Type {
let unsized_part = ccx.tcx().struct_tail(ty);
match unsized_part.sty {
ty::TyStr | ty::TyArray(..) | ty::TySlice(_) => {
Type::uint_from_ty(ccx, ast::UintTy::Us)
}
ty::TyTrait(_) => Type::vtable_ptr(ccx),
_ => unreachable!("Unexpected tail in unsized_info_ty: {:?} for ty={:?}",
unsized_part, ty)
}
}
pub fn arg_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, t: Ty<'tcx>) -> Type {
if t.is_bool() {
Type::i1(cx)
@ -283,16 +295,7 @@ pub fn in_memory_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, t: Ty<'tcx>) ->
cx.tn().find_type("str_slice").unwrap()
} else {
let ptr_ty = in_memory_type_of(cx, ty).ptr_to();
let unsized_part = cx.tcx().struct_tail(ty);
let info_ty = match unsized_part.sty {
ty::TyStr | ty::TyArray(..) | ty::TySlice(_) => {
Type::uint_from_ty(cx, ast::UintTy::Us)
}
ty::TyTrait(_) => Type::vtable_ptr(cx),
_ => panic!("Unexpected type returned from \
struct_tail: {:?} for ty={:?}",
unsized_part, ty)
};
let info_ty = unsized_info_ty(cx, ty);
Type::struct_(cx, &[ptr_ty, info_ty], false)
}
} else {