diff --git a/src/librustc_trans/abi.rs b/src/librustc_trans/abi.rs index 9c4246e079b7..40589a989a53 100644 --- a/src/librustc_trans/abi.rs +++ b/src/librustc_trans/abi.rs @@ -25,6 +25,7 @@ use cabi_mips64; use cabi_asmjs; use cabi_msp430; use cabi_sparc; +use cabi_sparc64; use machine::{llalign_of_min, llsize_of, llsize_of_alloc}; use type_::Type; use type_of; @@ -608,6 +609,7 @@ impl FnType { "wasm32" => cabi_asmjs::compute_abi_info(ccx, self), "msp430" => cabi_msp430::compute_abi_info(ccx, self), "sparc" => cabi_sparc::compute_abi_info(ccx, self), + "sparc64" => cabi_sparc64::compute_abi_info(ccx, self), a => ccx.sess().fatal(&format!("unrecognized arch \"{}\" in target specification", a)) } diff --git a/src/librustc_trans/cabi_sparc64.rs b/src/librustc_trans/cabi_sparc64.rs new file mode 100644 index 000000000000..e675cca33d1b --- /dev/null +++ b/src/librustc_trans/cabi_sparc64.rs @@ -0,0 +1,185 @@ +// Copyright 2014-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. + +// FIXME: This needs an audit for correctness and completeness. + +use llvm::{Integer, Pointer, Float, Double, Struct, Vector, Array}; +use abi::{self, FnType, ArgType}; +use context::CrateContext; +use type_::Type; + +fn ty_size(ty: Type) -> usize { + if ty.kind() == Vector { + bug!("ty_size: unhandled type") + } else { + abi::ty_size(ty, 8) + } +} + +fn is_homogenous_aggregate_ty(ty: Type) -> Option<(Type, u64)> { + fn check_array(ty: Type) -> Option<(Type, u64)> { + let len = ty.array_length() as u64; + if len == 0 { + return None + } + let elt = ty.element_type(); + + // if our element is an HFA/HVA, so are we; multiply members by our len + is_homogenous_aggregate_ty(elt).map(|(base_ty, members)| (base_ty, len * members)) + } + + fn check_struct(ty: Type) -> Option<(Type, u64)> { + let str_tys = ty.field_types(); + if str_tys.len() == 0 { + return None + } + + let mut prev_base_ty = None; + let mut members = 0; + for opt_homog_agg in str_tys.iter().map(|t| is_homogenous_aggregate_ty(*t)) { + match (prev_base_ty, opt_homog_agg) { + // field isn't itself an HFA, so we aren't either + (_, None) => return None, + + // first field - store its type and number of members + (None, Some((field_ty, field_members))) => { + prev_base_ty = Some(field_ty); + members = field_members; + }, + + // 2nd or later field - give up if it's a different type; otherwise incr. members + (Some(prev_ty), Some((field_ty, field_members))) => { + if prev_ty != field_ty { + return None; + } + members += field_members; + } + } + } + + // Because of previous checks, we know prev_base_ty is Some(...) because + // 1. str_tys has at least one element; and + // 2. prev_base_ty was filled in (or we would've returned early) + let (base_ty, members) = (prev_base_ty.unwrap(), members); + + // Ensure there is no padding. + if ty_size(ty) == ty_size(base_ty) * (members as usize) { + Some((base_ty, members)) + } else { + None + } + } + + let homog_agg = match ty.kind() { + Float => Some((ty, 1)), + Double => Some((ty, 1)), + Array => check_array(ty), + Struct => check_struct(ty), + _ => None + }; + + // Ensure we have at most eight uniquely addressable members + homog_agg.and_then(|(base_ty, members)| { + if members > 0 && members <= 8 { + Some((base_ty, members)) + } else { + None + } + }) +} + +fn classify_ret_ty(ccx: &CrateContext, ret: &mut ArgType) { + if is_reg_ty(ret.ty) { + ret.extend_integer_width_to(64); + return; + } + + // don't return aggregates in registers + ret.make_indirect(ccx); + + if let Some((base_ty, members)) = is_homogenous_aggregate_ty(ret.ty) { + ret.cast = Some(Type::array(&base_ty, members)); + return; + } + let size = ty_size(ret.ty); + if size <= 16 { + let llty = if size <= 1 { + Type::i8(ccx) + } else if size <= 2 { + Type::i16(ccx) + } else if size <= 4 { + Type::i32(ccx) + } else if size <= 8 { + Type::i64(ccx) + } else { + Type::array(&Type::i64(ccx), ((size + 7 ) / 8 ) as u64) + }; + ret.cast = Some(llty); + return; + } +} + +fn classify_arg_ty(ccx: &CrateContext, arg: &mut ArgType) { + if is_reg_ty(arg.ty) { + arg.extend_integer_width_to(64); + return; + } + + if let Some((base_ty, members)) = is_homogenous_aggregate_ty(arg.ty) { + arg.cast = Some(Type::array(&base_ty, members)); + return; + } + + arg.cast = Some(struct_ty(ccx, arg.ty)); +} + +fn is_reg_ty(ty: Type) -> bool { + match ty.kind() { + Integer + | Pointer + | Float + | Double => true, + _ => false + } +} + +fn coerce_to_long(ccx: &CrateContext, size: usize) -> Vec { + let long_ty = Type::i64(ccx); + let mut args = Vec::new(); + + let mut n = size / 64; + while n > 0 { + args.push(long_ty); + n -= 1; + } + + let r = size % 64; + if r > 0 { + args.push(Type::ix(ccx, r as u64)); + } + + args +} + +fn struct_ty(ccx: &CrateContext, ty: Type) -> Type { + let size = ty_size(ty) * 8; + Type::struct_(ccx, &coerce_to_long(ccx, size), false) +} + +pub fn compute_abi_info(ccx: &CrateContext, fty: &mut FnType) { + if !fty.ret.is_ignore() { + classify_ret_ty(ccx, &mut fty.ret); + } + + for arg in &mut fty.args { + if arg.is_ignore() { continue; } + classify_arg_ty(ccx, arg); + } +} diff --git a/src/librustc_trans/lib.rs b/src/librustc_trans/lib.rs index 3a8eef131a29..e02b595bf884 100644 --- a/src/librustc_trans/lib.rs +++ b/src/librustc_trans/lib.rs @@ -107,6 +107,7 @@ mod cabi_powerpc; mod cabi_powerpc64; mod cabi_s390x; mod cabi_sparc; +mod cabi_sparc64; mod cabi_x86; mod cabi_x86_64; mod cabi_x86_win64;