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) => match rm.bits() {
18 128 => "&XmmMem".to_string(),
19 _ => "&GprMem".to_string(),
20 },
21 OperandKind::Reg(r) => match r.bits() {
22 128 => "Xmm".to_string(),
23 _ => "Gpr".to_string(),
24 },
25 OperandKind::FixedReg(_) => "Gpr".to_string(),
26 }
27}
28
29pub fn rust_convert_isle_to_assembler(op: &Operand) -> Option<&'static str> {
33 match op.location.kind() {
34 OperandKind::Reg(r) => Some(match (r.bits(), op.mutability) {
35 (128, Mutability::Read) => "cranelift_assembler_x64::Xmm::new",
36 (128, Mutability::ReadWrite) => "self.convert_xmm_to_assembler_read_write_xmm",
37 (_, Mutability::Read) => "cranelift_assembler_x64::Gpr::new",
38 (_, Mutability::ReadWrite) => "self.convert_gpr_to_assembler_read_write_gpr",
39 }),
40 OperandKind::RegMem(r) => Some(match (r.bits(), op.mutability) {
41 (128, Mutability::Read) => "self.convert_xmm_mem_to_assembler_read_xmm_mem",
42 (128, Mutability::ReadWrite) => "self.convert_xmm_mem_to_assembler_read_write_xmm_mem",
43 (_, Mutability::Read) => "self.convert_gpr_mem_to_assembler_read_gpr_mem",
44 (_, Mutability::ReadWrite) => "self.convert_gpr_mem_to_assembler_read_write_gpr_mem",
45 }),
46 OperandKind::Imm(loc) => match (op.extension.is_sign_extended(), loc.bits()) {
47 (true, 8) => Some("cranelift_assembler_x64::Simm8::new"),
48 (true, 16) => Some("cranelift_assembler_x64::Simm16::new"),
49 (true, 32) => Some("cranelift_assembler_x64::Simm32::new"),
50 (false, 8) => Some("cranelift_assembler_x64::Imm8::new"),
51 (false, 16) => Some("cranelift_assembler_x64::Imm16::new"),
52 (false, 32) => Some("cranelift_assembler_x64::Imm32::new"),
53 _ => None,
54 },
55 OperandKind::FixedReg(_) => None,
56 }
57}
58
59pub fn generate_macro_inst_fn(f: &mut Formatter, inst: &Inst) {
65 let struct_name = inst.name();
66 let params = inst
67 .format
68 .operands
69 .iter()
70 .filter(|o| o.mutability.is_read())
71 .filter(|o| !matches!(o.location.kind(), OperandKind::FixedReg(_)))
73 .collect::<Vec<_>>();
74 let results = inst
75 .format
76 .operands
77 .iter()
78 .filter(|o| o.mutability.is_write())
79 .collect::<Vec<_>>();
80 let rust_params = params
81 .iter()
82 .map(|o| format!("{}: {}", o.location, rust_param_raw(o)))
83 .collect::<Vec<_>>()
84 .join(", ");
85 f.add_block(
86 &format!("fn x64_{struct_name}_raw(&mut self, {rust_params}) -> AssemblerOutputs"),
87 |f| {
88 for o in params.iter() {
89 let l = o.location;
90 match rust_convert_isle_to_assembler(o) {
91 Some(cvt) => fmtln!(f, "let {l} = {cvt}({l});"),
92 None => fmtln!(f, "let {l} = {l}.clone();"),
93 }
94 }
95 let args = params
96 .iter()
97 .map(|o| format!("{}.clone()", o.location))
98 .collect::<Vec<_>>();
99 let args = args.join(", ");
100 fmtln!(
101 f,
102 "let inst = cranelift_assembler_x64::inst::{struct_name}::new({args}).into();"
103 );
104 if let Some(OperandKind::FixedReg(_)) = results.first().map(|o| o.location.kind()) {
105 fmtln!(f, "#[allow(unused_variables, reason = \"FIXME(#10238): fixed register instructions have TODOs\")]");
106 }
107 fmtln!(f, "let inst = MInst::External {{ inst }};");
108
109 use cranelift_assembler_x64_meta::dsl::Mutability::*;
110 match results.as_slice() {
111 [] => fmtln!(f, "SideEffectNoResult::Inst(inst)"),
112 [one] => match one.mutability {
113 Read => unreachable!(),
114 ReadWrite => match one.location.kind() {
115 OperandKind::Imm(_) => unreachable!(),
116 OperandKind::FixedReg(_) => fmtln!(f, "todo!()"),
118 OperandKind::Reg(r) => match r.bits() {
121 128 => {
122 fmtln!(
123 f,
124 "let xmm = {}.as_ref().write.to_reg();",
125 results[0].location
126 );
127 fmtln!(f, "AssemblerOutputs::RetXmm {{ inst, xmm }}")
128 }
129 _ => {
130 fmtln!(
131 f,
132 "let gpr = {}.as_ref().write.to_reg();",
133 results[0].location
134 );
135 fmtln!(f, "AssemblerOutputs::RetGpr {{ inst, gpr }}")
136 }
137 },
138 OperandKind::RegMem(_) => {
142 assert_eq!(results.len(), 1);
143 let l = results[0].location;
144 f.add_block(&format!("match {l}"), |f| match l.bits() {
145 128 => {
146 f.add_block("asm::XmmMem::Xmm(reg) => ", |f| {
147 fmtln!(f, "let xmm = reg.write.to_reg();");
148 fmtln!(f, "AssemblerOutputs::RetXmm {{ inst, xmm }} ");
149 });
150 f.add_block("asm::XmmMem::Mem(_) => ", |f| {
151 fmtln!(f, "AssemblerOutputs::SideEffect {{ inst }} ");
152 });
153 }
154 _ => {
155 f.add_block("asm::GprMem::Gpr(reg) => ", |f| {
156 fmtln!(f, "let gpr = reg.write.to_reg();");
157 fmtln!(f, "AssemblerOutputs::RetGpr {{ inst, gpr }} ")
158 });
159 f.add_block("asm::GprMem::Mem(_) => ", |f| {
160 fmtln!(f, "AssemblerOutputs::SideEffect {{ inst }} ");
161 });
162 }
163 });
164 }
165 },
166 },
167 _ => panic!("instruction has more than one result"),
168 }
169 },
170 );
171}
172
173pub fn generate_rust_macro(f: &mut Formatter, insts: &[Inst]) {
175 fmtln!(f, "#[doc(hidden)]");
176 fmtln!(f, "macro_rules! isle_assembler_methods {{");
177 f.indent(|f| {
178 fmtln!(f, "() => {{");
179 f.indent(|f| {
180 for inst in insts {
181 generate_macro_inst_fn(f, inst);
182 }
183 });
184 fmtln!(f, "}};");
185 });
186 fmtln!(f, "}}");
187}
188
189pub fn isle_param_raw(op: &Operand) -> String {
192 match op.location.kind() {
193 OperandKind::Imm(loc) => {
194 let bits = loc.bits();
195 if op.extension.is_sign_extended() {
196 format!("i{bits}")
197 } else {
198 format!("u{bits}")
199 }
200 }
201 OperandKind::Reg(r) => match r.bits() {
202 128 => "Xmm".to_string(),
203 _ => "Gpr".to_string(),
204 },
205 OperandKind::FixedReg(_) => "Gpr".to_string(),
206 OperandKind::RegMem(rm) => match rm.bits() {
207 128 => "XmmMem".to_string(),
208 _ => "GprMem".to_string(),
209 },
210 }
211}
212
213#[derive(Copy, Clone, Debug)]
221pub enum IsleConstructor {
222 RetMemorySideEffect,
226
227 RetGpr,
230
231 RetXmm,
234}
235
236impl IsleConstructor {
237 pub fn result_ty(&self) -> &'static str {
239 match self {
240 IsleConstructor::RetMemorySideEffect => "SideEffectNoResult",
241 IsleConstructor::RetGpr => "Gpr",
242 IsleConstructor::RetXmm => "Xmm",
243 }
244 }
245
246 pub fn conversion_constructor(&self) -> &'static str {
249 match self {
250 IsleConstructor::RetMemorySideEffect => "defer_side_effect",
251 IsleConstructor::RetGpr => "emit_ret_gpr",
252 IsleConstructor::RetXmm => "emit_ret_xmm",
253 }
254 }
255
256 pub fn suffix(&self) -> &'static str {
258 match self {
259 IsleConstructor::RetMemorySideEffect => "_mem",
260 IsleConstructor::RetGpr => "",
261 IsleConstructor::RetXmm => "",
262 }
263 }
264}
265
266pub fn isle_param_for_ctor(op: &Operand, ctor: IsleConstructor) -> String {
269 match op.location.kind() {
270 OperandKind::RegMem(_) if op.mutability.is_write() => match ctor {
275 IsleConstructor::RetMemorySideEffect => "Amode".to_string(),
276 IsleConstructor::RetGpr => "Gpr".to_string(),
277 IsleConstructor::RetXmm => "Xmm".to_string(),
278 },
279
280 _ => isle_param_raw(op),
282 }
283}
284
285pub fn isle_constructors(format: &Format) -> Vec<IsleConstructor> {
291 use Mutability::*;
292 use OperandKind::*;
293
294 let write_operands = format
295 .operands
296 .iter()
297 .filter(|o| o.mutability.is_write())
298 .collect::<Vec<_>>();
299 match &write_operands[..] {
300 [] => unimplemented!("if you truly need this (and not a `SideEffect*`), add a `NoReturn` variant to `AssemblerOutputs`"),
301 [one] => match one.mutability {
302 Read => unreachable!(),
303 ReadWrite => match one.location.kind() {
304 Imm(_) => unreachable!(),
305 FixedReg(_) => vec![IsleConstructor::RetGpr],
306 Reg(r) => match r.bits() {
309 128 => vec![IsleConstructor::RetXmm],
310 _ => vec![IsleConstructor::RetGpr],
311 },
312 RegMem(rm) => match rm.bits() {
315 128 => vec![IsleConstructor::RetXmm, IsleConstructor::RetMemorySideEffect],
316 _ => vec![IsleConstructor::RetGpr, IsleConstructor::RetMemorySideEffect],
317 },
318 }
319 },
320 other => panic!("unsupported number of write operands {other:?}"),
321 }
322}
323
324pub fn generate_isle_inst_decls(f: &mut Formatter, inst: &Inst) {
353 let struct_name = inst.name();
358 let raw_name = format!("x64_{struct_name}_raw");
359 let params = inst
360 .format
361 .operands
362 .iter()
363 .filter(|o| o.mutability.is_read())
364 .filter(|o| !matches!(o.location.kind(), OperandKind::FixedReg(_)))
366 .collect::<Vec<_>>();
367 let raw_param_tys = params
368 .iter()
369 .map(|o| isle_param_raw(o))
370 .collect::<Vec<_>>()
371 .join(" ");
372 fmtln!(f, "(decl {raw_name} ({raw_param_tys}) AssemblerOutputs)");
373 fmtln!(f, "(extern constructor {raw_name} {raw_name})");
374
375 for ctor in isle_constructors(&inst.format) {
383 let suffix = ctor.suffix();
384 let rule_name = format!("x64_{struct_name}{suffix}");
385 let result_ty = ctor.result_ty();
386 let param_tys = params
387 .iter()
388 .map(|o| isle_param_for_ctor(o, ctor))
389 .collect::<Vec<_>>()
390 .join(" ");
391 let param_names = params
392 .iter()
393 .map(|o| o.location.to_string())
394 .collect::<Vec<_>>()
395 .join(" ");
396 let convert = ctor.conversion_constructor();
397
398 fmtln!(f, "(decl {rule_name} ({param_tys}) {result_ty})");
399 fmtln!(
400 f,
401 "(rule ({rule_name} {param_names}) ({convert} ({raw_name} {param_names})))"
402 );
403 }
404}
405
406pub fn generate_isle(f: &mut Formatter, insts: &[Inst]) {
409 fmtln!(f, "(type AssemblerOutputs (enum");
410 fmtln!(f, " ;; Used for instructions that have ISLE");
411 fmtln!(f, " ;; `SideEffect`s (memory stores, traps,");
412 fmtln!(f, " ;; etc.) and do not return a `Value`.");
413 fmtln!(f, " (SideEffect (inst MInst))");
414 fmtln!(f, " ;; Used for instructions that return a");
415 fmtln!(f, " ;; GPR (including `GprMem` variants with");
416 fmtln!(f, " ;; a GPR as the first argument).");
417 fmtln!(f, " (RetGpr (inst MInst) (gpr Gpr))");
418 fmtln!(f, " ;; Used for instructions that return an");
419 fmtln!(f, " ;; XMM register.");
420 fmtln!(f, " (RetXmm (inst MInst) (xmm Xmm))");
421 fmtln!(f, " ;; TODO: eventually add more variants for");
422 fmtln!(f, " ;; multi-return, XMM, etc.; see");
423 fmtln!(
424 f,
425 " ;; https://github.com/bytecodealliance/wasmtime/pull/10276"
426 );
427 fmtln!(f, "))");
428 f.empty_line();
429
430 fmtln!(f, ";; Directly emit instructions that return a GPR.");
431 fmtln!(f, "(decl emit_ret_gpr (AssemblerOutputs) Gpr)");
432 fmtln!(f, "(rule (emit_ret_gpr (AssemblerOutputs.RetGpr inst gpr))");
433 fmtln!(f, " (let ((_ Unit (emit inst))) gpr))");
434 f.empty_line();
435
436 fmtln!(f, ";; Directly emit instructions that return an");
437 fmtln!(f, ";; XMM register.");
438 fmtln!(f, "(decl emit_ret_xmm (AssemblerOutputs) Xmm)");
439 fmtln!(f, "(rule (emit_ret_xmm (AssemblerOutputs.RetXmm inst xmm))");
440 fmtln!(f, " (let ((_ Unit (emit inst))) xmm))");
441 f.empty_line();
442
443 fmtln!(f, ";; Pass along the side-effecting instruction");
444 fmtln!(f, ";; for later emission.");
445 fmtln!(
446 f,
447 "(decl defer_side_effect (AssemblerOutputs) SideEffectNoResult)"
448 );
449 fmtln!(
450 f,
451 "(rule (defer_side_effect (AssemblerOutputs.SideEffect inst))"
452 );
453 fmtln!(f, " (SideEffectNoResult.Inst inst))");
454 f.empty_line();
455
456 for inst in insts {
457 generate_isle_inst_decls(f, inst);
458 f.empty_line();
459 }
460}