use abi::{ArgAttribute, FnType, LayoutExt, Reg, RegKind};
use common::CrateContext;
+use rustc::ty::layout::{self, Layout, TyLayout};
+
#[derive(PartialEq)]
pub enum Flavor {
General,
Fastcall
}
+fn is_single_fp_element<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
+ layout: TyLayout<'tcx>) -> bool {
+ match *layout {
+ Layout::Scalar { value: layout::F32, .. } |
+ Layout::Scalar { value: layout::F64, .. } => true,
+ Layout::Univariant { .. } => {
+ if layout.field_count() == 1 {
+ is_single_fp_element(ccx, layout.field(ccx, 0))
+ } else {
+ false
+ }
+ }
+ _ => false
+ }
+}
+
pub fn compute_abi_info<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
fty: &mut FnType<'tcx>,
flavor: Flavor) {
if t.options.is_like_osx || t.options.is_like_windows
|| t.options.is_like_openbsd {
let size = fty.ret.layout.size(ccx);
- match size.bytes() {
- 1 => fty.ret.cast_to(ccx, Reg::i8()),
- 2 => fty.ret.cast_to(ccx, Reg::i16()),
- 4 => fty.ret.cast_to(ccx, Reg::i32()),
- 8 => fty.ret.cast_to(ccx, Reg::i64()),
- _ => fty.ret.make_indirect(ccx)
+
+ // According to Clang, everyone but MSVC returns single-element
+ // float aggregates directly in a floating-point register.
+ if !t.options.is_like_msvc && is_single_fp_element(ccx, fty.ret.layout) {
+ match size.bytes() {
+ 4 => fty.ret.cast_to(ccx, Reg::f32()),
+ 8 => fty.ret.cast_to(ccx, Reg::f64()),
+ _ => fty.ret.make_indirect(ccx)
+ }
+ } else {
+ match size.bytes() {
+ 1 => fty.ret.cast_to(ccx, Reg::i8()),
+ 2 => fty.ret.cast_to(ccx, Reg::i16()),
+ 4 => fty.ret.cast_to(ccx, Reg::i32()),
+ 8 => fty.ret.cast_to(ccx, Reg::i64()),
+ _ => fty.ret.make_indirect(ccx)
+ }
}
} else {
fty.ret.make_indirect(ccx);