diff --git a/lib/cretonne/meta/build.py b/lib/cretonne/meta/build.py index 51f8e7c056..dd7aeac5f9 100644 --- a/lib/cretonne/meta/build.py +++ b/lib/cretonne/meta/build.py @@ -11,6 +11,7 @@ import gen_settings import gen_build_deps import gen_encoding import gen_legalizer +import gen_registers parser = argparse.ArgumentParser(description='Generate sources for Cretonne.') parser.add_argument('--out-dir', help='set output directory') @@ -25,4 +26,5 @@ gen_instr.generate(isas, out_dir) gen_settings.generate(isas, out_dir) gen_encoding.generate(isas, out_dir) gen_legalizer.generate(isas, out_dir) +gen_registers.generate(isas, out_dir) gen_build_deps.generate() diff --git a/lib/cretonne/meta/gen_registers.py b/lib/cretonne/meta/gen_registers.py new file mode 100644 index 0000000000..323aec6173 --- /dev/null +++ b/lib/cretonne/meta/gen_registers.py @@ -0,0 +1,51 @@ +""" +Generate register bank descriptions for each ISA. +""" + +from __future__ import absolute_import +import srcgen + +try: + from typing import Sequence # noqa + from cdsl.isa import TargetISA # noqa + from cdsl.registers import RegBank # noqa +except ImportError: + pass + + +def gen_regbank(regbank, fmt): + # type: (RegBank, srcgen.Formatter) -> None + """ + Emit a static data definition for regbank. + """ + with fmt.indented( + 'RegBank {{'.format(regbank.name), '},'): + fmt.line('name: "{}",'.format(regbank.name)) + fmt.line('first_unit: {},'.format(regbank.first_unit)) + fmt.line('units: {},'.format(regbank.units)) + fmt.line( + 'names: &[{}],' + .format(', '.join('"{}"'.format(n) for n in regbank.names))) + fmt.line('prefix: "{}",'.format(regbank.prefix)) + + +def gen_isa(isa, fmt): + # type: (TargetISA, srcgen.Formatter) -> None + """ + Generate register tables for isa. + """ + if not isa.regbanks: + print('cargo:warning={} has no register banks'.format(isa.name)) + with fmt.indented('pub static INFO: RegInfo = RegInfo {', '};'): + # Bank descriptors. + with fmt.indented('banks: &[', '],'): + for regbank in isa.regbanks: + gen_regbank(regbank, fmt) + + +def generate(isas, out_dir): + # type: (Sequence[TargetISA], str) -> None + for isa in isas: + fmt = srcgen.Formatter() + gen_isa(isa, fmt) + fmt.update_file('registers-{}.rs'.format(isa.name), out_dir) diff --git a/lib/cretonne/meta/isa/arm32/__init__.py b/lib/cretonne/meta/isa/arm32/__init__.py index 9c992d0c17..52f150b8e1 100644 --- a/lib/cretonne/meta/isa/arm32/__init__.py +++ b/lib/cretonne/meta/isa/arm32/__init__.py @@ -8,6 +8,7 @@ This target ISA generates code for ARMv7 and ARMv8 CPUs in 32-bit mode from __future__ import absolute_import from . import defs +from . import registers # noqa # Re-export the primary target ISA definition. ISA = defs.ISA.finish() diff --git a/lib/cretonne/meta/isa/arm64/__init__.py b/lib/cretonne/meta/isa/arm64/__init__.py index 198fc338b1..75f5e85f38 100644 --- a/lib/cretonne/meta/isa/arm64/__init__.py +++ b/lib/cretonne/meta/isa/arm64/__init__.py @@ -7,6 +7,7 @@ ARMv8 CPUs running the Aarch64 architecture. from __future__ import absolute_import from . import defs +from . import registers # noqa # Re-export the primary target ISA definition. ISA = defs.ISA.finish() diff --git a/lib/cretonne/meta/isa/intel/__init__.py b/lib/cretonne/meta/isa/intel/__init__.py index a97b424099..81948699d2 100644 --- a/lib/cretonne/meta/isa/intel/__init__.py +++ b/lib/cretonne/meta/isa/intel/__init__.py @@ -17,6 +17,7 @@ is no x87 floating point support. from __future__ import absolute_import from . import defs +from . import registers # noqa # Re-export the primary target ISA definition. ISA = defs.ISA.finish() diff --git a/lib/cretonne/meta/isa/riscv/__init__.py b/lib/cretonne/meta/isa/riscv/__init__.py index 3d5d19ed99..cf61cbdcf5 100644 --- a/lib/cretonne/meta/isa/riscv/__init__.py +++ b/lib/cretonne/meta/isa/riscv/__init__.py @@ -26,7 +26,7 @@ RV32G / RV64G """ from __future__ import absolute_import from . import defs -from . import encodings, settings # noqa +from . import encodings, settings, registers # noqa # Re-export the primary target ISA definition. ISA = defs.ISA.finish() diff --git a/lib/cretonne/src/isa/mod.rs b/lib/cretonne/src/isa/mod.rs index aa7b9a76d8..b1b3a8a8df 100644 --- a/lib/cretonne/src/isa/mod.rs +++ b/lib/cretonne/src/isa/mod.rs @@ -41,12 +41,14 @@ //! concurrent function compilations. pub use isa::encoding::Encoding; +pub use isa::registers::{RegUnit, RegBank, RegInfo}; use settings; use ir::{InstructionData, DataFlowGraph}; pub mod riscv; mod encoding; mod enc_tables; +mod registers; /// Look for a supported ISA with the given `name`. /// Return a builder that can create a corresponding `TargetIsa`. @@ -108,6 +110,9 @@ pub trait TargetIsa { /// Get the ISA-independent flags that were used to make this trait object. fn flags(&self) -> &settings::Flags; + /// Get a data structure describing the registers in this ISA. + fn register_info(&self) -> &RegInfo; + /// Encode an instruction after determining it is legal. /// /// If `inst` can legally be encoded in this ISA, produce the corresponding `Encoding` object. diff --git a/lib/cretonne/src/isa/registers.rs b/lib/cretonne/src/isa/registers.rs new file mode 100644 index 0000000000..8736f06e84 --- /dev/null +++ b/lib/cretonne/src/isa/registers.rs @@ -0,0 +1,126 @@ +//! Data structures describing the registers in an ISA. + +use std::fmt; + +/// Register units are the smallest units of register allocation. +/// +/// Normally there is a 1-1 correspondence between registers and register units, but when an ISA +/// has aliasing registers, the aliasing can be modeled with registers that cover multiple +/// register units. +/// +/// The register allocator will enforce that each register unit only gets used for one thing. +pub type RegUnit = u16; + +/// The register units in a target ISA are divided into disjoint register banks. Each bank covers a +/// contiguous range of register units. +/// +/// The `RegBank` struct provides a static description of a register bank. +pub struct RegBank { + /// The name of this register bank as defined in the ISA's `registers.py` file. + pub name: &'static str, + + /// The first register unit in this bank. + pub first_unit: RegUnit, + + /// The total number of register units in this bank. + pub units: u16, + + /// Array of specially named register units. This array can be shorter than the number of units + /// in the bank. + pub names: &'static [&'static str], + + /// Name prefix to use for those register units in the bank not covered by the `names` array. + /// The remaining register units will be named this prefix followed by their decimal offset in + /// the bank. So with a prefix `r`, registers will be named `r8`, `r9`, ... + pub prefix: &'static str, +} + +impl RegBank { + /// Does this bank contain `regunit`? + fn contains(&self, regunit: RegUnit) -> bool { + regunit >= self.first_unit && regunit - self.first_unit < self.units + } + + /// Try to parse a regunit name. The name is not expected to begin with `%`. + fn parse_regunit(&self, name: &str) -> Option { + match self.names.iter().position(|&x| x == name) { + Some(offset) => { + // This is one of the special-cased names. + Some(offset as RegUnit) + } + None => { + // Try a regular prefixed name. + if name.starts_with(self.prefix) { + name[self.prefix.len()..].parse().ok() + } else { + None + } + } + } + .and_then(|offset| { + if offset < self.units { + Some(offset + self.first_unit) + } else { + None + } + }) + } + + /// Write `regunit` to `w`, assuming that it belongs to this bank. + /// All regunits are written with a `%` prefix. + fn write_regunit(&self, f: &mut fmt::Formatter, regunit: RegUnit) -> fmt::Result { + let offset = regunit - self.first_unit; + assert!(offset < self.units); + if (offset as usize) < self.names.len() { + write!(f, "%{}", self.names[offset as usize]) + } else { + write!(f, "%{}{}", self.prefix, offset) + } + } +} + +/// Information about the registers in an ISA. +/// +/// The `RegUnit` data structure collects all relevant static information about the registers in an +/// ISA. +pub struct RegInfo { + /// All register banks, ordered by their `first_unit`. The register banks are disjoint, but + /// there may be holes of unused register unit numbers between banks due to alignment. + pub banks: &'static [RegBank], +} + +impl RegInfo { + /// Get the register bank holding `regunit`. + pub fn bank_containing_regunit(&self, regunit: RegUnit) -> Option<&RegBank> { + // We could do a binary search, but most ISAs have only two register banks... + self.banks.iter().find(|b| b.contains(regunit)) + } + + /// Try to parse a regunit name. The name is not expected to begin with `%`. + pub fn parse_regunit(&self, name: &str) -> Option { + self.banks.iter().filter_map(|b| b.parse_regunit(name)).next() + } + + /// Make a temporary object that can display a register unit. + pub fn display_regunit(&self, regunit: RegUnit) -> DisplayRegUnit { + DisplayRegUnit { + regunit: regunit, + reginfo: self, + } + } +} + +/// Temporary object that holds enough information to print a register unit. +pub struct DisplayRegUnit<'a> { + pub regunit: RegUnit, + pub reginfo: &'a RegInfo, +} + +impl<'a> fmt::Display for DisplayRegUnit<'a> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self.reginfo.bank_containing_regunit(self.regunit) { + Some(b) => b.write_regunit(f, self.regunit), + None => write!(f, "%INVALID{}", self.regunit), + } + } +} diff --git a/lib/cretonne/src/isa/riscv/mod.rs b/lib/cretonne/src/isa/riscv/mod.rs index 63ab5ccf89..762df2249c 100644 --- a/lib/cretonne/src/isa/riscv/mod.rs +++ b/lib/cretonne/src/isa/riscv/mod.rs @@ -2,11 +2,12 @@ pub mod settings; mod enc_tables; +mod registers; use super::super::settings as shared_settings; use isa::enc_tables::{self as shared_enc_tables, lookup_enclist, general_encoding}; use isa::Builder as IsaBuilder; -use isa::{TargetIsa, Encoding, Legalize}; +use isa::{TargetIsa, RegInfo, Encoding, Legalize}; use ir::{InstructionData, DataFlowGraph}; #[allow(dead_code)] @@ -48,6 +49,10 @@ impl TargetIsa for Isa { &self.shared_flags } + fn register_info(&self) -> &RegInfo { + ®isters::INFO + } + fn encode(&self, _: &DataFlowGraph, inst: &InstructionData) -> Result { lookup_enclist(inst.first_type(), inst.opcode(), diff --git a/lib/cretonne/src/isa/riscv/registers.py b/lib/cretonne/src/isa/riscv/registers.py new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/lib/cretonne/src/isa/riscv/registers.py @@ -0,0 +1 @@ + diff --git a/lib/cretonne/src/isa/riscv/registers.rs b/lib/cretonne/src/isa/riscv/registers.rs new file mode 100644 index 0000000000..a3fe4739e8 --- /dev/null +++ b/lib/cretonne/src/isa/riscv/registers.rs @@ -0,0 +1,37 @@ +//! RISC-V register descriptions. + +use isa::registers::{RegBank, RegInfo}; + +include!(concat!(env!("OUT_DIR"), "/registers-riscv.rs")); + +#[cfg(test)] +mod tests { + use super::INFO; + use isa::RegUnit; + + #[test] + fn unit_encodings() { + assert_eq!(INFO.parse_regunit("x0"), Some(0)); + assert_eq!(INFO.parse_regunit("x31"), Some(31)); + assert_eq!(INFO.parse_regunit("f0"), Some(32)); + assert_eq!(INFO.parse_regunit("f31"), Some(63)); + + assert_eq!(INFO.parse_regunit("x32"), None); + assert_eq!(INFO.parse_regunit("f32"), None); + } + + #[test] + fn unit_names() { + fn uname(ru: RegUnit) -> String { + INFO.display_regunit(ru).to_string() + } + + assert_eq!(uname(0), "%x0"); + assert_eq!(uname(1), "%x1"); + assert_eq!(uname(31), "%x31"); + assert_eq!(uname(32), "%f0"); + assert_eq!(uname(33), "%f1"); + assert_eq!(uname(63), "%f31"); + assert_eq!(uname(64), "%INVALID64"); + } +}