285 lines
8.0 KiB
Rust
285 lines
8.0 KiB
Rust
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.
|
|
pub fn is_immediate_or_entityref(&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 build(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,
|
|
|
|
pub fields: OperandKindFields,
|
|
}
|
|
|
|
impl OperandKind {
|
|
pub fn imm_name(&self) -> Option<&str> {
|
|
match self.fields {
|
|
OperandKindFields::ImmEnum(_)
|
|
| OperandKindFields::ImmValue
|
|
| OperandKindFields::EntityRef => Some(&self.name),
|
|
_ => 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 build(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())).build()
|
|
}
|
|
}
|
|
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()).build()
|
|
}
|
|
|
|
/// 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).build()
|
|
}
|