From 99e8b4d75544d90e28766e7052c7f8873a96128f Mon Sep 17 00:00:00 2001 From: Simonas Kazlauskas Date: Tue, 12 Jan 2016 15:20:18 +0200 Subject: [PATCH] [MIR] Implement extern call support --- src/librustc_trans/trans/mir/block.rs | 64 +++++++++++++++---- src/rt/rust_test_helpers.c | 16 +++++ src/test/run-pass/mir_trans_calls.rs | 10 +++ src/test/run-pass/mir_trans_calls_variadic.rs | 32 ++++++++++ 4 files changed, 109 insertions(+), 13 deletions(-) create mode 100644 src/test/run-pass/mir_trans_calls_variadic.rs diff --git a/src/librustc_trans/trans/mir/block.rs b/src/librustc_trans/trans/mir/block.rs index 18a9aad0e91..47370a80072 100644 --- a/src/librustc_trans/trans/mir/block.rs +++ b/src/librustc_trans/trans/mir/block.rs @@ -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), + _ => () + }; + }, } } } diff --git a/src/rt/rust_test_helpers.c b/src/rt/rust_test_helpers.c index f7895d694c8..320cd3dbd85 100644 --- a/src/rt/rust_test_helpers.c +++ b/src/rt/rust_test_helpers.c @@ -12,6 +12,7 @@ #include #include +#include // 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; +} diff --git a/src/test/run-pass/mir_trans_calls.rs b/src/test/run-pass/mir_trans_calls.rs index bca72330c85..8fdedb6581f 100644 --- a/src/test/run-pass/mir_trans_calls.rs +++ b/src/test/run-pass/mir_trans_calls.rs @@ -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, 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 index 00000000000..ff66daace38 --- /dev/null +++ b/src/test/run-pass/mir_trans_calls_variadic.rs @@ -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 or the MIT license +// , 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); +} -- 2.44.0