1use cranelift_assembler_x64_meta::dsl::{
4 Feature, Format, Inst, Location, Mutability, Operand, OperandKind, RegClass,
5};
6use cranelift_srcgen::{Formatter, fmtln};
7
8const ASM: &str = "cranelift_assembler_x64";
10
11fn include_inst(inst: &Inst) -> bool {
12 if inst.mnemonic.starts_with("push") {
15 return false;
16 }
17
18 true
19}
20
21fn rust_param_raw(op: &Operand) -> String {
26 match op.location.kind() {
27 OperandKind::Imm(loc) => {
28 let bits = loc.bits();
29 if op.extension.is_sign_extended() {
30 format!("i{bits}")
31 } else {
32 format!("u{bits}")
33 }
34 }
35 OperandKind::RegMem(rm) => {
36 let reg = rm.reg_class().unwrap();
37 let aligned = if op.align { "Aligned" } else { "" };
38 let bits = rm.bits();
39 format!("&{reg}Mem{aligned}{bits}")
40 }
41 OperandKind::Mem(_) => {
42 format!("&SyntheticAmode")
43 }
44 OperandKind::Reg(r) | OperandKind::FixedReg(r) => r.reg_class().unwrap().to_string(),
45 }
46}
47
48fn rust_convert_isle_to_assembler(op: &Operand) -> String {
52 match op.location.kind() {
53 OperandKind::Imm(loc) => {
54 let bits = loc.bits();
55 let ty = if op.extension.is_sign_extended() {
56 "Simm"
57 } else {
58 "Imm"
59 };
60 format!("{ASM}::{ty}{bits}::new({loc})")
61 }
62 OperandKind::FixedReg(r) => {
63 let reg = r.reg_class().unwrap().to_string().to_lowercase();
64 match op.mutability {
65 Mutability::Read => format!("{ASM}::Fixed({r})"),
66 Mutability::Write => {
67 format!("{ASM}::Fixed(self.temp_writable_{reg}())")
68 }
69 Mutability::ReadWrite => {
70 format!("self.convert_{reg}_to_assembler_fixed_read_write_{reg}({r})")
71 }
72 }
73 }
74 OperandKind::Reg(r) => {
75 let reg = r.reg_class().unwrap();
76 let reg_lower = reg.to_string().to_lowercase();
77 match op.mutability {
78 Mutability::Read => {
79 format!("{ASM}::{reg}::new({r})")
80 }
81 Mutability::Write => {
82 format!("{ASM}::{reg}::new(self.temp_writable_{reg_lower}())")
83 }
84 Mutability::ReadWrite => {
85 format!("self.convert_{reg_lower}_to_assembler_read_write_{reg_lower}({r})")
86 }
87 }
88 }
89 OperandKind::RegMem(rm) => {
90 let reg = rm.reg_class().unwrap().to_string().to_lowercase();
91 let mut_ = op.mutability.generate_snake_case();
92 let align = if op.align { "_aligned" } else { "" };
93 format!("self.convert_{reg}_mem_to_assembler_{mut_}_{reg}_mem{align}({rm})")
94 }
95 OperandKind::Mem(mem) => format!("self.convert_amode_to_assembler_amode({mem})"),
96 }
97}
98
99fn generate_macro_inst_fn(f: &mut Formatter, inst: &Inst) {
105 use OperandKind::*;
106
107 let struct_name = inst.name();
108 let operands = inst.format.operands.iter().cloned().collect::<Vec<_>>();
109 let results = operands
110 .iter()
111 .filter(|o| o.mutability.is_write())
112 .collect::<Vec<_>>();
113 let rust_params = operands
114 .iter()
115 .filter(|o| is_raw_operand_param(o))
116 .map(|o| format!("{}: {}", o.location, rust_param_raw(o)))
117 .chain(if inst.has_trap {
118 Some(format!("trap: &TrapCode"))
119 } else {
120 None
121 })
122 .collect::<Vec<_>>()
123 .join(", ");
124 f.add_block(
125 &format!("fn x64_{struct_name}_raw(&mut self, {rust_params}) -> AssemblerOutputs"),
126 |f| {
127 f.comment("Convert ISLE types to assembler types.");
128 for op in operands.iter() {
129 let loc = op.location;
130 let cvt = rust_convert_isle_to_assembler(op);
131 fmtln!(f, "let {loc} = {cvt};");
132 }
133 let mut args = operands
134 .iter()
135 .map(|o| format!("{}.clone()", o.location))
136 .collect::<Vec<_>>();
137 if inst.has_trap {
138 args.push(format!("{ASM}::TrapCode(trap.as_raw())"));
139 }
140 let args = args.join(", ");
141 f.empty_line();
142
143 f.comment("Build the instruction.");
144 fmtln!(
145 f,
146 "let inst = {ASM}::inst::{struct_name}::new({args}).into();"
147 );
148 fmtln!(f, "let inst = MInst::External {{ inst }};");
149 f.empty_line();
150
151 f.comment("Return a type ISLE can work with.");
158 let access_reg = |op: &Operand| match op.mutability {
159 Mutability::Read => unreachable!(),
160 Mutability::Write => "to_reg()",
161 Mutability::ReadWrite => "write.to_reg()",
162 };
163 let ty_var_of_reg = |loc: Location| {
164 let ty = loc.reg_class().unwrap().to_string();
165 let var = ty.to_lowercase();
166 (ty, var)
167 };
168 match results.as_slice() {
169 [] => fmtln!(f, "AssemblerOutputs::SideEffect {{ inst }}"),
170 [op] => match op.location.kind() {
171 Imm(_) => unreachable!(),
172 Reg(r) | FixedReg(r) => {
173 let (ty, var) = ty_var_of_reg(r);
174 fmtln!(f, "let {var} = {r}.as_ref().{};", access_reg(op));
175 fmtln!(f, "AssemblerOutputs::Ret{ty} {{ inst, {var} }}");
176 }
177 Mem(_) => {
178 fmtln!(f, "AssemblerOutputs::SideEffect {{ inst }}")
179 }
180 RegMem(rm) => {
181 let (ty, var) = ty_var_of_reg(rm);
182 f.add_block(&format!("match {rm}"), |f| {
183 f.add_block(&format!("{ASM}::{ty}Mem::{ty}(reg) => "), |f| {
184 fmtln!(f, "let {var} = reg.{};", access_reg(op));
185 fmtln!(f, "AssemblerOutputs::Ret{ty} {{ inst, {var} }} ");
186 });
187 f.add_block(&format!("{ASM}::{ty}Mem::Mem(_) => "), |f| {
188 fmtln!(f, "AssemblerOutputs::SideEffect {{ inst }} ");
189 });
190 });
191 }
192 },
193 [op1, op2] => match (op1.location.kind(), op2.location.kind()) {
197 (FixedReg(loc1) | Reg(loc1), FixedReg(loc2) | Reg(loc2)) => {
198 fmtln!(f, "let one = {loc1}.as_ref().{}.to_reg();", access_reg(op1));
199 fmtln!(f, "let two = {loc2}.as_ref().{}.to_reg();", access_reg(op2));
200 fmtln!(f, "let regs = ValueRegs::two(one, two);");
201 fmtln!(f, "AssemblerOutputs::RetValueRegs {{ inst, regs }}");
202 }
203 (Reg(reg), Mem(_)) | (Mem(_) | RegMem(_), Reg(reg) | FixedReg(reg)) => {
204 let (ty, var) = ty_var_of_reg(reg);
205 fmtln!(f, "let {var} = {reg}.as_ref().{};", access_reg(op2));
206 fmtln!(f, "AssemblerOutputs::Ret{ty} {{ inst, {var} }}");
207 }
208 _ => unimplemented!("unhandled results: {results:?}"),
209 },
210
211 [op1, op2, op3] => match (
212 op1.location.kind(),
213 op2.location.kind(),
214 op3.location.kind(),
215 ) {
216 (FixedReg(loc1), FixedReg(loc2), Mem(_)) => {
217 fmtln!(f, "let one = {loc1}.as_ref().{}.to_reg();", access_reg(op1));
218 fmtln!(f, "let two = {loc2}.as_ref().{}.to_reg();", access_reg(op2));
219 fmtln!(f, "let regs = ValueRegs::two(one, two);");
220 fmtln!(f, "AssemblerOutputs::RetValueRegs {{ inst, regs }}");
221 }
222 _ => unimplemented!("unhandled results: {results:?}"),
223 },
224
225 _ => panic!("instruction has more than one result"),
226 }
227 },
228 );
229}
230
231pub fn generate_rust_macro(f: &mut Formatter, insts: &[Inst]) {
233 fmtln!(f, "#[doc(hidden)]");
234 fmtln!(f, "macro_rules! isle_assembler_methods {{");
235 f.indent(|f| {
236 fmtln!(f, "() => {{");
237 f.indent(|f| {
238 for inst in insts {
239 if include_inst(inst) {
240 generate_macro_inst_fn(f, inst);
241 }
242 }
243 });
244 fmtln!(f, "}};");
245 });
246 fmtln!(f, "}}");
247}
248
249fn isle_param_raw(op: &Operand) -> String {
253 match op.location.kind() {
254 OperandKind::Imm(loc) => {
255 let bits = loc.bits();
256 if op.extension.is_sign_extended() {
257 format!("i{bits}")
258 } else {
259 format!("u{bits}")
260 }
261 }
262 OperandKind::Reg(r) | OperandKind::FixedReg(r) => r.reg_class().unwrap().to_string(),
263 OperandKind::Mem(_) => {
264 if op.align {
265 unimplemented!("no way yet to mark an SyntheticAmode as aligned")
266 } else {
267 "SyntheticAmode".to_string()
268 }
269 }
270 OperandKind::RegMem(rm) => {
271 let reg = rm.reg_class().unwrap();
272 let aligned = if op.align { "Aligned" } else { "" };
273 let bits = rm.bits();
274 format!("{reg}Mem{aligned}{bits}")
275 }
276 }
277}
278
279#[derive(Copy, Clone, Debug)]
287enum IsleConstructor {
288 RetMemorySideEffect,
292
293 RetGpr,
296
297 RetXmm,
299
300 RetValueRegs,
303
304 NoReturnSideEffect,
306
307 ProducesFlagsSideEffect,
310
311 ConsumesFlagsReturnsGpr,
314}
315
316impl IsleConstructor {
317 fn result_ty(&self) -> &'static str {
319 match self {
320 IsleConstructor::RetGpr => "Gpr",
321 IsleConstructor::RetXmm => "Xmm",
322 IsleConstructor::RetValueRegs => "ValueRegs",
323 IsleConstructor::NoReturnSideEffect | IsleConstructor::RetMemorySideEffect => {
324 "SideEffectNoResult"
325 }
326 IsleConstructor::ProducesFlagsSideEffect => "ProducesFlags",
327 IsleConstructor::ConsumesFlagsReturnsGpr => "ConsumesFlags",
328 }
329 }
330
331 fn conversion_constructor(&self) -> &'static str {
334 match self {
335 IsleConstructor::NoReturnSideEffect | IsleConstructor::RetMemorySideEffect => {
336 "defer_side_effect"
337 }
338 IsleConstructor::RetGpr => "emit_ret_gpr",
339 IsleConstructor::RetXmm => "emit_ret_xmm",
340 IsleConstructor::RetValueRegs => "emit_ret_value_regs",
341 IsleConstructor::ProducesFlagsSideEffect => "asm_produce_flags_side_effect",
342 IsleConstructor::ConsumesFlagsReturnsGpr => "asm_consumes_flags_returns_gpr",
343 }
344 }
345
346 fn suffix(&self) -> &'static str {
348 match self {
349 IsleConstructor::RetMemorySideEffect => "_mem",
350 IsleConstructor::RetGpr
351 | IsleConstructor::RetXmm
352 | IsleConstructor::RetValueRegs
353 | IsleConstructor::NoReturnSideEffect
354 | IsleConstructor::ProducesFlagsSideEffect
355 | IsleConstructor::ConsumesFlagsReturnsGpr => "",
356 }
357 }
358
359 fn includes_write_only_reg_mem(&self) -> bool {
365 match self {
366 IsleConstructor::RetMemorySideEffect => true,
367 IsleConstructor::RetGpr
368 | IsleConstructor::RetXmm
369 | IsleConstructor::RetValueRegs
370 | IsleConstructor::NoReturnSideEffect
371 | IsleConstructor::ProducesFlagsSideEffect
372 | IsleConstructor::ConsumesFlagsReturnsGpr => false,
373 }
374 }
375}
376
377fn isle_param_for_ctor(op: &Operand, ctor: IsleConstructor) -> String {
380 match op.location.kind() {
381 OperandKind::RegMem(_) if op.mutability.is_write() => match ctor {
386 IsleConstructor::RetMemorySideEffect => "SyntheticAmode".to_string(),
387 IsleConstructor::NoReturnSideEffect => "".to_string(),
388 IsleConstructor::RetGpr | IsleConstructor::ConsumesFlagsReturnsGpr => "Gpr".to_string(),
389 IsleConstructor::RetXmm => "Xmm".to_string(),
390 IsleConstructor::RetValueRegs => "ValueRegs".to_string(),
391 IsleConstructor::ProducesFlagsSideEffect => todo!(),
392 },
393
394 _ => isle_param_raw(op),
396 }
397}
398
399fn isle_constructors(format: &Format) -> Vec<IsleConstructor> {
405 use Mutability::*;
406 use OperandKind::*;
407
408 let write_operands = format
409 .operands
410 .iter()
411 .filter(|o| o.mutability.is_write())
412 .collect::<Vec<_>>();
413 match &write_operands[..] {
414 [] => {
415 if format.eflags.is_write() {
416 vec![IsleConstructor::ProducesFlagsSideEffect]
417 } else {
418 vec![IsleConstructor::NoReturnSideEffect]
419 }
420 }
421 [one] => match one.mutability {
422 Read => unreachable!(),
423 ReadWrite | Write => match one.location.kind() {
424 Imm(_) => unreachable!(),
425 Reg(r) | FixedReg(r) => match r.reg_class().unwrap() {
428 RegClass::Xmm => {
429 assert!(!format.eflags.is_read());
430 vec![IsleConstructor::RetXmm]
431 }
432 RegClass::Gpr => {
433 if format.eflags.is_read() {
434 vec![IsleConstructor::ConsumesFlagsReturnsGpr]
435 } else {
436 vec![IsleConstructor::RetGpr]
437 }
438 }
439 },
440 Mem(_) => {
442 assert!(!format.eflags.is_read());
443 vec![IsleConstructor::RetMemorySideEffect]
444 }
445 RegMem(rm) => match rm.reg_class().unwrap() {
448 RegClass::Xmm => {
449 assert!(!format.eflags.is_read());
450 vec![
451 IsleConstructor::RetXmm,
452 IsleConstructor::RetMemorySideEffect,
453 ]
454 }
455 RegClass::Gpr => {
456 if format.eflags.is_read() {
457 vec![IsleConstructor::ConsumesFlagsReturnsGpr]
463 } else {
464 vec![
465 IsleConstructor::RetGpr,
466 IsleConstructor::RetMemorySideEffect,
467 ]
468 }
469 }
470 },
471 },
472 },
473 [one, two] => {
474 assert!(!format.eflags.is_read());
475 match (one.location.kind(), two.location.kind()) {
476 (FixedReg(_) | Reg(_), FixedReg(_) | Reg(_)) => {
477 vec![IsleConstructor::RetValueRegs]
478 }
479 (Reg(r), Mem(_)) | (Mem(_) | RegMem(_), Reg(r) | FixedReg(r)) => {
480 assert!(matches!(r.reg_class().unwrap(), RegClass::Gpr));
481 vec![IsleConstructor::RetGpr]
482 }
483 other => panic!("unsupported number of write operands {other:?}"),
484 }
485 }
486 [one, two, three] => {
487 assert!(!format.eflags.is_read());
488 match (
489 one.location.kind(),
490 two.location.kind(),
491 three.location.kind(),
492 ) {
493 (FixedReg(_), FixedReg(_), Mem(_)) => {
494 vec![IsleConstructor::RetValueRegs]
495 }
496 other => panic!("unsupported number of write operands {other:?}"),
497 }
498 }
499
500 other => panic!("unsupported number of write operands {other:?}"),
501 }
502}
503
504fn generate_isle_inst_decls(f: &mut Formatter, inst: &Inst) {
533 let (trap_type, trap_name) = if inst.has_trap {
534 (Some("TrapCode".to_string()), Some("trap".to_string()))
535 } else {
536 (None, None)
537 };
538
539 let struct_name = inst.name();
544 let raw_name = format!("x64_{struct_name}_raw");
545 let params = inst
546 .format
547 .operands
548 .iter()
549 .filter(|o| is_raw_operand_param(o))
550 .collect::<Vec<_>>();
551 let raw_param_tys = params
552 .iter()
553 .map(|o| isle_param_raw(o))
554 .chain(trap_type.clone())
555 .collect::<Vec<_>>()
556 .join(" ");
557 fmtln!(f, "(decl {raw_name} ({raw_param_tys}) AssemblerOutputs)");
558 fmtln!(f, "(extern constructor {raw_name} {raw_name})");
559
560 for ctor in isle_constructors(&inst.format) {
574 let suffix = ctor.suffix();
575 let rule_name = format!("x64_{struct_name}{suffix}");
576 let result_ty = ctor.result_ty();
577 let mut explicit_params = Vec::new();
578 let mut implicit_params = Vec::new();
579 for param in params.iter() {
580 if param.mutability.is_read() || ctor.includes_write_only_reg_mem() {
581 explicit_params.push(param);
582 } else {
583 implicit_params.push(param);
584 }
585 }
586 assert!(implicit_params.len() <= 1);
587 let param_tys = explicit_params
588 .iter()
589 .map(|o| isle_param_for_ctor(o, ctor))
590 .chain(trap_type.clone())
591 .collect::<Vec<_>>()
592 .join(" ");
593 let param_names = explicit_params
594 .iter()
595 .map(|o| o.location.to_string())
596 .chain(trap_name.clone())
597 .collect::<Vec<_>>()
598 .join(" ");
599 let convert = ctor.conversion_constructor();
600
601 let implicit_params = implicit_params
605 .iter()
606 .map(|o| {
607 assert!(matches!(o.location.kind(), OperandKind::RegMem(_)));
608 match ctor {
609 IsleConstructor::RetMemorySideEffect | IsleConstructor::NoReturnSideEffect => {
610 unreachable!()
611 }
612 IsleConstructor::RetGpr | IsleConstructor::ConsumesFlagsReturnsGpr => {
613 "(temp_writable_gpr)"
614 }
615 IsleConstructor::RetXmm => "(temp_writable_xmm)",
616 IsleConstructor::RetValueRegs | IsleConstructor::ProducesFlagsSideEffect => {
617 todo!()
618 }
619 }
620 })
621 .collect::<Vec<_>>()
622 .join(" ");
623
624 fmtln!(f, "(decl {rule_name} ({param_tys}) {result_ty})");
625 fmtln!(
626 f,
627 "(rule ({rule_name} {param_names}) ({convert} ({raw_name} {implicit_params} {param_names})))"
628 );
629
630 if let Some(alternate) = &inst.alternate {
631 assert!(
636 inst.format
637 .operands
638 .iter()
639 .any(|o| matches!(o.location.reg_class(), Some(RegClass::Xmm)))
640 );
641 let param_tys = if alternate.feature == Feature::avx {
642 param_tys.replace("Aligned", "")
643 } else {
644 param_tys
645 };
646 let alt_feature = alternate.feature.to_string();
647 let alt_name = &alternate.name;
648 let rule_name_or_feat = format!("{rule_name}_or_{alt_feature}");
649 fmtln!(f, "(decl {rule_name_or_feat} ({param_tys}) {result_ty})");
650 fmtln!(f, "(rule 1 ({rule_name_or_feat} {param_names})");
651 f.indent(|f| {
652 fmtln!(f, "(if-let true (has_{alt_feature}))");
653 fmtln!(f, "(x64_{alt_name}{suffix} {param_names}))");
654 });
655 fmtln!(
656 f,
657 "(rule 0 ({rule_name_or_feat} {param_names}) ({rule_name} {param_names}))"
658 );
659 }
660 }
661}
662
663pub fn generate_isle(f: &mut Formatter, insts: &[Inst]) {
666 fmtln!(f, "(type AssemblerOutputs (enum");
667 fmtln!(f, " ;; Used for instructions that have ISLE");
668 fmtln!(f, " ;; `SideEffect`s (memory stores, traps,");
669 fmtln!(f, " ;; etc.) and do not return a `Value`.");
670 fmtln!(f, " (SideEffect (inst MInst))");
671 fmtln!(f, " ;; Used for instructions that return a");
672 fmtln!(f, " ;; GPR (including `GprMem` variants with");
673 fmtln!(f, " ;; a GPR as the first argument).");
674 fmtln!(f, " (RetGpr (inst MInst) (gpr Gpr))");
675 fmtln!(f, " ;; Used for instructions that return an");
676 fmtln!(f, " ;; XMM register.");
677 fmtln!(f, " (RetXmm (inst MInst) (xmm Xmm))");
678 fmtln!(f, " ;; Used for multi-return instructions.");
679 fmtln!(f, " (RetValueRegs (inst MInst) (regs ValueRegs))");
680 fmtln!(
681 f,
682 " ;; https://github.com/bytecodealliance/wasmtime/pull/10276"
683 );
684 fmtln!(f, "))");
685 f.empty_line();
686
687 fmtln!(f, ";; Directly emit instructions that return a GPR.");
688 fmtln!(f, "(decl emit_ret_gpr (AssemblerOutputs) Gpr)");
689 fmtln!(f, "(rule (emit_ret_gpr (AssemblerOutputs.RetGpr inst gpr))");
690 fmtln!(f, " (let ((_ Unit (emit inst))) gpr))");
691 f.empty_line();
692
693 fmtln!(f, ";; Directly emit instructions that return an");
694 fmtln!(f, ";; XMM register.");
695 fmtln!(f, "(decl emit_ret_xmm (AssemblerOutputs) Xmm)");
696 fmtln!(f, "(rule (emit_ret_xmm (AssemblerOutputs.RetXmm inst xmm))");
697 fmtln!(f, " (let ((_ Unit (emit inst))) xmm))");
698 f.empty_line();
699
700 fmtln!(f, ";; Directly emit instructions that return multiple");
701 fmtln!(f, ";; registers (e.g. `mul`).");
702 fmtln!(f, "(decl emit_ret_value_regs (AssemblerOutputs) ValueRegs)");
703 fmtln!(
704 f,
705 "(rule (emit_ret_value_regs (AssemblerOutputs.RetValueRegs inst regs))"
706 );
707 fmtln!(f, " (let ((_ Unit (emit inst))) regs))");
708 f.empty_line();
709
710 fmtln!(f, ";; Pass along the side-effecting instruction");
711 fmtln!(f, ";; for later emission.");
712 fmtln!(
713 f,
714 "(decl defer_side_effect (AssemblerOutputs) SideEffectNoResult)"
715 );
716 fmtln!(
717 f,
718 "(rule (defer_side_effect (AssemblerOutputs.SideEffect inst))"
719 );
720 fmtln!(f, " (SideEffectNoResult.Inst inst))");
721 f.empty_line();
722
723 for inst in insts {
724 if include_inst(inst) {
725 generate_isle_inst_decls(f, inst);
726 f.empty_line();
727 }
728 }
729}
730
731fn is_raw_operand_param(o: &Operand) -> bool {
741 o.mutability.is_read()
742 || matches!(
743 o.location.kind(),
744 OperandKind::RegMem(_) | OperandKind::Mem(_)
745 )
746}