From 39dcdf1392a228d44ae4f15d91cc8f0eb1d202b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Steinbrink?= Date: Tue, 17 Jun 2014 21:51:24 +0200 Subject: [PATCH] Add missing attributes to indirect calls for foreign functions When calling a foreign function, some arguments and/or return value attributes are required to conform to the foreign ABI. Currently those attributes are only added to the declaration of foreign functions. With direct calls, this is no problem, because LLVM can see that those attributes apply to the call. But with an indirect call, LLVM cannot do that and the attribute is missing. To fix that, we have to add those attribute to the calls to foreign functions as well. This also allows to remove the special handling of the SRet attribute, which is ABI-dependent and will be set via the `attr` field of the return type's `ArgType`. --- src/librustc/middle/trans/foreign.rs | 25 ++++++++++++--- src/rt/rust_test_helpers.c | 18 +++++++++++ src/test/run-pass/foreign-fn-with-byval.rs | 36 ++++++++++++++++++++++ 3 files changed, 74 insertions(+), 5 deletions(-) create mode 100644 src/test/run-pass/foreign-fn-with-byval.rs diff --git a/src/librustc/middle/trans/foreign.rs b/src/librustc/middle/trans/foreign.rs index b43b47573b95f..19fb590c296ac 100644 --- a/src/librustc/middle/trans/foreign.rs +++ b/src/librustc/middle/trans/foreign.rs @@ -11,7 +11,7 @@ use back::{link}; use lib::llvm::llvm; -use lib::llvm::{ValueRef, CallConv, StructRetAttribute, Linkage}; +use lib::llvm::{ValueRef, CallConv, Linkage}; use lib; use middle::weak_lang_items; use middle::trans::base::push_ctxt; @@ -373,18 +373,33 @@ pub fn trans_native_call<'a>( }; // A function pointer is called without the declaration available, so we have to apply - // any attributes with ABI implications directly to the call instruction. Right now, the - // only attribute we need to worry about is `sret`. + // any attributes with ABI implications directly to the call instruction. let mut attrs = Vec::new(); - if fn_type.ret_ty.is_indirect() { - attrs.push((1, lib::llvm::StructRetAttribute as u64)); + // Add attributes that are always applicable, independent of the concrete foreign ABI + if fn_type.ret_ty.is_indirect() { // The outptr can be noalias and nocapture because it's entirely // invisible to the program. We can also mark it as nonnull attrs.push((1, lib::llvm::NoAliasAttribute as u64)); attrs.push((1, lib::llvm::NoCaptureAttribute as u64)); attrs.push((1, lib::llvm::NonNullAttribute as u64)); }; + + // Add attributes that depend on the concrete foreign ABI + let ret_offset = if fn_type.ret_ty.is_indirect() { 1 } else { 0 }; + match fn_type.ret_ty.attr { + Some(attr) => attrs.push((ret_offset, attr as u64)), + _ => () + } + + let first_arg_offset = ret_offset + 1; + for (idx, &arg_ty) in fn_type.arg_tys.iter().enumerate() { + match arg_ty.attr { + Some(attr) => attrs.push((first_arg_offset + idx, attr as u64)), + _ => {} + } + } + let llforeign_retval = CallWithConv(bcx, llfn, llargs_foreign.as_slice(), diff --git a/src/rt/rust_test_helpers.c b/src/rt/rust_test_helpers.c index 4d5b95155cabb..c755cf67caa9f 100644 --- a/src/rt/rust_test_helpers.c +++ b/src/rt/rust_test_helpers.c @@ -199,3 +199,21 @@ void rust_dbg_static_mut_check_four() { assert(rust_dbg_static_mut == 4); } + +struct S { + uint64_t x; + uint64_t y; + uint64_t z; +}; + +uint64_t get_x(struct S s) { + return s.x; +} + +uint64_t get_y(struct S s) { + return s.y; +} + +uint64_t get_z(struct S s) { + return s.z; +} diff --git a/src/test/run-pass/foreign-fn-with-byval.rs b/src/test/run-pass/foreign-fn-with-byval.rs new file mode 100644 index 0000000000000..6a26ec44312d7 --- /dev/null +++ b/src/test/run-pass/foreign-fn-with-byval.rs @@ -0,0 +1,36 @@ +// Copyright 2014 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. + +pub struct S { + x: u64, + y: u64, + z: u64, +} + +#[link(name = "rust_test_helpers")] +extern { + pub fn get_x(x: S) -> u64; + pub fn get_y(x: S) -> u64; + pub fn get_z(x: S) -> u64; +} + +#[inline(never)] +fn indirect_call(func: unsafe extern fn(s: S) -> u64, s: S) -> u64 { + unsafe { + func(s) + } +} + +fn main() { + let s = S { x: 1, y: 2, z: 3 }; + assert_eq!(s.x, indirect_call(get_x, s)); + assert_eq!(s.y, indirect_call(get_y, s)); + assert_eq!(s.z, indirect_call(get_z, s)); +}