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:
parent
f92ce2e9fe
commit
4815f7e668
11 changed files with 102 additions and 21 deletions
|
@ -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.
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 };
|
||||
|
|
|
@ -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 };
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
22
src/test/run-pass/cabi-int-widening.rs
Normal file
22
src/test/run-pass/cabi-int-widening.rs
Normal 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);
|
||||
}
|
Loading…
Add table
Reference in a new issue