cranelift_codegen_meta/cdsl/
instructions.rs

1use std::fmt;
2use std::rc::Rc;
3
4use crate::cdsl::camel_case;
5use crate::cdsl::formats::InstructionFormat;
6use crate::cdsl::operands::Operand;
7use crate::cdsl::typevar::TypeVar;
8
9pub(crate) type AllInstructions = Vec<Instruction>;
10
11pub(crate) struct InstructionGroupBuilder<'all_inst> {
12    all_instructions: &'all_inst mut AllInstructions,
13}
14
15impl<'all_inst> InstructionGroupBuilder<'all_inst> {
16    pub fn new(all_instructions: &'all_inst mut AllInstructions) -> Self {
17        Self { all_instructions }
18    }
19
20    pub fn push(&mut self, builder: InstructionBuilder) {
21        let inst = builder.build();
22        self.all_instructions.push(inst);
23    }
24}
25
26#[derive(Debug)]
27pub(crate) struct PolymorphicInfo {
28    pub use_typevar_operand: bool,
29    pub ctrl_typevar: TypeVar,
30}
31
32#[derive(Debug)]
33pub(crate) struct InstructionContent {
34    /// Instruction mnemonic, also becomes opcode name.
35    pub name: String,
36    pub camel_name: String,
37
38    /// Documentation string.
39    pub doc: String,
40
41    /// Input operands. This can be a mix of SSA value operands and other operand kinds.
42    pub operands_in: Vec<Operand>,
43    /// Output operands. The output operands must be SSA values or `variable_args`.
44    pub operands_out: Vec<Operand>,
45
46    /// Instruction format.
47    pub format: Rc<InstructionFormat>,
48
49    /// One of the input or output operands is a free type variable. None if the instruction is not
50    /// polymorphic, set otherwise.
51    pub polymorphic_info: Option<PolymorphicInfo>,
52
53    /// Indices in operands_in of input operands that are values.
54    pub value_opnums: Vec<usize>,
55    /// Indices in operands_in of input operands that are immediates or entities.
56    pub imm_opnums: Vec<usize>,
57    /// Indices in operands_out of output operands that are values.
58    pub value_results: Vec<usize>,
59
60    /// True for instructions that terminate the block.
61    pub is_terminator: bool,
62    /// True for all branch or jump instructions.
63    pub is_branch: bool,
64    /// Is this a call instruction?
65    pub is_call: bool,
66    /// Is this a return instruction?
67    pub is_return: bool,
68    /// Can this instruction read from memory?
69    pub can_load: bool,
70    /// Can this instruction write to memory?
71    pub can_store: bool,
72    /// Can this instruction cause a trap?
73    pub can_trap: bool,
74    /// Does this instruction have other side effects besides can_* flags?
75    pub other_side_effects: bool,
76    /// Despite having other side effects, is this instruction okay to GVN?
77    pub side_effects_idempotent: bool,
78}
79
80impl InstructionContent {
81    pub fn snake_name(&self) -> &str {
82        if &self.name == "return" {
83            "return_"
84        } else {
85            &self.name
86        }
87    }
88}
89
90pub(crate) type Instruction = Rc<InstructionContent>;
91
92impl fmt::Display for InstructionContent {
93    fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
94        if !self.operands_out.is_empty() {
95            let operands_out = self
96                .operands_out
97                .iter()
98                .map(|op| op.name)
99                .collect::<Vec<_>>()
100                .join(", ");
101            fmt.write_str(&operands_out)?;
102            fmt.write_str(" = ")?;
103        }
104
105        fmt.write_str(&self.name)?;
106
107        if !self.operands_in.is_empty() {
108            let operands_in = self
109                .operands_in
110                .iter()
111                .map(|op| op.name)
112                .collect::<Vec<_>>()
113                .join(", ");
114            fmt.write_str(" ")?;
115            fmt.write_str(&operands_in)?;
116        }
117
118        Ok(())
119    }
120}
121
122pub(crate) struct InstructionBuilder {
123    name: String,
124    doc: String,
125    format: Rc<InstructionFormat>,
126    operands_in: Option<Vec<Operand>>,
127    operands_out: Option<Vec<Operand>>,
128
129    // See Instruction comments for the meaning of these fields.
130    is_terminator: bool,
131    is_branch: bool,
132    is_call: bool,
133    is_return: bool,
134    can_load: bool,
135    can_store: bool,
136    can_trap: bool,
137    other_side_effects: bool,
138    side_effects_idempotent: bool,
139}
140
141impl InstructionBuilder {
142    pub fn new<S: Into<String>>(name: S, doc: S, format: &Rc<InstructionFormat>) -> Self {
143        Self {
144            name: name.into(),
145            doc: doc.into(),
146            format: format.clone(),
147            operands_in: None,
148            operands_out: None,
149
150            is_terminator: false,
151            is_branch: false,
152            is_call: false,
153            is_return: false,
154            can_load: false,
155            can_store: false,
156            can_trap: false,
157            other_side_effects: false,
158            side_effects_idempotent: false,
159        }
160    }
161
162    pub fn operands_in(mut self, operands: Vec<Operand>) -> Self {
163        assert!(self.operands_in.is_none());
164        self.operands_in = Some(operands);
165        self
166    }
167
168    pub fn operands_out(mut self, operands: Vec<Operand>) -> Self {
169        assert!(self.operands_out.is_none());
170        self.operands_out = Some(operands);
171        self
172    }
173
174    /// Mark this instruction as a block terminator.
175    pub fn terminates_block(mut self) -> Self {
176        self.is_terminator = true;
177        self
178    }
179
180    /// Mark this instruction as a branch instruction. This also implies that the instruction is a
181    /// block terminator.
182    pub fn branches(mut self) -> Self {
183        self.is_branch = true;
184        self.terminates_block()
185    }
186
187    /// Mark this instruction as a call instruction.
188    pub fn call(mut self) -> Self {
189        self.is_call = true;
190        self
191    }
192
193    /// Mark this instruction as a return instruction. This also implies that the instruction is a
194    /// block terminator.
195    pub fn returns(mut self) -> Self {
196        self.is_return = true;
197        self.terminates_block()
198    }
199
200    /// Mark this instruction as one that can load from memory.
201    pub fn can_load(mut self) -> Self {
202        self.can_load = true;
203        self
204    }
205
206    /// Mark this instruction as one that can store to memory.
207    pub fn can_store(mut self) -> Self {
208        self.can_store = true;
209        self
210    }
211
212    /// Mark this instruction as possibly trapping.
213    pub fn can_trap(mut self) -> Self {
214        self.can_trap = true;
215        self
216    }
217
218    /// Mark this instruction as one that has side-effects.
219    pub fn other_side_effects(mut self) -> Self {
220        self.other_side_effects = true;
221        self
222    }
223
224    /// Mark this instruction as one whose side-effects may be de-duplicated.
225    pub fn side_effects_idempotent(mut self) -> Self {
226        self.side_effects_idempotent = true;
227        self
228    }
229
230    fn build(self) -> Instruction {
231        let operands_in = self.operands_in.unwrap_or_default();
232        let operands_out = self.operands_out.unwrap_or_default();
233
234        let mut value_opnums = Vec::new();
235        let mut imm_opnums = Vec::new();
236        for (i, op) in operands_in.iter().enumerate() {
237            if op.is_value() {
238                value_opnums.push(i);
239            } else if op.is_immediate_or_entityref() {
240                imm_opnums.push(i);
241            } else {
242                assert!(op.is_varargs());
243            }
244        }
245
246        let value_results = operands_out
247            .iter()
248            .enumerate()
249            .filter_map(|(i, op)| if op.is_value() { Some(i) } else { None })
250            .collect();
251
252        verify_format(&self.name, &operands_in, &self.format);
253
254        let polymorphic_info =
255            verify_polymorphic(&operands_in, &operands_out, &self.format, &value_opnums);
256
257        let camel_name = camel_case(&self.name);
258
259        Rc::new(InstructionContent {
260            name: self.name,
261            camel_name,
262            doc: self.doc,
263            operands_in,
264            operands_out,
265            format: self.format,
266            polymorphic_info,
267            value_opnums,
268            value_results,
269            imm_opnums,
270            is_terminator: self.is_terminator,
271            is_branch: self.is_branch,
272            is_call: self.is_call,
273            is_return: self.is_return,
274            can_load: self.can_load,
275            can_store: self.can_store,
276            can_trap: self.can_trap,
277            other_side_effects: self.other_side_effects,
278            side_effects_idempotent: self.side_effects_idempotent,
279        })
280    }
281}
282
283/// Checks that the input operands actually match the given format.
284fn verify_format(inst_name: &str, operands_in: &[Operand], format: &InstructionFormat) {
285    // A format is defined by:
286    // - its number of input value operands,
287    // - its number and names of input immediate operands,
288    // - whether it has a value list or not.
289    let mut num_values = 0;
290    let mut num_blocks = 0;
291    let mut num_immediates = 0;
292
293    for operand in operands_in.iter() {
294        if operand.is_varargs() {
295            assert!(
296                format.has_value_list,
297                "instruction {} has varargs, but its format {} doesn't have a value list; you may \
298                 need to use a different format.",
299                inst_name, format.name
300            );
301        }
302        if operand.is_value() {
303            num_values += 1;
304        }
305        if operand.kind.is_block() {
306            num_blocks += 1;
307        } else if operand.is_immediate_or_entityref() {
308            if let Some(format_field) = format.imm_fields.get(num_immediates) {
309                assert_eq!(
310                    format_field.kind.rust_field_name,
311                    operand.kind.rust_field_name,
312                    "{}th operand of {} should be {} (according to format), not {} (according to \
313                     inst definition). You may need to use a different format.",
314                    num_immediates,
315                    inst_name,
316                    format_field.kind.rust_field_name,
317                    operand.kind.rust_field_name
318                );
319                num_immediates += 1;
320            }
321        }
322    }
323
324    assert_eq!(
325        num_values, format.num_value_operands,
326        "inst {} doesn't have as many value input operands as its format {} declares; you may need \
327         to use a different format.",
328        inst_name, format.name
329    );
330
331    assert_eq!(
332        num_blocks, format.num_block_operands,
333        "inst {} doesn't have as many block input operands as its format {} declares; you may need \
334        to use a different format.",
335        inst_name, format.name,
336    );
337
338    assert_eq!(
339        num_immediates,
340        format.imm_fields.len(),
341        "inst {} doesn't have as many immediate input \
342         operands as its format {} declares; you may need to use a different format.",
343        inst_name,
344        format.name
345    );
346}
347
348/// Check if this instruction is polymorphic, and verify its use of type variables.
349fn verify_polymorphic(
350    operands_in: &[Operand],
351    operands_out: &[Operand],
352    format: &InstructionFormat,
353    value_opnums: &[usize],
354) -> Option<PolymorphicInfo> {
355    // The instruction is polymorphic if it has one free input or output operand.
356    let is_polymorphic = operands_in
357        .iter()
358        .any(|op| op.is_value() && op.type_var().unwrap().free_typevar().is_some())
359        || operands_out
360            .iter()
361            .any(|op| op.is_value() && op.type_var().unwrap().free_typevar().is_some());
362
363    if !is_polymorphic {
364        return None;
365    }
366
367    // Verify the use of type variables.
368    let tv_op = format.typevar_operand;
369    let mut maybe_error_message = None;
370    if let Some(tv_op) = tv_op {
371        if tv_op < value_opnums.len() {
372            let op_num = value_opnums[tv_op];
373            let tv = operands_in[op_num].type_var().unwrap();
374            let free_typevar = tv.free_typevar();
375            if (free_typevar.is_some() && tv == &free_typevar.unwrap())
376                || tv.singleton_type().is_some()
377            {
378                match is_ctrl_typevar_candidate(tv, operands_in, operands_out) {
379                    Ok(_other_typevars) => {
380                        return Some(PolymorphicInfo {
381                            use_typevar_operand: true,
382                            ctrl_typevar: tv.clone(),
383                        });
384                    }
385                    Err(error_message) => {
386                        maybe_error_message = Some(error_message);
387                    }
388                }
389            }
390        }
391    };
392
393    // If we reached here, it means the type variable indicated as the typevar operand couldn't
394    // control every other input and output type variable. We need to look at the result type
395    // variables.
396    if operands_out.is_empty() {
397        // No result means no other possible type variable, so it's a type inference failure.
398        match maybe_error_message {
399            Some(msg) => panic!("{}", msg),
400            None => panic!("typevar_operand must be a free type variable"),
401        }
402    }
403
404    // Otherwise, try to infer the controlling type variable by looking at the first result.
405    let tv = operands_out[0].type_var().unwrap();
406    let free_typevar = tv.free_typevar();
407    if free_typevar.is_some() && tv != &free_typevar.unwrap() {
408        panic!("first result must be a free type variable");
409    }
410
411    // At this point, if the next unwrap() fails, it means the output type couldn't be used as a
412    // controlling type variable either; panicking is the right behavior.
413    is_ctrl_typevar_candidate(tv, operands_in, operands_out).unwrap();
414
415    Some(PolymorphicInfo {
416        use_typevar_operand: false,
417        ctrl_typevar: tv.clone(),
418    })
419}
420
421/// Verify that the use of TypeVars is consistent with `ctrl_typevar` as the controlling type
422/// variable.
423///
424/// All polymorphic inputs must either be derived from `ctrl_typevar` or be independent free type
425/// variables only used once.
426///
427/// All polymorphic results must be derived from `ctrl_typevar`.
428///
429/// Return a vector of other type variables used, or a string explaining what went wrong.
430fn is_ctrl_typevar_candidate(
431    ctrl_typevar: &TypeVar,
432    operands_in: &[Operand],
433    operands_out: &[Operand],
434) -> Result<Vec<TypeVar>, String> {
435    let mut other_typevars = Vec::new();
436
437    // Check value inputs.
438    for input in operands_in {
439        if !input.is_value() {
440            continue;
441        }
442
443        let typ = input.type_var().unwrap();
444        let free_typevar = typ.free_typevar();
445
446        // Non-polymorphic or derived from ctrl_typevar is OK.
447        if free_typevar.is_none() {
448            continue;
449        }
450        let free_typevar = free_typevar.unwrap();
451        if &free_typevar == ctrl_typevar {
452            continue;
453        }
454
455        // No other derived typevars allowed.
456        if typ != &free_typevar {
457            return Err(format!(
458                "{:?}: type variable {} must be derived from {:?} while it is derived from {:?}",
459                input, typ.name, ctrl_typevar, free_typevar
460            ));
461        }
462
463        // Other free type variables can only be used once each.
464        for other_tv in &other_typevars {
465            if &free_typevar == other_tv {
466                return Err(format!(
467                    "non-controlling type variable {} can't be used more than once",
468                    free_typevar.name
469                ));
470            }
471        }
472
473        other_typevars.push(free_typevar);
474    }
475
476    // Check outputs.
477    for result in operands_out {
478        if !result.is_value() {
479            continue;
480        }
481
482        let typ = result.type_var().unwrap();
483        let free_typevar = typ.free_typevar();
484
485        // Non-polymorphic or derived from ctrl_typevar is OK.
486        if free_typevar.is_none() || &free_typevar.unwrap() == ctrl_typevar {
487            continue;
488        }
489
490        return Err("type variable in output not derived from ctrl_typevar".into());
491    }
492
493    Ok(other_typevars)
494}