1 //! Optimization driver using ISLE rewrite rules on an egraph.
2 
3 use crate::egraph::{NewOrExistingInst, OptimizeCtx};
4 use crate::ir::condcodes;
5 pub use crate::ir::condcodes::{FloatCC, IntCC};
6 use crate::ir::dfg::ValueDef;
7 pub use crate::ir::immediates::{Ieee32, Ieee64, Imm64, Offset32, Uimm8, V128Imm};
8 pub use crate::ir::types::*;
9 pub use crate::ir::{
10     dynamic_to_fixed, AtomicRmwOp, BlockCall, Constant, DynamicStackSlot, FuncRef, GlobalValue,
11     Immediate, InstructionData, MemFlags, Opcode, StackSlot, Table, TrapCode, Type, Value,
12 };
13 use crate::isle_common_prelude_methods;
14 use crate::machinst::isle::*;
15 use crate::trace;
16 use cranelift_entity::packed_option::ReservedValue;
17 use smallvec::{smallvec, SmallVec};
18 use std::marker::PhantomData;
19 
20 #[allow(dead_code)]
21 pub type Unit = ();
22 pub type Range = (usize, usize);
23 pub type ValueArray2 = [Value; 2];
24 pub type ValueArray3 = [Value; 3];
25 
26 const MAX_ISLE_RETURNS: usize = 8;
27 
28 pub type ConstructorVec<T> = SmallVec<[T; MAX_ISLE_RETURNS]>;
29 
30 type TypeAndInstructionData = (Type, InstructionData);
31 
32 impl<T: smallvec::Array> generated_code::Length for SmallVec<T> {
33     #[inline]
34     fn len(&self) -> usize {
35         SmallVec::len(self)
36     }
37 }
38 
39 pub(crate) mod generated_code;
40 use generated_code::{ContextIter, IntoContextIter};
41 
42 pub(crate) struct IsleContext<'a, 'b, 'c> {
43     pub(crate) ctx: &'a mut OptimizeCtx<'b, 'c>,
44 }
45 
46 pub(crate) struct InstDataEtorIter<'a, 'b, 'c> {
47     stack: SmallVec<[Value; 8]>,
48     _phantom1: PhantomData<&'a ()>,
49     _phantom2: PhantomData<&'b ()>,
50     _phantom3: PhantomData<&'c ()>,
51 }
52 
53 impl Default for InstDataEtorIter<'_, '_, '_> {
54     fn default() -> Self {
55         InstDataEtorIter {
56             stack: SmallVec::default(),
57             _phantom1: PhantomData,
58             _phantom2: PhantomData,
59             _phantom3: PhantomData,
60         }
61     }
62 }
63 
64 impl<'a, 'b, 'c> InstDataEtorIter<'a, 'b, 'c> {
65     fn new(root: Value) -> Self {
66         debug_assert_ne!(root, Value::reserved_value());
67         Self {
68             stack: smallvec![root],
69             _phantom1: PhantomData,
70             _phantom2: PhantomData,
71             _phantom3: PhantomData,
72         }
73     }
74 }
75 
76 impl<'a, 'b, 'c> ContextIter for InstDataEtorIter<'a, 'b, 'c>
77 where
78     'b: 'a,
79     'c: 'b,
80 {
81     type Context = IsleContext<'a, 'b, 'c>;
82     type Output = (Type, InstructionData);
83 
84     fn next(&mut self, ctx: &mut IsleContext<'a, 'b, 'c>) -> Option<Self::Output> {
85         while let Some(value) = self.stack.pop() {
86             debug_assert_ne!(value, Value::reserved_value());
87             let value = ctx.ctx.func.dfg.resolve_aliases(value);
88             trace!("iter: value {:?}", value);
89             match ctx.ctx.func.dfg.value_def(value) {
90                 ValueDef::Union(x, y) => {
91                     debug_assert_ne!(x, Value::reserved_value());
92                     debug_assert_ne!(y, Value::reserved_value());
93                     trace!(" -> {}, {}", x, y);
94                     self.stack.push(x);
95                     self.stack.push(y);
96                     continue;
97                 }
98                 ValueDef::Result(inst, _) if ctx.ctx.func.dfg.inst_results(inst).len() == 1 => {
99                     let ty = ctx.ctx.func.dfg.value_type(value);
100                     trace!(" -> value of type {}", ty);
101                     return Some((ty, ctx.ctx.func.dfg.insts[inst].clone()));
102                 }
103                 _ => {}
104             }
105         }
106         None
107     }
108 }
109 
110 impl<'a, 'b, 'c> IntoContextIter for InstDataEtorIter<'a, 'b, 'c>
111 where
112     'b: 'a,
113     'c: 'b,
114 {
115     type Context = IsleContext<'a, 'b, 'c>;
116     type Output = (Type, InstructionData);
117     type IntoIter = Self;
118 
119     fn into_context_iter(self) -> Self {
120         self
121     }
122 }
123 
124 impl<'a, 'b, 'c> generated_code::Context for IsleContext<'a, 'b, 'c> {
125     isle_common_prelude_methods!();
126 
127     type inst_data_etor_returns = InstDataEtorIter<'a, 'b, 'c>;
128 
129     fn inst_data_etor(&mut self, eclass: Value, returns: &mut InstDataEtorIter<'a, 'b, 'c>) {
130         *returns = InstDataEtorIter::new(eclass);
131     }
132 
133     type inst_data_tupled_etor_returns = InstDataEtorIter<'a, 'b, 'c>;
134 
135     fn inst_data_tupled_etor(&mut self, eclass: Value, returns: &mut InstDataEtorIter<'a, 'b, 'c>) {
136         // Literally identical to `inst_data_etor`, just a different nominal type in ISLE
137         self.inst_data_etor(eclass, returns);
138     }
139 
140     fn make_inst_ctor(&mut self, ty: Type, op: &InstructionData) -> Value {
141         let value = self
142             .ctx
143             .insert_pure_enode(NewOrExistingInst::New(op.clone(), ty));
144         trace!("make_inst_ctor: {:?} -> {}", op, value);
145         value
146     }
147 
148     fn value_array_2_ctor(&mut self, arg0: Value, arg1: Value) -> ValueArray2 {
149         [arg0, arg1]
150     }
151 
152     fn value_array_3_ctor(&mut self, arg0: Value, arg1: Value, arg2: Value) -> ValueArray3 {
153         [arg0, arg1, arg2]
154     }
155 
156     #[inline]
157     fn value_type(&mut self, val: Value) -> Type {
158         self.ctx.func.dfg.value_type(val)
159     }
160 
161     fn iconst_sextend_etor(
162         &mut self,
163         (ty, inst_data): (Type, InstructionData),
164     ) -> Option<(Type, i64)> {
165         if let InstructionData::UnaryImm {
166             opcode: Opcode::Iconst,
167             imm,
168         } = inst_data
169         {
170             Some((ty, self.i64_sextend_imm64(ty, imm)))
171         } else {
172             None
173         }
174     }
175 
176     fn remat(&mut self, value: Value) -> Value {
177         trace!("remat: {}", value);
178         self.ctx.remat_values.insert(value);
179         self.ctx.stats.remat += 1;
180         value
181     }
182 
183     fn subsume(&mut self, value: Value) -> Value {
184         trace!("subsume: {}", value);
185         self.ctx.subsume_values.insert(value);
186         self.ctx.stats.subsume += 1;
187         value
188     }
189 
190     fn splat64(&mut self, val: u64) -> Constant {
191         let val = u128::from(val);
192         let val = val | (val << 64);
193         let imm = V128Imm(val.to_le_bytes());
194         self.ctx.func.dfg.constants.insert(imm.into())
195     }
196 }
197