1 use crate::cdsl::instructions::{InstSpec, Instruction, InstructionPredicate};
2 use crate::cdsl::operands::{OperandKind, OperandKindFields};
3 use crate::cdsl::types::ValueType;
4 use crate::cdsl::typevar::{TypeSetBuilder, TypeVar};
5
6 use cranelift_entity::{entity_impl, PrimaryMap, SparseMap, SparseMapValue};
7
8 use std::fmt;
9 use std::iter::IntoIterator;
10
11 pub(crate) enum Expr {
12 Var(VarIndex),
13 Literal(Literal),
14 }
15
16 impl Expr {
maybe_literal(&self) -> Option<&Literal>17 pub fn maybe_literal(&self) -> Option<&Literal> {
18 match &self {
19 Expr::Literal(lit) => Some(lit),
20 _ => None,
21 }
22 }
23
maybe_var(&self) -> Option<VarIndex>24 pub fn maybe_var(&self) -> Option<VarIndex> {
25 if let Expr::Var(var) = &self {
26 Some(*var)
27 } else {
28 None
29 }
30 }
31
unwrap_var(&self) -> VarIndex32 pub fn unwrap_var(&self) -> VarIndex {
33 self.maybe_var()
34 .expect("tried to unwrap a non-Var content in Expr::unwrap_var")
35 }
36
to_rust_code(&self, var_pool: &VarPool) -> String37 pub fn to_rust_code(&self, var_pool: &VarPool) -> String {
38 match self {
39 Expr::Var(var_index) => var_pool.get(*var_index).to_rust_code(),
40 Expr::Literal(literal) => literal.to_rust_code(),
41 }
42 }
43 }
44
45 /// An AST definition associates a set of variables with the values produced by an expression.
46 pub(crate) struct Def {
47 pub apply: Apply,
48 pub defined_vars: Vec<VarIndex>,
49 }
50
51 impl Def {
to_comment_string(&self, var_pool: &VarPool) -> String52 pub fn to_comment_string(&self, var_pool: &VarPool) -> String {
53 let results = self
54 .defined_vars
55 .iter()
56 .map(|&x| var_pool.get(x).name.as_str())
57 .collect::<Vec<_>>();
58
59 let results = if results.len() == 1 {
60 results[0].to_string()
61 } else {
62 format!("({})", results.join(", "))
63 };
64
65 format!("{} := {}", results, self.apply.to_comment_string(var_pool))
66 }
67 }
68
69 pub(crate) struct DefPool {
70 pool: PrimaryMap<DefIndex, Def>,
71 }
72
73 impl DefPool {
new() -> Self74 pub fn new() -> Self {
75 Self {
76 pool: PrimaryMap::new(),
77 }
78 }
get(&self, index: DefIndex) -> &Def79 pub fn get(&self, index: DefIndex) -> &Def {
80 self.pool.get(index).unwrap()
81 }
next_index(&self) -> DefIndex82 pub fn next_index(&self) -> DefIndex {
83 self.pool.next_key()
84 }
create_inst(&mut self, apply: Apply, defined_vars: Vec<VarIndex>) -> DefIndex85 pub fn create_inst(&mut self, apply: Apply, defined_vars: Vec<VarIndex>) -> DefIndex {
86 self.pool.push(Def {
87 apply,
88 defined_vars,
89 })
90 }
91 }
92
93 #[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
94 pub(crate) struct DefIndex(u32);
95 entity_impl!(DefIndex);
96
97 /// A definition which would lead to generate a block creation.
98 #[derive(Clone)]
99 pub(crate) struct Block {
100 /// Instruction index after which the block entry is set.
101 pub location: DefIndex,
102 /// Variable holding the new created block.
103 pub name: VarIndex,
104 }
105
106 pub(crate) struct BlockPool {
107 pool: SparseMap<DefIndex, Block>,
108 }
109
110 impl SparseMapValue<DefIndex> for Block {
key(&self) -> DefIndex111 fn key(&self) -> DefIndex {
112 self.location
113 }
114 }
115
116 impl BlockPool {
new() -> Self117 pub fn new() -> Self {
118 Self {
119 pool: SparseMap::new(),
120 }
121 }
get(&self, index: DefIndex) -> Option<&Block>122 pub fn get(&self, index: DefIndex) -> Option<&Block> {
123 self.pool.get(index)
124 }
create_block(&mut self, name: VarIndex, location: DefIndex)125 pub fn create_block(&mut self, name: VarIndex, location: DefIndex) {
126 if self.pool.contains_key(location) {
127 panic!("Attempt to insert 2 blocks after the same instruction")
128 }
129 self.pool.insert(Block { location, name });
130 }
is_empty(&self) -> bool131 pub fn is_empty(&self) -> bool {
132 self.pool.is_empty()
133 }
134 }
135
136 // Implement IntoIterator such that we can iterate over blocks which are in the block pool.
137 impl<'a> IntoIterator for &'a BlockPool {
138 type Item = <&'a SparseMap<DefIndex, Block> as IntoIterator>::Item;
139 type IntoIter = <&'a SparseMap<DefIndex, Block> as IntoIterator>::IntoIter;
140
into_iter(self) -> Self::IntoIter141 fn into_iter(self) -> Self::IntoIter {
142 self.pool.into_iter()
143 }
144 }
145
146 #[derive(Clone, Debug)]
147 pub(crate) enum Literal {
148 /// A value of an enumerated immediate operand.
149 ///
150 /// Some immediate operand kinds like `intcc` and `floatcc` have an enumerated range of values
151 /// corresponding to a Rust enum type. An `Enumerator` object is an AST leaf node representing one
152 /// of the values.
153 Enumerator {
154 rust_type: &'static str,
155 value: &'static str,
156 },
157
158 /// A bitwise value of an immediate operand, used for bitwise exact floating point constants.
159 Bits { rust_type: &'static str, value: u64 },
160
161 /// A value of an integer immediate operand.
162 Int(i64),
163
164 /// A empty list of variable set of arguments.
165 EmptyVarArgs,
166 }
167
168 impl Literal {
enumerator_for(kind: &OperandKind, value: &'static str) -> Self169 pub fn enumerator_for(kind: &OperandKind, value: &'static str) -> Self {
170 let value = match &kind.fields {
171 OperandKindFields::ImmEnum(values) => values.get(value).unwrap_or_else(|| {
172 panic!(
173 "nonexistent value '{}' in enumeration '{}'",
174 value, kind.rust_type
175 )
176 }),
177 _ => panic!("enumerator is for enum values"),
178 };
179 Literal::Enumerator {
180 rust_type: kind.rust_type,
181 value,
182 }
183 }
184
bits(kind: &OperandKind, bits: u64) -> Self185 pub fn bits(kind: &OperandKind, bits: u64) -> Self {
186 match kind.fields {
187 OperandKindFields::ImmValue => {}
188 _ => panic!("bits_of is for immediate scalar types"),
189 }
190 Literal::Bits {
191 rust_type: kind.rust_type,
192 value: bits,
193 }
194 }
195
constant(kind: &OperandKind, value: i64) -> Self196 pub fn constant(kind: &OperandKind, value: i64) -> Self {
197 match kind.fields {
198 OperandKindFields::ImmValue => {}
199 _ => panic!("constant is for immediate scalar types"),
200 }
201 Literal::Int(value)
202 }
203
empty_vararg() -> Self204 pub fn empty_vararg() -> Self {
205 Literal::EmptyVarArgs
206 }
207
to_rust_code(&self) -> String208 pub fn to_rust_code(&self) -> String {
209 match self {
210 Literal::Enumerator { rust_type, value } => format!("{}::{}", rust_type, value),
211 Literal::Bits { rust_type, value } => format!("{}::with_bits({:#x})", rust_type, value),
212 Literal::Int(val) => val.to_string(),
213 Literal::EmptyVarArgs => "&[]".into(),
214 }
215 }
216 }
217
218 #[derive(Clone, Copy, Debug)]
219 pub(crate) enum PatternPosition {
220 Source,
221 Destination,
222 }
223
224 /// A free variable.
225 ///
226 /// When variables are used in `XForms` with source and destination patterns, they are classified
227 /// as follows:
228 ///
229 /// Input values: Uses in the source pattern with no preceding def. These may appear as inputs in
230 /// the destination pattern too, but no new inputs can be introduced.
231 ///
232 /// Output values: Variables that are defined in both the source and destination pattern. These
233 /// values may have uses outside the source pattern, and the destination pattern must compute the
234 /// same value.
235 ///
236 /// Intermediate values: Values that are defined in the source pattern, but not in the destination
237 /// pattern. These may have uses outside the source pattern, so the defining instruction can't be
238 /// deleted immediately.
239 ///
240 /// Temporary values are defined only in the destination pattern.
241 pub(crate) struct Var {
242 pub name: String,
243
244 /// The `Def` defining this variable in a source pattern.
245 pub src_def: Option<DefIndex>,
246
247 /// The `Def` defining this variable in a destination pattern.
248 pub dst_def: Option<DefIndex>,
249
250 /// TypeVar representing the type of this variable.
251 type_var: Option<TypeVar>,
252
253 /// Is this the original type variable, or has it be redefined with set_typevar?
254 is_original_type_var: bool,
255 }
256
257 impl Var {
new(name: String) -> Self258 fn new(name: String) -> Self {
259 Self {
260 name,
261 src_def: None,
262 dst_def: None,
263 type_var: None,
264 is_original_type_var: false,
265 }
266 }
267
268 /// Is this an input value to the src pattern?
is_input(&self) -> bool269 pub fn is_input(&self) -> bool {
270 self.src_def.is_none() && self.dst_def.is_none()
271 }
272
273 /// Is this an output value, defined in both src and dst patterns?
is_output(&self) -> bool274 pub fn is_output(&self) -> bool {
275 self.src_def.is_some() && self.dst_def.is_some()
276 }
277
278 /// Is this an intermediate value, defined only in the src pattern?
is_intermediate(&self) -> bool279 pub fn is_intermediate(&self) -> bool {
280 self.src_def.is_some() && self.dst_def.is_none()
281 }
282
283 /// Is this a temp value, defined only in the dst pattern?
is_temp(&self) -> bool284 pub fn is_temp(&self) -> bool {
285 self.src_def.is_none() && self.dst_def.is_some()
286 }
287
288 /// Get the def of this variable according to the position.
get_def(&self, position: PatternPosition) -> Option<DefIndex>289 pub fn get_def(&self, position: PatternPosition) -> Option<DefIndex> {
290 match position {
291 PatternPosition::Source => self.src_def,
292 PatternPosition::Destination => self.dst_def,
293 }
294 }
295
set_def(&mut self, position: PatternPosition, def: DefIndex)296 pub fn set_def(&mut self, position: PatternPosition, def: DefIndex) {
297 assert!(
298 self.get_def(position).is_none(),
299 "redefinition of variable {}",
300 self.name
301 );
302 match position {
303 PatternPosition::Source => {
304 self.src_def = Some(def);
305 }
306 PatternPosition::Destination => {
307 self.dst_def = Some(def);
308 }
309 }
310 }
311
312 /// Get the type variable representing the type of this variable.
get_or_create_typevar(&mut self) -> TypeVar313 pub fn get_or_create_typevar(&mut self) -> TypeVar {
314 match &self.type_var {
315 Some(tv) => tv.clone(),
316 None => {
317 // Create a new type var in which we allow all types.
318 let tv = TypeVar::new(
319 format!("typeof_{}", self.name),
320 format!("Type of the pattern variable {:?}", self),
321 TypeSetBuilder::all(),
322 );
323 self.type_var = Some(tv.clone());
324 self.is_original_type_var = true;
325 tv
326 }
327 }
328 }
get_typevar(&self) -> Option<TypeVar>329 pub fn get_typevar(&self) -> Option<TypeVar> {
330 self.type_var.clone()
331 }
set_typevar(&mut self, tv: TypeVar)332 pub fn set_typevar(&mut self, tv: TypeVar) {
333 self.is_original_type_var = if let Some(previous_tv) = &self.type_var {
334 *previous_tv == tv
335 } else {
336 false
337 };
338 self.type_var = Some(tv);
339 }
340
341 /// Check if this variable has a free type variable. If not, the type of this variable is
342 /// computed from the type of another variable.
has_free_typevar(&self) -> bool343 pub fn has_free_typevar(&self) -> bool {
344 match &self.type_var {
345 Some(tv) => tv.base.is_none() && self.is_original_type_var,
346 None => false,
347 }
348 }
349
to_rust_code(&self) -> String350 pub fn to_rust_code(&self) -> String {
351 self.name.clone()
352 }
rust_type(&self) -> String353 fn rust_type(&self) -> String {
354 self.type_var.as_ref().unwrap().to_rust_code()
355 }
356 }
357
358 impl fmt::Debug for Var {
fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error>359 fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
360 fmt.write_fmt(format_args!(
361 "Var({}{}{})",
362 self.name,
363 if self.src_def.is_some() { ", src" } else { "" },
364 if self.dst_def.is_some() { ", dst" } else { "" }
365 ))
366 }
367 }
368
369 #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
370 pub(crate) struct VarIndex(u32);
371 entity_impl!(VarIndex);
372
373 pub(crate) struct VarPool {
374 pool: PrimaryMap<VarIndex, Var>,
375 }
376
377 impl VarPool {
new() -> Self378 pub fn new() -> Self {
379 Self {
380 pool: PrimaryMap::new(),
381 }
382 }
get(&self, index: VarIndex) -> &Var383 pub fn get(&self, index: VarIndex) -> &Var {
384 self.pool.get(index).unwrap()
385 }
get_mut(&mut self, index: VarIndex) -> &mut Var386 pub fn get_mut(&mut self, index: VarIndex) -> &mut Var {
387 self.pool.get_mut(index).unwrap()
388 }
create(&mut self, name: impl Into<String>) -> VarIndex389 pub fn create(&mut self, name: impl Into<String>) -> VarIndex {
390 self.pool.push(Var::new(name.into()))
391 }
392 }
393
394 /// Contains constants created in the AST that must be inserted into the true [ConstantPool] when
395 /// the legalizer code is generated. The constant data is named in the order it is inserted;
396 /// inserting data using [insert] will avoid duplicates.
397 ///
398 /// [ConstantPool]: ../../../cranelift_codegen/ir/constant/struct.ConstantPool.html
399 /// [insert]: ConstPool::insert
400 pub(crate) struct ConstPool {
401 pool: Vec<Vec<u8>>,
402 }
403
404 impl ConstPool {
405 /// Create an empty constant pool.
new() -> Self406 pub fn new() -> Self {
407 Self { pool: vec![] }
408 }
409
410 /// Create a name for a constant from its position in the pool.
create_name(position: usize) -> String411 fn create_name(position: usize) -> String {
412 format!("const{}", position)
413 }
414
415 /// Insert constant data into the pool, returning the name of the variable used to reference it.
416 /// This method will search for data that matches the new data and return the existing constant
417 /// name to avoid duplicates.
insert(&mut self, data: Vec<u8>) -> String418 pub fn insert(&mut self, data: Vec<u8>) -> String {
419 let possible_position = self.pool.iter().position(|d| d == &data);
420 let position = if let Some(found_position) = possible_position {
421 found_position
422 } else {
423 let new_position = self.pool.len();
424 self.pool.push(data);
425 new_position
426 };
427 ConstPool::create_name(position)
428 }
429
430 /// Iterate over the name/value pairs in the pool.
iter(&self) -> impl Iterator<Item = (String, &Vec<u8>)>431 pub fn iter(&self) -> impl Iterator<Item = (String, &Vec<u8>)> {
432 self.pool
433 .iter()
434 .enumerate()
435 .map(|(i, v)| (ConstPool::create_name(i), v))
436 }
437 }
438
439 /// Apply an instruction to arguments.
440 ///
441 /// An `Apply` AST expression is created by using function call syntax on instructions. This
442 /// applies to both bound and unbound polymorphic instructions.
443 pub(crate) struct Apply {
444 pub inst: Instruction,
445 pub args: Vec<Expr>,
446 pub value_types: Vec<ValueType>,
447 }
448
449 impl Apply {
new(target: InstSpec, args: Vec<Expr>) -> Self450 pub fn new(target: InstSpec, args: Vec<Expr>) -> Self {
451 let (inst, value_types) = match target {
452 InstSpec::Inst(inst) => (inst, Vec::new()),
453 InstSpec::Bound(bound_inst) => (bound_inst.inst, bound_inst.value_types),
454 };
455
456 // Apply should only operate on concrete value types, not "any".
457 let value_types = value_types
458 .into_iter()
459 .map(|vt| vt.expect("shouldn't be Any"))
460 .collect();
461
462 // Basic check on number of arguments.
463 assert!(
464 inst.operands_in.len() == args.len(),
465 "incorrect number of arguments in instruction {}",
466 inst.name
467 );
468
469 // Check that the kinds of Literals arguments match the expected operand.
470 for &imm_index in &inst.imm_opnums {
471 let arg = &args[imm_index];
472 if let Some(literal) = arg.maybe_literal() {
473 let op = &inst.operands_in[imm_index];
474 match &op.kind.fields {
475 OperandKindFields::ImmEnum(values) => {
476 if let Literal::Enumerator { value, .. } = literal {
477 assert!(
478 values.iter().any(|(_key, v)| v == value),
479 "Nonexistent enum value '{}' passed to field of kind '{}' -- \
480 did you use the right enum?",
481 value,
482 op.kind.rust_type
483 );
484 } else {
485 panic!(
486 "Passed non-enum field value {:?} to field of kind {}",
487 literal, op.kind.rust_type
488 );
489 }
490 }
491 OperandKindFields::ImmValue => match &literal {
492 Literal::Enumerator { value, .. } => panic!(
493 "Expected immediate value in immediate field of kind '{}', \
494 obtained enum value '{}'",
495 op.kind.rust_type, value
496 ),
497 Literal::Bits { .. } | Literal::Int(_) | Literal::EmptyVarArgs => {}
498 },
499 _ => {
500 panic!(
501 "Literal passed to non-literal field of kind {}",
502 op.kind.rust_type
503 );
504 }
505 }
506 }
507 }
508
509 Self {
510 inst,
511 args,
512 value_types,
513 }
514 }
515
to_comment_string(&self, var_pool: &VarPool) -> String516 fn to_comment_string(&self, var_pool: &VarPool) -> String {
517 let args = self
518 .args
519 .iter()
520 .map(|arg| arg.to_rust_code(var_pool))
521 .collect::<Vec<_>>()
522 .join(", ");
523
524 let mut inst_and_bound_types = vec![self.inst.name.to_string()];
525 inst_and_bound_types.extend(self.value_types.iter().map(|vt| vt.to_string()));
526 let inst_name = inst_and_bound_types.join(".");
527
528 format!("{}({})", inst_name, args)
529 }
530
inst_predicate(&self, var_pool: &VarPool) -> InstructionPredicate531 pub fn inst_predicate(&self, var_pool: &VarPool) -> InstructionPredicate {
532 let mut pred = InstructionPredicate::new();
533 for (format_field, &op_num) in self
534 .inst
535 .format
536 .imm_fields
537 .iter()
538 .zip(self.inst.imm_opnums.iter())
539 {
540 let arg = &self.args[op_num];
541 if arg.maybe_var().is_some() {
542 // Ignore free variables for now.
543 continue;
544 }
545 pred = pred.and(InstructionPredicate::new_is_field_equal_ast(
546 &*self.inst.format,
547 format_field,
548 arg.to_rust_code(var_pool),
549 ));
550 }
551
552 // Add checks for any bound secondary type variables. We can't check the controlling type
553 // variable this way since it may not appear as the type of an operand.
554 if self.value_types.len() > 1 {
555 let poly = self
556 .inst
557 .polymorphic_info
558 .as_ref()
559 .expect("must have polymorphic info if it has bounded types");
560 for (bound_type, type_var) in
561 self.value_types[1..].iter().zip(poly.other_typevars.iter())
562 {
563 pred = pred.and(InstructionPredicate::new_typevar_check(
564 &self.inst, type_var, bound_type,
565 ));
566 }
567 }
568
569 pred
570 }
571
572 /// Same as `inst_predicate()`, but also check the controlling type variable.
inst_predicate_with_ctrl_typevar(&self, var_pool: &VarPool) -> InstructionPredicate573 pub fn inst_predicate_with_ctrl_typevar(&self, var_pool: &VarPool) -> InstructionPredicate {
574 let mut pred = self.inst_predicate(var_pool);
575
576 if !self.value_types.is_empty() {
577 let bound_type = &self.value_types[0];
578 let poly = self.inst.polymorphic_info.as_ref().unwrap();
579 let type_check = if poly.use_typevar_operand {
580 InstructionPredicate::new_typevar_check(&self.inst, &poly.ctrl_typevar, bound_type)
581 } else {
582 InstructionPredicate::new_ctrl_typevar_check(&bound_type)
583 };
584 pred = pred.and(type_check);
585 }
586
587 pred
588 }
589
rust_builder(&self, defined_vars: &[VarIndex], var_pool: &VarPool) -> String590 pub fn rust_builder(&self, defined_vars: &[VarIndex], var_pool: &VarPool) -> String {
591 let mut args = self
592 .args
593 .iter()
594 .map(|expr| expr.to_rust_code(var_pool))
595 .collect::<Vec<_>>()
596 .join(", ");
597
598 // Do we need to pass an explicit type argument?
599 if let Some(poly) = &self.inst.polymorphic_info {
600 if !poly.use_typevar_operand {
601 args = format!("{}, {}", var_pool.get(defined_vars[0]).rust_type(), args);
602 }
603 }
604
605 format!("{}({})", self.inst.snake_name(), args)
606 }
607 }
608
609 // Simple helpers for legalize actions construction.
610
611 pub(crate) enum DummyExpr {
612 Var(DummyVar),
613 Literal(Literal),
614 Constant(DummyConstant),
615 Apply(InstSpec, Vec<DummyExpr>),
616 Block(DummyVar),
617 }
618
619 #[derive(Clone)]
620 pub(crate) struct DummyVar {
621 pub name: String,
622 }
623
624 impl Into<DummyExpr> for DummyVar {
into(self) -> DummyExpr625 fn into(self) -> DummyExpr {
626 DummyExpr::Var(self)
627 }
628 }
629 impl Into<DummyExpr> for Literal {
into(self) -> DummyExpr630 fn into(self) -> DummyExpr {
631 DummyExpr::Literal(self)
632 }
633 }
634
635 #[derive(Clone)]
636 pub(crate) struct DummyConstant(pub(crate) Vec<u8>);
637
constant(data: Vec<u8>) -> DummyConstant638 pub(crate) fn constant(data: Vec<u8>) -> DummyConstant {
639 DummyConstant(data)
640 }
641
642 impl Into<DummyExpr> for DummyConstant {
into(self) -> DummyExpr643 fn into(self) -> DummyExpr {
644 DummyExpr::Constant(self)
645 }
646 }
647
var(name: &str) -> DummyVar648 pub(crate) fn var(name: &str) -> DummyVar {
649 DummyVar {
650 name: name.to_owned(),
651 }
652 }
653
654 pub(crate) struct DummyDef {
655 pub expr: DummyExpr,
656 pub defined_vars: Vec<DummyVar>,
657 }
658
659 pub(crate) struct ExprBuilder {
660 expr: DummyExpr,
661 }
662
663 impl ExprBuilder {
apply(inst: InstSpec, args: Vec<DummyExpr>) -> Self664 pub fn apply(inst: InstSpec, args: Vec<DummyExpr>) -> Self {
665 let expr = DummyExpr::Apply(inst, args);
666 Self { expr }
667 }
668
assign_to(self, defined_vars: Vec<DummyVar>) -> DummyDef669 pub fn assign_to(self, defined_vars: Vec<DummyVar>) -> DummyDef {
670 DummyDef {
671 expr: self.expr,
672 defined_vars,
673 }
674 }
675
block(name: DummyVar) -> Self676 pub fn block(name: DummyVar) -> Self {
677 let expr = DummyExpr::Block(name);
678 Self { expr }
679 }
680 }
681
682 macro_rules! def_rhs {
683 // inst(a, b, c)
684 ($inst:ident($($src:expr),*)) => {
685 ExprBuilder::apply($inst.into(), vec![$($src.clone().into()),*])
686 };
687
688 // inst.type(a, b, c)
689 ($inst:ident.$type:ident($($src:expr),*)) => {
690 ExprBuilder::apply($inst.bind($type).into(), vec![$($src.clone().into()),*])
691 };
692 }
693
694 // Helper macro to define legalization recipes.
695 macro_rules! def {
696 // x = ...
697 ($dest:ident = $($tt:tt)*) => {
698 def_rhs!($($tt)*).assign_to(vec![$dest.clone()])
699 };
700
701 // (x, y, ...) = ...
702 (($($dest:ident),*) = $($tt:tt)*) => {
703 def_rhs!($($tt)*).assign_to(vec![$($dest.clone()),*])
704 };
705
706 // An instruction with no results.
707 ($($tt:tt)*) => {
708 def_rhs!($($tt)*).assign_to(Vec::new())
709 }
710 }
711
712 // Helper macro to define legalization recipes.
713 macro_rules! block {
714 // a basic block definition, splitting the current block in 2.
715 ($block: ident) => {
716 ExprBuilder::block($block).assign_to(Vec::new())
717 };
718 }
719
720 #[cfg(test)]
721 mod tests {
722 use crate::cdsl::ast::ConstPool;
723
724 #[test]
const_pool_returns_var_names()725 fn const_pool_returns_var_names() {
726 let mut c = ConstPool::new();
727 assert_eq!(c.insert([0, 1, 2].to_vec()), "const0");
728 assert_eq!(c.insert([1, 2, 3].to_vec()), "const1");
729 }
730
731 #[test]
const_pool_avoids_duplicates()732 fn const_pool_avoids_duplicates() {
733 let data = [0, 1, 2].to_vec();
734 let mut c = ConstPool::new();
735 assert_eq!(c.pool.len(), 0);
736
737 assert_eq!(c.insert(data.clone()), "const0");
738 assert_eq!(c.pool.len(), 1);
739
740 assert_eq!(c.insert(data), "const0");
741 assert_eq!(c.pool.len(), 1);
742 }
743
744 #[test]
const_pool_iterates()745 fn const_pool_iterates() {
746 let mut c = ConstPool::new();
747 c.insert([0, 1, 2].to_vec());
748 c.insert([3, 4, 5].to_vec());
749
750 let mut iter = c.iter();
751 assert_eq!(iter.next(), Some(("const0".to_owned(), &vec![0, 1, 2])));
752 assert_eq!(iter.next(), Some(("const1".to_owned(), &vec![3, 4, 5])));
753 assert_eq!(iter.next(), None);
754 }
755 }
756