cranelift_codegen_meta/
gen_inst.rs

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