1use crate::cdsl::formats::InstructionFormat;
2use crate::cdsl::instructions::AllInstructions;
3use crate::error;
4use cranelift_srcgen::{fmtln, Formatter, Language};
5use std::rc::Rc;
6
7#[derive(Clone, Copy, PartialEq, Eq)]
9enum IsleTarget {
10 Lower,
12 Opt,
14}
15
16fn gen_common_isle(
17 formats: &[Rc<InstructionFormat>],
18 instructions: &AllInstructions,
19 fmt: &mut Formatter,
20 isle_target: IsleTarget,
21) {
22 use std::collections::{BTreeMap, BTreeSet};
23 use std::fmt::Write;
24
25 use crate::cdsl::formats::FormatField;
26
27 fmt.multi_line(
28 r#"
29;; GENERATED BY `gen_isle`. DO NOT EDIT!!!
30;;
31;; This ISLE file defines all the external type declarations for Cranelift's
32;; data structures that ISLE will process, such as `InstructionData` and
33;; `Opcode`.
34 "#,
35 );
36 fmt.empty_line();
37
38 let rust_name = |f: &FormatField| f.kind.rust_type.rsplit("::").next().unwrap();
40 let fields = |f: &FormatField| f.kind.fields.clone();
41 let immediate_types: BTreeMap<_, _> = formats
42 .iter()
43 .flat_map(|f| {
44 f.imm_fields
45 .iter()
46 .map(|i| (rust_name(i), fields(i)))
47 .collect::<Vec<_>>()
48 })
49 .collect();
50
51 let (enums, others): (BTreeMap<_, _>, BTreeMap<_, _>) = immediate_types
54 .iter()
55 .partition(|(_, field)| field.enum_values().is_some());
56
57 fmt.line(";;;; Extern type declarations for immediates ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;");
60 fmt.empty_line();
61 for ty in others.keys() {
62 fmtln!(fmt, "(type {} (primitive {}))", ty, ty);
63 }
64 fmt.empty_line();
65
66 for (name, field) in enums {
69 let field = field.enum_values().expect("only enums considered here");
70 let variants = field.values().cloned().collect();
71 gen_isle_enum(name, variants, fmt)
72 }
73
74 fmt.line(";;;; Value Arrays ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;");
77 fmt.empty_line();
78 let value_array_arities: BTreeSet<_> = formats
79 .iter()
80 .filter(|f| f.typevar_operand.is_some() && !f.has_value_list && f.num_value_operands != 1)
81 .map(|f| f.num_value_operands)
82 .collect();
83 for n in value_array_arities {
84 fmtln!(fmt, ";; ISLE representation of `[Value; {}]`.", n);
85 fmtln!(fmt, "(type ValueArray{} extern (enum))", n);
86 fmt.empty_line();
87
88 fmtln!(
89 fmt,
90 "(decl value_array_{} ({}) ValueArray{})",
91 n,
92 (0..n).map(|_| "Value").collect::<Vec<_>>().join(" "),
93 n
94 );
95 fmtln!(
96 fmt,
97 "(extern constructor value_array_{} pack_value_array_{})",
98 n,
99 n
100 );
101 fmtln!(
102 fmt,
103 "(extern extractor infallible value_array_{} unpack_value_array_{})",
104 n,
105 n
106 );
107 fmt.empty_line();
108 }
109
110 fmt.line(";;;; Block Arrays ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;");
113 fmt.empty_line();
114 let block_array_arities: BTreeSet<_> = formats
115 .iter()
116 .filter(|f| f.num_block_operands > 1)
117 .map(|f| f.num_block_operands)
118 .collect();
119 for n in block_array_arities {
120 fmtln!(fmt, ";; ISLE representation of `[BlockCall; {}]`.", n);
121 fmtln!(fmt, "(type BlockArray{} extern (enum))", n);
122 fmt.empty_line();
123
124 fmtln!(
125 fmt,
126 "(decl block_array_{0} ({1}) BlockArray{0})",
127 n,
128 (0..n).map(|_| "BlockCall").collect::<Vec<_>>().join(" ")
129 );
130
131 fmtln!(
132 fmt,
133 "(extern constructor block_array_{0} pack_block_array_{0})",
134 n
135 );
136
137 fmtln!(
138 fmt,
139 "(extern extractor infallible block_array_{0} unpack_block_array_{0})",
140 n
141 );
142 fmt.empty_line();
143 }
144
145 fmt.line(";;;; `Opcode` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;");
147 fmt.empty_line();
148 fmt.line("(type Opcode extern");
149 fmt.indent(|fmt| {
150 fmt.line("(enum");
151 fmt.indent(|fmt| {
152 for inst in instructions {
153 fmtln!(fmt, "{}", inst.camel_name);
154 }
155 });
156 fmt.line(")");
157 });
158 fmt.line(")");
159 fmt.empty_line();
160
161 fmtln!(
163 fmt,
164 ";;;; `InstructionData` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;",
165 );
166 fmt.empty_line();
167 fmtln!(fmt, "(type InstructionData extern");
168 fmt.indent(|fmt| {
169 fmt.line("(enum");
170 fmt.indent(|fmt| {
171 for format in formats {
172 let mut s = format!("({} (opcode Opcode)", format.name);
173 if format.has_value_list {
174 s.push_str(" (args ValueList)");
175 } else if format.num_value_operands == 1 {
176 s.push_str(" (arg Value)");
177 } else if format.num_value_operands > 1 {
178 write!(&mut s, " (args ValueArray{})", format.num_value_operands).unwrap();
179 }
180
181 match format.num_block_operands {
182 0 => (),
183 1 => write!(&mut s, " (destination BlockCall)").unwrap(),
184 n => write!(&mut s, " (blocks BlockArray{n})").unwrap(),
185 }
186
187 for field in &format.imm_fields {
188 write!(
189 &mut s,
190 " ({} {})",
191 field.member,
192 field.kind.rust_type.rsplit("::").next().unwrap()
193 )
194 .unwrap();
195 }
196 s.push(')');
197 fmt.line(&s);
198 }
199 });
200 fmt.line(")");
201 });
202 fmt.line(")");
203 fmt.empty_line();
204
205 fmtln!(
207 fmt,
208 ";;;; Extracting Opcode, Operands, and Immediates from `InstructionData` ;;;;;;;;",
209 );
210 fmt.empty_line();
211 for inst in instructions {
212 let results_len = inst.value_results.len();
213 let is_var_args = inst.format.has_value_list;
214 let has_side_effects = inst.can_trap || inst.other_side_effects;
215
216 let (ret_ty, ty_in_decl, make_inst_ctor, inst_data_etor) =
217 match (isle_target, is_var_args, results_len, has_side_effects) {
218 (IsleTarget::Opt, true, _, _) => continue,
220
221 (IsleTarget::Opt, _, 1, false) => ("Value", true, "make_inst", "inst_data_value"),
222 (IsleTarget::Opt, _, _, _) => ("Inst", false, "make_skeleton_inst", "inst_data"),
223 (IsleTarget::Lower, _, _, _) => ("Inst", false, "make_inst", "inst_data_value"),
224 };
225
226 fmtln!(
227 fmt,
228 "(decl {} ({}{}) {})",
229 inst.name,
230 if ty_in_decl { "Type " } else { "" },
231 inst.operands_in
232 .iter()
233 .map(|o| {
234 let ty = o.kind.rust_type;
235 if ty == "&[Value]" {
236 "ValueSlice"
237 } else {
238 ty.rsplit("::").next().unwrap()
239 }
240 })
241 .collect::<Vec<_>>()
242 .join(" "),
243 ret_ty
244 );
245 fmtln!(fmt, "(extractor");
246 fmt.indent(|fmt| {
247 fmtln!(
248 fmt,
249 "({} {}{})",
250 inst.name,
251 if ty_in_decl { "ty " } else { "" },
252 inst.operands_in
253 .iter()
254 .map(|o| { o.name })
255 .collect::<Vec<_>>()
256 .join(" ")
257 );
258
259 let mut s = format!(
260 "({inst_data_etor} {}(InstructionData.{} (Opcode.{})",
261 if ty_in_decl { "ty " } else { "" },
262 inst.format.name,
263 inst.camel_name
264 );
265
266 if inst.format.has_value_list {
268 let values: Vec<_> = inst
274 .operands_in
275 .iter()
276 .filter(|o| o.is_value())
277 .map(|o| o.name)
278 .collect();
279 let varargs = inst
280 .operands_in
281 .iter()
282 .find(|o| o.is_varargs())
283 .unwrap()
284 .name;
285 if values.is_empty() {
286 write!(&mut s, " (value_list_slice {varargs})").unwrap();
287 } else {
288 write!(
289 &mut s,
290 " (unwrap_head_value_list_{} {} {})",
291 values.len(),
292 values.join(" "),
293 varargs
294 )
295 .unwrap();
296 }
297 } else if inst.format.num_value_operands == 1 {
298 write!(
299 &mut s,
300 " {}",
301 inst.operands_in.iter().find(|o| o.is_value()).unwrap().name
302 )
303 .unwrap();
304 } else if inst.format.num_value_operands > 1 {
305 let values = inst
306 .operands_in
307 .iter()
308 .filter(|o| o.is_value())
309 .map(|o| o.name)
310 .collect::<Vec<_>>();
311 assert_eq!(values.len(), inst.format.num_value_operands);
312 let values = values.join(" ");
313 write!(
314 &mut s,
315 " (value_array_{} {})",
316 inst.format.num_value_operands, values,
317 )
318 .unwrap();
319 }
320
321 let imm_operands: Vec<_> = inst
323 .operands_in
324 .iter()
325 .filter(|o| !o.is_value() && !o.is_varargs() && !o.kind.is_block())
326 .collect();
327 assert_eq!(imm_operands.len(), inst.format.imm_fields.len(),);
328 for op in imm_operands {
329 write!(&mut s, " {}", op.name).unwrap();
330 }
331
332 let block_operands: Vec<_> = inst
334 .operands_in
335 .iter()
336 .filter(|o| o.kind.is_block())
337 .collect();
338 assert_eq!(block_operands.len(), inst.format.num_block_operands);
339 assert!(block_operands.len() <= 2);
340
341 if !block_operands.is_empty() {
342 if block_operands.len() == 1 {
343 write!(&mut s, " {}", block_operands[0].name).unwrap();
344 } else {
345 let blocks: Vec<_> = block_operands.iter().map(|o| o.name).collect();
346 let blocks = blocks.join(" ");
347 write!(
348 &mut s,
349 " (block_array_{} {})",
350 inst.format.num_block_operands, blocks,
351 )
352 .unwrap();
353 }
354 }
355
356 s.push_str("))");
357 fmt.line(&s);
358 });
359 fmt.line(")");
360
361 if isle_target == IsleTarget::Opt {
363 fmtln!(
364 fmt,
365 "(rule ({}{} {})",
366 inst.name,
367 if ty_in_decl { " ty" } else { "" },
368 inst.operands_in
369 .iter()
370 .map(|o| o.name)
371 .collect::<Vec<_>>()
372 .join(" ")
373 );
374 fmt.indent(|fmt| {
375 let mut s = format!(
376 "({make_inst_ctor}{} (InstructionData.{} (Opcode.{})",
377 if ty_in_decl { " ty" } else { "" },
378 inst.format.name,
379 inst.camel_name
380 );
381
382 assert!(!inst.format.has_value_list);
397 if inst.format.num_value_operands == 1 {
398 write!(
399 &mut s,
400 " {}",
401 inst.operands_in.iter().find(|o| o.is_value()).unwrap().name
402 )
403 .unwrap();
404 } else if inst.format.num_value_operands > 1 {
405 let values = inst
409 .operands_in
410 .iter()
411 .filter(|o| o.is_value())
412 .map(|o| o.name)
413 .collect::<Vec<_>>();
414 assert_eq!(values.len(), inst.format.num_value_operands);
415 let values = values.join(" ");
416 write!(
417 &mut s,
418 " (value_array_{}_ctor {})",
419 inst.format.num_value_operands, values
420 )
421 .unwrap();
422 }
423
424 if inst.format.num_block_operands > 0 {
425 let blocks: Vec<_> = inst
426 .operands_in
427 .iter()
428 .filter(|o| o.kind.is_block())
429 .map(|o| o.name)
430 .collect();
431 if inst.format.num_block_operands == 1 {
432 write!(&mut s, " {}", blocks.first().unwrap(),).unwrap();
433 } else {
434 write!(
435 &mut s,
436 " (block_array_{} {})",
437 inst.format.num_block_operands,
438 blocks.join(" ")
439 )
440 .unwrap();
441 }
442 }
443
444 for o in inst
446 .operands_in
447 .iter()
448 .filter(|o| !o.is_value() && !o.is_varargs() && !o.kind.is_block())
449 {
450 write!(&mut s, " {}", o.name).unwrap();
451 }
452 s.push_str("))");
453 fmt.line(&s);
454 });
455 fmt.line(")");
456 }
457
458 fmt.empty_line();
459 }
460}
461
462fn gen_opt_isle(
463 formats: &[Rc<InstructionFormat>],
464 instructions: &AllInstructions,
465 fmt: &mut Formatter,
466) {
467 gen_common_isle(formats, instructions, fmt, IsleTarget::Opt);
468}
469
470fn gen_lower_isle(
471 formats: &[Rc<InstructionFormat>],
472 instructions: &AllInstructions,
473 fmt: &mut Formatter,
474) {
475 gen_common_isle(formats, instructions, fmt, IsleTarget::Lower);
476}
477
478fn gen_isle_enum(name: &str, mut variants: Vec<&str>, fmt: &mut Formatter) {
480 variants.sort();
481 let prefix = format!(";;;; Enumerated Immediate: {name} ");
482 fmtln!(fmt, "{:;<80}", prefix);
483 fmt.empty_line();
484 fmtln!(fmt, "(type {} extern", name);
485 fmt.indent(|fmt| {
486 fmt.line("(enum");
487 fmt.indent(|fmt| {
488 for variant in variants {
489 fmtln!(fmt, "{}", variant);
490 }
491 });
492 fmt.line(")");
493 });
494 fmt.line(")");
495 fmt.empty_line();
496}
497
498pub(crate) fn generate(
499 formats: &[Rc<InstructionFormat>],
500 all_inst: &AllInstructions,
501 isle_opt_filename: &str,
502 isle_lower_filename: &str,
503 isle_dir: &std::path::Path,
504) -> Result<(), error::Error> {
505 let mut fmt = Formatter::new(Language::Isle);
507 gen_opt_isle(&formats, all_inst, &mut fmt);
508 fmt.write(isle_opt_filename, isle_dir)?;
509
510 let mut fmt = Formatter::new(Language::Isle);
512 gen_lower_isle(&formats, all_inst, &mut fmt);
513 fmt.write(isle_lower_filename, isle_dir)?;
514
515 Ok(())
516}