// option. This file may not be copied, modified, or distributed
// except according to those terms.
-use llvm::{self, ValueRef, Integer, Pointer, Float, Double, Struct, Array, Vector, AttributePlace};
+use llvm::{self, ValueRef, AttributePlace};
use base;
use builder::Builder;
use common::{type_is_fat_ptr, C_uint};
use cabi_sparc64;
use cabi_nvptx;
use cabi_nvptx64;
-use machine::{llalign_of_min, llsize_of, llsize_of_alloc};
+use machine::llalign_of_min;
use type_::Type;
use type_of;
use rustc::hir;
use rustc::ty::{self, Ty};
-use rustc::ty::layout::{Layout, LayoutTyper};
+use rustc::ty::layout::{self, Layout, LayoutTyper, TyLayout, Size};
use libc::c_uint;
use std::cmp;
+use std::iter;
pub use syntax::abi::Abi;
pub use rustc::ty::layout::{FAT_PTR_ADDR, FAT_PTR_EXTRA};
}
}
}
+#[derive(Copy, Clone, PartialEq, Eq, Debug)]
+pub enum RegKind {
+ Integer,
+ Float,
+ Vector
+}
+
+#[derive(Copy, Clone, PartialEq, Eq, Debug)]
+pub struct Reg {
+ pub kind: RegKind,
+ pub size: Size,
+}
+
+macro_rules! reg_ctor {
+ ($name:ident, $kind:ident, $bits:expr) => {
+ pub fn $name() -> Reg {
+ Reg {
+ kind: RegKind::$kind,
+ size: Size::from_bits($bits)
+ }
+ }
+ }
+}
+
+impl Reg {
+ reg_ctor!(i8, Integer, 8);
+ reg_ctor!(i16, Integer, 16);
+ reg_ctor!(i32, Integer, 32);
+ reg_ctor!(i64, Integer, 64);
+
+ reg_ctor!(f32, Float, 32);
+ reg_ctor!(f64, Float, 64);
+}
+
+impl Reg {
+ fn llvm_type(&self, ccx: &CrateContext) -> Type {
+ match self.kind {
+ RegKind::Integer => Type::ix(ccx, self.size.bits()),
+ RegKind::Float => {
+ match self.size.bits() {
+ 32 => Type::f32(ccx),
+ 64 => Type::f64(ccx),
+ _ => bug!("unsupported float: {:?}", self)
+ }
+ }
+ RegKind::Vector => {
+ Type::vector(&Type::i8(ccx), self.size.bytes())
+ }
+ }
+ }
+}
+
+/// An argument passed entirely registers with the
+/// same kind (e.g. HFA / HVA on PPC64 and AArch64).
+#[derive(Copy, Clone)]
+pub struct Uniform {
+ pub unit: Reg,
+
+ /// The total size of the argument, which can be:
+ /// * equal to `unit.size` (one scalar/vector)
+ /// * a multiple of `unit.size` (an array of scalar/vectors)
+ /// * if `unit.kind` is `Integer`, the last element
+ /// can be shorter, i.e. `{ i64, i64, i32 }` for
+ /// 64-bit integers with a total size of 20 bytes
+ pub total: Size,
+}
+
+impl From<Reg> for Uniform {
+ fn from(unit: Reg) -> Uniform {
+ Uniform {
+ unit,
+ total: unit.size
+ }
+ }
+}
+
+impl Uniform {
+ fn llvm_type(&self, ccx: &CrateContext) -> Type {
+ let llunit = self.unit.llvm_type(ccx);
+
+ if self.total <= self.unit.size {
+ return llunit;
+ }
+
+ let count = self.total.bytes() / self.unit.size.bytes();
+ let rem_bytes = self.total.bytes() % self.unit.size.bytes();
+
+ if rem_bytes == 0 {
+ return Type::array(&llunit, count);
+ }
+
+ // Only integers can be really split further.
+ assert_eq!(self.unit.kind, RegKind::Integer);
+
+ let args: Vec<_> = (0..count).map(|_| llunit)
+ .chain(iter::once(Type::ix(ccx, rem_bytes * 8)))
+ .collect();
+
+ Type::struct_(ccx, &args, false)
+ }
+}
+
+pub trait LayoutExt<'tcx> {
+ fn is_aggregate(&self) -> bool;
+ fn homogenous_aggregate<'a>(&self, ccx: &CrateContext<'a, 'tcx>) -> Option<Reg>;
+}
+
+impl<'tcx> LayoutExt<'tcx> for TyLayout<'tcx> {
+ fn is_aggregate(&self) -> bool {
+ match *self.layout {
+ Layout::Scalar { .. } |
+ Layout::RawNullablePointer { .. } |
+ Layout::CEnum { .. } |
+ Layout::Vector { .. } => false,
+
+ Layout::Array { .. } |
+ Layout::FatPointer { .. } |
+ Layout::Univariant { .. } |
+ Layout::UntaggedUnion { .. } |
+ Layout::General { .. } |
+ Layout::StructWrappedNullablePointer { .. } => true
+ }
+ }
+
+ fn homogenous_aggregate<'a>(&self, ccx: &CrateContext<'a, 'tcx>) -> Option<Reg> {
+ match *self.layout {
+ // The primitives for this algorithm.
+ Layout::Scalar { value, .. } |
+ Layout::RawNullablePointer { value, .. } => {
+ let kind = match value {
+ layout::Int(_) |
+ layout::Pointer => RegKind::Integer,
+ layout::F32 |
+ layout::F64 => RegKind::Float
+ };
+ Some(Reg {
+ kind,
+ size: self.size(ccx)
+ })
+ }
+
+ Layout::CEnum { .. } => {
+ Some(Reg {
+ kind: RegKind::Integer,
+ size: self.size(ccx)
+ })
+ }
+
+ Layout::Vector { .. } => {
+ Some(Reg {
+ kind: RegKind::Integer,
+ size: self.size(ccx)
+ })
+ }
+
+ Layout::Array { count, .. } => {
+ if count > 0 {
+ self.field(ccx, 0).homogenous_aggregate(ccx)
+ } else {
+ None
+ }
+ }
+
+ Layout::Univariant { ref variant, .. } => {
+ let mut unaligned_offset = Size::from_bytes(0);
+ let mut result = None;
+
+ for i in 0..self.field_count() {
+ if unaligned_offset != variant.offsets[i] {
+ return None;
+ }
+
+ let field = self.field(ccx, i);
+ match (result, field.homogenous_aggregate(ccx)) {
+ // The field itself must be a homogenous aggregate.
+ (_, None) => return None,
+ // If this is the first field, record the unit.
+ (None, Some(unit)) => {
+ result = Some(unit);
+ }
+ // For all following fields, the unit must be the same.
+ (Some(prev_unit), Some(unit)) => {
+ if prev_unit != unit {
+ return None;
+ }
+ }
+ }
+
+ // Keep track of the offset (without padding).
+ let size = field.size(ccx);
+ match unaligned_offset.checked_add(size, ccx) {
+ Some(offset) => unaligned_offset = offset,
+ None => return None
+ }
+ }
+
+ // There needs to be no padding.
+ if unaligned_offset != self.size(ccx) {
+ None
+ } else {
+ result
+ }
+ }
+
+ Layout::UntaggedUnion { .. } => {
+ let mut max = Size::from_bytes(0);
+ let mut result = None;
+
+ for i in 0..self.field_count() {
+ let field = self.field(ccx, i);
+ match (result, field.homogenous_aggregate(ccx)) {
+ // The field itself must be a homogenous aggregate.
+ (_, None) => return None,
+ // If this is the first field, record the unit.
+ (None, Some(unit)) => {
+ result = Some(unit);
+ }
+ // For all following fields, the unit must be the same.
+ (Some(prev_unit), Some(unit)) => {
+ if prev_unit != unit {
+ return None;
+ }
+ }
+ }
+
+ // Keep track of the offset (without padding).
+ let size = field.size(ccx);
+ if size > max {
+ max = size;
+ }
+ }
+
+ // There needs to be no padding.
+ if max != self.size(ccx) {
+ None
+ } else {
+ result
+ }
+ }
+
+ // Rust-specific types, which we can ignore for C ABIs.
+ Layout::FatPointer { .. } |
+ Layout::General { .. } |
+ Layout::StructWrappedNullablePointer { .. } => None
+ }
+ }
+}
+
+pub enum CastTarget {
+ Uniform(Uniform),
+ Pair(Reg, Reg)
+}
+
+impl From<Reg> for CastTarget {
+ fn from(unit: Reg) -> CastTarget {
+ CastTarget::Uniform(Uniform::from(unit))
+ }
+}
+
+impl From<Uniform> for CastTarget {
+ fn from(uniform: Uniform) -> CastTarget {
+ CastTarget::Uniform(uniform)
+ }
+}
+
+impl CastTarget {
+ fn llvm_type(&self, ccx: &CrateContext) -> Type {
+ match *self {
+ CastTarget::Uniform(u) => u.llvm_type(ccx),
+ CastTarget::Pair(a, b) => {
+ Type::struct_(ccx, &[
+ a.llvm_type(ccx),
+ b.llvm_type(ccx)
+ ], false)
+ }
+ }
+ }
+}
/// Information about how a specific C type
/// should be passed to or returned from a function
///
/// This is borrowed from clang's ABIInfo.h
#[derive(Clone, Copy, Debug)]
-pub struct ArgType {
+pub struct ArgType<'tcx> {
kind: ArgKind,
- /// Original LLVM type
- pub original_ty: Type,
- /// Sizing LLVM type (pointers are opaque).
- /// Unlike original_ty, this is guaranteed to be complete.
- ///
- /// For example, while we're computing the function pointer type in
- /// `struct Foo(fn(Foo));`, `original_ty` is still LLVM's `%Foo = {}`.
- /// The field type will likely end up being `void(%Foo)*`, but we cannot
- /// use `%Foo` to compute properties (e.g. size and alignment) of `Foo`,
- /// until `%Foo` is completed by having all of its field types inserted,
- /// so `ty` holds the "sizing type" of `Foo`, which replaces all pointers
- /// with opaque ones, resulting in `{i8*}` for `Foo`.
- /// ABI-specific logic can then look at the size, alignment and fields of
- /// `{i8*}` in order to determine how the argument will be passed.
- /// 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<bool>,
+ pub layout: TyLayout<'tcx>,
/// Coerced LLVM Type
pub cast: Option<Type>,
/// Dummy argument, which is emitted before the real argument
pub attrs: ArgAttributes
}
-impl ArgType {
- fn new(original_ty: Type, ty: Type) -> ArgType {
+impl<'a, 'tcx> ArgType<'tcx> {
+ fn new(layout: TyLayout<'tcx>) -> ArgType<'tcx> {
ArgType {
kind: ArgKind::Direct,
- original_ty: original_ty,
- ty: ty,
- signedness: None,
+ layout: layout,
cast: None,
pad: None,
attrs: ArgAttributes::default()
}
}
- pub fn make_indirect(&mut self, ccx: &CrateContext) {
+ pub fn make_indirect(&mut self, ccx: &CrateContext<'a, 'tcx>) {
assert_eq!(self.kind, ArgKind::Direct);
// Wipe old attributes, likely not valid through indirection.
self.attrs = ArgAttributes::default();
- let llarg_sz = llsize_of_alloc(ccx, self.ty);
+ let llarg_sz = self.layout.size(ccx).bytes();
// For non-immediate arguments the callee gets its own copy of
// the value on the stack, so there are no aliases. It's also
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 {
- ArgAttribute::SExt
- } else {
- ArgAttribute::ZExt
- });
+ let (i, signed) = match *self.layout {
+ Layout::Scalar { value, .. } => {
+ match value {
+ layout::Int(i) => {
+ if self.layout.ty.is_integral() {
+ (i, self.layout.ty.is_signed())
+ } else {
+ return;
+ }
+ }
+ _ => return
+ }
}
+
+ // Rust enum types that map onto C enums also need to follow
+ // the target ABI zero-/sign-extension rules.
+ Layout::CEnum { discr, signed, .. } => (discr, signed),
+
+ _ => return
+ };
+
+ if i.size().bits() < bits {
+ self.attrs.set(if signed {
+ ArgAttribute::SExt
+ } else {
+ ArgAttribute::ZExt
+ });
}
}
+ pub fn cast_to<T: Into<CastTarget>>(&mut self, ccx: &CrateContext, target: T) {
+ self.cast = Some(target.into().llvm_type(ccx));
+ }
+
+ pub fn pad_with(&mut self, ccx: &CrateContext, reg: Reg) {
+ self.pad = Some(reg.llvm_type(ccx));
+ }
+
pub fn is_indirect(&self) -> bool {
self.kind == ArgKind::Indirect
}
self.kind == ArgKind::Ignore
}
+ /// Get the LLVM type for an lvalue of the original Rust type of
+ /// this argument/return, i.e. the result of `type_of::type_of`.
+ pub fn memory_ty(&self, ccx: &CrateContext<'a, 'tcx>) -> Type {
+ type_of::type_of(ccx, self.layout.ty)
+ }
+
/// Store a direct/indirect value described by this ArgType into a
/// lvalue for the original Rust type of this argument/return.
/// Can be used for both storing formal arguments into Rust variables
/// or results of call/invoke instructions into their destinations.
- pub fn store(&self, bcx: &Builder, mut val: ValueRef, dst: ValueRef) {
+ pub fn store(&self, bcx: &Builder<'a, 'tcx>, mut val: ValueRef, dst: ValueRef) {
if self.is_ignore() {
return;
}
let ccx = bcx.ccx;
if self.is_indirect() {
- let llsz = llsize_of(ccx, self.ty);
- let llalign = llalign_of_min(ccx, self.ty);
+ let llsz = C_uint(ccx, self.layout.size(ccx).bytes());
+ let llalign = self.layout.align(ccx).abi();
base::call_memcpy(bcx, dst, val, llsz, llalign as u32);
} else if let Some(ty) = self.cast {
// FIXME(eddyb): Figure out when the simpler Store is safe, clang
let can_store_through_cast_ptr = false;
if can_store_through_cast_ptr {
let cast_dst = bcx.pointercast(dst, ty.ptr_to());
- let llalign = llalign_of_min(ccx, self.ty);
- bcx.store(val, cast_dst, Some(llalign));
+ let llalign = self.layout.align(ccx).abi();
+ bcx.store(val, cast_dst, Some(llalign as u32));
} else {
// The actual return type is a struct, but the ABI
// adaptation code has cast it into some scalar type. The
base::call_memcpy(bcx,
bcx.pointercast(dst, Type::i8p(ccx)),
bcx.pointercast(llscratch, Type::i8p(ccx)),
- C_uint(ccx, llsize_of_alloc(ccx, self.ty)),
- cmp::min(llalign_of_min(ccx, self.ty),
- llalign_of_min(ccx, ty)) as u32);
+ C_uint(ccx, self.layout.size(ccx).bytes()),
+ cmp::min(self.layout.align(ccx).abi() as u32,
+ llalign_of_min(ccx, ty)));
base::Lifetime::End.call(bcx, llscratch);
}
} else {
- if self.original_ty == Type::i1(ccx) {
+ if self.layout.ty == ccx.tcx().types.bool {
val = bcx.zext(val, Type::i8(ccx));
}
bcx.store(val, dst, None);
}
}
- pub fn store_fn_arg(&self, bcx: &Builder, idx: &mut usize, dst: ValueRef) {
+ pub fn store_fn_arg(&self, bcx: &Builder<'a, 'tcx>, idx: &mut usize, dst: ValueRef) {
if self.pad.is_some() {
*idx += 1;
}
/// I will do my best to describe this structure, but these
/// comments are reverse-engineered and may be inaccurate. -NDM
#[derive(Clone, Debug)]
-pub struct FnType {
+pub struct FnType<'tcx> {
/// The LLVM types of each argument.
- pub args: Vec<ArgType>,
+ pub args: Vec<ArgType<'tcx>>,
/// LLVM return type.
- pub ret: ArgType,
+ pub ret: ArgType<'tcx>,
pub variadic: bool,
pub cconv: llvm::CallConv
}
-impl FnType {
- pub fn new<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
- sig: ty::FnSig<'tcx>,
- extra_args: &[Ty<'tcx>]) -> FnType {
+impl<'a, 'tcx> FnType<'tcx> {
+ pub fn new(ccx: &CrateContext<'a, 'tcx>,
+ sig: ty::FnSig<'tcx>,
+ extra_args: &[Ty<'tcx>]) -> FnType<'tcx> {
let mut fn_ty = FnType::unadjusted(ccx, sig, extra_args);
fn_ty.adjust_for_abi(ccx, sig);
fn_ty
}
- pub fn new_vtable<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
- sig: ty::FnSig<'tcx>,
- extra_args: &[Ty<'tcx>]) -> FnType {
+ pub fn new_vtable(ccx: &CrateContext<'a, 'tcx>,
+ sig: ty::FnSig<'tcx>,
+ extra_args: &[Ty<'tcx>]) -> FnType<'tcx> {
let mut fn_ty = FnType::unadjusted(ccx, sig, extra_args);
// Don't pass the vtable, it's not an argument of the virtual fn.
fn_ty.args[1].ignore();
fn_ty
}
- fn unadjusted<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
- sig: ty::FnSig<'tcx>,
- extra_args: &[Ty<'tcx>]) -> FnType {
+ pub fn unadjusted(ccx: &CrateContext<'a, 'tcx>,
+ sig: ty::FnSig<'tcx>,
+ extra_args: &[Ty<'tcx>]) -> FnType<'tcx> {
use self::Abi::*;
let cconv = match ccx.sess().target.target.adjust_abi(sig.abi) {
RustIntrinsic | PlatformIntrinsic |
};
let arg_of = |ty: Ty<'tcx>, is_return: bool| {
+ let mut arg = ArgType::new(ccx.layout_of(ty));
if ty.is_bool() {
- let llty = Type::i1(ccx);
- let mut arg = ArgType::new(llty, llty);
arg.attrs.set(ArgAttribute::ZExt);
- arg
} 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());
- }
- // Rust enum types that map onto C enums also need to follow
- // the target ABI zero-/sign-extension rules.
- if let Layout::CEnum { signed, .. } = *ccx.layout_of(ty) {
- arg.signedness = Some(signed);
- }
- if llsize_of_alloc(ccx, arg.ty) == 0 {
+ if arg.layout.size(ccx).bytes() == 0 {
// For some forsaken reason, x86_64-pc-windows-gnu
// doesn't ignore zero-sized struct arguments.
// The same is true for s390x-unknown-linux-gnu.
arg.ignore();
}
}
- arg
}
+ arg
};
let ret_ty = sig.output();
for ty in inputs.iter().chain(extra_args.iter()) {
let mut arg = arg_of(ty, false);
- if type_is_fat_ptr(ccx, ty) {
- let original_tys = arg.original_ty.field_types();
- let sizing_tys = arg.ty.field_types();
- assert_eq!((original_tys.len(), sizing_tys.len()), (2, 2));
-
- let mut data = ArgType::new(original_tys[0], sizing_tys[0]);
- let mut info = ArgType::new(original_tys[1], sizing_tys[1]);
+ if let ty::layout::FatPointer { .. } = *arg.layout {
+ let mut data = ArgType::new(arg.layout.field(ccx, 0));
+ let mut info = ArgType::new(arg.layout.field(ccx, 1));
if let Some(inner) = rust_ptr_attrs(ty, &mut data) {
data.attrs.set(ArgAttribute::NonNull);
}
}
- fn adjust_for_abi<'a, 'tcx>(&mut self,
- ccx: &CrateContext<'a, 'tcx>,
- sig: ty::FnSig<'tcx>) {
+ fn adjust_for_abi(&mut self,
+ ccx: &CrateContext<'a, 'tcx>,
+ sig: ty::FnSig<'tcx>) {
let abi = sig.abi;
if abi == Abi::Unadjusted { return }
if abi == Abi::Rust || abi == Abi::RustCall ||
abi == Abi::RustIntrinsic || abi == Abi::PlatformIntrinsic {
- let fixup = |arg: &mut ArgType| {
- let mut llty = arg.ty;
-
- // Replace newtypes with their inner-most type.
- while llty.kind() == llvm::TypeKind::Struct {
- let inner = llty.field_types();
- if inner.len() != 1 {
- break;
- }
- llty = inner[0];
+ let fixup = |arg: &mut ArgType<'tcx>| {
+ if !arg.layout.is_aggregate() {
+ return;
}
- if !llty.is_aggregate() {
- // Scalars and vectors, always immediate.
- if llty != arg.ty {
+ let size = arg.layout.size(ccx);
+
+ if let Some(unit) = arg.layout.homogenous_aggregate(ccx) {
+ // Replace newtypes with their inner-most type.
+ if unit.size == size {
// Needs a cast as we've unpacked a newtype.
- arg.cast = Some(llty);
+ arg.cast_to(ccx, unit);
+ return;
+ }
+
+ // Pairs of floats.
+ if unit.kind == RegKind::Float {
+ if unit.size.checked_mul(2, ccx) == Some(size) {
+ // FIXME(eddyb) This should be using Uniform instead of a pair,
+ // but the resulting [2 x float/double] breaks emscripten.
+ // See https://github.com/kripken/emscripten-fastcomp/issues/178.
+ arg.cast_to(ccx, CastTarget::Pair(unit, unit));
+ return;
+ }
}
- return;
}
- let size = llsize_of_alloc(ccx, llty);
- if size > llsize_of_alloc(ccx, ccx.int_type()) {
+ if size > layout::Pointer.size(ccx) {
arg.make_indirect(ccx);
- } else if size > 0 {
+ } else {
// We want to pass small aggregates as immediates, but using
// a LLVM aggregate type for this leads to bad optimizations,
// so we pick an appropriately sized integer type instead.
- arg.cast = Some(Type::ix(ccx, size * 8));
+ arg.cast_to(ccx, Reg {
+ kind: RegKind::Integer,
+ size
+ });
}
};
// Fat pointers are returned by-value.
cabi_x86_64::compute_abi_info(ccx, self);
},
"aarch64" => cabi_aarch64::compute_abi_info(ccx, self),
- "arm" => {
- let flavor = if ccx.sess().target.target.target_os == "ios" {
- cabi_arm::Flavor::Ios
- } else {
- cabi_arm::Flavor::General
- };
- cabi_arm::compute_abi_info(ccx, self, flavor);
- },
+ "arm" => cabi_arm::compute_abi_info(ccx, self),
"mips" => cabi_mips::compute_abi_info(ccx, self),
"mips64" => cabi_mips64::compute_abi_info(ccx, self),
"powerpc" => cabi_powerpc::compute_abi_info(ccx, self),
}
}
- pub fn llvm_type(&self, ccx: &CrateContext) -> Type {
+ pub fn llvm_type(&self, ccx: &CrateContext<'a, 'tcx>) -> Type {
let mut llargument_tys = Vec::new();
let llreturn_ty = if self.ret.is_ignore() {
Type::void(ccx)
} else if self.ret.is_indirect() {
- llargument_tys.push(self.ret.original_ty.ptr_to());
+ llargument_tys.push(self.ret.memory_ty(ccx).ptr_to());
Type::void(ccx)
} else {
- self.ret.cast.unwrap_or(self.ret.original_ty)
+ self.ret.cast.unwrap_or_else(|| {
+ type_of::immediate_type_of(ccx, self.ret.layout.ty)
+ })
};
for arg in &self.args {
}
let llarg_ty = if arg.is_indirect() {
- arg.original_ty.ptr_to()
+ arg.memory_ty(ccx).ptr_to()
} else {
- arg.cast.unwrap_or(arg.original_ty)
+ arg.cast.unwrap_or_else(|| {
+ type_of::immediate_type_of(ccx, arg.layout.ty)
+ })
};
llargument_tys.push(llarg_ty);
}
}
-pub fn align_up_to(off: usize, a: usize) -> usize {
- return (off + a - 1) / a * a;
-}
-
-fn align(off: usize, ty: Type, pointer: usize) -> usize {
- let a = ty_align(ty, pointer);
- return align_up_to(off, a);
-}
-
-pub fn ty_align(ty: Type, pointer: usize) -> usize {
- match ty.kind() {
- Integer => ((ty.int_width() as usize) + 7) / 8,
- Pointer => pointer,
- 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, ty_align(*t, pointer)))
- }
- }
- Array => {
- let elt = ty.element_type();
- ty_align(elt, pointer)
- }
- Vector => {
- let len = ty.vector_length();
- let elt = ty.element_type();
- ty_align(elt, pointer) * len
- }
- _ => bug!("ty_align: unhandled type")
- }
-}
-
-pub fn ty_size(ty: Type, pointer: usize) -> usize {
- match ty.kind() {
- Integer => ((ty.int_width() as usize) + 7) / 8,
- Pointer => pointer,
- 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, pointer))
- } else {
- let str_tys = ty.field_types();
- let size = str_tys.iter().fold(0, |s, t| {
- align(s, *t, pointer) + ty_size(*t, pointer)
- });
- align(size, ty, pointer)
- }
- }
- Array => {
- let len = ty.array_length();
- let elt = ty.element_type();
- let eltsz = ty_size(elt, pointer);
- len * eltsz
- }
- Vector => {
- let len = ty.vector_length();
- let elt = ty.element_type();
- let eltsz = ty_size(elt, pointer);
- len * eltsz
- },
- _ => bug!("ty_size: unhandled type")
- }
+pub fn align_up_to(off: u64, a: u64) -> u64 {
+ (off + a - 1) / a * a
}
generic_type_of(cx, t, None, false, false)
}
-
-// Pass dst=true if the type you are passing is a DST. Yes, we could figure
-// this out, but if you call this on an unsized type without realising it, you
-// are going to get the wrong type (it will not include the unsized parts of it).
-pub fn sizing_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
- t: Ty<'tcx>, dst: bool) -> Type {
- generic_type_of(cx, t, None, true, dst)
-}
-
pub fn incomplete_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
t: Ty<'tcx>, name: &str) -> Type {
generic_type_of(cx, t, Some(name), false, false)
};
let nnty = monomorphize::field_ty(cx.tcx(), substs,
&def.variants[nndiscr as usize].fields[0]);
- type_of::sizing_type_of(cx, nnty)
+ if let layout::Scalar { value: layout::Pointer, .. } = *cx.layout_of(nnty) {
+ Type::i8p(cx)
+ } else {
+ type_of::type_of(cx, nnty)
+ }
}
layout::StructWrappedNullablePointer { nndiscr, ref nonnull, .. } => {
let fields = compute_fields(cx, t, nndiscr as usize, false);
}
}
}
- layout::Vector { element, count } => {
- let elem_ty = Type::from_primitive(cx, element);
- Type::vector(&elem_ty, count)
- }
layout::UntaggedUnion { ref variants, .. }=> {
// Use alignment-sized ints to fill all the union storage.
let size = variants.stride().bytes();
fn struct_llfields<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, fields: &Vec<Ty<'tcx>>,
variant: &layout::Struct,
- sizing: bool, dst: bool) -> Vec<Type> {
+ sizing: bool, _dst: bool) -> Vec<Type> {
let fields = variant.field_index_by_increasing_offset().map(|i| fields[i as usize]);
if sizing {
- fields.filter(|ty| !dst || cx.shared().type_is_sized(*ty))
- .map(|ty| type_of::sizing_type_of(cx, ty)).collect()
+ bug!()
} else {
fields.map(|ty| type_of::in_memory_type_of(cx, ty)).collect()
}
// 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::{self, FnType, ArgType};
+use abi::{FnType, ArgType, LayoutExt, Reg, RegKind, Uniform};
use context::CrateContext;
-use type_::Type;
-
-fn ty_size(ty: Type) -> usize {
- 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);
+fn is_homogenous_aggregate<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, arg: &mut ArgType<'tcx>)
+ -> Option<Uniform> {
+ arg.layout.homogenous_aggregate(ccx).and_then(|unit| {
+ let size = arg.layout.size(ccx);
- // Ensure there is no padding.
- if ty_size(ty) == ty_size(base_ty) * (members as usize) {
- Some((base_ty, members))
- } else {
- None
+ // Ensure we have at most four uniquely addressable members.
+ if size > unit.size.checked_mul(4, ccx).unwrap() {
+ return None;
}
- }
- let homog_agg = match ty.kind() {
- Float => Some((ty, 1)),
- Double => Some((ty, 1)),
- Array => check_array(ty),
- Struct => check_struct(ty),
- Vector => match ty_size(ty) {
- 4|8 => Some((ty, 1)),
- _ => None
- },
- _ => None
- };
+ let valid_unit = match unit.kind {
+ RegKind::Integer => false,
+ RegKind::Float => true,
+ RegKind::Vector => size.bits() == 64 || size.bits() == 128
+ };
- // Ensure we have at most four uniquely addressable members
- homog_agg.and_then(|(base_ty, members)| {
- if members > 0 && members <= 4 {
- Some((base_ty, members))
+ if valid_unit {
+ Some(Uniform {
+ unit,
+ total: size
+ })
} else {
None
}
})
}
-fn classify_ret_ty(ccx: &CrateContext, ret: &mut ArgType) {
- if is_reg_ty(ret.ty) {
+fn classify_ret_ty<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, ret: &mut ArgType<'tcx>) {
+ if !ret.layout.is_aggregate() {
ret.extend_integer_width_to(32);
return;
}
- if let Some((base_ty, members)) = is_homogenous_aggregate_ty(ret.ty) {
- ret.cast = Some(Type::array(&base_ty, members));
+ if let Some(uniform) = is_homogenous_aggregate(ccx, ret) {
+ ret.cast_to(ccx, uniform);
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)
+ let size = ret.layout.size(ccx);
+ let bits = size.bits();
+ if bits <= 128 {
+ let unit = if bits <= 8 {
+ Reg::i8()
+ } else if bits <= 16 {
+ Reg::i16()
+ } else if bits <= 32 {
+ Reg::i32()
} else {
- Type::array(&Type::i64(ccx), ((size + 7 ) / 8 ) as u64)
+ Reg::i64()
};
- ret.cast = Some(llty);
+
+ ret.cast_to(ccx, Uniform {
+ unit,
+ total: size
+ });
return;
}
ret.make_indirect(ccx);
}
-fn classify_arg_ty(ccx: &CrateContext, arg: &mut ArgType) {
- if is_reg_ty(arg.ty) {
+fn classify_arg_ty<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, arg: &mut ArgType<'tcx>) {
+ if !arg.layout.is_aggregate() {
arg.extend_integer_width_to(32);
return;
}
- if let Some((base_ty, members)) = is_homogenous_aggregate_ty(arg.ty) {
- arg.cast = Some(Type::array(&base_ty, members));
+ if let Some(uniform) = is_homogenous_aggregate(ccx, arg) {
+ arg.cast_to(ccx, uniform);
return;
}
- let size = ty_size(arg.ty);
- if size <= 16 {
- let llty = if size == 0 {
- Type::array(&Type::i64(ccx), 0)
- } else 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)
+ let size = arg.layout.size(ccx);
+ let bits = size.bits();
+ if bits <= 128 {
+ let unit = if bits <= 8 {
+ Reg::i8()
+ } else if bits <= 16 {
+ Reg::i16()
+ } else if bits <= 32 {
+ Reg::i32()
} else {
- Type::array(&Type::i64(ccx), ((size + 7 ) / 8 ) as u64)
+ Reg::i64()
};
- arg.cast = Some(llty);
+
+ arg.cast_to(ccx, Uniform {
+ unit,
+ total: size
+ });
return;
}
arg.make_indirect(ccx);
}
-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) {
+pub fn compute_abi_info<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, fty: &mut FnType<'tcx>) {
if !fty.ret.is_ignore() {
classify_ret_ty(ccx, &mut fty.ret);
}
// option. This file may not be copied, modified, or distributed
// except according to those terms.
-use llvm::{Integer, Pointer, Float, Double, Struct, Array, Vector};
-use abi::{self, align_up_to, FnType, ArgType};
+use abi::{FnType, ArgType, LayoutExt, Reg, Uniform};
use context::CrateContext;
-use type_::Type;
-use std::cmp;
-
-pub enum Flavor {
- General,
- Ios
-}
-
-type TyAlignFn = fn(ty: Type) -> usize;
-
-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 {
- abi::ty_align(ty, 4)
-}
-
-// 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) {
+fn classify_ret_ty<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, ret: &mut ArgType<'tcx>) {
+ if !ret.layout.is_aggregate() {
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)
+ let size = ret.layout.size(ccx);
+ let bits = size.bits();
+ if bits <= 32 {
+ let unit = if bits <= 8 {
+ Reg::i8()
+ } else if bits <= 16 {
+ Reg::i16()
} else {
- Type::i32(ccx)
+ Reg::i32()
};
- ret.cast = Some(llty);
+ ret.cast_to(ccx, Uniform {
+ unit,
+ total: size
+ });
return;
}
ret.make_indirect(ccx);
}
-fn classify_arg_ty(ccx: &CrateContext, arg: &mut ArgType, align_fn: TyAlignFn) {
- if is_reg_ty(arg.ty) {
+fn classify_arg_ty<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, arg: &mut ArgType<'tcx>) {
+ if !arg.layout.is_aggregate() {
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);
+ let align = arg.layout.align(ccx).abi();
+ let total = arg.layout.size(ccx);
+ arg.cast_to(ccx, Uniform {
+ unit: if align <= 4 { Reg::i32() } else { Reg::i64() },
+ total
+ });
}
-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,
- };
-
+pub fn compute_abi_info<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, fty: &mut FnType<'tcx>) {
if !fty.ret.is_ignore() {
- classify_ret_ty(ccx, &mut fty.ret, align_fn);
+ classify_ret_ty(ccx, &mut fty.ret);
}
for arg in &mut fty.args {
if arg.is_ignore() { continue; }
- classify_arg_ty(ccx, arg, align_fn);
+ classify_arg_ty(ccx, arg);
}
}
// option. This file may not be copied, modified, or distributed
// except according to those terms.
-#![allow(non_upper_case_globals)]
-
-use llvm::{Struct, Array};
-use abi::{FnType, ArgType, ArgAttribute};
+use abi::{FnType, ArgType, ArgAttribute, LayoutExt, Uniform};
use context::CrateContext;
// Data layout: e-p:32:32-i64:64-v128:32:128-n32-S128
// See the https://github.com/kripken/emscripten-fastcomp-clang repository.
// The class `EmscriptenABIInfo` in `/lib/CodeGen/TargetInfo.cpp` contains the ABI definitions.
-fn classify_ret_ty(ccx: &CrateContext, ret: &mut ArgType) {
- match ret.ty.kind() {
- Struct => {
- let field_types = ret.ty.field_types();
- if field_types.len() == 1 {
- ret.cast = Some(field_types[0]);
- } else {
- ret.make_indirect(ccx);
+fn classify_ret_ty<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, ret: &mut ArgType<'tcx>) {
+ if ret.layout.is_aggregate() {
+ if let Some(unit) = ret.layout.homogenous_aggregate(ccx) {
+ let size = ret.layout.size(ccx);
+ if unit.size == size {
+ ret.cast_to(ccx, Uniform {
+ unit,
+ total: size
+ });
+ return;
}
}
- Array => {
- ret.make_indirect(ccx);
- }
- _ => {}
+
+ ret.make_indirect(ccx);
}
}
-fn classify_arg_ty(ccx: &CrateContext, arg: &mut ArgType) {
- if arg.ty.is_aggregate() {
+fn classify_arg_ty<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, arg: &mut ArgType<'tcx>) {
+ if arg.layout.is_aggregate() {
arg.make_indirect(ccx);
arg.attrs.set(ArgAttribute::ByVal);
}
}
-pub fn compute_abi_info(ccx: &CrateContext, fty: &mut FnType) {
+pub fn compute_abi_info<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, fty: &mut FnType<'tcx>) {
if !fty.ret.is_ignore() {
classify_ret_ty(ccx, &mut fty.ret);
}
// option. This file may not be copied, modified, or distributed
// except according to those terms.
-#![allow(non_upper_case_globals)]
-
-use libc::c_uint;
use std::cmp;
-use llvm;
-use llvm::{Integer, Pointer, Float, Double, Vector};
-use abi::{self, align_up_to, ArgType, FnType};
+use abi::{align_up_to, ArgType, FnType, LayoutExt, Reg, Uniform};
use context::CrateContext;
-use type_::Type;
-
-fn ty_align(ty: Type) -> usize {
- abi::ty_align(ty, 4)
-}
-fn ty_size(ty: Type) -> usize {
- abi::ty_size(ty, 4)
-}
-
-fn classify_ret_ty(ccx: &CrateContext, ret: &mut ArgType) {
- if is_reg_ty(ret.ty) {
+fn classify_ret_ty<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, ret: &mut ArgType<'tcx>) {
+ if !ret.layout.is_aggregate() {
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;
- let mut align = ty_align(arg.ty);
-
+fn classify_arg_ty(ccx: &CrateContext, arg: &mut ArgType, offset: &mut u64) {
+ let size = arg.layout.size(ccx);
+ let mut align = arg.layout.align(ccx).abi();
align = cmp::min(cmp::max(align, 4), 8);
- *offset = align_up_to(*offset, align);
- *offset += align_up_to(size, align * 8) / 8;
- if !is_reg_ty(arg.ty) {
- arg.cast = Some(struct_ty(ccx, arg.ty));
- arg.pad = padding_ty(ccx, align, orig_offset);
+ if arg.layout.is_aggregate() {
+ arg.cast_to(ccx, Uniform {
+ unit: Reg::i32(),
+ total: size
+ });
+ if ((align - 1) & *offset) > 0 {
+ arg.pad_with(ccx, Reg::i32());
+ }
} else {
arg.extend_integer_width_to(32);
}
-}
-
-fn is_reg_ty(ty: Type) -> bool {
- return match ty.kind() {
- Integer
- | Pointer
- | Float
- | Double
- | Vector => true,
- _ => false
- };
-}
-
-fn padding_ty(ccx: &CrateContext, align: usize, offset: usize) -> Option<Type> {
- if ((align - 1 ) & offset) > 0 {
- Some(Type::i32(ccx))
- } else {
- None
- }
-}
-
-fn coerce_to_int(ccx: &CrateContext, size: usize) -> Vec<Type> {
- let int_ty = Type::i32(ccx);
- let mut args = Vec::new();
-
- let mut n = size / 32;
- while n > 0 {
- args.push(int_ty);
- n -= 1;
- }
- let r = size % 32;
- if r > 0 {
- unsafe {
- args.push(Type::from_ref(llvm::LLVMIntTypeInContext(ccx.llcx(), r as c_uint)));
- }
- }
-
- args
-}
-
-fn struct_ty(ccx: &CrateContext, ty: Type) -> Type {
- let size = ty_size(ty) * 8;
- Type::struct_(ccx, &coerce_to_int(ccx, size), false)
+ *offset = align_up_to(*offset, align);
+ *offset += align_up_to(size.bytes(), align);
}
-pub fn compute_abi_info(ccx: &CrateContext, fty: &mut FnType) {
+pub fn compute_abi_info<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, fty: &mut FnType<'tcx>) {
if !fty.ret.is_ignore() {
classify_ret_ty(ccx, &mut fty.ret);
}
// option. This file may not be copied, modified, or distributed
// except according to those terms.
-#![allow(non_upper_case_globals)]
-
-use libc::c_uint;
use std::cmp;
-use llvm;
-use llvm::{Integer, Pointer, Float, Double, Vector};
-use abi::{self, align_up_to, ArgType, FnType};
+use abi::{align_up_to, ArgType, FnType, LayoutExt, Reg, Uniform};
use context::CrateContext;
-use type_::Type;
-
-fn ty_align(ty: Type) -> usize {
- abi::ty_align(ty, 8)
-}
-fn ty_size(ty: Type) -> usize {
- abi::ty_size(ty, 8)
-}
-
-fn classify_ret_ty(ccx: &CrateContext, ret: &mut ArgType) {
- if is_reg_ty(ret.ty) {
+fn classify_ret_ty<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, ret: &mut ArgType<'tcx>) {
+ if !ret.layout.is_aggregate() {
ret.extend_integer_width_to(64);
} 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;
- let mut align = ty_align(arg.ty);
-
+fn classify_arg_ty(ccx: &CrateContext, arg: &mut ArgType, offset: &mut u64) {
+ let size = arg.layout.size(ccx);
+ let mut align = arg.layout.align(ccx).abi();
align = cmp::min(cmp::max(align, 4), 8);
- *offset = align_up_to(*offset, align);
- *offset += align_up_to(size, align * 8) / 8;
- if !is_reg_ty(arg.ty) {
- arg.cast = Some(struct_ty(ccx, arg.ty));
- arg.pad = padding_ty(ccx, align, orig_offset);
+ if arg.layout.is_aggregate() {
+ arg.cast_to(ccx, Uniform {
+ unit: Reg::i64(),
+ total: size
+ });
+ if ((align - 1) & *offset) > 0 {
+ arg.pad_with(ccx, Reg::i64());
+ }
} else {
arg.extend_integer_width_to(64);
}
-}
-
-fn is_reg_ty(ty: Type) -> bool {
- return match ty.kind() {
- Integer
- | Pointer
- | Float
- | Double
- | Vector => true,
- _ => false
- };
-}
-
-fn padding_ty(ccx: &CrateContext, align: usize, offset: usize) -> Option<Type> {
- if ((align - 1 ) & offset) > 0 {
- Some(Type::i64(ccx))
- } else {
- None
- }
-}
-
-fn coerce_to_int(ccx: &CrateContext, size: usize) -> Vec<Type> {
- let int_ty = Type::i64(ccx);
- let mut args = Vec::new();
-
- let mut n = size / 64;
- while n > 0 {
- args.push(int_ty);
- n -= 1;
- }
- let r = size % 64;
- if r > 0 {
- unsafe {
- args.push(Type::from_ref(llvm::LLVMIntTypeInContext(ccx.llcx(), r as c_uint)));
- }
- }
-
- args
-}
-
-fn struct_ty(ccx: &CrateContext, ty: Type) -> Type {
- let size = ty_size(ty) * 8;
- Type::struct_(ccx, &coerce_to_int(ccx, size), false)
+ *offset = align_up_to(*offset, align);
+ *offset += align_up_to(size.bytes(), align);
}
-pub fn compute_abi_info(ccx: &CrateContext, fty: &mut FnType) {
+pub fn compute_abi_info<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, fty: &mut FnType<'tcx>) {
if !fty.ret.is_ignore() {
classify_ret_ty(ccx, &mut fty.ret);
}
// Reference: MSP430 Embedded Application Binary Interface
// http://www.ti.com/lit/an/slaa534/slaa534.pdf
-#![allow(non_upper_case_globals)]
-
-use llvm::Struct;
-
-use abi::{self, ArgType, FnType};
+use abi::{ArgType, FnType, LayoutExt};
use context::CrateContext;
-use type_::Type;
-
-fn ty_size(ty: Type) -> usize {
- abi::ty_size(ty, 2)
-}
// 3.5 Structures or Unions Passed and Returned by Reference
//
// returned by reference. To pass a structure or union by reference, the caller
// places its address in the appropriate location: either in a register or on
// the stack, according to its position in the argument list. (..)"
-fn classify_ret_ty(ccx: &CrateContext, ret: &mut ArgType) {
- if ret.ty.kind() == Struct && ty_size(ret.ty) > 32 {
+fn classify_ret_ty<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, ret: &mut ArgType<'tcx>) {
+ if ret.layout.is_aggregate() && ret.layout.size(ccx).bits() > 32 {
ret.make_indirect(ccx);
} else {
ret.extend_integer_width_to(16);
}
}
-fn classify_arg_ty(ccx: &CrateContext, arg: &mut ArgType) {
- if arg.ty.kind() == Struct && ty_size(arg.ty) > 32 {
+fn classify_arg_ty<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, arg: &mut ArgType<'tcx>) {
+ if arg.layout.is_aggregate() && arg.layout.size(ccx).bits() > 32 {
arg.make_indirect(ccx);
} else {
arg.extend_integer_width_to(16);
}
}
-pub fn compute_abi_info(ccx: &CrateContext, fty: &mut FnType) {
+pub fn compute_abi_info<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, fty: &mut FnType<'tcx>) {
if !fty.ret.is_ignore() {
classify_ret_ty(ccx, &mut fty.ret);
}
// Reference: PTX Writer's Guide to Interoperability
// http://docs.nvidia.com/cuda/ptx-writers-guide-to-interoperability
-#![allow(non_upper_case_globals)]
-
-use llvm::Struct;
-
-use abi::{self, ArgType, FnType};
+use abi::{ArgType, FnType, LayoutExt};
use context::CrateContext;
-use type_::Type;
-
-fn ty_size(ty: Type) -> usize {
- abi::ty_size(ty, 4)
-}
-fn classify_ret_ty(ccx: &CrateContext, ret: &mut ArgType) {
- if ret.ty.kind() == Struct && ty_size(ret.ty) > 32 {
+fn classify_ret_ty<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, ret: &mut ArgType<'tcx>) {
+ if ret.layout.is_aggregate() && ret.layout.size(ccx).bits() > 32 {
ret.make_indirect(ccx);
} else {
ret.extend_integer_width_to(32);
}
}
-fn classify_arg_ty(ccx: &CrateContext, arg: &mut ArgType) {
- if arg.ty.kind() == Struct && ty_size(arg.ty) > 32 {
+fn classify_arg_ty<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, arg: &mut ArgType<'tcx>) {
+ if arg.layout.is_aggregate() && arg.layout.size(ccx).bits() > 32 {
arg.make_indirect(ccx);
} else {
arg.extend_integer_width_to(32);
}
}
-pub fn compute_abi_info(ccx: &CrateContext, fty: &mut FnType) {
+pub fn compute_abi_info<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, fty: &mut FnType<'tcx>) {
if !fty.ret.is_ignore() {
classify_ret_ty(ccx, &mut fty.ret);
}
// Reference: PTX Writer's Guide to Interoperability
// http://docs.nvidia.com/cuda/ptx-writers-guide-to-interoperability
-#![allow(non_upper_case_globals)]
-
-use llvm::Struct;
-
-use abi::{self, ArgType, FnType};
+use abi::{ArgType, FnType, LayoutExt};
use context::CrateContext;
-use type_::Type;
-
-fn ty_size(ty: Type) -> usize {
- abi::ty_size(ty, 8)
-}
-fn classify_ret_ty(ccx: &CrateContext, ret: &mut ArgType) {
- if ret.ty.kind() == Struct && ty_size(ret.ty) > 64 {
+fn classify_ret_ty<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, ret: &mut ArgType<'tcx>) {
+ if ret.layout.is_aggregate() && ret.layout.size(ccx).bits() > 64 {
ret.make_indirect(ccx);
} else {
ret.extend_integer_width_to(64);
}
}
-fn classify_arg_ty(ccx: &CrateContext, arg: &mut ArgType) {
- if arg.ty.kind() == Struct && ty_size(arg.ty) > 64 {
+fn classify_arg_ty<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, arg: &mut ArgType<'tcx>) {
+ if arg.layout.is_aggregate() && arg.layout.size(ccx).bits() > 64 {
arg.make_indirect(ccx);
} else {
arg.extend_integer_width_to(64);
}
}
-pub fn compute_abi_info(ccx: &CrateContext, fty: &mut FnType) {
+pub fn compute_abi_info<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, fty: &mut FnType<'tcx>) {
if !fty.ret.is_ignore() {
classify_ret_ty(ccx, &mut fty.ret);
}
// option. This file may not be copied, modified, or distributed
// except according to those terms.
-use libc::c_uint;
-use llvm;
-use llvm::{Integer, Pointer, Float, Double, Vector};
-use abi::{self, align_up_to, FnType, ArgType};
+use abi::{align_up_to, FnType, ArgType, LayoutExt, Reg, Uniform};
use context::CrateContext;
-use type_::Type;
use std::cmp;
-fn ty_align(ty: Type) -> usize {
- if ty.kind() == Vector {
- bug!("ty_size: unhandled type")
- } else {
- abi::ty_align(ty, 4)
- }
-}
-
-fn ty_size(ty: Type) -> usize {
- if ty.kind() == Vector {
- bug!("ty_size: unhandled type")
- } else {
- abi::ty_size(ty, 4)
- }
-}
-
-fn classify_ret_ty(ccx: &CrateContext, ret: &mut ArgType) {
- if is_reg_ty(ret.ty) {
+fn classify_ret_ty<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, ret: &mut ArgType<'tcx>) {
+ if !ret.layout.is_aggregate() {
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;
- let mut align = ty_align(arg.ty);
-
+fn classify_arg_ty(ccx: &CrateContext, arg: &mut ArgType, offset: &mut u64) {
+ let size = arg.layout.size(ccx);
+ let mut align = arg.layout.align(ccx).abi();
align = cmp::min(cmp::max(align, 4), 8);
- *offset = align_up_to(*offset, align);
- *offset += align_up_to(size, align * 8) / 8;
- if !is_reg_ty(arg.ty) {
- arg.cast = Some(struct_ty(ccx, arg.ty));
- arg.pad = padding_ty(ccx, align, orig_offset);
+ if arg.layout.is_aggregate() {
+ arg.cast_to(ccx, Uniform {
+ unit: Reg::i32(),
+ total: size
+ });
+ if ((align - 1) & *offset) > 0 {
+ arg.pad_with(ccx, Reg::i32());
+ }
} else {
arg.extend_integer_width_to(32);
}
-}
-
-fn is_reg_ty(ty: Type) -> bool {
- return match ty.kind() {
- Integer
- | Pointer
- | Float
- | Double => true,
- _ => false
- };
-}
-fn padding_ty(ccx: &CrateContext, align: usize, offset: usize) -> Option<Type> {
- if ((align - 1 ) & offset) > 0 {
- Some(Type::i32(ccx))
- } else {
- None
- }
-}
-
-fn coerce_to_int(ccx: &CrateContext, size: usize) -> Vec<Type> {
- let int_ty = Type::i32(ccx);
- let mut args = Vec::new();
-
- let mut n = size / 32;
- while n > 0 {
- args.push(int_ty);
- n -= 1;
- }
-
- let r = size % 32;
- if r > 0 {
- unsafe {
- args.push(Type::from_ref(llvm::LLVMIntTypeInContext(ccx.llcx(), r as c_uint)));
- }
- }
-
- args
-}
-
-fn struct_ty(ccx: &CrateContext, ty: Type) -> Type {
- let size = ty_size(ty) * 8;
- Type::struct_(ccx, &coerce_to_int(ccx, size), false)
+ *offset = align_up_to(*offset, align);
+ *offset += align_up_to(size.bytes(), align);
}
-pub fn compute_abi_info(ccx: &CrateContext, fty: &mut FnType) {
+pub fn compute_abi_info<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, fty: &mut FnType<'tcx>) {
if !fty.ret.is_ignore() {
classify_ret_ty(ccx, &mut fty.ret);
}
// option. This file may not be copied, modified, or distributed
// except according to those terms.
-// FIXME: The PowerPC64 ABI needs to zero or sign extend function
-// call parameters, but compute_abi_info() is passed LLVM types
-// which have no sign information.
-//
+// FIXME:
// Alignment of 128 bit types is not currently handled, this will
// need to be fixed when PowerPC vector support is added.
-use llvm::{Integer, Pointer, Float, Double, Struct, Vector, Array};
-use abi::{self, FnType, ArgType};
+use abi::{FnType, ArgType, LayoutExt, Reg, RegKind, Uniform};
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
- }
+fn is_homogenous_aggregate<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, arg: &mut ArgType<'tcx>)
+ -> Option<Uniform> {
+ arg.layout.homogenous_aggregate(ccx).and_then(|unit| {
+ let size = arg.layout.size(ccx);
- 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;
- }
- }
+ // Ensure we have at most eight uniquely addressable members.
+ if size > unit.size.checked_mul(8, ccx).unwrap() {
+ return None;
}
- // 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 valid_unit = match unit.kind {
+ RegKind::Integer => false,
+ RegKind::Float => true,
+ RegKind::Vector => size.bits() == 128
+ };
- 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))
+ if valid_unit {
+ Some(Uniform {
+ unit,
+ total: size
+ })
} else {
None
}
})
}
-fn classify_ret_ty(ccx: &CrateContext, ret: &mut ArgType) {
- if is_reg_ty(ret.ty) {
+fn classify_ret_ty<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, ret: &mut ArgType<'tcx>) {
+ if !ret.layout.is_aggregate() {
ret.extend_integer_width_to(64);
return;
}
ret.make_indirect(ccx);
}
- if let Some((base_ty, members)) = is_homogenous_aggregate_ty(ret.ty) {
- ret.cast = Some(Type::array(&base_ty, members));
+ if let Some(uniform) = is_homogenous_aggregate(ccx, ret) {
+ ret.cast_to(ccx, uniform);
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)
+ let size = ret.layout.size(ccx);
+ let bits = size.bits();
+ if bits <= 128 {
+ let unit = if bits <= 8 {
+ Reg::i8()
+ } else if bits <= 16 {
+ Reg::i16()
+ } else if bits <= 32 {
+ Reg::i32()
} else {
- Type::array(&Type::i64(ccx), ((size + 7 ) / 8 ) as u64)
+ Reg::i64()
};
- ret.cast = Some(llty);
+
+ ret.cast_to(ccx, Uniform {
+ unit,
+ total: size
+ });
return;
}
ret.make_indirect(ccx);
}
-fn classify_arg_ty(ccx: &CrateContext, arg: &mut ArgType) {
- if is_reg_ty(arg.ty) {
+fn classify_arg_ty<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, arg: &mut ArgType<'tcx>) {
+ if !arg.layout.is_aggregate() {
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));
+ if let Some(uniform) = is_homogenous_aggregate(ccx, arg) {
+ arg.cast_to(ccx, uniform);
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<Type> {
- 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)
+ let total = arg.layout.size(ccx);
+ arg.cast_to(ccx, Uniform {
+ unit: Reg::i64(),
+ total
+ });
}
-pub fn compute_abi_info(ccx: &CrateContext, fty: &mut FnType) {
+pub fn compute_abi_info<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, fty: &mut FnType<'tcx>) {
if !fty.ret.is_ignore() {
classify_ret_ty(ccx, &mut fty.ret);
}
// FIXME: The assumes we're using the non-vector ABI, i.e. compiling
// for a pre-z13 machine or using -mno-vx.
-use llvm::{Integer, Pointer, Float, Double, Struct, Array, Vector};
-use abi::{align_up_to, FnType, ArgType};
+use abi::{FnType, ArgType, LayoutExt, Reg};
use context::CrateContext;
-use type_::Type;
-use std::cmp;
+use rustc::ty::layout::{self, Layout, TyLayout};
-fn align(off: usize, ty: Type) -> usize {
- let a = ty_align(ty);
- return align_up_to(off, a);
-}
-
-fn ty_align(ty: Type) -> usize {
- match ty.kind() {
- Integer => ((ty.int_width() as usize) + 7) / 8,
- Pointer => 8,
- 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, ty_align(*t)))
- }
- }
- Array => {
- let elt = ty.element_type();
- ty_align(elt)
- }
- Vector => ty_size(ty),
- _ => bug!("ty_align: unhandled type")
- }
-}
-
-fn ty_size(ty: Type) -> usize {
- match ty.kind() {
- Integer => ((ty.int_width() as usize) + 7) / 8,
- Pointer => 8,
- 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))
- } else {
- let str_tys = ty.field_types();
- let size = str_tys.iter().fold(0, |s, t| align(s, *t) + ty_size(*t));
- align(size, ty)
- }
- }
- Array => {
- let len = ty.array_length();
- let elt = ty.element_type();
- let eltsz = ty_size(elt);
- len * eltsz
- }
- Vector => {
- let len = ty.vector_length();
- let elt = ty.element_type();
- let eltsz = ty_size(elt);
- len * eltsz
- }
- _ => bug!("ty_size: unhandled type")
- }
-}
-
-fn classify_ret_ty(ccx: &CrateContext, ret: &mut ArgType) {
- if is_reg_ty(ret.ty) {
+fn classify_ret_ty<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, ret: &mut ArgType<'tcx>) {
+ if !ret.layout.is_aggregate() && ret.layout.size(ccx).bits() <= 64 {
ret.extend_integer_width_to(64);
} else {
ret.make_indirect(ccx);
}
}
-fn classify_arg_ty(ccx: &CrateContext, arg: &mut ArgType) {
- if arg.ty.kind() == Struct {
- fn is_single_fp_element(tys: &[Type]) -> bool {
- if tys.len() != 1 {
- return false;
- }
- match tys[0].kind() {
- Float | Double => true,
- Struct => is_single_fp_element(&tys[0].field_types()),
- _ => false
- }
- }
-
- if is_single_fp_element(&arg.ty.field_types()) {
- match ty_size(arg.ty) {
- 4 => arg.cast = Some(Type::f32(ccx)),
- 8 => arg.cast = Some(Type::f64(ccx)),
- _ => arg.make_indirect(ccx)
- }
- } else {
- match ty_size(arg.ty) {
- 1 => arg.cast = Some(Type::i8(ccx)),
- 2 => arg.cast = Some(Type::i16(ccx)),
- 4 => arg.cast = Some(Type::i32(ccx)),
- 8 => arg.cast = Some(Type::i64(ccx)),
- _ => arg.make_indirect(ccx)
+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
}
}
- return;
+ _ => false
}
+}
- if is_reg_ty(arg.ty) {
+fn classify_arg_ty<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, arg: &mut ArgType<'tcx>) {
+ let size = arg.layout.size(ccx);
+ if !arg.layout.is_aggregate() && size.bits() <= 64 {
arg.extend_integer_width_to(64);
- } else {
- arg.make_indirect(ccx);
+ return;
}
-}
-fn is_reg_ty(ty: Type) -> bool {
- match ty.kind() {
- Integer
- | Pointer
- | Float
- | Double => ty_size(ty) <= 8,
- _ => false
+ if is_single_fp_element(ccx, arg.layout) {
+ match size.bytes() {
+ 4 => arg.cast_to(ccx, Reg::f32()),
+ 8 => arg.cast_to(ccx, Reg::f64()),
+ _ => arg.make_indirect(ccx)
+ }
+ } else {
+ match size.bytes() {
+ 1 => arg.cast_to(ccx, Reg::i8()),
+ 2 => arg.cast_to(ccx, Reg::i16()),
+ 4 => arg.cast_to(ccx, Reg::i32()),
+ 8 => arg.cast_to(ccx, Reg::i64()),
+ _ => arg.make_indirect(ccx)
+ }
}
}
-pub fn compute_abi_info(ccx: &CrateContext, fty: &mut FnType) {
+pub fn compute_abi_info<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, fty: &mut FnType<'tcx>) {
if !fty.ret.is_ignore() {
classify_ret_ty(ccx, &mut fty.ret);
}
// option. This file may not be copied, modified, or distributed
// except according to those terms.
-#![allow(non_upper_case_globals)]
-
-use libc::c_uint;
use std::cmp;
-use llvm;
-use llvm::{Integer, Pointer, Float, Double, Vector};
-use abi::{self, align_up_to, ArgType, FnType};
+use abi::{align_up_to, ArgType, FnType, LayoutExt, Reg, Uniform};
use context::CrateContext;
-use type_::Type;
-
-fn ty_align(ty: Type) -> usize {
- abi::ty_align(ty, 4)
-}
-fn ty_size(ty: Type) -> usize {
- abi::ty_size(ty, 4)
-}
-
-fn classify_ret_ty(ccx: &CrateContext, ret: &mut ArgType) {
- if is_reg_ty(ret.ty) {
+fn classify_ret_ty<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, ret: &mut ArgType<'tcx>) {
+ if !ret.layout.is_aggregate() {
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;
- let mut align = ty_align(arg.ty);
-
+fn classify_arg_ty(ccx: &CrateContext, arg: &mut ArgType, offset: &mut u64) {
+ let size = arg.layout.size(ccx);
+ let mut align = arg.layout.align(ccx).abi();
align = cmp::min(cmp::max(align, 4), 8);
- *offset = align_up_to(*offset, align);
- *offset += align_up_to(size, align * 8) / 8;
-
- 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);
- }
-}
-
-fn is_reg_ty(ty: Type) -> bool {
- return match ty.kind() {
- Integer
- | Pointer
- | Float
- | Double
- | Vector => true,
- _ => false
- };
-}
-
-fn padding_ty(ccx: &CrateContext, align: usize, offset: usize) -> Option<Type> {
- if ((align - 1 ) & offset) > 0 {
- Some(Type::i32(ccx))
- } else {
- None
- }
-}
-
-fn coerce_to_int(ccx: &CrateContext, size: usize) -> Vec<Type> {
- let int_ty = Type::i32(ccx);
- let mut args = Vec::new();
- let mut n = size / 32;
- while n > 0 {
- args.push(int_ty);
- n -= 1;
- }
-
- let r = size % 32;
- if r > 0 {
- unsafe {
- args.push(Type::from_ref(llvm::LLVMIntTypeInContext(ccx.llcx(), r as c_uint)));
+ if arg.layout.is_aggregate() {
+ arg.cast_to(ccx, Uniform {
+ unit: Reg::i32(),
+ total: size
+ });
+ if ((align - 1) & *offset) > 0 {
+ arg.pad_with(ccx, Reg::i32());
}
+ } else {
+ arg.extend_integer_width_to(32)
}
- args
-}
-
-fn struct_ty(ccx: &CrateContext, ty: Type) -> Type {
- let size = ty_size(ty) * 8;
- Type::struct_(ccx, &coerce_to_int(ccx, size), false)
+ *offset = align_up_to(*offset, align);
+ *offset += align_up_to(size.bytes(), align);
}
-pub fn compute_abi_info(ccx: &CrateContext, fty: &mut FnType) {
+pub fn compute_abi_info<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, fty: &mut FnType<'tcx>) {
if !fty.ret.is_ignore() {
classify_ret_ty(ccx, &mut fty.ret);
}
// FIXME: This needs an audit for correctness and completeness.
-use llvm::{Integer, Pointer, Float, Double, Struct, Vector, Array};
-use abi::{self, FnType, ArgType};
+use abi::{FnType, ArgType, LayoutExt, Reg, RegKind, Uniform};
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,
+fn is_homogenous_aggregate<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, arg: &mut ArgType<'tcx>)
+ -> Option<Uniform> {
+ arg.layout.homogenous_aggregate(ccx).and_then(|unit| {
+ let size = arg.layout.size(ccx);
- // 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;
- }
- }
+ // Ensure we have at most eight uniquely addressable members.
+ if size > unit.size.checked_mul(8, ccx).unwrap() {
+ return None;
}
- // 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
- };
+ let valid_unit = match unit.kind {
+ RegKind::Integer => false,
+ RegKind::Float => true,
+ RegKind::Vector => size.bits() == 128
+ };
- // 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))
+ if valid_unit {
+ Some(Uniform {
+ unit,
+ total: size
+ })
} else {
None
}
})
}
-fn classify_ret_ty(ccx: &CrateContext, ret: &mut ArgType) {
- if is_reg_ty(ret.ty) {
+fn classify_ret_ty<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, ret: &mut ArgType<'tcx>) {
+ if !ret.layout.is_aggregate() {
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));
+ if let Some(uniform) = is_homogenous_aggregate(ccx, ret) {
+ ret.cast_to(ccx, uniform);
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)
+ let size = ret.layout.size(ccx);
+ let bits = size.bits();
+ if bits <= 128 {
+ let unit = if bits <= 8 {
+ Reg::i8()
+ } else if bits <= 16 {
+ Reg::i16()
+ } else if bits <= 32 {
+ Reg::i32()
} else {
- Type::array(&Type::i64(ccx), ((size + 7 ) / 8 ) as u64)
+ Reg::i64()
};
- ret.cast = Some(llty);
+
+ ret.cast_to(ccx, Uniform {
+ unit,
+ total: size
+ });
return;
}
+
+ // don't return aggregates in registers
+ ret.make_indirect(ccx);
}
-fn classify_arg_ty(ccx: &CrateContext, arg: &mut ArgType) {
- if is_reg_ty(arg.ty) {
+fn classify_arg_ty<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, arg: &mut ArgType<'tcx>) {
+ if !arg.layout.is_aggregate() {
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));
+ if let Some(uniform) = is_homogenous_aggregate(ccx, arg) {
+ arg.cast_to(ccx, uniform);
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<Type> {
- 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)
+ let total = arg.layout.size(ccx);
+ arg.cast_to(ccx, Uniform {
+ unit: Reg::i64(),
+ total
+ });
}
-pub fn compute_abi_info(ccx: &CrateContext, fty: &mut FnType) {
+pub fn compute_abi_info<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, fty: &mut FnType<'tcx>) {
if !fty.ret.is_ignore() {
classify_ret_ty(ccx, &mut fty.ret);
}
// option. This file may not be copied, modified, or distributed
// except according to those terms.
-use llvm::*;
-use abi::{ArgAttribute, FnType};
-use type_::Type;
-use super::common::*;
-use super::machine::*;
+use abi::{ArgAttribute, FnType, LayoutExt, Reg, RegKind};
+use common::CrateContext;
#[derive(PartialEq)]
pub enum Flavor {
Fastcall
}
-pub fn compute_abi_info(ccx: &CrateContext, fty: &mut FnType, flavor: Flavor) {
+pub fn compute_abi_info<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
+ fty: &mut FnType<'tcx>,
+ flavor: Flavor) {
if !fty.ret.is_ignore() {
- if fty.ret.ty.kind() == Struct {
+ if fty.ret.layout.is_aggregate() {
// Returning a structure. Most often, this will use
// a hidden first argument. On some platforms, though,
// small structs are returned as integers.
let t = &ccx.sess().target.target;
if t.options.is_like_osx || t.options.is_like_windows
|| t.options.is_like_openbsd {
- 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)),
+ 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)
}
} else {
for arg in &mut fty.args {
if arg.is_ignore() { continue; }
- if arg.ty.kind() == Struct {
+ if arg.layout.is_aggregate() {
arg.make_indirect(ccx);
arg.attrs.set(ArgAttribute::ByVal);
} else {
for arg in &mut fty.args {
if arg.is_ignore() || arg.is_indirect() { continue; }
- if arg.ty.kind() == Float {
+ // At this point we know this must be a primitive of sorts.
+ let unit = arg.layout.homogenous_aggregate(ccx).unwrap();
+ let size = arg.layout.size(ccx);
+ assert_eq!(unit.size, size);
+ if unit.kind == RegKind::Float {
continue;
}
- let size = llbitsize_of_real(ccx, arg.ty);
- let size_in_regs = (size + 31) / 32;
+ let size_in_regs = (size.bits() + 31) / 32;
if size_in_regs == 0 {
continue;
free_regs -= size_in_regs;
- if size <= 32 && (arg.ty.kind() == Pointer || arg.ty.kind() == Integer) {
+ if size.bits() <= 32 && unit.kind == RegKind::Integer {
arg.attrs.set(ArgAttribute::InReg);
}
// The classification code for the x86_64 ABI is taken from the clay language
// https://github.com/jckarter/clay/blob/master/compiler/src/externals.cpp
-#![allow(non_upper_case_globals)]
-use self::RegClass::*;
-
-use llvm::{Integer, Pointer, Float, Double};
-use llvm::{Struct, Array, Vector};
-use abi::{self, ArgType, ArgAttribute, FnType};
+use abi::{ArgType, ArgAttribute, CastTarget, FnType, LayoutExt, Reg, RegKind};
use context::CrateContext;
-use type_::Type;
-
-#[derive(Clone, Copy, PartialEq)]
-enum RegClass {
- NoClass,
- Int,
- SSEFs,
- SSEFv,
- SSEDs,
- SSEDv,
- SSEInt(/* bitwidth */ u64),
- /// Data that can appear in the upper half of an SSE register.
- SSEUp,
- X87,
- X87Up,
- ComplexX87,
- Memory
-}
-
-trait TypeMethods {
- fn is_reg_ty(&self) -> bool;
-}
-
-impl TypeMethods for Type {
- fn is_reg_ty(&self) -> bool {
- match self.kind() {
- Integer | Pointer | Float | Double => true,
- _ => false
- }
- }
-}
-
-impl RegClass {
- fn is_sse(&self) -> bool {
- match *self {
- SSEFs | SSEFv | SSEDs | SSEDv | SSEInt(_) => true,
- _ => false
- }
- }
-}
-
-trait ClassList {
- fn is_pass_byval(&self) -> bool;
- fn is_ret_bysret(&self) -> bool;
-}
-
-impl ClassList for [RegClass] {
- fn is_pass_byval(&self) -> bool {
- if self.is_empty() { return false; }
-
- let class = self[0];
- class == Memory
- || class == X87
- || class == ComplexX87
- }
- fn is_ret_bysret(&self) -> bool {
- if self.is_empty() { return false; }
+use rustc::ty::layout::{self, Layout, TyLayout, Size};
- self[0] == Memory
- }
+#[derive(Clone, Copy, PartialEq, Debug)]
+enum Class {
+ None,
+ Int,
+ Sse,
+ SseUp
}
-fn classify_ty(ty: Type) -> Vec<RegClass> {
- fn align(off: usize, ty: Type) -> usize {
- let a = ty_align(ty);
- return (off + a - 1) / a * a;
- }
-
- fn ty_align(ty: Type) -> usize {
- abi::ty_align(ty, 8)
- }
-
- fn ty_size(ty: Type) -> usize {
- abi::ty_size(ty, 8)
- }
-
- fn all_mem(cls: &mut [RegClass]) {
- for elt in cls {
- *elt = Memory;
- }
- }
-
- fn unify(cls: &mut [RegClass],
- i: usize,
- newv: RegClass) {
- if cls[i] == newv { return }
+#[derive(Clone, Copy, Debug)]
+struct Memory;
- let to_write = match (cls[i], newv) {
- (NoClass, _) => newv,
- (_, NoClass) => return,
+// Currently supported vector size (AVX).
+const LARGEST_VECTOR_SIZE: usize = 256;
+const MAX_EIGHTBYTES: usize = LARGEST_VECTOR_SIZE / 64;
- (Memory, _) |
- (_, Memory) => Memory,
+fn classify_arg<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, arg: &ArgType<'tcx>)
+ -> Result<[Class; MAX_EIGHTBYTES], Memory> {
+ fn unify(cls: &mut [Class],
+ off: u64,
+ c: Class) {
+ let i = (off / 8) as usize;
+ let to_write = match (cls[i], c) {
+ (Class::None, _) => c,
+ (_, Class::None) => return,
- (Int, _) |
- (_, Int) => Int,
+ (Class::Int, _) |
+ (_, Class::Int) => Class::Int,
- (X87, _) |
- (X87Up, _) |
- (ComplexX87, _) |
- (_, X87) |
- (_, X87Up) |
- (_, ComplexX87) => Memory,
+ (Class::Sse, _) |
+ (_, Class::Sse) => Class::Sse,
- (SSEFv, SSEUp) |
- (SSEFs, SSEUp) |
- (SSEDv, SSEUp) |
- (SSEDs, SSEUp) |
- (SSEInt(_), SSEUp) => return,
-
- (..) => newv
+ (Class::SseUp, Class::SseUp) => Class::SseUp
};
cls[i] = to_write;
}
- fn classify_struct(tys: &[Type],
- cls: &mut [RegClass],
- i: usize,
- off: usize,
- packed: bool) {
- let mut field_off = off;
- for ty in tys {
- if !packed {
- field_off = align(field_off, *ty);
+ fn classify<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
+ layout: TyLayout<'tcx>,
+ cls: &mut [Class],
+ off: u64)
+ -> Result<(), Memory> {
+ if off % layout.align(ccx).abi() != 0 {
+ if layout.size(ccx).bytes() > 0 {
+ return Err(Memory);
}
- classify(*ty, cls, i, field_off);
- field_off += ty_size(*ty);
+ return Ok(());
}
- }
- fn classify(ty: Type,
- cls: &mut [RegClass], ix: usize,
- off: usize) {
- let t_align = ty_align(ty);
- let t_size = ty_size(ty);
-
- let misalign = off % t_align;
- if misalign != 0 {
- let mut i = off / 8;
- let e = (off + t_size + 7) / 8;
- while i < e {
- unify(cls, ix + i, Memory);
- i += 1;
+ match *layout {
+ Layout::Scalar { value, .. } |
+ Layout::RawNullablePointer { value, .. } => {
+ let reg = match value {
+ layout::Int(_) |
+ layout::Pointer => Class::Int,
+ layout::F32 |
+ layout::F64 => Class::Sse
+ };
+ unify(cls, off, reg);
}
- return;
- }
- match ty.kind() {
- Integer |
- Pointer => {
- unify(cls, ix + off / 8, Int);
+ Layout::CEnum { .. } => {
+ unify(cls, off, Class::Int);
}
- Float => {
- if off % 8 == 4 {
- unify(cls, ix + off / 8, SSEFv);
- } else {
- unify(cls, ix + off / 8, SSEFs);
+
+ Layout::Vector { element, count } => {
+ unify(cls, off, Class::Sse);
+
+ // everything after the first one is the upper
+ // half of a register.
+ let eltsz = element.size(ccx).bytes();
+ for i in 1..count {
+ unify(cls, off + i * eltsz, Class::SseUp);
}
}
- Double => {
- unify(cls, ix + off / 8, SSEDs);
- }
- Struct => {
- classify_struct(&ty.field_types(), cls, ix, off, ty.is_packed());
- }
- Array => {
- let len = ty.array_length();
- let elt = ty.element_type();
- let eltsz = ty_size(elt);
- let mut i = 0;
- while i < len {
- classify(elt, cls, ix, off + i * eltsz);
- i += 1;
+
+ Layout::Array { count, .. } => {
+ if count > 0 {
+ let elt = layout.field(ccx, 0);
+ let eltsz = elt.size(ccx).bytes();
+ for i in 0..count {
+ classify(ccx, elt, cls, off + i * eltsz)?;
+ }
}
}
- Vector => {
- let len = ty.vector_length();
- let elt = ty.element_type();
- let eltsz = ty_size(elt);
- let mut reg = match elt.kind() {
- Integer => SSEInt(elt.int_width()),
- Float => SSEFv,
- Double => SSEDv,
- _ => bug!("classify: unhandled vector element type")
- };
- let mut i = 0;
- while i < len {
- unify(cls, ix + (off + i * eltsz) / 8, reg);
+ Layout::Univariant { ref variant, .. } => {
+ for i in 0..layout.field_count() {
+ let field_off = off + variant.offsets[i].bytes();
+ classify(ccx, layout.field(ccx, i), cls, field_off)?;
+ }
+ }
- // everything after the first one is the upper
- // half of a register.
- reg = SSEUp;
- i += 1;
+ Layout::UntaggedUnion { .. } => {
+ for i in 0..layout.field_count() {
+ classify(ccx, layout.field(ccx, i), cls, off)?;
}
}
- _ => bug!("classify: unhandled type")
+
+ Layout::FatPointer { .. } |
+ Layout::General { .. } |
+ Layout::StructWrappedNullablePointer { .. } => return Err(Memory)
}
+
+ Ok(())
+ }
+
+ let n = ((arg.layout.size(ccx).bytes() + 7) / 8) as usize;
+ if n > MAX_EIGHTBYTES {
+ return Err(Memory);
}
- fn fixup(ty: Type, cls: &mut [RegClass]) {
+ let mut cls = [Class::None; MAX_EIGHTBYTES];
+ classify(ccx, arg.layout, &mut cls, 0)?;
+ if n > 2 {
+ if cls[0] != Class::Sse {
+ return Err(Memory);
+ }
+ if cls[1..n].iter().any(|&c| c != Class::SseUp) {
+ return Err(Memory);
+ }
+ } else {
let mut i = 0;
- let ty_kind = ty.kind();
- let e = cls.len();
- if cls.len() > 2 && (ty_kind == Struct || ty_kind == Array || ty_kind == Vector) {
- if cls[i].is_sse() {
+ while i < n {
+ if cls[i] == Class::SseUp {
+ cls[i] = Class::Sse;
+ } else if cls[i] == Class::Sse {
i += 1;
- while i < e {
- if cls[i] != SSEUp {
- all_mem(cls);
- return;
- }
- i += 1;
- }
+ while i != n && cls[i] == Class::SseUp { i += 1; }
} else {
- all_mem(cls);
- return
- }
- } else {
- while i < e {
- if cls[i] == Memory {
- all_mem(cls);
- return;
- }
- if cls[i] == X87Up {
- // for darwin
- // cls[i] = SSEDs;
- all_mem(cls);
- return;
- }
- if cls[i] == SSEUp {
- cls[i] = SSEDv;
- } else if cls[i].is_sse() {
- i += 1;
- while i != e && cls[i] == SSEUp { i += 1; }
- } else if cls[i] == X87 {
- i += 1;
- while i != e && cls[i] == X87Up { i += 1; }
- } else {
- i += 1;
- }
+ i += 1;
}
}
}
- let words = (ty_size(ty) + 7) / 8;
- let mut cls = vec![NoClass; words];
- if words > 4 {
- all_mem(&mut cls);
- return cls;
- }
- classify(ty, &mut cls, 0, 0);
- fixup(ty, &mut cls);
- return cls;
+ Ok(cls)
}
-fn llreg_ty(ccx: &CrateContext, cls: &[RegClass]) -> Type {
- fn llvec_len(cls: &[RegClass]) -> usize {
- let mut len = 1;
- for c in cls {
- if *c != SSEUp {
- break;
- }
- len += 1;
- }
- return len;
+fn reg_component(cls: &[Class], i: &mut usize, size: u64) -> Option<Reg> {
+ if *i >= cls.len() {
+ return None;
}
- let mut tys = Vec::new();
- let mut i = 0;
- let e = cls.len();
- while i < e {
- match cls[i] {
- Int => {
- tys.push(Type::i64(ccx));
- }
- SSEFv | SSEDv | SSEInt(_) => {
- let (elts_per_word, elt_ty) = match cls[i] {
- SSEFv => (2, Type::f32(ccx)),
- SSEDv => (1, Type::f64(ccx)),
- SSEInt(bits) => {
- assert!(bits == 8 || bits == 16 || bits == 32 || bits == 64,
- "llreg_ty: unsupported SSEInt width {}", bits);
- (64 / bits, Type::ix(ccx, bits))
+ match cls[*i] {
+ Class::None => None,
+ Class::Int => {
+ *i += 1;
+ Some(match size {
+ 1 => Reg::i8(),
+ 2 => Reg::i16(),
+ 3 |
+ 4 => Reg::i32(),
+ _ => Reg::i64()
+ })
+ }
+ Class::Sse => {
+ let vec_len = 1 + cls[*i+1..].iter().take_while(|&&c| c == Class::SseUp).count();
+ *i += vec_len;
+ Some(match size {
+ 4 => Reg::f32(),
+ 8 => Reg::f64(),
+ _ => {
+ Reg {
+ kind: RegKind::Vector,
+ size: Size::from_bytes(vec_len as u64 * 8)
}
- _ => bug!(),
- };
- let vec_len = llvec_len(&cls[i + 1..]);
- let vec_ty = Type::vector(&elt_ty, vec_len as u64 * elts_per_word);
- tys.push(vec_ty);
- i += vec_len;
- continue;
- }
- SSEFs => {
- tys.push(Type::f32(ccx));
- }
- SSEDs => {
- tys.push(Type::f64(ccx));
- }
- _ => bug!("llregtype: unhandled class")
+ }
+ })
}
- i += 1;
+ c => bug!("reg_component: unhandled class {:?}", c)
}
- if tys.len() == 1 && tys[0].kind() == Vector {
- // if the type contains only a vector, pass it as that vector.
- tys[0]
+}
+
+fn cast_target(cls: &[Class], size: u64) -> CastTarget {
+ let mut i = 0;
+ let lo = reg_component(cls, &mut i, size).unwrap();
+ let offset = i as u64 * 8;
+ let target = if size <= offset {
+ CastTarget::from(lo)
} else {
- Type::struct_(ccx, &tys, false)
- }
+ let hi = reg_component(cls, &mut i, size - offset).unwrap();
+ CastTarget::Pair(lo, hi)
+ };
+ assert_eq!(reg_component(cls, &mut i, 0), None);
+ target
}
-pub fn compute_abi_info(ccx: &CrateContext, fty: &mut FnType) {
- fn x86_64_ty<F>(ccx: &CrateContext,
- arg: &mut ArgType,
- is_mem_cls: F,
- ind_attr: Option<ArgAttribute>)
- where F: FnOnce(&[RegClass]) -> bool
- {
- if !arg.ty.is_reg_ty() {
- let cls = classify_ty(arg.ty);
- if is_mem_cls(&cls) {
- arg.make_indirect(ccx);
- if let Some(attr) = ind_attr {
- arg.attrs.set(attr);
+pub fn compute_abi_info<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, fty: &mut FnType<'tcx>) {
+ let mut int_regs = 6; // RDI, RSI, RDX, RCX, R8, R9
+ let mut sse_regs = 8; // XMM0-7
+
+ let mut x86_64_ty = |arg: &mut ArgType<'tcx>, is_arg: bool| {
+ let cls = classify_arg(ccx, arg);
+
+ let mut needed_int = 0;
+ let mut needed_sse = 0;
+ let in_mem = match cls {
+ Err(Memory) => true,
+ Ok(ref cls) if is_arg => {
+ for &c in cls {
+ match c {
+ Class::Int => needed_int += 1,
+ Class::Sse => needed_sse += 1,
+ _ => {}
+ }
}
- } else {
- arg.cast = Some(llreg_ty(ccx, &cls));
+ arg.layout.is_aggregate() &&
+ (int_regs < needed_int || sse_regs < needed_sse)
}
- } else {
- arg.extend_integer_width_to(32);
- }
- }
+ Ok(_) => false
+ };
- let mut int_regs = 6; // RDI, RSI, RDX, RCX, R8, R9
- let mut sse_regs = 8; // XMM0-7
+ if in_mem {
+ // `sret` / `byval` parameter thus one less integer register available
+ int_regs -= 1;
- if !fty.ret.is_ignore() {
- x86_64_ty(ccx, &mut fty.ret, |cls| {
- if cls.is_ret_bysret() {
- // `sret` parameter thus one less register available
- int_regs -= 1;
- true
+ arg.make_indirect(ccx);
+ if is_arg {
+ arg.attrs.set(ArgAttribute::ByVal);
+ }
+ } else {
+ // split into sized chunks passed individually
+ int_regs -= needed_int;
+ sse_regs -= needed_sse;
+
+ if arg.layout.is_aggregate() {
+ let size = arg.layout.size(ccx).bytes();
+ arg.cast_to(ccx, cast_target(cls.as_ref().unwrap(), size))
} else {
- false
+ arg.extend_integer_width_to(32);
}
- }, None);
+ }
+ };
+
+ if !fty.ret.is_ignore() {
+ x86_64_ty(&mut fty.ret, false);
}
for arg in &mut fty.args {
if arg.is_ignore() { continue; }
- x86_64_ty(ccx, arg, |cls| {
- let needed_int = cls.iter().filter(|&&c| c == Int).count() as isize;
- let needed_sse = cls.iter().filter(|c| c.is_sse()).count() as isize;
- let in_mem = cls.is_pass_byval() ||
- int_regs < needed_int ||
- sse_regs < needed_sse;
- if in_mem {
- // `byval` parameter thus one less integer register available
- int_regs -= 1;
- } else {
- // split into sized chunks passed individually
- int_regs -= needed_int;
- sse_regs -= needed_sse;
- }
- in_mem
- }, Some(ArgAttribute::ByVal));
-
- // An integer, pointer, double or float parameter
- // thus the above closure passed to `x86_64_ty` won't
- // get called.
- match arg.ty.kind() {
- Integer | Pointer => int_regs -= 1,
- Double | Float => sse_regs -= 1,
- _ => {}
- }
+ x86_64_ty(arg, true);
}
}
// option. This file may not be copied, modified, or distributed
// except according to those terms.
-use llvm::*;
-use super::common::*;
-use super::machine::*;
-use abi::{ArgType, FnType};
-use type_::Type;
+use abi::{ArgType, FnType, LayoutExt, Reg};
+use common::CrateContext;
+
+use rustc::ty::layout::Layout;
// Win64 ABI: http://msdn.microsoft.com/en-us/library/zthk2dkh.aspx
-pub fn compute_abi_info(ccx: &CrateContext, fty: &mut FnType) {
- let fixup = |a: &mut ArgType| {
- match a.ty.kind() {
- Struct => match llsize_of_alloc(ccx, a.ty) {
- 1 => a.cast = Some(Type::i8(ccx)),
- 2 => a.cast = Some(Type::i16(ccx)),
- 4 => a.cast = Some(Type::i32(ccx)),
- 8 => a.cast = Some(Type::i64(ccx)),
- _ => a.make_indirect(ccx)
- },
- Integer => match llsize_of_alloc(ccx, a.ty) {
- 1 ... 8 => a.extend_integer_width_to(32),
- 16 => a.make_indirect(ccx),
- _ => bug!(),
- },
- _ => (),
+pub fn compute_abi_info<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, fty: &mut FnType<'tcx>) {
+ let fixup = |a: &mut ArgType<'tcx>| {
+ let size = a.layout.size(ccx);
+ if a.layout.is_aggregate() {
+ match size.bits() {
+ 8 => a.cast_to(ccx, Reg::i8()),
+ 16 => a.cast_to(ccx, Reg::i16()),
+ 32 => a.cast_to(ccx, Reg::i32()),
+ 64 => a.cast_to(ccx, Reg::i64()),
+ _ => a.make_indirect(ccx)
+ };
+ } else {
+ if let Layout::Vector { .. } = *a.layout {
+ // FIXME(eddyb) there should be a size cap here
+ // (probably what clang calls "illegal vectors").
+ } else if size.bytes() > 8 {
+ a.make_indirect(ccx);
+ } else {
+ a.extend_integer_width_to(32);
+ }
}
};
};
let llslot = match op.val {
Immediate(_) | Pair(..) => {
- let llscratch = bcx.alloca(ret.original_ty, "ret");
+ let llscratch = bcx.alloca(ret.memory_ty(bcx.ccx), "ret");
self.store_operand(&bcx, llscratch, None, op);
llscratch
}
};
let load = bcx.load(
bcx.pointercast(llslot, cast_ty.ptr_to()),
- Some(llalign_of_min(bcx.ccx, ret.ty)));
+ Some(ret.layout.align(bcx.ccx).abi() as u32));
load
} else {
let op = self.trans_consume(&bcx, &mir::Lvalue::Local(mir::RETURN_POINTER));
(llargs[0], &llargs[1..])
}
ReturnDest::Nothing => {
- (C_undef(fn_ty.ret.original_ty.ptr_to()), &llargs[..])
+ (C_undef(fn_ty.ret.memory_ty(bcx.ccx).ptr_to()), &llargs[..])
}
ReturnDest::IndirectOperand(dst, _) |
ReturnDest::Store(dst) => (dst, &llargs[..]),
val: Ref(dst, Alignment::AbiAligned),
ty: sig.output(),
};
- self.store_return(&bcx, ret_dest, fn_ty.ret, op);
+ self.store_return(&bcx, ret_dest, &fn_ty.ret, op);
}
if let Some((_, target)) = *destination {
val: Immediate(invokeret),
ty: sig.output(),
};
- self.store_return(&ret_bcx, ret_dest, fn_ty.ret, op);
+ self.store_return(&ret_bcx, ret_dest, &fn_ty.ret, op);
}
} else {
let llret = bcx.call(fn_ptr, &llargs, cleanup_bundle);
val: Immediate(llret),
ty: sig.output(),
};
- self.store_return(&bcx, ret_dest, fn_ty.ret, op);
+ self.store_return(&bcx, ret_dest, &fn_ty.ret, op);
funclet_br(self, bcx, target);
} else {
bcx.unreachable();
bcx: &Builder<'a, 'tcx>,
op: OperandRef<'tcx>,
llargs: &mut Vec<ValueRef>,
- fn_ty: &FnType,
+ fn_ty: &FnType<'tcx>,
next_idx: &mut usize,
llfn: &mut Option<ValueRef>,
def: &Option<ty::InstanceDef<'tcx>>) {
let (mut llval, align, by_ref) = match op.val {
Immediate(_) | Pair(..) => {
if arg.is_indirect() || arg.cast.is_some() {
- let llscratch = bcx.alloca(arg.original_ty, "arg");
+ let llscratch = bcx.alloca(arg.memory_ty(bcx.ccx), "arg");
self.store_operand(bcx, llscratch, None, op);
(llscratch, Alignment::AbiAligned, true)
} else {
// think that ATM (Rust 1.16) we only pass temporaries, but we shouldn't
// have scary latent bugs around.
- let llscratch = bcx.alloca(arg.original_ty, "arg");
+ let llscratch = bcx.alloca(arg.memory_ty(bcx.ccx), "arg");
base::memcpy_ty(bcx, llscratch, llval, op.ty, Some(1));
(llscratch, Alignment::AbiAligned, true)
}
if by_ref && !arg.is_indirect() {
// Have to load the argument, maybe while casting it.
- if arg.original_ty == Type::i1(bcx.ccx) {
+ if arg.layout.ty == bcx.tcx().types.bool {
// We store bools as i8 so we need to truncate to i1.
llval = bcx.load_range_assert(llval, 0, 2, llvm::False, None);
- llval = bcx.trunc(llval, arg.original_ty);
+ llval = bcx.trunc(llval, Type::i1(bcx.ccx));
} else if let Some(ty) = arg.cast {
llval = bcx.load(bcx.pointercast(llval, ty.ptr_to()),
- align.min_with(llalign_of_min(bcx.ccx, arg.ty)));
+ align.min_with(arg.layout.align(bcx.ccx).abi() as u32));
} else {
llval = bcx.load(llval, align.to_align());
}
bcx: &Builder<'a, 'tcx>,
operand: &mir::Operand<'tcx>,
llargs: &mut Vec<ValueRef>,
- fn_ty: &FnType,
+ fn_ty: &FnType<'tcx>,
next_idx: &mut usize,
llfn: &mut Option<ValueRef>,
def: &Option<ty::InstanceDef<'tcx>>) {
fn store_return(&mut self,
bcx: &Builder<'a, 'tcx>,
dest: ReturnDest,
- ret_ty: ArgType,
+ ret_ty: &ArgType<'tcx>,
op: OperandRef<'tcx>) {
use self::ReturnDest::*;
ccx: &'a CrateContext<'a, 'tcx>,
- fn_ty: FnType,
+ fn_ty: FnType<'tcx>,
/// When unwinding is initiated, we have to store this personality
/// value somewhere so that we can load it and re-use it in the
assert_eq!((meta.cast, meta.pad), (None, None));
let llmeta = llvm::get_param(bcx.llfn(), llarg_idx as c_uint);
llarg_idx += 1;
+
+ // FIXME(eddyb) As we can't perfectly represent the data and/or
+ // vtable pointer in a fat pointers in Rust's typesystem, and
+ // because we split fat pointers into two ArgType's, they're
+ // not the right type so we have to cast them for now.
+ let pointee = match arg_ty.sty {
+ ty::TyRef(_, ty::TypeAndMut{ty, ..}) |
+ ty::TyRawPtr(ty::TypeAndMut{ty, ..}) => ty,
+ ty::TyAdt(def, _) if def.is_box() => arg_ty.boxed_ty(),
+ _ => bug!()
+ };
+ let data_llty = type_of::in_memory_type_of(bcx.ccx, pointee);
+ let meta_llty = type_of::unsized_info_ty(bcx.ccx, pointee);
+
+ let llarg = bcx.pointercast(llarg, data_llty.ptr_to());
+ let llmeta = bcx.pointercast(llmeta, meta_llty);
+
OperandValue::Pair(llarg, llmeta)
} else {
OperandValue::Immediate(llarg)
use syntax::ast;
-
-// A "sizing type" is an LLVM type, the size and alignment of which are
-// guaranteed to be equivalent to what you would get out of `type_of()`. It's
-// useful because:
-//
-// (1) It may be cheaper to compute the sizing type than the full type if all
-// you're interested in is the size and/or alignment;
-//
-// (2) It won't make any recursive calls to determine the structure of the
-// type behind pointers. This can help prevent infinite loops for
-// recursive types. For example, enum types rely on this behavior.
-
-pub fn sizing_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, t: Ty<'tcx>) -> Type {
- if let Some(t) = cx.llsizingtypes().borrow().get(&t).cloned() {
- return t;
- }
-
- debug!("sizing_type_of {:?}", t);
- let _recursion_lock = cx.enter_type_of(t);
-
- let ptr_sizing_ty = |ty: Ty<'tcx>| {
- if cx.shared().type_is_sized(ty) {
- Type::i8p(cx)
- } else {
- Type::struct_(cx, &[Type::i8p(cx), unsized_info_ty(cx, ty)], false)
- }
- };
- let llsizingty = match t.sty {
- _ if !cx.shared().type_is_sized(t) => {
- Type::struct_(cx, &[Type::i8p(cx), unsized_info_ty(cx, t)], false)
- }
-
- ty::TyBool => Type::bool(cx),
- ty::TyChar => Type::char(cx),
- ty::TyInt(t) => Type::int_from_ty(cx, t),
- ty::TyUint(t) => Type::uint_from_ty(cx, t),
- ty::TyFloat(t) => Type::float_from_ty(cx, t),
- ty::TyNever => Type::nil(cx),
-
- ty::TyRef(_, ty::TypeAndMut{ty, ..}) |
- ty::TyRawPtr(ty::TypeAndMut{ty, ..}) => {
- ptr_sizing_ty(ty)
- }
- ty::TyAdt(def, _) if def.is_box() => {
- ptr_sizing_ty(t.boxed_ty())
- }
-
- ty::TyFnDef(..) => Type::nil(cx),
- ty::TyFnPtr(_) => Type::i8p(cx),
-
- ty::TyArray(ty, size) => {
- let llty = sizing_type_of(cx, ty);
- let size = size as u64;
- Type::array(&llty, size)
- }
-
- ty::TyTuple(ref tys, _) if tys.is_empty() => {
- Type::nil(cx)
- }
-
- ty::TyAdt(..) if t.is_simd() => {
- let e = t.simd_type(cx.tcx());
- if !e.is_machine() {
- cx.sess().fatal(&format!("monomorphising SIMD type `{}` with \
- a non-machine element type `{}`",
- t, e))
- }
- let llet = type_of(cx, e);
- let n = t.simd_size(cx.tcx()) as u64;
- Type::vector(&llet, n)
- }
-
- ty::TyTuple(..) | ty::TyAdt(..) | ty::TyClosure(..) => {
- adt::sizing_type_of(cx, t, false)
- }
-
- ty::TyProjection(..) | ty::TyInfer(..) | ty::TyParam(..) |
- ty::TyAnon(..) | ty::TyError => {
- bug!("fictitious type {:?} in sizing_type_of()", t)
- }
- ty::TySlice(_) | ty::TyDynamic(..) | ty::TyStr => bug!()
- };
-
- debug!("--> mapped t={:?} to llsizingty={:?}", t, llsizingty);
-
- cx.llsizingtypes().borrow_mut().insert(t, llsizingty);
-
- // FIXME(eddyb) Temporary sanity check for ty::layout.
- let layout = cx.layout_of(t);
- if !cx.shared().type_is_sized(t) {
- if !layout.is_unsized() {
- bug!("layout should be unsized for type `{}` / {:#?}",
- t, layout);
- }
-
- // Unsized types get turned into a fat pointer for LLVM.
- return llsizingty;
- }
-
- let r = layout.size(cx).bytes();
- let l = machine::llsize_of_alloc(cx, llsizingty);
- if r != l {
- bug!("size differs (rustc: {}, llvm: {}) for type `{}` / {:#?}",
- r, l, t, layout);
- }
-
- let r = layout.align(cx).abi();
- let l = machine::llalign_of_min(cx, llsizingty) as u64;
- if r != l {
- bug!("align differs (rustc: {}, llvm: {}) for type `{}` / {:#?}",
- r, l, t, layout);
- }
-
- llsizingty
-}
-
pub fn fat_ptr_base_ty<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, ty: Ty<'tcx>) -> Type {
match ty.sty {
ty::TyRef(_, ty::TypeAndMut { ty: t, .. }) |
}
}
-fn unsized_info_ty<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, ty: Ty<'tcx>) -> Type {
+pub fn unsized_info_ty<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, ty: Ty<'tcx>) -> Type {
let unsized_part = ccx.tcx().struct_tail(ty);
match unsized_part.sty {
ty::TyStr | ty::TyArray(..) | ty::TySlice(_) => {
/// of that field's type - this is useful for taking the address of
/// that field and ensuring the struct has the right alignment.
/// For the LLVM type of a value as a whole, see `type_of`.
-/// NB: If you update this, be sure to update `sizing_type_of()` as well.
pub fn in_memory_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, t: Ty<'tcx>) -> Type {
// Check the cache.
if let Some(&llty) = cx.lltypes().borrow().get(&t) {
fn str(_: &[u8]) {
}
-// CHECK: @trait_borrow(i8* nonnull, void (i8*)** noalias nonnull readonly)
+// CHECK: @trait_borrow({}* nonnull, {}* noalias nonnull readonly)
// FIXME #25759 This should also have `nocapture`
#[no_mangle]
fn trait_borrow(_: &Drop) {
}
-// CHECK: @trait_box(i8* noalias nonnull, void (i8*)** noalias nonnull readonly)
+// CHECK: @trait_box({}* noalias nonnull, {}* noalias nonnull readonly)
#[no_mangle]
fn trait_box(_: Box<Drop>) {
}