Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bytecodealliance
GitHub Repository: bytecodealliance/wasmtime
Path: blob/main/cranelift/codegen/meta/src/gen_isle.rs
1692 views
1
use crate::cdsl::formats::InstructionFormat;
2
use crate::cdsl::instructions::AllInstructions;
3
use crate::error;
4
use cranelift_srcgen::{Formatter, Language, fmtln};
5
use std::{borrow::Cow, cmp::Ordering, rc::Rc};
6
7
/// Which ISLE target are we generating code for?
8
#[derive(Clone, Copy, PartialEq, Eq)]
9
enum IsleTarget {
10
/// Generating code for instruction selection and lowering.
11
Lower,
12
/// Generating code for CLIF to CLIF optimizations.
13
Opt,
14
}
15
16
fn 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
// Collect and deduplicate the immediate types from the instruction fields.
39
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
// Separate the `enum` immediates (e.g., `FloatCC`) from other kinds of
52
// immediates.
53
let (enums, others): (BTreeMap<_, _>, BTreeMap<_, _>) = immediate_types
54
.iter()
55
.partition(|(_, field)| field.enum_values().is_some());
56
57
// Generate all the extern type declarations we need for the non-`enum`
58
// immediates.
59
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
// Generate the `enum` immediates, expanding all of the available variants
67
// into ISLE.
68
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
// Generate all of the value arrays we need for `InstructionData` as well as
75
// the constructors and extractors for them.
76
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
// Generate all of the block arrays we need for `InstructionData` as well as
111
// the constructors and extractors for them.
112
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
// Raw block entities.
146
fmtln!(fmt, "(type Block extern (enum))");
147
fmt.empty_line();
148
149
// Generate the extern type declaration for `Opcode`.
150
fmt.line(";;;; `Opcode` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;");
151
fmt.empty_line();
152
fmt.line("(type Opcode extern");
153
fmt.indent(|fmt| {
154
fmt.line("(enum");
155
fmt.indent(|fmt| {
156
for inst in instructions {
157
fmtln!(fmt, "{}", inst.camel_name);
158
}
159
});
160
fmt.line(")");
161
});
162
fmt.line(")");
163
fmt.empty_line();
164
165
// Generate the extern type declaration for `InstructionData`.
166
fmtln!(
167
fmt,
168
";;;; `InstructionData` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;",
169
);
170
fmt.empty_line();
171
fmtln!(fmt, "(type InstructionData extern");
172
fmt.indent(|fmt| {
173
fmt.line("(enum");
174
fmt.indent(|fmt| {
175
for format in formats {
176
let mut s = format!("({} (opcode Opcode)", format.name);
177
if format.has_value_list {
178
s.push_str(" (args ValueList)");
179
} else if format.num_value_operands == 1 {
180
s.push_str(" (arg Value)");
181
} else if format.num_value_operands > 1 {
182
write!(&mut s, " (args ValueArray{})", format.num_value_operands).unwrap();
183
}
184
185
match format.num_block_operands {
186
0 => (),
187
1 => write!(&mut s, " (destination BlockCall)").unwrap(),
188
n => write!(&mut s, " (blocks BlockArray{n})").unwrap(),
189
}
190
191
match format.num_raw_block_operands {
192
0 => (),
193
1 => write!(&mut s, "(block Block)").unwrap(),
194
_ => panic!("Too many raw block arguments"),
195
}
196
197
for field in &format.imm_fields {
198
write!(
199
&mut s,
200
" ({} {})",
201
field.member,
202
field.kind.rust_type.rsplit("::").next().unwrap()
203
)
204
.unwrap();
205
}
206
s.push(')');
207
fmt.line(&s);
208
}
209
});
210
fmt.line(")");
211
});
212
fmt.line(")");
213
fmt.empty_line();
214
215
// Generate the helper extractors for each opcode's full instruction.
216
fmtln!(
217
fmt,
218
";;;; Extracting Opcode, Operands, and Immediates from `InstructionData` ;;;;;;;;",
219
);
220
fmt.empty_line();
221
for inst in instructions {
222
let results_len = inst.value_results.len();
223
let is_var_args = inst.format.has_value_list;
224
let has_side_effects = inst.can_trap || inst.other_side_effects;
225
226
let (ret_ty, ty_in_decl, make_inst_ctor, inst_data_etor) =
227
match (isle_target, is_var_args, results_len, has_side_effects) {
228
// The mid-end does not deal with instructions that have var-args right now.
229
(IsleTarget::Opt, true, _, _) => continue,
230
231
(IsleTarget::Opt, _, 1, false) => ("Value", true, "make_inst", "inst_data_value"),
232
(IsleTarget::Opt, _, _, _) => ("Inst", false, "make_skeleton_inst", "inst_data"),
233
(IsleTarget::Lower, _, _, _) => ("Inst", false, "make_inst", "inst_data_value"),
234
};
235
236
fmtln!(
237
fmt,
238
"(decl {} ({}{}) {})",
239
inst.name,
240
if ty_in_decl { "Type " } else { "" },
241
inst.operands_in
242
.iter()
243
.map(|o| {
244
let ty = o.kind.rust_type;
245
if ty == "&[Value]" {
246
"ValueSlice"
247
} else {
248
ty.rsplit("::").next().unwrap()
249
}
250
})
251
.collect::<Vec<_>>()
252
.join(" "),
253
ret_ty
254
);
255
fmtln!(fmt, "(extractor");
256
fmt.indent(|fmt| {
257
fmtln!(
258
fmt,
259
"({} {}{})",
260
inst.name,
261
if ty_in_decl { "ty " } else { "" },
262
inst.operands_in
263
.iter()
264
.map(|o| { o.name })
265
.collect::<Vec<_>>()
266
.join(" ")
267
);
268
269
let mut s = format!(
270
"({inst_data_etor} {}(InstructionData.{} (Opcode.{})",
271
if ty_in_decl { "ty " } else { "" },
272
inst.format.name,
273
inst.camel_name
274
);
275
276
// Value and varargs operands.
277
if inst.format.has_value_list {
278
// The instruction format uses a value list, but the
279
// instruction itself might have not only a `&[Value]`
280
// varargs operand, but also one or more `Value` operands as
281
// well. If this is the case, then we need to read them off
282
// the front of the `ValueList`.
283
let values: Vec<_> = inst
284
.operands_in
285
.iter()
286
.filter(|o| o.is_value())
287
.map(|o| o.name)
288
.collect();
289
let varargs = inst
290
.operands_in
291
.iter()
292
.find(|o| o.is_varargs())
293
.unwrap()
294
.name;
295
if values.is_empty() {
296
write!(&mut s, " (value_list_slice {varargs})").unwrap();
297
} else {
298
write!(
299
&mut s,
300
" (unwrap_head_value_list_{} {} {})",
301
values.len(),
302
values.join(" "),
303
varargs
304
)
305
.unwrap();
306
}
307
} else if inst.format.num_value_operands == 1 {
308
write!(
309
&mut s,
310
" {}",
311
inst.operands_in.iter().find(|o| o.is_value()).unwrap().name
312
)
313
.unwrap();
314
} else if inst.format.num_value_operands > 1 {
315
let values = inst
316
.operands_in
317
.iter()
318
.filter(|o| o.is_value())
319
.map(|o| o.name)
320
.collect::<Vec<_>>();
321
assert_eq!(values.len(), inst.format.num_value_operands);
322
let values = values.join(" ");
323
write!(
324
&mut s,
325
" (value_array_{} {})",
326
inst.format.num_value_operands, values,
327
)
328
.unwrap();
329
}
330
331
// Immediates.
332
let imm_operands: Vec<_> = inst
333
.operands_in
334
.iter()
335
.filter(|o| {
336
!o.is_value() && !o.is_varargs() && !o.kind.is_block() && !o.kind.is_raw_block()
337
})
338
.collect();
339
assert_eq!(imm_operands.len(), inst.format.imm_fields.len(),);
340
for op in imm_operands {
341
write!(&mut s, " {}", op.name).unwrap();
342
}
343
344
// Blocks.
345
let block_operands: Vec<_> = inst
346
.operands_in
347
.iter()
348
.filter(|o| o.kind.is_block())
349
.collect();
350
assert_eq!(block_operands.len(), inst.format.num_block_operands);
351
assert!(block_operands.len() <= 2);
352
353
if !block_operands.is_empty() {
354
if block_operands.len() == 1 {
355
write!(&mut s, " {}", block_operands[0].name).unwrap();
356
} else {
357
let blocks: Vec<_> = block_operands.iter().map(|o| o.name).collect();
358
let blocks = blocks.join(" ");
359
write!(
360
&mut s,
361
" (block_array_{} {})",
362
inst.format.num_block_operands, blocks,
363
)
364
.unwrap();
365
}
366
}
367
368
// Raw blocks.
369
match inst.format.num_raw_block_operands {
370
0 => {}
371
1 => {
372
write!(&mut s, " block").unwrap();
373
}
374
_ => panic!("Too many raw block arguments"),
375
}
376
377
s.push_str("))");
378
fmt.line(&s);
379
});
380
fmt.line(")");
381
382
// Generate a constructor if this is the mid-end prelude.
383
if isle_target == IsleTarget::Opt {
384
fmtln!(
385
fmt,
386
"(rule ({}{} {})",
387
inst.name,
388
if ty_in_decl { " ty" } else { "" },
389
inst.operands_in
390
.iter()
391
.map(|o| o.name)
392
.collect::<Vec<_>>()
393
.join(" ")
394
);
395
fmt.indent(|fmt| {
396
let mut s = format!(
397
"({make_inst_ctor}{} (InstructionData.{} (Opcode.{})",
398
if ty_in_decl { " ty" } else { "" },
399
inst.format.name,
400
inst.camel_name
401
);
402
403
// Handle values. Note that we skip generating
404
// constructors for any instructions with variadic
405
// value lists. This is fine for the mid-end because
406
// in practice only calls and branches (for branch
407
// args) use this functionality, and neither can
408
// really be optimized or rewritten in the mid-end
409
// (currently).
410
//
411
// As a consequence, we only have to handle the
412
// one-`Value` case, in which the `Value` is directly
413
// in the `InstructionData`, and the multiple-`Value`
414
// case, in which the `Value`s are in a
415
// statically-sized array (e.g. `[Value; 2]` for a
416
// binary op).
417
assert!(!inst.format.has_value_list);
418
if inst.format.num_value_operands == 1 {
419
write!(
420
&mut s,
421
" {}",
422
inst.operands_in.iter().find(|o| o.is_value()).unwrap().name
423
)
424
.unwrap();
425
} else if inst.format.num_value_operands > 1 {
426
// As above, get all bindings together, and pass
427
// to a sub-term; here we use a constructor to
428
// build the value array.
429
let values = inst
430
.operands_in
431
.iter()
432
.filter(|o| o.is_value())
433
.map(|o| o.name)
434
.collect::<Vec<_>>();
435
assert_eq!(values.len(), inst.format.num_value_operands);
436
let values = values.join(" ");
437
write!(
438
&mut s,
439
" (value_array_{}_ctor {})",
440
inst.format.num_value_operands, values
441
)
442
.unwrap();
443
}
444
445
if inst.format.num_block_operands > 0 {
446
let blocks: Vec<_> = inst
447
.operands_in
448
.iter()
449
.filter(|o| o.kind.is_block())
450
.map(|o| o.name)
451
.collect();
452
if inst.format.num_block_operands == 1 {
453
write!(&mut s, " {}", blocks.first().unwrap(),).unwrap();
454
} else {
455
write!(
456
&mut s,
457
" (block_array_{} {})",
458
inst.format.num_block_operands,
459
blocks.join(" ")
460
)
461
.unwrap();
462
}
463
}
464
465
match inst.format.num_raw_block_operands {
466
0 => {}
467
1 => {
468
write!(&mut s, " block").unwrap();
469
}
470
_ => panic!("Too many raw block arguments"),
471
}
472
473
// Immediates (non-value args).
474
for o in inst.operands_in.iter().filter(|o| {
475
!o.is_value() && !o.is_varargs() && !o.kind.is_block() && !o.kind.is_raw_block()
476
}) {
477
write!(&mut s, " {}", o.name).unwrap();
478
}
479
s.push_str("))");
480
fmt.line(&s);
481
});
482
fmt.line(")");
483
}
484
485
fmt.empty_line();
486
}
487
}
488
489
fn gen_opt_isle(
490
formats: &[Rc<InstructionFormat>],
491
instructions: &AllInstructions,
492
fmt: &mut Formatter,
493
) {
494
gen_common_isle(formats, instructions, fmt, IsleTarget::Opt);
495
}
496
497
fn gen_lower_isle(
498
formats: &[Rc<InstructionFormat>],
499
instructions: &AllInstructions,
500
fmt: &mut Formatter,
501
) {
502
gen_common_isle(formats, instructions, fmt, IsleTarget::Lower);
503
}
504
505
/// Generate an `enum` immediate in ISLE.
506
fn gen_isle_enum(name: &str, mut variants: Vec<&str>, fmt: &mut Formatter) {
507
variants.sort();
508
let prefix = format!(";;;; Enumerated Immediate: {name} ");
509
fmtln!(fmt, "{:;<80}", prefix);
510
fmt.empty_line();
511
fmtln!(fmt, "(type {} extern", name);
512
fmt.indent(|fmt| {
513
fmt.line("(enum");
514
fmt.indent(|fmt| {
515
for variant in variants {
516
fmtln!(fmt, "{}", variant);
517
}
518
});
519
fmt.line(")");
520
});
521
fmt.line(")");
522
fmt.empty_line();
523
}
524
525
#[derive(Clone, Copy, PartialEq, Eq)]
526
struct NumericType {
527
signed: bool,
528
byte_width: u8,
529
}
530
531
impl NumericType {
532
fn all() -> impl Iterator<Item = NumericType> {
533
[1, 2, 4, 8, 16].into_iter().flat_map(|byte_width| {
534
[true, false]
535
.into_iter()
536
.map(move |signed| NumericType { signed, byte_width })
537
})
538
}
539
540
fn name(&self) -> &'static str {
541
let idx = self.byte_width.ilog2();
542
let idx = usize::try_from(idx).unwrap();
543
if self.signed {
544
["i8", "i16", "i32", "i64", "i128"][idx]
545
} else {
546
["u8", "u16", "u32", "u64", "u128"][idx]
547
}
548
}
549
}
550
551
#[derive(Clone, Default, PartialEq, Eq)]
552
struct NumericOp<'a> {
553
/// The name of this operation.
554
name: &'a str,
555
/// The return type of this operation.
556
ret: &'a str,
557
/// Whether this operation is partial.
558
partial: bool,
559
/// (name, type) pairs of arguments.
560
args: Rc<[(&'a str, &'a str)]>,
561
/// The source text for the constructor's body.
562
body: &'a str,
563
/// Whether extractors should be generated for this op.
564
///
565
/// Must have `arity == 1`, `ret == bool`, and `name.starts_with("is_")`.
566
etors: bool,
567
}
568
569
impl NumericOp<'_> {
570
fn ops_for_type(ty: &NumericType) -> impl Iterator<Item = NumericOp<'_>> {
571
let arity1 = NumericOp {
572
args: [("a", ty.name())].into(),
573
..NumericOp::default()
574
};
575
576
let arity2 = NumericOp {
577
args: [("a", ty.name()), ("b", ty.name())].into(),
578
..NumericOp::default()
579
};
580
581
let comparison = NumericOp {
582
ret: "bool",
583
..arity2.clone()
584
};
585
586
let predicate = NumericOp {
587
ret: "bool",
588
etors: true,
589
..arity1.clone()
590
};
591
592
let binop = NumericOp {
593
ret: ty.name(),
594
..arity2.clone()
595
};
596
597
let partial_binop = NumericOp {
598
ret: ty.name(),
599
partial: true,
600
..binop.clone()
601
};
602
603
let unop = NumericOp {
604
ret: ty.name(),
605
..arity1.clone()
606
};
607
608
let partial_unop = NumericOp {
609
ret: ty.name(),
610
partial: true,
611
..unop.clone()
612
};
613
614
let shift = NumericOp {
615
args: [("a", ty.name()), ("b", "u32")].into(),
616
..binop.clone()
617
};
618
619
let partial_shift = NumericOp {
620
args: [("a", ty.name()), ("b", "u32")].into(),
621
..partial_binop.clone()
622
};
623
624
// Operations that apply to both signed and unsigned numbers.
625
let ops = [
626
// Comparisons.
627
NumericOp {
628
name: "eq",
629
body: "a == b",
630
..comparison.clone()
631
},
632
NumericOp {
633
name: "ne",
634
body: "a != b",
635
..comparison.clone()
636
},
637
NumericOp {
638
name: "lt",
639
body: "a < b",
640
..comparison.clone()
641
},
642
NumericOp {
643
name: "lt_eq",
644
body: "a <= b",
645
..comparison.clone()
646
},
647
NumericOp {
648
name: "gt",
649
body: "a > b",
650
..comparison.clone()
651
},
652
NumericOp {
653
name: "gt_eq",
654
body: "a >= b",
655
..comparison.clone()
656
},
657
// Arithmetic operations.
658
//
659
// For each operation (e.g. addition) we have three variants:
660
//
661
// * partial ctor `checked_add`: no return value on overflow
662
// * ctor `wrapping_add`: wraps on overflow
663
// * ctor `add`: non-partial but panics at runtime on overflow
664
NumericOp {
665
name: "checked_add",
666
body: "a.checked_add(b)",
667
..partial_binop.clone()
668
},
669
NumericOp {
670
name: "wrapping_add",
671
body: "a.wrapping_add(b)",
672
..binop.clone()
673
},
674
NumericOp {
675
name: "add",
676
body: r#"a.checked_add(b).unwrap_or_else(|| panic!("addition overflow: {a} + {b}"))"#,
677
..binop.clone()
678
},
679
NumericOp {
680
name: "checked_sub",
681
body: "a.checked_sub(b)",
682
..partial_binop.clone()
683
},
684
NumericOp {
685
name: "wrapping_sub",
686
body: "a.wrapping_sub(b)",
687
..binop.clone()
688
},
689
NumericOp {
690
name: "sub",
691
body: r#"a.checked_sub(b).unwrap_or_else(|| panic!("subtraction overflow: {a} - {b}"))"#,
692
..binop.clone()
693
},
694
NumericOp {
695
name: "checked_mul",
696
body: "a.checked_mul(b)",
697
..partial_binop.clone()
698
},
699
NumericOp {
700
name: "wrapping_mul",
701
body: "a.wrapping_mul(b)",
702
..binop.clone()
703
},
704
NumericOp {
705
name: "mul",
706
body: r#"a.checked_mul(b).unwrap_or_else(|| panic!("multiplication overflow: {a} * {b}"))"#,
707
..binop.clone()
708
},
709
NumericOp {
710
name: "checked_div",
711
body: "a.checked_div(b)",
712
..partial_binop.clone()
713
},
714
NumericOp {
715
name: "wrapping_div",
716
body: "a.wrapping_div(b)",
717
..binop.clone()
718
},
719
NumericOp {
720
name: "div",
721
body: r#"a.checked_div(b).unwrap_or_else(|| panic!("div failure: {a} / {b}"))"#,
722
..binop.clone()
723
},
724
NumericOp {
725
name: "checked_rem",
726
body: "a.checked_rem(b)",
727
..partial_binop.clone()
728
},
729
NumericOp {
730
name: "rem",
731
body: r#"a.checked_rem(b).unwrap_or_else(|| panic!("rem failure: {a} % {b}"))"#,
732
..binop.clone()
733
},
734
// Bitwise operations.
735
//
736
// When applicable (e.g. shifts) we have checked, wrapping, and
737
// unwrapping variants, similar to arithmetic operations.
738
NumericOp {
739
name: "and",
740
body: "a & b",
741
..binop.clone()
742
},
743
NumericOp {
744
name: "or",
745
body: "a | b",
746
..binop.clone()
747
},
748
NumericOp {
749
name: "xor",
750
body: "a ^ b",
751
..binop.clone()
752
},
753
NumericOp {
754
name: "not",
755
body: "!a",
756
..unop.clone()
757
},
758
NumericOp {
759
name: "checked_shl",
760
body: "a.checked_shl(b)",
761
..partial_shift.clone()
762
},
763
NumericOp {
764
name: "wrapping_shl",
765
body: "a.wrapping_shl(b)",
766
..shift.clone()
767
},
768
NumericOp {
769
name: "shl",
770
body: r#"a.checked_shl(b).unwrap_or_else(|| panic!("shl overflow: {a} << {b}"))"#,
771
..shift.clone()
772
},
773
NumericOp {
774
name: "checked_shr",
775
body: "a.checked_shr(b)",
776
..partial_shift.clone()
777
},
778
NumericOp {
779
name: "wrapping_shr",
780
body: "a.wrapping_shr(b)",
781
..shift.clone()
782
},
783
NumericOp {
784
name: "shr",
785
body: r#"a.checked_shr(b).unwrap_or_else(|| panic!("shr overflow: {a} >> {b}"))"#,
786
..shift.clone()
787
},
788
// Predicates.
789
//
790
// We generate both pure constructors and a variety of extractors
791
// for these. See the relevant comments in `gen_numerics_isle` about
792
// the extractors.
793
NumericOp {
794
name: "is_zero",
795
body: "a == 0",
796
..predicate.clone()
797
},
798
NumericOp {
799
name: "is_non_zero",
800
body: "a != 0",
801
..predicate.clone()
802
},
803
NumericOp {
804
name: "is_odd",
805
body: "a & 1 == 1",
806
..predicate.clone()
807
},
808
NumericOp {
809
name: "is_even",
810
body: "a & 1 == 0",
811
..predicate.clone()
812
},
813
// Miscellaneous unary operations.
814
NumericOp {
815
name: "checked_ilog2",
816
body: "a.checked_ilog2()",
817
ret: "u32",
818
..partial_unop.clone()
819
},
820
NumericOp {
821
name: "ilog2",
822
body: r#"a.checked_ilog2().unwrap_or_else(|| panic!("ilog2 overflow: {a}"))"#,
823
ret: "u32",
824
..unop.clone()
825
},
826
NumericOp {
827
name: "trailing_zeros",
828
body: "a.trailing_zeros()",
829
ret: "u32",
830
..unop.clone()
831
},
832
NumericOp {
833
name: "trailing_ones",
834
body: "a.trailing_ones()",
835
ret: "u32",
836
..unop.clone()
837
},
838
NumericOp {
839
name: "leading_zeros",
840
body: "a.leading_zeros()",
841
ret: "u32",
842
..unop.clone()
843
},
844
NumericOp {
845
name: "leading_ones",
846
body: "a.leading_ones()",
847
ret: "u32",
848
..unop.clone()
849
},
850
];
851
852
// Operations that apply only to signed numbers.
853
let signed_ops = [
854
NumericOp {
855
name: "checked_neg",
856
body: "a.checked_neg()",
857
..partial_unop.clone()
858
},
859
NumericOp {
860
name: "wrapping_neg",
861
body: "a.wrapping_neg()",
862
..unop.clone()
863
},
864
NumericOp {
865
name: "neg",
866
body: r#"a.checked_neg().unwrap_or_else(|| panic!("negation overflow: {a}"))"#,
867
..unop.clone()
868
},
869
];
870
871
// Operations that apply only to unsigned numbers.
872
let unsigned_ops = [NumericOp {
873
name: "is_power_of_two",
874
body: "a.is_power_of_two()",
875
..predicate.clone()
876
}];
877
878
struct IterIf<I> {
879
condition: bool,
880
iter: I,
881
}
882
883
impl<I: Iterator> Iterator for IterIf<I> {
884
type Item = I::Item;
885
886
fn next(&mut self) -> Option<Self::Item> {
887
if self.condition {
888
self.iter.next()
889
} else {
890
None
891
}
892
}
893
}
894
895
ops.into_iter()
896
.chain(IterIf {
897
condition: ty.signed,
898
iter: signed_ops.into_iter(),
899
})
900
.chain(IterIf {
901
condition: !ty.signed,
902
iter: unsigned_ops.into_iter(),
903
})
904
}
905
}
906
907
fn gen_numerics_isle(isle: &mut Formatter, rust: &mut Formatter) {
908
fmtln!(rust, "#[macro_export]");
909
fmtln!(rust, "#[doc(hidden)]");
910
fmtln!(rust, "macro_rules! isle_numerics_methods {{");
911
rust.indent_push();
912
fmtln!(rust, "() => {{");
913
rust.indent_push();
914
915
for ty in NumericType::all() {
916
for op in NumericOp::ops_for_type(&ty) {
917
let ty = ty.name();
918
let op_name = format!("{ty}_{}", op.name);
919
let partial = if op.partial { " partial" } else { "" };
920
let ret = op.ret;
921
fmtln!(isle, "(decl pure{partial} {op_name} (");
922
isle.indent(|isle| {
923
for (_arg_name, arg_ty) in op.args.iter() {
924
fmtln!(isle, "{arg_ty}");
925
}
926
});
927
fmtln!(isle, ") {ret})");
928
fmtln!(isle, "(extern constructor {op_name} {op_name})");
929
930
let ret = if op.partial {
931
Cow::from(format!("Option<{ret}>"))
932
} else {
933
Cow::from(ret)
934
};
935
let body = op.body;
936
fmtln!(rust, "#[inline]");
937
fmtln!(rust, "fn {op_name}(");
938
rust.indent(|rust| {
939
fmtln!(rust, "&mut self,");
940
for (arg_name, arg_ty) in op.args.iter() {
941
fmtln!(rust, "{arg_name}: {arg_ty},");
942
}
943
});
944
fmtln!(rust, ") -> {ret} {{");
945
rust.indent(|rust| {
946
fmtln!(rust, "{body}");
947
});
948
fmtln!(rust, "}}");
949
950
// When generating extractors for a `{ty}_is_foo` predicate,
951
// we generate the following:
952
//
953
// * bool <- ty etor: `{ty}_matches_foo`
954
// * ty <- ty etor: `{ty}_extract_foo`
955
// * () <- ty etor: `{ty}_when_foo`
956
// * () <- ty etor: `{ty}_when_not_foo`
957
//
958
// The last three are defined as local extractors that are
959
// implemented in terms of the first. This gives the ISLE compiler
960
// visibility into the extractors' overlapping-ness.
961
if op.etors {
962
debug_assert_eq!(op.args.len(), 1);
963
debug_assert_eq!(op.args[0].1, ty);
964
debug_assert_eq!(op.ret, "bool");
965
debug_assert!(op.name.starts_with("is_"));
966
967
// Cut of the `is_` prefix.
968
let base_name = &op.name[3..];
969
debug_assert!(base_name.len() > 0);
970
971
fmtln!(isle, "(decl pure {ty}_matches_{base_name} (bool) {ty})");
972
fmtln!(
973
isle,
974
"(extern extractor {ty}_matches_{base_name} {ty}_matches_{base_name})"
975
);
976
fmtln!(rust, "#[inline]");
977
fmtln!(
978
rust,
979
"fn {ty}_matches_{base_name}(&mut self, a: {ty}) -> Option<bool> {{"
980
);
981
rust.indent(|rust| {
982
fmtln!(rust, "Some({body})");
983
});
984
fmtln!(rust, "}}");
985
986
fmtln!(isle, "(decl pure {ty}_extract_{base_name} ({ty}) {ty})");
987
fmtln!(
988
isle,
989
"(extractor ({ty}_extract_{base_name} x) (and ({ty}_matches_{base_name} true) x))"
990
);
991
992
fmtln!(isle, "(decl pure {ty}_when_{base_name} () {ty})");
993
fmtln!(
994
isle,
995
"(extractor ({ty}_when_{base_name}) ({ty}_matches_{base_name} true))"
996
);
997
998
fmtln!(isle, "(decl pure {ty}_when_not_{base_name} () {ty})");
999
fmtln!(
1000
isle,
1001
"(extractor ({ty}_when_not_{base_name}) ({ty}_matches_{base_name} false))"
1002
);
1003
}
1004
1005
isle.empty_line();
1006
rust.empty_line();
1007
}
1008
}
1009
1010
// Numeric type conversions.
1011
//
1012
// Naming and conventions:
1013
//
1014
// * Constructors:
1015
// * "<from>_into_<to>" for lossless, infallible conversion
1016
// * "<from>_try_into_<to>" for lossless, fallible conversions (exposed as
1017
// partial constructors)
1018
// * "<from>_unwrap_into_<to>" for lossless, fallible conversions that will
1019
// panic at runtime if the conversion would be lossy
1020
// * "<from>_truncate_into_<to>" for lossy, infallible conversions that
1021
// ignore upper bits
1022
// * "<from>_cast_[un]signed" for signed-to-unsigned (and vice versa)
1023
// reinterpretation
1024
// * Extractors:
1025
// * "<to>_from_<from>" for both fallible and infallible extractors
1026
// * No unwrapping extractors
1027
// * No truncating extractors
1028
// * No signed-to-unsigned reinterpreting extractors
1029
for from in NumericType::all() {
1030
for to in NumericType::all() {
1031
if from == to {
1032
continue;
1033
}
1034
1035
let from_name = from.name();
1036
let to_name = to.name();
1037
1038
let lossy = match (from.byte_width.cmp(&to.byte_width), from.signed, to.signed) {
1039
// Widening with the same signedness is lossless.
1040
(Ordering::Less, true, true) | (Ordering::Less, false, false) => false,
1041
// Widening from unsigned to signed is lossless.
1042
(Ordering::Less, false, true) => false,
1043
// Widening from signed to unsigned is lossy.
1044
(Ordering::Less, true, false) => true,
1045
// Same width means we must be changing sign, since we skip
1046
// `from == to`, and this is lossy.
1047
(Ordering::Equal, _, _) => {
1048
debug_assert_ne!(from.signed, to.signed);
1049
true
1050
}
1051
// Narrowing is always lossy.
1052
(Ordering::Greater, _, _) => true,
1053
};
1054
1055
let (ctor, partial, rust_ret) = if lossy {
1056
(
1057
"try_into",
1058
" partial",
1059
Cow::from(format!("Option<{to_name}>")),
1060
)
1061
} else {
1062
("into", "", Cow::from(to_name))
1063
};
1064
1065
// Constructor.
1066
fmtln!(
1067
isle,
1068
"(decl pure{partial} {from_name}_{ctor}_{to_name} ({from_name}) {to_name})"
1069
);
1070
fmtln!(
1071
isle,
1072
"(extern constructor {from_name}_{ctor}_{to_name} {from_name}_{ctor}_{to_name})"
1073
);
1074
if !lossy {
1075
fmtln!(
1076
isle,
1077
"(convert {from_name} {to_name} {from_name}_{ctor}_{to_name})"
1078
);
1079
}
1080
fmtln!(rust, "#[inline]");
1081
fmtln!(
1082
rust,
1083
"fn {from_name}_{ctor}_{to_name}(&mut self, x: {from_name}) -> {rust_ret} {{"
1084
);
1085
rust.indent(|rust| {
1086
if lossy {
1087
fmtln!(rust, "{to_name}::try_from(x).ok()");
1088
} else {
1089
fmtln!(rust, "{to_name}::from(x)");
1090
}
1091
});
1092
fmtln!(rust, "}}");
1093
1094
// Unwrapping constructor.
1095
if lossy {
1096
fmtln!(
1097
isle,
1098
"(decl pure {from_name}_unwrap_into_{to_name} ({from_name}) {to_name})"
1099
);
1100
fmtln!(
1101
isle,
1102
"(extern constructor {from_name}_unwrap_into_{to_name} {from_name}_unwrap_into_{to_name})"
1103
);
1104
fmtln!(rust, "#[inline]");
1105
fmtln!(
1106
rust,
1107
"fn {from_name}_unwrap_into_{to_name}(&mut self, x: {from_name}) -> {to_name} {{"
1108
);
1109
rust.indent(|rust| {
1110
fmtln!(rust, "{to_name}::try_from(x).unwrap()");
1111
});
1112
fmtln!(rust, "}}");
1113
}
1114
1115
// Truncating constructor.
1116
if lossy && from.signed == to.signed {
1117
fmtln!(
1118
isle,
1119
"(decl pure {from_name}_truncate_into_{to_name} ({from_name}) {to_name})"
1120
);
1121
fmtln!(
1122
isle,
1123
"(extern constructor {from_name}_truncate_into_{to_name} {from_name}_truncate_into_{to_name})"
1124
);
1125
fmtln!(rust, "#[inline]");
1126
fmtln!(
1127
rust,
1128
"fn {from_name}_truncate_into_{to_name}(&mut self, x: {from_name}) -> {to_name} {{"
1129
);
1130
rust.indent(|rust| {
1131
fmtln!(rust, "x as {to_name}");
1132
});
1133
fmtln!(rust, "}}");
1134
}
1135
1136
// Signed-to-unsigned reinterpreting constructor.
1137
if from.byte_width == to.byte_width {
1138
debug_assert_ne!(from.signed, to.signed);
1139
let cast_name = if to.signed {
1140
"cast_signed"
1141
} else {
1142
"cast_unsigned"
1143
};
1144
fmtln!(
1145
isle,
1146
"(decl pure {from_name}_{cast_name} ({from_name}) {to_name})"
1147
);
1148
fmtln!(
1149
isle,
1150
"(extern constructor {from_name}_{cast_name} {from_name}_{cast_name})"
1151
);
1152
fmtln!(rust, "#[inline]");
1153
fmtln!(
1154
rust,
1155
"fn {from_name}_{cast_name}(&mut self, x: {from_name}) -> {to_name} {{"
1156
);
1157
rust.indent(|rust| {
1158
// TODO: Once our MSRV is >= 1.87, we should use
1159
// `x.cast_[un]signed()` here.
1160
fmtln!(rust, "x as {to_name}");
1161
});
1162
fmtln!(rust, "}}");
1163
}
1164
1165
// Extractor.
1166
fmtln!(
1167
isle,
1168
"(decl pure {to_name}_from_{from_name} ({to_name}) {from_name})"
1169
);
1170
fmtln!(
1171
isle,
1172
"(extern extractor {to_name}_from_{from_name} {from_name}_from_{to_name})"
1173
);
1174
fmtln!(rust, "#[inline]");
1175
fmtln!(
1176
rust,
1177
"fn {from_name}_from_{to_name}(&mut self, x: {from_name}) -> Option<{to_name}> {{"
1178
);
1179
rust.indent(|rust| {
1180
if lossy {
1181
fmtln!(rust, "x.try_into().ok()");
1182
} else {
1183
fmtln!(rust, "Some(x.into())");
1184
}
1185
});
1186
fmtln!(rust, "}}");
1187
1188
isle.empty_line();
1189
rust.empty_line();
1190
}
1191
}
1192
1193
rust.indent_pop();
1194
fmtln!(rust, "}}");
1195
rust.indent_pop();
1196
fmtln!(rust, "}}");
1197
}
1198
1199
pub(crate) fn generate(
1200
formats: &[Rc<InstructionFormat>],
1201
all_inst: &AllInstructions,
1202
isle_numerics_filename: &str,
1203
rust_numerics_filename: &str,
1204
isle_opt_filename: &str,
1205
isle_lower_filename: &str,
1206
isle_dir: &std::path::Path,
1207
) -> Result<(), error::Error> {
1208
// Numerics
1209
let mut isle_fmt = Formatter::new(Language::Isle);
1210
let mut rust_fmt = Formatter::new(Language::Rust);
1211
gen_numerics_isle(&mut isle_fmt, &mut rust_fmt);
1212
isle_fmt.write(isle_numerics_filename, isle_dir)?;
1213
rust_fmt.write(rust_numerics_filename, isle_dir)?;
1214
1215
// ISLE DSL: mid-end ("opt") generated bindings.
1216
let mut fmt = Formatter::new(Language::Isle);
1217
gen_opt_isle(&formats, all_inst, &mut fmt);
1218
fmt.write(isle_opt_filename, isle_dir)?;
1219
1220
// ISLE DSL: lowering generated bindings.
1221
let mut fmt = Formatter::new(Language::Isle);
1222
gen_lower_isle(&formats, all_inst, &mut fmt);
1223
fmt.write(isle_lower_filename, isle_dir)?;
1224
1225
Ok(())
1226
}
1227
1228