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<&'static str>,
23     pub kind: OperandKind,
24 }
25 
26 impl Operand {
27     pub fn new(name: &'static str, kind: impl Into<OperandKind>) -> Self {
28         Self {
29             name,
30             doc: None,
31             kind: kind.into(),
32         }
33     }
34     pub fn with_doc(mut self, doc: &'static str) -> Self {
35         self.doc = Some(doc);
36         self
37     }
38 
39     pub fn doc(&self) -> Option<&str> {
40         if let Some(doc) = &self.doc {
41             return Some(doc);
42         }
43         match &self.kind.fields {
44             OperandKindFields::TypeVar(tvar) => Some(&tvar.doc),
45             _ => self.kind.doc(),
46         }
47     }
48 
49     pub fn is_value(&self) -> bool {
50         match self.kind.fields {
51             OperandKindFields::TypeVar(_) => true,
52             _ => false,
53         }
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         match self.kind.fields {
65             OperandKindFields::VariableArgs => true,
66             _ => false,
67         }
68     }
69 
70     /// Returns true if the operand has an immediate kind or is an EntityRef.
71     pub fn is_immediate_or_entityref(&self) -> bool {
72         match self.kind.fields {
73             OperandKindFields::ImmEnum(_)
74             | OperandKindFields::ImmValue
75             | OperandKindFields::EntityRef => true,
76             _ => false,
77         }
78     }
79 
80     /// Returns true if the operand has an immediate kind.
81     pub fn is_immediate(&self) -> bool {
82         match self.kind.fields {
83             OperandKindFields::ImmEnum(_) | OperandKindFields::ImmValue => true,
84             _ => false,
85         }
86     }
87 
88     pub fn is_cpu_flags(&self) -> bool {
89         match &self.kind.fields {
90             OperandKindFields::TypeVar(type_var)
91                 if type_var.name == "iflags" || type_var.name == "fflags" =>
92             {
93                 true
94             }
95             _ => false,
96         }
97     }
98 }
99 
100 type EnumValues = HashMap<&'static str, &'static str>;
101 
102 #[derive(Clone, Debug)]
103 pub(crate) enum OperandKindFields {
104     EntityRef,
105     VariableArgs,
106     ImmValue,
107     ImmEnum(EnumValues),
108     TypeVar(TypeVar),
109 }
110 
111 #[derive(Clone, Debug)]
112 pub(crate) struct OperandKind {
113     pub name: &'static str,
114     doc: Option<&'static str>,
115     rust_field_name: Option<&'static str>,
116     /// The camel-cased name of an operand kind is also the Rust type used to represent it.
117     pub rust_type: String,
118     pub fields: OperandKindFields,
119 }
120 
121 impl OperandKind {
122     fn new(
123         name: &'static str,
124         doc: Option<&'static str>,
125         rust_field_name: Option<&'static str>,
126         rust_type: Option<&'static str>,
127         fields: OperandKindFields,
128     ) -> Self {
129         // Compute the default rust_type value, if it wasn't provided.
130         let rust_type = match rust_type {
131             Some(rust_type) => rust_type.to_string(),
132             None => match &fields {
133                 OperandKindFields::ImmEnum(_) | OperandKindFields::ImmValue => {
134                     format!("ir::immediates::{}", camel_case(name))
135                 }
136                 OperandKindFields::VariableArgs => "&[Value]".to_string(),
137                 OperandKindFields::TypeVar(_) | OperandKindFields::EntityRef => {
138                     format!("ir::{}", camel_case(name))
139                 }
140             },
141         };
142         Self {
143             name,
144             doc,
145             rust_field_name,
146             rust_type,
147             fields,
148         }
149     }
150 
151     /// Name of this OperandKind in the format's member field.
152     pub fn rust_field_name(&self) -> Option<&'static str> {
153         if let Some(member) = &self.rust_field_name {
154             return Some(member);
155         }
156         match &self.fields {
157             OperandKindFields::ImmEnum(_) | OperandKindFields::ImmValue => Some("imm"),
158             OperandKindFields::TypeVar(_) | OperandKindFields::EntityRef => Some(self.name),
159             OperandKindFields::VariableArgs => None,
160         }
161     }
162 
163     fn doc(&self) -> Option<&str> {
164         if let Some(doc) = &self.doc {
165             return Some(doc);
166         }
167         match &self.fields {
168             OperandKindFields::TypeVar(type_var) => Some(&type_var.doc),
169             OperandKindFields::ImmEnum(_)
170             | OperandKindFields::ImmValue
171             | OperandKindFields::EntityRef
172             | OperandKindFields::VariableArgs => None,
173         }
174     }
175 }
176 
177 impl Into<OperandKind> for &TypeVar {
178     fn into(self) -> OperandKind {
179         OperandKindBuilder::new("value", OperandKindFields::TypeVar(self.into())).build()
180     }
181 }
182 impl Into<OperandKind> for &OperandKind {
183     fn into(self) -> OperandKind {
184         self.clone()
185     }
186 }
187 
188 pub(crate) struct OperandKindBuilder {
189     name: &'static str,
190     doc: Option<&'static str>,
191     rust_field_name: Option<&'static str>,
192     rust_type: Option<&'static str>,
193     fields: OperandKindFields,
194 }
195 
196 impl OperandKindBuilder {
197     pub fn new(name: &'static str, fields: OperandKindFields) -> Self {
198         Self {
199             name,
200             doc: None,
201             rust_field_name: None,
202             rust_type: None,
203             fields,
204         }
205     }
206     pub fn new_imm(name: &'static str) -> Self {
207         Self {
208             name,
209             doc: None,
210             rust_field_name: None,
211             rust_type: None,
212             fields: OperandKindFields::ImmValue,
213         }
214     }
215     pub fn new_enum(name: &'static str, values: EnumValues) -> Self {
216         Self {
217             name,
218             doc: None,
219             rust_field_name: None,
220             rust_type: None,
221             fields: OperandKindFields::ImmEnum(values),
222         }
223     }
224     pub fn doc(mut self, doc: &'static str) -> Self {
225         assert!(self.doc.is_none());
226         self.doc = Some(doc);
227         self
228     }
229     pub fn rust_field_name(mut self, rust_field_name: &'static str) -> Self {
230         assert!(self.rust_field_name.is_none());
231         self.rust_field_name = Some(rust_field_name);
232         self
233     }
234     pub fn rust_type(mut self, rust_type: &'static str) -> Self {
235         assert!(self.rust_type.is_none());
236         self.rust_type = Some(rust_type);
237         self
238     }
239     pub fn build(self) -> OperandKind {
240         OperandKind::new(
241             self.name,
242             self.doc,
243             self.rust_field_name,
244             self.rust_type,
245             self.fields,
246         )
247     }
248 }
249