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 {
23 match op.location.kind() {
24 OperandKind::Imm(loc) => {
25 let bits = loc.bits();
26 if op.extension.is_sign_extended() {
27 format!("i{bits}")
28 } else {
29 format!("u{bits}")
30 }
31 }
32 OperandKind::RegMem(rm) => {
33 let reg = rm.reg_class().unwrap();
34 let aligned = if op.align { "Aligned" } else { "" };
35 format!("&{reg}Mem{aligned}")
36 }
37 OperandKind::Mem(_) => {
38 format!("&SyntheticAmode")
39 }
40 OperandKind::Reg(r) | OperandKind::FixedReg(r) => r.reg_class().unwrap().to_string(),
41 }
42}
43
44fn rust_convert_isle_to_assembler(op: &Operand) -> String {
48 match op.location.kind() {
49 OperandKind::Imm(loc) => {
50 let bits = loc.bits();
51 let ty = if op.extension.is_sign_extended() {
52 "Simm"
53 } else {
54 "Imm"
55 };
56 format!("{ASM}::{ty}{bits}::new({loc})")
57 }
58 OperandKind::FixedReg(r) => {
59 let reg = r.reg_class().unwrap().to_string().to_lowercase();
60 match op.mutability {
61 Mutability::Read => format!("{ASM}::Fixed({r})"),
62 Mutability::Write => {
63 format!("{ASM}::Fixed(self.temp_writable_{reg}())")
64 }
65 Mutability::ReadWrite => {
66 format!("self.convert_{reg}_to_assembler_fixed_read_write_{reg}({r})")
67 }
68 }
69 }
70 OperandKind::Reg(r) => {
71 let reg = r.reg_class().unwrap();
72 let reg_lower = reg.to_string().to_lowercase();
73 match op.mutability {
74 Mutability::Read => {
75 format!("{ASM}::{reg}::new({r})")
76 }
77 Mutability::Write => {
78 format!("{ASM}::{reg}::new(self.temp_writable_{reg_lower}())")
79 }
80 Mutability::ReadWrite => {
81 format!("self.convert_{reg_lower}_to_assembler_read_write_{reg_lower}({r})")
82 }
83 }
84 }
85 OperandKind::RegMem(rm) => {
86 let reg = rm.reg_class().unwrap().to_string().to_lowercase();
87 let mut_ = op.mutability.generate_snake_case();
88 let align = if op.align { "_aligned" } else { "" };
89 format!("self.convert_{reg}_mem_to_assembler_{mut_}_{reg}_mem{align}({rm})")
90 }
91 OperandKind::Mem(mem) => format!("self.convert_amode_to_assembler_amode({mem})"),
92 }
93}
94
95fn generate_macro_inst_fn(f: &mut Formatter, inst: &Inst) {
101 use OperandKind::*;
102
103 let struct_name = inst.name();
104 let operands = inst.format.operands.iter().cloned().collect::<Vec<_>>();
105 let results = operands
106 .iter()
107 .filter(|o| o.mutability.is_write())
108 .collect::<Vec<_>>();
109 let rust_params = operands
110 .iter()
111 .filter(|o| is_raw_operand_param(o))
112 .map(|o| format!("{}: {}", o.location, rust_param_raw(o)))
113 .chain(if inst.has_trap {
114 Some(format!("trap: &TrapCode"))
115 } else {
116 None
117 })
118 .collect::<Vec<_>>()
119 .join(", ");
120 f.add_block(
121 &format!("fn x64_{struct_name}_raw(&mut self, {rust_params}) -> AssemblerOutputs"),
122 |f| {
123 f.comment("Convert ISLE types to assembler types.");
124 for op in operands.iter() {
125 let loc = op.location;
126 let cvt = rust_convert_isle_to_assembler(op);
127 fmtln!(f, "let {loc} = {cvt};");
128 }
129 let mut args = operands
130 .iter()
131 .map(|o| format!("{}.clone()", o.location))
132 .collect::<Vec<_>>();
133 if inst.has_trap {
134 args.push(format!("{ASM}::TrapCode(trap.as_raw())"));
135 }
136 let args = args.join(", ");
137 f.empty_line();
138
139 f.comment("Build the instruction.");
140 fmtln!(
141 f,
142 "let inst = {ASM}::inst::{struct_name}::new({args}).into();"
143 );
144 fmtln!(f, "let inst = MInst::External {{ inst }};");
145 f.empty_line();
146
147 f.comment("Return a type ISLE can work with.");
154 let access_reg = |op: &Operand| match op.mutability {
155 Mutability::Read => unreachable!(),
156 Mutability::Write => "to_reg()",
157 Mutability::ReadWrite => "write.to_reg()",
158 };
159 let ty_var_of_reg = |loc: Location| {
160 let ty = loc.reg_class().unwrap().to_string();
161 let var = ty.to_lowercase();
162 (ty, var)
163 };
164 match results.as_slice() {
165 [] => fmtln!(f, "AssemblerOutputs::SideEffect {{ inst }}"),
166 [op] => match op.location.kind() {
167 Imm(_) => unreachable!(),
168 Reg(r) | FixedReg(r) => {
169 let (ty, var) = ty_var_of_reg(r);
170 fmtln!(f, "let {var} = {r}.as_ref().{};", access_reg(op));
171 fmtln!(f, "AssemblerOutputs::Ret{ty} {{ inst, {var} }}");
172 }
173 Mem(_) => {
174 fmtln!(f, "AssemblerOutputs::SideEffect {{ inst }}")
175 }
176 RegMem(rm) => {
177 let (ty, var) = ty_var_of_reg(rm);
178 f.add_block(&format!("match {rm}"), |f| {
179 f.add_block(&format!("{ASM}::{ty}Mem::{ty}(reg) => "), |f| {
180 fmtln!(f, "let {var} = reg.{};", access_reg(op));
181 fmtln!(f, "AssemblerOutputs::Ret{ty} {{ inst, {var} }} ");
182 });
183 f.add_block(&format!("{ASM}::{ty}Mem::Mem(_) => "), |f| {
184 fmtln!(f, "AssemblerOutputs::SideEffect {{ inst }} ");
185 });
186 });
187 }
188 },
189 [op1, op2] => match (op1.location.kind(), op2.location.kind()) {
193 (FixedReg(loc1) | Reg(loc1), FixedReg(loc2) | Reg(loc2)) => {
194 fmtln!(f, "let one = {loc1}.as_ref().{}.to_reg();", access_reg(op1));
195 fmtln!(f, "let two = {loc2}.as_ref().{}.to_reg();", access_reg(op2));
196 fmtln!(f, "let regs = ValueRegs::two(one, two);");
197 fmtln!(f, "AssemblerOutputs::RetValueRegs {{ inst, regs }}");
198 }
199 (Reg(reg), Mem(_)) | (Mem(_) | RegMem(_), Reg(reg) | FixedReg(reg)) => {
200 let (ty, var) = ty_var_of_reg(reg);
201 fmtln!(f, "let {var} = {reg}.as_ref().{};", access_reg(op2));
202 fmtln!(f, "AssemblerOutputs::Ret{ty} {{ inst, {var} }}");
203 }
204 _ => unimplemented!("unhandled results: {results:?}"),
205 },
206
207 [op1, op2, op3] => match (
208 op1.location.kind(),
209 op2.location.kind(),
210 op3.location.kind(),
211 ) {
212 (FixedReg(loc1), FixedReg(loc2), Mem(_)) => {
213 fmtln!(f, "let one = {loc1}.as_ref().{}.to_reg();", access_reg(op1));
214 fmtln!(f, "let two = {loc2}.as_ref().{}.to_reg();", access_reg(op2));
215 fmtln!(f, "let regs = ValueRegs::two(one, two);");
216 fmtln!(f, "AssemblerOutputs::RetValueRegs {{ inst, regs }}");
217 }
218 _ => unimplemented!("unhandled results: {results:?}"),
219 },
220
221 _ => panic!("instruction has more than one result"),
222 }
223 },
224 );
225}
226
227pub fn generate_rust_macro(f: &mut Formatter, insts: &[Inst]) {
229 fmtln!(f, "#[doc(hidden)]");
230 fmtln!(f, "macro_rules! isle_assembler_methods {{");
231 f.indent(|f| {
232 fmtln!(f, "() => {{");
233 f.indent(|f| {
234 for inst in insts {
235 if include_inst(inst) {
236 generate_macro_inst_fn(f, inst);
237 }
238 }
239 });
240 fmtln!(f, "}};");
241 });
242 fmtln!(f, "}}");
243}
244
245fn isle_param_raw(op: &Operand) -> String {
248 match op.location.kind() {
249 OperandKind::Imm(loc) => {
250 let bits = loc.bits();
251 if op.extension.is_sign_extended() {
252 format!("i{bits}")
253 } else {
254 format!("u{bits}")
255 }
256 }
257 OperandKind::Reg(r) | OperandKind::FixedReg(r) => r.reg_class().unwrap().to_string(),
258 OperandKind::Mem(_) => {
259 if op.align {
260 unimplemented!("no way yet to mark an SyntheticAmode as aligned")
261 } else {
262 "SyntheticAmode".to_string()
263 }
264 }
265 OperandKind::RegMem(rm) => {
266 let reg = rm.reg_class().unwrap();
267 let aligned = if op.align { "Aligned" } else { "" };
268 format!("{reg}Mem{aligned}")
269 }
270 }
271}
272
273#[derive(Copy, Clone, Debug)]
281enum IsleConstructor {
282 RetMemorySideEffect,
286
287 RetGpr,
290
291 RetXmm,
293
294 RetValueRegs,
297
298 NoReturnSideEffect,
300
301 ProducesFlagsSideEffect,
304
305 ConsumesFlagsReturnsGpr,
308}
309
310impl IsleConstructor {
311 fn result_ty(&self) -> &'static str {
313 match self {
314 IsleConstructor::RetGpr => "Gpr",
315 IsleConstructor::RetXmm => "Xmm",
316 IsleConstructor::RetValueRegs => "ValueRegs",
317 IsleConstructor::NoReturnSideEffect | IsleConstructor::RetMemorySideEffect => {
318 "SideEffectNoResult"
319 }
320 IsleConstructor::ProducesFlagsSideEffect => "ProducesFlags",
321 IsleConstructor::ConsumesFlagsReturnsGpr => "ConsumesFlags",
322 }
323 }
324
325 fn conversion_constructor(&self) -> &'static str {
328 match self {
329 IsleConstructor::NoReturnSideEffect | IsleConstructor::RetMemorySideEffect => {
330 "defer_side_effect"
331 }
332 IsleConstructor::RetGpr => "emit_ret_gpr",
333 IsleConstructor::RetXmm => "emit_ret_xmm",
334 IsleConstructor::RetValueRegs => "emit_ret_value_regs",
335 IsleConstructor::ProducesFlagsSideEffect => "asm_produce_flags_side_effect",
336 IsleConstructor::ConsumesFlagsReturnsGpr => "asm_consumes_flags_returns_gpr",
337 }
338 }
339
340 fn suffix(&self) -> &'static str {
342 match self {
343 IsleConstructor::RetMemorySideEffect => "_mem",
344 IsleConstructor::RetGpr
345 | IsleConstructor::RetXmm
346 | IsleConstructor::RetValueRegs
347 | IsleConstructor::NoReturnSideEffect
348 | IsleConstructor::ProducesFlagsSideEffect
349 | IsleConstructor::ConsumesFlagsReturnsGpr => "",
350 }
351 }
352
353 fn includes_write_only_reg_mem(&self) -> bool {
359 match self {
360 IsleConstructor::RetMemorySideEffect => true,
361 IsleConstructor::RetGpr
362 | IsleConstructor::RetXmm
363 | IsleConstructor::RetValueRegs
364 | IsleConstructor::NoReturnSideEffect
365 | IsleConstructor::ProducesFlagsSideEffect
366 | IsleConstructor::ConsumesFlagsReturnsGpr => false,
367 }
368 }
369}
370
371fn isle_param_for_ctor(op: &Operand, ctor: IsleConstructor) -> String {
374 match op.location.kind() {
375 OperandKind::RegMem(_) if op.mutability.is_write() => match ctor {
380 IsleConstructor::RetMemorySideEffect => "SyntheticAmode".to_string(),
381 IsleConstructor::NoReturnSideEffect => "".to_string(),
382 IsleConstructor::RetGpr | IsleConstructor::ConsumesFlagsReturnsGpr => "Gpr".to_string(),
383 IsleConstructor::RetXmm => "Xmm".to_string(),
384 IsleConstructor::RetValueRegs => "ValueRegs".to_string(),
385 IsleConstructor::ProducesFlagsSideEffect => todo!(),
386 },
387
388 _ => isle_param_raw(op),
390 }
391}
392
393fn isle_constructors(format: &Format) -> Vec<IsleConstructor> {
399 use Mutability::*;
400 use OperandKind::*;
401
402 let write_operands = format
403 .operands
404 .iter()
405 .filter(|o| o.mutability.is_write())
406 .collect::<Vec<_>>();
407 match &write_operands[..] {
408 [] => {
409 if format.eflags.is_write() {
410 vec![IsleConstructor::ProducesFlagsSideEffect]
411 } else {
412 vec![IsleConstructor::NoReturnSideEffect]
413 }
414 }
415 [one] => match one.mutability {
416 Read => unreachable!(),
417 ReadWrite | Write => match one.location.kind() {
418 Imm(_) => unreachable!(),
419 Reg(r) | FixedReg(r) => match r.reg_class().unwrap() {
422 RegClass::Xmm => {
423 assert!(!format.eflags.is_read());
424 vec![IsleConstructor::RetXmm]
425 }
426 RegClass::Gpr => {
427 if format.eflags.is_read() {
428 vec![IsleConstructor::ConsumesFlagsReturnsGpr]
429 } else {
430 vec![IsleConstructor::RetGpr]
431 }
432 }
433 },
434 Mem(_) => {
436 assert!(!format.eflags.is_read());
437 vec![IsleConstructor::RetMemorySideEffect]
438 }
439 RegMem(rm) => match rm.reg_class().unwrap() {
442 RegClass::Xmm => {
443 assert!(!format.eflags.is_read());
444 vec![
445 IsleConstructor::RetXmm,
446 IsleConstructor::RetMemorySideEffect,
447 ]
448 }
449 RegClass::Gpr => {
450 if format.eflags.is_read() {
451 vec![IsleConstructor::ConsumesFlagsReturnsGpr]
457 } else {
458 vec![
459 IsleConstructor::RetGpr,
460 IsleConstructor::RetMemorySideEffect,
461 ]
462 }
463 }
464 },
465 },
466 },
467 [one, two] => {
468 assert!(!format.eflags.is_read());
469 match (one.location.kind(), two.location.kind()) {
470 (FixedReg(_) | Reg(_), FixedReg(_) | Reg(_)) => {
471 vec![IsleConstructor::RetValueRegs]
472 }
473 (Reg(r), Mem(_)) | (Mem(_) | RegMem(_), Reg(r) | FixedReg(r)) => {
474 assert!(matches!(r.reg_class().unwrap(), RegClass::Gpr));
475 vec![IsleConstructor::RetGpr]
476 }
477 other => panic!("unsupported number of write operands {other:?}"),
478 }
479 }
480 [one, two, three] => {
481 assert!(!format.eflags.is_read());
482 match (
483 one.location.kind(),
484 two.location.kind(),
485 three.location.kind(),
486 ) {
487 (FixedReg(_), FixedReg(_), Mem(_)) => {
488 vec![IsleConstructor::RetValueRegs]
489 }
490 other => panic!("unsupported number of write operands {other:?}"),
491 }
492 }
493
494 other => panic!("unsupported number of write operands {other:?}"),
495 }
496}
497
498fn generate_isle_inst_decls(f: &mut Formatter, inst: &Inst) {
527 let (trap_type, trap_name) = if inst.has_trap {
528 (Some("TrapCode".to_string()), Some("trap".to_string()))
529 } else {
530 (None, None)
531 };
532
533 let struct_name = inst.name();
538 let raw_name = format!("x64_{struct_name}_raw");
539 let params = inst
540 .format
541 .operands
542 .iter()
543 .filter(|o| is_raw_operand_param(o))
544 .collect::<Vec<_>>();
545 let raw_param_tys = params
546 .iter()
547 .map(|o| isle_param_raw(o))
548 .chain(trap_type.clone())
549 .collect::<Vec<_>>()
550 .join(" ");
551 fmtln!(f, "(decl {raw_name} ({raw_param_tys}) AssemblerOutputs)");
552 fmtln!(f, "(extern constructor {raw_name} {raw_name})");
553
554 for ctor in isle_constructors(&inst.format) {
568 let suffix = ctor.suffix();
569 let rule_name = format!("x64_{struct_name}{suffix}");
570 let result_ty = ctor.result_ty();
571 let mut explicit_params = Vec::new();
572 let mut implicit_params = Vec::new();
573 for param in params.iter() {
574 if param.mutability.is_read() || ctor.includes_write_only_reg_mem() {
575 explicit_params.push(param);
576 } else {
577 implicit_params.push(param);
578 }
579 }
580 assert!(implicit_params.len() <= 1);
581 let param_tys = explicit_params
582 .iter()
583 .map(|o| isle_param_for_ctor(o, ctor))
584 .chain(trap_type.clone())
585 .collect::<Vec<_>>()
586 .join(" ");
587 let param_names = explicit_params
588 .iter()
589 .map(|o| o.location.to_string())
590 .chain(trap_name.clone())
591 .collect::<Vec<_>>()
592 .join(" ");
593 let convert = ctor.conversion_constructor();
594
595 let implicit_params = implicit_params
599 .iter()
600 .map(|o| {
601 assert!(matches!(o.location.kind(), OperandKind::RegMem(_)));
602 match ctor {
603 IsleConstructor::RetMemorySideEffect | IsleConstructor::NoReturnSideEffect => {
604 unreachable!()
605 }
606 IsleConstructor::RetGpr | IsleConstructor::ConsumesFlagsReturnsGpr => {
607 "(temp_writable_gpr)"
608 }
609 IsleConstructor::RetXmm => "(temp_writable_xmm)",
610 IsleConstructor::RetValueRegs | IsleConstructor::ProducesFlagsSideEffect => {
611 todo!()
612 }
613 }
614 })
615 .collect::<Vec<_>>()
616 .join(" ");
617
618 fmtln!(f, "(decl {rule_name} ({param_tys}) {result_ty})");
619 fmtln!(
620 f,
621 "(rule ({rule_name} {param_names}) ({convert} ({raw_name} {implicit_params} {param_names})))"
622 );
623
624 if let Some(alternate) = &inst.alternate {
625 assert!(
630 inst.format
631 .operands
632 .iter()
633 .any(|o| matches!(o.location.reg_class(), Some(RegClass::Xmm)))
634 );
635 let param_tys = if alternate.feature == Feature::avx {
636 param_tys.replace("Aligned", "")
637 } else {
638 param_tys
639 };
640 let alt_feature = alternate.feature.to_string();
641 let alt_name = &alternate.name;
642 let rule_name_or_feat = format!("{rule_name}_or_{alt_feature}");
643 fmtln!(f, "(decl {rule_name_or_feat} ({param_tys}) {result_ty})");
644 fmtln!(f, "(rule 1 ({rule_name_or_feat} {param_names})");
645 f.indent(|f| {
646 fmtln!(f, "(if-let true (use_{alt_feature}))");
647 fmtln!(f, "(x64_{alt_name}{suffix} {param_names}))");
648 });
649 fmtln!(
650 f,
651 "(rule 0 ({rule_name_or_feat} {param_names}) ({rule_name} {param_names}))"
652 );
653 }
654 }
655}
656
657pub fn generate_isle(f: &mut Formatter, insts: &[Inst]) {
660 fmtln!(f, "(type AssemblerOutputs (enum");
661 fmtln!(f, " ;; Used for instructions that have ISLE");
662 fmtln!(f, " ;; `SideEffect`s (memory stores, traps,");
663 fmtln!(f, " ;; etc.) and do not return a `Value`.");
664 fmtln!(f, " (SideEffect (inst MInst))");
665 fmtln!(f, " ;; Used for instructions that return a");
666 fmtln!(f, " ;; GPR (including `GprMem` variants with");
667 fmtln!(f, " ;; a GPR as the first argument).");
668 fmtln!(f, " (RetGpr (inst MInst) (gpr Gpr))");
669 fmtln!(f, " ;; Used for instructions that return an");
670 fmtln!(f, " ;; XMM register.");
671 fmtln!(f, " (RetXmm (inst MInst) (xmm Xmm))");
672 fmtln!(f, " ;; Used for multi-return instructions.");
673 fmtln!(f, " (RetValueRegs (inst MInst) (regs ValueRegs))");
674 fmtln!(
675 f,
676 " ;; https://github.com/bytecodealliance/wasmtime/pull/10276"
677 );
678 fmtln!(f, "))");
679 f.empty_line();
680
681 fmtln!(f, ";; Directly emit instructions that return a GPR.");
682 fmtln!(f, "(decl emit_ret_gpr (AssemblerOutputs) Gpr)");
683 fmtln!(f, "(rule (emit_ret_gpr (AssemblerOutputs.RetGpr inst gpr))");
684 fmtln!(f, " (let ((_ Unit (emit inst))) gpr))");
685 f.empty_line();
686
687 fmtln!(f, ";; Directly emit instructions that return an");
688 fmtln!(f, ";; XMM register.");
689 fmtln!(f, "(decl emit_ret_xmm (AssemblerOutputs) Xmm)");
690 fmtln!(f, "(rule (emit_ret_xmm (AssemblerOutputs.RetXmm inst xmm))");
691 fmtln!(f, " (let ((_ Unit (emit inst))) xmm))");
692 f.empty_line();
693
694 fmtln!(f, ";; Directly emit instructions that return multiple");
695 fmtln!(f, ";; registers (e.g. `mul`).");
696 fmtln!(f, "(decl emit_ret_value_regs (AssemblerOutputs) ValueRegs)");
697 fmtln!(
698 f,
699 "(rule (emit_ret_value_regs (AssemblerOutputs.RetValueRegs inst regs))"
700 );
701 fmtln!(f, " (let ((_ Unit (emit inst))) regs))");
702 f.empty_line();
703
704 fmtln!(f, ";; Pass along the side-effecting instruction");
705 fmtln!(f, ";; for later emission.");
706 fmtln!(
707 f,
708 "(decl defer_side_effect (AssemblerOutputs) SideEffectNoResult)"
709 );
710 fmtln!(
711 f,
712 "(rule (defer_side_effect (AssemblerOutputs.SideEffect inst))"
713 );
714 fmtln!(f, " (SideEffectNoResult.Inst inst))");
715 f.empty_line();
716
717 for inst in insts {
718 if include_inst(inst) {
719 generate_isle_inst_decls(f, inst);
720 f.empty_line();
721 }
722 }
723}
724
725fn is_raw_operand_param(o: &Operand) -> bool {
735 o.mutability.is_read()
736 || matches!(
737 o.location.kind(),
738 OperandKind::RegMem(_) | OperandKind::Mem(_)
739 )
740}