]> git.lizzy.rs Git - rust.git/commitdiff
[MIR] Implement extern call support
authorSimonas Kazlauskas <git@kazlauskas.me>
Tue, 12 Jan 2016 13:20:18 +0000 (15:20 +0200)
committerSimonas Kazlauskas <git@kazlauskas.me>
Tue, 19 Jan 2016 13:14:04 +0000 (15:14 +0200)
src/librustc_trans/trans/mir/block.rs
src/rt/rust_test_helpers.c
src/test/run-pass/mir_trans_calls.rs
src/test/run-pass/mir_trans_calls_variadic.rs [new file with mode: 0644]

index 18a9aad0e915d1a885ea49c439ae64183f4b4e94..47370a80072675bfbde61bc5471891923979f160 100644 (file)
@@ -9,13 +9,16 @@
 // except according to those terms.
 
 use llvm::{BasicBlockRef, ValueRef};
+use rustc::middle::ty;
 use rustc::mir::repr as mir;
+use syntax::abi::Abi;
 use trans::adt;
+use trans::attributes;
 use trans::base;
 use trans::build;
-use trans::attributes;
 use trans::common::{self, Block};
 use trans::debuginfo::DebugLoc;
+use trans::foreign;
 use trans::type_of;
 use trans::type_::Type;
 
@@ -57,7 +60,6 @@ pub fn trans_block(&mut self, bb: mir::BasicBlock) {
                 // The else branch of the Switch can't be hit, so branch to an unreachable
                 // instruction so LLVM knows that
                 let unreachable_blk = self.unreachable_block();
-
                 let switch = build::Switch(bcx, discr, unreachable_blk.llbb, targets.len());
                 assert_eq!(adt_def.variants.len(), targets.len());
                 for (adt_variant, target) in adt_def.variants.iter().zip(targets) {
@@ -98,12 +100,24 @@ pub fn trans_block(&mut self, bb: mir::BasicBlock) {
                 let debugloc = DebugLoc::None;
                 // The arguments we'll be passing. Plus one to account for outptr, if used.
                 let mut llargs = Vec::with_capacity(args.len() + 1);
+                // Types of the arguments. We do not preallocate, because this vector is only
+                // filled when `is_foreign` is `true` and foreign calls are minority of the cases.
+                let mut arg_tys = Vec::new();
+
+                // Foreign-ABI functions are translated differently
+                let is_foreign = if let ty::TyBareFn(_, ref f) = callee.ty.sty {
+                    // We do not translate intrinsics here (they shouldn’t be functions)
+                    assert!(f.abi != Abi::RustIntrinsic && f.abi != Abi::PlatformIntrinsic);
+                    f.abi != Abi::Rust && f.abi != Abi::RustCall
+                } else {
+                    false
+                };
 
                 // Prepare the return value destination
                 let (ret_dest_ty, must_copy_dest) = if let Some(d) = kind.destination() {
                     let dest = self.trans_lvalue(bcx, d);
                     let ret_ty = dest.ty.to_ty(bcx.tcx());
-                    if type_of::return_uses_outptr(bcx.ccx(), ret_ty) {
+                    if !is_foreign && type_of::return_uses_outptr(bcx.ccx(), ret_ty) {
                         llargs.push(dest.llval);
                         (Some((dest, ret_ty)), false)
                     } else {
@@ -115,30 +129,35 @@ pub fn trans_block(&mut self, bb: mir::BasicBlock) {
 
                 // Process the rest of the args.
                 for arg in args {
-                    match self.trans_operand(bcx, arg).val {
+                    let operand = self.trans_operand(bcx, arg);
+                    match operand.val {
                         Ref(llval) | Immediate(llval) => llargs.push(llval),
                         FatPtr(b, e) => {
                             llargs.push(b);
                             llargs.push(e);
                         }
                     }
+                    if is_foreign {
+                        arg_tys.push(operand.ty);
+                    }
                 }
 
                 // Many different ways to call a function handled here
-                match (base::avoid_invoke(bcx), kind) {
+                match (is_foreign, base::avoid_invoke(bcx), kind) {
                     // The two cases below are the only ones to use LLVM’s `invoke`.
-                    (false, &mir::CallKind::DivergingCleanup(cleanup)) => {
+                    (false, false, &mir::CallKind::DivergingCleanup(cleanup)) => {
                         let cleanup = self.bcx(cleanup);
                         let landingpad = self.make_landing_pad(cleanup);
+                        let unreachable_blk = self.unreachable_block();
                         build::Invoke(bcx,
                                       callee.immediate(),
                                       &llargs[..],
-                                      self.unreachable_block().llbb,
+                                      unreachable_blk.llbb,
                                       landingpad.llbb,
                                       Some(attrs),
                                       debugloc);
                     },
-                    (false, &mir::CallKind::ConvergingCleanup { ref targets, .. }) => {
+                    (false, false, &mir::CallKind::ConvergingCleanup { ref targets, .. }) => {
                         let cleanup = self.bcx(targets.1);
                         let landingpad = self.make_landing_pad(cleanup);
                         let (target, postinvoke) = if must_copy_dest {
@@ -184,14 +203,14 @@ pub fn trans_block(&mut self, bb: mir::BasicBlock) {
                             build::Br(target, postinvoketarget.llbb, debugloc);
                         }
                     },
-                    (_, &mir::CallKind::DivergingCleanup(_)) |
-                    (_, &mir::CallKind::Diverging) => {
+                    (false, _, &mir::CallKind::DivergingCleanup(_)) |
+                    (false, _, &mir::CallKind::Diverging) => {
                         build::Call(bcx, callee.immediate(), &llargs[..], Some(attrs), debugloc);
                         build::Unreachable(bcx);
                     }
-                    (_, k@&mir::CallKind::ConvergingCleanup { .. }) |
-                    (_, k@&mir::CallKind::Converging { .. }) => {
-                        // Bug #20046
+                    (false, _, k@&mir::CallKind::ConvergingCleanup { .. }) |
+                    (false, _, k@&mir::CallKind::Converging { .. }) => {
+                        // FIXME: Bug #20046
                         let target = match *k {
                             mir::CallKind::ConvergingCleanup { targets, .. } => targets.0,
                             mir::CallKind::Converging { target, .. } => target,
@@ -209,6 +228,25 @@ pub fn trans_block(&mut self, bb: mir::BasicBlock) {
                         }
                         build::Br(bcx, self.llblock(target), debugloc);
                     }
+                    // Foreign functions
+                    (true, _, k) => {
+                        let (dest, _) = ret_dest_ty
+                            .expect("return destination is not set");
+                        bcx = foreign::trans_native_call(bcx,
+                                                   callee.ty,
+                                                   callee.immediate(),
+                                                   dest.llval,
+                                                   &llargs[..],
+                                                   arg_tys,
+                                                   debugloc);
+                        match *k {
+                            mir::CallKind::ConvergingCleanup { targets, .. } =>
+                                build::Br(bcx, self.llblock(targets.0), debugloc),
+                            mir::CallKind::Converging { target, .. } =>
+                                build::Br(bcx, self.llblock(target), debugloc),
+                            _ => ()
+                        };
+                    },
                 }
             }
         }
index f7895d694c8246923e15c57a8839c0811ff62ff6..320cd3dbd851a4e638f5c008da30c4de74413c46 100644 (file)
@@ -12,6 +12,7 @@
 
 #include <stdint.h>
 #include <assert.h>
+#include <stdarg.h>
 
 // These functions are used in the unit tests for C ABI calls.
 
@@ -222,3 +223,18 @@ uint64_t get_z(struct S s) {
 uint64_t get_c_many_params(void *a, void *b, void *c, void *d, struct quad f) {
     return f.c;
 }
+
+// Calculates the average of `(x + y) / n` where x: i64, y: f64. There must be exactly n pairs
+// passed as variadic arguments.
+double rust_interesting_average(uint64_t n, ...) {
+    va_list pairs;
+    double sum = 0.0;
+    int i;
+    va_start(pairs, n);
+    for(i = 0; i < n; i += 1) {
+        sum += (double)va_arg(pairs, int64_t);
+        sum += va_arg(pairs, double);
+    }
+    va_end(pairs);
+    return sum / n;
+}
index bca72330c85ad99aa8419e21b58fa3acd452be68..8fdedb6581fab9541ec8bc91bc777909549381a9 100644 (file)
@@ -88,6 +88,15 @@ fn test8() -> isize {
     Two::two()
 }
 
+extern fn simple_extern(x: u32, y: (u32, u32)) -> u32 {
+    x + y.0 * y.1
+}
+
+#[rustc_mir]
+fn test9() -> u32 {
+    simple_extern(41, (42, 43))
+}
+
 #[rustc_mir]
 fn test_closure<F>(f: &F, x: i32, y: i32) -> i32
     where F: Fn(i32, i32) -> i32
@@ -117,6 +126,7 @@ fn main() {
     assert_eq!(test6(&Foo, 12367), 12367);
     assert_eq!(test7(), 1);
     assert_eq!(test8(), 2);
+    assert_eq!(test9(), 41 + 42 * 43);
 
     let closure = |x: i32, y: i32| { x + y };
     assert_eq!(test_closure(&closure, 100, 1), 101);
diff --git a/src/test/run-pass/mir_trans_calls_variadic.rs b/src/test/run-pass/mir_trans_calls_variadic.rs
new file mode 100644 (file)
index 0000000..ff66daa
--- /dev/null
@@ -0,0 +1,32 @@
+// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(rustc_attrs)]
+
+#[link(name = "rust_test_helpers")]
+extern {
+    fn rust_interesting_average(_: i64, ...) -> f64;
+}
+
+#[rustc_mir]
+fn test(a: i64, b: i64, c: i64, d: i64, e: i64, f: i64) -> i64 {
+    unsafe {
+        rust_interesting_average(6, a, a as f64,
+                                    b, b as f64,
+                                    c, c as f64,
+                                    d, d as f64,
+                                    e, e as f64,
+                                    f, f as f64) as i64
+    }
+}
+
+fn main(){
+    assert_eq!(test(10, 20, 30, 40, 50, 60), 70);
+}