Auto merge of #30845 - nagisa:mir-extern-calls, r=dotdash
Supersedes https://github.com/rust-lang/rust/pull/30517 Fixes https://github.com/rust-lang/rust/issues/29575 cc @luqmana r? @nikomatsakis
This commit is contained in:
commit
41b74b11b4
4 changed files with 109 additions and 13 deletions
|
@ -9,13 +9,16 @@
|
||||||
// except according to those terms.
|
// except according to those terms.
|
||||||
|
|
||||||
use llvm::{BasicBlockRef, ValueRef};
|
use llvm::{BasicBlockRef, ValueRef};
|
||||||
|
use rustc::middle::ty;
|
||||||
use rustc::mir::repr as mir;
|
use rustc::mir::repr as mir;
|
||||||
|
use syntax::abi::Abi;
|
||||||
use trans::adt;
|
use trans::adt;
|
||||||
|
use trans::attributes;
|
||||||
use trans::base;
|
use trans::base;
|
||||||
use trans::build;
|
use trans::build;
|
||||||
use trans::attributes;
|
|
||||||
use trans::common::{self, Block};
|
use trans::common::{self, Block};
|
||||||
use trans::debuginfo::DebugLoc;
|
use trans::debuginfo::DebugLoc;
|
||||||
|
use trans::foreign;
|
||||||
use trans::type_of;
|
use trans::type_of;
|
||||||
use trans::type_::Type;
|
use trans::type_::Type;
|
||||||
use trans::Disr;
|
use trans::Disr;
|
||||||
|
@ -57,7 +60,6 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
|
||||||
// The else branch of the Switch can't be hit, so branch to an unreachable
|
// The else branch of the Switch can't be hit, so branch to an unreachable
|
||||||
// instruction so LLVM knows that
|
// instruction so LLVM knows that
|
||||||
let unreachable_blk = self.unreachable_block();
|
let unreachable_blk = self.unreachable_block();
|
||||||
|
|
||||||
let switch = build::Switch(bcx, discr, unreachable_blk.llbb, targets.len());
|
let switch = build::Switch(bcx, discr, unreachable_blk.llbb, targets.len());
|
||||||
assert_eq!(adt_def.variants.len(), targets.len());
|
assert_eq!(adt_def.variants.len(), targets.len());
|
||||||
for (adt_variant, target) in adt_def.variants.iter().zip(targets) {
|
for (adt_variant, target) in adt_def.variants.iter().zip(targets) {
|
||||||
|
@ -98,12 +100,24 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
|
||||||
let debugloc = DebugLoc::None;
|
let debugloc = DebugLoc::None;
|
||||||
// The arguments we'll be passing. Plus one to account for outptr, if used.
|
// The arguments we'll be passing. Plus one to account for outptr, if used.
|
||||||
let mut llargs = Vec::with_capacity(args.len() + 1);
|
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
|
// Prepare the return value destination
|
||||||
let (ret_dest_ty, must_copy_dest) = if let Some(d) = kind.destination() {
|
let (ret_dest_ty, must_copy_dest) = if let Some(d) = kind.destination() {
|
||||||
let dest = self.trans_lvalue(bcx, d);
|
let dest = self.trans_lvalue(bcx, d);
|
||||||
let ret_ty = dest.ty.to_ty(bcx.tcx());
|
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);
|
llargs.push(dest.llval);
|
||||||
(Some((dest, ret_ty)), false)
|
(Some((dest, ret_ty)), false)
|
||||||
} else {
|
} else {
|
||||||
|
@ -115,30 +129,35 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
|
||||||
|
|
||||||
// Process the rest of the args.
|
// Process the rest of the args.
|
||||||
for arg in 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),
|
Ref(llval) | Immediate(llval) => llargs.push(llval),
|
||||||
FatPtr(b, e) => {
|
FatPtr(b, e) => {
|
||||||
llargs.push(b);
|
llargs.push(b);
|
||||||
llargs.push(e);
|
llargs.push(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if is_foreign {
|
||||||
|
arg_tys.push(operand.ty);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Many different ways to call a function handled here
|
// 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`.
|
// 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 cleanup = self.bcx(cleanup);
|
||||||
let landingpad = self.make_landing_pad(cleanup);
|
let landingpad = self.make_landing_pad(cleanup);
|
||||||
|
let unreachable_blk = self.unreachable_block();
|
||||||
build::Invoke(bcx,
|
build::Invoke(bcx,
|
||||||
callee.immediate(),
|
callee.immediate(),
|
||||||
&llargs[..],
|
&llargs[..],
|
||||||
self.unreachable_block().llbb,
|
unreachable_blk.llbb,
|
||||||
landingpad.llbb,
|
landingpad.llbb,
|
||||||
Some(attrs),
|
Some(attrs),
|
||||||
debugloc);
|
debugloc);
|
||||||
},
|
},
|
||||||
(false, &mir::CallKind::ConvergingCleanup { ref targets, .. }) => {
|
(false, false, &mir::CallKind::ConvergingCleanup { ref targets, .. }) => {
|
||||||
let cleanup = self.bcx(targets.1);
|
let cleanup = self.bcx(targets.1);
|
||||||
let landingpad = self.make_landing_pad(cleanup);
|
let landingpad = self.make_landing_pad(cleanup);
|
||||||
let (target, postinvoke) = if must_copy_dest {
|
let (target, postinvoke) = if must_copy_dest {
|
||||||
|
@ -184,14 +203,14 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
|
||||||
build::Br(target, postinvoketarget.llbb, debugloc);
|
build::Br(target, postinvoketarget.llbb, debugloc);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
(_, &mir::CallKind::DivergingCleanup(_)) |
|
(false, _, &mir::CallKind::DivergingCleanup(_)) |
|
||||||
(_, &mir::CallKind::Diverging) => {
|
(false, _, &mir::CallKind::Diverging) => {
|
||||||
build::Call(bcx, callee.immediate(), &llargs[..], Some(attrs), debugloc);
|
build::Call(bcx, callee.immediate(), &llargs[..], Some(attrs), debugloc);
|
||||||
build::Unreachable(bcx);
|
build::Unreachable(bcx);
|
||||||
}
|
}
|
||||||
(_, k@&mir::CallKind::ConvergingCleanup { .. }) |
|
(false, _, k@&mir::CallKind::ConvergingCleanup { .. }) |
|
||||||
(_, k@&mir::CallKind::Converging { .. }) => {
|
(false, _, k@&mir::CallKind::Converging { .. }) => {
|
||||||
// Bug #20046
|
// FIXME: Bug #20046
|
||||||
let target = match *k {
|
let target = match *k {
|
||||||
mir::CallKind::ConvergingCleanup { targets, .. } => targets.0,
|
mir::CallKind::ConvergingCleanup { targets, .. } => targets.0,
|
||||||
mir::CallKind::Converging { target, .. } => target,
|
mir::CallKind::Converging { target, .. } => target,
|
||||||
|
@ -209,6 +228,25 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
|
||||||
}
|
}
|
||||||
build::Br(bcx, self.llblock(target), debugloc);
|
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),
|
||||||
|
_ => ()
|
||||||
|
};
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
|
||||||
// These functions are used in the unit tests for C ABI calls.
|
// 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) {
|
uint64_t get_c_many_params(void *a, void *b, void *c, void *d, struct quad f) {
|
||||||
return f.c;
|
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;
|
||||||
|
}
|
||||||
|
|
|
@ -88,6 +88,15 @@ fn test8() -> isize {
|
||||||
Two::two()
|
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]
|
#[rustc_mir]
|
||||||
fn test_closure<F>(f: &F, x: i32, y: i32) -> i32
|
fn test_closure<F>(f: &F, x: i32, y: i32) -> i32
|
||||||
where F: Fn(i32, i32) -> i32
|
where F: Fn(i32, i32) -> i32
|
||||||
|
@ -117,6 +126,7 @@ fn main() {
|
||||||
assert_eq!(test6(&Foo, 12367), 12367);
|
assert_eq!(test6(&Foo, 12367), 12367);
|
||||||
assert_eq!(test7(), 1);
|
assert_eq!(test7(), 1);
|
||||||
assert_eq!(test8(), 2);
|
assert_eq!(test8(), 2);
|
||||||
|
assert_eq!(test9(), 41 + 42 * 43);
|
||||||
|
|
||||||
let closure = |x: i32, y: i32| { x + y };
|
let closure = |x: i32, y: i32| { x + y };
|
||||||
assert_eq!(test_closure(&closure, 100, 1), 101);
|
assert_eq!(test_closure(&closure, 100, 1), 101);
|
||||||
|
|
32
src/test/run-pass/mir_trans_calls_variadic.rs
Normal file
32
src/test/run-pass/mir_trans_calls_variadic.rs
Normal file
|
@ -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);
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue