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    /// Index of the value input operand that is used to infer the controlling type variable. By
44    /// default, this is `0`, the first `value` operand. The index is relative to the values only,
45    /// ignoring immediate operands.
46    pub typevar_operand: Option<usize>,
47}
48
49/// A tuple serving as a key to deduplicate InstructionFormat.
50#[derive(Hash, PartialEq, Eq)]
51pub(crate) struct FormatStructure {
52    pub num_value_operands: usize,
53    pub has_value_list: bool,
54    pub num_block_operands: usize,
55    /// Tuples of (Rust field name / Rust type) for each immediate field.
56    pub imm_field_names: Vec<(&'static str, &'static str)>,
57}
58
59impl fmt::Display for InstructionFormat {
60    fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
61        let imm_args = self
62            .imm_fields
63            .iter()
64            .map(|field| format!("{}: {}", field.member, field.kind.rust_type))
65            .collect::<Vec<_>>()
66            .join(", ");
67        fmt.write_fmt(format_args!(
68            "{}(imms=({}), vals={}, blocks={})",
69            self.name, imm_args, self.num_value_operands, self.num_block_operands,
70        ))?;
71        Ok(())
72    }
73}
74
75impl InstructionFormat {
76    /// Returns a tuple that uniquely identifies the structure.
77    pub fn structure(&self) -> FormatStructure {
78        FormatStructure {
79            num_value_operands: self.num_value_operands,
80            has_value_list: self.has_value_list,
81            num_block_operands: self.num_block_operands,
82            imm_field_names: self
83                .imm_fields
84                .iter()
85                .map(|field| (field.kind.rust_field_name, field.kind.rust_type))
86                .collect::<Vec<_>>(),
87        }
88    }
89}
90
91pub(crate) struct InstructionFormatBuilder(InstructionFormat);
92
93impl InstructionFormatBuilder {
94    pub fn new(name: &'static str) -> Self {
95        Self(InstructionFormat {
96            name,
97            num_value_operands: 0,
98            has_value_list: false,
99            num_block_operands: 0,
100            imm_fields: Vec::new(),
101            typevar_operand: None,
102        })
103    }
104
105    pub fn value(mut self) -> Self {
106        self.0.num_value_operands += 1;
107        self
108    }
109
110    pub fn varargs(mut self) -> Self {
111        self.0.has_value_list = true;
112        self
113    }
114
115    pub fn block(mut self) -> Self {
116        self.0.num_block_operands += 1;
117        self
118    }
119
120    pub fn imm(mut self, operand_kind: &OperandKind) -> Self {
121        let field = FormatField {
122            kind: operand_kind.clone(),
123            member: operand_kind.rust_field_name,
124        };
125        self.0.imm_fields.push(field);
126        self
127    }
128
129    pub fn typevar_operand(mut self, operand_index: usize) -> Self {
130        assert!(self.0.typevar_operand.is_none());
131        assert!(operand_index < self.0.num_value_operands);
132        self.0.typevar_operand = Some(operand_index);
133        self
134    }
135
136    pub fn build(mut self) -> Rc<InstructionFormat> {
137        if self.0.typevar_operand.is_none() && self.0.num_value_operands > 0 {
138            // Default to the first value operand, if there's one.
139            self.0.typevar_operand = Some(0);
140        };
141
142        Rc::new(self.0)
143    }
144}