1 //! Generate transformations to legalize instructions without encodings.
2 use crate::cdsl::ast::{Def, DefPool, Expr, VarPool};
3 use crate::cdsl::isa::TargetIsa;
4 use crate::cdsl::operands::Operand;
5 use crate::cdsl::type_inference::Constraint;
6 use crate::cdsl::typevar::{TypeSet, TypeVar};
7 use crate::cdsl::xform::{Transform, TransformGroup, TransformGroups};
8 
9 use crate::error;
10 use crate::gen_inst::gen_typesets_table;
11 use crate::srcgen::Formatter;
12 use crate::unique_table::UniqueTable;
13 
14 use std::collections::{HashMap, HashSet};
15 use std::iter::FromIterator;
16 
17 /// Given a `Def` node, emit code that extracts all the instruction fields from
18 /// `pos.func.dfg[iref]`.
19 ///
20 /// Create local variables named after the `Var` instances in `node`.
21 ///
22 /// Also create a local variable named `predicate` with the value of the evaluated instruction
23 /// predicate, or `true` if the node has no predicate.
unwrap_inst(transform: &Transform, fmt: &mut Formatter) -> bool24 fn unwrap_inst(transform: &Transform, fmt: &mut Formatter) -> bool {
25     let var_pool = &transform.var_pool;
26     let def_pool = &transform.def_pool;
27 
28     let def = def_pool.get(transform.src);
29     let apply = &def.apply;
30     let inst = &apply.inst;
31     let iform = &inst.format;
32 
33     fmt.comment(format!(
34         "Unwrap fields from instruction format {}",
35         def.to_comment_string(&transform.var_pool)
36     ));
37 
38     // Extract the Var arguments.
39     let arg_names = apply
40         .args
41         .iter()
42         .enumerate()
43         .filter(|(arg_num, _)| {
44             // Variable args are specially handled after extracting args.
45             !inst.operands_in[*arg_num].is_varargs()
46         })
47         .map(|(arg_num, arg)| match &arg {
48             Expr::Var(var_index) => var_pool.get(*var_index).name.as_ref(),
49             Expr::Literal(_) => {
50                 let n = inst.imm_opnums.iter().position(|&i| i == arg_num).unwrap();
51                 iform.imm_fields[n].member
52             }
53         })
54         .collect::<Vec<_>>()
55         .join(", ");
56 
57     // May we need "args" in the values consumed by predicates?
58     let emit_args = iform.num_value_operands >= 1 || iform.has_value_list;
59 
60     // We need a tuple:
61     // - if there's at least one value operand, then we emit a variable for the value, and the
62     // value list as args.
63     // - otherwise, if there's the count of immediate operands added to the presence of a value list exceeds one.
64     let need_tuple = if iform.num_value_operands >= 1 {
65         true
66     } else {
67         let mut imm_and_varargs = inst
68             .operands_in
69             .iter()
70             .filter(|op| op.is_immediate_or_entityref())
71             .count();
72         if iform.has_value_list {
73             imm_and_varargs += 1;
74         }
75         imm_and_varargs > 1
76     };
77 
78     let maybe_args = if emit_args { ", args" } else { "" };
79     let defined_values = format!("{}{}", arg_names, maybe_args);
80 
81     let tuple_or_value = if need_tuple {
82         format!("({})", defined_values)
83     } else {
84         defined_values
85     };
86 
87     fmtln!(
88         fmt,
89         "let {} = if let ir::InstructionData::{} {{",
90         tuple_or_value,
91         iform.name
92     );
93 
94     fmt.indent(|fmt| {
95         // Fields are encoded directly.
96         for field in &iform.imm_fields {
97             fmtln!(fmt, "{},", field.member);
98         }
99 
100         if iform.has_value_list || iform.num_value_operands > 1 {
101             fmt.line("ref args,");
102         } else if iform.num_value_operands == 1 {
103             fmt.line("arg,");
104         }
105 
106         fmt.line("..");
107         fmt.outdented_line("} = pos.func.dfg[inst] {");
108 
109         if iform.has_value_list {
110             fmt.line("let args = args.as_slice(&pos.func.dfg.value_lists);");
111         } else if iform.num_value_operands == 1 {
112             fmt.line("let args = [arg];")
113         }
114 
115         // Generate the values for the tuple.
116         let emit_one_value =
117             |fmt: &mut Formatter, needs_comma: bool, op_num: usize, op: &Operand| {
118                 let comma = if needs_comma { "," } else { "" };
119                 if op.is_immediate_or_entityref() {
120                     let n = inst.imm_opnums.iter().position(|&i| i == op_num).unwrap();
121                     fmtln!(fmt, "{}{}", iform.imm_fields[n].member, comma);
122                 } else if op.is_value() {
123                     let n = inst.value_opnums.iter().position(|&i| i == op_num).unwrap();
124                     fmtln!(fmt, "pos.func.dfg.resolve_aliases(args[{}]),", n);
125                 } else {
126                     // This is a value list argument or a varargs.
127                     assert!(iform.has_value_list || op.is_varargs());
128                 }
129             };
130 
131         if need_tuple {
132             fmt.line("(");
133             fmt.indent(|fmt| {
134                 for (op_num, op) in inst.operands_in.iter().enumerate() {
135                     let needs_comma = emit_args || op_num + 1 < inst.operands_in.len();
136                     emit_one_value(fmt, needs_comma, op_num, op);
137                 }
138                 if emit_args {
139                     fmt.line("args");
140                 }
141             });
142             fmt.line(")");
143         } else {
144             // Only one of these can be true at the same time, otherwise we'd need a tuple.
145             emit_one_value(fmt, false, 0, &inst.operands_in[0]);
146             if emit_args {
147                 fmt.line("args");
148             }
149         }
150 
151         fmt.outdented_line("} else {");
152         fmt.line(r#"unreachable!("bad instruction format")"#);
153     });
154     fmtln!(fmt, "};");
155     fmt.empty_line();
156 
157     assert_eq!(inst.operands_in.len(), apply.args.len());
158     for (i, op) in inst.operands_in.iter().enumerate() {
159         if op.is_varargs() {
160             let name = &var_pool
161                 .get(apply.args[i].maybe_var().expect("vararg without name"))
162                 .name;
163             let n = inst
164                 .imm_opnums
165                 .iter()
166                 .chain(inst.value_opnums.iter())
167                 .max()
168                 .copied()
169                 .unwrap_or(0);
170             fmtln!(fmt, "let {} = &Vec::from(&args[{}..]);", name, n);
171         }
172     }
173 
174     for &op_num in &inst.value_opnums {
175         let arg = &apply.args[op_num];
176         if let Some(var_index) = arg.maybe_var() {
177             let var = var_pool.get(var_index);
178             if var.has_free_typevar() {
179                 fmtln!(
180                     fmt,
181                     "let typeof_{} = pos.func.dfg.value_type({});",
182                     var.name,
183                     var.name
184                 );
185             }
186         }
187     }
188 
189     // If the definition creates results, detach the values and place them in locals.
190     let mut replace_inst = false;
191     if !def.defined_vars.is_empty() {
192         if def.defined_vars
193             == def_pool
194                 .get(var_pool.get(def.defined_vars[0]).dst_def.unwrap())
195                 .defined_vars
196         {
197             // Special case: The instruction replacing node defines the exact same values.
198             fmt.comment(format!(
199                 "Results handled by {}.",
200                 def_pool
201                     .get(var_pool.get(def.defined_vars[0]).dst_def.unwrap())
202                     .to_comment_string(var_pool)
203             ));
204 
205             fmt.line("let r = pos.func.dfg.inst_results(inst);");
206             for (i, &var_index) in def.defined_vars.iter().enumerate() {
207                 let var = var_pool.get(var_index);
208                 fmtln!(fmt, "let {} = &r[{}];", var.name, i);
209                 fmtln!(
210                     fmt,
211                     "let typeof_{} = pos.func.dfg.value_type(*{});",
212                     var.name,
213                     var.name
214                 );
215             }
216 
217             replace_inst = true;
218         } else {
219             // Boring case: Detach the result values, capture them in locals.
220             for &var_index in &def.defined_vars {
221                 fmtln!(fmt, "let {};", var_pool.get(var_index).name);
222             }
223 
224             fmt.line("{");
225             fmt.indent(|fmt| {
226                 fmt.line("let r = pos.func.dfg.inst_results(inst);");
227                 for i in 0..def.defined_vars.len() {
228                     let var = var_pool.get(def.defined_vars[i]);
229                     fmtln!(fmt, "{} = r[{}];", var.name, i);
230                 }
231             });
232             fmt.line("}");
233 
234             for &var_index in &def.defined_vars {
235                 let var = var_pool.get(var_index);
236                 if var.has_free_typevar() {
237                     fmtln!(
238                         fmt,
239                         "let typeof_{} = pos.func.dfg.value_type({});",
240                         var.name,
241                         var.name
242                     );
243                 }
244             }
245         }
246     }
247     replace_inst
248 }
249 
build_derived_expr(tv: &TypeVar) -> String250 fn build_derived_expr(tv: &TypeVar) -> String {
251     let base = match &tv.base {
252         Some(base) => base,
253         None => {
254             assert!(tv.name.starts_with("typeof_"));
255             return format!("Some({})", tv.name);
256         }
257     };
258     let base_expr = build_derived_expr(&base.type_var);
259     format!(
260         "{}.map(|t: crate::ir::Type| t.{}())",
261         base_expr,
262         base.derived_func.name()
263     )
264 }
265 
266 /// Emit rust code for the given check.
267 ///
268 /// The emitted code is a statement redefining the `predicate` variable like this:
269 ///     let predicate = predicate && ...
emit_runtime_typecheck<'a>( constraint: &'a Constraint, type_sets: &mut UniqueTable<'a, TypeSet>, fmt: &mut Formatter, )270 fn emit_runtime_typecheck<'a>(
271     constraint: &'a Constraint,
272     type_sets: &mut UniqueTable<'a, TypeSet>,
273     fmt: &mut Formatter,
274 ) {
275     match constraint {
276         Constraint::InTypeset(tv, ts) => {
277             let ts_index = type_sets.add(&ts);
278             fmt.comment(format!(
279                 "{} must belong to {:?}",
280                 tv.name,
281                 type_sets.get(ts_index)
282             ));
283             fmtln!(
284                 fmt,
285                 "let predicate = predicate && TYPE_SETS[{}].contains({});",
286                 ts_index,
287                 tv.name
288             );
289         }
290         Constraint::Eq(tv1, tv2) => {
291             fmtln!(
292                 fmt,
293                 "let predicate = predicate && match ({}, {}) {{",
294                 build_derived_expr(tv1),
295                 build_derived_expr(tv2)
296             );
297             fmt.indent(|fmt| {
298                 fmt.line("(Some(a), Some(b)) => a == b,");
299                 fmt.comment("On overflow, constraint doesn\'t apply");
300                 fmt.line("_ => false,");
301             });
302             fmtln!(fmt, "};");
303         }
304         Constraint::WiderOrEq(tv1, tv2) => {
305             fmtln!(
306                 fmt,
307                 "let predicate = predicate && match ({}, {}) {{",
308                 build_derived_expr(tv1),
309                 build_derived_expr(tv2)
310             );
311             fmt.indent(|fmt| {
312                 fmt.line("(Some(a), Some(b)) => a.wider_or_equal(b),");
313                 fmt.comment("On overflow, constraint doesn\'t apply");
314                 fmt.line("_ => false,");
315             });
316             fmtln!(fmt, "};");
317         }
318     }
319 }
320 
321 /// Determine if `node` represents one of the value splitting instructions: `isplit` or `vsplit.
322 /// These instructions are lowered specially by the `legalize::split` module.
is_value_split(def: &Def) -> bool323 fn is_value_split(def: &Def) -> bool {
324     let name = &def.apply.inst.name;
325     name == "isplit" || name == "vsplit"
326 }
327 
emit_dst_inst(def: &Def, def_pool: &DefPool, var_pool: &VarPool, fmt: &mut Formatter)328 fn emit_dst_inst(def: &Def, def_pool: &DefPool, var_pool: &VarPool, fmt: &mut Formatter) {
329     let defined_vars = {
330         let vars = def
331             .defined_vars
332             .iter()
333             .map(|&var_index| var_pool.get(var_index).name.as_ref())
334             .collect::<Vec<&str>>();
335         if vars.len() == 1 {
336             vars[0].to_string()
337         } else {
338             format!("({})", vars.join(", "))
339         }
340     };
341 
342     if is_value_split(def) {
343         // Split instructions are not emitted with the builder, but by calling special functions in
344         // the `legalizer::split` module. These functions will eliminate concat-split patterns.
345         fmt.line("let curpos = pos.position();");
346         fmt.line("let srcloc = pos.srcloc();");
347         fmtln!(
348             fmt,
349             "let {} = split::{}(pos.func, cfg, curpos, srcloc, {});",
350             defined_vars,
351             def.apply.inst.snake_name(),
352             def.apply.args[0].to_rust_code(var_pool)
353         );
354         return;
355     }
356 
357     if def.defined_vars.is_empty() {
358         // This node doesn't define any values, so just insert the new instruction.
359         fmtln!(
360             fmt,
361             "pos.ins().{};",
362             def.apply.rust_builder(&def.defined_vars, var_pool)
363         );
364         return;
365     }
366 
367     if let Some(src_def0) = var_pool.get(def.defined_vars[0]).src_def {
368         if def.defined_vars == def_pool.get(src_def0).defined_vars {
369             // The replacement instruction defines the exact same values as the source pattern.
370             // Unwrapping would have left the results intact.  Replace the whole instruction.
371             fmtln!(
372                 fmt,
373                 "let {} = pos.func.dfg.replace(inst).{};",
374                 defined_vars,
375                 def.apply.rust_builder(&def.defined_vars, var_pool)
376             );
377 
378             // We need to bump the cursor so following instructions are inserted *after* the
379             // replaced instruction.
380             fmt.line("if pos.current_inst() == Some(inst) {");
381             fmt.indent(|fmt| {
382                 fmt.line("pos.next_inst();");
383             });
384             fmt.line("}");
385             return;
386         }
387     }
388 
389     // Insert a new instruction.
390     let mut builder = format!("let {} = pos.ins()", defined_vars);
391 
392     if def.defined_vars.len() == 1 && var_pool.get(def.defined_vars[0]).is_output() {
393         // Reuse the single source result value.
394         builder = format!(
395             "{}.with_result({})",
396             builder,
397             var_pool.get(def.defined_vars[0]).to_rust_code()
398         );
399     } else if def
400         .defined_vars
401         .iter()
402         .any(|&var_index| var_pool.get(var_index).is_output())
403     {
404         // There are more than one output values that can be reused.
405         let array = def
406             .defined_vars
407             .iter()
408             .map(|&var_index| {
409                 let var = var_pool.get(var_index);
410                 if var.is_output() {
411                     format!("Some({})", var.name)
412                 } else {
413                     "None".into()
414                 }
415             })
416             .collect::<Vec<_>>()
417             .join(", ");
418         builder = format!("{}.with_results([{}])", builder, array);
419     }
420 
421     fmtln!(
422         fmt,
423         "{}.{};",
424         builder,
425         def.apply.rust_builder(&def.defined_vars, var_pool)
426     );
427 }
428 
429 /// Emit code for `transform`, assuming that the opcode of transform's root instruction
430 /// has already been matched.
431 ///
432 /// `inst: Inst` is the variable to be replaced. It is pointed to by `pos: Cursor`.
433 /// `dfg: DataFlowGraph` is available and mutable.
gen_transform<'a>( replace_inst: bool, transform: &'a Transform, type_sets: &mut UniqueTable<'a, TypeSet>, fmt: &mut Formatter, )434 fn gen_transform<'a>(
435     replace_inst: bool,
436     transform: &'a Transform,
437     type_sets: &mut UniqueTable<'a, TypeSet>,
438     fmt: &mut Formatter,
439 ) {
440     // Evaluate the instruction predicate if any.
441     let apply = &transform.def_pool.get(transform.src).apply;
442 
443     let inst_predicate = apply
444         .inst_predicate_with_ctrl_typevar(&transform.var_pool)
445         .rust_predicate("pos.func");
446 
447     let has_extra_constraints = !transform.type_env.constraints.is_empty();
448     if has_extra_constraints {
449         // Extra constraints rely on the predicate being a variable that we can rebind as we add
450         // more constraint predicates.
451         if let Some(pred) = &inst_predicate {
452             fmt.multi_line(&format!("let predicate = {};", pred));
453         } else {
454             fmt.line("let predicate = true;");
455         }
456     }
457 
458     // Emit any runtime checks; these will rebind `predicate` emitted right above.
459     for constraint in &transform.type_env.constraints {
460         emit_runtime_typecheck(constraint, type_sets, fmt);
461     }
462 
463     let do_expand = |fmt: &mut Formatter| {
464         // Emit any constants that must be created before use.
465         for (name, value) in transform.const_pool.iter() {
466             fmtln!(
467                 fmt,
468                 "let {} = pos.func.dfg.constants.insert(vec!{:?}.into());",
469                 name,
470                 value
471             );
472         }
473 
474         // If we are adding some blocks, we need to recall the original block, such that we can
475         // recompute it.
476         if !transform.block_pool.is_empty() {
477             fmt.line("let orig_block = pos.current_block().unwrap();");
478         }
479 
480         // If we're going to delete `inst`, we need to detach its results first so they can be
481         // reattached during pattern expansion.
482         if !replace_inst {
483             fmt.line("pos.func.dfg.clear_results(inst);");
484         }
485 
486         // Emit new block creation.
487         for block in &transform.block_pool {
488             let var = transform.var_pool.get(block.name);
489             fmtln!(fmt, "let {} = pos.func.dfg.make_block();", var.name);
490         }
491 
492         // Emit the destination pattern.
493         for &def_index in &transform.dst {
494             if let Some(block) = transform.block_pool.get(def_index) {
495                 let var = transform.var_pool.get(block.name);
496                 fmtln!(fmt, "pos.insert_block({});", var.name);
497             }
498             emit_dst_inst(
499                 transform.def_pool.get(def_index),
500                 &transform.def_pool,
501                 &transform.var_pool,
502                 fmt,
503             );
504         }
505 
506         // Insert a new block after the last instruction, if needed.
507         let def_next_index = transform.def_pool.next_index();
508         if let Some(block) = transform.block_pool.get(def_next_index) {
509             let var = transform.var_pool.get(block.name);
510             fmtln!(fmt, "pos.insert_block({});", var.name);
511         }
512 
513         // Delete the original instruction if we didn't have an opportunity to replace it.
514         if !replace_inst {
515             fmt.line("let removed = pos.remove_inst();");
516             fmt.line("debug_assert_eq!(removed, inst);");
517         }
518 
519         if transform.block_pool.is_empty() {
520             if transform.def_pool.get(transform.src).apply.inst.is_branch {
521                 // A branch might have been legalized into multiple branches, so we need to recompute
522                 // the cfg.
523                 fmt.line("cfg.recompute_block(pos.func, pos.current_block().unwrap());");
524             }
525         } else {
526             // Update CFG for the new blocks.
527             fmt.line("cfg.recompute_block(pos.func, orig_block);");
528             for block in &transform.block_pool {
529                 let var = transform.var_pool.get(block.name);
530                 fmtln!(fmt, "cfg.recompute_block(pos.func, {});", var.name);
531             }
532         }
533 
534         fmt.line("return true;");
535     };
536 
537     // Guard the actual expansion by `predicate`.
538     if has_extra_constraints {
539         fmt.line("if predicate {");
540         fmt.indent(|fmt| {
541             do_expand(fmt);
542         });
543         fmt.line("}");
544     } else if let Some(pred) = &inst_predicate {
545         fmt.multi_line(&format!("if {} {{", pred));
546         fmt.indent(|fmt| {
547             do_expand(fmt);
548         });
549         fmt.line("}");
550     } else {
551         // Unconditional transform (there was no predicate), just emit it.
552         do_expand(fmt);
553     }
554 }
555 
gen_transform_group<'a>( group: &'a TransformGroup, transform_groups: &TransformGroups, type_sets: &mut UniqueTable<'a, TypeSet>, fmt: &mut Formatter, )556 fn gen_transform_group<'a>(
557     group: &'a TransformGroup,
558     transform_groups: &TransformGroups,
559     type_sets: &mut UniqueTable<'a, TypeSet>,
560     fmt: &mut Formatter,
561 ) {
562     fmt.doc_comment(group.doc);
563     fmt.line("#[allow(unused_variables,unused_assignments,unused_imports,non_snake_case)]");
564 
565     // Function arguments.
566     fmtln!(fmt, "pub fn {}(", group.name);
567     fmt.indent(|fmt| {
568         fmt.line("inst: crate::ir::Inst,");
569         fmt.line("func: &mut crate::ir::Function,");
570         fmt.line("cfg: &mut crate::flowgraph::ControlFlowGraph,");
571         fmt.line("isa: &dyn crate::isa::TargetIsa,");
572     });
573     fmtln!(fmt, ") -> bool {");
574 
575     // Function body.
576     fmt.indent(|fmt| {
577         fmt.line("use crate::ir::InstBuilder;");
578         fmt.line("use crate::cursor::{Cursor, FuncCursor};");
579         fmt.line("let mut pos = FuncCursor::new(func).at_inst(inst);");
580         fmt.line("pos.use_srcloc(inst);");
581 
582         // Group the transforms by opcode so we can generate a big switch.
583         // Preserve ordering.
584         let mut inst_to_transforms = HashMap::new();
585         for transform in &group.transforms {
586             let def_index = transform.src;
587             let inst = &transform.def_pool.get(def_index).apply.inst;
588             inst_to_transforms
589                 .entry(inst.camel_name.clone())
590                 .or_insert_with(Vec::new)
591                 .push(transform);
592         }
593 
594         let mut sorted_inst_names = Vec::from_iter(inst_to_transforms.keys());
595         sorted_inst_names.sort();
596 
597         fmt.line("{");
598         fmt.indent(|fmt| {
599             fmt.line("match pos.func.dfg[inst].opcode() {");
600             fmt.indent(|fmt| {
601                 for camel_name in sorted_inst_names {
602                     fmtln!(fmt, "ir::Opcode::{} => {{", camel_name);
603                     fmt.indent(|fmt| {
604                         let transforms = inst_to_transforms.get(camel_name).unwrap();
605 
606                         // Unwrap the source instruction, create local variables for the input variables.
607                         let replace_inst = unwrap_inst(&transforms[0], fmt);
608                         fmt.empty_line();
609 
610                         for (i, transform) in transforms.iter().enumerate() {
611                             if i > 0 {
612                                 fmt.empty_line();
613                             }
614                             gen_transform(replace_inst, transform, type_sets, fmt);
615                         }
616                     });
617                     fmtln!(fmt, "}");
618                     fmt.empty_line();
619                 }
620 
621                 // Emit the custom transforms. The Rust compiler will complain about any overlap with
622                 // the normal transforms.
623                 let mut sorted_custom_legalizes = Vec::from_iter(&group.custom_legalizes);
624                 sorted_custom_legalizes.sort();
625                 for (inst_camel_name, func_name) in sorted_custom_legalizes {
626                     fmtln!(fmt, "ir::Opcode::{} => {{", inst_camel_name);
627                     fmt.indent(|fmt| {
628                         fmtln!(fmt, "{}(inst, func, cfg, isa);", func_name);
629                         fmt.line("return true;");
630                     });
631                     fmtln!(fmt, "}");
632                     fmt.empty_line();
633                 }
634 
635                 // We'll assume there are uncovered opcodes.
636                 fmt.line("_ => {},");
637             });
638             fmt.line("}");
639         });
640         fmt.line("}");
641 
642         // If we fall through, nothing was expanded; call the chain if any.
643         match &group.chain_with {
644             Some(group_id) => fmtln!(
645                 fmt,
646                 "{}(inst, func, cfg, isa)",
647                 transform_groups.get(*group_id).rust_name()
648             ),
649             None => fmt.line("false"),
650         };
651     });
652     fmtln!(fmt, "}");
653     fmt.empty_line();
654 }
655 
656 /// Generate legalization functions for `isa` and add any shared `TransformGroup`s
657 /// encountered to `shared_groups`.
658 ///
659 /// Generate `TYPE_SETS` and `LEGALIZE_ACTIONS` tables.
gen_isa( isa: &TargetIsa, transform_groups: &TransformGroups, shared_group_names: &mut HashSet<&'static str>, fmt: &mut Formatter, )660 fn gen_isa(
661     isa: &TargetIsa,
662     transform_groups: &TransformGroups,
663     shared_group_names: &mut HashSet<&'static str>,
664     fmt: &mut Formatter,
665 ) {
666     let mut type_sets = UniqueTable::new();
667     for group_index in isa.transitive_transform_groups(transform_groups) {
668         let group = transform_groups.get(group_index);
669         match group.isa_name {
670             Some(isa_name) => {
671                 assert!(
672                     isa_name == isa.name,
673                     "ISA-specific legalizations must be used by the same ISA"
674                 );
675                 gen_transform_group(group, transform_groups, &mut type_sets, fmt);
676             }
677             None => {
678                 shared_group_names.insert(group.name);
679             }
680         }
681     }
682 
683     gen_typesets_table(&type_sets, fmt);
684 
685     let direct_groups = isa.direct_transform_groups();
686     fmtln!(
687         fmt,
688         "pub static LEGALIZE_ACTIONS: [isa::Legalize; {}] = [",
689         direct_groups.len()
690     );
691     fmt.indent(|fmt| {
692         for &group_index in direct_groups {
693             fmtln!(fmt, "{},", transform_groups.get(group_index).rust_name());
694         }
695     });
696     fmtln!(fmt, "];");
697 }
698 
699 /// Generate the legalizer files.
generate( isas: &[TargetIsa], transform_groups: &TransformGroups, filename_prefix: &str, out_dir: &str, ) -> Result<(), error::Error>700 pub(crate) fn generate(
701     isas: &[TargetIsa],
702     transform_groups: &TransformGroups,
703     filename_prefix: &str,
704     out_dir: &str,
705 ) -> Result<(), error::Error> {
706     let mut shared_group_names = HashSet::new();
707 
708     for isa in isas {
709         let mut fmt = Formatter::new();
710         gen_isa(isa, transform_groups, &mut shared_group_names, &mut fmt);
711         fmt.update_file(format!("{}-{}.rs", filename_prefix, isa.name), out_dir)?;
712     }
713 
714     // Generate shared legalize groups.
715     let mut fmt = Formatter::new();
716     let mut type_sets = UniqueTable::new();
717     let mut sorted_shared_group_names = Vec::from_iter(shared_group_names);
718     sorted_shared_group_names.sort();
719     for group_name in &sorted_shared_group_names {
720         let group = transform_groups.by_name(group_name);
721         gen_transform_group(group, transform_groups, &mut type_sets, &mut fmt);
722     }
723     gen_typesets_table(&type_sets, &mut fmt);
724     fmt.update_file(format!("{}r.rs", filename_prefix), out_dir)?;
725 
726     Ok(())
727 }
728