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