]> git.lizzy.rs Git - rust.git/blobdiff - src/librustc_trans/cabi_x86_64.rs
Rollup merge of #41249 - GuillaumeGomez:rustdoc-render, r=steveklabnik,frewsxcv
[rust.git] / src / librustc_trans / cabi_x86_64.rs
index 33990148c8b7de13d4dbc1e4a39de610d9cd58f3..2daebf5cf3d6b191efeca18907acf854eb359aff 100644 (file)
 // 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, Attribute, Vector};
-use abi::{self, ArgType, 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
-        }
-    }
-}
+use rustc::ty::layout::{self, Layout, TyLayout, Size};
 
-trait ClassList {
-    fn is_pass_byval(&self) -> bool;
-    fn is_ret_bysret(&self) -> bool;
+#[derive(Clone, Copy, PartialEq, Debug)]
+enum Class {
+    None,
+    Int,
+    Sse,
+    SseUp
 }
 
-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; }
-
-        self[0] == Memory
-    }
-}
+#[derive(Clone, Copy, Debug)]
+struct Memory;
 
-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;
-    }
+// Currently supported vector size (AVX).
+const LARGEST_VECTOR_SIZE: usize = 256;
+const MAX_EIGHTBYTES: usize = LARGEST_VECTOR_SIZE / 64;
 
-    fn ty_align(ty: Type) -> usize {
-        abi::ty_align(ty, 8)
-    }
+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,
 
-    fn ty_size(ty: Type) -> usize {
-        abi::ty_size(ty, 8)
-    }
+            (Class::Int, _) |
+            (_, Class::Int) => Class::Int,
 
-    fn all_mem(cls: &mut [RegClass]) {
-        for elt in cls {
-            *elt = Memory;
-        }
-    }
+            (Class::Sse, _) |
+            (_, Class::Sse) => Class::Sse,
 
-    fn unify(cls: &mut [RegClass],
-             i: usize,
-             newv: RegClass) {
-        if cls[i] == newv { return }
-
-        let to_write = match (cls[i], newv) {
-            (NoClass,     _) => newv,
-            (_,           NoClass) => return,
-
-            (Memory,      _) |
-            (_,           Memory) => Memory,
-
-            (Int,         _) |
-            (_,           Int) => Int,
-
-            (X87,         _) |
-            (X87Up,       _) |
-            (ComplexX87,  _) |
-            (_,           X87) |
-            (_,           X87Up) |
-            (_,           ComplexX87) => Memory,
-
-            (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(())
     }
 
-    fn fixup(ty: Type, cls: &mut [RegClass]) {
+    let n = ((arg.layout.size(ccx).bytes() + 7) / 8) as usize;
+    if n > MAX_EIGHTBYTES {
+        return Err(Memory);
+    }
+
+    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))
-                    }
-                    _ => 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")
+    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()
+            })
         }
-        i += 1;
-    }
-    if tys.len() == 1 && tys[0].kind() == Vector {
-        // if the type contains only a vector, pass it as that vector.
-        tys[0]
-    } else {
-        Type::struct_(ccx, &tys, false)
-    }
-}
-
-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<Attribute>)
-        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);
+        Class::Sse => {
+            let vec_len = 1 + cls[*i+1..].iter().take_while(|&&c| c == Class::SseUp).count();
+            *i += vec_len;
+            Some(if vec_len == 1 {
+                match size {
+                    4 => Reg::f32(),
+                    _ => Reg::f64()
                 }
             } else {
-                arg.cast = Some(llreg_ty(ccx, &cls));
-            }
-        } else {
-            arg.extend_integer_width_to(32);
+                Reg {
+                    kind: RegKind::Vector,
+                    size: Size::from_bytes(vec_len as u64 * 8)
+                }
+            })
         }
+        c => bug!("reg_component: unhandled class {:?}", c)
     }
+}
+
+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 {
+        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<'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
 
-    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
+    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,
+                        _ => {}
+                    }
+                }
+                arg.layout.is_aggregate() &&
+                    (int_regs < needed_int || sse_regs < needed_sse)
+            }
+            Ok(_) => false
+        };
+
+        if in_mem {
+            // `sret` / `byval` parameter thus one less integer register available
+            int_regs -= 1;
+
+            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(Attribute::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);
     }
 }