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_imm_inst_builder(inst: &Instruction, fmt: &mut Formatter) {
1363 assert!(
1367 !inst.format.has_value_list,
1368 "inst_builder_imm_method is not supported for {} (has a value list)",
1369 inst.name
1370 );
1371 assert_eq!(
1372 inst.value_results.len(),
1373 1,
1374 "inst_builder_imm_method is only supported for single-result instructions, not {}",
1375 inst.name
1376 );
1377 assert!(
1378 inst.value_opnums.len() >= 2,
1379 "inst_builder_imm_method requires at least two value operands, but {} has {}",
1380 inst.name,
1381 inst.value_opnums.len()
1382 );
1383
1384 let historically_signed = matches!(
1387 inst.name.as_str(),
1388 "iadd" | "imul" | "sdiv" | "srem" | "icmp"
1389 );
1390
1391 gen_one_imm_inst_builder(inst, fmt, "_imm_s", true, None);
1392 fmt.empty_line();
1393 gen_one_imm_inst_builder(inst, fmt, "_imm_u", false, None);
1394 fmt.empty_line();
1395 let name = inst.snake_name();
1396 let deprecation = format!(
1397 "use `{name}_imm_s` (sign-extends the immediate) or `{name}_imm_u` \
1398 (zero-extends the immediate) instead",
1399 );
1400 gen_one_imm_inst_builder(inst, fmt, "_imm", historically_signed, Some(&deprecation));
1401}
1402
1403fn gen_one_imm_inst_builder(
1408 inst: &Instruction,
1409 fmt: &mut Formatter,
1410 suffix: &str,
1411 signed: bool,
1412 deprecation: Option<&str>,
1413) {
1414 let imm_opnum = *inst.value_opnums.last().unwrap();
1417 let ctrl_opnum = *inst.value_opnums.first().unwrap();
1418 let imm_name = inst.operands_in[imm_opnum].name;
1419 let ctrl_name = inst.operands_in[ctrl_opnum].name;
1420
1421 let mut args = vec!["mut self".to_string()];
1422 let mut args_doc = Vec::new();
1423 for (i, op) in inst.operands_in.iter().enumerate() {
1424 if i == imm_opnum {
1425 args.push(format!("{}: T", op.name));
1426 args_doc.push(format!("- {}: an immediate integer constant", op.name));
1427 } else if op.is_value() {
1428 args.push(format!("{}: Value", op.name));
1429 args_doc.push(format!("- {}: {}", op.name, op.doc()));
1430 } else {
1431 args.push(format!("{}: {}", op.name, op.kind.rust_type));
1432 args_doc.push(format!("- {}: {}", op.name, op.doc()));
1433 }
1434 }
1435
1436 let proto = format!(
1437 "{}{suffix}<T: Into<ir::immediates::Imm64>>({}) -> Value",
1438 inst.snake_name(),
1439 args.join(", "),
1440 );
1441
1442 let extends = if signed {
1443 "sign-extended"
1444 } else {
1445 "zero-extended"
1446 };
1447 fmt.doc_comment(format!(
1448 "Convenience method that is like [`{name}`][Self::{name}], but takes its \
1449 trailing operand as an immediate integer constant which is materialized \
1450 with an `iconst` and {extends} to the controlling type.",
1451 name = inst.snake_name(),
1452 ));
1453 fmt.line("///");
1454 fmt.doc_comment("Inputs:");
1455 fmt.line("///");
1456 for doc_line in args_doc {
1457 fmt.doc_comment(doc_line);
1458 }
1459
1460 if let Some(note) = deprecation {
1461 fmtln!(fmt, "#[deprecated(note = \"{note}\")]");
1462 }
1463 fmt.line("#[allow(non_snake_case, reason = \"generated code\")]");
1464 fmt.add_block(&format!("fn {proto}"), |fmt| {
1465 fmtln!(fmt, "let {imm_name} = {imm_name}.into();");
1466 fmtln!(
1467 fmt,
1468 "let imm_ty = self.data_flow_graph().value_type({ctrl_name});"
1469 );
1470 fmtln!(
1471 fmt,
1472 "let {imm_name} = self.build_imm_const(imm_ty, {imm_name}, {signed});",
1473 );
1474 let call_args = inst
1475 .operands_in
1476 .iter()
1477 .map(|op| op.name)
1478 .collect::<Vec<_>>()
1479 .join(", ");
1480 fmtln!(fmt, "self.{}({call_args})", inst.snake_name());
1481 });
1482}
1483
1484fn gen_stack_access_builders(fmt: &mut Formatter) {
1490 fmt.doc_comment(
1492 "Load a value from a stack slot at the constant offset.\n\n\
1493 This emits a `stack_addr` followed by a `load`.",
1494 );
1495 fmt.line("#[allow(non_snake_case, reason = \"generated code\")]");
1496 fmt.add_block(
1497 "fn stack_load<T1: Into<ir::immediates::Offset32>>(mut self, pointer_type: crate::ir::Type, Mem: crate::ir::Type, SS: ir::StackSlot, Offset: T1) -> Value",
1498 |fmt| {
1499 fmt.line("let Offset = Offset.into();");
1500 fmt.line("let addr = self.build_aux_inst(InstructionData::StackAddr { opcode: Opcode::StackAddr, stack_slot: SS, offset: Offset }, pointer_type);");
1501 fmt.line("let addr = self.data_flow_graph().first_result(addr);");
1502 fmt.line("// Stack slots are required to be accessible, but we can't");
1503 fmt.line("// currently ensure that they are aligned.");
1504 fmt.line("let mut flags = ir::MemFlagsData::new();");
1505 fmt.line("flags.set_notrap();");
1506 fmt.line("self.load(Mem, flags, addr, 0)");
1507 },
1508 );
1509 fmt.empty_line();
1510
1511 fmt.doc_comment(
1513 "Store a value to a stack slot at a constant offset.\n\n\
1514 This emits a `stack_addr` followed by a `store`.",
1515 );
1516 fmt.line("#[allow(non_snake_case, reason = \"generated code\")]");
1517 fmt.add_block(
1518 "fn stack_store<T1: Into<ir::immediates::Offset32>>(mut self, pointer_type: crate::ir::Type, x: ir::Value, SS: ir::StackSlot, Offset: T1) -> Inst",
1519 |fmt| {
1520 fmt.line("let Offset = Offset.into();");
1521 fmt.line("let addr = self.build_aux_inst(InstructionData::StackAddr { opcode: Opcode::StackAddr, stack_slot: SS, offset: Offset }, pointer_type);");
1522 fmt.line("let addr = self.data_flow_graph().first_result(addr);");
1523 fmt.line("// Stack slots are required to be accessible, but we can't");
1524 fmt.line("// currently ensure that they are aligned.");
1525 fmt.line("let mut flags = ir::MemFlagsData::new();");
1526 fmt.line("flags.set_notrap();");
1527 fmt.line("self.store(flags, x, addr, 0)");
1528 },
1529 );
1530 fmt.empty_line();
1531
1532 fmt.doc_comment(
1534 "Load a value from a dynamic stack slot.\n\n\
1535 This emits a `dynamic_stack_addr` followed by a `load`.",
1536 );
1537 fmt.line("#[allow(non_snake_case, reason = \"generated code\")]");
1538 fmt.add_block(
1539 "fn dynamic_stack_load(mut self, pointer_type: crate::ir::Type, Mem: crate::ir::Type, DSS: ir::DynamicStackSlot) -> Value",
1540 |fmt| {
1541 fmt.line("let addr = self.build_aux_inst(InstructionData::DynamicStackAddr { opcode: Opcode::DynamicStackAddr, dynamic_stack_slot: DSS }, pointer_type);");
1542 fmt.line("let addr = self.data_flow_graph().first_result(addr);");
1543 fmt.line("// Dynamic stack slots are required to be accessible and aligned.");
1544 fmt.line("let flags = ir::MemFlagsData::trusted();");
1545 fmt.line("self.load(Mem, flags, addr, 0)");
1546 },
1547 );
1548 fmt.empty_line();
1549
1550 fmt.doc_comment(
1552 "Store a value to a dynamic stack slot.\n\n\
1553 This emits a `dynamic_stack_addr` followed by a `store`.",
1554 );
1555 fmt.line("#[allow(non_snake_case, reason = \"generated code\")]");
1556 fmt.add_block(
1557 "fn dynamic_stack_store(mut self, pointer_type: crate::ir::Type, x: ir::Value, DSS: ir::DynamicStackSlot) -> Inst",
1558 |fmt| {
1559 fmt.line("let addr = self.build_aux_inst(InstructionData::DynamicStackAddr { opcode: Opcode::DynamicStackAddr, dynamic_stack_slot: DSS }, pointer_type);");
1560 fmt.line("let addr = self.data_flow_graph().first_result(addr);");
1561 fmt.line("// Dynamic stack slots are required to be accessible and aligned.");
1562 fmt.line("let flags = ir::MemFlagsData::trusted();");
1563 fmt.line("self.store(flags, x, addr, 0)");
1564 },
1565 );
1566}
1567
1568fn gen_bitwise_not_builders(fmt: &mut Formatter) {
1575 for (method, op, doc) in [
1576 ("band_not", "band", "Bitwise and not: computes `x & ~y`."),
1577 ("bor_not", "bor", "Bitwise or not: computes `x | ~y`."),
1578 ("bxor_not", "bxor", "Bitwise xor not: computes `x ^ ~y`."),
1579 ] {
1580 fmt.doc_comment(format!(
1581 "{doc}\n\nThis emits a `bnot` of `y` followed by a `{op}`."
1582 ));
1583 fmt.add_block(
1584 &format!("fn {method}(mut self, x: ir::Value, y: ir::Value) -> Value"),
1585 |fmt| {
1586 fmt.line("let ctrl_typevar = self.data_flow_graph().value_type(y);");
1587 fmt.line("let neg = self.build_aux_inst(InstructionData::Unary { opcode: Opcode::Bnot, arg: y }, ctrl_typevar);");
1588 fmt.line("let neg = self.data_flow_graph().first_result(neg);");
1589 fmtln!(fmt, "self.{op}(x, neg)");
1590 },
1591 );
1592 fmt.empty_line();
1593 }
1594}
1595
1596fn gen_builder(
1598 instructions: &AllInstructions,
1599 formats: &[Rc<InstructionFormat>],
1600 fmt: &mut Formatter,
1601) {
1602 fmt.doc_comment(
1603 r#"
1604 Convenience methods for building instructions.
1605
1606 The `InstBuilder` trait has one method per instruction opcode for
1607 conveniently constructing the instruction with minimum arguments.
1608 Polymorphic instructions infer their result types from the input
1609 arguments when possible. In some cases, an explicit `ctrl_typevar`
1610 argument is required.
1611
1612 The opcode methods return the new instruction's result values, or
1613 the `Inst` itself for instructions that don't have any results.
1614
1615 There is also a method per instruction format. These methods all
1616 return an `Inst`.
1617
1618 When an address to a load or store is specified, its integer
1619 size is required to be equal to the platform's pointer width.
1620 "#,
1621 );
1622 fmt.add_block("pub trait InstBuilder<'f>: InstBuilderBase<'f>", |fmt| {
1623 for inst in instructions.iter() {
1624 gen_inst_builder(inst, &inst.format, fmt);
1625 fmt.empty_line();
1626 if inst.inst_builder_imm_method {
1627 gen_imm_inst_builder(inst, fmt);
1628 fmt.empty_line();
1629 }
1630 }
1631 gen_stack_access_builders(fmt);
1632 fmt.empty_line();
1633 gen_bitwise_not_builders(fmt);
1634 for (i, format) in formats.iter().enumerate() {
1635 gen_format_constructor(format, fmt);
1636 if i + 1 != formats.len() {
1637 fmt.empty_line();
1638 }
1639 }
1640 });
1641}
1642
1643pub(crate) fn generate(
1644 formats: &[Rc<InstructionFormat>],
1645 all_inst: &AllInstructions,
1646 opcode_filename: &str,
1647 inst_builder_filename: &str,
1648 out_dir: &std::path::Path,
1649) -> Result<(), error::Error> {
1650 let mut fmt = Formatter::new(Language::Rust);
1652 gen_formats(&formats, &mut fmt);
1653 gen_instruction_data(&formats, &mut fmt);
1654 fmt.empty_line();
1655 gen_instruction_data_impl(&formats, &mut fmt);
1656 fmt.empty_line();
1657 gen_opcodes(all_inst, &mut fmt);
1658 fmt.empty_line();
1659 gen_type_constraints(all_inst, &mut fmt);
1660 fmt.write(opcode_filename, out_dir)?;
1661
1662 let mut fmt = Formatter::new(Language::Rust);
1664 gen_builder(all_inst, &formats, &mut fmt);
1665 fmt.write(inst_builder_filename, out_dir)?;
1666
1667 Ok(())
1668}