1use 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
17const TYPESET_LIMIT: usize = 0xff;
19
20fn 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 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
64fn 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 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 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
166fn 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 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 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 ); 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 ); 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 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 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 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 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 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 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 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
682fn 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
752pub(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
781fn gen_type_constraints(all_inst: &AllInstructions, fmt: &mut Formatter) {
786 let mut type_sets = UniqueTable::new();
788
789 let mut operand_seqs = UniqueSeqTable::new();
795
796 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 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 let use_typevar_operand = if let Some(poly) = &inst.polymorphic_info {
831 poly.use_typevar_operand
832 } else {
833 false
834 };
835
836 let use_result = fixed_results > 0 && inst.operands_out[inst.value_results[0]].type_var() == ctrl_typevar;
838
839 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 assert!(fixed_results < 8 && fixed_values < 8, "Bit field encoding too tight");
861 let mut flags = fixed_results; if use_typevar_operand {
863 flags |= 1<<3; }
865 if requires_typevar_operand {
866 flags |= 1<<4; }
868 flags |= fixed_values << 5; 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
899fn gen_member_inits(format: &InstructionFormat, fmt: &mut Formatter) {
901 for f in &format.imm_fields {
904 fmtln!(fmt, "{},", f.member);
905 }
906
907 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 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
934fn gen_format_constructor(format: &InstructionFormat, fmt: &mut Formatter) {
939 let mut args = vec![
941 "self".to_string(),
942 "opcode: Opcode".into(),
943 "ctrl_typevar: Type".into(),
944 ];
945
946 for f in &format.imm_fields {
948 args.push(format!("{}: {}", f.member, f.kind.rust_type));
949 }
950
951 args.extend((0..format.num_block_operands).map(|i| format!("block{i}: ir::BlockCall")));
953
954 if format.has_value_list {
956 args.push("args: ir::ValueList".into());
959 } else {
960 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 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 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
1006fn gen_inst_builder(inst: &Instruction, format: &InstructionFormat, fmt: &mut Formatter) {
1011 let mut args = vec![String::new()];
1013
1014 let mut args_doc = Vec::new();
1015 let mut rets_doc = Vec::new();
1016
1017 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 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 for arg in into_args {
1113 fmtln!(fmt, "let {} = {}.into();", arg, arg);
1114 }
1115
1116 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 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 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 args.push("ctrl_typevar");
1140 } else {
1141 args.push(&poly.ctrl_typevar.name);
1143 }
1144 } else {
1145 args.push("types::INVALID");
1147 }
1148
1149 for &op_num in &inst.imm_opnums {
1151 args.push(inst.operands_in[op_num].name);
1152 }
1153
1154 if format.has_value_list {
1156 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 for &op_num in &inst.value_opnums {
1174 args.push(inst.operands_in[op_num].name);
1175 }
1176 }
1177
1178 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
1210fn 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 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 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}