[meta] Port Formats and Operands to the Rust crate;

This commit is contained in:
Benjamin Bouvier
2019-03-11 19:25:11 +01:00
parent 146e0bd2f5
commit d59bef1902
7 changed files with 978 additions and 0 deletions

View File

@@ -0,0 +1,246 @@
use crate::cdsl::operands::{Operand, OperandKind};
use std::collections::{HashMap, HashSet};
use std::fmt;
use std::slice;
use cranelift_entity::{entity_impl, PrimaryMap};
/// An immediate field in an instruction format.
///
/// This corresponds to a single member of a variant of the `InstructionData`
/// data type.
///
/// :param iform: Parent `InstructionFormat`.
/// :param kind: Immediate Operand kind.
/// :param member: Member name in `InstructionData` variant.
#[derive(Debug)]
pub struct FormatField {
/// Immediate operand number in parent.
immnum: usize,
/// Immediate operand kind.
pub kind: OperandKind,
/// Member name in InstructionDate variant.
pub member: &'static str,
}
/// Every instruction opcode has a corresponding instruction format which
/// determines the number of operands and their kinds. Instruction formats are
/// identified structurally, i.e., the format of an instruction is derived from
/// the kinds of operands used in its declaration.
///
/// The instruction format stores two separate lists of operands: Immediates
/// and values. Immediate operands (including entity references) are
/// represented as explicit members in the `InstructionData` variants. The
/// value operands are stored differently, depending on how many there are.
/// Beyond a certain point, instruction formats switch to an external value
/// list for storing value arguments. Value lists can hold an arbitrary number
/// of values.
///
/// All instruction formats must be predefined in the meta shared/formats module.
///
/// :param kinds: List of `OperandKind` objects describing the operands.
/// :param name: Instruction format name in CamelCase. This is used as a Rust
/// variant name in both the `InstructionData` and `InstructionFormat`
/// enums.
/// :param typevar_operand: Index of the value input operand that is used to
/// infer the controlling type variable. By default, this is `0`, the first
/// `value` operand. The index is relative to the values only, ignoring
/// immediate operands.
#[derive(Debug)]
pub struct InstructionFormat {
pub name: &'static str,
pub num_value_operands: usize,
pub has_value_list: bool,
pub imm_fields: Vec<FormatField>,
pub typevar_operand: Option<usize>,
}
impl fmt::Display for InstructionFormat {
fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
let args = self
.imm_fields
.iter()
.map(|field| format!("{}: {}", field.member, field.kind.name))
.collect::<Vec<_>>()
.join(", ");
fmt.write_fmt(format_args!(
"{}(imms=({}), vals={})",
self.name, args, self.num_value_operands
))?;
Ok(())
}
}
pub struct InstructionFormatBuilder {
name: &'static str,
num_value_operands: usize,
has_value_list: bool,
imm_fields: Vec<FormatField>,
typevar_operand: Option<usize>,
}
pub struct ImmParameter {
kind: OperandKind,
member: &'static str,
}
impl Into<ImmParameter> for (&'static str, &OperandKind) {
fn into(self) -> ImmParameter {
ImmParameter {
kind: self.1.clone(),
member: self.0,
}
}
}
impl Into<ImmParameter> for &OperandKind {
fn into(self) -> ImmParameter {
ImmParameter {
kind: self.clone(),
member: self.default_member.unwrap(),
}
}
}
impl InstructionFormatBuilder {
pub fn new(name: &'static str) -> Self {
Self {
name,
num_value_operands: 0,
has_value_list: false,
imm_fields: Vec::new(),
typevar_operand: None,
}
}
pub fn value(mut self) -> Self {
self.num_value_operands += 1;
self
}
pub fn varargs(mut self) -> Self {
self.has_value_list = true;
self
}
pub fn imm(mut self, param: impl Into<ImmParameter>) -> Self {
let imm_param = param.into();
let field = FormatField {
immnum: self.imm_fields.len(),
kind: imm_param.kind,
member: imm_param.member,
};
self.imm_fields.push(field);
self
}
pub fn typevar_operand(mut self, operand_index: usize) -> Self {
assert!(self.typevar_operand.is_none());
assert!(self.has_value_list || operand_index < self.num_value_operands);
self.typevar_operand = Some(operand_index);
self
}
pub fn finish(self) -> InstructionFormat {
let typevar_operand = if self.typevar_operand.is_some() {
self.typevar_operand
} else if self.has_value_list || self.num_value_operands > 0 {
// Default to the first value operand, if there's one.
Some(0)
} else {
None
};
InstructionFormat {
name: self.name,
num_value_operands: self.num_value_operands,
has_value_list: self.has_value_list,
imm_fields: self.imm_fields,
typevar_operand,
}
}
}
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct InstructionFormatIndex(u32);
entity_impl!(InstructionFormatIndex);
pub struct FormatRegistry {
/// Map (immediate kinds names, number of values, has varargs) to an instruction format index
/// in the actual map.
sig_to_index: HashMap<(Vec<String>, usize, bool), InstructionFormatIndex>,
map: PrimaryMap<InstructionFormatIndex, InstructionFormat>,
name_set: HashSet<&'static str>,
}
impl FormatRegistry {
pub fn new() -> Self {
Self {
sig_to_index: HashMap::new(),
map: PrimaryMap::new(),
name_set: HashSet::new(),
}
}
/// Find an existing instruction format that matches the given lists of instruction inputs and
/// outputs.
pub fn lookup(&self, operands_in: &Vec<Operand>) -> InstructionFormatIndex {
let mut imm_keys = Vec::new();
let mut num_values = 0;
let mut has_varargs = false;
for operand in operands_in.iter() {
if operand.is_value() {
num_values += 1;
}
has_varargs = has_varargs || operand.is_varargs();
if let Some(imm_key) = operand.kind.imm_key() {
imm_keys.push(imm_key);
}
}
let sig = (imm_keys, num_values, has_varargs);
*self
.sig_to_index
.get(&sig)
.expect("unknown InstructionFormat; please define it in shared/formats.rs first")
}
pub fn get(&self, index: InstructionFormatIndex) -> &InstructionFormat {
self.map.get(index).unwrap()
}
pub fn insert(&mut self, inst_format: InstructionFormatBuilder) {
let name = &inst_format.name;
if !self.name_set.insert(name) {
panic!(
"Trying to add an InstructionFormat named {}, but it already exists!",
name
);
}
let format = inst_format.finish();
// Compute key.
let imm_keys = format
.imm_fields
.iter()
.map(|field| field.kind.imm_key().unwrap())
.collect();
let key = (imm_keys, format.num_value_operands, format.has_value_list);
let index = self.map.push(format);
if let Some(already_inserted) = self.sig_to_index.insert(key, index) {
panic!(
"duplicate InstructionFormat: trying to insert '{}' while '{}' already has the same structure.",
self.map.get(index).unwrap().name,
self.map.get(already_inserted).unwrap().name
);
}
}
pub fn iter(&self) -> slice::Iter<InstructionFormat> {
self.map.values()
}
}

View File

@@ -3,7 +3,9 @@
//! This module defines the classes that are used to define Cranelift
//! instructions and other entities.
pub mod formats;
pub mod isa;
pub mod operands;
pub mod regs;
pub mod settings;
pub mod types;

View File

@@ -0,0 +1,285 @@
use std::collections::HashMap;
use crate::cdsl::camel_case;
use crate::cdsl::typevar::TypeVar;
/// An instruction operand can be an *immediate*, an *SSA value*, or an *entity reference*. The
/// type of the operand is one of:
///
/// 1. A `ValueType` instance indicates an SSA value operand with a concrete type.
///
/// 2. A `TypeVar` instance indicates an SSA value operand, and the instruction is polymorphic over
/// the possible concrete types that the type variable can assume.
///
/// 3. An `ImmediateKind` instance indicates an immediate operand whose value is encoded in the
/// instruction itself rather than being passed as an SSA value.
///
/// 4. An `EntityRefKind` instance indicates an operand that references another entity in the
/// function, typically something declared in the function preamble.
#[derive(Clone, Debug)]
pub struct Operand {
pub name: &'static str,
pub doc: Option<String>,
pub kind: OperandKind,
}
impl Operand {
pub fn is_value(&self) -> bool {
match self.kind.fields {
OperandKindFields::TypeVar(_) => true,
_ => false,
}
}
pub fn type_var(&self) -> Option<&TypeVar> {
match &self.kind.fields {
OperandKindFields::TypeVar(typevar) => Some(typevar),
_ => None,
}
}
pub fn is_varargs(&self) -> bool {
match self.kind.fields {
OperandKindFields::VariableArgs => true,
_ => false,
}
}
/// Returns true if the operand has an immediate kind or is an EntityRef.
// TODO inherited name from the python, rename to is_immediate_or_entityref later.
pub fn is_immediate(&self) -> bool {
match self.kind.fields {
OperandKindFields::ImmEnum(_)
| OperandKindFields::ImmValue
| OperandKindFields::EntityRef => true,
_ => false,
}
}
/// Returns true if the operand has an immediate kind.
pub fn is_pure_immediate(&self) -> bool {
match self.kind.fields {
OperandKindFields::ImmEnum(_) | OperandKindFields::ImmValue => true,
_ => false,
}
}
pub fn is_cpu_flags(&self) -> bool {
match &self.kind.fields {
OperandKindFields::TypeVar(type_var)
if type_var.name == "iflags" || type_var.name == "fflags" =>
{
true
}
_ => false,
}
}
}
pub struct OperandBuilder {
name: &'static str,
doc: Option<String>,
kind: OperandKind,
}
impl OperandBuilder {
pub fn new(name: &'static str, kind: OperandKind) -> Self {
Self {
name,
doc: None,
kind,
}
}
pub fn doc(mut self, doc: impl Into<String>) -> Self {
assert!(self.doc.is_none());
self.doc = Some(doc.into());
self
}
pub fn finish(self) -> Operand {
let doc = match self.doc {
Some(doc) => Some(doc),
None => match &self.kind.fields {
OperandKindFields::TypeVar(tvar) => Some(tvar.doc.clone()),
_ => self.kind.doc.clone(),
},
};
Operand {
name: self.name,
doc,
kind: self.kind,
}
}
}
type EnumValues = HashMap<&'static str, &'static str>;
#[derive(Clone, Debug)]
pub enum OperandKindFields {
EntityRef,
VariableArgs,
ImmValue,
ImmEnum(EnumValues),
TypeVar(TypeVar),
}
#[derive(Clone, Debug)]
pub struct OperandKind {
pub name: &'static str,
doc: Option<String>,
pub default_member: Option<&'static str>,
/// The camel-cased name of an operand kind is also the Rust type used to represent it.
pub rust_type: String,
fields: OperandKindFields,
}
impl OperandKind {
pub fn imm_key(&self) -> Option<String> {
match self.fields {
OperandKindFields::ImmEnum(_)
| OperandKindFields::ImmValue
| OperandKindFields::EntityRef => Some(self.name.to_string()),
_ => None,
}
}
pub fn type_var(&self) -> TypeVar {
match &self.fields {
OperandKindFields::TypeVar(tvar) => tvar.clone(),
_ => panic!("not a typevar"),
}
}
}
pub struct OperandKindBuilder {
name: &'static str,
doc: Option<String>,
default_member: Option<&'static str>,
/// The camel-cased name of an operand kind is also the Rust type used to represent it.
rust_type: Option<String>,
fields: OperandKindFields,
}
impl OperandKindBuilder {
pub fn new(name: &'static str, fields: OperandKindFields) -> Self {
Self {
name,
doc: None,
default_member: None,
rust_type: None,
fields,
}
}
pub fn new_imm(name: &'static str) -> Self {
Self {
name,
doc: None,
default_member: None,
rust_type: None,
fields: OperandKindFields::ImmValue,
}
}
pub fn new_enum(name: &'static str, values: EnumValues) -> Self {
Self {
name,
doc: None,
default_member: None,
rust_type: None,
fields: OperandKindFields::ImmEnum(values),
}
}
pub fn doc(mut self, doc: &'static str) -> Self {
assert!(self.doc.is_none());
self.doc = Some(doc.to_string());
self
}
pub fn default_member(mut self, default_member: &'static str) -> Self {
assert!(self.default_member.is_none());
self.default_member = Some(default_member);
self
}
pub fn rust_type(mut self, rust_type: &'static str) -> Self {
assert!(self.rust_type.is_none());
self.rust_type = Some(rust_type.to_string());
self
}
pub fn finish(self) -> OperandKind {
let default_member = match self.default_member {
Some(default_member) => Some(default_member),
None => match &self.fields {
OperandKindFields::ImmEnum(_) | OperandKindFields::ImmValue => Some("imm"),
OperandKindFields::TypeVar(_) | OperandKindFields::EntityRef => Some(self.name),
OperandKindFields::VariableArgs => None,
},
};
let rust_type = match self.rust_type {
Some(rust_type) => rust_type.to_string(),
None => match &self.fields {
OperandKindFields::ImmEnum(_) | OperandKindFields::ImmValue => {
format!("ir::immediates::{}", camel_case(self.name))
}
OperandKindFields::VariableArgs => "&[Value]".to_string(),
OperandKindFields::TypeVar(_) | OperandKindFields::EntityRef => {
format!("ir::{}", camel_case(self.name))
}
},
};
let doc = match self.doc {
Some(doc) => Some(doc),
None => match &self.fields {
OperandKindFields::TypeVar(type_var) => Some(type_var.doc.clone()),
OperandKindFields::ImmEnum(_)
| OperandKindFields::ImmValue
| OperandKindFields::EntityRef
| OperandKindFields::VariableArgs => None,
},
};
OperandKind {
name: self.name,
doc,
default_member,
rust_type,
fields: self.fields,
}
}
}
impl Into<OperandKind> for &TypeVar {
fn into(self) -> OperandKind {
OperandKindBuilder::new("value", OperandKindFields::TypeVar(self.into())).finish()
}
}
impl Into<OperandKind> for &OperandKind {
fn into(self) -> OperandKind {
self.clone()
}
}
/// Helper to create an operand in definitions files.
pub fn create_operand(name: &'static str, kind: impl Into<OperandKind>) -> Operand {
OperandBuilder::new(name, kind.into()).finish()
}
/// Helper to create an operand with a documentation in definitions files.
pub fn create_operand_doc(
name: &'static str,
kind: impl Into<OperandKind>,
doc: &'static str,
) -> Operand {
OperandBuilder::new(name, kind.into()).doc(doc).finish()
}