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("std::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(("std::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 OperandKindFields::ImmEnum(_) |
562 OperandKindFields::TypeVar(_) => fmtln!(fmt, "{member},"),
563 }
564 }
565 });
566 });
567 }
568 });
569 });
570 });
571}
572
573fn gen_bool_accessor<T: Fn(&Instruction) -> bool>(
574 all_inst: &AllInstructions,
575 get_attr: T,
576 name: &'static str,
577 doc: &'static str,
578 fmt: &mut Formatter,
579) {
580 fmt.doc_comment(doc);
581 fmt.add_block(&format!("pub fn {name}(self) -> bool"), |fmt| {
582 let mut m = Match::new("self");
583 for inst in all_inst.iter() {
584 if get_attr(inst) {
585 m.arm_no_fields(format!("Self::{}", inst.camel_name), "true");
586 }
587 }
588 m.arm_no_fields("_", "false");
589 fmt.add_match(m);
590 });
591 fmt.empty_line();
592}
593
594fn gen_opcodes(all_inst: &AllInstructions, fmt: &mut Formatter) {
595 fmt.doc_comment(
596 r#"
597 An instruction opcode.
598
599 All instructions from all supported ISAs are present.
600 "#,
601 );
602 fmt.line("#[repr(u8)]");
603 fmt.line("#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]");
604 fmt.line(
605 r#"#[cfg_attr(
606 feature = "enable-serde",
607 derive(serde_derive::Serialize, serde_derive::Deserialize)
608 )]"#,
609 );
610
611 fmt.add_block("pub enum Opcode", |fmt| {
616 let mut is_first_opcode = true;
617 for inst in all_inst.iter() {
618 fmt.doc_comment(format!("`{}`. ({})", inst, inst.format.name));
619
620 if let Some(poly) = &inst.polymorphic_info {
622 if poly.use_typevar_operand {
623 let op_num = inst.value_opnums[inst.format.typevar_operand.unwrap()];
624 fmt.doc_comment(format!(
625 "Type inferred from `{}`.",
626 inst.operands_in[op_num].name
627 ));
628 }
629 }
630
631 if is_first_opcode {
633 fmtln!(fmt, "{} = 1,", inst.camel_name);
634 is_first_opcode = false;
635 } else {
636 fmtln!(fmt, "{},", inst.camel_name)
637 }
638 }
639 });
640 fmt.empty_line();
641
642 fmt.add_block("impl Opcode", |fmt| {
643 gen_bool_accessor(
644 all_inst,
645 |inst| inst.is_terminator,
646 "is_terminator",
647 "True for instructions that terminate the block",
648 fmt,
649 );
650 gen_bool_accessor(
651 all_inst,
652 |inst| inst.is_branch,
653 "is_branch",
654 "True for all branch or jump instructions.",
655 fmt,
656 );
657 gen_bool_accessor(
658 all_inst,
659 |inst| inst.is_call,
660 "is_call",
661 "Is this a call instruction?",
662 fmt,
663 );
664 gen_bool_accessor(
665 all_inst,
666 |inst| inst.is_return,
667 "is_return",
668 "Is this a return instruction?",
669 fmt,
670 );
671 gen_bool_accessor(
672 all_inst,
673 |inst| inst.can_load,
674 "can_load",
675 "Can this instruction read from memory?",
676 fmt,
677 );
678 gen_bool_accessor(
679 all_inst,
680 |inst| inst.can_store,
681 "can_store",
682 "Can this instruction write to memory?",
683 fmt,
684 );
685 gen_bool_accessor(
686 all_inst,
687 |inst| inst.can_trap,
688 "can_trap",
689 "Can this instruction cause a trap?",
690 fmt,
691 );
692 gen_bool_accessor(
693 all_inst,
694 |inst| inst.other_side_effects,
695 "other_side_effects",
696 "Does this instruction have other side effects besides can_* flags?",
697 fmt,
698 );
699 gen_bool_accessor(
700 all_inst,
701 |inst| inst.side_effects_idempotent,
702 "side_effects_idempotent",
703 "Despite having side effects, is this instruction okay to GVN?",
704 fmt,
705 );
706
707 fmt.doc_comment("All cranelift opcodes.");
709 fmt.add_block("pub fn all() -> &'static [Opcode]", |fmt| {
710 fmt.line("return &[");
711 for inst in all_inst {
712 fmt.indent(|fmt| {
713 fmtln!(fmt, "Opcode::{},", inst.camel_name);
714 });
715 }
716 fmt.line("];");
717 });
718 fmt.empty_line();
719 });
720 fmt.empty_line();
721
722 fmtln!(
724 fmt,
725 "const OPCODE_FORMAT: [InstructionFormat; {}] = [",
726 all_inst.len()
727 );
728 fmt.indent(|fmt| {
729 for inst in all_inst.iter() {
730 fmtln!(
731 fmt,
732 "InstructionFormat::{}, // {}",
733 inst.format.name,
734 inst.name
735 );
736 }
737 });
738 fmtln!(fmt, "];");
739 fmt.empty_line();
740
741 fmt.add_block("fn opcode_name(opc: Opcode) -> &\'static str", |fmt| {
743 let mut m = Match::new("opc");
744 for inst in all_inst.iter() {
745 m.arm_no_fields(
746 format!("Opcode::{}", inst.camel_name),
747 format!("\"{}\"", inst.name),
748 );
749 }
750 fmt.add_match(m);
751 });
752 fmt.empty_line();
753
754 let hash_table =
756 crate::constant_hash::generate_table(all_inst.iter(), all_inst.len(), |inst| {
757 constant_hash::simple_hash(&inst.name)
758 });
759 fmtln!(
760 fmt,
761 "const OPCODE_HASH_TABLE: [Option<Opcode>; {}] = [",
762 hash_table.len()
763 );
764 fmt.indent(|fmt| {
765 for i in hash_table {
766 match i {
767 Some(i) => fmtln!(fmt, "Some(Opcode::{}),", i.camel_name),
768 None => fmtln!(fmt, "None,"),
769 }
770 }
771 });
772 fmtln!(fmt, "];");
773 fmt.empty_line();
774}
775
776fn get_constraint<'entries, 'table>(
784 operand: &'entries Operand,
785 ctrl_typevar: Option<&TypeVar>,
786 type_sets: &'table mut UniqueTable<'entries, TypeSet>,
787) -> String {
788 assert!(operand.is_value());
789 let type_var = operand.type_var().unwrap();
790
791 if let Some(typ) = type_var.singleton_type() {
792 return format!("Concrete({})", typ.rust_name());
793 }
794
795 if let Some(free_typevar) = type_var.free_typevar() {
796 if ctrl_typevar.is_some() && free_typevar != *ctrl_typevar.unwrap() {
797 assert!(type_var.base.is_none());
798 return format!("Free({})", type_sets.add(type_var.get_raw_typeset()));
799 }
800 }
801
802 if let Some(base) = &type_var.base {
803 assert!(base.type_var == *ctrl_typevar.unwrap());
804 return camel_case(base.derived_func.name());
805 }
806
807 assert!(type_var == ctrl_typevar.unwrap());
808 "Same".into()
809}
810
811fn gen_bitset<'a, T: IntoIterator<Item = &'a u16>>(
812 iterable: T,
813 name: &'static str,
814 field_size: u8,
815 fmt: &mut Formatter,
816) {
817 let bits = iterable.into_iter().fold(0, |acc, x| {
818 assert!(x.is_power_of_two());
819 assert!(u32::from(*x) < (1 << u32::from(field_size)));
820 acc | x
821 });
822 fmtln!(fmt, "{}: ScalarBitSet::<u{}>({}),", name, field_size, bits);
823}
824
825fn iterable_to_string<I: fmt::Display, T: IntoIterator<Item = I>>(iterable: T) -> String {
826 let elems = iterable
827 .into_iter()
828 .map(|x| x.to_string())
829 .collect::<Vec<_>>()
830 .join(", ");
831 format!("{{{elems}}}")
832}
833
834fn typeset_to_string(ts: &TypeSet) -> String {
835 let mut result = format!("TypeSet(lanes={}", iterable_to_string(&ts.lanes));
836 if !ts.ints.is_empty() {
837 result += &format!(", ints={}", iterable_to_string(&ts.ints));
838 }
839 if !ts.floats.is_empty() {
840 result += &format!(", floats={}", iterable_to_string(&ts.floats));
841 }
842 result += ")";
843 result
844}
845
846pub(crate) fn gen_typesets_table(type_sets: &UniqueTable<TypeSet>, fmt: &mut Formatter) {
848 if type_sets.len() == 0 {
849 return;
850 }
851
852 fmt.comment("Table of value type sets.");
853 assert!(type_sets.len() <= TYPESET_LIMIT, "Too many type sets!");
854 fmtln!(
855 fmt,
856 "const TYPE_SETS: [ir::instructions::ValueTypeSet; {}] = [",
857 type_sets.len()
858 );
859 fmt.indent(|fmt| {
860 for ts in type_sets.iter() {
861 fmt.add_block("ir::instructions::ValueTypeSet", |fmt| {
862 fmt.comment(typeset_to_string(ts));
863 gen_bitset(&ts.lanes, "lanes", 16, fmt);
864 gen_bitset(&ts.dynamic_lanes, "dynamic_lanes", 16, fmt);
865 gen_bitset(&ts.ints, "ints", 8, fmt);
866 gen_bitset(&ts.floats, "floats", 8, fmt);
867 });
868 fmt.line(",");
869 }
870 });
871 fmtln!(fmt, "];");
872}
873
874fn gen_type_constraints(all_inst: &AllInstructions, fmt: &mut Formatter) {
879 let mut type_sets = UniqueTable::new();
881
882 let mut operand_seqs = UniqueSeqTable::new();
888
889 operand_seqs.add(&vec!["Same".to_string(); 3]);
891
892 fmt.comment("Table of opcode constraints.");
893 fmtln!(
894 fmt,
895 "const OPCODE_CONSTRAINTS: [OpcodeConstraints; {}] = [",
896 all_inst.len()
897 );
898 fmt.indent(|fmt| {
899 for inst in all_inst.iter() {
900 let (ctrl_typevar, ctrl_typeset) = if let Some(poly) = &inst.polymorphic_info {
901 let index = type_sets.add(poly.ctrl_typevar.get_raw_typeset());
902 (Some(&poly.ctrl_typevar), index)
903 } else {
904 (None, TYPESET_LIMIT)
905 };
906
907 let mut constraints = Vec::new();
910 for &index in &inst.value_results {
911 constraints.push(get_constraint(&inst.operands_out[index], ctrl_typevar, &mut type_sets));
912 }
913 for &index in &inst.value_opnums {
914 constraints.push(get_constraint(&inst.operands_in[index], ctrl_typevar, &mut type_sets));
915 }
916
917 let constraint_offset = operand_seqs.add(&constraints);
918
919 let fixed_results = inst.value_results.len();
920 let fixed_values = inst.value_opnums.len();
921
922 let use_typevar_operand = if let Some(poly) = &inst.polymorphic_info {
924 poly.use_typevar_operand
925 } else {
926 false
927 };
928
929 let use_result = fixed_results > 0 && inst.operands_out[inst.value_results[0]].type_var() == ctrl_typevar;
931
932 let requires_typevar_operand = use_typevar_operand && !use_result;
934
935 fmt.comment(
936 format!("{}: fixed_results={}, use_typevar_operand={}, requires_typevar_operand={}, fixed_values={}",
937 inst.camel_name,
938 fixed_results,
939 use_typevar_operand,
940 requires_typevar_operand,
941 fixed_values)
942 );
943 fmt.comment(format!("Constraints=[{}]", constraints
944 .iter()
945 .map(|x| format!("'{x}'"))
946 .collect::<Vec<_>>()
947 .join(", ")));
948 if let Some(poly) = &inst.polymorphic_info {
949 fmt.comment(format!("Polymorphic over {}", typeset_to_string(poly.ctrl_typevar.get_raw_typeset())));
950 }
951
952 assert!(fixed_results < 8 && fixed_values < 8, "Bit field encoding too tight");
954 let mut flags = fixed_results; if use_typevar_operand {
956 flags |= 1<<3; }
958 if requires_typevar_operand {
959 flags |= 1<<4; }
961 flags |= fixed_values << 5; fmt.add_block("OpcodeConstraints",|fmt| {
964 fmtln!(fmt, "flags: {:#04x},", flags);
965 fmtln!(fmt, "typeset_offset: {},", ctrl_typeset);
966 fmtln!(fmt, "constraint_offset: {},", constraint_offset);
967 });
968 fmt.line(",");
969 }
970 });
971 fmtln!(fmt, "];");
972 fmt.empty_line();
973
974 gen_typesets_table(&type_sets, fmt);
975 fmt.empty_line();
976
977 fmt.comment("Table of operand constraint sequences.");
978 fmtln!(
979 fmt,
980 "const OPERAND_CONSTRAINTS: [OperandConstraint; {}] = [",
981 operand_seqs.len()
982 );
983 fmt.indent(|fmt| {
984 for constraint in operand_seqs.iter() {
985 fmtln!(fmt, "OperandConstraint::{},", constraint);
986 }
987 });
988 fmtln!(fmt, "];");
989}
990
991fn gen_member_inits(format: &InstructionFormat, fmt: &mut Formatter) {
993 for f in &format.imm_fields {
996 fmtln!(fmt, "{},", f.member);
997 }
998
999 if format.has_value_list {
1001 fmt.line("args,");
1002 } else if format.num_value_operands == 1 {
1003 fmt.line("arg: arg0,");
1004 } else if format.num_value_operands > 1 {
1005 let mut args = Vec::new();
1006 for i in 0..format.num_value_operands {
1007 args.push(format!("arg{i}"));
1008 }
1009 fmtln!(fmt, "args: [{}],", args.join(", "));
1010 }
1011
1012 match format.num_block_operands {
1014 0 => (),
1015 1 => fmt.line("destination: block0"),
1016 n => {
1017 let mut blocks = Vec::new();
1018 for i in 0..n {
1019 blocks.push(format!("block{i}"));
1020 }
1021 fmtln!(fmt, "blocks: [{}],", blocks.join(", "));
1022 }
1023 }
1024
1025 match format.num_raw_block_operands {
1027 0 => (),
1028 1 => fmt.line("block: block0,"),
1029 _ => panic!("Too many raw block arguments"),
1030 }
1031}
1032
1033fn gen_format_constructor(format: &InstructionFormat, fmt: &mut Formatter) {
1038 let mut args = vec![
1040 "self".to_string(),
1041 "opcode: Opcode".into(),
1042 "ctrl_typevar: Type".into(),
1043 ];
1044
1045 args.extend((0..format.num_raw_block_operands).map(|i| format!("block{i}: ir::Block")));
1047
1048 for f in &format.imm_fields {
1050 args.push(format!("{}: {}", f.member, f.kind.rust_type));
1051 }
1052
1053 args.extend((0..format.num_block_operands).map(|i| format!("block{i}: ir::BlockCall")));
1055
1056 if format.has_value_list {
1058 args.push("args: ir::ValueList".into());
1061 } else {
1062 for i in 0..format.num_value_operands {
1064 args.push(format!("arg{i}: Value"));
1065 }
1066 }
1067
1068 let proto = format!(
1069 "{}({}) -> (Inst, &'f mut ir::DataFlowGraph)",
1070 format.name,
1071 args.join(", ")
1072 );
1073
1074 let imms_need_masking = format
1075 .imm_fields
1076 .iter()
1077 .any(|f| f.kind.rust_type == "ir::immediates::Imm64");
1078
1079 fmt.doc_comment(format.to_string());
1080 fmt.line("#[allow(non_snake_case, reason = \"generated code\")]");
1081 fmt.add_block(&format!("fn {proto}"), |fmt| {
1082 fmt.add_block(&format!(
1084 "let{} data = ir::InstructionData::{}",
1085 if imms_need_masking { " mut" } else { "" },
1086 format.name
1087 ), |fmt| {
1088 fmt.line("opcode,");
1089 gen_member_inits(format, fmt);
1090 });
1091 fmtln!(fmt, ";");
1092
1093 if imms_need_masking {
1094 fmtln!(fmt, "data.mask_immediates(ctrl_typevar);");
1095 }
1096
1097 fmtln!(fmt, "debug_assert_eq!(opcode.format(), InstructionFormat::from(&data), \"Wrong InstructionFormat for Opcode: {{opcode}}\");");
1099
1100 fmt.line("self.build(data, ctrl_typevar)");
1101 });
1102}
1103
1104fn gen_inst_builder(inst: &Instruction, format: &InstructionFormat, fmt: &mut Formatter) {
1109 let mut args = vec![String::new()];
1111
1112 let mut args_doc = Vec::new();
1113 let mut rets_doc = Vec::new();
1114
1115 if let Some(poly) = &inst.polymorphic_info {
1118 if !poly.use_typevar_operand {
1119 args.push(format!("{}: crate::ir::Type", poly.ctrl_typevar.name));
1120 args_doc.push(format!(
1121 "- {} (controlling type variable): {}",
1122 poly.ctrl_typevar.name, poly.ctrl_typevar.doc
1123 ));
1124 }
1125 }
1126
1127 let mut tmpl_types = Vec::new();
1128 let mut into_args = Vec::new();
1129 let mut block_args = Vec::new();
1130 let mut lifetime_param = None;
1131 for op in &inst.operands_in {
1132 if op.kind.is_block() {
1133 args.push(format!("{}_label: {}", op.name, "ir::Block"));
1134 args_doc.push(format!(
1135 "- {}_label: {}",
1136 op.name, "Destination basic block"
1137 ));
1138
1139 let lifetime = *lifetime_param.get_or_insert_with(|| {
1140 tmpl_types.insert(0, "'a".to_string());
1141 "'a"
1142 });
1143 args.push(format!(
1144 "{}_args: impl IntoIterator<Item = &{} BlockArg>",
1145 op.name, lifetime,
1146 ));
1147 args_doc.push(format!("- {}_args: {}", op.name, "Block arguments"));
1148
1149 block_args.push(op);
1150 } else if op.kind.is_raw_block() {
1151 args.push("block: ir::Block".into());
1152 args_doc.push("- block: raw basic block".into());
1153 } else {
1154 let t = if op.is_immediate() {
1155 let t = format!("T{}", tmpl_types.len() + 1);
1156 tmpl_types.push(format!("{}: Into<{}>", t, op.kind.rust_type));
1157 into_args.push(op.name);
1158 t
1159 } else {
1160 op.kind.rust_type.to_string()
1161 };
1162 args.push(format!("{}: {}", op.name, t));
1163 args_doc.push(format!("- {}: {}", op.name, op.doc()));
1164 }
1165 }
1166
1167 if format.has_value_list || !block_args.is_empty() {
1170 args[0].push_str("mut self");
1171 } else {
1172 args[0].push_str("self");
1173 }
1174
1175 for op in &inst.operands_out {
1176 rets_doc.push(format!("- {}: {}", op.name, op.doc()));
1177 }
1178
1179 let rtype = match inst.value_results.len() {
1180 0 => "Inst".into(),
1181 1 => "Value".into(),
1182 _ => format!("({})", vec!["Value"; inst.value_results.len()].join(", ")),
1183 };
1184
1185 let tmpl = if !tmpl_types.is_empty() {
1186 format!("<{}>", tmpl_types.join(", "))
1187 } else {
1188 "".into()
1189 };
1190
1191 let proto = format!(
1192 "{}{}({}) -> {}",
1193 inst.snake_name(),
1194 tmpl,
1195 args.join(", "),
1196 rtype
1197 );
1198
1199 fmt.doc_comment(&inst.doc);
1200 if !args_doc.is_empty() {
1201 fmt.line("///");
1202 fmt.doc_comment("Inputs:");
1203 fmt.line("///");
1204 for doc_line in args_doc {
1205 fmt.doc_comment(doc_line);
1206 }
1207 }
1208 if !rets_doc.is_empty() {
1209 fmt.line("///");
1210 fmt.doc_comment("Outputs:");
1211 fmt.line("///");
1212 for doc_line in rets_doc {
1213 fmt.doc_comment(doc_line);
1214 }
1215 }
1216
1217 fmt.line("#[allow(non_snake_case, reason = \"generated code\")]");
1218 fmt.add_block(&format!("fn {proto}"), |fmt| {
1219 for arg in into_args {
1221 fmtln!(fmt, "let {} = {}.into();", arg, arg);
1222 }
1223
1224 for op in block_args {
1226 fmtln!(
1227 fmt,
1228 "let {0} = self.data_flow_graph_mut().block_call({0}_label, {0}_args);",
1229 op.name
1230 );
1231 }
1232
1233 let first_arg = format!("Opcode::{}", inst.camel_name);
1235 let mut args = vec![first_arg.as_str()];
1236 if let Some(poly) = &inst.polymorphic_info {
1237 if poly.use_typevar_operand {
1238 let op_num = inst.value_opnums[format.typevar_operand.unwrap()];
1240 fmtln!(
1241 fmt,
1242 "let ctrl_typevar = self.data_flow_graph().value_type({});",
1243 inst.operands_in[op_num].name
1244 );
1245
1246 args.push("ctrl_typevar");
1248 } else {
1249 args.push(&poly.ctrl_typevar.name);
1251 }
1252 } else {
1253 args.push("types::INVALID");
1255 }
1256
1257 for &op_num in &inst.imm_opnums {
1259 args.push(inst.operands_in[op_num].name);
1260 }
1261
1262 if format.has_value_list {
1264 fmt.line("let mut vlist = ir::ValueList::default();");
1266 args.push("vlist");
1267 fmt.line("{");
1268 fmt.indent(|fmt| {
1269 fmt.line("let pool = &mut self.data_flow_graph_mut().value_lists;");
1270 for op in &inst.operands_in {
1271 if op.is_value() {
1272 fmtln!(fmt, "vlist.push({}, pool);", op.name);
1273 } else if op.is_varargs() {
1274 fmtln!(fmt, "vlist.extend({}.iter().cloned(), pool);", op.name);
1275 }
1276 }
1277 });
1278 fmt.line("}");
1279 } else {
1280 for &op_num in &inst.value_opnums {
1282 args.push(inst.operands_in[op_num].name);
1283 }
1284 }
1285
1286 let fcall = format!("self.{}({})", format.name, args.join(", "));
1288
1289 fmtln!(fmt, "let (inst, dfg) = {};", fcall);
1290 fmtln!(
1291 fmt,
1292 "crate::trace!(\"inserted {{inst:?}}: {{}}\", dfg.display_inst(inst));"
1293 );
1294
1295 if inst.value_results.is_empty() {
1296 fmtln!(fmt, "inst");
1297 return;
1298 }
1299
1300 if inst.value_results.len() == 1 {
1301 fmt.line("dfg.first_result(inst)");
1302 } else {
1303 fmtln!(
1304 fmt,
1305 "let results = &dfg.inst_results(inst)[0..{}];",
1306 inst.value_results.len()
1307 );
1308 fmtln!(
1309 fmt,
1310 "({})",
1311 inst.value_results
1312 .iter()
1313 .enumerate()
1314 .map(|(i, _)| format!("results[{i}]"))
1315 .collect::<Vec<_>>()
1316 .join(", ")
1317 );
1318 }
1319 });
1320}
1321
1322fn gen_builder(
1324 instructions: &AllInstructions,
1325 formats: &[Rc<InstructionFormat>],
1326 fmt: &mut Formatter,
1327) {
1328 fmt.doc_comment(
1329 r#"
1330 Convenience methods for building instructions.
1331
1332 The `InstBuilder` trait has one method per instruction opcode for
1333 conveniently constructing the instruction with minimum arguments.
1334 Polymorphic instructions infer their result types from the input
1335 arguments when possible. In some cases, an explicit `ctrl_typevar`
1336 argument is required.
1337
1338 The opcode methods return the new instruction's result values, or
1339 the `Inst` itself for instructions that don't have any results.
1340
1341 There is also a method per instruction format. These methods all
1342 return an `Inst`.
1343
1344 When an address to a load or store is specified, its integer
1345 size is required to be equal to the platform's pointer width.
1346 "#,
1347 );
1348 fmt.add_block("pub trait InstBuilder<'f>: InstBuilderBase<'f>", |fmt| {
1349 for inst in instructions.iter() {
1350 gen_inst_builder(inst, &inst.format, fmt);
1351 fmt.empty_line();
1352 }
1353 for (i, format) in formats.iter().enumerate() {
1354 gen_format_constructor(format, fmt);
1355 if i + 1 != formats.len() {
1356 fmt.empty_line();
1357 }
1358 }
1359 });
1360}
1361
1362pub(crate) fn generate(
1363 formats: &[Rc<InstructionFormat>],
1364 all_inst: &AllInstructions,
1365 opcode_filename: &str,
1366 inst_builder_filename: &str,
1367 out_dir: &std::path::Path,
1368) -> Result<(), error::Error> {
1369 let mut fmt = Formatter::new(Language::Rust);
1371 gen_formats(&formats, &mut fmt);
1372 gen_instruction_data(&formats, &mut fmt);
1373 fmt.empty_line();
1374 gen_instruction_data_impl(&formats, &mut fmt);
1375 fmt.empty_line();
1376 gen_opcodes(all_inst, &mut fmt);
1377 fmt.empty_line();
1378 gen_type_constraints(all_inst, &mut fmt);
1379 fmt.write(opcode_filename, out_dir)?;
1380
1381 let mut fmt = Formatter::new(Language::Rust);
1383 gen_builder(all_inst, &formats, &mut fmt);
1384 fmt.write(inst_builder_filename, out_dir)?;
1385
1386 Ok(())
1387}