Generate register bank descriptions.
Use the information in the ISA's registers.py files to generate a RegInfo Rust data structure.
This commit is contained in:
@@ -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()
|
||||
|
||||
51
lib/cretonne/meta/gen_registers.py
Normal file
51
lib/cretonne/meta/gen_registers.py
Normal file
@@ -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)
|
||||
@@ -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()
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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.
|
||||
|
||||
126
lib/cretonne/src/isa/registers.rs
Normal file
126
lib/cretonne/src/isa/registers.rs
Normal file
@@ -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<RegUnit> {
|
||||
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<RegUnit> {
|
||||
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),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<Encoding, Legalize> {
|
||||
lookup_enclist(inst.first_type(),
|
||||
inst.opcode(),
|
||||
|
||||
1
lib/cretonne/src/isa/riscv/registers.py
Normal file
1
lib/cretonne/src/isa/riscv/registers.py
Normal file
@@ -0,0 +1 @@
|
||||
|
||||
37
lib/cretonne/src/isa/riscv/registers.rs
Normal file
37
lib/cretonne/src/isa/riscv/registers.rs
Normal file
@@ -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");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user