Skip to content

Commit

Permalink
[MIR] Implement extern call support
Browse files Browse the repository at this point in the history
  • Loading branch information
nagisa committed Jan 14, 2016
1 parent 1586005 commit 54af95a
Show file tree
Hide file tree
Showing 2 changed files with 78 additions and 11 deletions.
60 changes: 49 additions & 11 deletions src/librustc_trans/trans/mir/block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -98,12 +101,24 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
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(ref 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 {
Expand All @@ -115,19 +130,23 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {

// 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);
build::Invoke(bcx,
Expand All @@ -138,7 +157,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
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 {
Expand Down Expand Up @@ -184,14 +203,14 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
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,
Expand All @@ -209,6 +228,25 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
}
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),
_ => ()
};
},
}
}
}
Expand Down
29 changes: 29 additions & 0 deletions src/test/run-pass/mir_trans_calls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
// except according to those terms.

#![feature(rustc_attrs)]
use std::os::raw;

#[rustc_mir]
fn test1(a: isize, b: (i32, i32), c: &[i32]) -> (isize, (i32, i32), &[i32]) {
Expand Down Expand Up @@ -88,6 +89,32 @@ 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))
}

extern {
#[cfg_attr(windows, link_name="wsprintfA")]
fn sprintf(_: *mut raw::c_char, _: *const raw::c_char, ...) -> raw::c_int;
}

#[rustc_mir]
fn test10(i: i32, j: i32, k: i32) -> Vec<raw::c_char> {
let mut x: Vec<raw::c_char> = Vec::with_capacity(512);
unsafe {
let out = sprintf(x.as_mut_ptr(), b"%d %d %d\0".as_ptr() as *const raw::c_char, i, j, k);
assert!(out > 0);
x.set_len(out as usize);
}
x
}


#[rustc_mir]
fn test_closure<F>(f: &F, x: i32, y: i32) -> i32
where F: Fn(i32, i32) -> i32
Expand Down Expand Up @@ -117,6 +144,8 @@ fn main() {
assert_eq!(test6(&Foo, 12367), 12367);
assert_eq!(test7(), 1);
assert_eq!(test8(), 2);
assert_eq!(test9(), 41 + 42 * 43);
assert_eq!(&test10(0, 42, 31415), &[48, 32, 52, 50, 32, 51, 49, 52, 49, 53]);

let closure = |x: i32, y: i32| { x + y };
assert_eq!(test_closure(&closure, 100, 1), 101);
Expand Down

0 comments on commit 54af95a

Please sign in to comment.