]> git.lizzy.rs Git - rust.git/commitdiff
Add support for aggregates in platform intrinsics.
authorHuon Wilson <dbau.pp+github@gmail.com>
Fri, 28 Aug 2015 23:34:07 +0000 (16:34 -0700)
committerHuon Wilson <dbau.pp+github@gmail.com>
Sat, 29 Aug 2015 22:36:16 +0000 (15:36 -0700)
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
src/librustc_trans/trans/intrinsic.rs
src/librustc_typeck/check/intrinsic.rs

index 476b9ee31feb2bae14116f4c9eceda87874f656a..88ad2dce55cd5526723fe73254952a8ca4f8bbdc 100755 (executable)
@@ -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));
index d6275f2b9e83ac090cc963952867490ada581ad6..267a8a15f69931e7a7f883caeeb853046b0511dc 100644 (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()
+                    }
+                }
+            }
+
+            // 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 inputs = intr.inputs.iter().map(|t| ty_to_type(ccx, t)).collect::<Vec<_>>();
-            let outputs = ty_to_type(ccx, &intr.output);
+
+            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,
index 24b63f5d4f4a61cf06fe802f4baaa0e7e134363e..74a1926916b40db7b7214db8acd3f3aefe664c4d 100644 (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")),
+            }
+        }
     }
 }