From 4815f7e668640ae968418307909f41a7eaabf050 Mon Sep 17 00:00:00 2001 From: James Miller Date: Wed, 17 Feb 2016 19:33:27 +1300 Subject: [PATCH] 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. --- src/librustc_trans/abi.rs | 19 ++++++++++++ src/librustc_trans/cabi_aarch64.rs | 2 ++ src/librustc_trans/cabi_arm.rs | 2 ++ src/librustc_trans/cabi_mips.rs | 14 +++++++-- src/librustc_trans/cabi_powerpc.rs | 14 +++++++-- src/librustc_trans/cabi_powerpc64.rs | 2 ++ src/librustc_trans/cabi_x86.rs | 40 +++++++++++++++----------- src/librustc_trans/cabi_x86_64.rs | 2 ++ src/librustc_trans/cabi_x86_win64.rs | 2 ++ src/rt/rust_test_helpers.c | 4 +++ src/test/run-pass/cabi-int-widening.rs | 22 ++++++++++++++ 11 files changed, 102 insertions(+), 21 deletions(-) create mode 100644 src/test/run-pass/cabi-int-widening.rs diff --git a/src/librustc_trans/abi.rs b/src/librustc_trans/abi.rs index 6edc26c70097a..73bed8dc56a85 100644 --- a/src/librustc_trans/abi.rs +++ b/src/librustc_trans/abi.rs @@ -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, /// Coerced LLVM Type pub cast: Option, /// 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. diff --git a/src/librustc_trans/cabi_aarch64.rs b/src/librustc_trans/cabi_aarch64.rs index 4903af2a6ff7d..fc11e3888d3c2 100644 --- a/src/librustc_trans/cabi_aarch64.rs +++ b/src/librustc_trans/cabi_aarch64.rs @@ -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) { diff --git a/src/librustc_trans/cabi_arm.rs b/src/librustc_trans/cabi_arm.rs index 35099399e317c..68a2e8aa8ce95 100644 --- a/src/librustc_trans/cabi_arm.rs +++ b/src/librustc_trans/cabi_arm.rs @@ -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); diff --git a/src/librustc_trans/cabi_mips.rs b/src/librustc_trans/cabi_mips.rs index be9d4cad1db68..680310e195a41 100644 --- a/src/librustc_trans/cabi_mips.rs +++ b/src/librustc_trans/cabi_mips.rs @@ -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 }; diff --git a/src/librustc_trans/cabi_powerpc.rs b/src/librustc_trans/cabi_powerpc.rs index d118cc86f2443..efbdce67a8b2a 100644 --- a/src/librustc_trans/cabi_powerpc.rs +++ b/src/librustc_trans/cabi_powerpc.rs @@ -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 }; diff --git a/src/librustc_trans/cabi_powerpc64.rs b/src/librustc_trans/cabi_powerpc64.rs index 7bc41d26f8b75..ba54e369fd838 100644 --- a/src/librustc_trans/cabi_powerpc64.rs +++ b/src/librustc_trans/cabi_powerpc64.rs @@ -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; } diff --git a/src/librustc_trans/cabi_x86.rs b/src/librustc_trans/cabi_x86.rs index 415579eb221dd..b52231fa6b432 100644 --- a/src/librustc_trans/cabi_x86.rs +++ b/src/librustc_trans/cabi_x86.rs @@ -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); } } } diff --git a/src/librustc_trans/cabi_x86_64.rs b/src/librustc_trans/cabi_x86_64.rs index e9e9e266c7786..805c7d345a0e7 100644 --- a/src/librustc_trans/cabi_x86_64.rs +++ b/src/librustc_trans/cabi_x86_64.rs @@ -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); } } diff --git a/src/librustc_trans/cabi_x86_win64.rs b/src/librustc_trans/cabi_x86_win64.rs index a5077f68fb58c..71ecb6e9ca104 100644 --- a/src/librustc_trans/cabi_x86_win64.rs +++ b/src/librustc_trans/cabi_x86_win64.rs @@ -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); } }; diff --git a/src/rt/rust_test_helpers.c b/src/rt/rust_test_helpers.c index 00bfa63e6fea8..d2ebdcca80cf0 100644 --- a/src/rt/rust_test_helpers.c +++ b/src/rt/rust_test_helpers.c @@ -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; +} diff --git a/src/test/run-pass/cabi-int-widening.rs b/src/test/run-pass/cabi-int-widening.rs new file mode 100644 index 0000000000000..c7a2275933335 --- /dev/null +++ b/src/test/run-pass/cabi-int-widening.rs @@ -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 or the MIT license +// , 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); +}