cranelift_codegen_meta/cdsl/
formats.rs

1use crate::cdsl::operands::OperandKind;
2use std::fmt;
3use std::rc::Rc;
4
5/// An immediate field in an instruction format.
6///
7/// This corresponds to a single member of a variant of the `InstructionData`
8/// data type.
9#[derive(Debug)]
10pub(crate) struct FormatField {
11    /// Immediate operand kind.
12    pub kind: OperandKind,
13
14    /// Member name in InstructionData variant.
15    pub member: &'static str,
16}
17
18/// Every instruction opcode has a corresponding instruction format which determines the number of
19/// operands and their kinds. Instruction formats are identified structurally, i.e., the format of
20/// an instruction is derived from the kinds of operands used in its declaration.
21///
22/// The instruction format stores two separate lists of operands: Immediates and values. Immediate
23/// operands (including entity references) are represented as explicit members in the
24/// `InstructionData` variants. The value operands are stored differently, depending on how many
25/// there are.  Beyond a certain point, instruction formats switch to an external value list for
26/// storing value arguments. Value lists can hold an arbitrary number of values.
27///
28/// All instruction formats must be predefined in the meta shared/formats.rs module.
29#[derive(Debug)]
30pub(crate) struct InstructionFormat {
31    /// Instruction format name in CamelCase. This is used as a Rust variant name in both the
32    /// `InstructionData` and `InstructionFormat` enums.
33    pub name: &'static str,
34
35    pub num_value_operands: usize,
36
37    pub has_value_list: bool,
38
39    pub imm_fields: Vec<FormatField>,
40
41    pub num_block_operands: usize,
42
43    pub num_raw_block_operands: usize,
44
45    /// Index of the value input operand that is used to infer the controlling type variable. By
46    /// default, this is `0`, the first `value` operand. The index is relative to the values only,
47    /// ignoring immediate operands.
48    pub typevar_operand: Option<usize>,
49}
50
51/// A tuple serving as a key to deduplicate InstructionFormat.
52#[derive(Hash, PartialEq, Eq)]
53pub(crate) struct FormatStructure {
54    pub num_value_operands: usize,
55    pub has_value_list: bool,
56    pub num_block_operands: usize,
57    pub num_raw_block_operands: usize,
58    /// Tuples of (Rust field name / Rust type) for each immediate field.
59    pub imm_field_names: Vec<(&'static str, &'static str)>,
60}
61
62impl fmt::Display for InstructionFormat {
63    fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
64        let imm_args = self
65            .imm_fields
66            .iter()
67            .map(|field| format!("{}: {}", field.member, field.kind.rust_type))
68            .collect::<Vec<_>>()
69            .join(", ");
70        fmt.write_fmt(format_args!(
71            "{}(imms=({}), vals={}, blocks={}, raw_blocks={})",
72            self.name,
73            imm_args,
74            self.num_value_operands,
75            self.num_block_operands,
76            self.num_raw_block_operands,
77        ))?;
78        Ok(())
79    }
80}
81
82impl InstructionFormat {
83    /// Returns a tuple that uniquely identifies the structure.
84    pub fn structure(&self) -> FormatStructure {
85        FormatStructure {
86            num_value_operands: self.num_value_operands,
87            has_value_list: self.has_value_list,
88            num_block_operands: self.num_block_operands,
89            num_raw_block_operands: self.num_raw_block_operands,
90            imm_field_names: self
91                .imm_fields
92                .iter()
93                .map(|field| (field.kind.rust_field_name, field.kind.rust_type))
94                .collect::<Vec<_>>(),
95        }
96    }
97}
98
99pub(crate) struct InstructionFormatBuilder(InstructionFormat);
100
101impl InstructionFormatBuilder {
102    pub fn new(name: &'static str) -> Self {
103        Self(InstructionFormat {
104            name,
105            num_value_operands: 0,
106            has_value_list: false,
107            num_block_operands: 0,
108            num_raw_block_operands: 0,
109            imm_fields: Vec::new(),
110            typevar_operand: None,
111        })
112    }
113
114    pub fn value(mut self) -> Self {
115        self.0.num_value_operands += 1;
116        self
117    }
118
119    pub fn varargs(mut self) -> Self {
120        self.0.has_value_list = true;
121        self
122    }
123
124    pub fn block(mut self) -> Self {
125        self.0.num_block_operands += 1;
126        self
127    }
128
129    pub fn raw_block(mut self) -> Self {
130        self.0.num_raw_block_operands += 1;
131        self
132    }
133
134    pub fn imm(mut self, operand_kind: &OperandKind) -> Self {
135        let field = FormatField {
136            kind: operand_kind.clone(),
137            member: operand_kind.rust_field_name,
138        };
139        self.0.imm_fields.push(field);
140        self
141    }
142
143    pub fn typevar_operand(mut self, operand_index: usize) -> Self {
144        assert!(self.0.typevar_operand.is_none());
145        assert!(operand_index < self.0.num_value_operands);
146        self.0.typevar_operand = Some(operand_index);
147        self
148    }
149
150    pub fn build(mut self) -> Rc<InstructionFormat> {
151        if self.0.typevar_operand.is_none() && self.0.num_value_operands > 0 {
152            // Default to the first value operand, if there's one.
153            self.0.typevar_operand = Some(0);
154        };
155
156        Rc::new(self.0)
157    }
158}