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