auto merge of #9517 : crabtw/rust/cabi, r=nikomatsakis

I borrow some ideas from clang's ABIInfo.h and TargetInfo.cpp.
LLVMType is replaced with ArgType, which is similar to clang's ABIArgInfo,
and I also merge attrs of FnType into it.

Now ABI implementation doesn't need to insert hidden return pointer
to arg_tys of FnType. Instead it is handled in foreign.rs.

This change also fixes LLVM assertion failure when compiling MIPS target.
This commit is contained in:
bors 2013-10-10 19:56:22 -07:00
commit 0daced63fc
6 changed files with 206 additions and 188 deletions

View File

@ -18,10 +18,62 @@ use middle::trans::cabi_mips;
use middle::trans::type_::Type;
use syntax::abi::{X86, X86_64, Arm, Mips};
#[deriving(Clone, Eq)]
pub enum ArgKind {
/// Pass the argument directly using the normal converted
/// LLVM type or by coercing to another specified type
Direct,
/// Pass the argument indirectly via a hidden pointer
Indirect
}
/// Information about how a specific C type
/// should be passed to or returned from a function
///
/// This is borrowed from clang's ABIInfo.h
#[deriving(Clone)]
pub struct LLVMType {
cast: bool,
ty: Type
pub struct ArgType {
kind: ArgKind,
/// Original LLVM type
ty: Type,
/// Coerced LLVM Type
cast: option::Option<Type>,
/// Dummy argument, which is emitted before the real argument
pad: option::Option<Type>,
/// LLVM attribute of argument
attr: option::Option<Attribute>
}
impl ArgType {
pub fn direct(ty: Type, cast: option::Option<Type>,
pad: option::Option<Type>,
attr: option::Option<Attribute>) -> ArgType {
ArgType {
kind: Direct,
ty: ty,
cast: cast,
pad: pad,
attr: attr
}
}
pub fn indirect(ty: Type, attr: option::Option<Attribute>) -> ArgType {
ArgType {
kind: Indirect,
ty: ty,
cast: option::None,
pad: option::None,
attr: attr
}
}
pub fn is_direct(&self) -> bool {
return self.kind == Direct;
}
pub fn is_indirect(&self) -> bool {
return self.kind == Indirect;
}
}
/// Metadata describing how the arguments to a native function
@ -30,22 +82,11 @@ pub struct LLVMType {
/// I will do my best to describe this structure, but these
/// comments are reverse-engineered and may be inaccurate. -NDM
pub struct FnType {
/// The LLVM types of each argument. If the cast flag is true,
/// then the argument should be cast, typically because the
/// official argument type will be an int and the rust type is i8
/// or something like that.
arg_tys: ~[LLVMType],
/// A list of attributes to be attached to each argument (parallel
/// the `arg_tys` array). If the attribute for a given is Some,
/// then the argument should be passed by reference.
attrs: ~[option::Option<Attribute>],
/// The LLVM types of each argument.
arg_tys: ~[ArgType],
/// LLVM return type.
ret_ty: LLVMType,
/// If true, then an implicit pointer should be added for the result.
sret: bool
ret_ty: ArgType,
}
pub fn compute_abi_info(ccx: &mut CrateContext,

View File

@ -11,14 +11,14 @@
#[allow(non_uppercase_pattern_statics)];
use lib::llvm::{llvm, Integer, Pointer, Float, Double, Struct, Array};
use lib::llvm::{Attribute, StructRetAttribute};
use middle::trans::cabi::{FnType, LLVMType};
use lib::llvm::StructRetAttribute;
use middle::trans::cabi::{FnType, ArgType};
use middle::trans::context::CrateContext;
use middle::trans::type_::Type;
use std::num;
use std::option::{Option, None, Some};
use std::option::{None, Some};
fn align_up_to(off: uint, a: uint) -> uint {
return (off + a - 1u) / a * a;
@ -85,9 +85,9 @@ fn ty_size(ty: Type) -> uint {
}
}
fn classify_ret_ty(ty: Type) -> (LLVMType, Option<Attribute>) {
fn classify_ret_ty(ty: Type) -> ArgType {
if is_reg_ty(ty) {
return (LLVMType { cast: false, ty: ty }, None);
return ArgType::direct(ty, None, None, None);
}
let size = ty_size(ty);
if size <= 4 {
@ -98,14 +98,14 @@ fn classify_ret_ty(ty: Type) -> (LLVMType, Option<Attribute>) {
} else {
Type::i32()
};
return (LLVMType { cast: true, ty: llty }, None);
return ArgType::direct(ty, Some(llty), None, None);
}
(LLVMType { cast: false, ty: ty.ptr_to() }, Some(StructRetAttribute))
ArgType::indirect(ty, Some(StructRetAttribute))
}
fn classify_arg_ty(ty: Type) -> (LLVMType, Option<Attribute>) {
fn classify_arg_ty(ty: Type) -> ArgType {
if is_reg_ty(ty) {
return (LLVMType { cast: false, ty: ty }, None);
return ArgType::direct(ty, None, None, None);
}
let align = ty_align(ty);
let size = ty_size(ty);
@ -114,7 +114,7 @@ fn classify_arg_ty(ty: Type) -> (LLVMType, Option<Attribute>) {
} else {
Type::array(&Type::i64(), ((size + 7) / 8) as u64)
};
(LLVMType { cast: true, ty: llty }, None)
ArgType::direct(ty, Some(llty), None, None)
}
fn is_reg_ty(ty: Type) -> bool {
@ -132,32 +132,19 @@ pub fn compute_abi_info(_ccx: &mut CrateContext,
rty: Type,
ret_def: bool) -> FnType {
let mut arg_tys = ~[];
let mut attrs = ~[];
for &aty in atys.iter() {
let (ty, attr) = classify_arg_ty(aty);
let ty = classify_arg_ty(aty);
arg_tys.push(ty);
attrs.push(attr);
}
let (ret_ty, ret_attr) = if ret_def {
let ret_ty = if ret_def {
classify_ret_ty(rty)
} else {
(LLVMType { cast: false, ty: Type::void() }, None)
ArgType::direct(Type::void(), None, None, None)
};
let mut ret_ty = ret_ty;
let sret = ret_attr.is_some();
if sret {
arg_tys.unshift(ret_ty);
attrs.unshift(ret_attr);
ret_ty = LLVMType { cast: false, ty: Type::void() };
}
return FnType {
arg_tys: arg_tys,
ret_ty: ret_ty,
attrs: attrs,
sret: sret
};
}

View File

@ -12,9 +12,8 @@
use std::libc::c_uint;
use std::num;
use std::vec;
use lib::llvm::{llvm, Integer, Pointer, Float, Double, Struct, Array};
use lib::llvm::{Attribute, StructRetAttribute};
use lib::llvm::StructRetAttribute;
use middle::trans::context::CrateContext;
use middle::trans::context::task_llcx;
use middle::trans::cabi::*;
@ -86,15 +85,15 @@ fn ty_size(ty: Type) -> uint {
}
}
fn classify_ret_ty(ty: Type) -> (LLVMType, Option<Attribute>) {
return if is_reg_ty(ty) {
(LLVMType { cast: false, ty: ty }, None)
fn classify_ret_ty(ty: Type) -> ArgType {
if is_reg_ty(ty) {
ArgType::direct(ty, None, None, None)
} else {
(LLVMType { cast: false, ty: ty.ptr_to() }, Some(StructRetAttribute))
};
ArgType::indirect(ty, Some(StructRetAttribute))
}
}
fn classify_arg_ty(ty: Type, offset: &mut uint) -> (LLVMType, Option<Attribute>) {
fn classify_arg_ty(ty: Type, offset: &mut uint) -> ArgType {
let orig_offset = *offset;
let size = ty_size(ty) * 8;
let mut align = ty_align(ty);
@ -103,20 +102,16 @@ fn classify_arg_ty(ty: Type, offset: &mut uint) -> (LLVMType, Option<Attribute>)
*offset = align_up_to(*offset, align);
*offset += align_up_to(size, align * 8) / 8;
let padding = padding_ty(align, orig_offset);
return if !is_reg_ty(ty) {
(LLVMType {
cast: true,
ty: struct_ty(ty, padding, true)
}, None)
} else if padding.is_some() {
(LLVMType {
cast: true,
ty: struct_ty(ty, padding, false)
}, None)
if is_reg_ty(ty) {
ArgType::direct(ty, None, None, None)
} else {
(LLVMType { cast: false, ty: ty }, None)
};
ArgType::direct(
ty,
Some(struct_ty(ty)),
padding_ty(align, orig_offset),
None
)
}
}
fn is_reg_ty(ty: Type) -> bool {
@ -157,18 +152,9 @@ fn coerce_to_int(size: uint) -> ~[Type] {
args
}
fn struct_ty(ty: Type,
padding: Option<Type>,
coerce: bool) -> Type {
fn struct_ty(ty: Type) -> Type {
let size = ty_size(ty) * 8;
let mut fields = padding.map_default(~[], |p| ~[p]);
if coerce {
fields = vec::append(fields, coerce_to_int(size));
} else {
fields.push(ty);
}
let fields = coerce_to_int(size);
return Type::struct_(fields, false);
}
@ -176,35 +162,23 @@ pub fn compute_abi_info(_ccx: &mut CrateContext,
atys: &[Type],
rty: Type,
ret_def: bool) -> FnType {
let (ret_ty, ret_attr) = if ret_def {
let ret_ty = if ret_def {
classify_ret_ty(rty)
} else {
(LLVMType { cast: false, ty: Type::void() }, None)
ArgType::direct(Type::void(), None, None, None)
};
let mut ret_ty = ret_ty;
let sret = ret_attr.is_some();
let sret = ret_ty.is_indirect();
let mut arg_tys = ~[];
let mut attrs = ~[];
let mut offset = if sret { 4 } else { 0 };
for aty in atys.iter() {
let (ty, attr) = classify_arg_ty(*aty, &mut offset);
let ty = classify_arg_ty(*aty, &mut offset);
arg_tys.push(ty);
attrs.push(attr);
};
if sret {
arg_tys = vec::append(~[ret_ty], arg_tys);
attrs = vec::append(~[ret_attr], attrs);
ret_ty = LLVMType { cast: false, ty: Type::void() };
}
return FnType {
arg_tys: arg_tys,
ret_ty: ret_ty,
attrs: attrs,
sret: sret
};
}

View File

@ -21,16 +21,10 @@ pub fn compute_abi_info(ccx: &mut CrateContext,
rty: Type,
ret_def: bool) -> FnType {
let mut arg_tys = ~[];
let mut attrs = ~[];
let ret_ty;
let sret;
if !ret_def {
ret_ty = LLVMType {
cast: false,
ty: Type::void(),
};
sret = false;
ret_ty = ArgType::direct(Type::void(), None, None, None);
} else if rty.kind() == Struct {
// Returning a structure. Most often, this will use
// a hidden first argument. On some platforms, though,
@ -58,43 +52,22 @@ pub fn compute_abi_info(ccx: &mut CrateContext,
match strategy {
RetValue(t) => {
ret_ty = LLVMType {
cast: true,
ty: t
};
sret = false;
ret_ty = ArgType::direct(rty, Some(t), None, None);
}
RetPointer => {
arg_tys.push(LLVMType {
cast: false,
ty: rty.ptr_to()
});
attrs.push(Some(StructRetAttribute));
ret_ty = LLVMType {
cast: false,
ty: Type::void(),
};
sret = true;
ret_ty = ArgType::indirect(rty, Some(StructRetAttribute));
}
}
} else {
ret_ty = LLVMType {
cast: false,
ty: rty
};
sret = false;
ret_ty = ArgType::direct(rty, None, None, None);
}
for &a in atys.iter() {
arg_tys.push(LLVMType { cast: false, ty: a });
attrs.push(None);
arg_tys.push(ArgType::direct(a, None, None, None));
}
return FnType {
arg_tys: arg_tys,
ret_ty: ret_ty,
attrs: attrs,
sret: sret
};
}

View File

@ -22,8 +22,6 @@ use middle::trans::context::CrateContext;
use middle::trans::type_::Type;
use std::num;
use std::option;
use std::option::Option;
use std::vec;
#[deriving(Clone, Eq)]
@ -340,50 +338,34 @@ pub fn compute_abi_info(_ccx: &mut CrateContext,
ret_def: bool) -> FnType {
fn x86_64_ty(ty: Type,
is_mem_cls: &fn(cls: &[RegClass]) -> bool,
attr: Attribute) -> (LLVMType, Option<Attribute>) {
attr: Attribute) -> ArgType {
let (cast, attr, ty) = if !ty.is_reg_ty() {
if !ty.is_reg_ty() {
let cls = classify_ty(ty);
if is_mem_cls(cls) {
(false, option::Some(attr), ty.ptr_to())
ArgType::indirect(ty, Some(attr))
} else {
(true, option::None, llreg_ty(cls))
ArgType::direct(ty, Some(llreg_ty(cls)), None, None)
}
} else {
(false, option::None, ty)
};
(LLVMType { cast: cast, ty: ty }, attr)
ArgType::direct(ty, None, None, None)
}
}
let mut arg_tys = ~[];
let mut attrs = ~[];
for t in atys.iter() {
let (ty, attr) = x86_64_ty(*t, |cls| cls.is_pass_byval(), ByValAttribute);
let ty = x86_64_ty(*t, |cls| cls.is_pass_byval(), ByValAttribute);
arg_tys.push(ty);
attrs.push(attr);
}
let (ret_ty, ret_attr) = x86_64_ty(rty, |cls| cls.is_ret_bysret(),
StructRetAttribute);
let mut ret_ty = ret_ty;
let sret = ret_attr.is_some();
if sret {
arg_tys = vec::append(~[ret_ty], arg_tys);
ret_ty = LLVMType {
cast: false,
ty: Type::void()
};
attrs = vec::append(~[ret_attr], attrs);
} else if !ret_def {
ret_ty = LLVMType {
cast: false,
ty: Type::void()
};
}
let ret_ty = if ret_def {
x86_64_ty(rty, |cls| cls.is_ret_bysret(), StructRetAttribute)
} else {
ArgType::direct(Type::void(), None, None, None)
};
return FnType {
arg_tys: arg_tys,
ret_ty: ret_ty,
attrs: attrs,
sret: sret
};
}

View File

@ -11,7 +11,7 @@
use back::{link};
use std::libc::c_uint;
use lib::llvm::{ValueRef, Attribute, CallConv, StructRetAttribute};
use lib::llvm::{ValueRef, CallConv, StructRetAttribute};
use lib::llvm::llvm;
use lib;
use middle::trans::machine;
@ -183,8 +183,7 @@ pub fn trans_native_call(bcx: @mut Block,
llsig.llret_ty,
ret_def);
let all_arg_tys: &[cabi::LLVMType] = fn_type.arg_tys;
let all_attributes: &[Option<Attribute>] = fn_type.attrs;
let arg_tys: &[cabi::ArgType] = fn_type.arg_tys;
let mut llargs_foreign = ~[];
@ -192,20 +191,18 @@ pub fn trans_native_call(bcx: @mut Block,
// pointer that Rust gave us. Sometimes we have to bitcast
// because foreign fns return slightly different (but equivalent)
// views on the same type (e.g., i64 in place of {i32,i32}).
let (arg_tys, attributes) = {
if fn_type.sret {
if all_arg_tys[0].cast {
if fn_type.ret_ty.is_indirect() {
match fn_type.ret_ty.cast {
Some(ty) => {
let llcastedretptr =
BitCast(bcx, llretptr, all_arg_tys[0].ty.ptr_to());
BitCast(bcx, llretptr, ty.ptr_to());
llargs_foreign.push(llcastedretptr);
} else {
}
None => {
llargs_foreign.push(llretptr);
}
(all_arg_tys.tail(), all_attributes.tail())
} else {
(all_arg_tys, all_attributes)
}
};
}
for (i, &llarg_rust) in llargs_rust.iter().enumerate() {
let mut llarg_rust = llarg_rust;
@ -231,16 +228,16 @@ pub fn trans_native_call(bcx: @mut Block,
ccx.tn.val_to_str(llarg_rust));
// Check whether we need to do any casting
let foreignarg_ty = arg_tys[i].ty;
if arg_tys[i].cast {
llarg_rust = BitCast(bcx, llarg_rust, foreignarg_ty.ptr_to());
match arg_tys[i].cast {
Some(ty) => llarg_rust = BitCast(bcx, llarg_rust, ty.ptr_to()),
None => ()
}
debug2!("llarg_rust={} (after casting)",
ccx.tn.val_to_str(llarg_rust));
// Finally, load the value if needed for the foreign ABI
let foreign_indirect = attributes[i].is_some();
let foreign_indirect = arg_tys[i].is_indirect();
let llarg_foreign = if foreign_indirect {
llarg_rust
} else {
@ -250,6 +247,11 @@ pub fn trans_native_call(bcx: @mut Block,
debug2!("argument {}, llarg_foreign={}",
i, ccx.tn.val_to_str(llarg_foreign));
// fill padding with undef value
match arg_tys[i].pad {
Some(ty) => llargs_foreign.push(C_undef(ty)),
None => ()
}
llargs_foreign.push(llarg_foreign);
}
@ -268,7 +270,7 @@ pub fn trans_native_call(bcx: @mut Block,
// any attributes with ABI implications directly to the call instruction. Right now, the
// only attribute we need to worry about is `sret`.
let attrs;
if fn_type.sret {
if fn_type.ret_ty.is_indirect() {
attrs = &[(1, StructRetAttribute)];
} else {
attrs = &[];
@ -280,9 +282,12 @@ pub fn trans_native_call(bcx: @mut Block,
// 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 ret_def && !fn_type.sret {
if ret_def && !fn_type.ret_ty.is_indirect() {
let llrust_ret_ty = llsig.llret_ty;
let llforeign_ret_ty = fn_type.ret_ty.ty;
let llforeign_ret_ty = match fn_type.ret_ty.cast {
Some(ty) => ty,
None => fn_type.ret_ty.ty
};
debug2!("llretptr={}", ccx.tn.val_to_str(llretptr));
debug2!("llforeign_retval={}", ccx.tn.val_to_str(llforeign_retval));
@ -474,17 +479,17 @@ pub fn trans_rust_fn_with_foreign_abi(ccx: @mut CrateContext,
// Array for the arguments we will pass to the rust function.
let mut llrust_args = ~[];
let mut next_foreign_arg_counter: c_uint = 0;
let next_foreign_arg: &fn() -> c_uint = {
|| {
next_foreign_arg_counter += 1;
let next_foreign_arg: &fn(pad: bool) -> c_uint = {
|pad: bool| {
next_foreign_arg_counter += if pad { 2 } else { 1 };
next_foreign_arg_counter - 1
}
};
// If there is an out pointer on the foreign function
let foreign_outptr = {
if tys.fn_ty.sret {
Some(llvm::LLVMGetParam(llwrapfn, next_foreign_arg()))
if tys.fn_ty.ret_ty.is_indirect() {
Some(llvm::LLVMGetParam(llwrapfn, next_foreign_arg(false)))
} else {
None
}
@ -553,9 +558,12 @@ pub fn trans_rust_fn_with_foreign_abi(ccx: @mut CrateContext,
for i in range(0, tys.fn_sig.inputs.len()) {
let rust_ty = tys.fn_sig.inputs[i];
let llrust_ty = tys.llsig.llarg_tys[i];
let foreign_index = next_foreign_arg();
let rust_indirect = type_of::arg_is_indirect(ccx, rust_ty);
let foreign_indirect = tys.fn_ty.attrs[foreign_index].is_some();
let llforeign_arg_ty = tys.fn_ty.arg_tys[i];
let foreign_indirect = llforeign_arg_ty.is_indirect();
// skip padding
let foreign_index = next_foreign_arg(llforeign_arg_ty.pad.is_some());
let mut llforeign_arg = llvm::LLVMGetParam(llwrapfn, foreign_index);
debug2!("llforeign_arg \\#{}: {}",
@ -578,7 +586,7 @@ pub fn trans_rust_fn_with_foreign_abi(ccx: @mut CrateContext,
// If the types in the ABI and the Rust types don't match,
// bitcast the llforeign_arg pointer so it matches the types
// Rust expects.
if tys.fn_ty.arg_tys[foreign_index].cast {
if llforeign_arg_ty.cast.is_some() {
assert!(!foreign_indirect);
llforeign_arg = llvm::LLVMBuildBitCast(
builder, llforeign_arg,
@ -604,7 +612,10 @@ pub fn trans_rust_fn_with_foreign_abi(ccx: @mut CrateContext,
};
// Get the return value where the foreign fn expects it.
let llforeign_ret_ty = tys.fn_ty.ret_ty.ty;
let llforeign_ret_ty = match tys.fn_ty.ret_ty.cast {
Some(ty) => ty,
None => tys.fn_ty.ret_ty.ty
};
match foreign_outptr {
None if !tys.ret_def => {
// Function returns `()` or `bot`, which in Rust is the LLVM
@ -744,9 +755,38 @@ fn foreign_types_for_fn_ty(ccx: &mut CrateContext,
}
fn lltype_for_fn_from_foreign_types(tys: &ForeignTypes) -> Type {
let llargument_tys: ~[Type] =
tys.fn_ty.arg_tys.iter().map(|t| t.ty).collect();
let llreturn_ty = tys.fn_ty.ret_ty.ty;
let mut llargument_tys = ~[];
let ret_ty = tys.fn_ty.ret_ty;
let llreturn_ty = if ret_ty.is_indirect() {
llargument_tys.push(ret_ty.ty.ptr_to());
Type::void()
} else {
match ret_ty.cast {
Some(ty) => ty,
None => ret_ty.ty
}
};
for &arg_ty in tys.fn_ty.arg_tys.iter() {
// add padding
match arg_ty.pad {
Some(ty) => llargument_tys.push(ty),
None => ()
}
let llarg_ty = if arg_ty.is_indirect() {
arg_ty.ty.ptr_to()
} else {
match arg_ty.cast {
Some(ty) => ty,
None => arg_ty.ty
}
};
llargument_tys.push(llarg_ty);
}
Type::func(llargument_tys, &llreturn_ty)
}
@ -757,8 +797,27 @@ pub fn lltype_for_foreign_fn(ccx: &mut CrateContext, ty: ty::t) -> Type {
fn add_argument_attributes(tys: &ForeignTypes,
llfn: ValueRef) {
for (i, a) in tys.fn_ty.attrs.iter().enumerate() {
match *a {
let mut i = 0;
if tys.fn_ty.ret_ty.is_indirect() {
match tys.fn_ty.ret_ty.attr {
Some(attr) => {
let llarg = get_param(llfn, i);
unsafe {
llvm::LLVMAddAttribute(llarg, attr as c_uint);
}
}
None => {}
}
i += 1;
}
for &arg_ty in tys.fn_ty.arg_tys.iter() {
// skip padding
if arg_ty.pad.is_some() { i += 1; }
match arg_ty.attr {
Some(attr) => {
let llarg = get_param(llfn, i);
unsafe {
@ -767,5 +826,7 @@ fn add_argument_attributes(tys: &ForeignTypes,
}
None => ()
}
i += 1;
}
}