1use crate::cdsl::camel_case;
4use crate::cdsl::formats::InstructionFormat;
5use crate::cdsl::instructions::{AllInstructions, Instruction};
6use crate::cdsl::operands::{Operand, OperandKindFields};
7use crate::cdsl::typevar::{TypeSet, TypeVar};
8use crate::unique_table::{UniqueSeqTable, UniqueTable};
9use cranelift_codegen_shared::constant_hash;
10use cranelift_srcgen::{Formatter, Language, Match, error, fmtln};
11use std::fmt;
12use std::rc::Rc;
13
14const TYPESET_LIMIT: usize = 0xff;
16
17fn gen_formats(formats: &[Rc<InstructionFormat>], fmt: &mut Formatter) {
19 fmt.doc_comment(
20 r#"
21 An instruction format
22
23 Every opcode has a corresponding instruction format
24 which is represented by both the `InstructionFormat`
25 and the `InstructionData` enums.
26 "#,
27 );
28 fmt.line("#[derive(Copy, Clone, PartialEq, Eq, Debug)]");
29 fmt.add_block("pub enum InstructionFormat", |fmt| {
30 for format in formats {
31 fmt.doc_comment(format.to_string());
32 fmtln!(fmt, "{},", format.name);
33 }
34 });
35 fmt.empty_line();
36
37 fmt.add_block(
40 "impl<'a> From<&'a InstructionData> for InstructionFormat",
41 |fmt| {
42 fmt.add_block("fn from(inst: &'a InstructionData) -> Self", |fmt| {
43 let mut m = Match::new("*inst");
44 for format in formats {
45 m.arm(
46 format!("InstructionData::{}", format.name),
47 vec![".."],
48 format!("Self::{}", format.name),
49 );
50 }
51 fmt.add_match(m);
52 });
53 },
54 );
55 fmt.empty_line();
56}
57
58fn gen_instruction_data(formats: &[Rc<InstructionFormat>], fmt: &mut Formatter) {
64 fmt.line("#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]");
65 fmt.line(r#"#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]"#);
66 fmt.line("#[allow(missing_docs, reason = \"generated code\")]");
67 fmt.add_block("pub enum InstructionData", |fmt| {
68 for format in formats {
69 fmt.add_block(&format!("{}", format.name), |fmt| {
70 fmt.line("opcode: Opcode,");
71 if format.has_value_list {
72 fmt.line("args: ValueList,");
73 } else if format.num_value_operands == 1 {
74 fmt.line("arg: Value,");
75 } else if format.num_value_operands > 0 {
76 fmtln!(fmt, "args: [Value; {}],", format.num_value_operands);
77 }
78
79 match format.num_block_operands {
80 0 => (),
81 1 => fmt.line("destination: ir::BlockCall,"),
82 2 => fmtln!(
83 fmt,
84 "blocks: [ir::BlockCall; {}],",
85 format.num_block_operands
86 ),
87 n => panic!("Too many block operands in instruction: {n}"),
88 }
89
90 match format.num_raw_block_operands {
91 0 => (),
92 1 => fmt.line("block: ir::Block,"),
93 n => panic!("Too many block operands in instruction: {n}"),
94 }
95
96 for field in &format.imm_fields {
97 fmtln!(fmt, "{}: {},", field.member, field.kind.rust_type);
98 }
99 });
100 fmtln!(fmt, ",");
101 }
102 });
103}
104
105fn gen_arguments_method(formats: &[Rc<InstructionFormat>], fmt: &mut Formatter, is_mut: bool) {
106 let (method, mut_, rslice, as_slice) = if is_mut {
107 (
108 "arguments_mut",
109 "mut ",
110 "core::slice::from_mut",
111 "as_mut_slice",
112 )
113 } else {
114 ("arguments", "", "core::slice::from_ref", "as_slice")
115 };
116
117 fmt.add_block(&format!(
118 "pub fn {method}<'a>(&'a {mut_}self, pool: &'a {mut_}ir::ValueListPool) -> &'a {mut_}[Value]"),
119
120 |fmt| {
121 let mut m = Match::new("*self");
122 for format in formats {
123 let name = format!("Self::{}", format.name);
124
125 if format.has_value_list {
129 m.arm(
130 name,
131 vec![format!("ref {}args", mut_), "..".to_string()],
132 format!("args.{as_slice}(pool)"),
133 );
134 continue;
135 }
136
137 let mut fields = Vec::new();
139 let arg = if format.num_value_operands == 0 {
140 format!("&{mut_}[]")
141 } else if format.num_value_operands == 1 {
142 fields.push(format!("ref {mut_}arg"));
143 format!("{rslice}(arg)")
144 } else {
145 let arg = format!("args_arity{}", format.num_value_operands);
146 fields.push(format!("args: ref {mut_}{arg}"));
147 arg
148 };
149 fields.push("..".into());
150
151 m.arm(name, fields, arg);
152 }
153 fmt.add_match(m);
154 });
155}
156
157fn gen_instruction_data_impl(formats: &[Rc<InstructionFormat>], fmt: &mut Formatter) {
168 fmt.add_block("impl InstructionData", |fmt| {
169 fmt.doc_comment("Get the opcode of this instruction.");
170 fmt.add_block("pub fn opcode(&self) -> Opcode",|fmt| {
171 let mut m = Match::new("*self");
172 for format in formats {
173 m.arm(format!("Self::{}", format.name), vec!["opcode", ".."],
174 "opcode".to_string());
175 }
176 fmt.add_match(m);
177 });
178 fmt.empty_line();
179
180 fmt.doc_comment("Get the controlling type variable operand.");
181 fmt.add_block("pub fn typevar_operand(&self, pool: &ir::ValueListPool) -> Option<Value>",|fmt| {
182 let mut m = Match::new("*self");
183 for format in formats {
184 let name = format!("Self::{}", format.name);
185 if format.typevar_operand.is_none() {
186 m.arm(name, vec![".."], "None".to_string());
187 } else if format.has_value_list {
188 m.arm(name, vec!["ref args", ".."], format!("args.get({}, pool)", format.typevar_operand.unwrap()));
190 } else if format.num_value_operands == 1 {
191 m.arm(name, vec!["arg", ".."], "Some(arg)".to_string());
192 } else {
193 let args = format!("args_arity{}", format.num_value_operands);
196 m.arm(name, vec![format!("args: ref {}", args), "..".to_string()],
197 format!("Some({}[{}])", args, format.typevar_operand.unwrap()));
198 }
199 }
200 fmt.add_match(m);
201 });
202 fmt.empty_line();
203
204 fmt.doc_comment("Get the value arguments to this instruction.");
205 gen_arguments_method(formats, fmt, false);
206 fmt.empty_line();
207
208 fmt.doc_comment(r#"Get mutable references to the value arguments to this
209 instruction."#);
210 gen_arguments_method(formats, fmt, true);
211 fmt.empty_line();
212
213 fmt.doc_comment(r#"
214 Compare two `InstructionData` for equality.
215
216 This operation requires a reference to a `ValueListPool` to
217 determine if the contents of any `ValueLists` are equal.
218
219 This operation takes a closure that is allowed to map each
220 argument value to some other value before the instructions
221 are compared. This allows various forms of canonicalization.
222 "#);
223 fmt.add_block("pub fn eq(&self, other: &Self, pool: &ir::ValueListPool) -> bool", |fmt| {
224 fmt.add_block("if ::core::mem::discriminant(self) != ::core::mem::discriminant(other)", |fmt| {
225 fmt.line("return false;");
226 });
227
228 fmt.add_block("match (self, other)",|fmt| {
229 for format in formats {
230 let name = format!("&Self::{}", format.name);
231 let mut members = vec!["opcode"];
232
233 let args_eq = if format.has_value_list {
234 members.push("args");
235 Some("args1.as_slice(pool).iter().zip(args2.as_slice(pool).iter()).all(|(a, b)| a == b)")
236 } else if format.num_value_operands == 1 {
237 members.push("arg");
238 Some("arg1 == arg2")
239 } else if format.num_value_operands > 0 {
240 members.push("args");
241 Some("args1.iter().zip(args2.iter()).all(|(a, b)| a == b)")
242 } else {
243 None
244 };
245
246 let blocks_eq = match format.num_block_operands {
247 0 => None,
248 1 => {
249 members.push("destination");
250 Some("destination1 == destination2")
251 },
252 _ => {
253 members.push("blocks");
254 Some("blocks1.iter().zip(blocks2.iter()).all(|(a, b)| a.block(pool) == b.block(pool))")
255 }
256 };
257
258 let raw_blocks_eq = match format.num_raw_block_operands {
259 0 => None,
260 1 => {
261 members.push("block");
262 Some("block1 == block2")
263 }
264 _ => unreachable!("Not a valid format"),
265 };
266
267 for field in &format.imm_fields {
268 members.push(field.member);
269 }
270
271 let pat1 = members.iter().map(|x| format!("{x}: ref {x}1")).collect::<Vec<_>>().join(", ");
272 let pat2 = members.iter().map(|x| format!("{x}: ref {x}2")).collect::<Vec<_>>().join(", ");
273 fmt.add_block(&format!("({name} {{ {pat1} }}, {name} {{ {pat2} }}) => "), |fmt| {
274 fmt.line("opcode1 == opcode2");
275 for field in &format.imm_fields {
276 fmtln!(fmt, "&& {}1 == {}2", field.member, field.member);
277 }
278 if let Some(args_eq) = args_eq {
279 fmtln!(fmt, "&& {}", args_eq);
280 }
281 if let Some(blocks_eq) = blocks_eq {
282 fmtln!(fmt, "&& {}", blocks_eq);
283 }
284 if let Some(raw_blocks_eq) = raw_blocks_eq {
285 fmtln!(fmt, "&& {}", raw_blocks_eq);
286 }
287 });
288 }
289 fmt.line("_ => unreachable!()");
290 });
291 });
292 fmt.empty_line();
293
294 fmt.doc_comment(r#"
295 Hash an `InstructionData`.
296
297 This operation requires a reference to a `ValueListPool` to
298 hash the contents of any `ValueLists`.
299
300 This operation takes a closure that is allowed to map each
301 argument value to some other value before it is hashed. This
302 allows various forms of canonicalization.
303 "#);
304 fmt.add_block("pub fn hash<H: ::core::hash::Hasher>(&self, state: &mut H, pool: &ir::ValueListPool)",|fmt| {
305 fmt.add_block("match *self",|fmt| {
306 for format in formats {
307 let name = format!("Self::{}", format.name);
308 let mut members = vec!["opcode"];
309
310 let (args, len) = if format.has_value_list {
311 members.push("ref args");
312 (Some("args.as_slice(pool)"), "args.len(pool)")
313 } else if format.num_value_operands == 1 {
314 members.push("ref arg");
315 (Some("core::slice::from_ref(arg)"), "1")
316 } else if format.num_value_operands > 0 {
317 members.push("ref args");
318 (Some("args"), "args.len()")
319 } else {
320 (None, "0")
321 };
322
323 let blocks = match format.num_block_operands {
324 0 => None,
325 1 => {
326 members.push("ref destination");
327 Some(("core::slice::from_ref(destination)", "1"))
328 }
329 _ => {
330 members.push("ref blocks");
331 Some(("blocks", "blocks.len()"))
332 }
333 };
334
335 let raw_block = match format.num_raw_block_operands {
336 0 => None,
337 1 => {
338 members.push("block");
339 Some("block")
340 }
341 _ => panic!("Too many raw block operands"),
342 };
343
344 for field in &format.imm_fields {
345 members.push(field.member);
346 }
347 let members = members.join(", ");
348
349 fmt.add_block(&format!("{name}{{{members}}} => "), |fmt| {
350 fmt.line("::core::hash::Hash::hash( &::core::mem::discriminant(self), state);");
351 fmt.line("::core::hash::Hash::hash(&opcode, state);");
352 for field in &format.imm_fields {
353 fmtln!(fmt, "::core::hash::Hash::hash(&{}, state);", field.member);
354 }
355 fmtln!(fmt, "::core::hash::Hash::hash(&{}, state);", len);
356 if let Some(args) = args {
357 fmt.add_block(&format!("for &arg in {args}"), |fmt| {
358 fmtln!(fmt, "::core::hash::Hash::hash(&arg, state);");
359 });
360 }
361
362 if let Some((blocks, len)) = blocks {
363 fmtln!(fmt, "::core::hash::Hash::hash(&{len}, state);");
364 fmt.add_block(&format!("for &block in {blocks}"), |fmt| {
365 fmtln!(fmt, "::core::hash::Hash::hash(&block.block(pool), state);");
366 fmt.add_block("for arg in block.args(pool)", |fmt| {
367 fmtln!(fmt, "::core::hash::Hash::hash(&arg, state);");
368 });
369 });
370 }
371
372 if let Some(raw_block) = raw_block {
373 fmtln!(fmt, "::core::hash::Hash::hash(&{raw_block}, state);");
374 }
375 });
376 }
377 });
378 });
379
380 fmt.empty_line();
381
382 fmt.doc_comment(r#"
383 Deep-clone an `InstructionData`, including any referenced lists.
384
385 This operation requires a reference to a `ValueListPool` to
386 clone the `ValueLists`.
387 "#);
388 fmt.add_block("pub fn deep_clone(&self, pool: &mut ir::ValueListPool) -> Self",|fmt| {
389 fmt.add_block("match *self",|fmt| {
390 for format in formats {
391 let name = format!("Self::{}", format.name);
392 let mut members = vec!["opcode"];
393
394 if format.has_value_list {
395 members.push("ref args");
396 } else if format.num_value_operands == 1 {
397 members.push("arg");
398 } else if format.num_value_operands > 0 {
399 members.push("args");
400 }
401
402 match format.num_block_operands {
403 0 => {}
404 1 => {
405 members.push("destination");
406 }
407 _ => {
408 members.push("blocks");
409 }
410 };
411
412 match format.num_raw_block_operands {
413 0 => {}
414 1 => {
415 members.push("block");
416 }
417 _ => panic!("Too many raw-block operands to format"),
418 }
419
420 for field in &format.imm_fields {
421 members.push(field.member);
422 }
423 let members = members.join(", ");
424
425 fmt.add_block(&format!("{name}{{{members}}} => "),|fmt| {
426 fmt.add_block(&format!("Self::{}", format.name), |fmt| {
427 fmtln!(fmt, "opcode,");
428
429 if format.has_value_list {
430 fmtln!(fmt, "args: args.deep_clone(pool),");
431 } else if format.num_value_operands == 1 {
432 fmtln!(fmt, "arg,");
433 } else if format.num_value_operands > 0 {
434 fmtln!(fmt, "args,");
435 }
436
437 match format.num_block_operands {
438 0 => {}
439 1 => {
440 fmtln!(fmt, "destination: destination.deep_clone(pool),");
441 }
442 2 => {
443 fmtln!(fmt, "blocks: [blocks[0].deep_clone(pool), blocks[1].deep_clone(pool)],");
444 }
445 _ => panic!("Too many block targets in instruction"),
446 }
447
448 match format.num_raw_block_operands {
449 0 => {}
450 1 => {
451 fmtln!(fmt, "block,");
452 }
453 _ => panic!("Too many raw-block operands in instruction"),
454 }
455
456 for field in &format.imm_fields {
457 fmtln!(fmt, "{},", field.member);
458 }
459 });
460 });
461 }
462 });
463 });
464 fmt.doc_comment(r#"
465 Map some functions, described by the given `InstructionMapper`, over each of the
466 entities within this instruction, producing a new `InstructionData`.
467 "#);
468 fmt.add_block("pub fn map(&self, mut mapper: impl crate::ir::instructions::InstructionMapper) -> Self", |fmt| {
469 fmt.add_block("match *self",|fmt| {
470 for format in formats {
471 let name = format!("Self::{}", format.name);
472 let mut members = vec!["opcode"];
473
474 if format.has_value_list {
475 members.push("args");
476 } else if format.num_value_operands == 1 {
477 members.push("arg");
478 } else if format.num_value_operands > 0 {
479 members.push("args");
480 }
481
482 match format.num_block_operands {
483 0 => {}
484 1 => {
485 members.push("destination");
486 }
487 _ => {
488 members.push("blocks");
489 }
490 };
491
492 match format.num_raw_block_operands {
493 0 => {}
494 1 => {
495 members.push("block");
496 }
497 _ => panic!("Too many raw-block operands"),
498 }
499
500 for field in &format.imm_fields {
501 members.push(field.member);
502 }
503 let members = members.join(", ");
504
505 fmt.add_block(&format!("{name}{{{members}}} => "), |fmt| {
506 fmt.add_block(&format!("Self::{}", format.name), |fmt| {
507 fmtln!(fmt, "opcode,");
508
509 if format.has_value_list {
510 fmtln!(fmt, "args: mapper.map_value_list(args),");
511 } else if format.num_value_operands == 1 {
512 fmtln!(fmt, "arg: mapper.map_value(arg),");
513 } else if format.num_value_operands > 0 {
514 let maps = (0..format.num_value_operands)
515 .map(|i| format!("mapper.map_value(args[{i}])"))
516 .collect::<Box<[_]>>()
517 .join(", ");
518 fmtln!(fmt, "args: [{maps}],");
519 }
520
521 match format.num_block_operands {
522 0 => {}
523 1 => {
524 fmtln!(fmt, "destination: mapper.map_block_call(destination),");
525 }
526 2 => {
527 fmtln!(fmt, "blocks: [mapper.map_block_call(blocks[0]), mapper.map_block_call(blocks[1])],");
528 }
529 _ => panic!("Too many block targets in instruction"),
530 }
531
532 match format.num_raw_block_operands {
533 0 => {}
534 1 => {
535 fmtln!(fmt, "block: mapper.map_block(block),");
536 }
537 _ => panic!("Too many raw block arguments in instruction"),
538 }
539
540 for field in &format.imm_fields {
541 let member = field.member;
542 match &field.kind.fields {
543 OperandKindFields::EntityRef => {
544 let mut kind = heck::ToSnakeCase::to_snake_case(
545 field
546 .kind
547 .rust_type
548 .split("::")
549 .last()
550 .unwrap_or(field.kind.rust_type),
551 );
552 if kind == "block" {
553 kind.push_str("_call");
554 }
555 fmtln!(fmt, "{member}: mapper.map_{kind}({member}),");
556 }
557 OperandKindFields::VariableArgs => {
558 fmtln!(fmt, "{member}: mapper.map_value_list({member}),");
559 }
560 OperandKindFields::ImmValue
561 if field.kind.rust_type == "ir::MemFlags" =>
562 {
563 fmtln!(fmt, "{member}: mapper.map_mem_flags({member}),");
564 }
565 OperandKindFields::ImmValue |
566 OperandKindFields::ImmEnum(_) |
567 OperandKindFields::TypeVar(_) => fmtln!(fmt, "{member},"),
568 }
569 }
570 });
571 });
572 }
573 });
574 });
575 });
576}
577
578fn gen_bool_accessor<T: Fn(&Instruction) -> bool>(
579 all_inst: &AllInstructions,
580 get_attr: T,
581 name: &'static str,
582 doc: &'static str,
583 fmt: &mut Formatter,
584) {
585 fmt.doc_comment(doc);
586 fmt.add_block(&format!("pub fn {name}(self) -> bool"), |fmt| {
587 let mut m = Match::new("self");
588 for inst in all_inst.iter() {
589 if get_attr(inst) {
590 m.arm_no_fields(format!("Self::{}", inst.camel_name), "true");
591 }
592 }
593 m.arm_no_fields("_", "false");
594 fmt.add_match(m);
595 });
596 fmt.empty_line();
597}
598
599fn gen_opcodes(all_inst: &AllInstructions, fmt: &mut Formatter) {
600 fmt.doc_comment(
601 r#"
602 An instruction opcode.
603
604 All instructions from all supported ISAs are present.
605 "#,
606 );
607 fmt.line("#[repr(u8)]");
608 fmt.line("#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]");
609 fmt.line(
610 r#"#[cfg_attr(
611 feature = "enable-serde",
612 derive(serde_derive::Serialize, serde_derive::Deserialize)
613 )]"#,
614 );
615
616 fmt.add_block("pub enum Opcode", |fmt| {
621 let mut is_first_opcode = true;
622 for inst in all_inst.iter() {
623 fmt.doc_comment(format!("`{}`. ({})", inst, inst.format.name));
624
625 if let Some(poly) = &inst.polymorphic_info {
627 if poly.use_typevar_operand {
628 let op_num = inst.value_opnums[inst.format.typevar_operand.unwrap()];
629 fmt.doc_comment(format!(
630 "Type inferred from `{}`.",
631 inst.operands_in[op_num].name
632 ));
633 }
634 }
635
636 if is_first_opcode {
638 fmtln!(fmt, "{} = 1,", inst.camel_name);
639 is_first_opcode = false;
640 } else {
641 fmtln!(fmt, "{},", inst.camel_name)
642 }
643 }
644 });
645 fmt.empty_line();
646
647 fmt.add_block("impl Opcode", |fmt| {
648 gen_bool_accessor(
649 all_inst,
650 |inst| inst.is_terminator,
651 "is_terminator",
652 "True for instructions that terminate the block",
653 fmt,
654 );
655 gen_bool_accessor(
656 all_inst,
657 |inst| inst.is_branch,
658 "is_branch",
659 "True for all branch or jump instructions.",
660 fmt,
661 );
662 gen_bool_accessor(
663 all_inst,
664 |inst| inst.is_call,
665 "is_call",
666 "Is this a call instruction?",
667 fmt,
668 );
669 gen_bool_accessor(
670 all_inst,
671 |inst| inst.is_return,
672 "is_return",
673 "Is this a return instruction?",
674 fmt,
675 );
676 gen_bool_accessor(
677 all_inst,
678 |inst| inst.can_load,
679 "can_load",
680 "Can this instruction read from memory?",
681 fmt,
682 );
683 gen_bool_accessor(
684 all_inst,
685 |inst| inst.can_store,
686 "can_store",
687 "Can this instruction write to memory?",
688 fmt,
689 );
690 gen_bool_accessor(
691 all_inst,
692 |inst| inst.can_trap,
693 "can_trap",
694 "Can this instruction cause a trap?",
695 fmt,
696 );
697 gen_bool_accessor(
698 all_inst,
699 |inst| inst.other_side_effects,
700 "other_side_effects",
701 "Does this instruction have other side effects besides can_* flags?",
702 fmt,
703 );
704 gen_bool_accessor(
705 all_inst,
706 |inst| inst.side_effects_idempotent,
707 "side_effects_idempotent",
708 "Despite having side effects, is this instruction okay to GVN?",
709 fmt,
710 );
711
712 fmt.doc_comment("All cranelift opcodes.");
714 fmt.add_block("pub fn all() -> &'static [Opcode]", |fmt| {
715 fmt.line("return &[");
716 for inst in all_inst {
717 fmt.indent(|fmt| {
718 fmtln!(fmt, "Opcode::{},", inst.camel_name);
719 });
720 }
721 fmt.line("];");
722 });
723 fmt.empty_line();
724 });
725 fmt.empty_line();
726
727 fmtln!(
729 fmt,
730 "const OPCODE_FORMAT: [InstructionFormat; {}] = [",
731 all_inst.len()
732 );
733 fmt.indent(|fmt| {
734 for inst in all_inst.iter() {
735 fmtln!(
736 fmt,
737 "InstructionFormat::{}, // {}",
738 inst.format.name,
739 inst.name
740 );
741 }
742 });
743 fmtln!(fmt, "];");
744 fmt.empty_line();
745
746 fmt.add_block("fn opcode_name(opc: Opcode) -> &\'static str", |fmt| {
748 let mut m = Match::new("opc");
749 for inst in all_inst.iter() {
750 m.arm_no_fields(
751 format!("Opcode::{}", inst.camel_name),
752 format!("\"{}\"", inst.name),
753 );
754 }
755 fmt.add_match(m);
756 });
757 fmt.empty_line();
758
759 let hash_table =
761 crate::constant_hash::generate_table(all_inst.iter(), all_inst.len(), |inst| {
762 constant_hash::simple_hash(&inst.name)
763 });
764 fmtln!(
765 fmt,
766 "const OPCODE_HASH_TABLE: [Option<Opcode>; {}] = [",
767 hash_table.len()
768 );
769 fmt.indent(|fmt| {
770 for i in hash_table {
771 match i {
772 Some(i) => fmtln!(fmt, "Some(Opcode::{}),", i.camel_name),
773 None => fmtln!(fmt, "None,"),
774 }
775 }
776 });
777 fmtln!(fmt, "];");
778 fmt.empty_line();
779}
780
781fn get_constraint<'entries, 'table>(
789 operand: &'entries Operand,
790 ctrl_typevar: Option<&TypeVar>,
791 type_sets: &'table mut UniqueTable<'entries, TypeSet>,
792) -> String {
793 assert!(operand.is_value());
794 let type_var = operand.type_var().unwrap();
795
796 if let Some(typ) = type_var.singleton_type() {
797 return format!("Concrete({})", typ.rust_name());
798 }
799
800 if let Some(free_typevar) = type_var.free_typevar() {
801 if ctrl_typevar.is_some() && free_typevar != *ctrl_typevar.unwrap() {
802 assert!(type_var.base.is_none());
803 return format!("Free({})", type_sets.add(type_var.get_raw_typeset()));
804 }
805 }
806
807 if let Some(base) = &type_var.base {
808 assert!(base.type_var == *ctrl_typevar.unwrap());
809 return camel_case(base.derived_func.name());
810 }
811
812 assert!(type_var == ctrl_typevar.unwrap());
813 "Same".into()
814}
815
816fn gen_bitset<'a, T: IntoIterator<Item = &'a u16>>(
817 iterable: T,
818 name: &'static str,
819 field_size: u8,
820 fmt: &mut Formatter,
821) {
822 let bits = iterable.into_iter().fold(0, |acc, x| {
823 assert!(x.is_power_of_two());
824 assert!(u32::from(*x) < (1 << u32::from(field_size)));
825 acc | x
826 });
827 fmtln!(fmt, "{}: ScalarBitSet::<u{}>({}),", name, field_size, bits);
828}
829
830fn iterable_to_string<I: fmt::Display, T: IntoIterator<Item = I>>(iterable: T) -> String {
831 let elems = iterable
832 .into_iter()
833 .map(|x| x.to_string())
834 .collect::<Vec<_>>()
835 .join(", ");
836 format!("{{{elems}}}")
837}
838
839fn typeset_to_string(ts: &TypeSet) -> String {
840 let mut result = format!("TypeSet(lanes={}", iterable_to_string(&ts.lanes));
841 if !ts.ints.is_empty() {
842 result += &format!(", ints={}", iterable_to_string(&ts.ints));
843 }
844 if !ts.floats.is_empty() {
845 result += &format!(", floats={}", iterable_to_string(&ts.floats));
846 }
847 result += ")";
848 result
849}
850
851pub(crate) fn gen_typesets_table(type_sets: &UniqueTable<TypeSet>, fmt: &mut Formatter) {
853 if type_sets.len() == 0 {
854 return;
855 }
856
857 fmt.comment("Table of value type sets.");
858 assert!(type_sets.len() <= TYPESET_LIMIT, "Too many type sets!");
859 fmtln!(
860 fmt,
861 "const TYPE_SETS: [ir::instructions::ValueTypeSet; {}] = [",
862 type_sets.len()
863 );
864 fmt.indent(|fmt| {
865 for ts in type_sets.iter() {
866 fmt.add_block("ir::instructions::ValueTypeSet", |fmt| {
867 fmt.comment(typeset_to_string(ts));
868 gen_bitset(&ts.lanes, "lanes", 16, fmt);
869 gen_bitset(&ts.dynamic_lanes, "dynamic_lanes", 16, fmt);
870 gen_bitset(&ts.ints, "ints", 8, fmt);
871 gen_bitset(&ts.floats, "floats", 8, fmt);
872 });
873 fmt.line(",");
874 }
875 });
876 fmtln!(fmt, "];");
877}
878
879fn gen_type_constraints(all_inst: &AllInstructions, fmt: &mut Formatter) {
884 let mut type_sets = UniqueTable::new();
886
887 let mut operand_seqs = UniqueSeqTable::new();
893
894 operand_seqs.add(&vec!["Same".to_string(); 3]);
896
897 fmt.comment("Table of opcode constraints.");
898 fmtln!(
899 fmt,
900 "const OPCODE_CONSTRAINTS: [OpcodeConstraints; {}] = [",
901 all_inst.len()
902 );
903 fmt.indent(|fmt| {
904 for inst in all_inst.iter() {
905 let (ctrl_typevar, ctrl_typeset) = if let Some(poly) = &inst.polymorphic_info {
906 let index = type_sets.add(poly.ctrl_typevar.get_raw_typeset());
907 (Some(&poly.ctrl_typevar), index)
908 } else {
909 (None, TYPESET_LIMIT)
910 };
911
912 let mut constraints = Vec::new();
915 for &index in &inst.value_results {
916 constraints.push(get_constraint(&inst.operands_out[index], ctrl_typevar, &mut type_sets));
917 }
918 for &index in &inst.value_opnums {
919 constraints.push(get_constraint(&inst.operands_in[index], ctrl_typevar, &mut type_sets));
920 }
921
922 let constraint_offset = operand_seqs.add(&constraints);
923
924 let fixed_results = inst.value_results.len();
925 let fixed_values = inst.value_opnums.len();
926
927 let use_typevar_operand = if let Some(poly) = &inst.polymorphic_info {
929 poly.use_typevar_operand
930 } else {
931 false
932 };
933
934 let use_result = fixed_results > 0 && inst.operands_out[inst.value_results[0]].type_var() == ctrl_typevar;
936
937 let requires_typevar_operand = use_typevar_operand && !use_result;
939
940 fmt.comment(
941 format!("{}: fixed_results={}, use_typevar_operand={}, requires_typevar_operand={}, fixed_values={}",
942 inst.camel_name,
943 fixed_results,
944 use_typevar_operand,
945 requires_typevar_operand,
946 fixed_values)
947 );
948 fmt.comment(format!("Constraints=[{}]", constraints
949 .iter()
950 .map(|x| format!("'{x}'"))
951 .collect::<Vec<_>>()
952 .join(", ")));
953 if let Some(poly) = &inst.polymorphic_info {
954 fmt.comment(format!("Polymorphic over {}", typeset_to_string(poly.ctrl_typevar.get_raw_typeset())));
955 }
956
957 assert!(fixed_results < 8 && fixed_values < 8, "Bit field encoding too tight");
959 let mut flags = fixed_results; if use_typevar_operand {
961 flags |= 1<<3; }
963 if requires_typevar_operand {
964 flags |= 1<<4; }
966 flags |= fixed_values << 5; fmt.add_block("OpcodeConstraints",|fmt| {
969 fmtln!(fmt, "flags: {:#04x},", flags);
970 fmtln!(fmt, "typeset_offset: {},", ctrl_typeset);
971 fmtln!(fmt, "constraint_offset: {},", constraint_offset);
972 });
973 fmt.line(",");
974 }
975 });
976 fmtln!(fmt, "];");
977 fmt.empty_line();
978
979 gen_typesets_table(&type_sets, fmt);
980 fmt.empty_line();
981
982 fmt.comment("Table of operand constraint sequences.");
983 fmtln!(
984 fmt,
985 "const OPERAND_CONSTRAINTS: [OperandConstraint; {}] = [",
986 operand_seqs.len()
987 );
988 fmt.indent(|fmt| {
989 for constraint in operand_seqs.iter() {
990 fmtln!(fmt, "OperandConstraint::{},", constraint);
991 }
992 });
993 fmtln!(fmt, "];");
994}
995
996fn gen_member_inits(format: &InstructionFormat, fmt: &mut Formatter) {
998 for f in &format.imm_fields {
1001 fmtln!(fmt, "{},", f.member);
1002 }
1003
1004 if format.has_value_list {
1006 fmt.line("args,");
1007 } else if format.num_value_operands == 1 {
1008 fmt.line("arg: arg0,");
1009 } else if format.num_value_operands > 1 {
1010 let mut args = Vec::new();
1011 for i in 0..format.num_value_operands {
1012 args.push(format!("arg{i}"));
1013 }
1014 fmtln!(fmt, "args: [{}],", args.join(", "));
1015 }
1016
1017 match format.num_block_operands {
1019 0 => (),
1020 1 => fmt.line("destination: block0"),
1021 n => {
1022 let mut blocks = Vec::new();
1023 for i in 0..n {
1024 blocks.push(format!("block{i}"));
1025 }
1026 fmtln!(fmt, "blocks: [{}],", blocks.join(", "));
1027 }
1028 }
1029
1030 match format.num_raw_block_operands {
1032 0 => (),
1033 1 => fmt.line("block: block0,"),
1034 _ => panic!("Too many raw block arguments"),
1035 }
1036}
1037
1038fn gen_format_constructor(format: &InstructionFormat, fmt: &mut Formatter) {
1043 let mut args = vec![
1045 "self".to_string(),
1046 "opcode: Opcode".into(),
1047 "ctrl_typevar: Type".into(),
1048 ];
1049
1050 args.extend((0..format.num_raw_block_operands).map(|i| format!("block{i}: ir::Block")));
1052
1053 for f in &format.imm_fields {
1055 args.push(format!("{}: {}", f.member, f.kind.rust_type));
1056 }
1057
1058 args.extend((0..format.num_block_operands).map(|i| format!("block{i}: ir::BlockCall")));
1060
1061 if format.has_value_list {
1063 args.push("args: ir::ValueList".into());
1066 } else {
1067 for i in 0..format.num_value_operands {
1069 args.push(format!("arg{i}: Value"));
1070 }
1071 }
1072
1073 let proto = format!(
1074 "{}({}) -> (Inst, &'f mut ir::DataFlowGraph)",
1075 format.name,
1076 args.join(", ")
1077 );
1078
1079 let imms_need_masking = format
1080 .imm_fields
1081 .iter()
1082 .any(|f| f.kind.rust_type == "ir::immediates::Imm64");
1083
1084 fmt.doc_comment(format.to_string());
1085 fmt.line("#[allow(non_snake_case, reason = \"generated code\")]");
1086 fmt.add_block(&format!("fn {proto}"), |fmt| {
1087 fmt.add_block(&format!(
1089 "let{} data = ir::InstructionData::{}",
1090 if imms_need_masking { " mut" } else { "" },
1091 format.name
1092 ), |fmt| {
1093 fmt.line("opcode,");
1094 gen_member_inits(format, fmt);
1095 });
1096 fmtln!(fmt, ";");
1097
1098 if imms_need_masking {
1099 fmtln!(fmt, "data.mask_immediates(ctrl_typevar);");
1100 }
1101
1102 fmtln!(fmt, "debug_assert_eq!(opcode.format(), InstructionFormat::from(&data), \"Wrong InstructionFormat for Opcode: {{opcode}}\");");
1104
1105 fmt.line("self.build(data, ctrl_typevar)");
1106 });
1107}
1108
1109fn gen_inst_builder(inst: &Instruction, format: &InstructionFormat, fmt: &mut Formatter) {
1114 let mut args = vec![String::new()];
1116
1117 let mut args_doc = Vec::new();
1118 let mut rets_doc = Vec::new();
1119
1120 if let Some(poly) = &inst.polymorphic_info {
1123 if !poly.use_typevar_operand {
1124 args.push(format!("{}: crate::ir::Type", poly.ctrl_typevar.name));
1125 args_doc.push(format!(
1126 "- {} (controlling type variable): {}",
1127 poly.ctrl_typevar.name, poly.ctrl_typevar.doc
1128 ));
1129 }
1130 }
1131
1132 let mut tmpl_types = Vec::new();
1133 let mut into_args = Vec::new();
1134 let mut block_args = Vec::new();
1135 let mut lifetime_param = None;
1136 for op in &inst.operands_in {
1137 if op.kind.is_block() {
1138 args.push(format!("{}_label: {}", op.name, "ir::Block"));
1139 args_doc.push(format!(
1140 "- {}_label: {}",
1141 op.name, "Destination basic block"
1142 ));
1143
1144 let lifetime = *lifetime_param.get_or_insert_with(|| {
1145 tmpl_types.insert(0, "'a".to_string());
1146 "'a"
1147 });
1148 args.push(format!(
1149 "{}_args: impl IntoIterator<Item = &{} BlockArg>",
1150 op.name, lifetime,
1151 ));
1152 args_doc.push(format!("- {}_args: {}", op.name, "Block arguments"));
1153
1154 block_args.push(op);
1155 } else if op.kind.is_raw_block() {
1156 args.push("block: ir::Block".into());
1157 args_doc.push("- block: raw basic block".into());
1158 } else {
1159 let t = if op.is_immediate() {
1160 let t = format!("T{}", tmpl_types.len() + 1);
1161 let api_type = if op.kind.rust_type == "ir::MemFlags" {
1164 "ir::MemFlagsData"
1165 } else {
1166 op.kind.rust_type
1167 };
1168 tmpl_types.push(format!("{t}: Into<{api_type}>"));
1169 into_args.push(op.name);
1170 t
1171 } else {
1172 op.kind.rust_type.to_string()
1173 };
1174 args.push(format!("{}: {}", op.name, t));
1175 args_doc.push(format!("- {}: {}", op.name, op.doc()));
1176 }
1177 }
1178
1179 let has_memflags = inst
1182 .operands_in
1183 .iter()
1184 .any(|op| op.kind.rust_type == "ir::MemFlags");
1185 if format.has_value_list || !block_args.is_empty() || has_memflags {
1186 args[0].push_str("mut self");
1187 } else {
1188 args[0].push_str("self");
1189 }
1190
1191 for op in &inst.operands_out {
1192 rets_doc.push(format!("- {}: {}", op.name, op.doc()));
1193 }
1194
1195 let rtype = match inst.value_results.len() {
1196 0 => "Inst".into(),
1197 1 => "Value".into(),
1198 _ => format!("({})", vec!["Value"; inst.value_results.len()].join(", ")),
1199 };
1200
1201 let tmpl = if !tmpl_types.is_empty() {
1202 format!("<{}>", tmpl_types.join(", "))
1203 } else {
1204 "".into()
1205 };
1206
1207 let proto = format!(
1208 "{}{}({}) -> {}",
1209 inst.snake_name(),
1210 tmpl,
1211 args.join(", "),
1212 rtype
1213 );
1214
1215 fmt.doc_comment(&inst.doc);
1216 if !args_doc.is_empty() {
1217 fmt.line("///");
1218 fmt.doc_comment("Inputs:");
1219 fmt.line("///");
1220 for doc_line in args_doc {
1221 fmt.doc_comment(doc_line);
1222 }
1223 }
1224 if !rets_doc.is_empty() {
1225 fmt.line("///");
1226 fmt.doc_comment("Outputs:");
1227 fmt.line("///");
1228 for doc_line in rets_doc {
1229 fmt.doc_comment(doc_line);
1230 }
1231 }
1232
1233 fmt.line("#[allow(non_snake_case, reason = \"generated code\")]");
1234 fmt.add_block(&format!("fn {proto}"), |fmt| {
1235 for arg in into_args {
1237 fmtln!(fmt, "let {} = {}.into();", arg, arg);
1238 }
1239
1240 for op in &inst.operands_in {
1242 if op.kind.rust_type == "ir::MemFlags" && op.is_immediate() {
1243 fmtln!(
1244 fmt,
1245 "let {0} = self.data_flow_graph_mut().mem_flags.insert({0}).unwrap();",
1246 op.name
1247 );
1248 }
1249 }
1250
1251 for op in block_args {
1253 fmtln!(
1254 fmt,
1255 "let {0} = self.data_flow_graph_mut().block_call({0}_label, {0}_args);",
1256 op.name
1257 );
1258 }
1259
1260 let first_arg = format!("Opcode::{}", inst.camel_name);
1262 let mut args = vec![first_arg.as_str()];
1263 if let Some(poly) = &inst.polymorphic_info {
1264 if poly.use_typevar_operand {
1265 let op_num = inst.value_opnums[format.typevar_operand.unwrap()];
1267 fmtln!(
1268 fmt,
1269 "let ctrl_typevar = self.data_flow_graph().value_type({});",
1270 inst.operands_in[op_num].name
1271 );
1272
1273 args.push("ctrl_typevar");
1275 } else {
1276 args.push(&poly.ctrl_typevar.name);
1278 }
1279 } else {
1280 args.push("types::INVALID");
1282 }
1283
1284 for &op_num in &inst.imm_opnums {
1286 args.push(inst.operands_in[op_num].name);
1287 }
1288
1289 if format.has_value_list {
1291 fmt.line("let mut vlist = ir::ValueList::default();");
1293 args.push("vlist");
1294 fmt.line("{");
1295 fmt.indent(|fmt| {
1296 fmt.line("let pool = &mut self.data_flow_graph_mut().value_lists;");
1297 for op in &inst.operands_in {
1298 if op.is_value() {
1299 fmtln!(fmt, "vlist.push({}, pool);", op.name);
1300 } else if op.is_varargs() {
1301 fmtln!(fmt, "vlist.extend({}.iter().cloned(), pool);", op.name);
1302 }
1303 }
1304 });
1305 fmt.line("}");
1306 } else {
1307 for &op_num in &inst.value_opnums {
1309 args.push(inst.operands_in[op_num].name);
1310 }
1311 }
1312
1313 let fcall = format!("self.{}({})", format.name, args.join(", "));
1315
1316 fmtln!(fmt, "let (inst, dfg) = {};", fcall);
1317 fmtln!(
1318 fmt,
1319 "crate::trace!(\"inserted {{inst:?}}: {{}}\", dfg.display_inst(inst));"
1320 );
1321
1322 if inst.value_results.is_empty() {
1323 fmtln!(fmt, "inst");
1324 return;
1325 }
1326
1327 if inst.value_results.len() == 1 {
1328 fmt.line("dfg.first_result(inst)");
1329 } else {
1330 fmtln!(
1331 fmt,
1332 "let results = &dfg.inst_results(inst)[0..{}];",
1333 inst.value_results.len()
1334 );
1335 fmtln!(
1336 fmt,
1337 "({})",
1338 inst.value_results
1339 .iter()
1340 .enumerate()
1341 .map(|(i, _)| format!("results[{i}]"))
1342 .collect::<Vec<_>>()
1343 .join(", ")
1344 );
1345 }
1346 });
1347}
1348
1349fn gen_builder(
1351 instructions: &AllInstructions,
1352 formats: &[Rc<InstructionFormat>],
1353 fmt: &mut Formatter,
1354) {
1355 fmt.doc_comment(
1356 r#"
1357 Convenience methods for building instructions.
1358
1359 The `InstBuilder` trait has one method per instruction opcode for
1360 conveniently constructing the instruction with minimum arguments.
1361 Polymorphic instructions infer their result types from the input
1362 arguments when possible. In some cases, an explicit `ctrl_typevar`
1363 argument is required.
1364
1365 The opcode methods return the new instruction's result values, or
1366 the `Inst` itself for instructions that don't have any results.
1367
1368 There is also a method per instruction format. These methods all
1369 return an `Inst`.
1370
1371 When an address to a load or store is specified, its integer
1372 size is required to be equal to the platform's pointer width.
1373 "#,
1374 );
1375 fmt.add_block("pub trait InstBuilder<'f>: InstBuilderBase<'f>", |fmt| {
1376 for inst in instructions.iter() {
1377 gen_inst_builder(inst, &inst.format, fmt);
1378 fmt.empty_line();
1379 }
1380 for (i, format) in formats.iter().enumerate() {
1381 gen_format_constructor(format, fmt);
1382 if i + 1 != formats.len() {
1383 fmt.empty_line();
1384 }
1385 }
1386 });
1387}
1388
1389pub(crate) fn generate(
1390 formats: &[Rc<InstructionFormat>],
1391 all_inst: &AllInstructions,
1392 opcode_filename: &str,
1393 inst_builder_filename: &str,
1394 out_dir: &std::path::Path,
1395) -> Result<(), error::Error> {
1396 let mut fmt = Formatter::new(Language::Rust);
1398 gen_formats(&formats, &mut fmt);
1399 gen_instruction_data(&formats, &mut fmt);
1400 fmt.empty_line();
1401 gen_instruction_data_impl(&formats, &mut fmt);
1402 fmt.empty_line();
1403 gen_opcodes(all_inst, &mut fmt);
1404 fmt.empty_line();
1405 gen_type_constraints(all_inst, &mut fmt);
1406 fmt.write(opcode_filename, out_dir)?;
1407
1408 let mut fmt = Formatter::new(Language::Rust);
1410 gen_builder(all_inst, &formats, &mut fmt);
1411 fmt.write(inst_builder_filename, out_dir)?;
1412
1413 Ok(())
1414}