Rollup merge of #32732 - dotdash:ext_arg, r=eddyb

Handle integer-extending for C ABI

We need to supply sext/zext attributes to LLVM to ensure that arguments
are extended to the appropriate width in the correct way.

Most platforms extend integers less than 32 bits, though not all.
This commit is contained in:
Manish Goregaokar 2016-04-07 23:26:18 +05:30
commit 02acff64a4
11 changed files with 102 additions and 21 deletions

View file

@ -80,6 +80,8 @@ pub struct ArgType {
/// Only later will `original_ty` aka `%Foo` be used in the LLVM function
/// pointer type, without ever having introspected it.
pub ty: Type,
/// Signedness for integer types, None for other types
pub signedness: Option<bool>,
/// Coerced LLVM Type
pub cast: Option<Type>,
/// Dummy argument, which is emitted before the real argument
@ -94,6 +96,7 @@ impl ArgType {
kind: ArgKind::Direct,
original_ty: original_ty,
ty: ty,
signedness: None,
cast: None,
pad: None,
attrs: llvm::Attributes::default()
@ -123,6 +126,19 @@ impl ArgType {
self.kind = ArgKind::Ignore;
}
pub fn extend_integer_width_to(&mut self, bits: u64) {
// Only integers have signedness
if let Some(signed) = self.signedness {
if self.ty.int_width() < bits {
self.attrs.set(if signed {
llvm::Attribute::SExt
} else {
llvm::Attribute::ZExt
});
}
}
}
pub fn is_indirect(&self) -> bool {
self.kind == ArgKind::Indirect
}
@ -268,6 +284,9 @@ impl FnType {
} else {
let mut arg = ArgType::new(type_of::type_of(ccx, ty),
type_of::sizing_type_of(ccx, ty));
if ty.is_integral() {
arg.signedness = Some(ty.is_signed());
}
if llsize_of_real(ccx, arg.ty) == 0 {
// For some forsaken reason, x86_64-pc-windows-gnu
// doesn't ignore zero-sized struct arguments.

View file

@ -163,6 +163,7 @@ fn is_homogenous_aggregate_ty(ty: Type) -> Option<(Type, u64)> {
fn classify_ret_ty(ccx: &CrateContext, ret: &mut ArgType) {
if is_reg_ty(ret.ty) {
ret.extend_integer_width_to(32);
return;
}
if let Some((base_ty, members)) = is_homogenous_aggregate_ty(ret.ty) {
@ -190,6 +191,7 @@ fn classify_ret_ty(ccx: &CrateContext, ret: &mut ArgType) {
fn classify_arg_ty(ccx: &CrateContext, arg: &mut ArgType) {
if is_reg_ty(arg.ty) {
arg.extend_integer_width_to(32);
return;
}
if let Some((base_ty, members)) = is_homogenous_aggregate_ty(arg.ty) {

View file

@ -131,6 +131,7 @@ fn ty_size(ty: Type, align_fn: TyAlignFn) -> usize {
fn classify_ret_ty(ccx: &CrateContext, ret: &mut ArgType, align_fn: TyAlignFn) {
if is_reg_ty(ret.ty) {
ret.extend_integer_width_to(32);
return;
}
let size = ty_size(ret.ty, align_fn);
@ -150,6 +151,7 @@ fn classify_ret_ty(ccx: &CrateContext, ret: &mut ArgType, align_fn: TyAlignFn) {
fn classify_arg_ty(ccx: &CrateContext, arg: &mut ArgType, align_fn: TyAlignFn) {
if is_reg_ty(arg.ty) {
arg.extend_integer_width_to(32);
return;
}
let align = align_fn(arg.ty);

View file

@ -86,6 +86,14 @@ fn ty_size(ty: Type) -> usize {
}
}
fn classify_ret_ty(ccx: &CrateContext, ret: &mut ArgType) {
if is_reg_ty(ret.ty) {
ret.extend_integer_width_to(32);
} else {
ret.make_indirect(ccx);
}
}
fn classify_arg_ty(ccx: &CrateContext, arg: &mut ArgType, offset: &mut usize) {
let orig_offset = *offset;
let size = ty_size(arg.ty) * 8;
@ -98,6 +106,8 @@ fn classify_arg_ty(ccx: &CrateContext, arg: &mut ArgType, offset: &mut usize) {
if !is_reg_ty(arg.ty) {
arg.cast = Some(struct_ty(ccx, arg.ty));
arg.pad = padding_ty(ccx, align, orig_offset);
} else {
arg.extend_integer_width_to(32);
}
}
@ -146,8 +156,8 @@ fn struct_ty(ccx: &CrateContext, ty: Type) -> Type {
}
pub fn compute_abi_info(ccx: &CrateContext, fty: &mut FnType) {
if !fty.ret.is_ignore() && !is_reg_ty(fty.ret.ty) {
fty.ret.make_indirect(ccx);
if !fty.ret.is_ignore() {
classify_ret_ty(ccx, &mut fty.ret);
}
let mut offset = if fty.ret.is_indirect() { 4 } else { 0 };

View file

@ -82,6 +82,14 @@ fn ty_size(ty: Type) -> usize {
}
}
fn classify_ret_ty(ccx: &CrateContext, ret: &mut ArgType) {
if is_reg_ty(ret.ty) {
ret.extend_integer_width_to(32);
} else {
ret.make_indirect(ccx);
}
}
fn classify_arg_ty(ccx: &CrateContext, arg: &mut ArgType, offset: &mut usize) {
let orig_offset = *offset;
let size = ty_size(arg.ty) * 8;
@ -94,6 +102,8 @@ fn classify_arg_ty(ccx: &CrateContext, arg: &mut ArgType, offset: &mut usize) {
if !is_reg_ty(arg.ty) {
arg.cast = Some(struct_ty(ccx, arg.ty));
arg.pad = padding_ty(ccx, align, orig_offset);
} else {
arg.extend_integer_width_to(32);
}
}
@ -141,8 +151,8 @@ fn struct_ty(ccx: &CrateContext, ty: Type) -> Type {
}
pub fn compute_abi_info(ccx: &CrateContext, fty: &mut FnType) {
if !fty.ret.is_ignore() && !is_reg_ty(fty.ret.ty) {
fty.ret.make_indirect(ccx);
if !fty.ret.is_ignore() {
classify_ret_ty(ccx, &mut fty.ret);
}
let mut offset = if fty.ret.is_indirect() { 4 } else { 0 };

View file

@ -153,6 +153,7 @@ fn is_homogenous_aggregate_ty(ty: Type) -> Option<(Type, u64)> {
fn classify_ret_ty(ccx: &CrateContext, ret: &mut ArgType) {
if is_reg_ty(ret.ty) {
ret.extend_integer_width_to(64);
return;
}
@ -187,6 +188,7 @@ fn classify_ret_ty(ccx: &CrateContext, ret: &mut ArgType) {
fn classify_arg_ty(ccx: &CrateContext, arg: &mut ArgType) {
if is_reg_ty(arg.ty) {
arg.extend_integer_width_to(64);
return;
}

View file

@ -15,25 +15,29 @@ use super::common::*;
use super::machine::*;
pub fn compute_abi_info(ccx: &CrateContext, fty: &mut FnType) {
if !fty.ret.is_ignore() && fty.ret.ty.kind() == Struct {
// Returning a structure. Most often, this will use
// a hidden first argument. On some platforms, though,
// small structs are returned as integers.
//
// Some links:
// http://www.angelcode.com/dev/callconv/callconv.html
// Clang's ABI handling is in lib/CodeGen/TargetInfo.cpp
let t = &ccx.sess().target.target;
if t.options.is_like_osx || t.options.is_like_windows {
match llsize_of_alloc(ccx, fty.ret.ty) {
1 => fty.ret.cast = Some(Type::i8(ccx)),
2 => fty.ret.cast = Some(Type::i16(ccx)),
4 => fty.ret.cast = Some(Type::i32(ccx)),
8 => fty.ret.cast = Some(Type::i64(ccx)),
_ => fty.ret.make_indirect(ccx)
if !fty.ret.is_ignore() {
if fty.ret.ty.kind() == Struct {
// Returning a structure. Most often, this will use
// a hidden first argument. On some platforms, though,
// small structs are returned as integers.
//
// Some links:
// http://www.angelcode.com/dev/callconv/callconv.html
// Clang's ABI handling is in lib/CodeGen/TargetInfo.cpp
let t = &ccx.sess().target.target;
if t.options.is_like_osx || t.options.is_like_windows {
match llsize_of_alloc(ccx, fty.ret.ty) {
1 => fty.ret.cast = Some(Type::i8(ccx)),
2 => fty.ret.cast = Some(Type::i16(ccx)),
4 => fty.ret.cast = Some(Type::i32(ccx)),
8 => fty.ret.cast = Some(Type::i64(ccx)),
_ => fty.ret.make_indirect(ccx)
}
} else {
fty.ret.make_indirect(ccx);
}
} else {
fty.ret.make_indirect(ccx);
fty.ret.extend_integer_width_to(32);
}
}
@ -42,6 +46,8 @@ pub fn compute_abi_info(ccx: &CrateContext, fty: &mut FnType) {
if arg.ty.kind() == Struct {
arg.make_indirect(ccx);
arg.attrs.set(Attribute::ByVal);
} else {
arg.extend_integer_width_to(32);
}
}
}

View file

@ -400,6 +400,8 @@ pub fn compute_abi_info(ccx: &CrateContext, fty: &mut FnType) {
} else {
arg.cast = Some(llreg_ty(ccx, &cls));
}
} else {
arg.extend_integer_width_to(32);
}
}

View file

@ -26,6 +26,8 @@ pub fn compute_abi_info(ccx: &CrateContext, fty: &mut FnType) {
8 => a.cast = Some(Type::i64(ccx)),
_ => a.make_indirect(ccx)
}
} else {
a.extend_integer_width_to(32);
}
};

View file

@ -243,3 +243,7 @@ double rust_interesting_average(uint64_t n, ...) {
va_end(pairs);
return sum / n;
}
int32_t rust_int8_to_int32(int8_t x) {
return (int32_t)x;
}

View file

@ -0,0 +1,22 @@
// Copyright 2016 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.
#[link(name = "rust_test_helpers")]
extern {
fn rust_int8_to_int32(_: i8) -> i32;
}
fn main() {
let x = unsafe {
rust_int8_to_int32(-1)
};
assert!(x == -1);
}