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, extra_legalization_groups: &[&'static str], filename_prefix: &str, out_dir: &str, ) -> Result<(), error::Error>700 pub(crate) fn generate(
701 isas: &[TargetIsa],
702 transform_groups: &TransformGroups,
703 extra_legalization_groups: &[&'static str],
704 filename_prefix: &str,
705 out_dir: &str,
706 ) -> Result<(), error::Error> {
707 let mut shared_group_names = HashSet::new();
708
709 for isa in isas {
710 let mut fmt = Formatter::new();
711 gen_isa(isa, transform_groups, &mut shared_group_names, &mut fmt);
712 fmt.update_file(format!("{}-{}.rs", filename_prefix, isa.name), out_dir)?;
713 }
714
715 // Add extra legalization groups that were explicitly requested.
716 for group in extra_legalization_groups {
717 shared_group_names.insert(group);
718 }
719
720 // Generate shared legalize groups.
721 let mut fmt = Formatter::new();
722 // Generate shared legalize groups.
723 let mut type_sets = UniqueTable::new();
724 let mut sorted_shared_group_names = Vec::from_iter(shared_group_names);
725 sorted_shared_group_names.sort();
726 for group_name in &sorted_shared_group_names {
727 let group = transform_groups.by_name(group_name);
728 gen_transform_group(group, transform_groups, &mut type_sets, &mut fmt);
729 }
730 gen_typesets_table(&type_sets, &mut fmt);
731 fmt.update_file(format!("{}r.rs", filename_prefix), out_dir)?;
732
733 Ok(())
734 }
735