cranelift_codegen_meta/
gen_inst.rs

1//! Generate instruction data (including opcodes, formats, builders, etc.).
2use std::fmt;
3
4use cranelift_codegen_shared::constant_hash;
5use cranelift_entity::EntityRef;
6
7use crate::cdsl::camel_case;
8use crate::cdsl::formats::InstructionFormat;
9use crate::cdsl::instructions::{AllInstructions, Instruction};
10use crate::cdsl::operands::Operand;
11use crate::cdsl::typevar::{TypeSet, TypeVar};
12
13use crate::error;
14use crate::srcgen::{Formatter, Match};
15use crate::unique_table::{UniqueSeqTable, UniqueTable};
16
17// TypeSet indexes are encoded in 8 bits, with `0xff` reserved.
18const TYPESET_LIMIT: usize = 0xff;
19
20/// Generate an instruction format enumeration.
21fn gen_formats(formats: &[&InstructionFormat], fmt: &mut Formatter) {
22    fmt.doc_comment(
23        r#"
24        An instruction format
25
26        Every opcode has a corresponding instruction format
27        which is represented by both the `InstructionFormat`
28        and the `InstructionData` enums.
29    "#,
30    );
31    fmt.line("#[derive(Copy, Clone, PartialEq, Eq, Debug)]");
32    fmt.line("pub enum InstructionFormat {");
33    fmt.indent(|fmt| {
34        for format in formats {
35            fmt.doc_comment(format.to_string());
36            fmtln!(fmt, "{},", format.name);
37        }
38    });
39    fmt.line("}");
40    fmt.empty_line();
41
42    // Emit a From<InstructionData> which also serves to verify that
43    // InstructionFormat and InstructionData are in sync.
44    fmt.line("impl<'a> From<&'a InstructionData> for InstructionFormat {");
45    fmt.indent(|fmt| {
46        fmt.line("fn from(inst: &'a InstructionData) -> Self {");
47        fmt.indent(|fmt| {
48            let mut m = Match::new("*inst");
49            for format in formats {
50                m.arm(
51                    format!("InstructionData::{}", format.name),
52                    vec![".."],
53                    format!("Self::{}", format.name),
54                );
55            }
56            fmt.add_match(m);
57        });
58        fmt.line("}");
59    });
60    fmt.line("}");
61    fmt.empty_line();
62}
63
64/// Generate the InstructionData enum.
65///
66/// Every variant must contain an `opcode` field. The size of `InstructionData` should be kept at
67/// 16 bytes on 64-bit architectures. If more space is needed to represent an instruction, use a
68/// `ValueList` to store the additional information out of line.
69fn gen_instruction_data(formats: &[&InstructionFormat], fmt: &mut Formatter) {
70    fmt.line("#[derive(Clone, Debug)]");
71    fmt.line("#[allow(missing_docs)]");
72    fmt.line("pub enum InstructionData {");
73    fmt.indent(|fmt| {
74        for format in formats {
75            fmtln!(fmt, "{} {{", format.name);
76            fmt.indent(|fmt| {
77                fmt.line("opcode: Opcode,");
78                if format.typevar_operand.is_some() {
79                    if format.has_value_list {
80                        fmt.line("args: ValueList,");
81                    } else if format.num_value_operands == 1 {
82                        fmt.line("arg: Value,");
83                    } else {
84                        fmtln!(fmt, "args: [Value; {}],", format.num_value_operands);
85                    }
86                }
87                for field in &format.imm_fields {
88                    fmtln!(fmt, "{}: {},", field.member, field.kind.rust_type);
89                }
90            });
91            fmtln!(fmt, "},");
92        }
93    });
94    fmt.line("}");
95}
96
97fn gen_arguments_method(formats: &[&InstructionFormat], fmt: &mut Formatter, is_mut: bool) {
98    let (method, mut_, rslice, as_slice) = if is_mut {
99        (
100            "arguments_mut",
101            "mut ",
102            "core::slice::from_mut",
103            "as_mut_slice",
104        )
105    } else {
106        ("arguments", "", "core::slice::from_ref", "as_slice")
107    };
108
109    fmtln!(
110        fmt,
111        "pub fn {}<'a>(&'a {}self, pool: &'a {}ir::ValueListPool) -> &{}[Value] {{",
112        method,
113        mut_,
114        mut_,
115        mut_
116    );
117    fmt.indent(|fmt| {
118        let mut m = Match::new("*self");
119        for format in formats {
120            let name = format!("Self::{}", format.name);
121
122            // Formats with a value list put all of their arguments in the list. We don't split
123            // them up, just return it all as variable arguments. (I expect the distinction to go
124            // away).
125            if format.has_value_list {
126                m.arm(
127                    name,
128                    vec![format!("ref {}args", mut_), "..".to_string()],
129                    format!("args.{}(pool)", as_slice),
130                );
131                continue;
132            }
133
134            // Fixed args.
135            let mut fields = Vec::new();
136            let arg = if format.num_value_operands == 0 {
137                format!("&{}[]", mut_)
138            } else if format.num_value_operands == 1 {
139                fields.push(format!("ref {}arg", mut_));
140                format!("{}(arg)", rslice)
141            } else {
142                let arg = format!("args_arity{}", format.num_value_operands);
143                fields.push(format!("args: ref {}{}", mut_, arg));
144                arg
145            };
146            fields.push("..".into());
147
148            m.arm(name, fields, arg);
149        }
150        fmt.add_match(m);
151    });
152    fmtln!(fmt, "}");
153}
154
155/// Generate the boring parts of the InstructionData implementation.
156///
157/// These methods in `impl InstructionData` can be generated automatically from the instruction
158/// formats:
159///
160/// - `pub fn opcode(&self) -> Opcode`
161/// - `pub fn arguments(&self, &pool) -> &[Value]`
162/// - `pub fn arguments_mut(&mut self, &pool) -> &mut [Value]`
163/// - `pub fn take_value_list(&mut self) -> Option<ir::ValueList>`
164/// - `pub fn put_value_list(&mut self, args: ir::ValueList>`
165/// - `pub fn eq(&self, &other: Self, &pool) -> bool`
166/// - `pub fn hash<H: Hasher>(&self, state: &mut H, &pool)`
167fn gen_instruction_data_impl(formats: &[&InstructionFormat], fmt: &mut Formatter) {
168    fmt.line("impl InstructionData {");
169    fmt.indent(|fmt| {
170        fmt.doc_comment("Get the opcode of this instruction.");
171        fmt.line("pub fn opcode(&self) -> Opcode {");
172        fmt.indent(|fmt| {
173            let mut m = Match::new("*self");
174            for format in formats {
175                m.arm(format!("Self::{}", format.name), vec!["opcode", ".."],
176                      "opcode".to_string());
177            }
178            fmt.add_match(m);
179        });
180        fmt.line("}");
181        fmt.empty_line();
182
183        fmt.doc_comment("Get the controlling type variable operand.");
184        fmt.line("pub fn typevar_operand(&self, pool: &ir::ValueListPool) -> Option<Value> {");
185        fmt.indent(|fmt| {
186            let mut m = Match::new("*self");
187            for format in formats {
188                let name = format!("Self::{}", format.name);
189                if format.typevar_operand.is_none() {
190                    m.arm(name, vec![".."], "None".to_string());
191                } else if format.has_value_list {
192                    // We keep all arguments in a value list.
193                    m.arm(name, vec!["ref args", ".."], format!("args.get({}, pool)", format.typevar_operand.unwrap()));
194                } else if format.num_value_operands == 1 {
195                    m.arm(name, vec!["arg", ".."], "Some(arg)".to_string());
196                } else {
197                    // We have multiple value operands and an array `args`.
198                    // Which `args` index to use?
199                    let args = format!("args_arity{}", format.num_value_operands);
200                    m.arm(name, vec![format!("args: ref {}", args), "..".to_string()],
201                        format!("Some({}[{}])", args, format.typevar_operand.unwrap()));
202                }
203            }
204            fmt.add_match(m);
205        });
206        fmt.line("}");
207        fmt.empty_line();
208
209        fmt.doc_comment("Get the value arguments to this instruction.");
210        gen_arguments_method(formats, fmt, false);
211        fmt.empty_line();
212
213        fmt.doc_comment(r#"Get mutable references to the value arguments to this
214                        instruction."#);
215        gen_arguments_method(formats, fmt, true);
216        fmt.empty_line();
217
218        fmt.doc_comment(r#"
219            Take out the value list with all the value arguments and return
220            it.
221
222            This leaves the value list in the instruction empty. Use
223            `put_value_list` to put the value list back.
224        "#);
225        fmt.line("pub fn take_value_list(&mut self) -> Option<ir::ValueList> {");
226        fmt.indent(|fmt| {
227            let mut m = Match::new("*self");
228
229            for format in formats {
230                if format.has_value_list {
231                    m.arm(format!("Self::{}", format.name),
232                    vec!["ref mut args", ".."],
233                    "Some(args.take())".to_string());
234                }
235            }
236
237            m.arm_no_fields("_", "None");
238
239            fmt.add_match(m);
240        });
241        fmt.line("}");
242        fmt.empty_line();
243
244        fmt.doc_comment(r#"
245            Put back a value list.
246
247            After removing a value list with `take_value_list()`, use this
248            method to put it back. It is required that this instruction has
249            a format that accepts a value list, and that the existing value
250            list is empty. This avoids leaking list pool memory.
251        "#);
252        fmt.line("pub fn put_value_list(&mut self, vlist: ir::ValueList) {");
253        fmt.indent(|fmt| {
254            fmt.line("let args = match *self {");
255            fmt.indent(|fmt| {
256                for format in formats {
257                    if format.has_value_list {
258                        fmtln!(fmt, "Self::{} {{ ref mut args, .. }} => args,", format.name);
259                    }
260                }
261                fmt.line("_ => panic!(\"No value list: {:?}\", self),");
262            });
263            fmt.line("};");
264            fmt.line("debug_assert!(args.is_empty(), \"Value list already in use\");");
265            fmt.line("*args = vlist;");
266        });
267        fmt.line("}");
268        fmt.empty_line();
269
270        fmt.doc_comment(r#"
271            Compare two `InstructionData` for equality.
272
273            This operation requires a reference to a `ValueListPool` to
274            determine if the contents of any `ValueLists` are equal.
275        "#);
276        fmt.line("pub fn eq(&self, other: &Self, pool: &ir::ValueListPool) -> bool {");
277        fmt.indent(|fmt| {
278            fmt.line("if ::core::mem::discriminant(self) != ::core::mem::discriminant(other) {");
279            fmt.indent(|fmt| {
280                fmt.line("return false;");
281            });
282            fmt.line("}");
283
284            fmt.line("match (self, other) {");
285            fmt.indent(|fmt| {
286                for format in formats {
287                    let name = format!("&Self::{}", format.name);
288                    let mut members = vec!["opcode"];
289
290                    let args_eq = if format.typevar_operand.is_none() {
291                        None
292                    } else if format.has_value_list {
293                        members.push("args");
294                        Some("args1.as_slice(pool) == args2.as_slice(pool)")
295                    } else if format.num_value_operands == 1 {
296                        members.push("arg");
297                        Some("arg1 == arg2")
298                    } else {
299                        members.push("args");
300                        Some("args1 == args2")
301                    };
302
303                    for field in &format.imm_fields {
304                        members.push(field.member);
305                    }
306
307                    let pat1 = members.iter().map(|x| format!("{}: ref {}1", x, x)).collect::<Vec<_>>().join(", ");
308                    let pat2 = members.iter().map(|x| format!("{}: ref {}2", x, x)).collect::<Vec<_>>().join(", ");
309                    fmtln!(fmt, "({} {{ {} }}, {} {{ {} }}) => {{", name, pat1, name, pat2);
310                    fmt.indent(|fmt| {
311                        fmt.line("opcode1 == opcode2");
312                        for field in &format.imm_fields {
313                            fmtln!(fmt, "&& {}1 == {}2", field.member, field.member);
314                        }
315                        if let Some(args_eq) = args_eq {
316                            fmtln!(fmt, "&& {}", args_eq);
317                        }
318                    });
319                    fmtln!(fmt, "}");
320                }
321                fmt.line("_ => unreachable!()");
322            });
323            fmt.line("}");
324        });
325        fmt.line("}");
326        fmt.empty_line();
327
328        fmt.doc_comment(r#"
329            Hash an `InstructionData`.
330
331            This operation requires a reference to a `ValueListPool` to
332            hash the contents of any `ValueLists`.
333        "#);
334        fmt.line("pub fn hash<H: ::core::hash::Hasher>(&self, state: &mut H, pool: &ir::ValueListPool) {");
335        fmt.indent(|fmt| {
336            fmt.line("match *self {");
337            fmt.indent(|fmt| {
338                for format in formats {
339                    let name = format!("Self::{}", format.name);
340                    let mut members = vec!["opcode"];
341
342                    let args = if format.typevar_operand.is_none() {
343                        "&()"
344                    } else if format.has_value_list {
345                        members.push("ref args");
346                        "args.as_slice(pool)"
347                    } else if format.num_value_operands == 1 {
348                        members.push("ref arg");
349                        "arg"
350                    } else {
351                        members.push("ref args");
352                        "args"
353                    };
354
355                    for field in &format.imm_fields {
356                        members.push(field.member);
357                    }
358                    let members = members.join(", ");
359
360                    fmtln!(fmt, "{}{{{}}} => {{", name, members ); // beware the moustaches
361                    fmt.indent(|fmt| {
362                        fmt.line("::core::hash::Hash::hash( &::core::mem::discriminant(self), state);");
363                        fmt.line("::core::hash::Hash::hash(&opcode, state);");
364                        for field in &format.imm_fields {
365                            fmtln!(fmt, "::core::hash::Hash::hash(&{}, state);", field.member);
366                        }
367                        fmtln!(fmt, "::core::hash::Hash::hash({}, state);", args);
368                    });
369                    fmtln!(fmt, "}");
370                }
371            });
372            fmt.line("}");
373        });
374        fmt.line("}");
375    });
376    fmt.line("}");
377}
378
379fn gen_bool_accessor<T: Fn(&Instruction) -> bool>(
380    all_inst: &AllInstructions,
381    get_attr: T,
382    name: &'static str,
383    doc: &'static str,
384    fmt: &mut Formatter,
385) {
386    fmt.doc_comment(doc);
387    fmtln!(fmt, "pub fn {}(self) -> bool {{", name);
388    fmt.indent(|fmt| {
389        let mut m = Match::new("self");
390        for inst in all_inst.values() {
391            if get_attr(inst) {
392                m.arm_no_fields(format!("Self::{}", inst.camel_name), "true");
393            }
394        }
395        m.arm_no_fields("_", "false");
396        fmt.add_match(m);
397    });
398    fmtln!(fmt, "}");
399    fmt.empty_line();
400}
401
402fn gen_opcodes(all_inst: &AllInstructions, fmt: &mut Formatter) {
403    fmt.doc_comment(
404        r#"
405        An instruction opcode.
406
407        All instructions from all supported ISAs are present.
408    "#,
409    );
410    fmt.line("#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]");
411
412    // We explicitly set the discriminant of the first variant to 1, which allows us to take
413    // advantage of the NonZero optimization, meaning that wrapping enums can use the 0
414    // discriminant instead of increasing the size of the whole type, and so the size of
415    // Option<Opcode> is the same as Opcode's.
416    fmt.line("pub enum Opcode {");
417    fmt.indent(|fmt| {
418        let mut is_first_opcode = true;
419        for inst in all_inst.values() {
420            fmt.doc_comment(format!("`{}`. ({})", inst, inst.format.name));
421
422            // Document polymorphism.
423            if let Some(poly) = &inst.polymorphic_info {
424                if poly.use_typevar_operand {
425                    let op_num = inst.value_opnums[inst.format.typevar_operand.unwrap()];
426                    fmt.doc_comment(format!(
427                        "Type inferred from `{}`.",
428                        inst.operands_in[op_num].name
429                    ));
430                }
431            }
432
433            // Enum variant itself.
434            if is_first_opcode {
435                assert!(inst.opcode_number.index() == 0);
436                // TODO the python crate requires opcode numbers to start from one.
437                fmtln!(fmt, "{} = 1,", inst.camel_name);
438                is_first_opcode = false;
439            } else {
440                fmtln!(fmt, "{},", inst.camel_name)
441            }
442        }
443    });
444    fmt.line("}");
445    fmt.empty_line();
446
447    fmt.line("impl Opcode {");
448    fmt.indent(|fmt| {
449        gen_bool_accessor(
450            all_inst,
451            |inst| inst.is_terminator,
452            "is_terminator",
453            "True for instructions that terminate the EBB",
454            fmt,
455        );
456        gen_bool_accessor(
457            all_inst,
458            |inst| inst.is_branch,
459            "is_branch",
460            "True for all branch or jump instructions.",
461            fmt,
462        );
463        gen_bool_accessor(
464            all_inst,
465            |inst| inst.is_indirect_branch,
466            "is_indirect_branch",
467            "True for all indirect branch or jump instructions.",
468            fmt,
469        );
470        gen_bool_accessor(
471            all_inst,
472            |inst| inst.is_call,
473            "is_call",
474            "Is this a call instruction?",
475            fmt,
476        );
477        gen_bool_accessor(
478            all_inst,
479            |inst| inst.is_return,
480            "is_return",
481            "Is this a return instruction?",
482            fmt,
483        );
484        gen_bool_accessor(
485            all_inst,
486            |inst| inst.is_ghost,
487            "is_ghost",
488            "Is this a ghost instruction?",
489            fmt,
490        );
491        gen_bool_accessor(
492            all_inst,
493            |inst| inst.can_load,
494            "can_load",
495            "Can this instruction read from memory?",
496            fmt,
497        );
498        gen_bool_accessor(
499            all_inst,
500            |inst| inst.can_store,
501            "can_store",
502            "Can this instruction write to memory?",
503            fmt,
504        );
505        gen_bool_accessor(
506            all_inst,
507            |inst| inst.can_trap,
508            "can_trap",
509            "Can this instruction cause a trap?",
510            fmt,
511        );
512        gen_bool_accessor(
513            all_inst,
514            |inst| inst.other_side_effects,
515            "other_side_effects",
516            "Does this instruction have other side effects besides can_* flags?",
517            fmt,
518        );
519        gen_bool_accessor(
520            all_inst,
521            |inst| inst.writes_cpu_flags,
522            "writes_cpu_flags",
523            "Does this instruction write to CPU flags?",
524            fmt,
525        );
526    });
527    fmt.line("}");
528    fmt.empty_line();
529
530    // Generate a private opcode_format table.
531    fmtln!(
532        fmt,
533        "const OPCODE_FORMAT: [InstructionFormat; {}] = [",
534        all_inst.len()
535    );
536    fmt.indent(|fmt| {
537        for inst in all_inst.values() {
538            fmtln!(
539                fmt,
540                "InstructionFormat::{}, // {}",
541                inst.format.name,
542                inst.name
543            );
544        }
545    });
546    fmtln!(fmt, "];");
547    fmt.empty_line();
548
549    // Generate a private opcode_name function.
550    fmt.line("fn opcode_name(opc: Opcode) -> &\'static str {");
551    fmt.indent(|fmt| {
552        let mut m = Match::new("opc");
553        for inst in all_inst.values() {
554            m.arm_no_fields(
555                format!("Opcode::{}", inst.camel_name),
556                format!("\"{}\"", inst.name),
557            );
558        }
559        fmt.add_match(m);
560    });
561    fmt.line("}");
562    fmt.empty_line();
563
564    // Generate an opcode hash table for looking up opcodes by name.
565    let hash_table = constant_hash::generate_table(all_inst.values(), all_inst.len(), |inst| {
566        constant_hash::simple_hash(&inst.name)
567    });
568    fmtln!(
569        fmt,
570        "const OPCODE_HASH_TABLE: [Option<Opcode>; {}] = [",
571        hash_table.len()
572    );
573    fmt.indent(|fmt| {
574        for i in hash_table {
575            match i {
576                Some(i) => fmtln!(fmt, "Some(Opcode::{}),", i.camel_name),
577                None => fmtln!(fmt, "None,"),
578            }
579        }
580    });
581    fmtln!(fmt, "];");
582    fmt.empty_line();
583}
584
585/// Get the value type constraint for an SSA value operand, where
586/// `ctrl_typevar` is the controlling type variable.
587///
588/// Each operand constraint is represented as a string, one of:
589/// - `Concrete(vt)`, where `vt` is a value type name.
590/// - `Free(idx)` where `idx` is an index into `type_sets`.
591/// - `Same`, `Lane`, `AsBool` for controlling typevar-derived constraints.
592fn get_constraint<'entries, 'table>(
593    operand: &'entries Operand,
594    ctrl_typevar: Option<&TypeVar>,
595    type_sets: &'table mut UniqueTable<'entries, TypeSet>,
596) -> String {
597    assert!(operand.is_value());
598    let type_var = operand.type_var().unwrap();
599
600    if let Some(typ) = type_var.singleton_type() {
601        return format!("Concrete({})", typ.rust_name());
602    }
603
604    if let Some(free_typevar) = type_var.free_typevar() {
605        if ctrl_typevar.is_some() && free_typevar != *ctrl_typevar.unwrap() {
606            assert!(type_var.base.is_none());
607            return format!("Free({})", type_sets.add(&type_var.get_raw_typeset()));
608        }
609    }
610
611    if let Some(base) = &type_var.base {
612        assert!(base.type_var == *ctrl_typevar.unwrap());
613        return camel_case(base.derived_func.name());
614    }
615
616    assert!(type_var == ctrl_typevar.unwrap());
617    "Same".into()
618}
619
620fn gen_bitset<'a, T: IntoIterator<Item = &'a u16>>(
621    iterable: T,
622    name: &'static str,
623    field_size: u8,
624    fmt: &mut Formatter,
625) {
626    let bits = iterable.into_iter().fold(0, |acc, x| {
627        assert!(x.is_power_of_two());
628        assert!(u32::from(*x) < (1 << u32::from(field_size)));
629        acc | x
630    });
631    fmtln!(fmt, "{}: BitSet::<u{}>({}),", name, field_size, bits);
632}
633
634fn iterable_to_string<I: fmt::Display, T: IntoIterator<Item = I>>(iterable: T) -> String {
635    let elems = iterable
636        .into_iter()
637        .map(|x| x.to_string())
638        .collect::<Vec<_>>()
639        .join(", ");
640    format!("{{{}}}", elems)
641}
642
643fn typeset_to_string(ts: &TypeSet) -> String {
644    let mut result = format!("TypeSet(lanes={}", iterable_to_string(&ts.lanes));
645    if !ts.ints.is_empty() {
646        result += &format!(", ints={}", iterable_to_string(&ts.ints));
647    }
648    if !ts.floats.is_empty() {
649        result += &format!(", floats={}", iterable_to_string(&ts.floats));
650    }
651    if !ts.bools.is_empty() {
652        result += &format!(", bools={}", iterable_to_string(&ts.bools));
653    }
654    if !ts.specials.is_empty() {
655        result += &format!(", specials=[{}]", iterable_to_string(&ts.specials));
656    }
657    if !ts.refs.is_empty() {
658        result += &format!(", refs={}", iterable_to_string(&ts.refs));
659    }
660    result += ")";
661    result
662}
663
664/// Generate the table of ValueTypeSets described by type_sets.
665pub(crate) fn gen_typesets_table(type_sets: &UniqueTable<TypeSet>, fmt: &mut Formatter) {
666    if type_sets.len() == 0 {
667        return;
668    }
669
670    fmt.comment("Table of value type sets.");
671    assert!(type_sets.len() <= TYPESET_LIMIT, "Too many type sets!");
672    fmtln!(
673        fmt,
674        "const TYPE_SETS: [ir::instructions::ValueTypeSet; {}] = [",
675        type_sets.len()
676    );
677    fmt.indent(|fmt| {
678        for ts in type_sets.iter() {
679            fmt.line("ir::instructions::ValueTypeSet {");
680            fmt.indent(|fmt| {
681                fmt.comment(typeset_to_string(ts));
682                gen_bitset(&ts.lanes, "lanes", 16, fmt);
683                gen_bitset(&ts.ints, "ints", 8, fmt);
684                gen_bitset(&ts.floats, "floats", 8, fmt);
685                gen_bitset(&ts.bools, "bools", 8, fmt);
686                gen_bitset(&ts.refs, "refs", 8, fmt);
687            });
688            fmt.line("},");
689        }
690    });
691    fmtln!(fmt, "];");
692}
693
694/// Generate value type constraints for all instructions.
695/// - Emit a compact constant table of ValueTypeSet objects.
696/// - Emit a compact constant table of OperandConstraint objects.
697/// - Emit an opcode-indexed table of instruction constraints.
698fn gen_type_constraints(all_inst: &AllInstructions, fmt: &mut Formatter) {
699    // Table of TypeSet instances.
700    let mut type_sets = UniqueTable::new();
701
702    // Table of operand constraint sequences (as tuples). Each operand
703    // constraint is represented as a string, one of:
704    // - `Concrete(vt)`, where `vt` is a value type name.
705    // - `Free(idx)` where `idx` is an index into `type_sets`.
706    // - `Same`, `Lane`, `AsBool` for controlling typevar-derived constraints.
707    let mut operand_seqs = UniqueSeqTable::new();
708
709    // Preload table with constraints for typical binops.
710    #[allow(clippy::useless_vec)]
711    operand_seqs.add(&vec!["Same".to_string(); 3]);
712
713    fmt.comment("Table of opcode constraints.");
714    fmtln!(
715        fmt,
716        "const OPCODE_CONSTRAINTS: [OpcodeConstraints; {}] = [",
717        all_inst.len()
718    );
719    fmt.indent(|fmt| {
720        for inst in all_inst.values() {
721            let (ctrl_typevar, ctrl_typeset) = if let Some(poly) = &inst.polymorphic_info {
722                let index = type_sets.add(&*poly.ctrl_typevar.get_raw_typeset());
723                (Some(&poly.ctrl_typevar), index)
724            } else {
725                (None, TYPESET_LIMIT)
726            };
727
728            // Collect constraints for the value results, not including `variable_args` results
729            // which are always special cased.
730            let mut constraints = Vec::new();
731            for &index in &inst.value_results {
732                constraints.push(get_constraint(&inst.operands_out[index], ctrl_typevar, &mut type_sets));
733            }
734            for &index in &inst.value_opnums {
735                constraints.push(get_constraint(&inst.operands_in[index], ctrl_typevar, &mut type_sets));
736            }
737
738            let constraint_offset = operand_seqs.add(&constraints);
739
740            let fixed_results = inst.value_results.len();
741            let fixed_values = inst.value_opnums.len();
742
743            // Can the controlling type variable be inferred from the designated operand?
744            let use_typevar_operand = if let Some(poly) = &inst.polymorphic_info {
745                poly.use_typevar_operand
746            } else {
747                false
748            };
749
750            // Can the controlling type variable be inferred from the result?
751            let use_result = fixed_results > 0 && inst.operands_out[inst.value_results[0]].type_var() == ctrl_typevar;
752
753            // Are we required to use the designated operand instead of the result?
754            let requires_typevar_operand = use_typevar_operand && !use_result;
755
756            fmt.comment(
757                format!("{}: fixed_results={}, use_typevar_operand={}, requires_typevar_operand={}, fixed_values={}",
758                inst.camel_name,
759                fixed_results,
760                use_typevar_operand,
761                requires_typevar_operand,
762                fixed_values)
763            );
764            fmt.comment(format!("Constraints=[{}]", constraints
765                .iter()
766                .map(|x| format!("'{}'", x))
767                .collect::<Vec<_>>()
768                .join(", ")));
769            if let Some(poly) = &inst.polymorphic_info {
770                fmt.comment(format!("Polymorphic over {}", typeset_to_string(&poly.ctrl_typevar.get_raw_typeset())));
771            }
772
773            // Compute the bit field encoding, c.f. instructions.rs.
774            assert!(fixed_results < 8 && fixed_values < 8, "Bit field encoding too tight");
775            let mut flags = fixed_results; // 3 bits
776            if use_typevar_operand {
777                flags |= 1<<3; // 4th bit
778            }
779            if requires_typevar_operand {
780                flags |= 1<<4; // 5th bit
781            }
782            flags |= fixed_values << 5; // 6th bit and more
783
784            fmt.line("OpcodeConstraints {");
785            fmt.indent(|fmt| {
786                fmtln!(fmt, "flags: {:#04x},", flags);
787                fmtln!(fmt, "typeset_offset: {},", ctrl_typeset);
788                fmtln!(fmt, "constraint_offset: {},", constraint_offset);
789            });
790            fmt.line("},");
791        }
792    });
793    fmtln!(fmt, "];");
794    fmt.empty_line();
795
796    gen_typesets_table(&type_sets, fmt);
797    fmt.empty_line();
798
799    fmt.comment("Table of operand constraint sequences.");
800    fmtln!(
801        fmt,
802        "const OPERAND_CONSTRAINTS: [OperandConstraint; {}] = [",
803        operand_seqs.len()
804    );
805    fmt.indent(|fmt| {
806        for constraint in operand_seqs.iter() {
807            fmtln!(fmt, "OperandConstraint::{},", constraint);
808        }
809    });
810    fmtln!(fmt, "];");
811}
812
813/// Emit member initializers for an instruction format.
814fn gen_member_inits(format: &InstructionFormat, fmt: &mut Formatter) {
815    // Immediate operands.
816    // We have local variables with the same names as the members.
817    for f in &format.imm_fields {
818        fmtln!(fmt, "{},", f.member);
819    }
820
821    // Value operands.
822    if format.has_value_list {
823        fmt.line("args,");
824    } else if format.num_value_operands == 1 {
825        fmt.line("arg: arg0,");
826    } else if format.num_value_operands > 1 {
827        let mut args = Vec::new();
828        for i in 0..format.num_value_operands {
829            args.push(format!("arg{}", i));
830        }
831        fmtln!(fmt, "args: [{}],", args.join(", "));
832    }
833}
834
835/// Emit a method for creating and inserting an instruction format.
836///
837/// All instruction formats take an `opcode` argument and a `ctrl_typevar` argument for deducing
838/// the result types.
839fn gen_format_constructor(format: &InstructionFormat, fmt: &mut Formatter) {
840    // Construct method arguments.
841    let mut args = vec![
842        "self".to_string(),
843        "opcode: Opcode".into(),
844        "ctrl_typevar: Type".into(),
845    ];
846
847    // Normal operand arguments. Start with the immediate operands.
848    for f in &format.imm_fields {
849        args.push(format!("{}: {}", f.member, f.kind.rust_type));
850    }
851
852    // Then the value operands.
853    if format.has_value_list {
854        // Take all value arguments as a finished value list. The value lists
855        // are created by the individual instruction constructors.
856        args.push("args: ir::ValueList".into());
857    } else {
858        // Take a fixed number of value operands.
859        for i in 0..format.num_value_operands {
860            args.push(format!("arg{}: Value", i));
861        }
862    }
863
864    let proto = format!(
865        "{}({}) -> (Inst, &'f mut ir::DataFlowGraph)",
866        format.name,
867        args.join(", ")
868    );
869
870    fmt.doc_comment(format.to_string());
871    fmt.line("#[allow(non_snake_case)]");
872    fmtln!(fmt, "fn {} {{", proto);
873    fmt.indent(|fmt| {
874        // Generate the instruction data.
875        fmtln!(fmt, "let data = ir::InstructionData::{} {{", format.name);
876        fmt.indent(|fmt| {
877            fmt.line("opcode,");
878            gen_member_inits(format, fmt);
879        });
880        fmtln!(fmt, "};");
881        fmt.line("self.build(data, ctrl_typevar)");
882    });
883    fmtln!(fmt, "}");
884}
885
886/// Emit a method for generating the instruction `inst`.
887///
888/// The method will create and insert an instruction, then return the result values, or the
889/// instruction reference itself for instructions that don't have results.
890fn gen_inst_builder(inst: &Instruction, format: &InstructionFormat, fmt: &mut Formatter) {
891    // Construct method arguments.
892    let mut args = vec![if format.has_value_list {
893        "mut self"
894    } else {
895        "self"
896    }
897    .to_string()];
898
899    let mut args_doc = Vec::new();
900    let mut rets_doc = Vec::new();
901
902    // The controlling type variable will be inferred from the input values if
903    // possible. Otherwise, it is the first method argument.
904    if let Some(poly) = &inst.polymorphic_info {
905        if !poly.use_typevar_operand {
906            args.push(format!("{}: crate::ir::Type", poly.ctrl_typevar.name));
907            args_doc.push(format!(
908                "- {} (controlling type variable): {}",
909                poly.ctrl_typevar.name, poly.ctrl_typevar.doc
910            ));
911        }
912    }
913
914    let mut tmpl_types = Vec::new();
915    let mut into_args = Vec::new();
916    for op in &inst.operands_in {
917        let t = if op.is_immediate() {
918            let t = format!("T{}", tmpl_types.len() + 1);
919            tmpl_types.push(format!("{}: Into<{}>", t, op.kind.rust_type));
920            into_args.push(op.name);
921            t
922        } else {
923            op.kind.rust_type.to_string()
924        };
925        args.push(format!("{}: {}", op.name, t));
926        args_doc.push(format!(
927            "- {}: {}",
928            op.name,
929            op.doc()
930                .expect("every instruction's input operand must be documented")
931        ));
932    }
933
934    for op in &inst.operands_out {
935        rets_doc.push(format!(
936            "- {}: {}",
937            op.name,
938            op.doc()
939                .expect("every instruction's output operand must be documented")
940        ));
941    }
942
943    let rtype = match inst.value_results.len() {
944        0 => "Inst".into(),
945        1 => "Value".into(),
946        _ => format!("({})", vec!["Value"; inst.value_results.len()].join(", ")),
947    };
948
949    let tmpl = if !tmpl_types.is_empty() {
950        format!("<{}>", tmpl_types.join(", "))
951    } else {
952        "".into()
953    };
954
955    let proto = format!(
956        "{}{}({}) -> {}",
957        inst.snake_name(),
958        tmpl,
959        args.join(", "),
960        rtype
961    );
962
963    fmt.doc_comment(&inst.doc);
964    if !args_doc.is_empty() {
965        fmt.line("///");
966        fmt.doc_comment("Inputs:");
967        fmt.line("///");
968        for doc_line in args_doc {
969            fmt.doc_comment(doc_line);
970        }
971    }
972    if !rets_doc.is_empty() {
973        fmt.line("///");
974        fmt.doc_comment("Outputs:");
975        fmt.line("///");
976        for doc_line in rets_doc {
977            fmt.doc_comment(doc_line);
978        }
979    }
980
981    fmt.line("#[allow(non_snake_case)]");
982    fmtln!(fmt, "fn {} {{", proto);
983    fmt.indent(|fmt| {
984        // Convert all of the `Into<>` arguments.
985        for arg in &into_args {
986            fmtln!(fmt, "let {} = {}.into();", arg, arg);
987        }
988
989        // Arguments for instruction constructor.
990        let first_arg = format!("Opcode::{}", inst.camel_name);
991        let mut args = vec![first_arg.as_str()];
992        if let Some(poly) = &inst.polymorphic_info {
993            if poly.use_typevar_operand {
994                // Infer the controlling type variable from the input operands.
995                let op_num = inst.value_opnums[format.typevar_operand.unwrap()];
996                fmtln!(
997                    fmt,
998                    "let ctrl_typevar = self.data_flow_graph().value_type({});",
999                    inst.operands_in[op_num].name
1000                );
1001
1002                // The format constructor will resolve the result types from the type var.
1003                args.push("ctrl_typevar");
1004            } else {
1005                // This was an explicit method argument.
1006                args.push(&poly.ctrl_typevar.name);
1007            }
1008        } else {
1009            // No controlling type variable needed.
1010            args.push("types::INVALID");
1011        }
1012
1013        // Now add all of the immediate operands to the constructor arguments.
1014        for &op_num in &inst.imm_opnums {
1015            args.push(inst.operands_in[op_num].name);
1016        }
1017
1018        // Finally, the value operands.
1019        if format.has_value_list {
1020            // We need to build a value list with all the arguments.
1021            fmt.line("let mut vlist = ir::ValueList::default();");
1022            args.push("vlist");
1023            fmt.line("{");
1024            fmt.indent(|fmt| {
1025                fmt.line("let pool = &mut self.data_flow_graph_mut().value_lists;");
1026                for op in &inst.operands_in {
1027                    if op.is_value() {
1028                        fmtln!(fmt, "vlist.push({}, pool);", op.name);
1029                    } else if op.is_varargs() {
1030                        fmtln!(fmt, "vlist.extend({}.iter().cloned(), pool);", op.name);
1031                    }
1032                }
1033            });
1034            fmt.line("}");
1035        } else {
1036            // With no value list, we're guaranteed to just have a set of fixed value operands.
1037            for &op_num in &inst.value_opnums {
1038                args.push(inst.operands_in[op_num].name);
1039            }
1040        }
1041
1042        // Call to the format constructor,
1043        let fcall = format!("self.{}({})", format.name, args.join(", "));
1044
1045        if inst.value_results.is_empty() {
1046            fmtln!(fmt, "{}.0", fcall);
1047            return;
1048        }
1049
1050        fmtln!(fmt, "let (inst, dfg) = {};", fcall);
1051        if inst.value_results.len() == 1 {
1052            fmt.line("dfg.first_result(inst)");
1053        } else {
1054            fmtln!(
1055                fmt,
1056                "let results = &dfg.inst_results(inst)[0..{}];",
1057                inst.value_results.len()
1058            );
1059            fmtln!(
1060                fmt,
1061                "({})",
1062                inst.value_results
1063                    .iter()
1064                    .enumerate()
1065                    .map(|(i, _)| format!("results[{}]", i))
1066                    .collect::<Vec<_>>()
1067                    .join(", ")
1068            );
1069        }
1070    });
1071    fmtln!(fmt, "}")
1072}
1073
1074/// Generate a Builder trait with methods for all instructions.
1075fn gen_builder(
1076    instructions: &AllInstructions,
1077    formats: &[&InstructionFormat],
1078    fmt: &mut Formatter,
1079) {
1080    fmt.doc_comment(
1081        r#"
1082        Convenience methods for building instructions.
1083
1084        The `InstBuilder` trait has one method per instruction opcode for
1085        conveniently constructing the instruction with minimum arguments.
1086        Polymorphic instructions infer their result types from the input
1087        arguments when possible. In some cases, an explicit `ctrl_typevar`
1088        argument is required.
1089
1090        The opcode methods return the new instruction's result values, or
1091        the `Inst` itself for instructions that don't have any results.
1092
1093        There is also a method per instruction format. These methods all
1094        return an `Inst`.
1095    "#,
1096    );
1097    fmt.line("pub trait InstBuilder<'f>: InstBuilderBase<'f> {");
1098    fmt.indent(|fmt| {
1099        for inst in instructions.values() {
1100            gen_inst_builder(inst, &*inst.format, fmt);
1101            fmt.empty_line();
1102        }
1103        for (i, format) in formats.iter().enumerate() {
1104            gen_format_constructor(format, fmt);
1105            if i + 1 != formats.len() {
1106                fmt.empty_line();
1107            }
1108        }
1109    });
1110    fmt.line("}");
1111}
1112
1113pub(crate) fn generate(
1114    formats: Vec<&InstructionFormat>,
1115    all_inst: &AllInstructions,
1116    opcode_filename: &str,
1117    inst_builder_filename: &str,
1118    out_dir: &str,
1119) -> Result<(), error::Error> {
1120    // Opcodes.
1121    let mut fmt = Formatter::new();
1122    gen_formats(&formats, &mut fmt);
1123    gen_instruction_data(&formats, &mut fmt);
1124    fmt.empty_line();
1125    gen_instruction_data_impl(&formats, &mut fmt);
1126    fmt.empty_line();
1127    gen_opcodes(all_inst, &mut fmt);
1128    gen_type_constraints(all_inst, &mut fmt);
1129    fmt.update_file(opcode_filename, out_dir)?;
1130
1131    // Instruction builder.
1132    let mut fmt = Formatter::new();
1133    gen_builder(all_inst, &formats, &mut fmt);
1134    fmt.update_file(inst_builder_filename, out_dir)?;
1135
1136    Ok(())
1137}