-
Notifications
You must be signed in to change notification settings - Fork 201
Added initial Rust codegen-meta implementation. #403
Changes from 7 commits
c05527a
4e3205a
565516b
4944d8c
69f34e0
61378a8
194ec5e
2f42b13
9d90806
8842b28
b9a1835
ebed1c9
7c39c33
bba565b
f842b07
a62db2e
07e2919
1a8f003
1d121c5
2c1232d
d8e9423
73b6da6
352a859
551b419
9534bed
46d4bb1
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
[package] | ||
name = "cranelift-codegen-meta" | ||
authors = ["The Cranelift Project Developers"] | ||
version = "0.15.0" | ||
description = "DSL for cranelift-codegen code generator library" | ||
license = "Apache-2.0" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Cranelift's license is now "Apache-2.0 WITH LLVM-exception". There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also, please add a copy of the LICENSE file under lib/codegen-meta (this is a recent change). |
||
documentation = "https://cranelift.readthedocs.io/" | ||
repository = "https://github.com/CraneStation/cranelift" | ||
keywords = ["compile", "compiler", "jit"] | ||
|
||
[dependencies] | ||
# It is a goal of the cranelift-codegen crate to have minimal external dependencies. | ||
# Please don't add any unless they are essential to the task of creating binary | ||
# machine code. | ||
|
||
[badges] | ||
maintenance = { status = "experimental" } | ||
travis-ci = { repository = "CraneStation/cranelift" } |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
//! Definitions for the base Cranelift language. | ||
|
||
pub mod types; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,247 @@ | ||
//! This module predefines all the Cranelift scalar types. | ||
|
||
// Numbering scheme for value types: | ||
// | ||
// 0: Void | ||
// 0x01-0x6f: Special types | ||
// 0x70-0x7f: Lane types | ||
// 0x80-0xff: Vector types | ||
// | ||
// Vector types are encoded with the lane type in the low 4 bits and log2(lanes) | ||
// in the high 4 bits, giving a range of 2-256 lanes. | ||
static LANE_BASE: u8 = 0x70; | ||
|
||
#[derive(Debug, Clone, Copy, Eq, PartialEq)] | ||
pub enum Bool { | ||
/// 1-bit bool. | ||
B1 = 1, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does this code depend on the enum fields having explicit values? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Lane types such as |
||
/// 8-bit bool. | ||
B8 = 8, | ||
/// 16-bit bool. | ||
B16 = 16, | ||
/// 32-bit bool. | ||
B32 = 32, | ||
/// 64-bit bool. | ||
B64 = 64, | ||
} | ||
|
||
impl Bool { | ||
/// Get the number of a boolean variant. | ||
pub fn number(self) -> u8 { | ||
let offset = match self { | ||
Bool::B1 => 0, | ||
Bool::B8 => 1, | ||
Bool::B16 => 2, | ||
Bool::B32 => 3, | ||
Bool::B64 => 4, | ||
}; | ||
|
||
LANE_BASE + offset | ||
} | ||
} | ||
|
||
pub struct BoolIterator { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please add doc comments to the iterator structs briefly explaining what they're for. Just "This provides an iterator through all supported bool types" or so. |
||
index: u8, | ||
} | ||
|
||
impl BoolIterator { | ||
pub fn new() -> Self { | ||
Self { index: 0 } | ||
} | ||
} | ||
|
||
impl Iterator for BoolIterator { | ||
type Item = Bool; | ||
fn next(&mut self) -> Option<Self::Item> { | ||
let res = match self.index { | ||
0 => Some(Bool::B1), | ||
1 => Some(Bool::B8), | ||
2 => Some(Bool::B16), | ||
3 => Some(Bool::B32), | ||
4 => Some(Bool::B64), | ||
_ => return None, | ||
}; | ||
self.index += 1; | ||
res | ||
} | ||
} | ||
|
||
#[derive(Debug, Clone, Copy, Eq, PartialEq)] | ||
pub enum Int { | ||
/// 8-bit int. | ||
I8 = 8, | ||
/// 16-bit int. | ||
I16 = 16, | ||
/// 32-bit int. | ||
I32 = 32, | ||
/// 64-bit int. | ||
I64 = 64, | ||
} | ||
|
||
impl Int { | ||
/// Get the number of an integer variant. | ||
pub fn number(self) -> u8 { | ||
let offset = 5 + match self { | ||
Int::I8 => 0, | ||
Int::I16 => 1, | ||
Int::I32 => 2, | ||
Int::I64 => 3, | ||
}; | ||
|
||
LANE_BASE + offset | ||
} | ||
} | ||
|
||
pub struct IntIterator { | ||
index: u8, | ||
} | ||
|
||
impl IntIterator { | ||
pub fn new() -> Self { | ||
Self { index: 0 } | ||
} | ||
} | ||
|
||
impl Iterator for IntIterator { | ||
type Item = Int; | ||
fn next(&mut self) -> Option<Self::Item> { | ||
let res = match self.index { | ||
0 => Some(Int::I8), | ||
1 => Some(Int::I16), | ||
2 => Some(Int::I32), | ||
3 => Some(Int::I64), | ||
_ => return None, | ||
}; | ||
self.index += 1; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What about release mode overflow? (Call |
||
res | ||
} | ||
} | ||
|
||
#[derive(Debug, Clone, Copy, Eq, PartialEq)] | ||
pub enum Float { | ||
F32 = 32, | ||
F64 = 64, | ||
} | ||
|
||
impl Float { | ||
/// Get the number of a float variant. | ||
pub fn number(self) -> u8 { | ||
let offset = 9 + match self { | ||
Float::F32 => 0, | ||
Float::F64 => 1, | ||
}; | ||
|
||
LANE_BASE + offset | ||
} | ||
} | ||
|
||
/// Iterator through the variants of the Float enum. | ||
pub struct FloatIterator { | ||
index: u8, | ||
} | ||
|
||
impl FloatIterator { | ||
pub fn new() -> Self { | ||
Self { index: 0 } | ||
} | ||
} | ||
|
||
impl Iterator for FloatIterator { | ||
type Item = Float; | ||
fn next(&mut self) -> Option<Self::Item> { | ||
let res = match self.index { | ||
0 => Some(Float::F32), | ||
1 => Some(Float::F64), | ||
_ => return None, | ||
}; | ||
self.index += 1; | ||
res | ||
} | ||
} | ||
|
||
/// A type representing CPU flags. | ||
/// | ||
/// Flags can't be stored in memory. | ||
#[derive(Debug, Clone, Copy, Eq, PartialEq)] | ||
pub enum Flag { | ||
/// CPU flags from an integer comparison. | ||
IFlags, | ||
/// CPU flags from a floating point comparison. | ||
FFlags, | ||
} | ||
|
||
impl Flag { | ||
/// Get the number of a flag variant. | ||
pub fn number(self) -> u8 { | ||
match self { | ||
Flag::IFlags => 1, | ||
Flag::FFlags => 2, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. IFlags and FFlags are 0 and 1 below. Why are they 1 and 2 here? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The pub const IFLAGS: Type = Type(0x1); I'm not totally content with this approach to be honest, and had been thinking about moving these up into the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah. I didn't notice this before, but a lot of this logic is in cdsl/types.py in the Python version, so yeah, that makes sense. |
||
} | ||
} | ||
} | ||
|
||
/// Iterator through the variants of the Flag enum. | ||
pub struct FlagIterator { | ||
index: u8, | ||
} | ||
|
||
impl FlagIterator { | ||
pub fn new() -> Self { | ||
Self { index: 0 } | ||
} | ||
} | ||
|
||
impl Iterator for FlagIterator { | ||
type Item = Flag; | ||
fn next(&mut self) -> Option<Self::Item> { | ||
let res = match self.index { | ||
0 => Some(Flag::IFlags), | ||
1 => Some(Flag::FFlags), | ||
_ => return None, | ||
}; | ||
self.index += 1; | ||
res | ||
} | ||
} | ||
|
||
#[cfg(test)] | ||
mod iter_tests { | ||
use super::*; | ||
|
||
#[test] | ||
fn bool_iter_works() { | ||
let mut bool_iter = BoolIterator::new(); | ||
assert_eq!(bool_iter.next(), Some(Bool::B1)); | ||
assert_eq!(bool_iter.next(), Some(Bool::B8)); | ||
assert_eq!(bool_iter.next(), Some(Bool::B16)); | ||
assert_eq!(bool_iter.next(), Some(Bool::B32)); | ||
assert_eq!(bool_iter.next(), Some(Bool::B64)); | ||
assert_eq!(bool_iter.next(), None); | ||
} | ||
|
||
#[test] | ||
fn int_iter_works() { | ||
let mut int_iter = IntIterator::new(); | ||
assert_eq!(int_iter.next(), Some(Int::I8)); | ||
assert_eq!(int_iter.next(), Some(Int::I16)); | ||
assert_eq!(int_iter.next(), Some(Int::I32)); | ||
assert_eq!(int_iter.next(), Some(Int::I64)); | ||
assert_eq!(int_iter.next(), None); | ||
} | ||
|
||
#[test] | ||
fn float_iter_works() { | ||
let mut float_iter = FloatIterator::new(); | ||
assert_eq!(float_iter.next(), Some(Float::F32)); | ||
assert_eq!(float_iter.next(), Some(Float::F64)); | ||
assert_eq!(float_iter.next(), None); | ||
} | ||
|
||
#[test] | ||
fn flag_iter_works() { | ||
let mut flag_iter = FlagIterator::new(); | ||
assert_eq!(flag_iter.next(), Some(Flag::IFlags)); | ||
assert_eq!(flag_iter.next(), Some(Flag::FFlags)); | ||
assert_eq!(flag_iter.next(), None); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
//! Cranelift DSL classes. | ||
//! | ||
//! This module defines the classes that are used to define Cranelift | ||
//! instructions and other entitties. | ||
|
||
pub mod types; | ||
|
||
/// Convert the string `s` to CamelCase. | ||
fn _camel_case(s: &str) -> String { | ||
let mut output_chars = String::with_capacity(s.len()); | ||
|
||
let mut capitalize = true; | ||
for curr_char in s.chars() { | ||
if curr_char == '_' { | ||
capitalize = true; | ||
} else { | ||
if capitalize { | ||
output_chars.extend(curr_char.to_uppercase()); | ||
} else { | ||
output_chars.push(curr_char); | ||
} | ||
capitalize = false; | ||
} | ||
} | ||
|
||
output_chars | ||
} | ||
|
||
/// Check if `x` is a power of two. | ||
fn _is_power_of_two(x: u8) -> bool { | ||
x > 0 && x & (x - 1) == 0 | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I know the Python code has this, but for Rust we should use the standard library's There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In that case, should I just go ahead and remove both this and the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes. |
||
|
||
/// Compute the next power of two that is greater than `x`. | ||
fn _next_power_of_two(x: u8) -> u8 { | ||
let mut s = 1; | ||
let mut res = x; | ||
while res & (res + 1) != 0 { | ||
res |= res >> s; | ||
s *= 2; | ||
} | ||
|
||
res + 1 | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same for |
||
|
||
#[cfg(test)] | ||
mod tests { | ||
use super::_camel_case as camel_case; | ||
use super::_is_power_of_two as is_power_of_two; | ||
use super::_next_power_of_two as next_power_of_two; | ||
|
||
#[test] | ||
fn camel_case_works() { | ||
assert_eq!(camel_case("x"), "X"); | ||
assert_eq!(camel_case("camel_case"), "CamelCase"); | ||
} | ||
|
||
#[test] | ||
fn is_power_of_two_works() { | ||
assert_eq!(is_power_of_two(1), true); | ||
assert_eq!(is_power_of_two(2), true); | ||
assert_eq!(is_power_of_two(4), true); | ||
assert_eq!(is_power_of_two(8), true); | ||
|
||
assert_eq!(is_power_of_two(3), false); | ||
assert_eq!(is_power_of_two(7), false); | ||
} | ||
|
||
#[test] | ||
fn next_power_of_two_works() { | ||
assert_eq!(next_power_of_two(0), 1); | ||
assert_eq!(next_power_of_two(1), 2); | ||
assert_eq!(next_power_of_two(2), 4); | ||
assert_eq!(next_power_of_two(3), 4); | ||
assert_eq!(next_power_of_two(4), 8); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As a minor point, I'm thinking I'd like to move away from "DSL" terminology in general. It feels like part of the problem with the Python code is the DSL approach, which leans a little toward being its own specialized world and a little away from being idiomatic Python code. This makes working within the system more streamlined, but it also makes debugging the system and changing how the system works harder.
So how about "Metaprogram for cranelift-codegen code generator library"?