1 use std::collections::HashMap;
2 
3 use crate::cdsl::camel_case;
4 use crate::cdsl::typevar::TypeVar;
5 
6 /// An instruction operand can be an *immediate*, an *SSA value*, or an *entity reference*. The
7 /// type of the operand is one of:
8 ///
9 /// 1. A `ValueType` instance indicates an SSA value operand with a concrete type.
10 ///
11 /// 2. A `TypeVar` instance indicates an SSA value operand, and the instruction is polymorphic over
12 ///    the possible concrete types that the type variable can assume.
13 ///
14 /// 3. An `ImmediateKind` instance indicates an immediate operand whose value is encoded in the
15 ///    instruction itself rather than being passed as an SSA value.
16 ///
17 /// 4. An `EntityRefKind` instance indicates an operand that references another entity in the
18 ///    function, typically something declared in the function preamble.
19 #[derive(Clone, Debug)]
20 pub(crate) struct Operand {
21     pub name: &'static str,
22     doc: Option<String>,
23     pub kind: OperandKind,
24 }
25 
26 impl Operand {
27     pub fn doc(&self) -> Option<&str> {
28         match &self.doc {
29             Some(doc) => Some(doc),
30             None => match &self.kind.fields {
31                 OperandKindFields::TypeVar(tvar) => Some(&tvar.doc),
32                 _ => self.kind.doc(),
33             },
34         }
35     }
36 
37     pub fn is_value(&self) -> bool {
38         match self.kind.fields {
39             OperandKindFields::TypeVar(_) => true,
40             _ => false,
41         }
42     }
43 
44     pub fn type_var(&self) -> Option<&TypeVar> {
45         match &self.kind.fields {
46             OperandKindFields::TypeVar(typevar) => Some(typevar),
47             _ => None,
48         }
49     }
50 
51     pub fn is_varargs(&self) -> bool {
52         match self.kind.fields {
53             OperandKindFields::VariableArgs => true,
54             _ => false,
55         }
56     }
57 
58     /// Returns true if the operand has an immediate kind or is an EntityRef.
59     pub fn is_immediate_or_entityref(&self) -> bool {
60         match self.kind.fields {
61             OperandKindFields::ImmEnum(_)
62             | OperandKindFields::ImmValue
63             | OperandKindFields::EntityRef => true,
64             _ => false,
65         }
66     }
67 
68     /// Returns true if the operand has an immediate kind.
69     pub fn is_immediate(&self) -> bool {
70         match self.kind.fields {
71             OperandKindFields::ImmEnum(_) | OperandKindFields::ImmValue => true,
72             _ => false,
73         }
74     }
75 
76     pub fn is_cpu_flags(&self) -> bool {
77         match &self.kind.fields {
78             OperandKindFields::TypeVar(type_var)
79                 if type_var.name == "iflags" || type_var.name == "fflags" =>
80             {
81                 true
82             }
83             _ => false,
84         }
85     }
86 }
87 
88 pub(crate) struct OperandBuilder {
89     name: &'static str,
90     doc: Option<String>,
91     kind: OperandKind,
92 }
93 
94 impl OperandBuilder {
95     pub fn new(name: &'static str, kind: OperandKind) -> Self {
96         Self {
97             name,
98             doc: None,
99             kind,
100         }
101     }
102     pub fn doc(mut self, doc: impl Into<String>) -> Self {
103         assert!(self.doc.is_none());
104         self.doc = Some(doc.into());
105         self
106     }
107     pub fn build(self) -> Operand {
108         Operand {
109             name: self.name,
110             doc: self.doc,
111             kind: self.kind,
112         }
113     }
114 }
115 
116 type EnumValues = HashMap<&'static str, &'static str>;
117 
118 #[derive(Clone, Debug)]
119 pub(crate) enum OperandKindFields {
120     EntityRef,
121     VariableArgs,
122     ImmValue,
123     ImmEnum(EnumValues),
124     TypeVar(TypeVar),
125 }
126 
127 #[derive(Clone, Debug)]
128 pub(crate) struct OperandKind {
129     pub name: &'static str,
130 
131     doc: Option<String>,
132 
133     pub default_member: Option<&'static str>,
134 
135     /// The camel-cased name of an operand kind is also the Rust type used to represent it.
136     pub rust_type: String,
137 
138     pub fields: OperandKindFields,
139 }
140 
141 impl OperandKind {
142     fn doc(&self) -> Option<&str> {
143         match &self.doc {
144             Some(doc) => Some(&doc),
145             None => match &self.fields {
146                 OperandKindFields::TypeVar(type_var) => Some(&type_var.doc),
147                 OperandKindFields::ImmEnum(_)
148                 | OperandKindFields::ImmValue
149                 | OperandKindFields::EntityRef
150                 | OperandKindFields::VariableArgs => None,
151             },
152         }
153     }
154 
155     pub fn imm_name(&self) -> Option<&str> {
156         match self.fields {
157             OperandKindFields::ImmEnum(_)
158             | OperandKindFields::ImmValue
159             | OperandKindFields::EntityRef => Some(&self.name),
160             _ => None,
161         }
162     }
163 }
164 
165 pub(crate) struct OperandKindBuilder {
166     name: &'static str,
167 
168     doc: Option<String>,
169 
170     default_member: Option<&'static str>,
171 
172     /// The camel-cased name of an operand kind is also the Rust type used to represent it.
173     rust_type: Option<String>,
174 
175     fields: OperandKindFields,
176 }
177 
178 impl OperandKindBuilder {
179     pub fn new(name: &'static str, fields: OperandKindFields) -> Self {
180         Self {
181             name,
182             doc: None,
183             default_member: None,
184             rust_type: None,
185             fields,
186         }
187     }
188 
189     pub fn new_imm(name: &'static str) -> Self {
190         Self {
191             name,
192             doc: None,
193             default_member: None,
194             rust_type: None,
195             fields: OperandKindFields::ImmValue,
196         }
197     }
198 
199     pub fn new_enum(name: &'static str, values: EnumValues) -> Self {
200         Self {
201             name,
202             doc: None,
203             default_member: None,
204             rust_type: None,
205             fields: OperandKindFields::ImmEnum(values),
206         }
207     }
208 
209     pub fn doc(mut self, doc: &'static str) -> Self {
210         assert!(self.doc.is_none());
211         self.doc = Some(doc.to_string());
212         self
213     }
214     pub fn default_member(mut self, default_member: &'static str) -> Self {
215         assert!(self.default_member.is_none());
216         self.default_member = Some(default_member);
217         self
218     }
219     pub fn rust_type(mut self, rust_type: &'static str) -> Self {
220         assert!(self.rust_type.is_none());
221         self.rust_type = Some(rust_type.to_string());
222         self
223     }
224 
225     pub fn build(self) -> OperandKind {
226         let default_member = match self.default_member {
227             Some(default_member) => Some(default_member),
228             None => match &self.fields {
229                 OperandKindFields::ImmEnum(_) | OperandKindFields::ImmValue => Some("imm"),
230                 OperandKindFields::TypeVar(_) | OperandKindFields::EntityRef => Some(self.name),
231                 OperandKindFields::VariableArgs => None,
232             },
233         };
234 
235         let rust_type = match self.rust_type {
236             Some(rust_type) => rust_type.to_string(),
237             None => match &self.fields {
238                 OperandKindFields::ImmEnum(_) | OperandKindFields::ImmValue => {
239                     format!("ir::immediates::{}", camel_case(self.name))
240                 }
241                 OperandKindFields::VariableArgs => "&[Value]".to_string(),
242                 OperandKindFields::TypeVar(_) | OperandKindFields::EntityRef => {
243                     format!("ir::{}", camel_case(self.name))
244                 }
245             },
246         };
247 
248         OperandKind {
249             name: self.name,
250             doc: self.doc,
251             default_member,
252             rust_type,
253             fields: self.fields,
254         }
255     }
256 }
257 
258 impl Into<OperandKind> for &TypeVar {
259     fn into(self) -> OperandKind {
260         OperandKindBuilder::new("value", OperandKindFields::TypeVar(self.into())).build()
261     }
262 }
263 impl Into<OperandKind> for &OperandKind {
264     fn into(self) -> OperandKind {
265         self.clone()
266     }
267 }
268 
269 /// Helper to create an operand in definitions files.
270 pub(crate) fn create_operand(name: &'static str, kind: impl Into<OperandKind>) -> Operand {
271     OperandBuilder::new(name, kind.into()).build()
272 }
273 
274 /// Helper to create an operand with a documentation in definitions files.
275 pub(crate) fn create_operand_doc(
276     name: &'static str,
277     kind: impl Into<OperandKind>,
278     doc: &'static str,
279 ) -> Operand {
280     OperandBuilder::new(name, kind.into()).doc(doc).build()
281 }
282