blob: 68a2e8aa8ce95d8c38bfd91ad30db2cc08927a39 [file] [log] [blame]
// Copyright 2012-2013 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.
#![allow(non_upper_case_globals)]
use llvm::{Integer, Pointer, Float, Double, Struct, Array, Vector};
use abi::{FnType, ArgType};
use context::CrateContext;
use type_::Type;
use std::cmp;
pub enum Flavor {
General,
Ios
}
type TyAlignFn = fn(ty: Type) -> usize;
fn align_up_to(off: usize, a: usize) -> usize {
return (off + a - 1) / a * a;
}
fn align(off: usize, ty: Type, align_fn: TyAlignFn) -> usize {
let a = align_fn(ty);
return align_up_to(off, a);
}
fn general_ty_align(ty: Type) -> usize {
match ty.kind() {
Integer => ((ty.int_width() as usize) + 7) / 8,
Pointer => 4,
Float => 4,
Double => 8,
Struct => {
if ty.is_packed() {
1
} else {
let str_tys = ty.field_types();
str_tys.iter().fold(1, |a, t| cmp::max(a, general_ty_align(*t)))
}
}
Array => {
let elt = ty.element_type();
general_ty_align(elt)
}
Vector => {
let len = ty.vector_length();
let elt = ty.element_type();
general_ty_align(elt) * len
}
_ => bug!("ty_align: unhandled type")
}
}
// For more information see:
// ARMv7
// https://developer.apple.com/library/ios/documentation/Xcode/Conceptual
// /iPhoneOSABIReference/Articles/ARMv7FunctionCallingConventions.html
// ARMv6
// https://developer.apple.com/library/ios/documentation/Xcode/Conceptual
// /iPhoneOSABIReference/Articles/ARMv6FunctionCallingConventions.html
fn ios_ty_align(ty: Type) -> usize {
match ty.kind() {
Integer => cmp::min(4, ((ty.int_width() as usize) + 7) / 8),
Pointer => 4,
Float => 4,
Double => 4,
Struct => {
if ty.is_packed() {
1
} else {
let str_tys = ty.field_types();
str_tys.iter().fold(1, |a, t| cmp::max(a, ios_ty_align(*t)))
}
}
Array => {
let elt = ty.element_type();
ios_ty_align(elt)
}
Vector => {
let len = ty.vector_length();
let elt = ty.element_type();
ios_ty_align(elt) * len
}
_ => bug!("ty_align: unhandled type")
}
}
fn ty_size(ty: Type, align_fn: TyAlignFn) -> usize {
match ty.kind() {
Integer => ((ty.int_width() as usize) + 7) / 8,
Pointer => 4,
Float => 4,
Double => 8,
Struct => {
if ty.is_packed() {
let str_tys = ty.field_types();
str_tys.iter().fold(0, |s, t| s + ty_size(*t, align_fn))
} else {
let str_tys = ty.field_types();
let size = str_tys.iter()
.fold(0, |s, t| {
align(s, *t, align_fn) + ty_size(*t, align_fn)
});
align(size, ty, align_fn)
}
}
Array => {
let len = ty.array_length();
let elt = ty.element_type();
let eltsz = ty_size(elt, align_fn);
len * eltsz
}
Vector => {
let len = ty.vector_length();
let elt = ty.element_type();
let eltsz = ty_size(elt, align_fn);
len * eltsz
}
_ => bug!("ty_size: unhandled type")
}
}
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);
if size <= 4 {
let llty = if size <= 1 {
Type::i8(ccx)
} else if size <= 2 {
Type::i16(ccx)
} else {
Type::i32(ccx)
};
ret.cast = Some(llty);
return;
}
ret.make_indirect(ccx);
}
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);
let size = ty_size(arg.ty, align_fn);
let llty = if align <= 4 {
Type::array(&Type::i32(ccx), ((size + 3) / 4) as u64)
} else {
Type::array(&Type::i64(ccx), ((size + 7) / 8) as u64)
};
arg.cast = Some(llty);
}
fn is_reg_ty(ty: Type) -> bool {
match ty.kind() {
Integer
| Pointer
| Float
| Double
| Vector => true,
_ => false
}
}
pub fn compute_abi_info(ccx: &CrateContext, fty: &mut FnType, flavor: Flavor) {
let align_fn = match flavor {
Flavor::General => general_ty_align as TyAlignFn,
Flavor::Ios => ios_ty_align as TyAlignFn,
};
if !fty.ret.is_ignore() {
classify_ret_ty(ccx, &mut fty.ret, align_fn);
}
for arg in &mut fty.args {
if arg.is_ignore() { continue; }
classify_arg_ty(ccx, arg, align_fn);
}
}