Skip to content

Commit 5e720aa

Browse files
committed
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`.
1 parent abdbaa2 commit 5e720aa

File tree

3 files changed

+82
-5
lines changed

3 files changed

+82
-5
lines changed

src/librustc/middle/trans/foreign.rs

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111

1212
use back::{link};
1313
use lib::llvm::llvm;
14-
use lib::llvm::{ValueRef, CallConv, StructRetAttribute, Linkage};
14+
use lib::llvm::{ValueRef, CallConv, Linkage};
1515
use lib;
1616
use middle::weak_lang_items;
1717
use middle::trans::base::push_ctxt;
@@ -373,18 +373,41 @@ pub fn trans_native_call<'a>(
373373
};
374374

375375
// A function pointer is called without the declaration available, so we have to apply
376-
// any attributes with ABI implications directly to the call instruction. Right now, the
377-
// only attribute we need to worry about is `sret`.
376+
// any attributes with ABI implications directly to the call instruction.
378377
let mut attrs = Vec::new();
379-
if fn_type.ret_ty.is_indirect() {
380-
attrs.push((1, lib::llvm::StructRetAttribute as u64));
381378

379+
// Add attributes that are always applicable, independent of the concrete foreign ABI
380+
if fn_type.ret_ty.is_indirect() {
382381
// The outptr can be noalias and nocapture because it's entirely
383382
// invisible to the program. We can also mark it as nonnull
384383
attrs.push((1, lib::llvm::NoAliasAttribute as u64));
385384
attrs.push((1, lib::llvm::NoCaptureAttribute as u64));
386385
attrs.push((1, lib::llvm::NonNullAttribute as u64));
387386
};
387+
388+
// Add attributes that depend on the concrete foreign ABI
389+
let mut arg_idx = if fn_type.ret_ty.is_indirect() { 1 } else { 0 };
390+
match fn_type.ret_ty.attr {
391+
Some(attr) => attrs.push((arg_idx, attr as u64)),
392+
_ => ()
393+
}
394+
395+
arg_idx += 1;
396+
for arg_ty in fn_type.arg_tys.iter() {
397+
if arg_ty.is_ignore() {
398+
continue;
399+
}
400+
// skip padding
401+
if arg_ty.pad.is_some() { arg_idx += 1; }
402+
403+
match arg_ty.attr {
404+
Some(attr) => attrs.push((arg_idx, attr as u64)),
405+
_ => {}
406+
}
407+
408+
arg_idx += 1;
409+
}
410+
388411
let llforeign_retval = CallWithConv(bcx,
389412
llfn,
390413
llargs_foreign.as_slice(),

src/rt/rust_test_helpers.c

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,3 +199,21 @@ void
199199
rust_dbg_static_mut_check_four() {
200200
assert(rust_dbg_static_mut == 4);
201201
}
202+
203+
struct S {
204+
uint64_t x;
205+
uint64_t y;
206+
uint64_t z;
207+
};
208+
209+
uint64_t get_x(struct S s) {
210+
return s.x;
211+
}
212+
213+
uint64_t get_y(struct S s) {
214+
return s.y;
215+
}
216+
217+
uint64_t get_z(struct S s) {
218+
return s.z;
219+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
pub struct S {
12+
x: u64,
13+
y: u64,
14+
z: u64,
15+
}
16+
17+
#[link(name = "rust_test_helpers")]
18+
extern {
19+
pub fn get_x(x: S) -> u64;
20+
pub fn get_y(x: S) -> u64;
21+
pub fn get_z(x: S) -> u64;
22+
}
23+
24+
#[inline(never)]
25+
fn indirect_call(func: unsafe extern fn(s: S) -> u64, s: S) -> u64 {
26+
unsafe {
27+
func(s)
28+
}
29+
}
30+
31+
fn main() {
32+
let s = S { x: 1, y: 2, z: 3 };
33+
assert_eq!(s.x, indirect_call(get_x, s));
34+
assert_eq!(s.y, indirect_call(get_y, s));
35+
assert_eq!(s.z, indirect_call(get_z, s));
36+
}

0 commit comments

Comments
 (0)