rust/src/libsyntax/abi.rs
2013-06-25 16:15:07 -04:00

409 lines
9.5 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(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).
*/
for each_abi |abi| {
if name == abi.data().name {
return Some(abi);
}
}
return None;
}
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
for self.each |abi| {
let data = abi.data();
match data.abi_arch {
Archs(a) if (a & arch.bit()) != 0 => { return Some(abi); }
Archs(_) => { }
RustArch | AllArch => { return Some(abi); }
}
}
None
}
pub fn check_valid(&self) -> Option<(Abi, Abi)> {
let mut abis = ~[];
for self.each |abi| { abis.push(abi); }
for abis.iter().enumerate().advance |(i, abi)| {
let data = abi.data();
for abis.slice(0, i).iter().advance |other_abi| {
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 = ~[];
for self.each |abi| {
strs.push(abi.data().name);
}
fmt!("\"%s\"", strs.connect(" "))
}
}
#[test]
fn lookup_Rust() {
let abi = lookup("Rust");
assert!(abi.is_some() && abi.get().data().name == "Rust");
}
#[test]
fn lookup_cdecl() {
let abi = lookup("cdecl");
assert!(abi.is_some() && abi.get().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 AbiDatas.iter().enumerate().advance |(i, abi_data)| {
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 abis.iter().advance |&abi| {
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);
}