Skip to main content

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