1 use std::collections::HashMap;
2 
3 use 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)]
19 pub(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 
29 impl Operand {
new(name: &'static str, kind: impl Into<OperandKind>) -> Self30     pub fn new(name: &'static str, kind: impl Into<OperandKind>) -> Self {
31         Self {
32             name,
33             doc: None,
34             kind: kind.into(),
35         }
36     }
with_doc(mut self, doc: &'static str) -> Self37     pub fn with_doc(mut self, doc: &'static str) -> Self {
38         self.doc = Some(doc);
39         self
40     }
41 
doc(&self) -> &str42     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 
is_value(&self) -> bool52     pub fn is_value(&self) -> bool {
53         matches!(self.kind.fields, OperandKindFields::TypeVar(_))
54     }
55 
type_var(&self) -> Option<&TypeVar>56     pub fn type_var(&self) -> Option<&TypeVar> {
57         match &self.kind.fields {
58             OperandKindFields::TypeVar(typevar) => Some(typevar),
59             _ => None,
60         }
61     }
62 
is_varargs(&self) -> bool63     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.
is_immediate_or_entityref(&self) -> bool68     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.
is_immediate(&self) -> bool78     pub fn is_immediate(&self) -> bool {
79         matches!(
80             self.kind.fields,
81             OperandKindFields::ImmEnum(_) | OperandKindFields::ImmValue
82         )
83     }
84 }
85 
86 pub type EnumValues = HashMap<&'static str, &'static str>;
87 
88 #[derive(Clone, Debug)]
89 pub(crate) enum OperandKindFields {
90     EntityRef,
91     VariableArgs,
92     ImmValue,
93     ImmEnum(EnumValues),
94     TypeVar(TypeVar),
95 }
96 
97 impl OperandKindFields {
98     /// Return the [EnumValues] for this field if it is an `enum`.
enum_values(&self) -> Option<&EnumValues>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)]
108 pub(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 
121 impl OperandKind {
new( rust_field_name: &'static str, rust_type: &'static str, fields: OperandKindFields, doc: &'static str, ) -> Self122     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     }
doc(&self) -> &str135     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 
is_block(&self) -> bool150     pub(crate) fn is_block(&self) -> bool {
151         self.rust_type == "ir::BlockCall"
152     }
153 
is_raw_block(&self) -> bool154     pub(crate) fn is_raw_block(&self) -> bool {
155         self.rust_type == "ir::Block"
156     }
157 }
158 
159 impl From<&TypeVar> for OperandKind {
from(type_var: &TypeVar) -> Self160     fn from(type_var: &TypeVar) -> Self {
161         OperandKind {
162             rust_field_name: "value",
163             rust_type: "ir::Value",
164             fields: OperandKindFields::TypeVar(type_var.into()),
165             doc: None,
166         }
167     }
168 }
169 impl From<&OperandKind> for OperandKind {
from(kind: &OperandKind) -> Self170     fn from(kind: &OperandKind) -> Self {
171         kind.clone()
172     }
173 }
174