cranelift_codegen_meta/cdsl/
operands.rs

1use std::collections::HashMap;
2
3use crate::cdsl::typevar::TypeVar;
4
5/// An instruction operand can be an *immediate*, an *SSA value*, or an *entity reference*. The
6/// type of the operand is one of:
7///
8/// 1. A `ValueType` instance indicates an SSA value operand with a concrete type.
9///
10/// 2. A `TypeVar` instance indicates an SSA value operand, and the instruction is polymorphic over
11///    the possible concrete types that the type variable can assume.
12///
13/// 3. An `ImmediateKind` instance indicates an immediate operand whose value is encoded in the
14///    instruction itself rather than being passed as an SSA value.
15///
16/// 4. An `EntityRefKind` instance indicates an operand that references another entity in the
17///    function, typically something declared in the function preamble.
18#[derive(Clone, Debug)]
19pub(crate) struct Operand {
20    /// Name of the operand variable, as it appears in function parameters, legalizations, etc.
21    pub name: &'static str,
22
23    /// Type of the operand.
24    pub kind: OperandKind,
25
26    doc: Option<&'static str>,
27}
28
29impl Operand {
30    pub fn new(name: &'static str, kind: impl Into<OperandKind>) -> Self {
31        Self {
32            name,
33            doc: None,
34            kind: kind.into(),
35        }
36    }
37    pub fn with_doc(mut self, doc: &'static str) -> Self {
38        self.doc = Some(doc);
39        self
40    }
41
42    pub fn doc(&self) -> &str {
43        if let Some(doc) = &self.doc {
44            return doc;
45        }
46        match &self.kind.fields {
47            OperandKindFields::TypeVar(tvar) => &tvar.doc,
48            _ => self.kind.doc(),
49        }
50    }
51
52    pub fn is_value(&self) -> bool {
53        matches!(self.kind.fields, OperandKindFields::TypeVar(_))
54    }
55
56    pub fn type_var(&self) -> Option<&TypeVar> {
57        match &self.kind.fields {
58            OperandKindFields::TypeVar(typevar) => Some(typevar),
59            _ => None,
60        }
61    }
62
63    pub fn is_varargs(&self) -> bool {
64        matches!(self.kind.fields, OperandKindFields::VariableArgs)
65    }
66
67    /// Returns true if the operand has an immediate kind or is an EntityRef.
68    pub fn is_immediate_or_entityref(&self) -> bool {
69        matches!(
70            self.kind.fields,
71            OperandKindFields::ImmEnum(_)
72                | OperandKindFields::ImmValue
73                | OperandKindFields::EntityRef
74        )
75    }
76
77    /// Returns true if the operand has an immediate kind.
78    pub fn is_immediate(&self) -> bool {
79        matches!(
80            self.kind.fields,
81            OperandKindFields::ImmEnum(_) | OperandKindFields::ImmValue
82        )
83    }
84}
85
86pub type EnumValues = HashMap<&'static str, &'static str>;
87
88#[derive(Clone, Debug)]
89pub(crate) enum OperandKindFields {
90    EntityRef,
91    VariableArgs,
92    ImmValue,
93    ImmEnum(EnumValues),
94    TypeVar(TypeVar),
95}
96
97impl OperandKindFields {
98    /// Return the [EnumValues] for this field if it is an `enum`.
99    pub(crate) fn enum_values(&self) -> Option<&EnumValues> {
100        match self {
101            OperandKindFields::ImmEnum(map) => Some(map),
102            _ => None,
103        }
104    }
105}
106
107#[derive(Clone, Debug)]
108pub(crate) struct OperandKind {
109    /// String representation of the Rust type mapping to this OperandKind.
110    pub rust_type: &'static str,
111
112    /// Name of this OperandKind in the format's member field.
113    pub rust_field_name: &'static str,
114
115    /// Type-specific fields for this OperandKind.
116    pub fields: OperandKindFields,
117
118    doc: Option<&'static str>,
119}
120
121impl OperandKind {
122    pub fn new(
123        rust_field_name: &'static str,
124        rust_type: &'static str,
125        fields: OperandKindFields,
126        doc: &'static str,
127    ) -> Self {
128        Self {
129            rust_field_name,
130            rust_type,
131            fields,
132            doc: Some(doc),
133        }
134    }
135    fn doc(&self) -> &str {
136        if let Some(doc) = &self.doc {
137            return doc;
138        }
139        match &self.fields {
140            OperandKindFields::TypeVar(type_var) => &type_var.doc,
141            // The only method to create an OperandKind with `doc` set to None is using a TypeVar,
142            // so all other options are unreachable here.
143            OperandKindFields::ImmEnum(_)
144            | OperandKindFields::ImmValue
145            | OperandKindFields::EntityRef
146            | OperandKindFields::VariableArgs => unreachable!(),
147        }
148    }
149
150    pub(crate) fn is_block(&self) -> bool {
151        self.rust_type == "ir::BlockCall"
152    }
153}
154
155impl From<&TypeVar> for OperandKind {
156    fn from(type_var: &TypeVar) -> Self {
157        OperandKind {
158            rust_field_name: "value",
159            rust_type: "ir::Value",
160            fields: OperandKindFields::TypeVar(type_var.into()),
161            doc: None,
162        }
163    }
164}
165impl From<&OperandKind> for OperandKind {
166    fn from(kind: &OperandKind) -> Self {
167        kind.clone()
168    }
169}