Add support for aggregates in platform intrinsics.

This adds support for flattened intrinsics, which are called in Rust
with tuples but in LLVM without them (e.g. `foo((a, b))` becomes `foo(a,
b)`). Unflattened ones could be supported, but are not yet.
This commit is contained in:
Huon Wilson 2015-08-28 16:34:07 -07:00
parent b03ca7f805
commit ee2de27996
3 changed files with 101 additions and 10 deletions

View File

@ -34,6 +34,7 @@ pub enum Type {
Float(u8),
Pointer(Box<Type>),
Vector(Box<Type>, u8),
Aggregate(bool, Vec<Type>),
}
pub enum IntrinsicDef {
@ -44,6 +45,9 @@ fn i(width: u8) -> Type { Type::Integer(true, width) }
fn u(width: u8) -> Type { Type::Integer(false, width) }
fn f(width: u8) -> Type { Type::Float(width) }
fn v(x: Type, length: u8) -> Type { Type::Vector(Box::new(x), length) }
fn agg(flatten: bool, types: Vec<Type>) -> Type {
Type::Aggregate(flatten, types)
}
macro_rules! ty {
(f32x8) => (v(f(32), 8));

View File

@ -171,9 +171,10 @@ pub fn trans_intrinsic_call<'a, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>,
let _icx = push_ctxt("trans_intrinsic_call");
let ret_ty = match callee_ty.sty {
let (arg_tys, ret_ty) = match callee_ty.sty {
ty::TyBareFn(_, ref f) => {
bcx.tcx().erase_late_bound_regions(&f.sig.output())
(bcx.tcx().erase_late_bound_regions(&f.sig.inputs()),
bcx.tcx().erase_late_bound_regions(&f.sig.output()))
}
_ => panic!("expected bare_fn in trans_intrinsic_call")
};
@ -924,25 +925,94 @@ pub fn trans_intrinsic_call<'a, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>,
Some(intr) => intr,
None => ccx.sess().span_bug(foreign_item.span, "unknown intrinsic"),
};
fn ty_to_type(ccx: &CrateContext, t: &intrinsics::Type) -> Type {
fn one<T>(x: Vec<T>) -> T {
assert_eq!(x.len(), 1);
x.into_iter().next().unwrap()
}
fn ty_to_type(ccx: &CrateContext, t: &intrinsics::Type,
any_flattened_aggregate: &mut bool) -> Vec<Type> {
use intrinsics::Type::*;
match *t {
Integer(_signed, x) => Type::ix(ccx, x as u64),
Integer(_signed, x) => vec![Type::ix(ccx, x as u64)],
Float(x) => {
match x {
32 => Type::f32(ccx),
64 => Type::f64(ccx),
32 => vec![Type::f32(ccx)],
64 => vec![Type::f64(ccx)],
_ => unreachable!()
}
}
Pointer(_) => unimplemented!(),
Vector(ref t, length) => Type::vector(&ty_to_type(ccx, t),
length as u64)
Vector(ref t, length) => {
let elem = one(ty_to_type(ccx, t,
any_flattened_aggregate));
vec![Type::vector(&elem,
length as u64)]
}
Aggregate(false, _) => unimplemented!(),
Aggregate(true, ref contents) => {
*any_flattened_aggregate = true;
contents.iter()
.flat_map(|t| ty_to_type(ccx, t, any_flattened_aggregate))
.collect()
}
}
}
let inputs = intr.inputs.iter().map(|t| ty_to_type(ccx, t)).collect::<Vec<_>>();
let outputs = ty_to_type(ccx, &intr.output);
// This allows an argument list like `foo, (bar, baz),
// qux` to be converted into `foo, bar, baz, qux`.
fn flatten_aggregate<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
t: &intrinsics::Type,
arg_type: Ty<'tcx>,
llarg: ValueRef)
-> Vec<ValueRef>
{
match *t {
intrinsics::Type::Aggregate(true, ref contents) => {
// We found a tuple that needs squishing! So
// run over the tuple and load each field.
//
// This assumes the type is "simple", i.e. no
// destructors, and the contents are SIMD
// etc.
assert!(!bcx.fcx.type_needs_drop(arg_type));
let repr = adt::represent_type(bcx.ccx(), arg_type);
let repr_ptr = &*repr;
(0..contents.len())
.map(|i| {
Load(bcx, adt::trans_field_ptr(bcx, repr_ptr, llarg, 0, i))
})
.collect()
}
_ => vec![llarg],
}
}
let mut any_flattened_aggregate = false;
let inputs = intr.inputs.iter()
.flat_map(|t| ty_to_type(ccx, t, &mut any_flattened_aggregate))
.collect::<Vec<_>>();
let mut out_flattening = false;
let outputs = one(ty_to_type(ccx, &intr.output, &mut out_flattening));
// outputting a flattened aggregate is nonsense
assert!(!out_flattening);
let llargs = if !any_flattened_aggregate {
// no aggregates to flatten, so no change needed
llargs
} else {
// there are some aggregates that need to be flattened
// in the LLVM call, so we need to run over the types
// again to find them and extract the arguments
intr.inputs.iter()
.zip(&llargs)
.zip(&arg_tys)
.flat_map(|((t, llarg), ty)| flatten_aggregate(bcx, t, ty, *llarg))
.collect()
};
match intr.definition {
intrinsics::IntrinsicDef::Named(name) => {
let f = declare::declare_cfn(ccx,

View File

@ -507,5 +507,22 @@ fn match_intrinsic_type_to_type<'tcx, 'a>(
inner_expected,
t_ty)
}
Aggregate(_flatten, ref expected_contents) => {
match t.sty {
ty::TyTuple(ref contents) => {
if contents.len() != expected_contents.len() {
simple_error(&format!("tuple with length {}", contents.len()),
&format!("tuple with length {}", expected_contents.len()));
return
}
for (e, c) in expected_contents.iter().zip(contents) {
match_intrinsic_type_to_type(tcx, position, span, structural_to_nominal,
e, c)
}
}
_ => simple_error(&format!("`{}`", t),
&format!("tuple")),
}
}
}
}