[meta] Port Formats and Operands to the Rust crate;
This commit is contained in:
246
cranelift/codegen/meta/src/cdsl/formats.rs
Normal file
246
cranelift/codegen/meta/src/cdsl/formats.rs
Normal 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()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,7 +3,9 @@
|
|||||||
//! 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 formats;
|
||||||
pub mod isa;
|
pub mod isa;
|
||||||
|
pub mod operands;
|
||||||
pub mod regs;
|
pub mod regs;
|
||||||
pub mod settings;
|
pub mod settings;
|
||||||
pub mod types;
|
pub mod types;
|
||||||
|
|||||||
285
cranelift/codegen/meta/src/cdsl/operands.rs
Normal file
285
cranelift/codegen/meta/src/cdsl/operands.rs
Normal 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()
|
||||||
|
}
|
||||||
65
cranelift/codegen/meta/src/shared/entities.rs
Normal file
65
cranelift/codegen/meta/src/shared/entities.rs
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
use crate::cdsl::operands::{OperandKind, OperandKindBuilder as Builder, OperandKindFields};
|
||||||
|
|
||||||
|
/// Small helper to initialize an OperandBuilder with the right kind, for a given name and doc.
|
||||||
|
fn create(name: &'static str, doc: &'static str) -> Builder {
|
||||||
|
Builder::new(name, OperandKindFields::EntityRef).doc(doc)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn define() -> Vec<OperandKind> {
|
||||||
|
let mut kinds = Vec::new();
|
||||||
|
|
||||||
|
// A reference to an extended basic block in the same function.
|
||||||
|
// This is primarliy used in control flow instructions.
|
||||||
|
let ebb = create("ebb", "An extended basic block in the same function.")
|
||||||
|
.default_member("destination")
|
||||||
|
.finish();
|
||||||
|
kinds.push(ebb);
|
||||||
|
|
||||||
|
// A reference to a stack slot declared in the function preamble.
|
||||||
|
let stack_slot = create("stack_slot", "A stack slot").finish();
|
||||||
|
kinds.push(stack_slot);
|
||||||
|
|
||||||
|
// A reference to a global value.
|
||||||
|
let global_value = create("global_value", "A global value.").finish();
|
||||||
|
kinds.push(global_value);
|
||||||
|
|
||||||
|
// A reference to a function signature declared in the function preamble.
|
||||||
|
// This is used to provide the call signature in a call_indirect instruction.
|
||||||
|
let sig_ref = create("sig_ref", "A function signature.").finish();
|
||||||
|
kinds.push(sig_ref);
|
||||||
|
|
||||||
|
// A reference to an external function declared in the function preamble.
|
||||||
|
// This is used to provide the callee and signature in a call instruction.
|
||||||
|
let func_ref = create("func_ref", "An external function.").finish();
|
||||||
|
kinds.push(func_ref);
|
||||||
|
|
||||||
|
// A reference to a jump table declared in the function preamble.
|
||||||
|
let jump_table = create("jump_table", "A jump table.")
|
||||||
|
.default_member("table")
|
||||||
|
.finish();
|
||||||
|
kinds.push(jump_table);
|
||||||
|
|
||||||
|
// A reference to a heap declared in the function preamble.
|
||||||
|
let heap = create("heap", "A heap.").finish();
|
||||||
|
kinds.push(heap);
|
||||||
|
|
||||||
|
// A reference to a table declared in the function preamble.
|
||||||
|
let table = create("table", "A table.").finish();
|
||||||
|
kinds.push(table);
|
||||||
|
|
||||||
|
// A variable-sized list of value operands. Use for Ebb and function call arguments.
|
||||||
|
let varargs = Builder::new("variable_args", OperandKindFields::VariableArgs)
|
||||||
|
.doc(
|
||||||
|
r#"
|
||||||
|
A variable size list of `value` operands.
|
||||||
|
|
||||||
|
Use this to represent arguments passed to a function call, arguments
|
||||||
|
passed to an extended basic block, or a variable number of results
|
||||||
|
returned from an instruction.
|
||||||
|
"#,
|
||||||
|
)
|
||||||
|
.finish();
|
||||||
|
kinds.push(varargs);
|
||||||
|
|
||||||
|
return kinds;
|
||||||
|
}
|
||||||
184
cranelift/codegen/meta/src/shared/formats.rs
Normal file
184
cranelift/codegen/meta/src/shared/formats.rs
Normal file
@@ -0,0 +1,184 @@
|
|||||||
|
use crate::cdsl::formats::{FormatRegistry, InstructionFormatBuilder as Builder};
|
||||||
|
use crate::shared::OperandKinds;
|
||||||
|
|
||||||
|
pub fn define(immediates: &OperandKinds, entities: &OperandKinds) -> FormatRegistry {
|
||||||
|
// Shorthands for immediates.
|
||||||
|
let uimm8 = immediates.by_name("uimm8");
|
||||||
|
let uimm32 = immediates.by_name("uimm32");
|
||||||
|
let imm64 = immediates.by_name("imm64");
|
||||||
|
let ieee32 = immediates.by_name("ieee32");
|
||||||
|
let ieee64 = immediates.by_name("ieee64");
|
||||||
|
let boolean = immediates.by_name("boolean");
|
||||||
|
let intcc = immediates.by_name("intcc");
|
||||||
|
let floatcc = immediates.by_name("floatcc");
|
||||||
|
let memflags = immediates.by_name("memflags");
|
||||||
|
let offset32 = immediates.by_name("offset32");
|
||||||
|
let trapcode = immediates.by_name("trapcode");
|
||||||
|
let regunit = immediates.by_name("regunit");
|
||||||
|
|
||||||
|
// Shorthands for entities.
|
||||||
|
let global_value = entities.by_name("global_value");
|
||||||
|
let ebb = entities.by_name("ebb");
|
||||||
|
let jump_table = entities.by_name("jump_table");
|
||||||
|
let func_ref = entities.by_name("func_ref");
|
||||||
|
let sig_ref = entities.by_name("sig_ref");
|
||||||
|
let stack_slot = entities.by_name("stack_slot");
|
||||||
|
let heap = entities.by_name("heap");
|
||||||
|
let table = entities.by_name("table");
|
||||||
|
|
||||||
|
let mut registry = FormatRegistry::new();
|
||||||
|
|
||||||
|
registry.insert(Builder::new("Unary").value());
|
||||||
|
registry.insert(Builder::new("UnaryImm").imm(imm64));
|
||||||
|
registry.insert(Builder::new("UnaryIeee32").imm(ieee32));
|
||||||
|
registry.insert(Builder::new("UnaryIeee64").imm(ieee64));
|
||||||
|
registry.insert(Builder::new("UnaryBool").imm(boolean));
|
||||||
|
registry.insert(Builder::new("UnaryGlobalValue").imm(global_value));
|
||||||
|
|
||||||
|
registry.insert(Builder::new("Binary").value().value());
|
||||||
|
registry.insert(Builder::new("BinaryImm").value().imm(imm64));
|
||||||
|
|
||||||
|
// The select instructions are controlled by the second VALUE operand.
|
||||||
|
// The first VALUE operand is the controlling flag which has a derived type.
|
||||||
|
// The fma instruction has the same constraint on all inputs.
|
||||||
|
registry.insert(
|
||||||
|
Builder::new("Ternary")
|
||||||
|
.value()
|
||||||
|
.value()
|
||||||
|
.value()
|
||||||
|
.typevar_operand(1),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Catch-all for instructions with many outputs and inputs and no immediate
|
||||||
|
// operands.
|
||||||
|
registry.insert(Builder::new("MultiAry").varargs());
|
||||||
|
|
||||||
|
registry.insert(Builder::new("NullAry"));
|
||||||
|
|
||||||
|
registry.insert(
|
||||||
|
Builder::new("InsertLane")
|
||||||
|
.value()
|
||||||
|
.imm(("lane", uimm8))
|
||||||
|
.value(),
|
||||||
|
);
|
||||||
|
registry.insert(Builder::new("ExtractLane").value().imm(("lane", uimm8)));
|
||||||
|
|
||||||
|
registry.insert(Builder::new("IntCompare").imm(intcc).value().value());
|
||||||
|
registry.insert(Builder::new("IntCompareImm").imm(intcc).value().imm(imm64));
|
||||||
|
registry.insert(Builder::new("IntCond").imm(intcc).value());
|
||||||
|
|
||||||
|
registry.insert(Builder::new("FloatCompare").imm(floatcc).value().value());
|
||||||
|
registry.insert(Builder::new("FloatCond").imm(floatcc).value());;
|
||||||
|
|
||||||
|
registry.insert(Builder::new("IntSelect").imm(intcc).value().value().value());
|
||||||
|
|
||||||
|
registry.insert(Builder::new("Jump").imm(ebb).varargs());
|
||||||
|
registry.insert(Builder::new("Branch").value().imm(ebb).varargs());
|
||||||
|
registry.insert(
|
||||||
|
Builder::new("BranchInt")
|
||||||
|
.imm(intcc)
|
||||||
|
.value()
|
||||||
|
.imm(ebb)
|
||||||
|
.varargs(),
|
||||||
|
);
|
||||||
|
registry.insert(
|
||||||
|
Builder::new("BranchFloat")
|
||||||
|
.imm(floatcc)
|
||||||
|
.value()
|
||||||
|
.imm(ebb)
|
||||||
|
.varargs(),
|
||||||
|
);
|
||||||
|
registry.insert(
|
||||||
|
Builder::new("BranchIcmp")
|
||||||
|
.imm(intcc)
|
||||||
|
.value()
|
||||||
|
.value()
|
||||||
|
.imm(ebb)
|
||||||
|
.varargs(),
|
||||||
|
);
|
||||||
|
registry.insert(Builder::new("BranchTable").value().imm(ebb).imm(jump_table));
|
||||||
|
registry.insert(
|
||||||
|
Builder::new("BranchTableEntry")
|
||||||
|
.value()
|
||||||
|
.value()
|
||||||
|
.imm(uimm8)
|
||||||
|
.imm(jump_table),
|
||||||
|
);
|
||||||
|
registry.insert(Builder::new("BranchTableBase").imm(jump_table));
|
||||||
|
registry.insert(Builder::new("IndirectJump").value().imm(jump_table));
|
||||||
|
|
||||||
|
registry.insert(Builder::new("Call").imm(func_ref).varargs());
|
||||||
|
registry.insert(Builder::new("CallIndirect").imm(sig_ref).value().varargs());
|
||||||
|
registry.insert(Builder::new("FuncAddr").imm(func_ref));
|
||||||
|
|
||||||
|
registry.insert(Builder::new("Load").imm(memflags).value().imm(offset32));
|
||||||
|
registry.insert(
|
||||||
|
Builder::new("LoadComplex")
|
||||||
|
.imm(memflags)
|
||||||
|
.varargs()
|
||||||
|
.imm(offset32),
|
||||||
|
);
|
||||||
|
registry.insert(
|
||||||
|
Builder::new("Store")
|
||||||
|
.imm(memflags)
|
||||||
|
.value()
|
||||||
|
.value()
|
||||||
|
.imm(offset32),
|
||||||
|
);
|
||||||
|
registry.insert(
|
||||||
|
Builder::new("StoreComplex")
|
||||||
|
.imm(memflags)
|
||||||
|
.value()
|
||||||
|
.varargs()
|
||||||
|
.imm(offset32),
|
||||||
|
);
|
||||||
|
registry.insert(Builder::new("StackLoad").imm(stack_slot).imm(offset32));
|
||||||
|
registry.insert(
|
||||||
|
Builder::new("StackStore")
|
||||||
|
.value()
|
||||||
|
.imm(stack_slot)
|
||||||
|
.imm(offset32),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Accessing a WebAssembly heap.
|
||||||
|
registry.insert(Builder::new("HeapAddr").imm(heap).value().imm(uimm32));
|
||||||
|
|
||||||
|
// Accessing a WebAssembly table.
|
||||||
|
registry.insert(Builder::new("TableAddr").imm(table).value().imm(offset32));
|
||||||
|
|
||||||
|
registry.insert(
|
||||||
|
Builder::new("RegMove")
|
||||||
|
.value()
|
||||||
|
.imm(("src", regunit))
|
||||||
|
.imm(("dst", regunit)),
|
||||||
|
);
|
||||||
|
registry.insert(
|
||||||
|
Builder::new("CopySpecial")
|
||||||
|
.imm(("src", regunit))
|
||||||
|
.imm(("dst", regunit)),
|
||||||
|
);
|
||||||
|
registry.insert(
|
||||||
|
Builder::new("RegSpill")
|
||||||
|
.value()
|
||||||
|
.imm(("src", regunit))
|
||||||
|
.imm(("dst", stack_slot)),
|
||||||
|
);
|
||||||
|
registry.insert(
|
||||||
|
Builder::new("RegFill")
|
||||||
|
.value()
|
||||||
|
.imm(("src", stack_slot))
|
||||||
|
.imm(("dst", regunit)),
|
||||||
|
);
|
||||||
|
|
||||||
|
registry.insert(Builder::new("Trap").imm(trapcode));
|
||||||
|
registry.insert(Builder::new("CondTrap").value().imm(trapcode));
|
||||||
|
registry.insert(Builder::new("IntCondTrap").imm(intcc).value().imm(trapcode));
|
||||||
|
registry.insert(
|
||||||
|
Builder::new("FloatCondTrap")
|
||||||
|
.imm(floatcc)
|
||||||
|
.value()
|
||||||
|
.imm(trapcode),
|
||||||
|
);
|
||||||
|
|
||||||
|
registry
|
||||||
|
}
|
||||||
145
cranelift/codegen/meta/src/shared/immediates.rs
Normal file
145
cranelift/codegen/meta/src/shared/immediates.rs
Normal file
@@ -0,0 +1,145 @@
|
|||||||
|
use crate::cdsl::operands::{OperandKind, OperandKindBuilder as Builder};
|
||||||
|
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
pub fn define() -> Vec<OperandKind> {
|
||||||
|
let mut kinds = Vec::new();
|
||||||
|
|
||||||
|
// A 64-bit immediate integer operand.
|
||||||
|
//
|
||||||
|
// This type of immediate integer can interact with SSA values with any
|
||||||
|
// IntType type.
|
||||||
|
let imm64 = Builder::new_imm("imm64")
|
||||||
|
.doc("A 64-bit immediate integer.")
|
||||||
|
.finish();
|
||||||
|
kinds.push(imm64);
|
||||||
|
|
||||||
|
// An unsigned 8-bit immediate integer operand.
|
||||||
|
//
|
||||||
|
// This small operand is used to indicate lane indexes in SIMD vectors and
|
||||||
|
// immediate bit counts on shift instructions.
|
||||||
|
let uimm8 = Builder::new_imm("uimm8")
|
||||||
|
.doc("An 8-bit immediate unsigned integer.")
|
||||||
|
.finish();
|
||||||
|
kinds.push(uimm8);
|
||||||
|
|
||||||
|
// An unsigned 32-bit immediate integer operand.
|
||||||
|
let uimm32 = Builder::new_imm("uimm32")
|
||||||
|
.doc("A 32-bit immediate unsigned integer.")
|
||||||
|
.finish();
|
||||||
|
kinds.push(uimm32);
|
||||||
|
|
||||||
|
// A 32-bit immediate signed offset.
|
||||||
|
//
|
||||||
|
// This is used to represent an immediate address offset in load/store
|
||||||
|
// instructions.
|
||||||
|
let offset32 = Builder::new_imm("offset32")
|
||||||
|
.doc("A 32-bit immediate signed offset.")
|
||||||
|
.default_member("offset")
|
||||||
|
.finish();
|
||||||
|
kinds.push(offset32);
|
||||||
|
|
||||||
|
// A 32-bit immediate floating point operand.
|
||||||
|
//
|
||||||
|
// IEEE 754-2008 binary32 interchange format.
|
||||||
|
let ieee32 = Builder::new_imm("ieee32")
|
||||||
|
.doc("A 32-bit immediate floating point number.")
|
||||||
|
.finish();
|
||||||
|
kinds.push(ieee32);
|
||||||
|
|
||||||
|
// A 64-bit immediate floating point operand.
|
||||||
|
//
|
||||||
|
// IEEE 754-2008 binary64 interchange format.
|
||||||
|
let ieee64 = Builder::new_imm("ieee64")
|
||||||
|
.doc("A 64-bit immediate floating point number.")
|
||||||
|
.finish();
|
||||||
|
kinds.push(ieee64);
|
||||||
|
|
||||||
|
// An immediate boolean operand.
|
||||||
|
//
|
||||||
|
// This type of immediate boolean can interact with SSA values with any
|
||||||
|
// BoolType type.
|
||||||
|
let boolean = Builder::new_imm("boolean")
|
||||||
|
.doc("An immediate boolean.")
|
||||||
|
.rust_type("bool")
|
||||||
|
.finish();
|
||||||
|
kinds.push(boolean);
|
||||||
|
|
||||||
|
// A condition code for comparing integer values.
|
||||||
|
// This enumerated operand kind is used for the `icmp` instruction and corresponds to the
|
||||||
|
// condcodes::IntCC` Rust type.
|
||||||
|
let mut intcc_values = HashMap::new();
|
||||||
|
intcc_values.insert("eq", "Equal");
|
||||||
|
intcc_values.insert("ne", "NotEqual");
|
||||||
|
intcc_values.insert("sge", "UnsignedGreaterThanOrEqual");
|
||||||
|
intcc_values.insert("sgt", "UnsignedGreaterThan");
|
||||||
|
intcc_values.insert("sle", "UnsignedLessThanOrEqual");
|
||||||
|
intcc_values.insert("slt", "UnsignedLessThan");
|
||||||
|
intcc_values.insert("uge", "UnsignedGreaterThanOrEqual");
|
||||||
|
intcc_values.insert("ugt", "UnsignedGreaterThan");
|
||||||
|
intcc_values.insert("ule", "UnsignedLessThanOrEqual");
|
||||||
|
intcc_values.insert("ult", "UnsignedLessThan");
|
||||||
|
let intcc = Builder::new_enum("intcc", intcc_values)
|
||||||
|
.doc("An integer comparison condition code.")
|
||||||
|
.default_member("cond")
|
||||||
|
.rust_type("ir::condcodes::IntCC")
|
||||||
|
.finish();
|
||||||
|
kinds.push(intcc);
|
||||||
|
|
||||||
|
// A condition code for comparing floating point values. This enumerated operand kind is used
|
||||||
|
// for the `fcmp` instruction and corresponds to the `condcodes::FloatCC` Rust type.
|
||||||
|
let mut floatcc_values = HashMap::new();
|
||||||
|
floatcc_values.insert("ord", "Ordered");
|
||||||
|
floatcc_values.insert("uno", "Unordered");
|
||||||
|
floatcc_values.insert("eq", "Equal");
|
||||||
|
floatcc_values.insert("ne", "NotEqual");
|
||||||
|
floatcc_values.insert("one", "OrderedNotEqual");
|
||||||
|
floatcc_values.insert("ueq", "UnorderedOrEqual");
|
||||||
|
floatcc_values.insert("lt", "LessThan");
|
||||||
|
floatcc_values.insert("le", "LessThanOrEqual");
|
||||||
|
floatcc_values.insert("gt", "GreaterThan");
|
||||||
|
floatcc_values.insert("ge", "GreaterThanOrEqual");
|
||||||
|
floatcc_values.insert("ult", "UnorderedOrLessThan");
|
||||||
|
floatcc_values.insert("ule", "UnorderedOrLessThanOrEqual");
|
||||||
|
floatcc_values.insert("ugt", "UnorderedOrGreaterThan");
|
||||||
|
floatcc_values.insert("uge", "UnorderedOrGreaterThanOrEqual");
|
||||||
|
let floatcc = Builder::new_enum("floatcc", floatcc_values)
|
||||||
|
.doc("A floating point comparison condition code")
|
||||||
|
.default_member("cond")
|
||||||
|
.rust_type("ir::condcodes::FloatCC")
|
||||||
|
.finish();
|
||||||
|
kinds.push(floatcc);
|
||||||
|
|
||||||
|
// Flags for memory operations like :clif:inst:`load` and :clif:inst:`store`.
|
||||||
|
let memflags = Builder::new_imm("memflags")
|
||||||
|
.doc("Memory operation flags")
|
||||||
|
.default_member("flags")
|
||||||
|
.rust_type("ir::MemFlags")
|
||||||
|
.finish();
|
||||||
|
kinds.push(memflags);
|
||||||
|
|
||||||
|
// A register unit in the current target ISA.
|
||||||
|
let regunit = Builder::new_imm("regunit")
|
||||||
|
.doc("A register unit in the target ISA")
|
||||||
|
.rust_type("isa::RegUnit")
|
||||||
|
.finish();
|
||||||
|
kinds.push(regunit);
|
||||||
|
|
||||||
|
// A trap code indicating the reason for trapping.
|
||||||
|
//
|
||||||
|
// The Rust enum type also has a `User(u16)` variant for user-provided trap
|
||||||
|
// codes.
|
||||||
|
let mut trapcode_values = HashMap::new();
|
||||||
|
trapcode_values.insert("stk_ovf", "StackOverflow");
|
||||||
|
trapcode_values.insert("heap_oob", "HeapOutOfBounds");
|
||||||
|
trapcode_values.insert("int_ovf", "IntegerOverflow");
|
||||||
|
trapcode_values.insert("int_divz", "IntegerDivisionByZero");
|
||||||
|
let trapcode = Builder::new_enum("trapcode", trapcode_values)
|
||||||
|
.doc("A trap reason code.")
|
||||||
|
.default_member("code")
|
||||||
|
.rust_type("ir::TrapCode")
|
||||||
|
.finish();
|
||||||
|
kinds.push(trapcode);
|
||||||
|
|
||||||
|
return kinds;
|
||||||
|
}
|
||||||
@@ -1,4 +1,55 @@
|
|||||||
//! Shared definitions for the Cranelift intermediate language.
|
//! Shared definitions for the Cranelift intermediate language.
|
||||||
|
|
||||||
|
pub mod entities;
|
||||||
|
pub mod formats;
|
||||||
|
pub mod immediates;
|
||||||
pub mod settings;
|
pub mod settings;
|
||||||
pub mod types;
|
pub mod types;
|
||||||
|
|
||||||
|
use crate::cdsl::formats::FormatRegistry;
|
||||||
|
use crate::cdsl::operands::OperandKind;
|
||||||
|
use crate::cdsl::settings::SettingGroup;
|
||||||
|
|
||||||
|
pub struct Definitions {
|
||||||
|
pub settings: SettingGroup,
|
||||||
|
pub operand_kinds: OperandKinds,
|
||||||
|
pub format_registry: FormatRegistry,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct OperandKinds(Vec<OperandKind>);
|
||||||
|
|
||||||
|
impl OperandKinds {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self(Vec::new())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn by_name(&self, name: &'static str) -> &OperandKind {
|
||||||
|
self.0
|
||||||
|
.iter()
|
||||||
|
.find(|op| op.name == name)
|
||||||
|
.expect(&format!("unknown Operand name: {}", name))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn push(&mut self, operand_kind: OperandKind) {
|
||||||
|
assert!(
|
||||||
|
self.0
|
||||||
|
.iter()
|
||||||
|
.find(|existing| existing.name == operand_kind.name)
|
||||||
|
.is_none(),
|
||||||
|
"trying to insert operand kind '{}' for the second time",
|
||||||
|
operand_kind.name
|
||||||
|
);
|
||||||
|
self.0.push(operand_kind);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn define() -> Definitions {
|
||||||
|
let immediates = OperandKinds(immediates::define());
|
||||||
|
let entities = OperandKinds(entities::define());
|
||||||
|
let format_registry = formats::define(&immediates, &entities);
|
||||||
|
Definitions {
|
||||||
|
settings: settings::define(),
|
||||||
|
operand_kinds: immediates,
|
||||||
|
format_registry,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user