// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. use std::to_bytes; #[deriving(Eq)] pub enum Os { OsWin32, OsMacos, OsLinux, OsAndroid, OsFreebsd, } #[deriving(Eq)] pub enum Abi { // NB: This ordering MUST match the AbiDatas array below. // (This is ensured by the test indices_are_correct().) // Single platform ABIs come first (`for_arch()` relies on this) Cdecl, Stdcall, Fastcall, Aapcs, Win64, // Multiplatform ABIs second Rust, C, System, RustIntrinsic, } #[allow(non_camel_case_types)] #[deriving(Eq)] pub enum Architecture { // NB. You cannot change the ordering of these // constants without adjusting IntelBits below. // (This is ensured by the test indices_are_correct().) X86, X86_64, Arm, Mips } static IntelBits: u32 = (1 << (X86 as uint)) | (1 << (X86_64 as uint)); static ArmBits: u32 = (1 << (Arm as uint)); struct AbiData { abi: Abi, // Name of this ABI as we like it called. name: &'static str, // Is it specific to a platform? If so, which one? Also, what is // the name that LLVM gives it (in case we disagree) abi_arch: AbiArchitecture } enum AbiArchitecture { RustArch, // Not a real ABI (e.g., intrinsic) AllArch, // An ABI that specifies cross-platform defaults (e.g., "C") Archs(u32) // Multiple architectures (bitset) } #[deriving(Clone, Eq, Encodable, Decodable)] pub struct AbiSet { priv bits: u32 // each bit represents one of the abis below } static AbiDatas: &'static [AbiData] = &[ // Platform-specific ABIs AbiData {abi: Cdecl, name: "cdecl", abi_arch: Archs(IntelBits)}, AbiData {abi: Stdcall, name: "stdcall", abi_arch: Archs(IntelBits)}, AbiData {abi: Fastcall, name:"fastcall", abi_arch: Archs(IntelBits)}, AbiData {abi: Aapcs, name: "aapcs", abi_arch: Archs(ArmBits)}, AbiData {abi: Win64, name: "win64", abi_arch: Archs(1 << (X86_64 as uint))}, // Cross-platform ABIs // // NB: Do not adjust this ordering without // adjusting the indices below. AbiData {abi: Rust, name: "Rust", abi_arch: RustArch}, AbiData {abi: C, name: "C", abi_arch: AllArch}, AbiData {abi: System, name: "system", abi_arch: AllArch}, AbiData {abi: RustIntrinsic, name: "rust-intrinsic", abi_arch: RustArch}, ]; fn each_abi(op: |abi: Abi| -> bool) -> bool { /*! * * Iterates through each of the defined ABIs. */ AbiDatas.iter().advance(|abi_data| op(abi_data.abi)) } pub fn lookup(name: &str) -> Option { /*! * * Returns the ABI with the given name (if any). */ let mut res = None; each_abi(|abi| { if name == abi.data().name { res = Some(abi); false } else { true } }); res } pub fn all_names() -> ~[&'static str] { AbiDatas.map(|d| d.name) } impl Abi { #[inline] pub fn index(&self) -> uint { *self as uint } #[inline] pub fn data(&self) -> &'static AbiData { &AbiDatas[self.index()] } pub fn name(&self) -> &'static str { self.data().name } pub fn for_target(&self, os: Os, arch: Architecture) -> Abi { match (*self, os, arch) { (System, OsWin32, X86) => Stdcall, (System, _, _) => C, (me, _, _) => me, } } } impl Architecture { fn bit(&self) -> u32 { 1 << (*self as u32) } } impl AbiSet { pub fn from(abi: Abi) -> AbiSet { AbiSet { bits: (1 << abi.index()) } } #[inline] pub fn Rust() -> AbiSet { AbiSet::from(Rust) } #[inline] pub fn C() -> AbiSet { AbiSet::from(C) } #[inline] pub fn Intrinsic() -> AbiSet { AbiSet::from(RustIntrinsic) } pub fn default() -> AbiSet { AbiSet::C() } pub fn empty() -> AbiSet { AbiSet { bits: 0 } } #[inline] pub fn is_rust(&self) -> bool { self.bits == 1 << Rust.index() } #[inline] pub fn is_c(&self) -> bool { self.bits == 1 << C.index() } #[inline] pub fn is_intrinsic(&self) -> bool { self.bits == 1 << RustIntrinsic.index() } pub fn contains(&self, abi: Abi) -> bool { (self.bits & (1 << abi.index())) != 0 } pub fn subset_of(&self, other_abi_set: AbiSet) -> bool { (self.bits & other_abi_set.bits) == self.bits } pub fn add(&mut self, abi: Abi) { self.bits |= (1 << abi.index()); } pub fn each(&self, op: |abi: Abi| -> bool) -> bool { each_abi(|abi| !self.contains(abi) || op(abi)) } pub fn is_empty(&self) -> bool { self.bits == 0 } pub fn for_target(&self, os: Os, arch: Architecture) -> Option { // NB---Single platform ABIs come first let mut res = None; self.each(|abi| { let data = abi.data(); match data.abi_arch { Archs(a) if (a & arch.bit()) != 0 => { res = Some(abi); false } Archs(_) => { true } RustArch | AllArch => { res = Some(abi); false } } }); res.map(|r| r.for_target(os, arch)) } pub fn check_valid(&self) -> Option<(Abi, Abi)> { let mut abis = ~[]; self.each(|abi| { abis.push(abi); true }); for (i, abi) in abis.iter().enumerate() { let data = abi.data(); for other_abi in abis.slice(0, i).iter() { let other_data = other_abi.data(); debug!("abis=({:?},{:?}) datas=({:?},{:?})", abi, data.abi_arch, other_abi, other_data.abi_arch); match (&data.abi_arch, &other_data.abi_arch) { (&AllArch, &AllArch) => { // Two cross-architecture ABIs return Some((*abi, *other_abi)); } (_, &RustArch) | (&RustArch, _) => { // Cannot combine Rust or Rust-Intrinsic with // anything else. return Some((*abi, *other_abi)); } (&Archs(is), &Archs(js)) if (is & js) != 0 => { // Two ABIs for same architecture return Some((*abi, *other_abi)); } _ => {} } } } return None; } } impl to_bytes::IterBytes for Abi { fn iter_bytes(&self, lsb0: bool, f: to_bytes::Cb) -> bool { self.index().iter_bytes(lsb0, f) } } impl to_bytes::IterBytes for AbiSet { fn iter_bytes(&self, lsb0: bool, f: to_bytes::Cb) -> bool { self.bits.iter_bytes(lsb0, f) } } impl ToStr for Abi { fn to_str(&self) -> ~str { self.data().name.to_str() } } impl ToStr for AbiSet { fn to_str(&self) -> ~str { let mut strs = ~[]; self.each(|abi| { strs.push(abi.data().name); true }); format!("\"{}\"", strs.connect(" ")) } } #[test] fn lookup_Rust() { let abi = lookup("Rust"); assert!(abi.is_some() && abi.unwrap().data().name == "Rust"); } #[test] fn lookup_cdecl() { let abi = lookup("cdecl"); assert!(abi.is_some() && abi.unwrap().data().name == "cdecl"); } #[test] fn lookup_baz() { let abi = lookup("baz"); assert!(abi.is_none()); } #[cfg(test)] fn cannot_combine(n: Abi, m: Abi) { let mut set = AbiSet::empty(); set.add(n); set.add(m); match set.check_valid() { Some((a, b)) => { assert!((n == a && m == b) || (m == a && n == b)); } None => { fail!("Invalid match not detected"); } } } #[cfg(test)] fn can_combine(n: Abi, m: Abi) { let mut set = AbiSet::empty(); set.add(n); set.add(m); match set.check_valid() { Some((_, _)) => { fail!("Valid match declared invalid"); } None => {} } } #[test] fn cannot_combine_cdecl_and_stdcall() { cannot_combine(Cdecl, Stdcall); } #[test] fn cannot_combine_c_and_rust() { cannot_combine(C, Rust); } #[test] fn cannot_combine_rust_and_cdecl() { cannot_combine(Rust, Cdecl); } #[test] fn cannot_combine_rust_intrinsic_and_cdecl() { cannot_combine(RustIntrinsic, Cdecl); } #[test] fn can_combine_system_and_cdecl() { can_combine(System, Cdecl); } #[test] fn can_combine_c_and_stdcall() { can_combine(C, Stdcall); } #[test] fn can_combine_aapcs_and_stdcall() { can_combine(Aapcs, Stdcall); } #[test] fn abi_to_str_stdcall_aaps() { let mut set = AbiSet::empty(); set.add(Aapcs); set.add(Stdcall); assert!(set.to_str() == ~"\"stdcall aapcs\""); } #[test] fn abi_to_str_c_aaps() { let mut set = AbiSet::empty(); set.add(Aapcs); set.add(C); debug!("set = {}", set.to_str()); assert!(set.to_str() == ~"\"aapcs C\""); } #[test] fn abi_to_str_rust() { let mut set = AbiSet::empty(); set.add(Rust); debug!("set = {}", set.to_str()); assert!(set.to_str() == ~"\"Rust\""); } #[test] fn indices_are_correct() { for (i, abi_data) in AbiDatas.iter().enumerate() { assert_eq!(i, abi_data.abi.index()); } let bits = 1 << (X86 as u32); let bits = bits | 1 << (X86_64 as u32); assert_eq!(IntelBits, bits); let bits = 1 << (Arm as u32); assert_eq!(ArmBits, bits); } #[cfg(test)] fn get_arch(abis: &[Abi], os: Os, arch: Architecture) -> Option { let mut set = AbiSet::empty(); for &abi in abis.iter() { set.add(abi); } set.for_target(os, arch) } #[test] fn pick_multiplatform() { assert_eq!(get_arch([C, Cdecl], OsLinux, X86), Some(Cdecl)); assert_eq!(get_arch([C, Cdecl], OsLinux, X86_64), Some(Cdecl)); assert_eq!(get_arch([C, Cdecl], OsLinux, Arm), Some(C)); } #[test] fn pick_uniplatform() { assert_eq!(get_arch([Stdcall], OsLinux, X86), Some(Stdcall)); assert_eq!(get_arch([Stdcall], OsLinux, Arm), None); assert_eq!(get_arch([System], OsLinux, X86), Some(C)); assert_eq!(get_arch([System], OsWin32, X86), Some(Stdcall)); assert_eq!(get_arch([System], OsWin32, X86_64), Some(C)); assert_eq!(get_arch([System], OsWin32, Arm), Some(C)); assert_eq!(get_arch([Stdcall], OsWin32, X86), Some(Stdcall)); assert_eq!(get_arch([Stdcall], OsWin32, X86_64), Some(Stdcall)); }