moved crates in lib/ to src/, renamed crates, modified some files' text (#660)
moved crates in lib/ to src/, renamed crates, modified some files' text (#660)
This commit is contained in:
4
cranelift/codegen/meta/src/base/mod.rs
Normal file
4
cranelift/codegen/meta/src/base/mod.rs
Normal file
@@ -0,0 +1,4 @@
|
||||
//! Definitions for the base Cranelift language.
|
||||
|
||||
pub mod settings;
|
||||
pub mod types;
|
||||
164
cranelift/codegen/meta/src/base/settings.rs
Normal file
164
cranelift/codegen/meta/src/base/settings.rs
Normal file
@@ -0,0 +1,164 @@
|
||||
use crate::cdsl::settings::{SettingGroup, SettingGroupBuilder};
|
||||
|
||||
pub fn generate() -> SettingGroup {
|
||||
let mut settings = SettingGroupBuilder::new("shared");
|
||||
|
||||
settings.add_enum(
|
||||
"opt_level",
|
||||
r#"
|
||||
Optimization level:
|
||||
|
||||
- default: Very profitable optimizations enabled, none slow.
|
||||
- best: Enable all optimizations
|
||||
- fastest: Optimize for compile time by disabling most optimizations.
|
||||
"#,
|
||||
vec!["default", "best", "fastest"],
|
||||
);
|
||||
|
||||
settings.add_bool(
|
||||
"enable_verifier",
|
||||
r#"
|
||||
Run the Cranelift IR verifier at strategic times during compilation.
|
||||
|
||||
This makes compilation slower but catches many bugs. The verifier is
|
||||
disabled by default, except when reading Cranelift IR from a text file.
|
||||
"#,
|
||||
true,
|
||||
);
|
||||
|
||||
// Note that Cranelift doesn't currently need an is_pie flag, because PIE is
|
||||
// just PIC where symbols can't be pre-empted, which can be expressed with the
|
||||
// `colocated` flag on external functions and global values.
|
||||
settings.add_bool(
|
||||
"is_pic",
|
||||
"Enable Position-Independent Code generation",
|
||||
false,
|
||||
);
|
||||
|
||||
settings.add_bool(
|
||||
"colocated_libcalls",
|
||||
r#"
|
||||
Use colocated libcalls.
|
||||
|
||||
Generate code that assumes that libcalls can be declared "colocated",
|
||||
meaning they will be defined along with the current function, such that
|
||||
they can use more efficient addressing.
|
||||
"#,
|
||||
false,
|
||||
);
|
||||
|
||||
settings.add_bool(
|
||||
"avoid_div_traps",
|
||||
r#"
|
||||
Generate explicit checks around native division instructions to avoid
|
||||
their trapping.
|
||||
|
||||
This is primarily used by SpiderMonkey which doesn't install a signal
|
||||
handler for SIGFPE, but expects a SIGILL trap for division by zero.
|
||||
|
||||
On ISAs like ARM where the native division instructions don't trap,
|
||||
this setting has no effect - explicit checks are always inserted.
|
||||
"#,
|
||||
false,
|
||||
);
|
||||
|
||||
settings.add_bool(
|
||||
"enable_float",
|
||||
r#"
|
||||
Enable the use of floating-point instructions
|
||||
|
||||
Disabling use of floating-point instructions is not yet implemented.
|
||||
"#,
|
||||
true,
|
||||
);
|
||||
|
||||
settings.add_bool(
|
||||
"enable_nan_canonicalization",
|
||||
r#"
|
||||
Enable NaN canonicalization
|
||||
|
||||
This replaces NaNs with a single canonical value, for users requiring
|
||||
entirely deterministic WebAssembly computation. This is not required
|
||||
by the WebAssembly spec, so it is not enabled by default.
|
||||
"#,
|
||||
false,
|
||||
);
|
||||
|
||||
settings.add_bool("enable_simd", "Enable the use of SIMD instructions.", true);
|
||||
|
||||
settings.add_bool(
|
||||
"enable_atomics",
|
||||
"Enable the use of atomic instructions",
|
||||
true,
|
||||
);
|
||||
|
||||
// Settings specific to the `baldrdash` calling convention.
|
||||
|
||||
settings.add_num(
|
||||
"baldrdash_prologue_words",
|
||||
r#"
|
||||
Number of pointer-sized words pushed by the baldrdash prologue.
|
||||
|
||||
Functions with the `baldrdash` calling convention don't generate their
|
||||
own prologue and epilogue. They depend on externally generated code
|
||||
that pushes a fixed number of words in the prologue and restores them
|
||||
in the epilogue.
|
||||
|
||||
This setting configures the number of pointer-sized words pushed on the
|
||||
stack when the Cranelift-generated code is entered. This includes the
|
||||
pushed return address on x86.
|
||||
"#,
|
||||
0,
|
||||
);
|
||||
|
||||
// BaldrMonkey requires that not-yet-relocated function addresses be encoded
|
||||
// as all-ones bitpatterns.
|
||||
settings.add_bool(
|
||||
"allones_funcaddrs",
|
||||
"Emit not-yet-relocated function addresses as all-ones bit patterns.",
|
||||
false,
|
||||
);
|
||||
|
||||
// Stack probing options.
|
||||
|
||||
settings.add_bool(
|
||||
"probestack_enabled",
|
||||
r#"
|
||||
Enable the use of stack probes, for calling conventions which support this
|
||||
functionality.
|
||||
"#,
|
||||
true,
|
||||
);
|
||||
|
||||
settings.add_bool(
|
||||
"probestack_func_adjusts_sp",
|
||||
r#"
|
||||
Set this to true of the stack probe function modifies the stack pointer
|
||||
itself.
|
||||
"#,
|
||||
false,
|
||||
);
|
||||
|
||||
settings.add_num(
|
||||
"probestack_size_log2",
|
||||
r#"
|
||||
The log2 of the size of the stack guard region.
|
||||
|
||||
Stack frames larger than this size will have stack overflow checked
|
||||
by calling the probestack function.
|
||||
|
||||
The default is 12, which translates to a size of 4096.
|
||||
"#,
|
||||
12,
|
||||
);
|
||||
|
||||
// Jump table options.
|
||||
|
||||
settings.add_bool(
|
||||
"jump_tables_enabled",
|
||||
"Enable the use of jump tables in generated machine code.",
|
||||
true,
|
||||
);
|
||||
|
||||
settings.finish()
|
||||
}
|
||||
188
cranelift/codegen/meta/src/base/types.rs
Normal file
188
cranelift/codegen/meta/src/base/types.rs
Normal file
@@ -0,0 +1,188 @@
|
||||
//! This module predefines all the Cranelift scalar types.
|
||||
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
|
||||
pub enum Bool {
|
||||
/// 1-bit bool.
|
||||
B1 = 1,
|
||||
/// 8-bit bool.
|
||||
B8 = 8,
|
||||
/// 16-bit bool.
|
||||
B16 = 16,
|
||||
/// 32-bit bool.
|
||||
B32 = 32,
|
||||
/// 64-bit bool.
|
||||
B64 = 64,
|
||||
}
|
||||
|
||||
/// This provides an iterator through all of the supported bool variants.
|
||||
pub struct BoolIterator {
|
||||
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,
|
||||
}
|
||||
|
||||
/// This provides an iterator through all of the supported int variants.
|
||||
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;
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
|
||||
pub enum Float {
|
||||
F32 = 32,
|
||||
F64 = 64,
|
||||
}
|
||||
|
||||
/// Iterator through the variants of the Float enum.
|
||||
pub struct FloatIterator {
|
||||
index: u8,
|
||||
}
|
||||
|
||||
impl FloatIterator {
|
||||
pub fn new() -> Self {
|
||||
Self { index: 0 }
|
||||
}
|
||||
}
|
||||
|
||||
/// This provides an iterator through all of the supported float variants.
|
||||
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,
|
||||
}
|
||||
|
||||
/// 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);
|
||||
}
|
||||
}
|
||||
192
cranelift/codegen/meta/src/cdsl/isa.rs
Normal file
192
cranelift/codegen/meta/src/cdsl/isa.rs
Normal file
@@ -0,0 +1,192 @@
|
||||
use cranelift_entity::PrimaryMap;
|
||||
|
||||
use super::regs::{
|
||||
RegBank, RegBankBuilder, RegBankIndex, RegClass, RegClassBuilder, RegClassIndex, RegClassProto,
|
||||
};
|
||||
use super::settings::SettingGroup;
|
||||
|
||||
pub struct TargetIsa {
|
||||
pub name: &'static str,
|
||||
pub reg_banks: PrimaryMap<RegBankIndex, RegBank>,
|
||||
pub reg_classes: PrimaryMap<RegClassIndex, RegClass>,
|
||||
pub settings: SettingGroup,
|
||||
}
|
||||
|
||||
impl TargetIsa {
|
||||
pub fn new(name: &'static str, settings: SettingGroup) -> Self {
|
||||
Self {
|
||||
name,
|
||||
reg_banks: PrimaryMap::new(),
|
||||
reg_classes: PrimaryMap::new(),
|
||||
settings,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TargetIsaBuilder {
|
||||
isa: TargetIsa,
|
||||
}
|
||||
|
||||
impl TargetIsaBuilder {
|
||||
pub fn new(name: &'static str, settings: SettingGroup) -> Self {
|
||||
Self {
|
||||
isa: TargetIsa::new(name, settings),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_reg_bank(&mut self, builder: RegBankBuilder) -> RegBankIndex {
|
||||
let first_unit = if self.isa.reg_banks.len() == 0 {
|
||||
0
|
||||
} else {
|
||||
let last = &self.isa.reg_banks.last().unwrap();
|
||||
let first_available_unit = (last.first_unit + last.units) as i8;
|
||||
let units = builder.units;
|
||||
let align = if units.is_power_of_two() {
|
||||
units
|
||||
} else {
|
||||
units.next_power_of_two()
|
||||
} as i8;
|
||||
(first_available_unit + align - 1) & -align
|
||||
} as u8;
|
||||
|
||||
self.isa.reg_banks.push(RegBank::new(
|
||||
builder.name,
|
||||
first_unit,
|
||||
builder.units,
|
||||
builder.names,
|
||||
builder.prefix,
|
||||
builder
|
||||
.pressure_tracking
|
||||
.expect("Pressure tracking must be explicitly set"),
|
||||
))
|
||||
}
|
||||
|
||||
pub fn add_reg_class(&mut self, builder: RegClassBuilder) -> RegClassIndex {
|
||||
let class_index = self.isa.reg_classes.next_key();
|
||||
|
||||
// Finish delayed construction of RegClass.
|
||||
let (bank, toprc, start, width) = match builder.proto {
|
||||
RegClassProto::TopLevel(bank_index) => {
|
||||
self.isa
|
||||
.reg_banks
|
||||
.get_mut(bank_index)
|
||||
.unwrap()
|
||||
.toprcs
|
||||
.push(class_index);
|
||||
(bank_index, class_index, builder.start, builder.width)
|
||||
}
|
||||
RegClassProto::SubClass(parent_class_index) => {
|
||||
assert!(builder.width == 0);
|
||||
let (bank, toprc, start, width) = {
|
||||
let parent = self.isa.reg_classes.get(parent_class_index).unwrap();
|
||||
(parent.bank, parent.toprc, parent.start, parent.width)
|
||||
};
|
||||
for reg_class in self.isa.reg_classes.values_mut() {
|
||||
if reg_class.toprc == toprc {
|
||||
reg_class.subclasses.push(class_index);
|
||||
}
|
||||
}
|
||||
let subclass_start = start + builder.start * width;
|
||||
(bank, toprc, subclass_start, width)
|
||||
}
|
||||
};
|
||||
|
||||
let reg_bank_units = self.isa.reg_banks.get(bank).unwrap().units;
|
||||
assert!(start < reg_bank_units);
|
||||
|
||||
let count = if builder.count != 0 {
|
||||
builder.count
|
||||
} else {
|
||||
reg_bank_units / width
|
||||
};
|
||||
|
||||
let reg_class = RegClass::new(builder.name, class_index, width, bank, toprc, count, start);
|
||||
self.isa.reg_classes.push(reg_class);
|
||||
|
||||
let reg_bank = self.isa.reg_banks.get_mut(bank).unwrap();
|
||||
reg_bank.classes.push(class_index);
|
||||
|
||||
class_index
|
||||
}
|
||||
|
||||
/// Checks that the set of register classes satisfies:
|
||||
///
|
||||
/// 1. Closed under intersection: The intersection of any two register
|
||||
/// classes in the set is either empty or identical to a member of the
|
||||
/// set.
|
||||
/// 2. There are no identical classes under different names.
|
||||
/// 3. Classes are sorted topologically such that all subclasses have a
|
||||
/// higher index that the superclass.
|
||||
pub fn finish(self) -> TargetIsa {
|
||||
for reg_bank in self.isa.reg_banks.values() {
|
||||
for i1 in reg_bank.classes.iter() {
|
||||
for i2 in reg_bank.classes.iter() {
|
||||
if i1 >= i2 {
|
||||
continue;
|
||||
}
|
||||
|
||||
let rc1 = self.isa.reg_classes.get(*i1).unwrap();
|
||||
let rc2 = self.isa.reg_classes.get(*i2).unwrap();
|
||||
|
||||
let rc1_mask = rc1.mask(0);
|
||||
let rc2_mask = rc2.mask(0);
|
||||
|
||||
assert!(
|
||||
rc1.width != rc2.width || rc1_mask != rc2_mask,
|
||||
"no duplicates"
|
||||
);
|
||||
if rc1.width != rc2.width {
|
||||
continue;
|
||||
}
|
||||
|
||||
let mut intersect = Vec::new();
|
||||
for (a, b) in rc1_mask.iter().zip(rc2_mask.iter()) {
|
||||
intersect.push(a & b);
|
||||
}
|
||||
if intersect == vec![0; intersect.len()] {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Classes must be topologically ordered, so the intersection can't be the
|
||||
// superclass.
|
||||
assert!(intersect != rc1_mask);
|
||||
|
||||
// If the intersection is the second one, then it must be a subclass.
|
||||
if intersect == rc2_mask {
|
||||
assert!(self
|
||||
.isa
|
||||
.reg_classes
|
||||
.get(*i1)
|
||||
.unwrap()
|
||||
.subclasses
|
||||
.iter()
|
||||
.find(|x| **x == *i2)
|
||||
.is_some());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This limit should be coordinated with the `RegClassMask` and `RegClassIndex` types in
|
||||
// isa/registers.rs of the non-meta code.
|
||||
assert!(
|
||||
self.isa.reg_classes.len() <= 32,
|
||||
"Too many register classes"
|
||||
);
|
||||
|
||||
// The maximum number of top-level register classes which have pressure tracking should be
|
||||
// kept in sync with the MAX_TRACKED_TOPRCS constant in isa/registers.rs of the non-meta
|
||||
// code.
|
||||
let num_toplevel = self
|
||||
.isa
|
||||
.reg_classes
|
||||
.values()
|
||||
.filter(|x| {
|
||||
x.toprc == x.index && self.isa.reg_banks.get(x.bank).unwrap().pressure_tracking
|
||||
})
|
||||
.count();
|
||||
assert!(num_toplevel <= 4, "Too many top-level register classes");
|
||||
|
||||
self.isa
|
||||
}
|
||||
}
|
||||
68
cranelift/codegen/meta/src/cdsl/mod.rs
Normal file
68
cranelift/codegen/meta/src/cdsl/mod.rs
Normal file
@@ -0,0 +1,68 @@
|
||||
//! Cranelift DSL classes.
|
||||
//!
|
||||
//! This module defines the classes that are used to define Cranelift
|
||||
//! instructions and other entities.
|
||||
|
||||
pub mod isa;
|
||||
pub mod regs;
|
||||
pub mod settings;
|
||||
pub mod types;
|
||||
|
||||
/// A macro that converts boolean settings into predicates to look more natural.
|
||||
#[macro_export]
|
||||
macro_rules! predicate {
|
||||
($a:ident && $($b:tt)*) => {
|
||||
PredicateNode::And(Box::new($a.into()), Box::new(predicate!($($b)*)))
|
||||
};
|
||||
($a:ident) => {
|
||||
$a.into()
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! preset {
|
||||
() => {
|
||||
vec![]
|
||||
};
|
||||
($($x:ident)&&*) => {
|
||||
{
|
||||
let mut v = Vec::new();
|
||||
$(
|
||||
v.push($x.into());
|
||||
)*
|
||||
v
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// Convert the string `s` to CamelCase.
|
||||
pub 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
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::camel_case;
|
||||
|
||||
#[test]
|
||||
fn camel_case_works() {
|
||||
assert_eq!(camel_case("x"), "X");
|
||||
assert_eq!(camel_case("camel_case"), "CamelCase");
|
||||
}
|
||||
}
|
||||
180
cranelift/codegen/meta/src/cdsl/regs.rs
Normal file
180
cranelift/codegen/meta/src/cdsl/regs.rs
Normal file
@@ -0,0 +1,180 @@
|
||||
use cranelift_entity::entity_impl;
|
||||
use cranelift_entity::EntityRef;
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||
pub struct RegBankIndex(u32);
|
||||
entity_impl!(RegBankIndex);
|
||||
|
||||
pub struct RegBank {
|
||||
pub name: &'static str,
|
||||
pub first_unit: u8,
|
||||
pub units: u8,
|
||||
pub names: Vec<&'static str>,
|
||||
pub prefix: &'static str,
|
||||
pub pressure_tracking: bool,
|
||||
pub toprcs: Vec<RegClassIndex>,
|
||||
pub classes: Vec<RegClassIndex>,
|
||||
}
|
||||
|
||||
impl RegBank {
|
||||
pub fn new(
|
||||
name: &'static str,
|
||||
first_unit: u8,
|
||||
units: u8,
|
||||
names: Vec<&'static str>,
|
||||
prefix: &'static str,
|
||||
pressure_tracking: bool,
|
||||
) -> Self {
|
||||
RegBank {
|
||||
name,
|
||||
first_unit,
|
||||
units,
|
||||
names,
|
||||
prefix,
|
||||
pressure_tracking,
|
||||
toprcs: Vec::new(),
|
||||
classes: Vec::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||
pub struct RegClassIndex(u32);
|
||||
entity_impl!(RegClassIndex);
|
||||
|
||||
pub struct RegClass {
|
||||
pub name: &'static str,
|
||||
pub index: RegClassIndex,
|
||||
pub width: u8,
|
||||
pub bank: RegBankIndex,
|
||||
pub toprc: RegClassIndex,
|
||||
pub count: u8,
|
||||
pub start: u8,
|
||||
pub subclasses: Vec<RegClassIndex>,
|
||||
}
|
||||
|
||||
impl RegClass {
|
||||
pub fn new(
|
||||
name: &'static str,
|
||||
index: RegClassIndex,
|
||||
width: u8,
|
||||
bank: RegBankIndex,
|
||||
toprc: RegClassIndex,
|
||||
count: u8,
|
||||
start: u8,
|
||||
) -> Self {
|
||||
Self {
|
||||
name,
|
||||
index,
|
||||
width,
|
||||
bank,
|
||||
toprc,
|
||||
count,
|
||||
start,
|
||||
subclasses: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Compute a bit-mask of subclasses, including self.
|
||||
pub fn subclass_mask(&self) -> u64 {
|
||||
let mut m = 1 << self.index.index();
|
||||
for rc in self.subclasses.iter() {
|
||||
m |= 1 << rc.index();
|
||||
}
|
||||
m
|
||||
}
|
||||
|
||||
/// Compute a bit-mask of the register units allocated by this register class.
|
||||
pub fn mask(&self, bank_first_unit: u8) -> Vec<u32> {
|
||||
let mut u = (self.start + bank_first_unit) as usize;
|
||||
let mut out_mask = vec![0, 0, 0];
|
||||
for _ in 0..self.count {
|
||||
out_mask[u / 32] |= 1 << (u % 32);
|
||||
u += self.width as usize;
|
||||
}
|
||||
out_mask
|
||||
}
|
||||
}
|
||||
|
||||
pub enum RegClassProto {
|
||||
TopLevel(RegBankIndex),
|
||||
SubClass(RegClassIndex),
|
||||
}
|
||||
|
||||
pub struct RegClassBuilder {
|
||||
pub name: &'static str,
|
||||
pub width: u8,
|
||||
pub count: u8,
|
||||
pub start: u8,
|
||||
pub proto: RegClassProto,
|
||||
}
|
||||
|
||||
impl RegClassBuilder {
|
||||
pub fn new_toplevel(name: &'static str, bank: RegBankIndex) -> Self {
|
||||
Self {
|
||||
name,
|
||||
width: 1,
|
||||
count: 0,
|
||||
start: 0,
|
||||
proto: RegClassProto::TopLevel(bank),
|
||||
}
|
||||
}
|
||||
pub fn subclass_of(
|
||||
name: &'static str,
|
||||
parent_index: RegClassIndex,
|
||||
start: u8,
|
||||
stop: u8,
|
||||
) -> Self {
|
||||
assert!(stop >= start);
|
||||
Self {
|
||||
name,
|
||||
width: 0,
|
||||
count: stop - start,
|
||||
start: start,
|
||||
proto: RegClassProto::SubClass(parent_index),
|
||||
}
|
||||
}
|
||||
pub fn count(mut self, count: u8) -> Self {
|
||||
self.count = count;
|
||||
self
|
||||
}
|
||||
pub fn width(mut self, width: u8) -> Self {
|
||||
match self.proto {
|
||||
RegClassProto::TopLevel(_) => self.width = width,
|
||||
RegClassProto::SubClass(_) => panic!("Subclasses inherit their parent's width."),
|
||||
}
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
pub struct RegBankBuilder {
|
||||
pub name: &'static str,
|
||||
pub units: u8,
|
||||
pub names: Vec<&'static str>,
|
||||
pub prefix: &'static str,
|
||||
pub pressure_tracking: Option<bool>,
|
||||
}
|
||||
|
||||
impl RegBankBuilder {
|
||||
pub fn new(name: &'static str, prefix: &'static str) -> Self {
|
||||
Self {
|
||||
name,
|
||||
units: 0,
|
||||
names: vec![],
|
||||
prefix,
|
||||
pressure_tracking: None,
|
||||
}
|
||||
}
|
||||
pub fn units(mut self, units: u8) -> Self {
|
||||
self.units = units;
|
||||
self
|
||||
}
|
||||
pub fn names(mut self, names: Vec<&'static str>) -> Self {
|
||||
self.names = names;
|
||||
self
|
||||
}
|
||||
pub fn track_pressure(mut self, track: bool) -> Self {
|
||||
self.pressure_tracking = Some(track);
|
||||
self
|
||||
}
|
||||
}
|
||||
387
cranelift/codegen/meta/src/cdsl/settings.rs
Normal file
387
cranelift/codegen/meta/src/cdsl/settings.rs
Normal file
@@ -0,0 +1,387 @@
|
||||
use std::iter;
|
||||
|
||||
#[derive(Clone, Copy, Hash, PartialEq, Eq)]
|
||||
pub struct BoolSettingIndex(usize);
|
||||
|
||||
#[derive(Hash, PartialEq, Eq)]
|
||||
pub struct BoolSetting {
|
||||
pub default: bool,
|
||||
pub bit_offset: u8,
|
||||
pub predicate_number: u8,
|
||||
}
|
||||
|
||||
#[derive(Hash, PartialEq, Eq)]
|
||||
pub enum SpecificSetting {
|
||||
Bool(BoolSetting),
|
||||
Enum(Vec<&'static str>),
|
||||
Num(u8),
|
||||
}
|
||||
|
||||
#[derive(Hash, PartialEq, Eq)]
|
||||
pub struct Setting {
|
||||
pub name: &'static str,
|
||||
pub comment: &'static str,
|
||||
pub specific: SpecificSetting,
|
||||
pub byte_offset: u8,
|
||||
}
|
||||
|
||||
impl Setting {
|
||||
pub fn default_byte(&self) -> u8 {
|
||||
match self.specific {
|
||||
SpecificSetting::Bool(BoolSetting {
|
||||
default,
|
||||
bit_offset,
|
||||
..
|
||||
}) => {
|
||||
if default {
|
||||
1 << bit_offset
|
||||
} else {
|
||||
0
|
||||
}
|
||||
}
|
||||
SpecificSetting::Enum(_) => 0,
|
||||
SpecificSetting::Num(default) => default,
|
||||
}
|
||||
}
|
||||
|
||||
fn byte_for_value(&self, v: bool) -> u8 {
|
||||
match self.specific {
|
||||
SpecificSetting::Bool(BoolSetting { bit_offset, .. }) => {
|
||||
if v {
|
||||
1 << bit_offset
|
||||
} else {
|
||||
0
|
||||
}
|
||||
}
|
||||
_ => panic!("byte_for_value shouldn't be used for non-boolean settings."),
|
||||
}
|
||||
}
|
||||
|
||||
fn byte_mask(&self) -> u8 {
|
||||
match self.specific {
|
||||
SpecificSetting::Bool(BoolSetting { bit_offset, .. }) => 1 << bit_offset,
|
||||
_ => panic!("byte_for_value shouldn't be used for non-boolean settings."),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Hash, PartialEq, Eq)]
|
||||
pub struct PresetIndex(usize);
|
||||
|
||||
#[derive(Hash, PartialEq, Eq)]
|
||||
pub enum PresetType {
|
||||
BoolSetting(BoolSettingIndex),
|
||||
OtherPreset(PresetIndex),
|
||||
}
|
||||
|
||||
impl Into<PresetType> for BoolSettingIndex {
|
||||
fn into(self) -> PresetType {
|
||||
PresetType::BoolSetting(self)
|
||||
}
|
||||
}
|
||||
impl Into<PresetType> for PresetIndex {
|
||||
fn into(self) -> PresetType {
|
||||
PresetType::OtherPreset(self)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Hash, PartialEq, Eq)]
|
||||
pub struct Preset {
|
||||
pub name: &'static str,
|
||||
values: Vec<BoolSettingIndex>,
|
||||
}
|
||||
|
||||
impl Preset {
|
||||
pub fn layout(&self, group: &SettingGroup) -> Vec<(u8, u8)> {
|
||||
let mut layout: Vec<(u8, u8)> = iter::repeat((0, 0))
|
||||
.take(group.settings_size as usize)
|
||||
.collect();
|
||||
for bool_index in &self.values {
|
||||
let setting = &group.settings[bool_index.0];
|
||||
let mask = setting.byte_mask();
|
||||
let val = setting.byte_for_value(true);
|
||||
assert!((val & !mask) == 0);
|
||||
let (ref mut l_mask, ref mut l_val) =
|
||||
*layout.get_mut(setting.byte_offset as usize).unwrap();
|
||||
*l_mask |= mask;
|
||||
*l_val = (*l_val & !mask) | val;
|
||||
}
|
||||
layout
|
||||
}
|
||||
}
|
||||
|
||||
pub struct SettingGroup {
|
||||
pub name: &'static str,
|
||||
pub settings: Vec<Setting>,
|
||||
pub bool_start_byte_offset: u8,
|
||||
pub settings_size: u8,
|
||||
pub presets: Vec<Preset>,
|
||||
pub predicates: Vec<Predicate>,
|
||||
}
|
||||
|
||||
impl SettingGroup {
|
||||
fn num_bool_settings(&self) -> u8 {
|
||||
self.settings
|
||||
.iter()
|
||||
.filter(|s| {
|
||||
if let SpecificSetting::Bool(_) = s.specific {
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
})
|
||||
.count() as u8
|
||||
}
|
||||
|
||||
pub fn byte_size(&self) -> u8 {
|
||||
let num_predicates = self.num_bool_settings() + (self.predicates.len() as u8);
|
||||
self.bool_start_byte_offset + (num_predicates + 7) / 8
|
||||
}
|
||||
|
||||
pub fn get_bool(&self, name: &'static str) -> (BoolSettingIndex, &Self) {
|
||||
for (i, s) in self.settings.iter().enumerate() {
|
||||
if let SpecificSetting::Bool(_) = s.specific {
|
||||
if s.name == name {
|
||||
return (BoolSettingIndex(i), self);
|
||||
}
|
||||
}
|
||||
}
|
||||
panic!("Should have found bool setting by name.");
|
||||
}
|
||||
}
|
||||
|
||||
/// This is the basic information needed to track the specific parts of a setting when building
|
||||
/// them.
|
||||
pub enum ProtoSpecificSetting {
|
||||
Bool(bool, u8),
|
||||
Enum(Vec<&'static str>),
|
||||
Num(u8),
|
||||
}
|
||||
|
||||
/// This is the information provided during building for a setting.
|
||||
struct ProtoSetting {
|
||||
name: &'static str,
|
||||
comment: &'static str,
|
||||
specific: ProtoSpecificSetting,
|
||||
}
|
||||
|
||||
#[derive(Hash, PartialEq, Eq)]
|
||||
pub enum PredicateNode {
|
||||
OwnedBool(BoolSettingIndex),
|
||||
SharedBool(&'static str, &'static str),
|
||||
And(Box<PredicateNode>, Box<PredicateNode>),
|
||||
}
|
||||
|
||||
impl Into<PredicateNode> for BoolSettingIndex {
|
||||
fn into(self) -> PredicateNode {
|
||||
PredicateNode::OwnedBool(self)
|
||||
}
|
||||
}
|
||||
impl<'a> Into<PredicateNode> for (BoolSettingIndex, &'a SettingGroup) {
|
||||
fn into(self) -> PredicateNode {
|
||||
let (index, group) = (self.0, self.1);
|
||||
let setting = &group.settings[index.0];
|
||||
PredicateNode::SharedBool(group.name, setting.name)
|
||||
}
|
||||
}
|
||||
|
||||
impl PredicateNode {
|
||||
fn render(&self, group: &SettingGroup) -> String {
|
||||
match *self {
|
||||
PredicateNode::OwnedBool(bool_setting_index) => format!(
|
||||
"{}.{}()",
|
||||
group.name, group.settings[bool_setting_index.0].name
|
||||
),
|
||||
PredicateNode::SharedBool(ref group_name, ref bool_name) => {
|
||||
format!("{}.{}()", group_name, bool_name)
|
||||
}
|
||||
PredicateNode::And(ref lhs, ref rhs) => {
|
||||
format!("{} && {}", lhs.render(group), rhs.render(group))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Predicate {
|
||||
pub name: &'static str,
|
||||
node: PredicateNode,
|
||||
pub number: u8,
|
||||
}
|
||||
|
||||
impl Predicate {
|
||||
pub fn render(&self, group: &SettingGroup) -> String {
|
||||
self.node.render(group)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct SettingGroupBuilder {
|
||||
name: &'static str,
|
||||
settings: Vec<ProtoSetting>,
|
||||
presets: Vec<Preset>,
|
||||
predicates: Vec<Predicate>,
|
||||
predicate_number: u8,
|
||||
}
|
||||
|
||||
impl SettingGroupBuilder {
|
||||
pub fn new(name: &'static str) -> Self {
|
||||
Self {
|
||||
name,
|
||||
settings: Vec::new(),
|
||||
presets: Vec::new(),
|
||||
predicates: Vec::new(),
|
||||
predicate_number: 0,
|
||||
}
|
||||
}
|
||||
|
||||
fn add_setting(
|
||||
&mut self,
|
||||
name: &'static str,
|
||||
comment: &'static str,
|
||||
specific: ProtoSpecificSetting,
|
||||
) {
|
||||
self.settings.push(ProtoSetting {
|
||||
name,
|
||||
comment,
|
||||
specific,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn add_bool(
|
||||
&mut self,
|
||||
name: &'static str,
|
||||
comment: &'static str,
|
||||
default: bool,
|
||||
) -> BoolSettingIndex {
|
||||
assert!(
|
||||
self.predicates.len() == 0,
|
||||
"predicates must be added after the boolean settings"
|
||||
);
|
||||
let predicate_number = self.predicate_number;
|
||||
self.predicate_number += 1;
|
||||
self.add_setting(
|
||||
name,
|
||||
comment,
|
||||
ProtoSpecificSetting::Bool(default, predicate_number),
|
||||
);
|
||||
BoolSettingIndex(self.settings.len() - 1)
|
||||
}
|
||||
|
||||
pub fn add_enum(
|
||||
&mut self,
|
||||
name: &'static str,
|
||||
comment: &'static str,
|
||||
values: Vec<&'static str>,
|
||||
) {
|
||||
self.add_setting(name, comment, ProtoSpecificSetting::Enum(values));
|
||||
}
|
||||
|
||||
pub fn add_num(&mut self, name: &'static str, comment: &'static str, default: u8) {
|
||||
self.add_setting(name, comment, ProtoSpecificSetting::Num(default));
|
||||
}
|
||||
|
||||
pub fn add_predicate(&mut self, name: &'static str, node: PredicateNode) {
|
||||
let number = self.predicate_number;
|
||||
self.predicate_number += 1;
|
||||
self.predicates.push(Predicate { name, node, number });
|
||||
}
|
||||
|
||||
pub fn add_preset(&mut self, name: &'static str, args: Vec<PresetType>) -> PresetIndex {
|
||||
let mut values = Vec::new();
|
||||
for arg in args {
|
||||
match arg {
|
||||
PresetType::OtherPreset(index) => {
|
||||
values.extend(self.presets[index.0].values.iter());
|
||||
}
|
||||
PresetType::BoolSetting(index) => values.push(index),
|
||||
}
|
||||
}
|
||||
self.presets.push(Preset { name, values });
|
||||
PresetIndex(self.presets.len() - 1)
|
||||
}
|
||||
|
||||
/// Compute the layout of the byte vector used to represent this settings
|
||||
/// group.
|
||||
///
|
||||
/// The byte vector contains the following entries in order:
|
||||
///
|
||||
/// 1. Byte-sized settings like `NumSetting` and `EnumSetting`.
|
||||
/// 2. `BoolSetting` settings.
|
||||
/// 3. Precomputed named predicates.
|
||||
/// 4. Other numbered predicates, including anonymous predicates and parent
|
||||
/// predicates that need to be accessible by number.
|
||||
///
|
||||
/// Set `self.settings_size` to the length of the byte vector prefix that
|
||||
/// contains the settings. All bytes after that are computed, not
|
||||
/// configured.
|
||||
///
|
||||
/// Set `self.boolean_offset` to the beginning of the numbered predicates,
|
||||
/// 2. in the list above.
|
||||
///
|
||||
/// Assign `byte_offset` and `bit_offset` fields in all settings.
|
||||
///
|
||||
/// After calling this method, no more settings can be added, but
|
||||
/// additional predicates can be made accessible with `number_predicate()`.
|
||||
pub fn finish(self) -> SettingGroup {
|
||||
let mut group = SettingGroup {
|
||||
name: self.name,
|
||||
settings: Vec::new(),
|
||||
bool_start_byte_offset: 0,
|
||||
settings_size: 0,
|
||||
presets: Vec::new(),
|
||||
predicates: Vec::new(),
|
||||
};
|
||||
|
||||
let mut byte_offset = 0;
|
||||
|
||||
// Assign the non-boolean settings first.
|
||||
for s in &self.settings {
|
||||
let specific = match s.specific {
|
||||
ProtoSpecificSetting::Bool(..) => continue,
|
||||
ProtoSpecificSetting::Enum(ref values) => SpecificSetting::Enum(values.clone()),
|
||||
ProtoSpecificSetting::Num(default) => SpecificSetting::Num(default),
|
||||
};
|
||||
|
||||
group.settings.push(Setting {
|
||||
name: s.name,
|
||||
comment: s.comment,
|
||||
byte_offset,
|
||||
specific,
|
||||
});
|
||||
|
||||
byte_offset += 1;
|
||||
}
|
||||
|
||||
group.bool_start_byte_offset = byte_offset;
|
||||
|
||||
// Then the boolean settings.
|
||||
for s in &self.settings {
|
||||
let (default, predicate_number) = match s.specific {
|
||||
ProtoSpecificSetting::Bool(default, predicate_number) => {
|
||||
(default, predicate_number)
|
||||
}
|
||||
ProtoSpecificSetting::Enum(_) | ProtoSpecificSetting::Num(_) => continue,
|
||||
};
|
||||
group.settings.push(Setting {
|
||||
name: s.name,
|
||||
comment: s.comment,
|
||||
byte_offset: byte_offset + predicate_number / 8,
|
||||
specific: SpecificSetting::Bool(BoolSetting {
|
||||
default,
|
||||
bit_offset: predicate_number % 8,
|
||||
predicate_number,
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
assert!(
|
||||
group.predicates.len() == 0,
|
||||
"settings_size is the byte size before adding predicates"
|
||||
);
|
||||
group.settings_size = group.byte_size();
|
||||
|
||||
group.predicates.extend(self.predicates);
|
||||
group.presets.extend(self.presets);
|
||||
|
||||
group
|
||||
}
|
||||
}
|
||||
473
cranelift/codegen/meta/src/cdsl/types.rs
Normal file
473
cranelift/codegen/meta/src/cdsl/types.rs
Normal file
@@ -0,0 +1,473 @@
|
||||
//! Cranelift ValueType hierarchy
|
||||
|
||||
// Temporary disabled: Unused at the moment.
|
||||
// use std::collections::HashMap;
|
||||
|
||||
use std::fmt;
|
||||
|
||||
use crate::base::types as base_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;
|
||||
|
||||
// Rust name prefix used for the `rust_name` method.
|
||||
static _RUST_NAME_PREFIX: &'static str = "ir::types::";
|
||||
|
||||
// ValueType variants (i8, i32, ...) are provided in `base::types.rs`.
|
||||
|
||||
/// A concrete SSA value type.
|
||||
///
|
||||
/// All SSA values have a type that is described by an instance of `ValueType`
|
||||
/// or one of its subclasses.
|
||||
#[derive(Debug)]
|
||||
pub enum ValueType {
|
||||
BV(BVType),
|
||||
Lane(LaneType),
|
||||
Special(SpecialType),
|
||||
Vector(VectorType),
|
||||
}
|
||||
|
||||
impl ValueType {
|
||||
/// Iterate through all of the lane types.
|
||||
pub fn all_lane_types() -> LaneTypeIterator {
|
||||
LaneTypeIterator::new()
|
||||
}
|
||||
|
||||
/// Iterate through all of the special types (neither lanes nor vectors).
|
||||
pub fn all_special_types() -> SpecialTypeIterator {
|
||||
SpecialTypeIterator::new()
|
||||
}
|
||||
|
||||
/// Return a string containing the documentation comment for this type.
|
||||
pub fn doc(&self) -> String {
|
||||
match *self {
|
||||
ValueType::BV(ref b) => b.doc(),
|
||||
ValueType::Lane(l) => l.doc(),
|
||||
ValueType::Special(s) => s.doc(),
|
||||
ValueType::Vector(ref v) => v.doc(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the number of bits in a lane.
|
||||
pub fn lane_bits(&self) -> u64 {
|
||||
match *self {
|
||||
ValueType::BV(ref b) => b.lane_bits(),
|
||||
ValueType::Lane(l) => l.lane_bits(),
|
||||
ValueType::Special(s) => s.lane_bits(),
|
||||
ValueType::Vector(ref v) => v.lane_bits(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the number of lanes.
|
||||
pub fn lane_count(&self) -> u64 {
|
||||
match *self {
|
||||
ValueType::Vector(ref v) => v.lane_count(),
|
||||
_ => 1,
|
||||
}
|
||||
}
|
||||
|
||||
/// Find the number of bytes that this type occupies in memory.
|
||||
pub fn membytes(&self) -> u64 {
|
||||
self.width() / 8
|
||||
}
|
||||
|
||||
/// Find the unique number associated with this type.
|
||||
pub fn number(&self) -> Option<u8> {
|
||||
match *self {
|
||||
ValueType::BV(_) => None,
|
||||
ValueType::Lane(l) => Some(l.number()),
|
||||
ValueType::Special(s) => Some(s.number()),
|
||||
ValueType::Vector(ref v) => Some(v.number()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the name of this type for generated Rust source files.
|
||||
pub fn _rust_name(&self) -> String {
|
||||
format!("{}{}", _RUST_NAME_PREFIX, self.to_string().to_uppercase())
|
||||
}
|
||||
|
||||
/// Return true iff:
|
||||
/// 1. self and other have equal number of lanes
|
||||
/// 2. each lane in self has at least as many bits as a lane in other
|
||||
pub fn _wider_or_equal(&self, rhs: &ValueType) -> bool {
|
||||
(self.lane_count() == rhs.lane_count()) && (self.lane_bits() >= rhs.lane_bits())
|
||||
}
|
||||
|
||||
/// Return the total number of bits of an instance of this type.
|
||||
pub fn width(&self) -> u64 {
|
||||
self.lane_count() * self.lane_bits()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for ValueType {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match *self {
|
||||
ValueType::BV(ref b) => b.fmt(f),
|
||||
ValueType::Lane(l) => l.fmt(f),
|
||||
ValueType::Special(s) => s.fmt(f),
|
||||
ValueType::Vector(ref v) => v.fmt(f),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a ValueType from a given bitvector type.
|
||||
impl From<BVType> for ValueType {
|
||||
fn from(bv: BVType) -> Self {
|
||||
ValueType::BV(bv)
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a ValueType from a given lane type.
|
||||
impl From<LaneType> for ValueType {
|
||||
fn from(lane: LaneType) -> Self {
|
||||
ValueType::Lane(lane)
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a ValueType from a given special type.
|
||||
impl From<SpecialType> for ValueType {
|
||||
fn from(spec: SpecialType) -> Self {
|
||||
ValueType::Special(spec)
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a ValueType from a given vector type.
|
||||
impl From<VectorType> for ValueType {
|
||||
fn from(vector: VectorType) -> Self {
|
||||
ValueType::Vector(vector)
|
||||
}
|
||||
}
|
||||
|
||||
/// A concrete scalar type that can appear as a vector lane too.
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum LaneType {
|
||||
BoolType(base_types::Bool),
|
||||
FloatType(base_types::Float),
|
||||
IntType(base_types::Int),
|
||||
}
|
||||
|
||||
impl LaneType {
|
||||
/// Return a string containing the documentation comment for this lane type.
|
||||
pub fn doc(self) -> String {
|
||||
match self {
|
||||
LaneType::BoolType(_) => format!("A boolean type with {} bits.", self.lane_bits()),
|
||||
LaneType::FloatType(base_types::Float::F32) => String::from(
|
||||
"A 32-bit floating point type represented in the IEEE 754-2008
|
||||
*binary32* interchange format. This corresponds to the :c:type:`float`
|
||||
type in most C implementations.",
|
||||
),
|
||||
LaneType::FloatType(base_types::Float::F64) => String::from(
|
||||
"A 64-bit floating point type represented in the IEEE 754-2008
|
||||
*binary64* interchange format. This corresponds to the :c:type:`double`
|
||||
type in most C implementations.",
|
||||
),
|
||||
LaneType::IntType(_) if self.lane_bits() < 32 => format!(
|
||||
"An integer type with {} bits.
|
||||
WARNING: arithmetic on {}bit integers is incomplete",
|
||||
self.lane_bits(),
|
||||
self.lane_bits()
|
||||
),
|
||||
LaneType::IntType(_) => format!("An integer type with {} bits.", self.lane_bits()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the number of bits in a lane.
|
||||
pub fn lane_bits(self) -> u64 {
|
||||
match self {
|
||||
LaneType::BoolType(ref b) => *b as u64,
|
||||
LaneType::FloatType(ref f) => *f as u64,
|
||||
LaneType::IntType(ref i) => *i as u64,
|
||||
}
|
||||
}
|
||||
|
||||
/// Find the unique number associated with this lane type.
|
||||
pub fn number(self) -> u8 {
|
||||
LANE_BASE
|
||||
+ match self {
|
||||
LaneType::BoolType(base_types::Bool::B1) => 0,
|
||||
LaneType::BoolType(base_types::Bool::B8) => 1,
|
||||
LaneType::BoolType(base_types::Bool::B16) => 2,
|
||||
LaneType::BoolType(base_types::Bool::B32) => 3,
|
||||
LaneType::BoolType(base_types::Bool::B64) => 4,
|
||||
LaneType::IntType(base_types::Int::I8) => 5,
|
||||
LaneType::IntType(base_types::Int::I16) => 6,
|
||||
LaneType::IntType(base_types::Int::I32) => 7,
|
||||
LaneType::IntType(base_types::Int::I64) => 8,
|
||||
LaneType::FloatType(base_types::Float::F32) => 9,
|
||||
LaneType::FloatType(base_types::Float::F64) => 10,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for LaneType {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match *self {
|
||||
LaneType::BoolType(_) => write!(f, "b{}", self.lane_bits()),
|
||||
LaneType::FloatType(_) => write!(f, "f{}", self.lane_bits()),
|
||||
LaneType::IntType(_) => write!(f, "i{}", self.lane_bits()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for LaneType {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let inner_msg = format!("bits={}", self.lane_bits());
|
||||
write!(
|
||||
f,
|
||||
"{}",
|
||||
match *self {
|
||||
LaneType::BoolType(_) => format!("BoolType({})", inner_msg),
|
||||
LaneType::FloatType(_) => format!("FloatType({})", inner_msg),
|
||||
LaneType::IntType(_) => format!("IntType({})", inner_msg),
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a LaneType from a given bool variant.
|
||||
impl From<base_types::Bool> for LaneType {
|
||||
fn from(b: base_types::Bool) -> Self {
|
||||
LaneType::BoolType(b)
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a LaneType from a given float variant.
|
||||
impl From<base_types::Float> for LaneType {
|
||||
fn from(f: base_types::Float) -> Self {
|
||||
LaneType::FloatType(f)
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a LaneType from a given int variant.
|
||||
impl From<base_types::Int> for LaneType {
|
||||
fn from(i: base_types::Int) -> Self {
|
||||
LaneType::IntType(i)
|
||||
}
|
||||
}
|
||||
|
||||
/// An iterator for different lane types.
|
||||
pub struct LaneTypeIterator {
|
||||
bool_iter: base_types::BoolIterator,
|
||||
int_iter: base_types::IntIterator,
|
||||
float_iter: base_types::FloatIterator,
|
||||
}
|
||||
|
||||
impl LaneTypeIterator {
|
||||
/// Create a new lane type iterator.
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
bool_iter: base_types::BoolIterator::new(),
|
||||
int_iter: base_types::IntIterator::new(),
|
||||
float_iter: base_types::FloatIterator::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Iterator for LaneTypeIterator {
|
||||
type Item = LaneType;
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if let Some(b) = self.bool_iter.next() {
|
||||
Some(LaneType::from(b))
|
||||
} else if let Some(i) = self.int_iter.next() {
|
||||
Some(LaneType::from(i))
|
||||
} else if let Some(f) = self.float_iter.next() {
|
||||
Some(LaneType::from(f))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A concrete SIMD vector type.
|
||||
///
|
||||
/// A vector type has a lane type which is an instance of `LaneType`,
|
||||
/// and a positive number of lanes.
|
||||
pub struct VectorType {
|
||||
base: LaneType,
|
||||
lanes: u64,
|
||||
}
|
||||
|
||||
impl VectorType {
|
||||
/// Initialize a new integer type with `n` bits.
|
||||
pub fn new(base: LaneType, lanes: u64) -> Self {
|
||||
Self { base, lanes }
|
||||
}
|
||||
|
||||
/// Return a string containing the documentation comment for this vector type.
|
||||
pub fn doc(&self) -> String {
|
||||
format!(
|
||||
"A SIMD vector with {} lanes containing a `{}` each.",
|
||||
self.lane_count(),
|
||||
self.base
|
||||
)
|
||||
}
|
||||
|
||||
/// Return the number of bits in a lane.
|
||||
pub fn lane_bits(&self) -> u64 {
|
||||
self.base.lane_bits()
|
||||
}
|
||||
|
||||
/// Return the number of lanes.
|
||||
pub fn lane_count(&self) -> u64 {
|
||||
self.lanes
|
||||
}
|
||||
|
||||
/// Find the unique number associated with this vector type.
|
||||
///
|
||||
/// 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.
|
||||
pub fn number(&self) -> u8 {
|
||||
let lanes_log_2: u32 = 63 - self.lane_count().leading_zeros();
|
||||
let base_num = u32::from(self.base.number());
|
||||
let num = (lanes_log_2 << 4) + base_num;
|
||||
num as u8
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for VectorType {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}x{}", self.base, self.lane_count())
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for VectorType {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"VectorType(base={}, lanes={})",
|
||||
self.base,
|
||||
self.lane_count()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// A flat bitvector type. Used for semantics description only.
|
||||
pub struct BVType {
|
||||
bits: u64,
|
||||
}
|
||||
|
||||
impl BVType {
|
||||
/// Initialize a new bitvector type with `n` bits.
|
||||
pub fn _new(bits: u64) -> Self {
|
||||
Self { bits }
|
||||
}
|
||||
|
||||
/// Return a string containing the documentation comment for this bitvector type.
|
||||
pub fn doc(&self) -> String {
|
||||
format!("A bitvector type with {} bits.", self.bits)
|
||||
}
|
||||
|
||||
/// Return the number of bits in a lane.
|
||||
pub fn lane_bits(&self) -> u64 {
|
||||
self.bits
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for BVType {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "bv{}", self.bits)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for BVType {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "BVType(bits={})", self.lane_bits())
|
||||
}
|
||||
}
|
||||
|
||||
/// A concrete scalar type that is neither a vector nor a lane type.
|
||||
///
|
||||
/// Special types cannot be used to form vectors.
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum SpecialType {
|
||||
Flag(base_types::Flag),
|
||||
}
|
||||
|
||||
impl SpecialType {
|
||||
/// Return a string containing the documentation comment for this special type.
|
||||
pub fn doc(self) -> String {
|
||||
match self {
|
||||
SpecialType::Flag(base_types::Flag::IFlags) => String::from(
|
||||
"CPU flags representing the result of an integer comparison. These flags
|
||||
can be tested with an :type:`intcc` condition code.",
|
||||
),
|
||||
SpecialType::Flag(base_types::Flag::FFlags) => String::from(
|
||||
"CPU flags representing the result of a floating point comparison. These
|
||||
flags can be tested with a :type:`floatcc` condition code.",
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the number of bits in a lane.
|
||||
pub fn lane_bits(self) -> u64 {
|
||||
match self {
|
||||
SpecialType::Flag(_) => 0,
|
||||
}
|
||||
}
|
||||
|
||||
/// Find the unique number associated with this special type.
|
||||
pub fn number(self) -> u8 {
|
||||
match self {
|
||||
SpecialType::Flag(base_types::Flag::IFlags) => 1,
|
||||
SpecialType::Flag(base_types::Flag::FFlags) => 2,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for SpecialType {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match *self {
|
||||
SpecialType::Flag(base_types::Flag::IFlags) => write!(f, "iflags"),
|
||||
SpecialType::Flag(base_types::Flag::FFlags) => write!(f, "fflags"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for SpecialType {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"{}",
|
||||
match *self {
|
||||
SpecialType::Flag(_) => format!("FlagsType({})", self),
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<base_types::Flag> for SpecialType {
|
||||
fn from(f: base_types::Flag) -> Self {
|
||||
SpecialType::Flag(f)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct SpecialTypeIterator {
|
||||
flag_iter: base_types::FlagIterator,
|
||||
}
|
||||
|
||||
impl SpecialTypeIterator {
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
flag_iter: base_types::FlagIterator::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Iterator for SpecialTypeIterator {
|
||||
type Item = SpecialType;
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if let Some(f) = self.flag_iter.next() {
|
||||
Some(SpecialType::from(f))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
49
cranelift/codegen/meta/src/constant_hash.rs
Normal file
49
cranelift/codegen/meta/src/constant_hash.rs
Normal file
@@ -0,0 +1,49 @@
|
||||
pub fn simple_hash(s: &str) -> usize {
|
||||
let mut h: u32 = 5381;
|
||||
for c in s.chars() {
|
||||
h = (h ^ c as u32).wrapping_add(h.rotate_right(6));
|
||||
}
|
||||
h as usize
|
||||
}
|
||||
|
||||
/// Compute an open addressed, quadratically probed hash table containing
|
||||
/// `items`. The returned table is a list containing the elements of the
|
||||
/// iterable `items` and `None` in unused slots.
|
||||
pub fn generate_table<T, H: Fn(&T) -> usize>(items: &Vec<T>, hash_function: H) -> Vec<Option<&T>> {
|
||||
let size = (1.20 * items.len() as f64) as usize;
|
||||
// TODO do we really need the multiply by two here?
|
||||
let size = if size.is_power_of_two() {
|
||||
size * 2
|
||||
} else {
|
||||
size.next_power_of_two()
|
||||
};
|
||||
|
||||
let mut table: Vec<Option<&T>> = vec![None; size];
|
||||
|
||||
for i in items {
|
||||
let mut h = hash_function(i) % size;
|
||||
let mut s = 0;
|
||||
while table[h].is_some() {
|
||||
s += 1;
|
||||
h = (h + s) % size;
|
||||
}
|
||||
table[h] = Some(i);
|
||||
}
|
||||
|
||||
table
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_generate_table() {
|
||||
let v = vec!["Hello".to_string(), "world".to_string()];
|
||||
let table = generate_table(&v, |s| simple_hash(&s));
|
||||
assert_eq!(
|
||||
table,
|
||||
vec![
|
||||
None,
|
||||
Some(&"Hello".to_string()),
|
||||
Some(&"world".to_string()),
|
||||
None
|
||||
]
|
||||
);
|
||||
}
|
||||
47
cranelift/codegen/meta/src/error.rs
Normal file
47
cranelift/codegen/meta/src/error.rs
Normal file
@@ -0,0 +1,47 @@
|
||||
use std::fmt;
|
||||
use std::io;
|
||||
|
||||
/// An error that occurred when the cranelift_codegen_meta crate was generating
|
||||
/// source files for the cranelift_codegen crate.
|
||||
#[derive(Debug)]
|
||||
pub struct Error {
|
||||
inner: Box<ErrorInner>,
|
||||
}
|
||||
|
||||
impl Error {
|
||||
/// Create a new error object with the given message.
|
||||
pub fn with_msg<S: Into<String>>(msg: S) -> Error {
|
||||
Error {
|
||||
inner: Box::new(ErrorInner::Msg(msg.into())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}", self.inner)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<io::Error> for Error {
|
||||
fn from(e: io::Error) -> Self {
|
||||
Error {
|
||||
inner: Box::new(ErrorInner::IoError(e)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum ErrorInner {
|
||||
Msg(String),
|
||||
IoError(io::Error),
|
||||
}
|
||||
|
||||
impl fmt::Display for ErrorInner {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match *self {
|
||||
ErrorInner::Msg(ref s) => write!(f, "{}", s),
|
||||
ErrorInner::IoError(ref e) => write!(f, "{}", e),
|
||||
}
|
||||
}
|
||||
}
|
||||
138
cranelift/codegen/meta/src/gen_registers.rs
Normal file
138
cranelift/codegen/meta/src/gen_registers.rs
Normal file
@@ -0,0 +1,138 @@
|
||||
use crate::cdsl::isa::TargetIsa;
|
||||
use crate::cdsl::regs::{RegBank, RegClass};
|
||||
use crate::error;
|
||||
use crate::srcgen::Formatter;
|
||||
use cranelift_entity::EntityRef;
|
||||
|
||||
fn gen_regbank(fmt: &mut Formatter, reg_bank: &RegBank) {
|
||||
let names = if reg_bank.names.len() > 0 {
|
||||
format!(r#""{}""#, reg_bank.names.join(r#"", ""#))
|
||||
} else {
|
||||
"".to_string()
|
||||
};
|
||||
fmt.line("RegBank {");
|
||||
fmt.indent(|fmt| {
|
||||
fmt.line(&format!(r#"name: "{}","#, reg_bank.name));
|
||||
fmt.line(&format!("first_unit: {},", reg_bank.first_unit));
|
||||
fmt.line(&format!("units: {},", reg_bank.units));
|
||||
fmt.line(&format!("names: &[{}],", names));
|
||||
fmt.line(&format!(r#"prefix: "{}","#, reg_bank.prefix));
|
||||
fmt.line(&format!("first_toprc: {},", reg_bank.toprcs[0].index()));
|
||||
fmt.line(&format!("num_toprcs: {},", reg_bank.toprcs.len()));
|
||||
fmt.line(&format!(
|
||||
"pressure_tracking: {},",
|
||||
if reg_bank.pressure_tracking {
|
||||
"true"
|
||||
} else {
|
||||
"false"
|
||||
}
|
||||
));
|
||||
});
|
||||
fmt.line("},");
|
||||
}
|
||||
|
||||
fn gen_regclass(isa: &TargetIsa, reg_class: &RegClass, fmt: &mut Formatter) {
|
||||
let reg_bank = isa.reg_banks.get(reg_class.bank).unwrap();
|
||||
|
||||
let mask: Vec<String> = reg_class
|
||||
.mask(reg_bank.first_unit)
|
||||
.iter()
|
||||
.map(|x| format!("0x{:08x}", x))
|
||||
.collect();
|
||||
let mask = mask.join(", ");
|
||||
|
||||
fmt.line(&format!(
|
||||
"pub static {}_DATA: RegClassData = RegClassData {{",
|
||||
reg_class.name
|
||||
));
|
||||
fmt.indent(|fmt| {
|
||||
fmt.line(&format!(r#"name: "{}","#, reg_class.name));
|
||||
fmt.line(&format!("index: {},", reg_class.index.index()));
|
||||
fmt.line(&format!("width: {},", reg_class.width));
|
||||
fmt.line(&format!("bank: {},", reg_class.bank.index()));
|
||||
fmt.line(&format!("toprc: {},", reg_class.toprc.index()));
|
||||
fmt.line(&format!(
|
||||
"first: {},",
|
||||
reg_bank.first_unit + reg_class.start
|
||||
));
|
||||
fmt.line(&format!("subclasses: {:#x},", reg_class.subclass_mask()));
|
||||
fmt.line(&format!("mask: [{}],", mask));
|
||||
fmt.line("info: &INFO,");
|
||||
});
|
||||
fmt.line("};");
|
||||
fmt.line("#[allow(dead_code)]");
|
||||
fmt.line(&format!(
|
||||
"pub static {}: RegClass = &{}_DATA;",
|
||||
reg_class.name, reg_class.name
|
||||
));
|
||||
}
|
||||
|
||||
fn gen_regbank_units(reg_bank: &RegBank, fmt: &mut Formatter) {
|
||||
for unit in 0..reg_bank.units {
|
||||
let v = unit + reg_bank.first_unit;
|
||||
if (unit as usize) < reg_bank.names.len() {
|
||||
fmt.line(&format!("{} = {},", reg_bank.names[unit as usize], v));
|
||||
continue;
|
||||
}
|
||||
fmt.line(&format!("{}{} = {},", reg_bank.prefix, unit, v));
|
||||
}
|
||||
}
|
||||
|
||||
fn gen_isa(isa: &TargetIsa, fmt: &mut Formatter) {
|
||||
// Emit RegInfo.
|
||||
fmt.line("pub static INFO: RegInfo = RegInfo {");
|
||||
|
||||
fmt.indent(|fmt| {
|
||||
fmt.line("banks: &[");
|
||||
// Bank descriptors.
|
||||
fmt.indent(|fmt| {
|
||||
for reg_bank in isa.reg_banks.values() {
|
||||
gen_regbank(fmt, ®_bank);
|
||||
}
|
||||
});
|
||||
fmt.line("],");
|
||||
// References to register classes.
|
||||
fmt.line("classes: &[");
|
||||
fmt.indent(|fmt| {
|
||||
for reg_class in isa.reg_classes.values() {
|
||||
fmt.line(&format!("&{}_DATA,", reg_class.name));
|
||||
}
|
||||
});
|
||||
fmt.line("],");
|
||||
});
|
||||
fmt.line("};");
|
||||
|
||||
// Register class descriptors.
|
||||
for rc in isa.reg_classes.values() {
|
||||
gen_regclass(&isa, rc, fmt);
|
||||
}
|
||||
|
||||
// Emit constants for all the register units.
|
||||
fmt.line("#[allow(dead_code, non_camel_case_types)]");
|
||||
fmt.line("#[derive(Clone, Copy)]");
|
||||
fmt.line("pub enum RU {");
|
||||
fmt.indent(|fmt| {
|
||||
for reg_bank in isa.reg_banks.values() {
|
||||
gen_regbank_units(reg_bank, fmt);
|
||||
}
|
||||
});
|
||||
fmt.line("}");
|
||||
|
||||
// Emit Into conversion for the RU class.
|
||||
fmt.line("impl Into<RegUnit> for RU {");
|
||||
fmt.indent(|fmt| {
|
||||
fmt.line("fn into(self) -> RegUnit {");
|
||||
fmt.indent(|fmt| {
|
||||
fmt.line("self as RegUnit");
|
||||
});
|
||||
fmt.line("}")
|
||||
});
|
||||
fmt.line("}");
|
||||
}
|
||||
|
||||
pub fn generate(isa: &TargetIsa, base_filename: &str, out_dir: &str) -> Result<(), error::Error> {
|
||||
let mut fmt = Formatter::new();
|
||||
gen_isa(&isa, &mut fmt);
|
||||
fmt.update_file(&format!("{}-{}.rs", base_filename, isa.name), out_dir)?;
|
||||
Ok(())
|
||||
}
|
||||
448
cranelift/codegen/meta/src/gen_settings.rs
Normal file
448
cranelift/codegen/meta/src/gen_settings.rs
Normal file
@@ -0,0 +1,448 @@
|
||||
use crate::base;
|
||||
use crate::cdsl::camel_case;
|
||||
use crate::cdsl::isa::TargetIsa;
|
||||
use crate::cdsl::settings::{
|
||||
BoolSetting, Predicate, Preset, Setting, SettingGroup, SpecificSetting,
|
||||
};
|
||||
use crate::constant_hash::{generate_table, simple_hash};
|
||||
use crate::error;
|
||||
use crate::srcgen::{Formatter, Match};
|
||||
use crate::unique_table::UniqueTable;
|
||||
use std::collections::HashMap;
|
||||
|
||||
enum ParentGroup {
|
||||
None,
|
||||
Shared,
|
||||
}
|
||||
|
||||
/// Emits the constructor of the Flags structure.
|
||||
fn gen_constructor(group: &SettingGroup, parent: ParentGroup, fmt: &mut Formatter) {
|
||||
let args = match parent {
|
||||
ParentGroup::None => "builder: Builder",
|
||||
ParentGroup::Shared => "shared: &settings::Flags, builder: Builder",
|
||||
};
|
||||
fmt.line("impl Flags {");
|
||||
fmt.indent(|fmt| {
|
||||
fmt.doc_comment(&format!("Create flags {} settings group.", group.name));
|
||||
fmt.line("#[allow(unused_variables)]");
|
||||
fmt.line(&format!("pub fn new({}) -> Self {{", args));
|
||||
fmt.indent(|fmt| {
|
||||
fmt.line(&format!(
|
||||
"let bvec = builder.state_for(\"{}\");",
|
||||
group.name
|
||||
));
|
||||
fmt.line(&format!(
|
||||
"let mut {} = Self {{ bytes: [0; {}] }};",
|
||||
group.name,
|
||||
group.byte_size()
|
||||
));
|
||||
fmt.line(&format!(
|
||||
"debug_assert_eq!(bvec.len(), {});",
|
||||
group.settings_size
|
||||
));
|
||||
fmt.line(&format!(
|
||||
"{}.bytes[0..{}].copy_from_slice(&bvec);",
|
||||
group.name, group.settings_size
|
||||
));
|
||||
|
||||
// Now compute the predicates.
|
||||
for p in &group.predicates {
|
||||
fmt.comment(&format!("Precompute #{}.", p.number));
|
||||
fmt.line(&format!("if {} {{", p.render(group)));
|
||||
fmt.indent(|fmt| {
|
||||
fmt.line(&format!(
|
||||
"{}.bytes[{}] |= 1 << {};",
|
||||
group.name,
|
||||
group.bool_start_byte_offset + p.number / 8,
|
||||
p.number % 8
|
||||
));
|
||||
});
|
||||
fmt.line("}");
|
||||
}
|
||||
|
||||
fmt.line(group.name);
|
||||
});
|
||||
fmt.line("}");
|
||||
});
|
||||
fmt.line("}");
|
||||
}
|
||||
|
||||
/// Emit Display and FromStr implementations for enum settings.
|
||||
fn gen_to_and_from_str(name: &str, values: &[&'static str], fmt: &mut Formatter) {
|
||||
fmt.line(&format!("impl fmt::Display for {} {{", name));
|
||||
fmt.indent(|fmt| {
|
||||
fmt.line("fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {");
|
||||
fmt.indent(|fmt| {
|
||||
fmt.line("f.write_str(match *self {");
|
||||
fmt.indent(|fmt| {
|
||||
for v in values.iter() {
|
||||
fmt.line(&format!("{}::{} => \"{}\",", name, camel_case(v), v));
|
||||
}
|
||||
});
|
||||
fmt.line("})");
|
||||
});
|
||||
fmt.line("}");
|
||||
});
|
||||
fmt.line("}");
|
||||
|
||||
fmt.line(&format!("impl str::FromStr for {} {{", name));
|
||||
fmt.indent(|fmt| {
|
||||
fmt.line("type Err = ();");
|
||||
fmt.line("fn from_str(s: &str) -> Result<Self, Self::Err> {");
|
||||
fmt.indent(|fmt| {
|
||||
fmt.line("match s {");
|
||||
fmt.indent(|fmt| {
|
||||
for v in values.iter() {
|
||||
fmt.line(&format!("\"{}\" => Ok({}::{}),", v, name, camel_case(v)));
|
||||
}
|
||||
fmt.line("_ => Err(()),");
|
||||
});
|
||||
fmt.line("}");
|
||||
});
|
||||
fmt.line("}");
|
||||
});
|
||||
fmt.line("}");
|
||||
}
|
||||
|
||||
/// Emit real enum for the Enum settings.
|
||||
fn gen_enum_types(group: &SettingGroup, fmt: &mut Formatter) {
|
||||
for setting in group.settings.iter() {
|
||||
let values = match setting.specific {
|
||||
SpecificSetting::Bool(_) | SpecificSetting::Num(_) => continue,
|
||||
SpecificSetting::Enum(ref values) => values,
|
||||
};
|
||||
let name = camel_case(setting.name);
|
||||
|
||||
fmt.doc_comment(&format!("Values for `{}.{}`.", group.name, setting.name));
|
||||
fmt.line("#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]");
|
||||
fmt.line(&format!("pub enum {} {{", name));
|
||||
fmt.indent(|fmt| {
|
||||
for v in values.iter() {
|
||||
fmt.doc_comment(&format!("`{}`.", v));
|
||||
fmt.line(&format!("{},", camel_case(v)));
|
||||
}
|
||||
});
|
||||
fmt.line("}");
|
||||
|
||||
gen_to_and_from_str(&name, values, fmt);
|
||||
}
|
||||
}
|
||||
|
||||
/// Emit a getter function for `setting`.
|
||||
fn gen_getter(setting: &Setting, fmt: &mut Formatter) {
|
||||
fmt.doc_comment(setting.comment);
|
||||
match setting.specific {
|
||||
SpecificSetting::Bool(BoolSetting {
|
||||
predicate_number, ..
|
||||
}) => {
|
||||
fmt.line(&format!("pub fn {}(&self) -> bool {{", setting.name));
|
||||
fmt.indent(|fmt| {
|
||||
fmt.line(&format!("self.numbered_predicate({})", predicate_number));
|
||||
});
|
||||
fmt.line("}");
|
||||
}
|
||||
SpecificSetting::Enum(ref values) => {
|
||||
let ty = camel_case(setting.name);
|
||||
fmt.line(&format!("pub fn {}(&self) -> {} {{", setting.name, ty));
|
||||
fmt.indent(|fmt| {
|
||||
let mut m = Match::new(format!("self.bytes[{}]", setting.byte_offset));
|
||||
for (i, v) in values.iter().enumerate() {
|
||||
m.arm(
|
||||
format!("{}", i),
|
||||
vec![],
|
||||
format!("{}::{}", ty, camel_case(v)),
|
||||
);
|
||||
}
|
||||
m.arm("_", vec![], "panic!(\"Invalid enum value\")");
|
||||
fmt.add_match(m);
|
||||
});
|
||||
fmt.line("}");
|
||||
}
|
||||
SpecificSetting::Num(_) => {
|
||||
fmt.line(&format!("pub fn {}(&self) -> u8 {{", setting.name));
|
||||
fmt.indent(|fmt| {
|
||||
fmt.line(&format!("self.bytes[{}]", setting.byte_offset));
|
||||
});
|
||||
fmt.line("}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn gen_pred_getter(predicate: &Predicate, group: &SettingGroup, fmt: &mut Formatter) {
|
||||
fmt.doc_comment(&format!(
|
||||
"Computed predicate `{}`.",
|
||||
predicate.render(group)
|
||||
));
|
||||
fmt.line(&format!("pub fn {}(&self) -> bool {{", predicate.name));
|
||||
fmt.indent(|fmt| {
|
||||
fmt.line(&format!("self.numbered_predicate({})", predicate.number));
|
||||
});
|
||||
fmt.line("}");
|
||||
}
|
||||
|
||||
/// Emits getters for each setting value.
|
||||
fn gen_getters(group: &SettingGroup, fmt: &mut Formatter) {
|
||||
fmt.doc_comment("User-defined settings.");
|
||||
fmt.line("#[allow(dead_code)]");
|
||||
fmt.line("impl Flags {");
|
||||
fmt.indent(|fmt| {
|
||||
fmt.doc_comment("Get a view of the boolean predicates.");
|
||||
fmt.line("pub fn predicate_view(&self) -> ::settings::PredicateView {");
|
||||
fmt.indent(|fmt| {
|
||||
fmt.line(&format!(
|
||||
"::settings::PredicateView::new(&self.bytes[{}..])",
|
||||
group.bool_start_byte_offset
|
||||
));
|
||||
});
|
||||
fmt.line("}");
|
||||
|
||||
if group.settings.len() > 0 {
|
||||
fmt.doc_comment("Dynamic numbered predicate getter.");
|
||||
fmt.line("fn numbered_predicate(&self, p: usize) -> bool {");
|
||||
fmt.indent(|fmt| {
|
||||
fmt.line(&format!(
|
||||
"self.bytes[{} + p / 8] & (1 << (p % 8)) != 0",
|
||||
group.bool_start_byte_offset
|
||||
));
|
||||
});
|
||||
fmt.line("}");
|
||||
}
|
||||
|
||||
for setting in &group.settings {
|
||||
gen_getter(&setting, fmt);
|
||||
}
|
||||
for predicate in &group.predicates {
|
||||
gen_pred_getter(&predicate, &group, fmt);
|
||||
}
|
||||
});
|
||||
fmt.line("}");
|
||||
}
|
||||
|
||||
#[derive(Hash, PartialEq, Eq)]
|
||||
enum SettingOrPreset<'a> {
|
||||
Setting(&'a Setting),
|
||||
Preset(&'a Preset),
|
||||
}
|
||||
|
||||
impl<'a> SettingOrPreset<'a> {
|
||||
fn name(&self) -> &str {
|
||||
match *self {
|
||||
SettingOrPreset::Setting(s) => s.name,
|
||||
SettingOrPreset::Preset(p) => p.name,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Emits DESCRIPTORS, ENUMERATORS, HASH_TABLE and PRESETS.
|
||||
fn gen_descriptors(group: &SettingGroup, fmt: &mut Formatter) {
|
||||
let mut enum_table: UniqueTable<&'static str> = UniqueTable::new();
|
||||
|
||||
let mut descriptor_index_map: HashMap<SettingOrPreset, usize> = HashMap::new();
|
||||
|
||||
// Generate descriptors.
|
||||
fmt.line(&format!(
|
||||
"static DESCRIPTORS: [detail::Descriptor; {}] = [",
|
||||
group.settings.len() + group.presets.len()
|
||||
));
|
||||
fmt.indent(|fmt| {
|
||||
for (idx, setting) in group.settings.iter().enumerate() {
|
||||
fmt.line("detail::Descriptor {");
|
||||
fmt.indent(|fmt| {
|
||||
fmt.line(&format!("name: \"{}\",", setting.name));
|
||||
fmt.line(&format!("offset: {},", setting.byte_offset));
|
||||
match setting.specific {
|
||||
SpecificSetting::Bool(BoolSetting { bit_offset, .. }) => {
|
||||
fmt.line(&format!(
|
||||
"detail: detail::Detail::Bool {{ bit: {} }},",
|
||||
bit_offset
|
||||
));
|
||||
}
|
||||
SpecificSetting::Enum(ref values) => {
|
||||
let offset = enum_table.add(values);
|
||||
fmt.line(&format!(
|
||||
"detail: detail::Detail::Enum {{ last: {}, enumerators: {} }},",
|
||||
values.len() - 1,
|
||||
offset
|
||||
));
|
||||
}
|
||||
SpecificSetting::Num(_) => {
|
||||
fmt.line("detail: detail::Detail::Num,");
|
||||
}
|
||||
}
|
||||
|
||||
descriptor_index_map.insert(SettingOrPreset::Setting(setting), idx);
|
||||
});
|
||||
fmt.line("},");
|
||||
}
|
||||
|
||||
for (idx, preset) in group.presets.iter().enumerate() {
|
||||
fmt.line("detail::Descriptor {");
|
||||
fmt.indent(|fmt| {
|
||||
fmt.line(&format!("name: \"{}\",", preset.name));
|
||||
fmt.line(&format!("offset: {},", (idx as u8) * group.settings_size));
|
||||
fmt.line("detail: detail::Detail::Preset,");
|
||||
});
|
||||
fmt.line("},");
|
||||
|
||||
descriptor_index_map.insert(SettingOrPreset::Preset(preset), idx);
|
||||
}
|
||||
});
|
||||
fmt.line("];");
|
||||
|
||||
// Generate enumerators.
|
||||
fmt.line(&format!(
|
||||
"static ENUMERATORS: [&str; {}] = [",
|
||||
enum_table.len()
|
||||
));
|
||||
fmt.indent(|fmt| {
|
||||
for enum_val in enum_table.iter() {
|
||||
fmt.line(&format!("\"{}\",", enum_val));
|
||||
}
|
||||
});
|
||||
fmt.line("];");
|
||||
|
||||
// Generate hash table.
|
||||
let mut hash_entries: Vec<SettingOrPreset> = Vec::new();
|
||||
hash_entries.extend(
|
||||
group
|
||||
.settings
|
||||
.iter()
|
||||
.map(|x| SettingOrPreset::Setting(x))
|
||||
.collect::<Vec<SettingOrPreset>>(),
|
||||
);
|
||||
hash_entries.extend(
|
||||
group
|
||||
.presets
|
||||
.iter()
|
||||
.map(|x| SettingOrPreset::Preset(x))
|
||||
.collect::<Vec<SettingOrPreset>>(),
|
||||
);
|
||||
let hash_table = generate_table(&hash_entries, |entry| simple_hash(entry.name()));
|
||||
fmt.line(&format!(
|
||||
"static HASH_TABLE: [u16; {}] = [",
|
||||
hash_table.len()
|
||||
));
|
||||
fmt.indent(|fmt| {
|
||||
for h in &hash_table {
|
||||
match *h {
|
||||
Some(setting_or_preset) => fmt.line(&format!(
|
||||
"{},",
|
||||
&descriptor_index_map
|
||||
.get(setting_or_preset)
|
||||
.unwrap()
|
||||
.to_string()
|
||||
)),
|
||||
None => fmt.line("0xffff,"),
|
||||
}
|
||||
}
|
||||
});
|
||||
fmt.line("];");
|
||||
|
||||
// Generate presets.
|
||||
fmt.line(&format!(
|
||||
"static PRESETS: [(u8, u8); {}] = [",
|
||||
group.presets.len()
|
||||
));
|
||||
fmt.indent(|fmt| {
|
||||
for preset in &group.presets {
|
||||
fmt.comment(preset.name);
|
||||
for (mask, value) in preset.layout(&group) {
|
||||
fmt.line(&format!("(0b{:08b}, 0b{:08b}),", mask, value));
|
||||
}
|
||||
}
|
||||
});
|
||||
fmt.line("];");
|
||||
}
|
||||
|
||||
fn gen_template(group: &SettingGroup, fmt: &mut Formatter) {
|
||||
let mut default_bytes: Vec<u8> = vec![0; group.settings_size as usize];
|
||||
for setting in &group.settings {
|
||||
*default_bytes.get_mut(setting.byte_offset as usize).unwrap() |= setting.default_byte();
|
||||
}
|
||||
|
||||
let default_bytes: Vec<String> = default_bytes
|
||||
.iter()
|
||||
.map(|x| format!("{:#04x}", x))
|
||||
.collect();
|
||||
let default_bytes_str = default_bytes.join(", ");
|
||||
|
||||
fmt.line("static TEMPLATE: detail::Template = detail::Template {");
|
||||
fmt.indent(|fmt| {
|
||||
fmt.line(&format!("name: \"{}\",", group.name));
|
||||
fmt.line("descriptors: &DESCRIPTORS,");
|
||||
fmt.line("enumerators: &ENUMERATORS,");
|
||||
fmt.line("hash_table: &HASH_TABLE,");
|
||||
fmt.line(&format!("defaults: &[{}],", default_bytes_str));
|
||||
fmt.line("presets: &PRESETS,");
|
||||
});
|
||||
fmt.line("};");
|
||||
|
||||
fmt.doc_comment(&format!(
|
||||
"Create a `settings::Builder` for the {} settings group.",
|
||||
group.name
|
||||
));
|
||||
fmt.line("pub fn builder() -> Builder {");
|
||||
fmt.indent(|fmt| {
|
||||
fmt.line("Builder::new(&TEMPLATE)");
|
||||
});
|
||||
fmt.line("}");
|
||||
}
|
||||
|
||||
fn gen_display(group: &SettingGroup, fmt: &mut Formatter) {
|
||||
fmt.line("impl fmt::Display for Flags {");
|
||||
fmt.indent(|fmt| {
|
||||
fmt.line("fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {");
|
||||
fmt.indent(|fmt| {
|
||||
fmt.line(&format!("writeln!(f, \"[{}]\")?;", group.name));
|
||||
fmt.line("for d in &DESCRIPTORS {");
|
||||
fmt.indent(|fmt| {
|
||||
fmt.line("if !d.detail.is_preset() {");
|
||||
fmt.indent(|fmt| {
|
||||
fmt.line("write!(f, \"{} = \", d.name)?;");
|
||||
fmt.line(
|
||||
"TEMPLATE.format_toml_value(d.detail, self.bytes[d.offset as usize], f)?;",
|
||||
);
|
||||
fmt.line("writeln!(f)?;");
|
||||
});
|
||||
fmt.line("}");
|
||||
});
|
||||
fmt.line("}");
|
||||
fmt.line("Ok(())");
|
||||
});
|
||||
fmt.line("}")
|
||||
});
|
||||
fmt.line("}");
|
||||
}
|
||||
|
||||
fn gen_group(group: &SettingGroup, parent: ParentGroup, fmt: &mut Formatter) {
|
||||
// Generate struct.
|
||||
fmt.line("#[derive(Clone)]");
|
||||
fmt.doc_comment(&format!("Flags group `{}`.", group.name));
|
||||
fmt.line("pub struct Flags {");
|
||||
fmt.indent(|fmt| {
|
||||
fmt.line(&format!("bytes: [u8; {}],", group.byte_size()));
|
||||
});
|
||||
fmt.line("}");
|
||||
|
||||
gen_constructor(group, parent, fmt);
|
||||
gen_enum_types(group, fmt);
|
||||
gen_getters(group, fmt);
|
||||
gen_descriptors(group, fmt);
|
||||
gen_template(group, fmt);
|
||||
gen_display(group, fmt);
|
||||
}
|
||||
|
||||
pub fn generate_common(filename: &str, out_dir: &str) -> Result<SettingGroup, error::Error> {
|
||||
let settings = base::settings::generate();
|
||||
let mut fmt = Formatter::new();
|
||||
gen_group(&settings, ParentGroup::None, &mut fmt);
|
||||
fmt.update_file(filename, out_dir)?;
|
||||
Ok(settings)
|
||||
}
|
||||
|
||||
pub fn generate(isa: &TargetIsa, prefix: &str, out_dir: &str) -> Result<(), error::Error> {
|
||||
let mut fmt = Formatter::new();
|
||||
gen_group(&isa.settings, ParentGroup::Shared, &mut fmt);
|
||||
fmt.update_file(&format!("{}-{}.rs", prefix, isa.name), out_dir)?;
|
||||
Ok(())
|
||||
}
|
||||
73
cranelift/codegen/meta/src/gen_types.rs
Normal file
73
cranelift/codegen/meta/src/gen_types.rs
Normal file
@@ -0,0 +1,73 @@
|
||||
//! Generate sources with type info.
|
||||
//!
|
||||
//! This generates a `types.rs` file which is included in
|
||||
//! `cranelift-codegen/ir/types.rs`. The file provides constant definitions for the
|
||||
//! most commonly used types, including all of the scalar types.
|
||||
//!
|
||||
//! This ensures that the metaprogram and the generated program see the same
|
||||
//! type numbering.
|
||||
|
||||
use crate::cdsl::types as cdsl_types;
|
||||
use crate::error;
|
||||
use crate::srcgen;
|
||||
|
||||
/// Emit a constant definition of a single value type.
|
||||
fn emit_type(ty: &cdsl_types::ValueType, fmt: &mut srcgen::Formatter) -> Result<(), error::Error> {
|
||||
let name = ty.to_string().to_uppercase();
|
||||
let number = ty.number().ok_or_else(|| {
|
||||
error::Error::with_msg(format!(
|
||||
"Could not emit type `{}` which has no number.",
|
||||
name
|
||||
))
|
||||
})?;
|
||||
|
||||
let definition = format!("pub const {}: Type = Type({:#x});\n", name, number);
|
||||
|
||||
fmt.doc_comment(&ty.doc());
|
||||
fmt.line(&definition);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Emit definition for all vector types with `bits` total size.
|
||||
fn emit_vectors(bits: u64, fmt: &mut srcgen::Formatter) -> Result<(), error::Error> {
|
||||
let vec_size: u64 = bits / 8;
|
||||
for vec in cdsl_types::ValueType::all_lane_types()
|
||||
.map(|ty| (ty, cdsl_types::ValueType::from(ty).membytes()))
|
||||
.filter(|&(_, lane_size)| lane_size != 0 && lane_size < vec_size)
|
||||
.map(|(ty, lane_size)| (ty, vec_size / lane_size))
|
||||
.map(|(ty, lanes)| cdsl_types::VectorType::new(ty, lanes))
|
||||
{
|
||||
emit_type(&cdsl_types::ValueType::from(vec), fmt)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Emit types using the given formatter object.
|
||||
fn emit_types(fmt: &mut srcgen::Formatter) -> Result<(), error::Error> {
|
||||
// Emit all of the special types, such as types for CPU flags.
|
||||
for spec in cdsl_types::ValueType::all_special_types().map(cdsl_types::ValueType::from) {
|
||||
emit_type(&spec, fmt)?;
|
||||
}
|
||||
|
||||
// Emit all of the lane types, such integers, floats, and booleans.
|
||||
for ty in cdsl_types::ValueType::all_lane_types().map(cdsl_types::ValueType::from) {
|
||||
emit_type(&ty, fmt)?;
|
||||
}
|
||||
|
||||
// Emit vector definitions for common SIMD sizes.
|
||||
for vec_size in &[64_u64, 128, 256, 512] {
|
||||
emit_vectors(*vec_size, fmt)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Generate the types file.
|
||||
pub fn generate(filename: &str, out_dir: &str) -> Result<(), error::Error> {
|
||||
let mut fmt = srcgen::Formatter::new();
|
||||
emit_types(&mut fmt)?;
|
||||
fmt.update_file(filename, out_dir)?;
|
||||
Ok(())
|
||||
}
|
||||
45
cranelift/codegen/meta/src/isa/arm32/mod.rs
Normal file
45
cranelift/codegen/meta/src/isa/arm32/mod.rs
Normal file
@@ -0,0 +1,45 @@
|
||||
use crate::cdsl::isa::{TargetIsa, TargetIsaBuilder};
|
||||
use crate::cdsl::regs::{RegBankBuilder, RegClassBuilder};
|
||||
use crate::cdsl::settings::{SettingGroup, SettingGroupBuilder};
|
||||
|
||||
fn define_settings(_shared: &SettingGroup) -> SettingGroup {
|
||||
let setting = SettingGroupBuilder::new("arm32");
|
||||
setting.finish()
|
||||
}
|
||||
|
||||
pub fn define(shared_settings: &SettingGroup) -> TargetIsa {
|
||||
let mut isa = TargetIsaBuilder::new("arm32", define_settings(shared_settings));
|
||||
|
||||
let builder = RegBankBuilder::new("FloatRegs", "s")
|
||||
.units(64)
|
||||
.track_pressure(true);
|
||||
let float_regs = isa.add_reg_bank(builder);
|
||||
|
||||
let builder = RegBankBuilder::new("IntRegs", "r")
|
||||
.units(16)
|
||||
.track_pressure(true);
|
||||
let int_regs = isa.add_reg_bank(builder);
|
||||
|
||||
let builder = RegBankBuilder::new("FlagRegs", "")
|
||||
.units(1)
|
||||
.names(vec!["nzcv"])
|
||||
.track_pressure(false);
|
||||
let flag_reg = isa.add_reg_bank(builder);
|
||||
|
||||
let builder = RegClassBuilder::new_toplevel("S", float_regs).count(32);
|
||||
isa.add_reg_class(builder);
|
||||
|
||||
let builder = RegClassBuilder::new_toplevel("D", float_regs).width(2);
|
||||
isa.add_reg_class(builder);
|
||||
|
||||
let builder = RegClassBuilder::new_toplevel("Q", float_regs).width(4);
|
||||
isa.add_reg_class(builder);
|
||||
|
||||
let builder = RegClassBuilder::new_toplevel("GPR", int_regs);
|
||||
isa.add_reg_class(builder);
|
||||
|
||||
let builder = RegClassBuilder::new_toplevel("FLAG", flag_reg);
|
||||
isa.add_reg_class(builder);
|
||||
|
||||
isa.finish()
|
||||
}
|
||||
41
cranelift/codegen/meta/src/isa/arm64/mod.rs
Normal file
41
cranelift/codegen/meta/src/isa/arm64/mod.rs
Normal file
@@ -0,0 +1,41 @@
|
||||
use crate::cdsl::isa::{TargetIsa, TargetIsaBuilder};
|
||||
use crate::cdsl::regs::{RegBankBuilder, RegClassBuilder};
|
||||
use crate::cdsl::settings::{SettingGroup, SettingGroupBuilder};
|
||||
|
||||
fn define_settings(_shared: &SettingGroup) -> SettingGroup {
|
||||
let setting = SettingGroupBuilder::new("arm64");
|
||||
setting.finish()
|
||||
}
|
||||
|
||||
pub fn define(shared_settings: &SettingGroup) -> TargetIsa {
|
||||
let mut isa = TargetIsaBuilder::new("arm64", define_settings(shared_settings));
|
||||
|
||||
// The `x31` regunit serves as the stack pointer / zero register depending on context. We
|
||||
// reserve it and don't model the difference.
|
||||
let builder = RegBankBuilder::new("IntRegs", "x")
|
||||
.units(32)
|
||||
.track_pressure(true);
|
||||
let int_regs = isa.add_reg_bank(builder);
|
||||
|
||||
let builder = RegBankBuilder::new("FloatRegs", "v")
|
||||
.units(32)
|
||||
.track_pressure(true);
|
||||
let float_regs = isa.add_reg_bank(builder);
|
||||
|
||||
let builder = RegBankBuilder::new("FlagRegs", "")
|
||||
.units(1)
|
||||
.names(vec!["nzcv"])
|
||||
.track_pressure(false);
|
||||
let flag_reg = isa.add_reg_bank(builder);
|
||||
|
||||
let builder = RegClassBuilder::new_toplevel("GPR", int_regs);
|
||||
isa.add_reg_class(builder);
|
||||
|
||||
let builder = RegClassBuilder::new_toplevel("FPR", float_regs);
|
||||
isa.add_reg_class(builder);
|
||||
|
||||
let builder = RegClassBuilder::new_toplevel("FLAG", flag_reg);
|
||||
isa.add_reg_class(builder);
|
||||
|
||||
isa.finish()
|
||||
}
|
||||
72
cranelift/codegen/meta/src/isa/mod.rs
Normal file
72
cranelift/codegen/meta/src/isa/mod.rs
Normal file
@@ -0,0 +1,72 @@
|
||||
use crate::cdsl::isa::TargetIsa;
|
||||
use crate::cdsl::settings::SettingGroup;
|
||||
use std::fmt;
|
||||
|
||||
mod arm32;
|
||||
mod arm64;
|
||||
mod riscv;
|
||||
mod x86;
|
||||
|
||||
/// Represents known ISA target.
|
||||
#[derive(Copy, Clone)]
|
||||
pub enum Isa {
|
||||
Riscv,
|
||||
X86,
|
||||
Arm32,
|
||||
Arm64,
|
||||
}
|
||||
|
||||
impl Isa {
|
||||
/// Creates isa target using name.
|
||||
pub fn new(name: &str) -> Option<Self> {
|
||||
Isa::all()
|
||||
.iter()
|
||||
.cloned()
|
||||
.filter(|isa| isa.to_string() == name)
|
||||
.next()
|
||||
}
|
||||
|
||||
/// Creates isa target from arch.
|
||||
pub fn from_arch(arch: &str) -> Option<Isa> {
|
||||
Isa::all()
|
||||
.iter()
|
||||
.cloned()
|
||||
.filter(|isa| isa.is_arch_applicable(arch))
|
||||
.next()
|
||||
}
|
||||
|
||||
/// Returns all supported isa targets.
|
||||
pub fn all() -> [Isa; 4] {
|
||||
[Isa::Riscv, Isa::X86, Isa::Arm32, Isa::Arm64]
|
||||
}
|
||||
|
||||
/// Checks if arch is applicable for the isa target.
|
||||
fn is_arch_applicable(&self, arch: &str) -> bool {
|
||||
match *self {
|
||||
Isa::Riscv => arch == "riscv",
|
||||
Isa::X86 => ["x86_64", "i386", "i586", "i686"].contains(&arch),
|
||||
Isa::Arm32 => arch.starts_with("arm") || arch.starts_with("thumb"),
|
||||
Isa::Arm64 => arch == "aarch64",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Isa {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match *self {
|
||||
Isa::Riscv => write!(f, "riscv"),
|
||||
Isa::X86 => write!(f, "x86"),
|
||||
Isa::Arm32 => write!(f, "arm32"),
|
||||
Isa::Arm64 => write!(f, "arm64"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn define_all(shared_settings: &SettingGroup) -> Vec<TargetIsa> {
|
||||
vec![
|
||||
riscv::define(shared_settings),
|
||||
arm32::define(shared_settings),
|
||||
arm64::define(shared_settings),
|
||||
x86::define(shared_settings),
|
||||
]
|
||||
}
|
||||
77
cranelift/codegen/meta/src/isa/riscv/mod.rs
Normal file
77
cranelift/codegen/meta/src/isa/riscv/mod.rs
Normal file
@@ -0,0 +1,77 @@
|
||||
use crate::cdsl::isa::{TargetIsa, TargetIsaBuilder};
|
||||
use crate::cdsl::regs::{RegBankBuilder, RegClassBuilder};
|
||||
use crate::cdsl::settings::{PredicateNode, SettingGroup, SettingGroupBuilder};
|
||||
|
||||
fn define_settings(shared: &SettingGroup) -> SettingGroup {
|
||||
let mut setting = SettingGroupBuilder::new("riscv");
|
||||
|
||||
let supports_m = setting.add_bool(
|
||||
"supports_m",
|
||||
"CPU supports the 'M' extension (mul/div)",
|
||||
false,
|
||||
);
|
||||
let supports_a = setting.add_bool(
|
||||
"supports_a",
|
||||
"CPU supports the 'A' extension (atomics)",
|
||||
false,
|
||||
);
|
||||
let supports_f = setting.add_bool(
|
||||
"supports_f",
|
||||
"CPU supports the 'F' extension (float)",
|
||||
false,
|
||||
);
|
||||
let supports_d = setting.add_bool(
|
||||
"supports_d",
|
||||
"CPU supports the 'D' extension (double)",
|
||||
false,
|
||||
);
|
||||
|
||||
let enable_m = setting.add_bool(
|
||||
"enable_m",
|
||||
"Enable the use of 'M' instructions if available",
|
||||
true,
|
||||
);
|
||||
|
||||
setting.add_bool(
|
||||
"enable_e",
|
||||
"Enable the 'RV32E' instruction set with only 16 registers",
|
||||
true,
|
||||
);
|
||||
|
||||
let shared_enable_atomics = shared.get_bool("enable_atomics");
|
||||
let shared_enable_float = shared.get_bool("enable_float");
|
||||
let shared_enable_simd = shared.get_bool("enable_simd");
|
||||
|
||||
setting.add_predicate("use_m", predicate!(supports_m && enable_m));
|
||||
setting.add_predicate("use_a", predicate!(supports_a && shared_enable_atomics));
|
||||
setting.add_predicate("use_f", predicate!(supports_f && shared_enable_float));
|
||||
setting.add_predicate("use_d", predicate!(supports_d && shared_enable_float));
|
||||
setting.add_predicate(
|
||||
"full_float",
|
||||
predicate!(shared_enable_simd && supports_f && supports_d),
|
||||
);
|
||||
|
||||
setting.finish()
|
||||
}
|
||||
|
||||
pub fn define(shared_settings: &SettingGroup) -> TargetIsa {
|
||||
let mut isa = TargetIsaBuilder::new("riscv", define_settings(shared_settings));
|
||||
|
||||
let builder = RegBankBuilder::new("IntRegs", "x")
|
||||
.units(32)
|
||||
.track_pressure(true);
|
||||
let int_regs = isa.add_reg_bank(builder);
|
||||
|
||||
let builder = RegBankBuilder::new("FloatRegs", "f")
|
||||
.units(32)
|
||||
.track_pressure(true);
|
||||
let float_regs = isa.add_reg_bank(builder);
|
||||
|
||||
let builder = RegClassBuilder::new_toplevel("GPR", int_regs);
|
||||
isa.add_reg_class(builder);
|
||||
|
||||
let builder = RegClassBuilder::new_toplevel("FPR", float_regs);
|
||||
isa.add_reg_class(builder);
|
||||
|
||||
isa.finish()
|
||||
}
|
||||
116
cranelift/codegen/meta/src/isa/x86/mod.rs
Normal file
116
cranelift/codegen/meta/src/isa/x86/mod.rs
Normal file
@@ -0,0 +1,116 @@
|
||||
use crate::cdsl::isa::{TargetIsa, TargetIsaBuilder};
|
||||
use crate::cdsl::regs::{RegBankBuilder, RegClassBuilder};
|
||||
use crate::cdsl::settings::{PredicateNode, SettingGroup, SettingGroupBuilder};
|
||||
|
||||
pub fn define_settings(_shared: &SettingGroup) -> SettingGroup {
|
||||
let mut settings = SettingGroupBuilder::new("x86");
|
||||
|
||||
// CPUID.01H:ECX
|
||||
let has_sse3 = settings.add_bool("has_sse3", "SSE3: CPUID.01H:ECX.SSE3[bit 0]", false);
|
||||
let has_ssse3 = settings.add_bool("has_ssse3", "SSSE3: CPUID.01H:ECX.SSSE3[bit 9]", false);
|
||||
let has_sse41 = settings.add_bool("has_sse41", "SSE4.1: CPUID.01H:ECX.SSE4_1[bit 19]", false);
|
||||
let has_sse42 = settings.add_bool("has_sse42", "SSE4.2: CPUID.01H:ECX.SSE4_2[bit 20]", false);
|
||||
let has_popcnt = settings.add_bool("has_popcnt", "POPCNT: CPUID.01H:ECX.POPCNT[bit 23]", false);
|
||||
settings.add_bool("has_avx", "AVX: CPUID.01H:ECX.AVX[bit 28]", false);
|
||||
|
||||
// CPUID.(EAX=07H, ECX=0H):EBX
|
||||
let has_bmi1 = settings.add_bool(
|
||||
"has_bmi1",
|
||||
"BMI1: CPUID.(EAX=07H, ECX=0H):EBX.BMI1[bit 3]",
|
||||
false,
|
||||
);
|
||||
let has_bmi2 = settings.add_bool(
|
||||
"has_bmi2",
|
||||
"BMI2: CPUID.(EAX=07H, ECX=0H):EBX.BMI2[bit 8]",
|
||||
false,
|
||||
);
|
||||
|
||||
// CPUID.EAX=80000001H:ECX
|
||||
let has_lzcnt = settings.add_bool(
|
||||
"has_lzcnt",
|
||||
"LZCNT: CPUID.EAX=80000001H:ECX.LZCNT[bit 5]",
|
||||
false,
|
||||
);
|
||||
|
||||
settings.add_predicate("use_sse41", predicate!(has_sse41));
|
||||
settings.add_predicate("use_sse42", predicate!(has_sse41 && has_sse42));
|
||||
settings.add_predicate("use_popcnt", predicate!(has_popcnt && has_sse42));
|
||||
settings.add_predicate("use_bmi1", predicate!(has_bmi1));
|
||||
settings.add_predicate("use_lznct", predicate!(has_lzcnt));
|
||||
|
||||
settings.add_preset("baseline", preset!());
|
||||
let nehalem = settings.add_preset(
|
||||
"nehalem",
|
||||
preset!(has_sse3 && has_ssse3 && has_sse41 && has_sse42 && has_popcnt),
|
||||
);
|
||||
let haswell = settings.add_preset(
|
||||
"haswell",
|
||||
preset!(nehalem && has_bmi1 && has_bmi2 && has_lzcnt),
|
||||
);
|
||||
let broadwell = settings.add_preset("broadwell", preset!(haswell));
|
||||
let skylake = settings.add_preset("skylake", preset!(broadwell));
|
||||
let cannonlake = settings.add_preset("cannonlake", preset!(skylake));
|
||||
settings.add_preset("icelake", preset!(cannonlake));
|
||||
settings.add_preset(
|
||||
"znver1",
|
||||
preset!(
|
||||
has_sse3
|
||||
&& has_ssse3
|
||||
&& has_sse41
|
||||
&& has_sse42
|
||||
&& has_popcnt
|
||||
&& has_bmi1
|
||||
&& has_bmi2
|
||||
&& has_lzcnt
|
||||
),
|
||||
);
|
||||
|
||||
settings.finish()
|
||||
}
|
||||
|
||||
fn define_registers(isa: &mut TargetIsaBuilder) {
|
||||
let builder = RegBankBuilder::new("IntRegs", "r")
|
||||
.units(16)
|
||||
.names(vec!["rax", "rcx", "rdx", "rbx", "rsp", "rbp", "rsi", "rdi"])
|
||||
.track_pressure(true);
|
||||
let int_regs = isa.add_reg_bank(builder);
|
||||
|
||||
let builder = RegBankBuilder::new("FloatRegs", "xmm")
|
||||
.units(16)
|
||||
.track_pressure(true);
|
||||
let float_regs = isa.add_reg_bank(builder);
|
||||
|
||||
let builder = RegBankBuilder::new("FlagRegs", "")
|
||||
.units(1)
|
||||
.names(vec!["rflags"])
|
||||
.track_pressure(false);
|
||||
let flag_reg = isa.add_reg_bank(builder);
|
||||
|
||||
let builder = RegClassBuilder::new_toplevel("GPR", int_regs);
|
||||
let gpr = isa.add_reg_class(builder);
|
||||
|
||||
let builder = RegClassBuilder::new_toplevel("FPR", float_regs);
|
||||
let fpr = isa.add_reg_class(builder);
|
||||
|
||||
let builder = RegClassBuilder::new_toplevel("FLAG", flag_reg);
|
||||
isa.add_reg_class(builder);
|
||||
|
||||
let builder = RegClassBuilder::subclass_of("GPR8", gpr, 0, 8);
|
||||
let gpr8 = isa.add_reg_class(builder);
|
||||
|
||||
let builder = RegClassBuilder::subclass_of("ABCD", gpr8, 0, 4);
|
||||
isa.add_reg_class(builder);
|
||||
|
||||
let builder = RegClassBuilder::subclass_of("FPR8", fpr, 0, 8);
|
||||
isa.add_reg_class(builder);
|
||||
}
|
||||
|
||||
pub fn define(shared_settings: &SettingGroup) -> TargetIsa {
|
||||
let settings = define_settings(shared_settings);
|
||||
|
||||
let mut isa = TargetIsaBuilder::new("x86", settings);
|
||||
|
||||
define_registers(&mut isa);
|
||||
|
||||
isa.finish()
|
||||
}
|
||||
13
cranelift/codegen/meta/src/lib.rs
Normal file
13
cranelift/codegen/meta/src/lib.rs
Normal file
@@ -0,0 +1,13 @@
|
||||
#[macro_use]
|
||||
mod cdsl;
|
||||
|
||||
pub mod error;
|
||||
pub mod gen_registers;
|
||||
pub mod gen_settings;
|
||||
pub mod gen_types;
|
||||
pub mod isa;
|
||||
|
||||
mod base;
|
||||
mod constant_hash;
|
||||
mod srcgen;
|
||||
mod unique_table;
|
||||
382
cranelift/codegen/meta/src/srcgen.rs
Normal file
382
cranelift/codegen/meta/src/srcgen.rs
Normal file
@@ -0,0 +1,382 @@
|
||||
//! Source code generator.
|
||||
//!
|
||||
//! The `srcgen` module contains generic helper routines and classes for
|
||||
//! generating source code.
|
||||
|
||||
use std::cmp;
|
||||
use std::collections::{BTreeMap, BTreeSet};
|
||||
use std::fs;
|
||||
use std::io::Write;
|
||||
use std::path;
|
||||
|
||||
use crate::error;
|
||||
|
||||
static SHIFTWIDTH: usize = 4;
|
||||
|
||||
pub struct Formatter {
|
||||
indent: usize,
|
||||
lines: Vec<String>,
|
||||
}
|
||||
|
||||
impl Formatter {
|
||||
/// Source code formatter class. Used to collect source code to be written
|
||||
/// to a file, and keep track of indentation.
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
indent: 0,
|
||||
lines: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Increase current indentation level by one.
|
||||
pub fn indent_push(&mut self) {
|
||||
self.indent += 1;
|
||||
}
|
||||
|
||||
/// Decrease indentation by one level.
|
||||
pub fn indent_pop(&mut self) {
|
||||
assert!(self.indent > 0, "Already at top level indentation");
|
||||
self.indent -= 1;
|
||||
}
|
||||
|
||||
pub fn indent<T, F: FnOnce(&mut Formatter) -> T>(&mut self, f: F) -> T {
|
||||
self.indent_push();
|
||||
let ret = f(self);
|
||||
self.indent_pop();
|
||||
ret
|
||||
}
|
||||
|
||||
/// Get the current whitespace indentation in the form of a String.
|
||||
fn get_indent(&self) -> String {
|
||||
if self.indent == 0 {
|
||||
String::new()
|
||||
} else {
|
||||
format!("{:-1$}", " ", self.indent * SHIFTWIDTH)
|
||||
}
|
||||
}
|
||||
|
||||
/// Get a string containing whitespace outdented one level. Used for
|
||||
/// lines of code that are inside a single indented block.
|
||||
fn _get_outdent(&mut self) -> String {
|
||||
self.indent_push();
|
||||
let s = self.get_indent();
|
||||
self.indent_pop();
|
||||
s
|
||||
}
|
||||
|
||||
/// Add an indented line.
|
||||
pub fn line(&mut self, contents: &str) {
|
||||
let indented_line = format!("{}{}\n", self.get_indent(), contents);
|
||||
self.lines.push(indented_line);
|
||||
}
|
||||
|
||||
/// Emit a line outdented one level.
|
||||
pub fn _outdented_line(&mut self, s: &str) {
|
||||
let new_line = format!("{}{}", self._get_outdent(), s);
|
||||
self.lines.push(new_line);
|
||||
}
|
||||
|
||||
/// Write `self.lines` to a file.
|
||||
pub fn update_file(&self, filename: &str, directory: &str) -> Result<(), error::Error> {
|
||||
#[cfg(target_family = "windows")]
|
||||
let path_str = format!("{}\\{}", directory, filename);
|
||||
#[cfg(not(target_family = "windows"))]
|
||||
let path_str = format!("{}/{}", directory, filename);
|
||||
|
||||
let path = path::Path::new(&path_str);
|
||||
let mut f = fs::File::create(path)?;
|
||||
|
||||
for l in self.lines.iter().map(|l| l.as_bytes()) {
|
||||
f.write_all(l)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Add one or more lines after stripping common indentation.
|
||||
pub fn _multi_line(&mut self, s: &str) {
|
||||
parse_multiline(s).into_iter().for_each(|l| self.line(&l));
|
||||
}
|
||||
|
||||
/// Add a comment line.
|
||||
pub fn comment(&mut self, s: &str) {
|
||||
let commented_line = format!("// {}", s);
|
||||
self.line(&commented_line);
|
||||
}
|
||||
|
||||
/// Add a (multi-line) documentation comment.
|
||||
pub fn doc_comment(&mut self, contents: &str) {
|
||||
parse_multiline(contents)
|
||||
.iter()
|
||||
.map(|l| {
|
||||
if l.len() == 0 {
|
||||
"///".into()
|
||||
} else {
|
||||
format!("/// {}", l)
|
||||
}
|
||||
})
|
||||
.for_each(|s| self.line(s.as_str()));
|
||||
}
|
||||
|
||||
/// Add a match expression.
|
||||
pub fn add_match(&mut self, m: Match) {
|
||||
self.line(&format!("match {} {{", m.expr));
|
||||
self.indent(|fmt| {
|
||||
for (&(ref fields, ref body), ref names) in m.arms.iter() {
|
||||
// name { fields } | name { fields } => { body }
|
||||
let conditions: Vec<String> = names
|
||||
.iter()
|
||||
.map(|name| {
|
||||
if fields.len() > 0 {
|
||||
format!("{} {{ {} }}", name, fields.join(", "))
|
||||
} else {
|
||||
name.clone()
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
let lhs = conditions.join(" | ");
|
||||
fmt.line(&format!("{} => {{", lhs));
|
||||
fmt.indent(|fmt| {
|
||||
fmt.line(body);
|
||||
});
|
||||
fmt.line("}");
|
||||
}
|
||||
});
|
||||
self.line("}");
|
||||
}
|
||||
}
|
||||
|
||||
/// Compute the indentation of s, or None of an empty line.
|
||||
fn _indent(s: &str) -> Option<usize> {
|
||||
if s.is_empty() {
|
||||
None
|
||||
} else {
|
||||
let t = s.trim_start();
|
||||
Some(s.len() - t.len())
|
||||
}
|
||||
}
|
||||
|
||||
/// Given a multi-line string, split it into a sequence of lines after
|
||||
/// stripping a common indentation. This is useful for strings defined with
|
||||
/// doc strings.
|
||||
fn parse_multiline(s: &str) -> Vec<String> {
|
||||
// Convert tabs into spaces.
|
||||
let expanded_tab = format!("{:-1$}", " ", SHIFTWIDTH);
|
||||
let lines: Vec<String> = s.lines().map(|l| l.replace("\t", &expanded_tab)).collect();
|
||||
|
||||
// Determine minimum indentation, ignoring the first line and empty lines.
|
||||
let indent = lines
|
||||
.iter()
|
||||
.skip(1)
|
||||
.filter(|l| !l.trim().is_empty())
|
||||
.map(|l| l.len() - l.trim_start().len())
|
||||
.min();
|
||||
|
||||
// Strip off leading blank lines.
|
||||
let mut lines_iter = lines.iter().skip_while(|l| l.is_empty());
|
||||
let mut trimmed = Vec::with_capacity(lines.len());
|
||||
|
||||
// Remove indentation (first line is special)
|
||||
if let Some(s) = lines_iter.next().map(|l| l.trim()).map(|l| l.to_string()) {
|
||||
trimmed.push(s);
|
||||
}
|
||||
|
||||
// Remove trailing whitespace from other lines.
|
||||
let mut other_lines = if let Some(indent) = indent {
|
||||
// Note that empty lines may have fewer than `indent` chars.
|
||||
lines_iter
|
||||
.map(|l| &l[cmp::min(indent, l.len())..])
|
||||
.map(|l| l.trim_end())
|
||||
.map(|l| l.to_string())
|
||||
.collect::<Vec<_>>()
|
||||
} else {
|
||||
lines_iter
|
||||
.map(|l| l.trim_end())
|
||||
.map(|l| l.to_string())
|
||||
.collect::<Vec<_>>()
|
||||
};
|
||||
|
||||
trimmed.append(&mut other_lines);
|
||||
|
||||
// Strip off trailing blank lines.
|
||||
while let Some(s) = trimmed.pop() {
|
||||
if s.is_empty() {
|
||||
continue;
|
||||
} else {
|
||||
trimmed.push(s);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
trimmed
|
||||
}
|
||||
|
||||
/// Match formatting class.
|
||||
///
|
||||
/// Match objects collect all the information needed to emit a Rust `match`
|
||||
/// expression, automatically deduplicating overlapping identical arms.
|
||||
///
|
||||
/// Note that this class is ignorant of Rust types, and considers two fields
|
||||
/// with the same name to be equivalent. BTreeMap/BTreeSet are used to
|
||||
/// represent the arms in order to make the order deterministic.
|
||||
pub struct Match {
|
||||
expr: String,
|
||||
arms: BTreeMap<(Vec<String>, String), BTreeSet<String>>,
|
||||
}
|
||||
|
||||
impl Match {
|
||||
/// Create a new match statement on `expr`.
|
||||
pub fn new<T: Into<String>>(expr: T) -> Self {
|
||||
Self {
|
||||
expr: expr.into(),
|
||||
arms: BTreeMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Add an arm to the Match statement.
|
||||
pub fn arm<T: Into<String>>(&mut self, name: T, fields: Vec<T>, body: T) {
|
||||
// let key = (fields, body);
|
||||
let body = body.into();
|
||||
let fields = fields.into_iter().map(|x| x.into()).collect();
|
||||
let match_arm = self
|
||||
.arms
|
||||
.entry((fields, body))
|
||||
.or_insert_with(BTreeSet::new);
|
||||
match_arm.insert(name.into());
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod srcgen_tests {
|
||||
use super::parse_multiline;
|
||||
use super::Formatter;
|
||||
use super::Match;
|
||||
|
||||
fn from_raw_string<S: Into<String>>(s: S) -> Vec<String> {
|
||||
s.into()
|
||||
.trim()
|
||||
.split("\n")
|
||||
.into_iter()
|
||||
.map(|x| format!("{}\n", x))
|
||||
.collect()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn adding_arms_works() {
|
||||
let mut m = Match::new("x");
|
||||
m.arm("Orange", vec!["a", "b"], "some body");
|
||||
m.arm("Yellow", vec!["a", "b"], "some body");
|
||||
m.arm("Green", vec!["a", "b"], "different body");
|
||||
m.arm("Blue", vec!["x", "y"], "some body");
|
||||
assert_eq!(m.arms.len(), 3);
|
||||
|
||||
let mut fmt = Formatter::new();
|
||||
fmt.add_match(m);
|
||||
|
||||
let expected_lines = from_raw_string(
|
||||
r#"
|
||||
match x {
|
||||
Green { a, b } => {
|
||||
different body
|
||||
}
|
||||
Orange { a, b } | Yellow { a, b } => {
|
||||
some body
|
||||
}
|
||||
Blue { x, y } => {
|
||||
some body
|
||||
}
|
||||
}
|
||||
"#,
|
||||
);
|
||||
assert_eq!(fmt.lines, expected_lines);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_multiline_works() {
|
||||
let input = "\n hello\n world\n";
|
||||
let expected = vec!["hello", "world"];
|
||||
let output = parse_multiline(input);
|
||||
assert_eq!(output, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn formatter_basic_example_works() {
|
||||
let mut fmt = Formatter::new();
|
||||
fmt.line("Hello line 1");
|
||||
fmt.indent_push();
|
||||
fmt.comment("Nested comment");
|
||||
fmt.indent_pop();
|
||||
fmt.line("Back home again");
|
||||
let expected_lines = vec![
|
||||
"Hello line 1\n",
|
||||
" // Nested comment\n",
|
||||
"Back home again\n",
|
||||
];
|
||||
assert_eq!(fmt.lines, expected_lines);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn get_indent_works() {
|
||||
let mut fmt = Formatter::new();
|
||||
let expected_results = vec!["", " ", " ", ""];
|
||||
|
||||
let actual_results = Vec::with_capacity(4);
|
||||
(0..3).for_each(|_| {
|
||||
fmt.get_indent();
|
||||
fmt.indent_push();
|
||||
});
|
||||
(0..3).for_each(|_| fmt.indent_pop());
|
||||
fmt.get_indent();
|
||||
|
||||
actual_results
|
||||
.into_iter()
|
||||
.zip(expected_results.into_iter())
|
||||
.for_each(|(actual, expected): (String, &str)| assert_eq!(&actual, expected));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fmt_can_add_type_to_lines() {
|
||||
let mut fmt = Formatter::new();
|
||||
fmt.line(&format!("pub const {}: Type = Type({:#x});", "example", 0,));
|
||||
let expected_lines = vec!["pub const example: Type = Type(0x0);\n"];
|
||||
assert_eq!(fmt.lines, expected_lines);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fmt_can_add_indented_line() {
|
||||
let mut fmt = Formatter::new();
|
||||
fmt.line("hello");
|
||||
fmt.indent_push();
|
||||
fmt.line("world");
|
||||
let expected_lines = vec!["hello\n", " world\n"];
|
||||
assert_eq!(fmt.lines, expected_lines);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fmt_can_add_doc_comments() {
|
||||
let mut fmt = Formatter::new();
|
||||
fmt.doc_comment("documentation\nis\ngood");
|
||||
let expected_lines = vec!["/// documentation\n", "/// is\n", "/// good\n"];
|
||||
assert_eq!(fmt.lines, expected_lines);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fmt_can_add_doc_comments_with_empty_lines() {
|
||||
let mut fmt = Formatter::new();
|
||||
fmt.doc_comment(
|
||||
r#"documentation
|
||||
can be really good.
|
||||
|
||||
If you stick to writing it.
|
||||
"#,
|
||||
);
|
||||
let expected_lines = from_raw_string(
|
||||
r#"
|
||||
/// documentation
|
||||
/// can be really good.
|
||||
///
|
||||
/// If you stick to writing it."#,
|
||||
);
|
||||
assert_eq!(fmt.lines, expected_lines);
|
||||
}
|
||||
}
|
||||
68
cranelift/codegen/meta/src/unique_table.rs
Normal file
68
cranelift/codegen/meta/src/unique_table.rs
Normal file
@@ -0,0 +1,68 @@
|
||||
use std::slice;
|
||||
|
||||
/// A table of sequences which tries to avoid common subsequences.
|
||||
pub struct UniqueTable<T: PartialEq + Clone> {
|
||||
table: Vec<T>,
|
||||
}
|
||||
|
||||
impl<T: PartialEq + Clone> UniqueTable<T> {
|
||||
pub fn new() -> Self {
|
||||
Self { table: Vec::new() }
|
||||
}
|
||||
pub fn add(&mut self, values: &Vec<T>) -> usize {
|
||||
if let Some(offset) = find_subsequence(values, &self.table) {
|
||||
offset
|
||||
} else {
|
||||
let offset = self.table.len();
|
||||
self.table.extend((*values).clone());
|
||||
offset
|
||||
}
|
||||
}
|
||||
pub fn len(&self) -> usize {
|
||||
self.table.len()
|
||||
}
|
||||
pub fn iter(&self) -> slice::Iter<T> {
|
||||
self.table.iter()
|
||||
}
|
||||
}
|
||||
|
||||
/// Try to find the subsequence `sub` in the `whole` sequence. Returns None if
|
||||
/// it's not been found, or Some(index) if it has been. Naive implementation
|
||||
/// until proven we need something better.
|
||||
fn find_subsequence<T: PartialEq>(sub: &Vec<T>, whole: &Vec<T>) -> Option<usize> {
|
||||
assert!(sub.len() > 0);
|
||||
// We want i + sub.len() <= whole.len(), i.e. i < whole.len() + 1 - sub.len().
|
||||
if whole.len() < sub.len() {
|
||||
return None;
|
||||
}
|
||||
let max = whole.len() + 1 - sub.len();
|
||||
for i in 0..max {
|
||||
let mut found: Option<usize> = Some(i);
|
||||
for j in 0..sub.len() {
|
||||
if sub[j] != whole[i + j] {
|
||||
found = None;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if found.is_some() {
|
||||
return found;
|
||||
}
|
||||
}
|
||||
return None;
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_find_subsequence() {
|
||||
assert_eq!(find_subsequence(&vec![1], &vec![4]), None);
|
||||
assert_eq!(find_subsequence(&vec![1], &vec![1]), Some(0));
|
||||
assert_eq!(find_subsequence(&vec![1, 2], &vec![1]), None);
|
||||
assert_eq!(find_subsequence(&vec![1, 2], &vec![1, 2]), Some(0));
|
||||
assert_eq!(find_subsequence(&vec![1, 2], &vec![1, 3]), None);
|
||||
assert_eq!(find_subsequence(&vec![1, 2], &vec![0, 1, 2]), Some(1));
|
||||
assert_eq!(find_subsequence(&vec![1, 2], &vec![0, 1, 3, 1]), None);
|
||||
assert_eq!(find_subsequence(&vec![1, 2], &vec![0, 1, 3, 1, 2]), Some(3));
|
||||
assert_eq!(
|
||||
find_subsequence(&vec![1, 1, 3], &vec![1, 1, 1, 3, 3]),
|
||||
Some(1)
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user