From c461cdfdf686caec2b93fe9e41efaa79b7a5c292 Mon Sep 17 00:00:00 2001 From: Ivan Molodetskikh Date: Wed, 21 Dec 2016 21:42:10 +0300 Subject: [PATCH 1/2] Fixed fastcall not applying inreg attributes to arguments like the C/C++ fastcall. --- src/librustc_llvm/ffi.rs | 1 + src/librustc_trans/abi.rs | 14 +++++++-- src/librustc_trans/cabi_x86.rs | 55 +++++++++++++++++++++++++++++++++- src/rustllvm/RustWrapper.cpp | 2 ++ src/rustllvm/rustllvm.h | 1 + 5 files changed, 69 insertions(+), 4 deletions(-) diff --git a/src/librustc_llvm/ffi.rs b/src/librustc_llvm/ffi.rs index d2b86ade7a2ab..f3dbac7ce682e 100644 --- a/src/librustc_llvm/ffi.rs +++ b/src/librustc_llvm/ffi.rs @@ -117,6 +117,7 @@ pub enum Attribute { StructRet = 16, UWTable = 17, ZExt = 18, + InReg = 19, } /// LLVMIntPredicate diff --git a/src/librustc_trans/abi.rs b/src/librustc_trans/abi.rs index 8b4343af1990f..f0f2511899066 100644 --- a/src/librustc_trans/abi.rs +++ b/src/librustc_trans/abi.rs @@ -58,7 +58,7 @@ mod attr_impl { // The subset of llvm::Attribute needed for arguments, packed into a bitfield. bitflags! { #[derive(Default, Debug)] - flags ArgAttribute : u8 { + flags ArgAttribute : u16 { const ByVal = 1 << 0, const NoAlias = 1 << 1, const NoCapture = 1 << 2, @@ -67,6 +67,7 @@ mod attr_impl { const SExt = 1 << 5, const StructRet = 1 << 6, const ZExt = 1 << 7, + const InReg = 1 << 8, } } } @@ -80,7 +81,7 @@ macro_rules! for_each_kind { impl ArgAttribute { fn for_each_kind(&self, mut f: F) where F: FnMut(llvm::Attribute) { for_each_kind!(self, f, - ByVal, NoAlias, NoCapture, NonNull, ReadOnly, SExt, StructRet, ZExt) + ByVal, NoAlias, NoCapture, NonNull, ReadOnly, SExt, StructRet, ZExt, InReg) } } @@ -573,7 +574,14 @@ impl FnType { } match &ccx.sess().target.target.arch[..] { - "x86" => cabi_x86::compute_abi_info(ccx, self), + "x86" => { + let flavor = if abi == Abi::Fastcall { + cabi_x86::Flavor::Fastcall + } else { + cabi_x86::Flavor::General + }; + cabi_x86::compute_abi_info(ccx, self, flavor); + }, "x86_64" => if abi == Abi::SysV64 { cabi_x86_64::compute_abi_info(ccx, self); } else if abi == Abi::Win64 || ccx.sess().target.target.options.is_like_windows { diff --git a/src/librustc_trans/cabi_x86.rs b/src/librustc_trans/cabi_x86.rs index ce85234f2034a..ae8b610882bb0 100644 --- a/src/librustc_trans/cabi_x86.rs +++ b/src/librustc_trans/cabi_x86.rs @@ -14,7 +14,13 @@ use type_::Type; use super::common::*; use super::machine::*; -pub fn compute_abi_info(ccx: &CrateContext, fty: &mut FnType) { +#[derive(PartialEq)] +pub enum Flavor { + General, + Fastcall +} + +pub fn compute_abi_info(ccx: &CrateContext, fty: &mut FnType, flavor: Flavor) { if !fty.ret.is_ignore() { if fty.ret.ty.kind() == Struct { // Returning a structure. Most often, this will use @@ -51,4 +57,51 @@ pub fn compute_abi_info(ccx: &CrateContext, fty: &mut FnType) { arg.extend_integer_width_to(32); } } + + if flavor == Flavor::Fastcall { + // Mark arguments as InReg like clang does it, + // so our fastcall is compatible with C/C++ fastcall. + // Clang reference: ib/CodeGen/TargetInfo.cpp + let is_mcu_abi = ccx.sess().target.target.target_os.eq("elfiamcu"); + let is_soft_float_abi = ccx.sess().target.target.options.features.contains("+soft-float"); + + let mut free_regs = 2; + + for arg in &mut fty.args { + if !arg.is_ignore() && !arg.is_indirect() { + if !is_soft_float_abi { + if arg.ty.kind() == Float { + continue; + } + } + + let size = llbitsize_of_real(ccx, arg.ty); + let size_in_regs = (size + 31) / 32; + + if size_in_regs == 0 { + continue; + } + + if !is_mcu_abi { + if size_in_regs > free_regs { + break; + } + } else { + if size_in_regs > free_regs || size_in_regs > 2 { + continue; + } + } + + free_regs -= size_in_regs; + + if !is_mcu_abi && size <= 32 && (arg.ty.kind() == Pointer || arg.ty.kind() == Integer) { + arg.attrs.set(ArgAttribute::InReg); + } + + if free_regs == 0 { + break; + } + } + } + } } diff --git a/src/rustllvm/RustWrapper.cpp b/src/rustllvm/RustWrapper.cpp index f5fa66f1b0e5a..5d5845213e230 100644 --- a/src/rustllvm/RustWrapper.cpp +++ b/src/rustllvm/RustWrapper.cpp @@ -150,6 +150,8 @@ from_rust(LLVMRustAttribute kind) { return Attribute::UWTable; case ZExt: return Attribute::ZExt; + case InReg: + return Attribute::InReg; default: llvm_unreachable("bad AttributeKind"); } diff --git a/src/rustllvm/rustllvm.h b/src/rustllvm/rustllvm.h index b8c4076f4ce45..8f7e0e3d91875 100644 --- a/src/rustllvm/rustllvm.h +++ b/src/rustllvm/rustllvm.h @@ -98,6 +98,7 @@ enum LLVMRustAttribute { StructRet = 16, UWTable = 17, ZExt = 18, + InReg = 19, }; typedef struct OpaqueRustString *RustStringRef; From 5e2cea9a4e270544d48ee7634ef3a7b6f67163a4 Mon Sep 17 00:00:00 2001 From: Ivan Molodetskikh Date: Thu, 22 Dec 2016 11:47:26 +0300 Subject: [PATCH 2/2] Cleaned up the code and added tests. --- src/librustc_trans/cabi_x86.rs | 55 +++++++++---------- src/test/codegen/fastcall-inreg.rs | 85 ++++++++++++++++++++++++++++++ 2 files changed, 110 insertions(+), 30 deletions(-) create mode 100644 src/test/codegen/fastcall-inreg.rs diff --git a/src/librustc_trans/cabi_x86.rs b/src/librustc_trans/cabi_x86.rs index ae8b610882bb0..fea005f3d77da 100644 --- a/src/librustc_trans/cabi_x86.rs +++ b/src/librustc_trans/cabi_x86.rs @@ -61,46 +61,41 @@ pub fn compute_abi_info(ccx: &CrateContext, fty: &mut FnType, flavor: Flavor) { if flavor == Flavor::Fastcall { // Mark arguments as InReg like clang does it, // so our fastcall is compatible with C/C++ fastcall. - // Clang reference: ib/CodeGen/TargetInfo.cpp - let is_mcu_abi = ccx.sess().target.target.target_os.eq("elfiamcu"); - let is_soft_float_abi = ccx.sess().target.target.options.features.contains("+soft-float"); + + // Clang reference: lib/CodeGen/TargetInfo.cpp + // See X86_32ABIInfo::shouldPrimitiveUseInReg(), X86_32ABIInfo::updateFreeRegs() + + // IsSoftFloatABI is only set to true on ARM platforms, + // which in turn can't be x86? let mut free_regs = 2; for arg in &mut fty.args { - if !arg.is_ignore() && !arg.is_indirect() { - if !is_soft_float_abi { - if arg.ty.kind() == Float { - continue; - } - } + if arg.is_ignore() || arg.is_indirect() { continue; } - let size = llbitsize_of_real(ccx, arg.ty); - let size_in_regs = (size + 31) / 32; + if arg.ty.kind() == Float { + continue; + } - if size_in_regs == 0 { - continue; - } + let size = llbitsize_of_real(ccx, arg.ty); + let size_in_regs = (size + 31) / 32; - if !is_mcu_abi { - if size_in_regs > free_regs { - break; - } - } else { - if size_in_regs > free_regs || size_in_regs > 2 { - continue; - } - } + if size_in_regs == 0 { + continue; + } - free_regs -= size_in_regs; + if size_in_regs > free_regs { + break; + } - if !is_mcu_abi && size <= 32 && (arg.ty.kind() == Pointer || arg.ty.kind() == Integer) { - arg.attrs.set(ArgAttribute::InReg); - } + free_regs -= size_in_regs; - if free_regs == 0 { - break; - } + if size <= 32 && (arg.ty.kind() == Pointer || arg.ty.kind() == Integer) { + arg.attrs.set(ArgAttribute::InReg); + } + + if free_regs == 0 { + break; } } } diff --git a/src/test/codegen/fastcall-inreg.rs b/src/test/codegen/fastcall-inreg.rs new file mode 100644 index 0000000000000..e1dda4d2be084 --- /dev/null +++ b/src/test/codegen/fastcall-inreg.rs @@ -0,0 +1,85 @@ +// 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. + +// Checks if the "fastcall" calling convention marks function arguments +// as "inreg" like the C/C++ compilers for the platforms. +// x86 only. + +// ignore-aarch64 +// ignore-aarch64_be +// ignore-arm +// ignore-armeb +// ignore-avr +// ignore-bpfel +// ignore-bpfeb +// ignore-hexagon +// ignore-mips +// ignore-mipsel +// ignore-mips64 +// ignore-mips64el +// ignore-msp430 +// ignore-powerpc64 +// ignore-powerpc64le +// ignore-powerpc +// ignore-r600 +// ignore-amdgcn +// ignore-sparc +// ignore-sparcv9 +// ignore-sparcel +// ignore-s390x +// ignore-tce +// ignore-thumb +// ignore-thumbeb +// ignore-x86_64 no-ignore-x86 +// ignore-xcore +// ignore-nvptx +// ignore-nvptx64 +// ignore-le32 +// ignore-le64 +// ignore-amdil +// ignore-amdil64 +// ignore-hsail +// ignore-hsail64 +// ignore-spir +// ignore-spir64 +// ignore-kalimba +// ignore-shave +// ignore-wasm32 +// ignore-wasm64 + +// compile-flags: -C no-prepopulate-passes + +#![crate_type = "lib"] + +mod tests { + // CHECK: @f1(i32 inreg, i32 inreg, i32) + #[no_mangle] + extern "fastcall" fn f1(_: i32, _: i32, _: i32) {} + + // CHECK: @f2(i32* inreg, i32* inreg, i32*) + #[no_mangle] + extern "fastcall" fn f2(_: *const i32, _: *const i32, _: *const i32) {} + + // CHECK: @f3(float, i32 inreg, i32 inreg, i32) + #[no_mangle] + extern "fastcall" fn f3(_: f32, _: i32, _: i32, _: i32) {} + + // CHECK: @f4(i32 inreg, float, i32 inreg, i32) + #[no_mangle] + extern "fastcall" fn f4(_: i32, _: f32, _: i32, _: i32) {} + + // CHECK: @f5(i64, i32) + #[no_mangle] + extern "fastcall" fn f5(_: i64, _: i32) {} + + // CHECK: @f6(i1 inreg zeroext, i32 inreg, i32) + #[no_mangle] + extern "fastcall" fn f6(_: bool, _: i32, _: i32) {} +}