1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
use std::collections::HashMap;

use crate::cdsl::typevar::TypeVar;

/// An instruction operand can be an *immediate*, an *SSA value*, or an *entity reference*. The
/// type of the operand is one of:
///
/// 1. A `ValueType` instance indicates an SSA value operand with a concrete type.
///
/// 2. A `TypeVar` instance indicates an SSA value operand, and the instruction is polymorphic over
///    the possible concrete types that the type variable can assume.
///
/// 3. An `ImmediateKind` instance indicates an immediate operand whose value is encoded in the
///    instruction itself rather than being passed as an SSA value.
///
/// 4. An `EntityRefKind` instance indicates an operand that references another entity in the
///    function, typically something declared in the function preamble.
#[derive(Clone, Debug)]
pub(crate) struct Operand {
    /// Name of the operand variable, as it appears in function parameters, legalizations, etc.
    pub name: &'static str,

    /// Type of the operand.
    pub kind: OperandKind,

    doc: Option<&'static str>,
}

impl Operand {
    pub fn new(name: &'static str, kind: impl Into<OperandKind>) -> Self {
        Self {
            name,
            doc: None,
            kind: kind.into(),
        }
    }
    pub fn with_doc(mut self, doc: &'static str) -> Self {
        self.doc = Some(doc);
        self
    }

    pub fn doc(&self) -> &str {
        if let Some(doc) = &self.doc {
            return doc;
        }
        match &self.kind.fields {
            OperandKindFields::TypeVar(tvar) => &tvar.doc,
            _ => self.kind.doc(),
        }
    }

    pub fn is_value(&self) -> bool {
        matches!(self.kind.fields, OperandKindFields::TypeVar(_))
    }

    pub fn type_var(&self) -> Option<&TypeVar> {
        match &self.kind.fields {
            OperandKindFields::TypeVar(typevar) => Some(typevar),
            _ => None,
        }
    }

    pub fn is_varargs(&self) -> bool {
        matches!(self.kind.fields, OperandKindFields::VariableArgs)
    }

    /// Returns true if the operand has an immediate kind or is an EntityRef.
    pub fn is_immediate_or_entityref(&self) -> bool {
        matches!(
            self.kind.fields,
            OperandKindFields::ImmEnum(_)
                | OperandKindFields::ImmValue
                | OperandKindFields::EntityRef
        )
    }

    /// Returns true if the operand has an immediate kind.
    pub fn is_immediate(&self) -> bool {
        matches!(
            self.kind.fields,
            OperandKindFields::ImmEnum(_) | OperandKindFields::ImmValue
        )
    }
}

pub type EnumValues = HashMap<&'static str, &'static str>;

#[derive(Clone, Debug)]
pub(crate) enum OperandKindFields {
    EntityRef,
    VariableArgs,
    ImmValue,
    ImmEnum(EnumValues),
    TypeVar(TypeVar),
}

impl OperandKindFields {
    /// Return the [EnumValues] for this field if it is an `enum`.
    pub(crate) fn enum_values(&self) -> Option<&EnumValues> {
        match self {
            OperandKindFields::ImmEnum(map) => Some(map),
            _ => None,
        }
    }
}

#[derive(Clone, Debug)]
pub(crate) struct OperandKind {
    /// String representation of the Rust type mapping to this OperandKind.
    pub rust_type: &'static str,

    /// Name of this OperandKind in the format's member field.
    pub rust_field_name: &'static str,

    /// Type-specific fields for this OperandKind.
    pub fields: OperandKindFields,

    doc: Option<&'static str>,
}

impl OperandKind {
    pub fn new(
        rust_field_name: &'static str,
        rust_type: &'static str,
        fields: OperandKindFields,
        doc: &'static str,
    ) -> Self {
        Self {
            rust_field_name,
            rust_type,
            fields,
            doc: Some(doc),
        }
    }
    fn doc(&self) -> &str {
        if let Some(doc) = &self.doc {
            return doc;
        }
        match &self.fields {
            OperandKindFields::TypeVar(type_var) => &type_var.doc,
            // The only method to create an OperandKind with `doc` set to None is using a TypeVar,
            // so all other options are unreachable here.
            OperandKindFields::ImmEnum(_)
            | OperandKindFields::ImmValue
            | OperandKindFields::EntityRef
            | OperandKindFields::VariableArgs => unreachable!(),
        }
    }

    pub(crate) fn is_block(&self) -> bool {
        self.rust_type == "ir::BlockCall"
    }
}

impl From<&TypeVar> for OperandKind {
    fn from(type_var: &TypeVar) -> Self {
        OperandKind {
            rust_field_name: "value",
            rust_type: "ir::Value",
            fields: OperandKindFields::TypeVar(type_var.into()),
            doc: None,
        }
    }
}
impl From<&OperandKind> for OperandKind {
    fn from(kind: &OperandKind) -> Self {
        kind.clone()
    }
}