Skip to content

Commit

Permalink
Implement type representation in Rust.
Browse files Browse the repository at this point in the history
Start the Cretonne library as a Rust crate.
  • Loading branch information
Jakob Stoklund Olesen committed Mar 11, 2016
1 parent 29cc36c commit 82e05bc
Show file tree
Hide file tree
Showing 4 changed files with 250 additions and 0 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
*.pyc
Cargo.lock
target
10 changes: 10 additions & 0 deletions src/libcretonne/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
[package]
authors = ["The cwCretonneRust Project Developers"]
name = "cretonne"
version = "0.0.0"

[lib]
name = "cretonne"
path = "lib.rs"

[dependencies]
8 changes: 8 additions & 0 deletions src/libcretonne/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@

//====--------------------------------------------------------------------------------------====//
//
// Cretonne code generation library.
//
//====--------------------------------------------------------------------------------------====//

mod types;
230 changes: 230 additions & 0 deletions src/libcretonne/types.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,230 @@

//! Common types for the Cretonne code generator.
use std::fmt::{self, Display, Formatter, Write};

/// The type of an SSA value.
///
/// The VOID type is only used for instructions that produce no value. It can't be part of a SIMD
/// vector.
///
/// Basic integer types: `I8`, `I16`, `I32`, and `I64`. These types are sign-agnostic.
///
/// Basic floating point types: `F32` and `F64`. IEEE single and double precision.
///
/// Boolean types: `B1`, `B8`, `B16`, `B32`, and `B64`. These all encode 'true' or 'false'. The
/// larger types use redundant bits.
///
/// SIMD vector types have power-of-two lanes, up to 256. Lanes can be any int/float/bool type.
///
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub struct Type(u8);

pub const VOID: Type = Type(0);

pub const I8: Type = Type(1);
pub const I16: Type = Type(2);
pub const I32: Type = Type(3);
pub const I64: Type = Type(4);

pub const F32: Type = Type(5);
pub const F64: Type = Type(6);

pub const B1: Type = Type(7);
pub const B8: Type = Type(8);
pub const B16: Type = Type(9);
pub const B32: Type = Type(10);
pub const B64: Type = Type(11);

impl Type {
/// Get the lane type of this SIMD vector type.
///
/// A scalar type is the same as a SIMD vector type with one lane, so it returns itself.
pub fn lane_type(self) -> Type {
Type(self.0 & 0x0f)
}

/// Get the number of bits in a lane.
pub fn lane_bits(self) -> u8 {
match self.lane_type() {
B1 => 1,
B8 | I8 => 8,
B16 | I16 => 16,
B32 | I32 | F32 => 32,
B64 | I64 | F64 => 64,
_ => 0,
}
}

/// Is this the VOID type?
pub fn is_void(self) -> bool {
self == VOID
}

/// Is this a scalar boolean type?
pub fn is_bool(self) -> bool {
match self {
B1 | B8 | B16 | B32 | B64 => true,
_ => false,
}
}

/// Is this a scalar integer type?
pub fn is_int(self) -> bool {
match self {
I8 | I16 | I32 | I64 => true,
_ => false,
}
}

/// Is this a scalar floating point type?
pub fn is_float(self) -> bool {
match self {
F32 | F64 => true,
_ => false,
}
}

/// Get log2 of the number of lanes in this SIMD vector type.
///
/// All SIMD types have a lane count that is a power of two and no larger than 256, so this
/// will be a number in the range 0-8.
///
/// A scalar type is the same as a SIMD vector type with one lane, so it return 0.
pub fn log2_lane_count(self) -> u8 {
self.0 >> 4
}

/// Is this a scalar type? (That is, not a SIMD vector type).
///
/// A scalar type is the same as a SIMD vector type with one lane.
pub fn is_scalar(self) -> bool {
self.log2_lane_count() == 0
}

/// Get the number of lanes in this SIMD vector type.
///
/// A scalar type is the same as a SIMD vector type with one lane, so it returns 1.
pub fn lane_count(self) -> u16 {
1 << self.log2_lane_count()
}

/// Get the total number of bits used to represent this type.
pub fn bits(self) -> u16 {
self.lane_bits() as u16 * self.lane_count()
}

/// Get a SIMD vector type with `n` times more lanes than this one.
///
/// If this is a scalar type, this produces a SIMD type with this as a lane type and `n` lanes.
///
/// If this is already a SIMD vector type, this produces a SIMD vector type with `n *
/// self.lane_count()` lanes.
pub fn by(self, n: u16) -> Type {
debug_assert!(self.lane_bits() > 0,
"Can't make SIMD vectors with void lanes.");
debug_assert!(n.is_power_of_two(),
"Number of SIMD lanes must be a power of two");
let log2_lanes: u32 = n.trailing_zeros();
let new_type = self.0 as u32 + (log2_lanes << 4);
assert!(new_type < 0x90, "No more than 256 SIMD lanes supported");
Type(new_type as u8)
}

/// Get a SIMD vector with half the number of lanes.
pub fn half_vector(self) -> Type {
assert!(!self.is_scalar(), "Expecting a proper SIMD vector type.");
Type(self.0 - 0x10)
}
}

impl Display for Type {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
if self.is_void() {
write!(f, "void")
} else if self.is_bool() {
write!(f, "b{}", self.lane_bits())
} else if self.is_int() {
write!(f, "i{}", self.lane_bits())
} else if self.is_float() {
write!(f, "f{}", self.lane_bits())
} else if !self.is_scalar() {
write!(f, "{}x{}", self.lane_type(), self.lane_count())
} else {
panic!("Invalid Type(0x{:x})", self.0)
}
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn basic_scalars() {
assert_eq!(VOID, VOID.lane_type());
assert_eq!(0, VOID.bits());
assert_eq!(B1, B1.lane_type());
assert_eq!(B8, B8.lane_type());
assert_eq!(B16, B16.lane_type());
assert_eq!(B32, B32.lane_type());
assert_eq!(B64, B64.lane_type());
assert_eq!(I8, I8.lane_type());
assert_eq!(I16, I16.lane_type());
assert_eq!(I32, I32.lane_type());
assert_eq!(I64, I64.lane_type());
assert_eq!(F32, F32.lane_type());
assert_eq!(F64, F64.lane_type());

assert_eq!(VOID.lane_bits(), 0);
assert_eq!(B1.lane_bits(), 1);
assert_eq!(B8.lane_bits(), 8);
assert_eq!(B16.lane_bits(), 16);
assert_eq!(B32.lane_bits(), 32);
assert_eq!(B64.lane_bits(), 64);
assert_eq!(I8.lane_bits(), 8);
assert_eq!(I16.lane_bits(), 16);
assert_eq!(I32.lane_bits(), 32);
assert_eq!(I64.lane_bits(), 64);
assert_eq!(F32.lane_bits(), 32);
assert_eq!(F64.lane_bits(), 64);
}

#[test]
fn vectors() {
let big = F64.by(256);
assert_eq!(big.lane_bits(), 64);
assert_eq!(big.lane_count(), 256);
assert_eq!(big.bits(), 64 * 256);

assert_eq!(format!("{}", big.half_vector()), "f64x128");
assert_eq!(format!("{}", B1.by(2).half_vector()), "b1");
}

#[test]
fn format_scalars() {
assert_eq!(format!("{}", VOID), "void");
assert_eq!(format!("{}", B1), "b1");
assert_eq!(format!("{}", B8), "b8");
assert_eq!(format!("{}", B16), "b16");
assert_eq!(format!("{}", B32), "b32");
assert_eq!(format!("{}", B64), "b64");
assert_eq!(format!("{}", I8), "i8");
assert_eq!(format!("{}", I16), "i16");
assert_eq!(format!("{}", I32), "i32");
assert_eq!(format!("{}", I64), "i64");
assert_eq!(format!("{}", F32), "f32");
assert_eq!(format!("{}", F64), "f64");
}

#[test]
fn format_vectors() {
assert_eq!(format!("{}", B1.by(8)), "b1x8");
assert_eq!(format!("{}", B8.by(1)), "b8");
assert_eq!(format!("{}", B16.by(256)), "b16x256");
assert_eq!(format!("{}", B32.by(4).by(2)), "b32x8");
assert_eq!(format!("{}", B64.by(8)), "b64x8");
assert_eq!(format!("{}", I8.by(64)), "i8x64");
assert_eq!(format!("{}", F64.by(2)), "f64x2");
}
}

0 comments on commit 82e05bc

Please sign in to comment.