1use crate::cdsl::camel_case;
4use crate::cdsl::formats::InstructionFormat;
5use crate::cdsl::instructions::{AllInstructions, Instruction};
6use crate::cdsl::operands::Operand;
7use crate::cdsl::typevar::{TypeSet, TypeVar};
8use crate::unique_table::{UniqueSeqTable, UniqueTable};
9use cranelift_codegen_shared::constant_hash;
10use cranelift_srcgen::{error, fmtln, Formatter, Language, Match};
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)]");
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 for field in &format.imm_fields {
91 fmtln!(fmt, "{}: {},", field.member, field.kind.rust_type);
92 }
93 });
94 fmtln!(fmt, ",");
95 }
96 });
97}
98
99fn gen_arguments_method(formats: &[Rc<InstructionFormat>], fmt: &mut Formatter, is_mut: bool) {
100 let (method, mut_, rslice, as_slice) = if is_mut {
101 (
102 "arguments_mut",
103 "mut ",
104 "core::slice::from_mut",
105 "as_mut_slice",
106 )
107 } else {
108 ("arguments", "", "core::slice::from_ref", "as_slice")
109 };
110
111 fmt.add_block(&format!(
112 "pub fn {method}<'a>(&'a {mut_}self, pool: &'a {mut_}ir::ValueListPool) -> &'a {mut_}[Value]"),
113
114 |fmt| {
115 let mut m = Match::new("*self");
116 for format in formats {
117 let name = format!("Self::{}", format.name);
118
119 if format.has_value_list {
123 m.arm(
124 name,
125 vec![format!("ref {}args", mut_), "..".to_string()],
126 format!("args.{as_slice}(pool)"),
127 );
128 continue;
129 }
130
131 let mut fields = Vec::new();
133 let arg = if format.num_value_operands == 0 {
134 format!("&{mut_}[]")
135 } else if format.num_value_operands == 1 {
136 fields.push(format!("ref {mut_}arg"));
137 format!("{rslice}(arg)")
138 } else {
139 let arg = format!("args_arity{}", format.num_value_operands);
140 fields.push(format!("args: ref {mut_}{arg}"));
141 arg
142 };
143 fields.push("..".into());
144
145 m.arm(name, fields, arg);
146 }
147 fmt.add_match(m);
148 });
149}
150
151fn gen_instruction_data_impl(formats: &[Rc<InstructionFormat>], fmt: &mut Formatter) {
162 fmt.add_block("impl InstructionData",|fmt| {
163 fmt.doc_comment("Get the opcode of this instruction.");
164 fmt.add_block("pub fn opcode(&self) -> Opcode",|fmt| {
165 let mut m = Match::new("*self");
166 for format in formats {
167 m.arm(format!("Self::{}", format.name), vec!["opcode", ".."],
168 "opcode".to_string());
169 }
170 fmt.add_match(m);
171 });
172 fmt.empty_line();
173
174 fmt.doc_comment("Get the controlling type variable operand.");
175 fmt.add_block("pub fn typevar_operand(&self, pool: &ir::ValueListPool) -> Option<Value>",|fmt| {
176 let mut m = Match::new("*self");
177 for format in formats {
178 let name = format!("Self::{}", format.name);
179 if format.typevar_operand.is_none() {
180 m.arm(name, vec![".."], "None".to_string());
181 } else if format.has_value_list {
182 m.arm(name, vec!["ref args", ".."], format!("args.get({}, pool)", format.typevar_operand.unwrap()));
184 } else if format.num_value_operands == 1 {
185 m.arm(name, vec!["arg", ".."], "Some(arg)".to_string());
186 } else {
187 let args = format!("args_arity{}", format.num_value_operands);
190 m.arm(name, vec![format!("args: ref {}", args), "..".to_string()],
191 format!("Some({}[{}])", args, format.typevar_operand.unwrap()));
192 }
193 }
194 fmt.add_match(m);
195 });
196 fmt.empty_line();
197
198 fmt.doc_comment("Get the value arguments to this instruction.");
199 gen_arguments_method(formats, fmt, false);
200 fmt.empty_line();
201
202 fmt.doc_comment(r#"Get mutable references to the value arguments to this
203 instruction."#);
204 gen_arguments_method(formats, fmt, true);
205 fmt.empty_line();
206
207 fmt.doc_comment(r#"
208 Compare two `InstructionData` for equality.
209
210 This operation requires a reference to a `ValueListPool` to
211 determine if the contents of any `ValueLists` are equal.
212
213 This operation takes a closure that is allowed to map each
214 argument value to some other value before the instructions
215 are compared. This allows various forms of canonicalization.
216 "#);
217 fmt.add_block("pub fn eq<F: Fn(Value) -> Value>(&self, other: &Self, pool: &ir::ValueListPool, mapper: F) -> bool",|fmt| {
218 fmt.add_block("if ::core::mem::discriminant(self) != ::core::mem::discriminant(other)",|fmt| {
219 fmt.line("return false;");
220 });
221
222 fmt.add_block("match (self, other)",|fmt| {
223 for format in formats {
224 let name = format!("&Self::{}", format.name);
225 let mut members = vec!["opcode"];
226
227 let args_eq = if format.has_value_list {
228 members.push("args");
229 Some("args1.as_slice(pool).iter().zip(args2.as_slice(pool).iter()).all(|(a, b)| mapper(*a) == mapper(*b))")
230 } else if format.num_value_operands == 1 {
231 members.push("arg");
232 Some("mapper(*arg1) == mapper(*arg2)")
233 } else if format.num_value_operands > 0 {
234 members.push("args");
235 Some("args1.iter().zip(args2.iter()).all(|(a, b)| mapper(*a) == mapper(*b))")
236 } else {
237 None
238 };
239
240 let blocks_eq = match format.num_block_operands {
241 0 => None,
242 1 => {
243 members.push("destination");
244 Some("destination1 == destination2")
245 },
246 _ => {
247 members.push("blocks");
248 Some("blocks1.iter().zip(blocks2.iter()).all(|(a, b)| a.block(pool) == b.block(pool))")
249 }
250 };
251
252 for field in &format.imm_fields {
253 members.push(field.member);
254 }
255
256 let pat1 = members.iter().map(|x| format!("{x}: ref {x}1")).collect::<Vec<_>>().join(", ");
257 let pat2 = members.iter().map(|x| format!("{x}: ref {x}2")).collect::<Vec<_>>().join(", ");
258 fmt.add_block(&format!("({name} {{ {pat1} }}, {name} {{ {pat2} }}) => "), |fmt| {
259 fmt.line("opcode1 == opcode2");
260 for field in &format.imm_fields {
261 fmtln!(fmt, "&& {}1 == {}2", field.member, field.member);
262 }
263 if let Some(args_eq) = args_eq {
264 fmtln!(fmt, "&& {}", args_eq);
265 }
266 if let Some(blocks_eq) = blocks_eq {
267 fmtln!(fmt, "&& {}", blocks_eq);
268 }
269 });
270 }
271 fmt.line("_ => unreachable!()");
272 });
273 });
274 fmt.empty_line();
275
276 fmt.doc_comment(r#"
277 Hash an `InstructionData`.
278
279 This operation requires a reference to a `ValueListPool` to
280 hash the contents of any `ValueLists`.
281
282 This operation takes a closure that is allowed to map each
283 argument value to some other value before it is hashed. This
284 allows various forms of canonicalization.
285 "#);
286 fmt.add_block("pub fn hash<H: ::core::hash::Hasher, F: Fn(Value) -> Value>(&self, state: &mut H, pool: &ir::ValueListPool, mapper: F)",|fmt| {
287 fmt.add_block("match *self",|fmt| {
288 for format in formats {
289 let name = format!("Self::{}", format.name);
290 let mut members = vec!["opcode"];
291
292 let (args, len) = if format.has_value_list {
293 members.push("ref args");
294 ("args.as_slice(pool)", "args.len(pool)")
295 } else if format.num_value_operands == 1 {
296 members.push("ref arg");
297 ("std::slice::from_ref(arg)", "1")
298 } else if format.num_value_operands > 0 {
299 members.push("ref args");
300 ("args", "args.len()")
301 } else {
302 ("&[]", "0")
303 };
304
305 let blocks = match format.num_block_operands {
306 0 => None,
307 1 => {
308 members.push("ref destination");
309 Some(("std::slice::from_ref(destination)", "1"))
310 }
311 _ => {
312 members.push("ref blocks");
313 Some(("blocks", "blocks.len()"))
314 }
315 };
316
317 for field in &format.imm_fields {
318 members.push(field.member);
319 }
320 let members = members.join(", ");
321
322 fmt.add_block(&format!("{name}{{{members}}} => "), |fmt| {
323 fmt.line("::core::hash::Hash::hash( &::core::mem::discriminant(self), state);");
324 fmt.line("::core::hash::Hash::hash(&opcode, state);");
325 for field in &format.imm_fields {
326 fmtln!(fmt, "::core::hash::Hash::hash(&{}, state);", field.member);
327 }
328 fmtln!(fmt, "::core::hash::Hash::hash(&{}, state);", len);
329 fmt.add_block(&format!("for &arg in {args}"), |fmt| {
330 fmtln!(fmt, "let arg = mapper(arg);");
331 fmtln!(fmt, "::core::hash::Hash::hash(&arg, state);");
332 });
333
334 if let Some((blocks, len)) = blocks {
335 fmtln!(fmt, "::core::hash::Hash::hash(&{len}, state);");
336 fmt.add_block(&format!("for &block in {blocks}"), |fmt| {
337 fmtln!(fmt, "::core::hash::Hash::hash(&block.block(pool), state);");
338 fmt.add_block("for &arg in block.args_slice(pool)", |fmt| {
339 fmtln!(fmt, "let arg = mapper(arg);");
340 fmtln!(fmt, "::core::hash::Hash::hash(&arg, state);");
341 });
342 });
343 }
344 });
345 }
346 });
347 });
348
349 fmt.empty_line();
350
351 fmt.doc_comment(r#"
352 Deep-clone an `InstructionData`, including any referenced lists.
353
354 This operation requires a reference to a `ValueListPool` to
355 clone the `ValueLists`.
356 "#);
357 fmt.add_block("pub fn deep_clone(&self, pool: &mut ir::ValueListPool) -> Self",|fmt| {
358 fmt.add_block("match *self",|fmt| {
359 for format in formats {
360 let name = format!("Self::{}", format.name);
361 let mut members = vec!["opcode"];
362
363 if format.has_value_list {
364 members.push("ref args");
365 } else if format.num_value_operands == 1 {
366 members.push("arg");
367 } else if format.num_value_operands > 0 {
368 members.push("args");
369 }
370
371 match format.num_block_operands {
372 0 => {}
373 1 => {
374 members.push("destination");
375 }
376 _ => {
377 members.push("blocks");
378 }
379 };
380
381 for field in &format.imm_fields {
382 members.push(field.member);
383 }
384 let members = members.join(", ");
385
386 fmt.add_block(&format!("{name}{{{members}}} => "),|fmt| {
387 fmt.add_block(&format!("Self::{}", format.name), |fmt| {
388 fmtln!(fmt, "opcode,");
389
390 if format.has_value_list {
391 fmtln!(fmt, "args: args.deep_clone(pool),");
392 } else if format.num_value_operands == 1 {
393 fmtln!(fmt, "arg,");
394 } else if format.num_value_operands > 0 {
395 fmtln!(fmt, "args,");
396 }
397
398 match format.num_block_operands {
399 0 => {}
400 1 => {
401 fmtln!(fmt, "destination: destination.deep_clone(pool),");
402 }
403 2 => {
404 fmtln!(fmt, "blocks: [blocks[0].deep_clone(pool), blocks[1].deep_clone(pool)],");
405 }
406 _ => panic!("Too many block targets in instruction"),
407 }
408
409 for field in &format.imm_fields {
410 fmtln!(fmt, "{},", field.member);
411 }
412 });
413 });
414 }
415 });
416 });
417 });
418}
419
420fn gen_bool_accessor<T: Fn(&Instruction) -> bool>(
421 all_inst: &AllInstructions,
422 get_attr: T,
423 name: &'static str,
424 doc: &'static str,
425 fmt: &mut Formatter,
426) {
427 fmt.doc_comment(doc);
428 fmt.add_block(&format!("pub fn {name}(self) -> bool"), |fmt| {
429 let mut m = Match::new("self");
430 for inst in all_inst.iter() {
431 if get_attr(inst) {
432 m.arm_no_fields(format!("Self::{}", inst.camel_name), "true");
433 }
434 }
435 m.arm_no_fields("_", "false");
436 fmt.add_match(m);
437 });
438 fmt.empty_line();
439}
440
441fn gen_opcodes(all_inst: &AllInstructions, fmt: &mut Formatter) {
442 fmt.doc_comment(
443 r#"
444 An instruction opcode.
445
446 All instructions from all supported ISAs are present.
447 "#,
448 );
449 fmt.line("#[repr(u8)]");
450 fmt.line("#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]");
451 fmt.line(
452 r#"#[cfg_attr(
453 feature = "enable-serde",
454 derive(serde_derive::Serialize, serde_derive::Deserialize)
455 )]"#,
456 );
457
458 fmt.add_block("pub enum Opcode", |fmt| {
463 let mut is_first_opcode = true;
464 for inst in all_inst.iter() {
465 fmt.doc_comment(format!("`{}`. ({})", inst, inst.format.name));
466
467 if let Some(poly) = &inst.polymorphic_info {
469 if poly.use_typevar_operand {
470 let op_num = inst.value_opnums[inst.format.typevar_operand.unwrap()];
471 fmt.doc_comment(format!(
472 "Type inferred from `{}`.",
473 inst.operands_in[op_num].name
474 ));
475 }
476 }
477
478 if is_first_opcode {
480 fmtln!(fmt, "{} = 1,", inst.camel_name);
481 is_first_opcode = false;
482 } else {
483 fmtln!(fmt, "{},", inst.camel_name)
484 }
485 }
486 });
487 fmt.empty_line();
488
489 fmt.add_block("impl Opcode", |fmt| {
490 gen_bool_accessor(
491 all_inst,
492 |inst| inst.is_terminator,
493 "is_terminator",
494 "True for instructions that terminate the block",
495 fmt,
496 );
497 gen_bool_accessor(
498 all_inst,
499 |inst| inst.is_branch,
500 "is_branch",
501 "True for all branch or jump instructions.",
502 fmt,
503 );
504 gen_bool_accessor(
505 all_inst,
506 |inst| inst.is_call,
507 "is_call",
508 "Is this a call instruction?",
509 fmt,
510 );
511 gen_bool_accessor(
512 all_inst,
513 |inst| inst.is_return,
514 "is_return",
515 "Is this a return instruction?",
516 fmt,
517 );
518 gen_bool_accessor(
519 all_inst,
520 |inst| inst.can_load,
521 "can_load",
522 "Can this instruction read from memory?",
523 fmt,
524 );
525 gen_bool_accessor(
526 all_inst,
527 |inst| inst.can_store,
528 "can_store",
529 "Can this instruction write to memory?",
530 fmt,
531 );
532 gen_bool_accessor(
533 all_inst,
534 |inst| inst.can_trap,
535 "can_trap",
536 "Can this instruction cause a trap?",
537 fmt,
538 );
539 gen_bool_accessor(
540 all_inst,
541 |inst| inst.other_side_effects,
542 "other_side_effects",
543 "Does this instruction have other side effects besides can_* flags?",
544 fmt,
545 );
546 gen_bool_accessor(
547 all_inst,
548 |inst| inst.side_effects_idempotent,
549 "side_effects_idempotent",
550 "Despite having side effects, is this instruction okay to GVN?",
551 fmt,
552 );
553
554 fmt.doc_comment("All cranelift opcodes.");
556 fmt.add_block("pub fn all() -> &'static [Opcode]", |fmt| {
557 fmt.line("return &[");
558 for inst in all_inst {
559 fmt.indent(|fmt| {
560 fmtln!(fmt, "Opcode::{},", inst.camel_name);
561 });
562 }
563 fmt.line("];");
564 });
565 fmt.empty_line();
566 });
567 fmt.empty_line();
568
569 fmtln!(
571 fmt,
572 "const OPCODE_FORMAT: [InstructionFormat; {}] = [",
573 all_inst.len()
574 );
575 fmt.indent(|fmt| {
576 for inst in all_inst.iter() {
577 fmtln!(
578 fmt,
579 "InstructionFormat::{}, // {}",
580 inst.format.name,
581 inst.name
582 );
583 }
584 });
585 fmtln!(fmt, "];");
586 fmt.empty_line();
587
588 fmt.add_block("fn opcode_name(opc: Opcode) -> &\'static str", |fmt| {
590 let mut m = Match::new("opc");
591 for inst in all_inst.iter() {
592 m.arm_no_fields(
593 format!("Opcode::{}", inst.camel_name),
594 format!("\"{}\"", inst.name),
595 );
596 }
597 fmt.add_match(m);
598 });
599 fmt.empty_line();
600
601 let hash_table =
603 crate::constant_hash::generate_table(all_inst.iter(), all_inst.len(), |inst| {
604 constant_hash::simple_hash(&inst.name)
605 });
606 fmtln!(
607 fmt,
608 "const OPCODE_HASH_TABLE: [Option<Opcode>; {}] = [",
609 hash_table.len()
610 );
611 fmt.indent(|fmt| {
612 for i in hash_table {
613 match i {
614 Some(i) => fmtln!(fmt, "Some(Opcode::{}),", i.camel_name),
615 None => fmtln!(fmt, "None,"),
616 }
617 }
618 });
619 fmtln!(fmt, "];");
620 fmt.empty_line();
621}
622
623fn get_constraint<'entries, 'table>(
631 operand: &'entries Operand,
632 ctrl_typevar: Option<&TypeVar>,
633 type_sets: &'table mut UniqueTable<'entries, TypeSet>,
634) -> String {
635 assert!(operand.is_value());
636 let type_var = operand.type_var().unwrap();
637
638 if let Some(typ) = type_var.singleton_type() {
639 return format!("Concrete({})", typ.rust_name());
640 }
641
642 if let Some(free_typevar) = type_var.free_typevar() {
643 if ctrl_typevar.is_some() && free_typevar != *ctrl_typevar.unwrap() {
644 assert!(type_var.base.is_none());
645 return format!("Free({})", type_sets.add(type_var.get_raw_typeset()));
646 }
647 }
648
649 if let Some(base) = &type_var.base {
650 assert!(base.type_var == *ctrl_typevar.unwrap());
651 return camel_case(base.derived_func.name());
652 }
653
654 assert!(type_var == ctrl_typevar.unwrap());
655 "Same".into()
656}
657
658fn gen_bitset<'a, T: IntoIterator<Item = &'a u16>>(
659 iterable: T,
660 name: &'static str,
661 field_size: u8,
662 fmt: &mut Formatter,
663) {
664 let bits = iterable.into_iter().fold(0, |acc, x| {
665 assert!(x.is_power_of_two());
666 assert!(u32::from(*x) < (1 << u32::from(field_size)));
667 acc | x
668 });
669 fmtln!(fmt, "{}: ScalarBitSet::<u{}>({}),", name, field_size, bits);
670}
671
672fn iterable_to_string<I: fmt::Display, T: IntoIterator<Item = I>>(iterable: T) -> String {
673 let elems = iterable
674 .into_iter()
675 .map(|x| x.to_string())
676 .collect::<Vec<_>>()
677 .join(", ");
678 format!("{{{elems}}}")
679}
680
681fn typeset_to_string(ts: &TypeSet) -> String {
682 let mut result = format!("TypeSet(lanes={}", iterable_to_string(&ts.lanes));
683 if !ts.ints.is_empty() {
684 result += &format!(", ints={}", iterable_to_string(&ts.ints));
685 }
686 if !ts.floats.is_empty() {
687 result += &format!(", floats={}", iterable_to_string(&ts.floats));
688 }
689 result += ")";
690 result
691}
692
693pub(crate) fn gen_typesets_table(type_sets: &UniqueTable<TypeSet>, fmt: &mut Formatter) {
695 if type_sets.len() == 0 {
696 return;
697 }
698
699 fmt.comment("Table of value type sets.");
700 assert!(type_sets.len() <= TYPESET_LIMIT, "Too many type sets!");
701 fmtln!(
702 fmt,
703 "const TYPE_SETS: [ir::instructions::ValueTypeSet; {}] = [",
704 type_sets.len()
705 );
706 fmt.indent(|fmt| {
707 for ts in type_sets.iter() {
708 fmt.add_block("ir::instructions::ValueTypeSet", |fmt| {
709 fmt.comment(typeset_to_string(ts));
710 gen_bitset(&ts.lanes, "lanes", 16, fmt);
711 gen_bitset(&ts.dynamic_lanes, "dynamic_lanes", 16, fmt);
712 gen_bitset(&ts.ints, "ints", 8, fmt);
713 gen_bitset(&ts.floats, "floats", 8, fmt);
714 });
715 fmt.line(",");
716 }
717 });
718 fmtln!(fmt, "];");
719}
720
721fn gen_type_constraints(all_inst: &AllInstructions, fmt: &mut Formatter) {
726 let mut type_sets = UniqueTable::new();
728
729 let mut operand_seqs = UniqueSeqTable::new();
735
736 operand_seqs.add(&vec!["Same".to_string(); 3]);
738
739 fmt.comment("Table of opcode constraints.");
740 fmtln!(
741 fmt,
742 "const OPCODE_CONSTRAINTS: [OpcodeConstraints; {}] = [",
743 all_inst.len()
744 );
745 fmt.indent(|fmt| {
746 for inst in all_inst.iter() {
747 let (ctrl_typevar, ctrl_typeset) = if let Some(poly) = &inst.polymorphic_info {
748 let index = type_sets.add(poly.ctrl_typevar.get_raw_typeset());
749 (Some(&poly.ctrl_typevar), index)
750 } else {
751 (None, TYPESET_LIMIT)
752 };
753
754 let mut constraints = Vec::new();
757 for &index in &inst.value_results {
758 constraints.push(get_constraint(&inst.operands_out[index], ctrl_typevar, &mut type_sets));
759 }
760 for &index in &inst.value_opnums {
761 constraints.push(get_constraint(&inst.operands_in[index], ctrl_typevar, &mut type_sets));
762 }
763
764 let constraint_offset = operand_seqs.add(&constraints);
765
766 let fixed_results = inst.value_results.len();
767 let fixed_values = inst.value_opnums.len();
768
769 let use_typevar_operand = if let Some(poly) = &inst.polymorphic_info {
771 poly.use_typevar_operand
772 } else {
773 false
774 };
775
776 let use_result = fixed_results > 0 && inst.operands_out[inst.value_results[0]].type_var() == ctrl_typevar;
778
779 let requires_typevar_operand = use_typevar_operand && !use_result;
781
782 fmt.comment(
783 format!("{}: fixed_results={}, use_typevar_operand={}, requires_typevar_operand={}, fixed_values={}",
784 inst.camel_name,
785 fixed_results,
786 use_typevar_operand,
787 requires_typevar_operand,
788 fixed_values)
789 );
790 fmt.comment(format!("Constraints=[{}]", constraints
791 .iter()
792 .map(|x| format!("'{x}'"))
793 .collect::<Vec<_>>()
794 .join(", ")));
795 if let Some(poly) = &inst.polymorphic_info {
796 fmt.comment(format!("Polymorphic over {}", typeset_to_string(poly.ctrl_typevar.get_raw_typeset())));
797 }
798
799 assert!(fixed_results < 8 && fixed_values < 8, "Bit field encoding too tight");
801 let mut flags = fixed_results; if use_typevar_operand {
803 flags |= 1<<3; }
805 if requires_typevar_operand {
806 flags |= 1<<4; }
808 flags |= fixed_values << 5; fmt.add_block("OpcodeConstraints",|fmt| {
811 fmtln!(fmt, "flags: {:#04x},", flags);
812 fmtln!(fmt, "typeset_offset: {},", ctrl_typeset);
813 fmtln!(fmt, "constraint_offset: {},", constraint_offset);
814 });
815 fmt.line(",");
816 }
817 });
818 fmtln!(fmt, "];");
819 fmt.empty_line();
820
821 gen_typesets_table(&type_sets, fmt);
822 fmt.empty_line();
823
824 fmt.comment("Table of operand constraint sequences.");
825 fmtln!(
826 fmt,
827 "const OPERAND_CONSTRAINTS: [OperandConstraint; {}] = [",
828 operand_seqs.len()
829 );
830 fmt.indent(|fmt| {
831 for constraint in operand_seqs.iter() {
832 fmtln!(fmt, "OperandConstraint::{},", constraint);
833 }
834 });
835 fmtln!(fmt, "];");
836}
837
838fn gen_member_inits(format: &InstructionFormat, fmt: &mut Formatter) {
840 for f in &format.imm_fields {
843 fmtln!(fmt, "{},", f.member);
844 }
845
846 if format.has_value_list {
848 fmt.line("args,");
849 } else if format.num_value_operands == 1 {
850 fmt.line("arg: arg0,");
851 } else if format.num_value_operands > 1 {
852 let mut args = Vec::new();
853 for i in 0..format.num_value_operands {
854 args.push(format!("arg{i}"));
855 }
856 fmtln!(fmt, "args: [{}],", args.join(", "));
857 }
858
859 match format.num_block_operands {
861 0 => (),
862 1 => fmt.line("destination: block0"),
863 n => {
864 let mut blocks = Vec::new();
865 for i in 0..n {
866 blocks.push(format!("block{i}"));
867 }
868 fmtln!(fmt, "blocks: [{}],", blocks.join(", "));
869 }
870 }
871}
872
873fn gen_format_constructor(format: &InstructionFormat, fmt: &mut Formatter) {
878 let mut args = vec![
880 "self".to_string(),
881 "opcode: Opcode".into(),
882 "ctrl_typevar: Type".into(),
883 ];
884
885 for f in &format.imm_fields {
887 args.push(format!("{}: {}", f.member, f.kind.rust_type));
888 }
889
890 args.extend((0..format.num_block_operands).map(|i| format!("block{i}: ir::BlockCall")));
892
893 if format.has_value_list {
895 args.push("args: ir::ValueList".into());
898 } else {
899 for i in 0..format.num_value_operands {
901 args.push(format!("arg{i}: Value"));
902 }
903 }
904
905 let proto = format!(
906 "{}({}) -> (Inst, &'f mut ir::DataFlowGraph)",
907 format.name,
908 args.join(", ")
909 );
910
911 let imms_need_masking = format
912 .imm_fields
913 .iter()
914 .any(|f| f.kind.rust_type == "ir::immediates::Imm64");
915
916 fmt.doc_comment(format.to_string());
917 fmt.line("#[allow(non_snake_case)]");
918 fmt.add_block(&format!("fn {proto}"), |fmt| {
919 fmt.add_block(&format!(
921 "let{} data = ir::InstructionData::{}",
922 if imms_need_masking { " mut" } else { "" },
923 format.name
924 ), |fmt| {
925 fmt.line("opcode,");
926 gen_member_inits(format, fmt);
927 });
928 fmtln!(fmt, ";");
929
930 if imms_need_masking {
931 fmtln!(fmt, "data.mask_immediates(ctrl_typevar);");
932 }
933
934 fmtln!(fmt, "debug_assert_eq!(opcode.format(), InstructionFormat::from(&data), \"Wrong InstructionFormat for Opcode: {{opcode}}\");");
936
937 fmt.line("self.build(data, ctrl_typevar)");
938 });
939}
940
941fn gen_inst_builder(inst: &Instruction, format: &InstructionFormat, fmt: &mut Formatter) {
946 let mut args = vec![String::new()];
948
949 let mut args_doc = Vec::new();
950 let mut rets_doc = Vec::new();
951
952 if let Some(poly) = &inst.polymorphic_info {
955 if !poly.use_typevar_operand {
956 args.push(format!("{}: crate::ir::Type", poly.ctrl_typevar.name));
957 args_doc.push(format!(
958 "- {} (controlling type variable): {}",
959 poly.ctrl_typevar.name, poly.ctrl_typevar.doc
960 ));
961 }
962 }
963
964 let mut tmpl_types = Vec::new();
965 let mut into_args = Vec::new();
966 let mut block_args = Vec::new();
967 for op in &inst.operands_in {
968 if op.kind.is_block() {
969 args.push(format!("{}_label: {}", op.name, "ir::Block"));
970 args_doc.push(format!(
971 "- {}_label: {}",
972 op.name, "Destination basic block"
973 ));
974
975 args.push(format!("{}_args: {}", op.name, "&[Value]"));
976 args_doc.push(format!("- {}_args: {}", op.name, "Block arguments"));
977
978 block_args.push(op);
979 } else {
980 let t = if op.is_immediate() {
981 let t = format!("T{}", tmpl_types.len() + 1);
982 tmpl_types.push(format!("{}: Into<{}>", t, op.kind.rust_type));
983 into_args.push(op.name);
984 t
985 } else {
986 op.kind.rust_type.to_string()
987 };
988 args.push(format!("{}: {}", op.name, t));
989 args_doc.push(format!("- {}: {}", op.name, op.doc()));
990 }
991 }
992
993 if format.has_value_list || !block_args.is_empty() {
996 args[0].push_str("mut self");
997 } else {
998 args[0].push_str("self");
999 }
1000
1001 for op in &inst.operands_out {
1002 rets_doc.push(format!("- {}: {}", op.name, op.doc()));
1003 }
1004
1005 let rtype = match inst.value_results.len() {
1006 0 => "Inst".into(),
1007 1 => "Value".into(),
1008 _ => format!("({})", vec!["Value"; inst.value_results.len()].join(", ")),
1009 };
1010
1011 let tmpl = if !tmpl_types.is_empty() {
1012 format!("<{}>", tmpl_types.join(", "))
1013 } else {
1014 "".into()
1015 };
1016
1017 let proto = format!(
1018 "{}{}({}) -> {}",
1019 inst.snake_name(),
1020 tmpl,
1021 args.join(", "),
1022 rtype
1023 );
1024
1025 fmt.doc_comment(&inst.doc);
1026 if !args_doc.is_empty() {
1027 fmt.line("///");
1028 fmt.doc_comment("Inputs:");
1029 fmt.line("///");
1030 for doc_line in args_doc {
1031 fmt.doc_comment(doc_line);
1032 }
1033 }
1034 if !rets_doc.is_empty() {
1035 fmt.line("///");
1036 fmt.doc_comment("Outputs:");
1037 fmt.line("///");
1038 for doc_line in rets_doc {
1039 fmt.doc_comment(doc_line);
1040 }
1041 }
1042
1043 fmt.line("#[allow(non_snake_case)]");
1044 fmt.add_block(&format!("fn {proto}"), |fmt| {
1045 for arg in into_args {
1047 fmtln!(fmt, "let {} = {}.into();", arg, arg);
1048 }
1049
1050 for op in block_args {
1052 fmtln!(
1053 fmt,
1054 "let {0} = self.data_flow_graph_mut().block_call({0}_label, {0}_args);",
1055 op.name
1056 );
1057 }
1058
1059 let first_arg = format!("Opcode::{}", inst.camel_name);
1061 let mut args = vec![first_arg.as_str()];
1062 if let Some(poly) = &inst.polymorphic_info {
1063 if poly.use_typevar_operand {
1064 let op_num = inst.value_opnums[format.typevar_operand.unwrap()];
1066 fmtln!(
1067 fmt,
1068 "let ctrl_typevar = self.data_flow_graph().value_type({});",
1069 inst.operands_in[op_num].name
1070 );
1071
1072 args.push("ctrl_typevar");
1074 } else {
1075 args.push(&poly.ctrl_typevar.name);
1077 }
1078 } else {
1079 args.push("types::INVALID");
1081 }
1082
1083 for &op_num in &inst.imm_opnums {
1085 args.push(inst.operands_in[op_num].name);
1086 }
1087
1088 if format.has_value_list {
1090 fmt.line("let mut vlist = ir::ValueList::default();");
1092 args.push("vlist");
1093 fmt.line("{");
1094 fmt.indent(|fmt| {
1095 fmt.line("let pool = &mut self.data_flow_graph_mut().value_lists;");
1096 for op in &inst.operands_in {
1097 if op.is_value() {
1098 fmtln!(fmt, "vlist.push({}, pool);", op.name);
1099 } else if op.is_varargs() {
1100 fmtln!(fmt, "vlist.extend({}.iter().cloned(), pool);", op.name);
1101 }
1102 }
1103 });
1104 fmt.line("}");
1105 } else {
1106 for &op_num in &inst.value_opnums {
1108 args.push(inst.operands_in[op_num].name);
1109 }
1110 }
1111
1112 let fcall = format!("self.{}({})", format.name, args.join(", "));
1114
1115 fmtln!(fmt, "let (inst, dfg) = {};", fcall);
1116 fmtln!(
1117 fmt,
1118 "crate::trace!(\"inserted {{inst:?}}: {{}}\", dfg.display_inst(inst));"
1119 );
1120
1121 if inst.value_results.is_empty() {
1122 fmtln!(fmt, "inst");
1123 return;
1124 }
1125
1126 if inst.value_results.len() == 1 {
1127 fmt.line("dfg.first_result(inst)");
1128 } else {
1129 fmtln!(
1130 fmt,
1131 "let results = &dfg.inst_results(inst)[0..{}];",
1132 inst.value_results.len()
1133 );
1134 fmtln!(
1135 fmt,
1136 "({})",
1137 inst.value_results
1138 .iter()
1139 .enumerate()
1140 .map(|(i, _)| format!("results[{i}]"))
1141 .collect::<Vec<_>>()
1142 .join(", ")
1143 );
1144 }
1145 });
1146}
1147
1148fn gen_builder(
1150 instructions: &AllInstructions,
1151 formats: &[Rc<InstructionFormat>],
1152 fmt: &mut Formatter,
1153) {
1154 fmt.doc_comment(
1155 r#"
1156 Convenience methods for building instructions.
1157
1158 The `InstBuilder` trait has one method per instruction opcode for
1159 conveniently constructing the instruction with minimum arguments.
1160 Polymorphic instructions infer their result types from the input
1161 arguments when possible. In some cases, an explicit `ctrl_typevar`
1162 argument is required.
1163
1164 The opcode methods return the new instruction's result values, or
1165 the `Inst` itself for instructions that don't have any results.
1166
1167 There is also a method per instruction format. These methods all
1168 return an `Inst`.
1169
1170 When an address to a load or store is specified, its integer
1171 size is required to be equal to the platform's pointer width.
1172 "#,
1173 );
1174 fmt.add_block("pub trait InstBuilder<'f>: InstBuilderBase<'f>", |fmt| {
1175 for inst in instructions.iter() {
1176 gen_inst_builder(inst, &inst.format, fmt);
1177 fmt.empty_line();
1178 }
1179 for (i, format) in formats.iter().enumerate() {
1180 gen_format_constructor(format, fmt);
1181 if i + 1 != formats.len() {
1182 fmt.empty_line();
1183 }
1184 }
1185 });
1186}
1187
1188pub(crate) fn generate(
1189 formats: &[Rc<InstructionFormat>],
1190 all_inst: &AllInstructions,
1191 opcode_filename: &str,
1192 inst_builder_filename: &str,
1193 out_dir: &std::path::Path,
1194) -> Result<(), error::Error> {
1195 let mut fmt = Formatter::new(Language::Rust);
1197 gen_formats(&formats, &mut fmt);
1198 gen_instruction_data(&formats, &mut fmt);
1199 fmt.empty_line();
1200 gen_instruction_data_impl(&formats, &mut fmt);
1201 fmt.empty_line();
1202 gen_opcodes(all_inst, &mut fmt);
1203 fmt.empty_line();
1204 gen_type_constraints(all_inst, &mut fmt);
1205 fmt.write(opcode_filename, out_dir)?;
1206
1207 let mut fmt = Formatter::new(Language::Rust);
1209 gen_builder(all_inst, &formats, &mut fmt);
1210 fmt.write(inst_builder_filename, out_dir)?;
1211
1212 Ok(())
1213}