diff --git a/lib/codegen/build.rs b/lib/codegen/build.rs index 39f6ebc62a..8eae5faf94 100644 --- a/lib/codegen/build.rs +++ b/lib/codegen/build.rs @@ -24,7 +24,11 @@ use meta::isa::Isa; use std::env; use std::process; +use std::time::Instant; + fn main() { + let start_time = Instant::now(); + 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 cranelift_targets = env::var("CRANELIFT_TARGETS").ok(); @@ -35,7 +39,7 @@ fn main() { match isa_targets(cranelift_targets, &target_triple) { Ok(isa_targets) => { for isa in &isa_targets { - println!("cargo:rustc-cfg=build_{}", isa.name()); + println!("cargo:rustc-cfg=build_{}", isa.to_string()); } } 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 crate_dir = cur_dir.as_path(); @@ -81,10 +83,28 @@ fn main() { // Now that the Python build process is complete, generate files that are // emitted by the `meta` crate. // ------------------------------------------------------------------------ + let isas = meta::isa::define_all(); + if let Err(err) = meta::gen_types::generate("types.rs", &out_dir) { eprintln!("Error: {}", err); 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 { diff --git a/lib/codegen/meta-python/cdsl/registers.py b/lib/codegen/meta-python/cdsl/registers.py index 0c96583db0..1e4ffe75b1 100644 --- a/lib/codegen/meta-python/cdsl/registers.py +++ b/lib/codegen/meta-python/cdsl/registers.py @@ -87,7 +87,6 @@ class RegBank(object): self.names = names self.classes = list() # type: List[RegClass] self.toprcs = list() # type: List[RegClass] - self.first_toprc_index = None # type: int assert len(names) <= units @@ -248,7 +247,7 @@ class RegClass(object): def intersect(self, other): # 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. """ diff --git a/lib/codegen/meta/Cargo.toml b/lib/codegen/meta/Cargo.toml index 78eed62419..98c22d24c1 100644 --- a/lib/codegen/meta/Cargo.toml +++ b/lib/codegen/meta/Cargo.toml @@ -7,6 +7,9 @@ license = "Apache-2.0 WITH LLVM-exception" repository = "https://github.com/CraneStation/cranelift" readme = "README.md" +[dependencies] +cranelift-entity = { path = "../../entity", version = "0.22.0", default-features = false } + [badges] maintenance = { status = "experimental" } travis-ci = { repository = "CraneStation/cranelift" } diff --git a/lib/codegen/meta/src/cdsl/isa.rs b/lib/codegen/meta/src/cdsl/isa.rs new file mode 100644 index 0000000000..4a6a819683 --- /dev/null +++ b/lib/codegen/meta/src/cdsl/isa.rs @@ -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, + pub reg_classes: PrimaryMap, +} + +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() + ); + } + } + } + } + } +} diff --git a/lib/codegen/meta/src/cdsl/mod.rs b/lib/codegen/meta/src/cdsl/mod.rs index 6a004b2220..4d29421e0b 100644 --- a/lib/codegen/meta/src/cdsl/mod.rs +++ b/lib/codegen/meta/src/cdsl/mod.rs @@ -3,6 +3,8 @@ //! This module defines the classes that are used to define Cranelift //! instructions and other entities. +pub mod isa; +pub mod regs; pub mod types; /// Convert the string `s` to CamelCase. diff --git a/lib/codegen/meta/src/cdsl/regs.rs b/lib/codegen/meta/src/cdsl/regs.rs new file mode 100644 index 0000000000..bc40ffaf69 --- /dev/null +++ b/lib/codegen/meta/src/cdsl/regs.rs @@ -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, + pub classes: Vec, +} + +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, +} + +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 { + 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, +} + +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 + } +} diff --git a/lib/codegen/meta/src/gen_registers.rs b/lib/codegen/meta/src/gen_registers.rs new file mode 100644 index 0000000000..637a3ea3fa --- /dev/null +++ b/lib/codegen/meta/src/gen_registers.rs @@ -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 = 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 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(()) +} diff --git a/lib/codegen/meta/src/isa/arm32/mod.rs b/lib/codegen/meta/src/isa/arm32/mod.rs new file mode 100644 index 0000000000..ce33422276 --- /dev/null +++ b/lib/codegen/meta/src/isa/arm32/mod.rs @@ -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 +} diff --git a/lib/codegen/meta/src/isa/arm64/mod.rs b/lib/codegen/meta/src/isa/arm64/mod.rs new file mode 100644 index 0000000000..8e13d28321 --- /dev/null +++ b/lib/codegen/meta/src/isa/arm64/mod.rs @@ -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 +} diff --git a/lib/codegen/meta/src/isa/mod.rs b/lib/codegen/meta/src/isa/mod.rs index 85141bb714..ffb13fb151 100644 --- a/lib/codegen/meta/src/isa/mod.rs +++ b/lib/codegen/meta/src/isa/mod.rs @@ -1,3 +1,11 @@ +use cdsl::isa::TargetIsa; +use std::fmt; + +mod arm32; +mod arm64; +mod riscv; +mod x86; + /// Represents known ISA target. #[derive(Copy, Clone)] pub enum Isa { @@ -13,7 +21,7 @@ impl Isa { Isa::all() .iter() .cloned() - .filter(|isa| isa.name() == name) + .filter(|isa| isa.to_string() == name) .next() } @@ -31,16 +39,6 @@ impl Isa { [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. fn is_arch_applicable(&self, arch: &str) -> bool { 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 { + let isas = vec![ + riscv::define(), + arm32::define(), + arm64::define(), + x86::define(), + ]; + for isa in isas.iter() { + isa.check(); + } + isas +} diff --git a/lib/codegen/meta/src/isa/riscv/mod.rs b/lib/codegen/meta/src/isa/riscv/mod.rs new file mode 100644 index 0000000000..315ee42233 --- /dev/null +++ b/lib/codegen/meta/src/isa/riscv/mod.rs @@ -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 +} diff --git a/lib/codegen/meta/src/isa/x86/mod.rs b/lib/codegen/meta/src/isa/x86/mod.rs new file mode 100644 index 0000000000..43fd4ed67d --- /dev/null +++ b/lib/codegen/meta/src/isa/x86/mod.rs @@ -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 +} diff --git a/lib/codegen/meta/src/lib.rs b/lib/codegen/meta/src/lib.rs index 5bf0646f35..233138e35d 100644 --- a/lib/codegen/meta/src/lib.rs +++ b/lib/codegen/meta/src/lib.rs @@ -1,4 +1,8 @@ +#[macro_use] +extern crate cranelift_entity; + pub mod error; +pub mod gen_registers; pub mod gen_types; pub mod isa; diff --git a/lib/codegen/meta/src/srcgen.rs b/lib/codegen/meta/src/srcgen.rs index 2e2babe512..bd404fbc06 100644 --- a/lib/codegen/meta/src/srcgen.rs +++ b/lib/codegen/meta/src/srcgen.rs @@ -12,24 +12,6 @@ use error; static SHIFTWIDTH: usize = 4; -struct _IndentedScope { - fmt: Formatter, - after: Option, -} - -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 { indent: usize, lines: Vec, @@ -46,16 +28,23 @@ impl Formatter { } /// Increase current indentation level by one. - pub fn _indent_push(&mut self) { + pub fn indent_push(&mut self) { self.indent += 1; } /// 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"); self.indent -= 1; } + pub fn indent 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 { @@ -68,9 +57,9 @@ impl Formatter { /// 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(); + self.indent_push(); let s = self.get_indent(); - self._indent_pop(); + self.indent_pop(); s } @@ -103,13 +92,6 @@ impl Formatter { 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. pub fn _multi_line(&mut self, s: &str) { parse_multiline(s).into_iter().for_each(|l| self.line(&l)); @@ -158,7 +140,6 @@ fn parse_multiline(s: &str) -> Vec { .iter() .skip(1) .map(|l| l.len() - l.trim_left().len()) - .filter(|&i| i > 0) .min(); // Strip off leading blank lines. @@ -257,9 +238,9 @@ mod srcgen_tests { fn formatter_basic_example_works() { let mut fmt = Formatter::new(); fmt.line("Hello line 1"); - fmt._indent_push(); + fmt.indent_push(); fmt._comment("Nested comment"); - fmt._indent_pop(); + fmt.indent_pop(); fmt.line("Back home again"); let expected_lines = vec![ "Hello line 1\n", @@ -277,9 +258,9 @@ mod srcgen_tests { let actual_results = Vec::with_capacity(4); (0..3).for_each(|_| { 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(); actual_results @@ -300,7 +281,7 @@ mod srcgen_tests { fn fmt_can_add_indented_line() { let mut fmt = Formatter::new(); fmt.line("hello"); - fmt._indent_push(); + fmt.indent_push(); fmt.line("world"); let expected_lines = vec!["hello\n", " world\n"]; assert_eq!(fmt.lines, expected_lines); diff --git a/lib/entity/src/primary.rs b/lib/entity/src/primary.rs index 0902621c22..e23e8e2a6b 100644 --- a/lib/entity/src/primary.rs +++ b/lib/entity/src/primary.rs @@ -50,6 +50,11 @@ where 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? pub fn is_empty(&self) -> bool { self.elems.is_empty() @@ -101,6 +106,11 @@ where self.elems.push(v); 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`.