0ac7a219f0
- Made naming schemes consistent between Option, Result and Either - Changed Options Add implementation to work like the maybe monad (return None if any of the inputs is None) - Removed duplicate Option::get and renamed all related functions to use the term `unwrap` instead
418 lines
9.6 KiB
Rust
418 lines
9.6 KiB
Rust
// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
|
|
// file at the top-level directory of this distribution and at
|
|
// http://rust-lang.org/COPYRIGHT.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
|
// option. This file may not be copied, modified, or distributed
|
|
// except according to those terms.
|
|
|
|
use std::to_bytes;
|
|
|
|
#[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,
|
|
|
|
// Multiplatform ABIs second
|
|
Rust,
|
|
C,
|
|
RustIntrinsic,
|
|
}
|
|
|
|
#[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)},
|
|
|
|
// 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: RustIntrinsic, name: "rust-intrinsic", abi_arch: RustArch},
|
|
];
|
|
|
|
fn each_abi(op: &fn(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<Abi> {
|
|
/*!
|
|
*
|
|
* Returns the ABI with the given name (if any).
|
|
*/
|
|
|
|
let mut res = None;
|
|
|
|
do 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
|
|
}
|
|
}
|
|
|
|
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: &fn(abi: Abi) -> bool) -> bool {
|
|
each_abi(|abi| !self.contains(abi) || op(abi))
|
|
}
|
|
|
|
pub fn is_empty(&self) -> bool {
|
|
self.bits == 0
|
|
}
|
|
|
|
pub fn for_arch(&self, arch: Architecture) -> Option<Abi> {
|
|
// NB---Single platform ABIs come first
|
|
|
|
let mut res = None;
|
|
|
|
do 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
|
|
}
|
|
|
|
pub fn check_valid(&self) -> Option<(Abi, Abi)> {
|
|
let mut abis = ~[];
|
|
do 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 = ~[];
|
|
do self.each |abi| {
|
|
strs.push(abi.data().name);
|
|
true
|
|
};
|
|
fmt!("\"%s\"", 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_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 = %s", 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 = %s", set.to_str());
|
|
assert!(set.to_str() == ~"\"Rust\"");
|
|
}
|
|
|
|
#[test]
|
|
fn indices_are_correct() {
|
|
for (i, abi_data) in AbiDatas.iter().enumerate() {
|
|
assert!(i == abi_data.abi.index());
|
|
}
|
|
|
|
let bits = 1 << (X86 as u32);
|
|
let bits = bits | 1 << (X86_64 as u32);
|
|
assert!(IntelBits == bits);
|
|
|
|
let bits = 1 << (Arm as u32);
|
|
assert!(ArmBits == bits);
|
|
}
|
|
|
|
#[cfg(test)]
|
|
fn check_arch(abis: &[Abi], arch: Architecture, expect: Option<Abi>) {
|
|
let mut set = AbiSet::empty();
|
|
for &abi in abis.iter() {
|
|
set.add(abi);
|
|
}
|
|
let r = set.for_arch(arch);
|
|
assert!(r == expect);
|
|
}
|
|
|
|
#[test]
|
|
fn pick_multiplatform() {
|
|
check_arch([C, Cdecl], X86, Some(Cdecl));
|
|
check_arch([C, Cdecl], X86_64, Some(Cdecl));
|
|
check_arch([C, Cdecl], Arm, Some(C));
|
|
}
|
|
|
|
#[test]
|
|
fn pick_uniplatform() {
|
|
check_arch([Stdcall], X86, Some(Stdcall));
|
|
check_arch([Stdcall], Arm, None);
|
|
}
|