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) -> Option<&str>42     pub fn doc(&self) -> Option<&str> {
43         if let Some(doc) = &self.doc {
44             return Some(doc);
45         }
46         match &self.kind.fields {
47             OperandKindFields::TypeVar(tvar) => Some(&tvar.doc),
48             _ => self.kind.doc(),
49         }
50     }
51 
is_value(&self) -> bool52     pub fn is_value(&self) -> bool {
53         match self.kind.fields {
54             OperandKindFields::TypeVar(_) => true,
55             _ => false,
56         }
57     }
58 
type_var(&self) -> Option<&TypeVar>59     pub fn type_var(&self) -> Option<&TypeVar> {
60         match &self.kind.fields {
61             OperandKindFields::TypeVar(typevar) => Some(typevar),
62             _ => None,
63         }
64     }
65 
is_varargs(&self) -> bool66     pub fn is_varargs(&self) -> bool {
67         match self.kind.fields {
68             OperandKindFields::VariableArgs => true,
69             _ => false,
70         }
71     }
72 
73     /// Returns true if the operand has an immediate kind or is an EntityRef.
is_immediate_or_entityref(&self) -> bool74     pub fn is_immediate_or_entityref(&self) -> bool {
75         match self.kind.fields {
76             OperandKindFields::ImmEnum(_)
77             | OperandKindFields::ImmValue
78             | OperandKindFields::EntityRef => true,
79             _ => false,
80         }
81     }
82 
83     /// Returns true if the operand has an immediate kind.
is_immediate(&self) -> bool84     pub fn is_immediate(&self) -> bool {
85         match self.kind.fields {
86             OperandKindFields::ImmEnum(_) | OperandKindFields::ImmValue => true,
87             _ => false,
88         }
89     }
90 
is_cpu_flags(&self) -> bool91     pub fn is_cpu_flags(&self) -> bool {
92         match &self.kind.fields {
93             OperandKindFields::TypeVar(type_var)
94                 if type_var.name == "iflags" || type_var.name == "fflags" =>
95             {
96                 true
97             }
98             _ => false,
99         }
100     }
101 }
102 
103 pub type EnumValues = HashMap<&'static str, &'static str>;
104 
105 #[derive(Clone, Debug)]
106 pub(crate) enum OperandKindFields {
107     EntityRef,
108     VariableArgs,
109     ImmValue,
110     ImmEnum(EnumValues),
111     TypeVar(TypeVar),
112 }
113 
114 #[derive(Clone, Debug)]
115 pub(crate) struct OperandKind {
116     /// String representation of the Rust type mapping to this OperandKind.
117     pub rust_type: &'static str,
118 
119     /// Name of this OperandKind in the format's member field.
120     pub rust_field_name: &'static str,
121 
122     /// Type-specific fields for this OperandKind.
123     pub fields: OperandKindFields,
124 
125     doc: Option<&'static str>,
126 }
127 
128 impl OperandKind {
new( rust_field_name: &'static str, rust_type: &'static str, fields: OperandKindFields, ) -> Self129     pub fn new(
130         rust_field_name: &'static str,
131         rust_type: &'static str,
132         fields: OperandKindFields,
133     ) -> Self {
134         Self {
135             rust_field_name,
136             rust_type,
137             fields,
138             doc: None,
139         }
140     }
with_doc(mut self, doc: &'static str) -> Self141     pub fn with_doc(mut self, doc: &'static str) -> Self {
142         assert!(self.doc.is_none());
143         self.doc = Some(doc);
144         self
145     }
doc(&self) -> Option<&str>146     fn doc(&self) -> Option<&str> {
147         if let Some(doc) = &self.doc {
148             return Some(doc);
149         }
150         match &self.fields {
151             OperandKindFields::TypeVar(type_var) => Some(&type_var.doc),
152             OperandKindFields::ImmEnum(_)
153             | OperandKindFields::ImmValue
154             | OperandKindFields::EntityRef
155             | OperandKindFields::VariableArgs => None,
156         }
157     }
158 }
159 
160 impl Into<OperandKind> for &TypeVar {
into(self) -> OperandKind161     fn into(self) -> OperandKind {
162         OperandKind::new(
163             "value",
164             "ir::Value",
165             OperandKindFields::TypeVar(self.into()),
166         )
167     }
168 }
169 impl Into<OperandKind> for &OperandKind {
into(self) -> OperandKind170     fn into(self) -> OperandKind {
171         self.clone()
172     }
173 }
174