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