From ee2de279965a27e8189a9129a4b3454dd82568b9 Mon Sep 17 00:00:00 2001 From: Huon Wilson Date: Fri, 28 Aug 2015 16:34:07 -0700 Subject: [PATCH] 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. --- src/librustc_platform_intrinsics/lib.rs | 4 ++ src/librustc_trans/trans/intrinsic.rs | 90 ++++++++++++++++++++++--- src/librustc_typeck/check/intrinsic.rs | 17 +++++ 3 files changed, 101 insertions(+), 10 deletions(-) diff --git a/src/librustc_platform_intrinsics/lib.rs b/src/librustc_platform_intrinsics/lib.rs index 476b9ee31feb..88ad2dce55cd 100755 --- a/src/librustc_platform_intrinsics/lib.rs +++ b/src/librustc_platform_intrinsics/lib.rs @@ -34,6 +34,7 @@ pub enum Type { Float(u8), Pointer(Box), Vector(Box, u8), + Aggregate(bool, Vec), } 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::Aggregate(flatten, types) +} macro_rules! ty { (f32x8) => (v(f(32), 8)); diff --git a/src/librustc_trans/trans/intrinsic.rs b/src/librustc_trans/trans/intrinsic.rs index d6275f2b9e83..267a8a15f699 100644 --- a/src/librustc_trans/trans/intrinsic.rs +++ b/src/librustc_trans/trans/intrinsic.rs @@ -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(x: Vec) -> 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 { 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::>(); - 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 + { + 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::>(); + + 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, diff --git a/src/librustc_typeck/check/intrinsic.rs b/src/librustc_typeck/check/intrinsic.rs index 24b63f5d4f4a..74a1926916b4 100644 --- a/src/librustc_typeck/check/intrinsic.rs +++ b/src/librustc_typeck/check/intrinsic.rs @@ -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")), + } + } } }