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_raw_blocks = 0;
292    let mut num_immediates = 0;
293
294    for operand in operands_in.iter() {
295        if operand.is_varargs() {
296            assert!(
297                format.has_value_list,
298                "instruction {} has varargs, but its format {} doesn't have a value list; you may \
299                 need to use a different format.",
300                inst_name, format.name
301            );
302        }
303        if operand.is_value() {
304            num_values += 1;
305        }
306        if operand.kind.is_block() {
307            num_blocks += 1;
308        } else if operand.kind.is_raw_block() {
309            num_raw_blocks += 1;
310        } else if operand.is_immediate_or_entityref() {
311            if let Some(format_field) = format.imm_fields.get(num_immediates) {
312                assert_eq!(
313                    format_field.kind.rust_field_name,
314                    operand.kind.rust_field_name,
315                    "{}th operand of {} should be {} (according to format), not {} (according to \
316                     inst definition). You may need to use a different format.",
317                    num_immediates,
318                    inst_name,
319                    format_field.kind.rust_field_name,
320                    operand.kind.rust_field_name
321                );
322                num_immediates += 1;
323            }
324        }
325    }
326
327    assert_eq!(
328        num_values, format.num_value_operands,
329        "inst {} doesn't have as many value input operands as its format {} declares; you may need \
330         to use a different format.",
331        inst_name, format.name
332    );
333
334    assert_eq!(
335        num_blocks, format.num_block_operands,
336        "inst {} doesn't have as many block input operands as its format {} declares; you may need \
337        to use a different format.",
338        inst_name, format.name,
339    );
340
341    assert_eq!(
342        num_raw_blocks, format.num_raw_block_operands,
343        "inst {} doesn't have as many raw-block input operands as its format {} declares; you may need \
344         to use a different format.",
345        inst_name, format.name,
346    );
347
348    assert_eq!(
349        num_immediates,
350        format.imm_fields.len(),
351        "inst {} doesn't have as many immediate input \
352         operands as its format {} declares; you may need to use a different format.",
353        inst_name,
354        format.name
355    );
356}
357
358/// Check if this instruction is polymorphic, and verify its use of type variables.
359fn verify_polymorphic(
360    operands_in: &[Operand],
361    operands_out: &[Operand],
362    format: &InstructionFormat,
363    value_opnums: &[usize],
364) -> Option<PolymorphicInfo> {
365    // The instruction is polymorphic if it has one free input or output operand.
366    let is_polymorphic = operands_in
367        .iter()
368        .any(|op| op.is_value() && op.type_var().unwrap().free_typevar().is_some())
369        || operands_out
370            .iter()
371            .any(|op| op.is_value() && op.type_var().unwrap().free_typevar().is_some());
372
373    if !is_polymorphic {
374        return None;
375    }
376
377    // Verify the use of type variables.
378    let tv_op = format.typevar_operand;
379    let mut maybe_error_message = None;
380    if let Some(tv_op) = tv_op {
381        if tv_op < value_opnums.len() {
382            let op_num = value_opnums[tv_op];
383            let tv = operands_in[op_num].type_var().unwrap();
384            let free_typevar = tv.free_typevar();
385            if (free_typevar.is_some() && tv == &free_typevar.unwrap())
386                || tv.singleton_type().is_some()
387            {
388                match is_ctrl_typevar_candidate(tv, operands_in, operands_out) {
389                    Ok(_other_typevars) => {
390                        return Some(PolymorphicInfo {
391                            use_typevar_operand: true,
392                            ctrl_typevar: tv.clone(),
393                        });
394                    }
395                    Err(error_message) => {
396                        maybe_error_message = Some(error_message);
397                    }
398                }
399            }
400        }
401    };
402
403    // If we reached here, it means the type variable indicated as the typevar operand couldn't
404    // control every other input and output type variable. We need to look at the result type
405    // variables.
406    if operands_out.is_empty() {
407        // No result means no other possible type variable, so it's a type inference failure.
408        match maybe_error_message {
409            Some(msg) => panic!("{}", msg),
410            None => panic!("typevar_operand must be a free type variable"),
411        }
412    }
413
414    // Otherwise, try to infer the controlling type variable by looking at the first result.
415    let tv = operands_out[0].type_var().unwrap();
416    let free_typevar = tv.free_typevar();
417    if free_typevar.is_some() && tv != &free_typevar.unwrap() {
418        panic!("first result must be a free type variable");
419    }
420
421    // At this point, if the next unwrap() fails, it means the output type couldn't be used as a
422    // controlling type variable either; panicking is the right behavior.
423    is_ctrl_typevar_candidate(tv, operands_in, operands_out).unwrap();
424
425    Some(PolymorphicInfo {
426        use_typevar_operand: false,
427        ctrl_typevar: tv.clone(),
428    })
429}
430
431/// Verify that the use of TypeVars is consistent with `ctrl_typevar` as the controlling type
432/// variable.
433///
434/// All polymorphic inputs must either be derived from `ctrl_typevar` or be independent free type
435/// variables only used once.
436///
437/// All polymorphic results must be derived from `ctrl_typevar`.
438///
439/// Return a vector of other type variables used, or a string explaining what went wrong.
440fn is_ctrl_typevar_candidate(
441    ctrl_typevar: &TypeVar,
442    operands_in: &[Operand],
443    operands_out: &[Operand],
444) -> Result<Vec<TypeVar>, String> {
445    let mut other_typevars = Vec::new();
446
447    // Check value inputs.
448    for input in operands_in {
449        if !input.is_value() {
450            continue;
451        }
452
453        let typ = input.type_var().unwrap();
454        let free_typevar = typ.free_typevar();
455
456        // Non-polymorphic or derived from ctrl_typevar is OK.
457        if free_typevar.is_none() {
458            continue;
459        }
460        let free_typevar = free_typevar.unwrap();
461        if &free_typevar == ctrl_typevar {
462            continue;
463        }
464
465        // No other derived typevars allowed.
466        if typ != &free_typevar {
467            return Err(format!(
468                "{:?}: type variable {} must be derived from {:?} while it is derived from {:?}",
469                input, typ.name, ctrl_typevar, free_typevar
470            ));
471        }
472
473        // Other free type variables can only be used once each.
474        for other_tv in &other_typevars {
475            if &free_typevar == other_tv {
476                return Err(format!(
477                    "non-controlling type variable {} can't be used more than once",
478                    free_typevar.name
479                ));
480            }
481        }
482
483        other_typevars.push(free_typevar);
484    }
485
486    // Check outputs.
487    for result in operands_out {
488        if !result.is_value() {
489            continue;
490        }
491
492        let typ = result.type_var().unwrap();
493        let free_typevar = typ.free_typevar();
494
495        // Non-polymorphic or derived from ctrl_typevar is OK.
496        if free_typevar.is_none() || &free_typevar.unwrap() == ctrl_typevar {
497            continue;
498        }
499
500        return Err("type variable in output not derived from ctrl_typevar".into());
501    }
502
503    Ok(other_typevars)
504}