[build] Implement registers code generation in the Rust meta crate;
This commit is contained in:
committed by
Dan Gohman
parent
4f2d7dd54f
commit
b7f2acf0ea
@@ -24,7 +24,11 @@ use meta::isa::Isa;
|
|||||||
use std::env;
|
use std::env;
|
||||||
use std::process;
|
use std::process;
|
||||||
|
|
||||||
|
use std::time::Instant;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
let start_time = Instant::now();
|
||||||
|
|
||||||
let out_dir = env::var("OUT_DIR").expect("The OUT_DIR environment variable must be set");
|
let out_dir = env::var("OUT_DIR").expect("The OUT_DIR environment variable must be set");
|
||||||
let target_triple = env::var("TARGET").expect("The TARGET environment variable must be set");
|
let target_triple = env::var("TARGET").expect("The TARGET environment variable must be set");
|
||||||
let cranelift_targets = env::var("CRANELIFT_TARGETS").ok();
|
let cranelift_targets = env::var("CRANELIFT_TARGETS").ok();
|
||||||
@@ -35,7 +39,7 @@ fn main() {
|
|||||||
match isa_targets(cranelift_targets, &target_triple) {
|
match isa_targets(cranelift_targets, &target_triple) {
|
||||||
Ok(isa_targets) => {
|
Ok(isa_targets) => {
|
||||||
for isa in &isa_targets {
|
for isa in &isa_targets {
|
||||||
println!("cargo:rustc-cfg=build_{}", isa.name());
|
println!("cargo:rustc-cfg=build_{}", isa.to_string());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
@@ -44,8 +48,6 @@ fn main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
println!("Build script generating files in {}", out_dir);
|
|
||||||
|
|
||||||
let cur_dir = env::current_dir().expect("Can't access current working directory");
|
let cur_dir = env::current_dir().expect("Can't access current working directory");
|
||||||
let crate_dir = cur_dir.as_path();
|
let crate_dir = cur_dir.as_path();
|
||||||
|
|
||||||
@@ -81,10 +83,28 @@ fn main() {
|
|||||||
// Now that the Python build process is complete, generate files that are
|
// Now that the Python build process is complete, generate files that are
|
||||||
// emitted by the `meta` crate.
|
// emitted by the `meta` crate.
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
|
let isas = meta::isa::define_all();
|
||||||
|
|
||||||
if let Err(err) = meta::gen_types::generate("types.rs", &out_dir) {
|
if let Err(err) = meta::gen_types::generate("types.rs", &out_dir) {
|
||||||
eprintln!("Error: {}", err);
|
eprintln!("Error: {}", err);
|
||||||
process::exit(1);
|
process::exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for isa in isas {
|
||||||
|
if let Err(err) = meta::gen_registers::generate(isa, "new_registers", &out_dir) {
|
||||||
|
eprintln!("Error: {}", err);
|
||||||
|
process::exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
println!(
|
||||||
|
"cargo:warning=Cranelift meta-build step took {:?}",
|
||||||
|
Instant::now() - start_time
|
||||||
|
);
|
||||||
|
println!(
|
||||||
|
"cargo:warning=Meta-build script generated files in {}",
|
||||||
|
out_dir
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn identify_python() -> &'static str {
|
fn identify_python() -> &'static str {
|
||||||
|
|||||||
@@ -87,7 +87,6 @@ class RegBank(object):
|
|||||||
self.names = names
|
self.names = names
|
||||||
self.classes = list() # type: List[RegClass]
|
self.classes = list() # type: List[RegClass]
|
||||||
self.toprcs = list() # type: List[RegClass]
|
self.toprcs = list() # type: List[RegClass]
|
||||||
self.first_toprc_index = None # type: int
|
|
||||||
|
|
||||||
assert len(names) <= units
|
assert len(names) <= units
|
||||||
|
|
||||||
@@ -248,7 +247,7 @@ class RegClass(object):
|
|||||||
def intersect(self, other):
|
def intersect(self, other):
|
||||||
# type: (RegClass) -> RCTup
|
# type: (RegClass) -> RCTup
|
||||||
"""
|
"""
|
||||||
Get a tuple representing the intersction of two register classes.
|
Get a tuple representing the intersection of two register classes.
|
||||||
|
|
||||||
Returns `None` if the two classes are disjoint.
|
Returns `None` if the two classes are disjoint.
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -7,6 +7,9 @@ license = "Apache-2.0 WITH LLVM-exception"
|
|||||||
repository = "https://github.com/CraneStation/cranelift"
|
repository = "https://github.com/CraneStation/cranelift"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
cranelift-entity = { path = "../../entity", version = "0.22.0", default-features = false }
|
||||||
|
|
||||||
[badges]
|
[badges]
|
||||||
maintenance = { status = "experimental" }
|
maintenance = { status = "experimental" }
|
||||||
travis-ci = { repository = "CraneStation/cranelift" }
|
travis-ci = { repository = "CraneStation/cranelift" }
|
||||||
|
|||||||
142
lib/codegen/meta/src/cdsl/isa.rs
Normal file
142
lib/codegen/meta/src/cdsl/isa.rs
Normal file
@@ -0,0 +1,142 @@
|
|||||||
|
use cranelift_entity::PrimaryMap;
|
||||||
|
|
||||||
|
use super::regs::{
|
||||||
|
RegBank, RegBankBuilder, RegBankIndex, RegClass, RegClassBuilder, RegClassIndex,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct TargetIsa {
|
||||||
|
pub name: &'static str,
|
||||||
|
pub reg_banks: PrimaryMap<RegBankIndex, RegBank>,
|
||||||
|
pub reg_classes: PrimaryMap<RegClassIndex, RegClass>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TargetIsa {
|
||||||
|
pub fn new(name: &'static str) -> Self {
|
||||||
|
Self {
|
||||||
|
name,
|
||||||
|
reg_banks: PrimaryMap::new(),
|
||||||
|
reg_classes: PrimaryMap::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_reg_bank(&mut self, builder: RegBankBuilder) -> RegBankIndex {
|
||||||
|
let first_unit = if self.reg_banks.len() == 0 {
|
||||||
|
0
|
||||||
|
} else {
|
||||||
|
let last = &self.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.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 reg_bank_units = self.reg_banks.get(builder.bank).unwrap().units;
|
||||||
|
|
||||||
|
let start = builder.start;
|
||||||
|
assert!(start < reg_bank_units);
|
||||||
|
|
||||||
|
let count = if builder.count != 0 {
|
||||||
|
builder.count
|
||||||
|
} else {
|
||||||
|
reg_bank_units / builder.width
|
||||||
|
};
|
||||||
|
|
||||||
|
let reg_class_index = builder.index;
|
||||||
|
assert!(
|
||||||
|
self.reg_classes.next_key() == reg_class_index,
|
||||||
|
"should have inserted RegClass where expected"
|
||||||
|
);
|
||||||
|
|
||||||
|
let reg_class = RegClass::new(
|
||||||
|
builder.name,
|
||||||
|
reg_class_index,
|
||||||
|
builder.width,
|
||||||
|
builder.bank,
|
||||||
|
builder.toprc,
|
||||||
|
count,
|
||||||
|
start,
|
||||||
|
);
|
||||||
|
self.reg_classes.push(reg_class);
|
||||||
|
|
||||||
|
let reg_bank = self.reg_banks.get_mut(builder.bank).unwrap();
|
||||||
|
reg_bank.classes.push(reg_class_index);
|
||||||
|
|
||||||
|
reg_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 check(&self) {
|
||||||
|
for reg_bank in self.reg_banks.values() {
|
||||||
|
for i1 in reg_bank.classes.iter() {
|
||||||
|
for i2 in reg_bank.classes.iter() {
|
||||||
|
if i1 >= i2 {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let rc1 = self.reg_classes.get(*i1).unwrap();
|
||||||
|
let rc2 = self.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.reg_classes
|
||||||
|
.get(*i1)
|
||||||
|
.unwrap()
|
||||||
|
.subclasses
|
||||||
|
.iter()
|
||||||
|
.find(|x| **x == *i2)
|
||||||
|
.is_some()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,6 +3,8 @@
|
|||||||
//! This module defines the classes that are used to define Cranelift
|
//! This module defines the classes that are used to define Cranelift
|
||||||
//! instructions and other entities.
|
//! instructions and other entities.
|
||||||
|
|
||||||
|
pub mod isa;
|
||||||
|
pub mod regs;
|
||||||
pub mod types;
|
pub mod types;
|
||||||
|
|
||||||
/// Convert the string `s` to CamelCase.
|
/// Convert the string `s` to CamelCase.
|
||||||
|
|||||||
199
lib/codegen/meta/src/cdsl/regs.rs
Normal file
199
lib/codegen/meta/src/cdsl/regs.rs
Normal file
@@ -0,0 +1,199 @@
|
|||||||
|
use cranelift_entity::EntityRef;
|
||||||
|
|
||||||
|
use super::isa::TargetIsa;
|
||||||
|
|
||||||
|
#[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 struct RegClassBuilder {
|
||||||
|
pub name: &'static str,
|
||||||
|
pub index: RegClassIndex,
|
||||||
|
pub width: u8,
|
||||||
|
pub bank: RegBankIndex,
|
||||||
|
pub toprc: RegClassIndex,
|
||||||
|
pub count: u8,
|
||||||
|
pub start: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RegClassBuilder {
|
||||||
|
pub fn new_toplevel(isa: &mut TargetIsa, name: &'static str, bank: RegBankIndex) -> Self {
|
||||||
|
let index = isa.reg_classes.next_key();
|
||||||
|
|
||||||
|
// Add it to the top-level register classes of the register bank.
|
||||||
|
isa.reg_banks.get_mut(bank).unwrap().toprcs.push(index);
|
||||||
|
|
||||||
|
Self {
|
||||||
|
name,
|
||||||
|
index,
|
||||||
|
width: 1,
|
||||||
|
bank,
|
||||||
|
toprc: index,
|
||||||
|
count: 0,
|
||||||
|
start: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn subclass_of(
|
||||||
|
isa: &mut TargetIsa,
|
||||||
|
name: &'static str,
|
||||||
|
parent_index: RegClassIndex,
|
||||||
|
start: u8,
|
||||||
|
stop: u8,
|
||||||
|
) -> Self {
|
||||||
|
assert!(stop >= start);
|
||||||
|
|
||||||
|
let index = isa.reg_classes.next_key();
|
||||||
|
|
||||||
|
let toprc = isa.reg_classes.get(parent_index).unwrap().toprc;
|
||||||
|
for reg_class in isa.reg_classes.values_mut() {
|
||||||
|
if reg_class.toprc == toprc {
|
||||||
|
reg_class.subclasses.push(index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let parent = &isa.reg_classes.get(parent_index).unwrap();
|
||||||
|
Self {
|
||||||
|
name,
|
||||||
|
count: stop - start,
|
||||||
|
width: parent.width,
|
||||||
|
start: parent.start + start * parent.width,
|
||||||
|
bank: parent.bank,
|
||||||
|
toprc: parent.toprc,
|
||||||
|
index,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn count(mut self, count: u8) -> Self {
|
||||||
|
self.count = count;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn width(mut self, width: u8) -> Self {
|
||||||
|
self.width = 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
|
||||||
|
}
|
||||||
|
}
|
||||||
140
lib/codegen/meta/src/gen_registers.rs
Normal file
140
lib/codegen/meta/src/gen_registers.rs
Normal file
@@ -0,0 +1,140 @@
|
|||||||
|
use cdsl::isa::TargetIsa;
|
||||||
|
use cdsl::regs::{RegBank, RegClass};
|
||||||
|
use cranelift_entity::EntityRef;
|
||||||
|
use error;
|
||||||
|
use srcgen::Formatter;
|
||||||
|
|
||||||
|
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) -> Result<(), error::Error> {
|
||||||
|
// 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("}");
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
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(())
|
||||||
|
}
|
||||||
39
lib/codegen/meta/src/isa/arm32/mod.rs
Normal file
39
lib/codegen/meta/src/isa/arm32/mod.rs
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
use cdsl::regs::{RegBankBuilder, RegClassBuilder};
|
||||||
|
use isa;
|
||||||
|
|
||||||
|
pub fn define() -> isa::TargetIsa {
|
||||||
|
let mut isa = isa::TargetIsa::new("arm32");
|
||||||
|
|
||||||
|
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(&mut isa, "S", float_regs).count(32);
|
||||||
|
isa.add_reg_class(builder);
|
||||||
|
|
||||||
|
let builder = RegClassBuilder::new_toplevel(&mut isa, "D", float_regs).width(2);
|
||||||
|
isa.add_reg_class(builder);
|
||||||
|
|
||||||
|
let builder = RegClassBuilder::new_toplevel(&mut isa, "Q", float_regs).width(4);
|
||||||
|
isa.add_reg_class(builder);
|
||||||
|
|
||||||
|
let builder = RegClassBuilder::new_toplevel(&mut isa, "GPR", int_regs);
|
||||||
|
isa.add_reg_class(builder);
|
||||||
|
|
||||||
|
let builder = RegClassBuilder::new_toplevel(&mut isa, "FLAG", flag_reg);
|
||||||
|
isa.add_reg_class(builder);
|
||||||
|
|
||||||
|
isa
|
||||||
|
}
|
||||||
35
lib/codegen/meta/src/isa/arm64/mod.rs
Normal file
35
lib/codegen/meta/src/isa/arm64/mod.rs
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
use cdsl::regs::{RegBankBuilder, RegClassBuilder};
|
||||||
|
use isa;
|
||||||
|
|
||||||
|
pub fn define() -> isa::TargetIsa {
|
||||||
|
let mut isa = isa::TargetIsa::new("arm64");
|
||||||
|
|
||||||
|
// 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(&mut isa, "GPR", int_regs);
|
||||||
|
isa.add_reg_class(builder);
|
||||||
|
|
||||||
|
let builder = RegClassBuilder::new_toplevel(&mut isa, "FPR", float_regs);
|
||||||
|
isa.add_reg_class(builder);
|
||||||
|
|
||||||
|
let builder = RegClassBuilder::new_toplevel(&mut isa, "FLAG", flag_reg);
|
||||||
|
isa.add_reg_class(builder);
|
||||||
|
|
||||||
|
isa
|
||||||
|
}
|
||||||
@@ -1,3 +1,11 @@
|
|||||||
|
use cdsl::isa::TargetIsa;
|
||||||
|
use std::fmt;
|
||||||
|
|
||||||
|
mod arm32;
|
||||||
|
mod arm64;
|
||||||
|
mod riscv;
|
||||||
|
mod x86;
|
||||||
|
|
||||||
/// Represents known ISA target.
|
/// Represents known ISA target.
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone)]
|
||||||
pub enum Isa {
|
pub enum Isa {
|
||||||
@@ -13,7 +21,7 @@ impl Isa {
|
|||||||
Isa::all()
|
Isa::all()
|
||||||
.iter()
|
.iter()
|
||||||
.cloned()
|
.cloned()
|
||||||
.filter(|isa| isa.name() == name)
|
.filter(|isa| isa.to_string() == name)
|
||||||
.next()
|
.next()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -31,16 +39,6 @@ impl Isa {
|
|||||||
[Isa::Riscv, Isa::X86, Isa::Arm32, Isa::Arm64]
|
[Isa::Riscv, Isa::X86, Isa::Arm32, Isa::Arm64]
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns name of the isa target.
|
|
||||||
pub fn name(&self) -> &'static str {
|
|
||||||
match *self {
|
|
||||||
Isa::Riscv => "riscv",
|
|
||||||
Isa::X86 => "x86",
|
|
||||||
Isa::Arm32 => "arm32",
|
|
||||||
Isa::Arm64 => "arm64",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Checks if arch is applicable for the isa target.
|
/// Checks if arch is applicable for the isa target.
|
||||||
fn is_arch_applicable(&self, arch: &str) -> bool {
|
fn is_arch_applicable(&self, arch: &str) -> bool {
|
||||||
match *self {
|
match *self {
|
||||||
@@ -51,3 +49,27 @@ impl Isa {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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() -> Vec<TargetIsa> {
|
||||||
|
let isas = vec![
|
||||||
|
riscv::define(),
|
||||||
|
arm32::define(),
|
||||||
|
arm64::define(),
|
||||||
|
x86::define(),
|
||||||
|
];
|
||||||
|
for isa in isas.iter() {
|
||||||
|
isa.check();
|
||||||
|
}
|
||||||
|
isas
|
||||||
|
}
|
||||||
|
|||||||
24
lib/codegen/meta/src/isa/riscv/mod.rs
Normal file
24
lib/codegen/meta/src/isa/riscv/mod.rs
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
use cdsl::regs::{RegBankBuilder, RegClassBuilder};
|
||||||
|
use isa;
|
||||||
|
|
||||||
|
pub fn define() -> isa::TargetIsa {
|
||||||
|
let mut isa = isa::TargetIsa::new("riscv");
|
||||||
|
|
||||||
|
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(&mut isa, "GPR", int_regs);
|
||||||
|
isa.add_reg_class(builder);
|
||||||
|
|
||||||
|
let builder = RegClassBuilder::new_toplevel(&mut isa, "FPR", float_regs);
|
||||||
|
isa.add_reg_class(builder);
|
||||||
|
|
||||||
|
isa
|
||||||
|
}
|
||||||
43
lib/codegen/meta/src/isa/x86/mod.rs
Normal file
43
lib/codegen/meta/src/isa/x86/mod.rs
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
use cdsl::regs::{RegBankBuilder, RegClassBuilder};
|
||||||
|
use isa;
|
||||||
|
|
||||||
|
pub fn define() -> isa::TargetIsa {
|
||||||
|
let mut isa = isa::TargetIsa::new("x86");
|
||||||
|
|
||||||
|
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(&mut isa, "GPR", int_regs);
|
||||||
|
let gpr = isa.add_reg_class(builder);
|
||||||
|
|
||||||
|
let builder = RegClassBuilder::new_toplevel(&mut isa, "FPR", float_regs);
|
||||||
|
let fpr = isa.add_reg_class(builder);
|
||||||
|
|
||||||
|
let builder = RegClassBuilder::new_toplevel(&mut isa, "FLAG", flag_reg);
|
||||||
|
isa.add_reg_class(builder);
|
||||||
|
|
||||||
|
let builder = RegClassBuilder::subclass_of(&mut isa, "GPR8", gpr, 0, 8);
|
||||||
|
let gpr8 = isa.add_reg_class(builder);
|
||||||
|
|
||||||
|
let builder = RegClassBuilder::subclass_of(&mut isa, "ABCD", gpr8, 0, 4);
|
||||||
|
isa.add_reg_class(builder);
|
||||||
|
|
||||||
|
let builder = RegClassBuilder::subclass_of(&mut isa, "FPR8", fpr, 0, 8);
|
||||||
|
isa.add_reg_class(builder);
|
||||||
|
|
||||||
|
isa
|
||||||
|
}
|
||||||
@@ -1,4 +1,8 @@
|
|||||||
|
#[macro_use]
|
||||||
|
extern crate cranelift_entity;
|
||||||
|
|
||||||
pub mod error;
|
pub mod error;
|
||||||
|
pub mod gen_registers;
|
||||||
pub mod gen_types;
|
pub mod gen_types;
|
||||||
pub mod isa;
|
pub mod isa;
|
||||||
|
|
||||||
|
|||||||
@@ -12,24 +12,6 @@ use error;
|
|||||||
|
|
||||||
static SHIFTWIDTH: usize = 4;
|
static SHIFTWIDTH: usize = 4;
|
||||||
|
|
||||||
struct _IndentedScope {
|
|
||||||
fmt: Formatter,
|
|
||||||
after: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl _IndentedScope {
|
|
||||||
fn _enter(&mut self) {
|
|
||||||
self.fmt._indent_push();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn _exit(&mut self) {
|
|
||||||
self.fmt._indent_pop();
|
|
||||||
if let Some(ref s) = self.after {
|
|
||||||
self.fmt.line(&s);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Formatter {
|
pub struct Formatter {
|
||||||
indent: usize,
|
indent: usize,
|
||||||
lines: Vec<String>,
|
lines: Vec<String>,
|
||||||
@@ -46,16 +28,23 @@ impl Formatter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Increase current indentation level by one.
|
/// Increase current indentation level by one.
|
||||||
pub fn _indent_push(&mut self) {
|
pub fn indent_push(&mut self) {
|
||||||
self.indent += 1;
|
self.indent += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Decrease indentation by one level.
|
/// Decrease indentation by one level.
|
||||||
pub fn _indent_pop(&mut self) {
|
pub fn indent_pop(&mut self) {
|
||||||
assert!(self.indent > 0, "Already at top level indentation");
|
assert!(self.indent > 0, "Already at top level indentation");
|
||||||
self.indent -= 1;
|
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.
|
/// Get the current whitespace indentation in the form of a String.
|
||||||
fn get_indent(&self) -> String {
|
fn get_indent(&self) -> String {
|
||||||
if self.indent == 0 {
|
if self.indent == 0 {
|
||||||
@@ -68,9 +57,9 @@ impl Formatter {
|
|||||||
/// Get a string containing whitespace outdented one level. Used for
|
/// Get a string containing whitespace outdented one level. Used for
|
||||||
/// lines of code that are inside a single indented block.
|
/// lines of code that are inside a single indented block.
|
||||||
fn _get_outdent(&mut self) -> String {
|
fn _get_outdent(&mut self) -> String {
|
||||||
self._indent_push();
|
self.indent_push();
|
||||||
let s = self.get_indent();
|
let s = self.get_indent();
|
||||||
self._indent_pop();
|
self.indent_pop();
|
||||||
s
|
s
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -103,13 +92,6 @@ impl Formatter {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return a scope object for use with a `with` statement.
|
|
||||||
/// The optional `before` and `after` parameters are surrounding lines
|
|
||||||
/// which are *not* indented.
|
|
||||||
fn _indented(&self, _before: Option<&str>, _after: Option<&str>) -> _IndentedScope {
|
|
||||||
unimplemented!();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Add one or more lines after stripping common indentation.
|
/// Add one or more lines after stripping common indentation.
|
||||||
pub fn _multi_line(&mut self, s: &str) {
|
pub fn _multi_line(&mut self, s: &str) {
|
||||||
parse_multiline(s).into_iter().for_each(|l| self.line(&l));
|
parse_multiline(s).into_iter().for_each(|l| self.line(&l));
|
||||||
@@ -158,7 +140,6 @@ fn parse_multiline(s: &str) -> Vec<String> {
|
|||||||
.iter()
|
.iter()
|
||||||
.skip(1)
|
.skip(1)
|
||||||
.map(|l| l.len() - l.trim_left().len())
|
.map(|l| l.len() - l.trim_left().len())
|
||||||
.filter(|&i| i > 0)
|
|
||||||
.min();
|
.min();
|
||||||
|
|
||||||
// Strip off leading blank lines.
|
// Strip off leading blank lines.
|
||||||
@@ -257,9 +238,9 @@ mod srcgen_tests {
|
|||||||
fn formatter_basic_example_works() {
|
fn formatter_basic_example_works() {
|
||||||
let mut fmt = Formatter::new();
|
let mut fmt = Formatter::new();
|
||||||
fmt.line("Hello line 1");
|
fmt.line("Hello line 1");
|
||||||
fmt._indent_push();
|
fmt.indent_push();
|
||||||
fmt._comment("Nested comment");
|
fmt._comment("Nested comment");
|
||||||
fmt._indent_pop();
|
fmt.indent_pop();
|
||||||
fmt.line("Back home again");
|
fmt.line("Back home again");
|
||||||
let expected_lines = vec![
|
let expected_lines = vec![
|
||||||
"Hello line 1\n",
|
"Hello line 1\n",
|
||||||
@@ -277,9 +258,9 @@ mod srcgen_tests {
|
|||||||
let actual_results = Vec::with_capacity(4);
|
let actual_results = Vec::with_capacity(4);
|
||||||
(0..3).for_each(|_| {
|
(0..3).for_each(|_| {
|
||||||
fmt.get_indent();
|
fmt.get_indent();
|
||||||
fmt._indent_push();
|
fmt.indent_push();
|
||||||
});
|
});
|
||||||
(0..3).for_each(|_| fmt._indent_pop());
|
(0..3).for_each(|_| fmt.indent_pop());
|
||||||
fmt.get_indent();
|
fmt.get_indent();
|
||||||
|
|
||||||
actual_results
|
actual_results
|
||||||
@@ -300,7 +281,7 @@ mod srcgen_tests {
|
|||||||
fn fmt_can_add_indented_line() {
|
fn fmt_can_add_indented_line() {
|
||||||
let mut fmt = Formatter::new();
|
let mut fmt = Formatter::new();
|
||||||
fmt.line("hello");
|
fmt.line("hello");
|
||||||
fmt._indent_push();
|
fmt.indent_push();
|
||||||
fmt.line("world");
|
fmt.line("world");
|
||||||
let expected_lines = vec!["hello\n", " world\n"];
|
let expected_lines = vec!["hello\n", " world\n"];
|
||||||
assert_eq!(fmt.lines, expected_lines);
|
assert_eq!(fmt.lines, expected_lines);
|
||||||
|
|||||||
@@ -50,6 +50,11 @@ where
|
|||||||
self.elems.get(k.index())
|
self.elems.get(k.index())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the element at `k` if it exists, mutable version.
|
||||||
|
pub fn get_mut(&mut self, k: K) -> Option<&mut V> {
|
||||||
|
self.elems.get_mut(k.index())
|
||||||
|
}
|
||||||
|
|
||||||
/// Is this map completely empty?
|
/// Is this map completely empty?
|
||||||
pub fn is_empty(&self) -> bool {
|
pub fn is_empty(&self) -> bool {
|
||||||
self.elems.is_empty()
|
self.elems.is_empty()
|
||||||
@@ -101,6 +106,11 @@ where
|
|||||||
self.elems.push(v);
|
self.elems.push(v);
|
||||||
k
|
k
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the last element that was inserted in the map.
|
||||||
|
pub fn last(&self) -> Option<&V> {
|
||||||
|
self.elems.last()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Immutable indexing into an `PrimaryMap`.
|
/// Immutable indexing into an `PrimaryMap`.
|
||||||
|
|||||||
Reference in New Issue
Block a user