1use cranelift_assembler_x64_meta::dsl::{Format, Inst, Mutability, Operand, OperandKind};
4use cranelift_srcgen::{fmtln, Formatter};
5
6pub fn rust_param_raw(op: &Operand) -> String {
8 match op.location.kind() {
9 OperandKind::Imm(loc) => {
10 let bits = loc.bits();
11 if op.extension.is_sign_extended() {
12 format!("i{bits}")
13 } else {
14 format!("u{bits}")
15 }
16 }
17 OperandKind::RegMem(rm) => {
18 let reg = rm.reg_class().unwrap();
19 let aligned = if op.align { "Aligned" } else { "" };
20 format!("&{reg}Mem{aligned}")
21 }
22 OperandKind::Mem(_) => {
23 format!("&Amode")
24 }
25 OperandKind::Reg(r) | OperandKind::FixedReg(r) => r.reg_class().unwrap().to_string(),
26 }
27}
28
29pub fn rust_convert_isle_to_assembler(op: &Operand) -> String {
33 match op.location.kind() {
34 OperandKind::Imm(loc) => {
35 let bits = loc.bits();
36 let ty = if op.extension.is_sign_extended() {
37 "Simm"
38 } else {
39 "Imm"
40 };
41 format!("cranelift_assembler_x64::{ty}{bits}::new")
42 }
43 OperandKind::FixedReg(r) => {
44 let reg = r.reg_class().unwrap().to_string().to_lowercase();
45 match op.mutability {
46 Mutability::Read => "cranelift_assembler_x64::Fixed".to_string(),
47 Mutability::ReadWrite => {
48 format!("self.convert_{reg}_to_assembler_fixed_read_write_{reg}")
49 }
50 }
51 }
52 OperandKind::Reg(r) => {
53 let reg = r.reg_class().unwrap();
54 let reg_lower = reg.to_string().to_lowercase();
55 match op.mutability {
56 Mutability::Read => format!("cranelift_assembler_x64::{reg}::new"),
57 Mutability::ReadWrite => {
58 format!("self.convert_{reg_lower}_to_assembler_read_write_{reg_lower}")
59 }
60 }
61 }
62 OperandKind::RegMem(r) => {
63 let reg = r.reg_class().unwrap().to_string().to_lowercase();
64 let mut_ = op.mutability.generate_snake_case();
65 let align = if op.align { "_aligned" } else { "" };
66 format!("self.convert_{reg}_mem_to_assembler_{mut_}_{reg}_mem{align}")
67 }
68 OperandKind::Mem(_) => "self.convert_amode_to_assembler_amode".to_string(),
69 }
70}
71
72pub fn generate_macro_inst_fn(f: &mut Formatter, inst: &Inst) {
78 let struct_name = inst.name();
79 let params = inst
80 .format
81 .operands
82 .iter()
83 .filter(|o| o.mutability.is_read())
84 .collect::<Vec<_>>();
85 let results = inst
86 .format
87 .operands
88 .iter()
89 .filter(|o| o.mutability.is_write())
90 .collect::<Vec<_>>();
91 let rust_params = params
92 .iter()
93 .map(|o| format!("{}: {}", o.location, rust_param_raw(o)))
94 .collect::<Vec<_>>()
95 .join(", ");
96 f.add_block(
97 &format!("fn x64_{struct_name}_raw(&mut self, {rust_params}) -> AssemblerOutputs"),
98 |f| {
99 for o in params.iter() {
100 let l = o.location;
101 let cvt = rust_convert_isle_to_assembler(o);
102 fmtln!(f, "let {l} = {cvt}({l});");
103 }
104 let args = params
105 .iter()
106 .map(|o| format!("{}.clone()", o.location))
107 .collect::<Vec<_>>();
108 let args = args.join(", ");
109 fmtln!(
110 f,
111 "let inst = cranelift_assembler_x64::inst::{struct_name}::new({args}).into();"
112 );
113 fmtln!(f, "let inst = MInst::External {{ inst }};");
114
115 use cranelift_assembler_x64_meta::dsl::Mutability::*;
116 match results.as_slice() {
117 [] => fmtln!(f, "SideEffectNoResult::Inst(inst)"),
118 [one] => match one.mutability {
119 Read => unreachable!(),
120 ReadWrite => match one.location.kind() {
121 OperandKind::Imm(_) => unreachable!(),
122 OperandKind::Reg(r) | OperandKind::FixedReg(r) => {
125 let ty = r.reg_class().unwrap().to_string();
126 let var = ty.to_lowercase();
127 fmtln!(f, "let {var} = {r}.as_ref().write.to_reg();",);
128 fmtln!(f, "AssemblerOutputs::Ret{ty} {{ inst, {var} }}");
129 }
130 OperandKind::Mem(_) => {
132 fmtln!(f, "AssemblerOutputs::SideEffect {{ inst }}")
133 }
134 OperandKind::RegMem(rm) => {
138 assert_eq!(results.len(), 1);
139 let ty = rm.reg_class().unwrap().to_string();
140 let var = ty.to_lowercase();
141 f.add_block(&format!("match {rm}"), |f| {
142 f.add_block(&format!("asm::{ty}Mem::{ty}(reg) => "), |f| {
143 fmtln!(f, "let {var} = reg.write.to_reg();");
144 fmtln!(f, "AssemblerOutputs::Ret{ty} {{ inst, {var} }} ");
145 });
146 f.add_block(&format!("asm::{ty}Mem::Mem(_) => "), |f| {
147 fmtln!(f, "AssemblerOutputs::SideEffect {{ inst }} ");
148 });
149 });
150 }
151 },
152 },
153 _ => panic!("instruction has more than one result"),
154 }
155 },
156 );
157}
158
159pub fn generate_rust_macro(f: &mut Formatter, insts: &[Inst]) {
161 fmtln!(f, "#[doc(hidden)]");
162 fmtln!(f, "macro_rules! isle_assembler_methods {{");
163 f.indent(|f| {
164 fmtln!(f, "() => {{");
165 f.indent(|f| {
166 for inst in insts {
167 generate_macro_inst_fn(f, inst);
168 }
169 });
170 fmtln!(f, "}};");
171 });
172 fmtln!(f, "}}");
173}
174
175pub fn isle_param_raw(op: &Operand) -> String {
178 match op.location.kind() {
179 OperandKind::Imm(loc) => {
180 let bits = loc.bits();
181 if op.extension.is_sign_extended() {
182 format!("i{bits}")
183 } else {
184 format!("u{bits}")
185 }
186 }
187 OperandKind::Reg(r) | OperandKind::FixedReg(r) => r.reg_class().unwrap().to_string(),
188 OperandKind::Mem(_) => {
189 if op.align {
190 unimplemented!("no way yet to mark an Amode as aligned")
191 } else {
192 "Amode".to_string()
193 }
194 }
195 OperandKind::RegMem(rm) => {
196 let reg = rm.reg_class().unwrap();
197 let aligned = if op.align { "Aligned" } else { "" };
198 format!("{reg}Mem{aligned}")
199 }
200 }
201}
202
203#[derive(Copy, Clone, Debug)]
211pub enum IsleConstructor {
212 RetMemorySideEffect,
216
217 RetGpr,
220
221 RetXmm,
224}
225
226impl IsleConstructor {
227 pub fn result_ty(&self) -> &'static str {
229 match self {
230 IsleConstructor::RetMemorySideEffect => "SideEffectNoResult",
231 IsleConstructor::RetGpr => "Gpr",
232 IsleConstructor::RetXmm => "Xmm",
233 }
234 }
235
236 pub fn conversion_constructor(&self) -> &'static str {
239 match self {
240 IsleConstructor::RetMemorySideEffect => "defer_side_effect",
241 IsleConstructor::RetGpr => "emit_ret_gpr",
242 IsleConstructor::RetXmm => "emit_ret_xmm",
243 }
244 }
245
246 pub fn suffix(&self) -> &'static str {
248 match self {
249 IsleConstructor::RetMemorySideEffect => "_mem",
250 IsleConstructor::RetGpr => "",
251 IsleConstructor::RetXmm => "",
252 }
253 }
254}
255
256pub fn isle_param_for_ctor(op: &Operand, ctor: IsleConstructor) -> String {
259 match op.location.kind() {
260 OperandKind::RegMem(_) if op.mutability.is_write() => match ctor {
265 IsleConstructor::RetMemorySideEffect => "Amode".to_string(),
266 IsleConstructor::RetGpr => "Gpr".to_string(),
267 IsleConstructor::RetXmm => "Xmm".to_string(),
268 },
269
270 _ => isle_param_raw(op),
272 }
273}
274
275pub fn isle_constructors(format: &Format) -> Vec<IsleConstructor> {
281 use Mutability::*;
282 use OperandKind::*;
283
284 let write_operands = format
285 .operands
286 .iter()
287 .filter(|o| o.mutability.is_write())
288 .collect::<Vec<_>>();
289 match &write_operands[..] {
290 [] => unimplemented!("if you truly need this (and not a `SideEffect*`), add a `NoReturn` variant to `AssemblerOutputs`"),
291 [one] => match one.mutability {
292 Read => unreachable!(),
293 ReadWrite => match one.location.kind() {
294 Imm(_) => unreachable!(),
295 Reg(r) | FixedReg(r) => match r.bits() {
298 128 => vec![IsleConstructor::RetXmm],
299 _ => vec![IsleConstructor::RetGpr],
300 },
301 Mem(_) => vec![IsleConstructor::RetMemorySideEffect],
303 RegMem(rm) => match rm.bits() {
306 128 => vec![IsleConstructor::RetXmm, IsleConstructor::RetMemorySideEffect],
307 _ => vec![IsleConstructor::RetGpr, IsleConstructor::RetMemorySideEffect],
308 },
309 }
310 },
311 other => panic!("unsupported number of write operands {other:?}"),
312 }
313}
314
315pub fn generate_isle_inst_decls(f: &mut Formatter, inst: &Inst) {
344 let struct_name = inst.name();
349 let raw_name = format!("x64_{struct_name}_raw");
350 let params = inst
351 .format
352 .operands
353 .iter()
354 .filter(|o| o.mutability.is_read())
355 .collect::<Vec<_>>();
356 let raw_param_tys = params
357 .iter()
358 .map(|o| isle_param_raw(o))
359 .collect::<Vec<_>>()
360 .join(" ");
361 fmtln!(f, "(decl {raw_name} ({raw_param_tys}) AssemblerOutputs)");
362 fmtln!(f, "(extern constructor {raw_name} {raw_name})");
363
364 for ctor in isle_constructors(&inst.format) {
372 let suffix = ctor.suffix();
373 let rule_name = format!("x64_{struct_name}{suffix}");
374 let result_ty = ctor.result_ty();
375 let param_tys = params
376 .iter()
377 .map(|o| isle_param_for_ctor(o, ctor))
378 .collect::<Vec<_>>()
379 .join(" ");
380 let param_names = params
381 .iter()
382 .map(|o| o.location.to_string())
383 .collect::<Vec<_>>()
384 .join(" ");
385 let convert = ctor.conversion_constructor();
386
387 fmtln!(f, "(decl {rule_name} ({param_tys}) {result_ty})");
388 fmtln!(
389 f,
390 "(rule ({rule_name} {param_names}) ({convert} ({raw_name} {param_names})))"
391 );
392 }
393}
394
395pub fn generate_isle(f: &mut Formatter, insts: &[Inst]) {
398 fmtln!(f, "(type AssemblerOutputs (enum");
399 fmtln!(f, " ;; Used for instructions that have ISLE");
400 fmtln!(f, " ;; `SideEffect`s (memory stores, traps,");
401 fmtln!(f, " ;; etc.) and do not return a `Value`.");
402 fmtln!(f, " (SideEffect (inst MInst))");
403 fmtln!(f, " ;; Used for instructions that return a");
404 fmtln!(f, " ;; GPR (including `GprMem` variants with");
405 fmtln!(f, " ;; a GPR as the first argument).");
406 fmtln!(f, " (RetGpr (inst MInst) (gpr Gpr))");
407 fmtln!(f, " ;; Used for instructions that return an");
408 fmtln!(f, " ;; XMM register.");
409 fmtln!(f, " (RetXmm (inst MInst) (xmm Xmm))");
410 fmtln!(f, " ;; TODO: eventually add more variants for");
411 fmtln!(f, " ;; multi-return, XMM, etc.; see");
412 fmtln!(
413 f,
414 " ;; https://github.com/bytecodealliance/wasmtime/pull/10276"
415 );
416 fmtln!(f, "))");
417 f.empty_line();
418
419 fmtln!(f, ";; Directly emit instructions that return a GPR.");
420 fmtln!(f, "(decl emit_ret_gpr (AssemblerOutputs) Gpr)");
421 fmtln!(f, "(rule (emit_ret_gpr (AssemblerOutputs.RetGpr inst gpr))");
422 fmtln!(f, " (let ((_ Unit (emit inst))) gpr))");
423 f.empty_line();
424
425 fmtln!(f, ";; Directly emit instructions that return an");
426 fmtln!(f, ";; XMM register.");
427 fmtln!(f, "(decl emit_ret_xmm (AssemblerOutputs) Xmm)");
428 fmtln!(f, "(rule (emit_ret_xmm (AssemblerOutputs.RetXmm inst xmm))");
429 fmtln!(f, " (let ((_ Unit (emit inst))) xmm))");
430 f.empty_line();
431
432 fmtln!(f, ";; Pass along the side-effecting instruction");
433 fmtln!(f, ";; for later emission.");
434 fmtln!(
435 f,
436 "(decl defer_side_effect (AssemblerOutputs) SideEffectNoResult)"
437 );
438 fmtln!(
439 f,
440 "(rule (defer_side_effect (AssemblerOutputs.SideEffect inst))"
441 );
442 fmtln!(f, " (SideEffectNoResult.Inst inst))");
443 f.empty_line();
444
445 for inst in insts {
446 generate_isle_inst_decls(f, inst);
447 f.empty_line();
448 }
449}