Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bytecodealliance
GitHub Repository: bytecodealliance/wasmtime
Path: blob/main/cranelift/codegen/src/opts.rs
1693 views
1
//! Optimization driver using ISLE rewrite rules on an egraph.
2
3
mod div_const;
4
5
use crate::egraph::{NewOrExistingInst, OptimizeCtx};
6
pub use crate::ir::condcodes::{FloatCC, IntCC};
7
use crate::ir::dfg::ValueDef;
8
pub use crate::ir::immediates::{Ieee16, Ieee32, Ieee64, Ieee128, Imm64, Offset32, Uimm8, V128Imm};
9
use crate::ir::instructions::InstructionFormat;
10
pub use crate::ir::types::*;
11
pub use crate::ir::{
12
AtomicRmwOp, BlockCall, Constant, DynamicStackSlot, FuncRef, GlobalValue, Immediate,
13
InstructionData, MemFlags, Opcode, StackSlot, TrapCode, Type, Value,
14
};
15
use crate::isle_common_prelude_methods;
16
use crate::machinst::isle::*;
17
use crate::trace;
18
use cranelift_entity::packed_option::ReservedValue;
19
use smallvec::{SmallVec, smallvec};
20
use std::marker::PhantomData;
21
22
pub type Unit = ();
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
impl IsleContext<'_, '_, '_> {
47
#[allow(dead_code, reason = "dead code, only on nightly rust at this time")]
48
pub(crate) fn dfg(&self) -> &crate::ir::DataFlowGraph {
49
&self.ctx.func.dfg
50
}
51
}
52
53
pub(crate) struct InstDataEtorIter<'a, 'b, 'c> {
54
stack: SmallVec<[Value; 8]>,
55
_phantom1: PhantomData<&'a ()>,
56
_phantom2: PhantomData<&'b ()>,
57
_phantom3: PhantomData<&'c ()>,
58
}
59
60
impl Default for InstDataEtorIter<'_, '_, '_> {
61
fn default() -> Self {
62
InstDataEtorIter {
63
stack: SmallVec::default(),
64
_phantom1: PhantomData,
65
_phantom2: PhantomData,
66
_phantom3: PhantomData,
67
}
68
}
69
}
70
71
impl<'a, 'b, 'c> InstDataEtorIter<'a, 'b, 'c> {
72
fn new(root: Value) -> Self {
73
debug_assert_ne!(root, Value::reserved_value());
74
trace!("new iter from root {root}");
75
Self {
76
stack: smallvec![root],
77
_phantom1: PhantomData,
78
_phantom2: PhantomData,
79
_phantom3: PhantomData,
80
}
81
}
82
}
83
84
impl<'a, 'b, 'c> ContextIter for InstDataEtorIter<'a, 'b, 'c>
85
where
86
'b: 'a,
87
'c: 'b,
88
{
89
type Context = IsleContext<'a, 'b, 'c>;
90
type Output = (Type, InstructionData);
91
92
fn next(&mut self, ctx: &mut IsleContext<'a, 'b, 'c>) -> Option<Self::Output> {
93
while let Some(value) = self.stack.pop() {
94
debug_assert!(ctx.ctx.func.dfg.value_is_real(value));
95
trace!("iter: value {:?}", value);
96
match ctx.ctx.func.dfg.value_def(value) {
97
ValueDef::Union(x, y) => {
98
debug_assert_ne!(x, Value::reserved_value());
99
debug_assert_ne!(y, Value::reserved_value());
100
trace!(" -> {}, {}", x, y);
101
self.stack.push(x);
102
self.stack.push(y);
103
continue;
104
}
105
ValueDef::Result(inst, _) if ctx.ctx.func.dfg.inst_results(inst).len() == 1 => {
106
let ty = ctx.ctx.func.dfg.value_type(value);
107
trace!(" -> value of type {}", ty);
108
return Some((ty, ctx.ctx.func.dfg.insts[inst]));
109
}
110
_ => {}
111
}
112
}
113
None
114
}
115
}
116
117
impl<'a, 'b, 'c> IntoContextIter for InstDataEtorIter<'a, 'b, 'c>
118
where
119
'b: 'a,
120
'c: 'b,
121
{
122
type Context = IsleContext<'a, 'b, 'c>;
123
type Output = (Type, InstructionData);
124
type IntoIter = Self;
125
126
fn into_context_iter(self) -> Self {
127
self
128
}
129
}
130
131
#[derive(Default)]
132
pub(crate) struct MaybeUnaryEtorIter<'a, 'b, 'c> {
133
opcode: Option<Opcode>,
134
inner: InstDataEtorIter<'a, 'b, 'c>,
135
fallback: Option<Value>,
136
}
137
138
impl MaybeUnaryEtorIter<'_, '_, '_> {
139
fn new(opcode: Opcode, value: Value) -> Self {
140
debug_assert_eq!(opcode.format(), InstructionFormat::Unary);
141
Self {
142
opcode: Some(opcode),
143
inner: InstDataEtorIter::new(value),
144
fallback: Some(value),
145
}
146
}
147
}
148
149
impl<'a, 'b, 'c> ContextIter for MaybeUnaryEtorIter<'a, 'b, 'c>
150
where
151
'b: 'a,
152
'c: 'b,
153
{
154
type Context = IsleContext<'a, 'b, 'c>;
155
type Output = (Type, Value);
156
157
fn next(&mut self, ctx: &mut IsleContext<'a, 'b, 'c>) -> Option<Self::Output> {
158
debug_assert_ne!(self.opcode, None);
159
while let Some((ty, inst_def)) = self.inner.next(ctx) {
160
let InstructionData::Unary { opcode, arg } = inst_def else {
161
continue;
162
};
163
if Some(opcode) == self.opcode {
164
self.fallback = None;
165
return Some((ty, arg));
166
}
167
}
168
169
self.fallback.take().map(|value| {
170
let ty = generated_code::Context::value_type(ctx, value);
171
(ty, value)
172
})
173
}
174
}
175
176
impl<'a, 'b, 'c> IntoContextIter for MaybeUnaryEtorIter<'a, 'b, 'c>
177
where
178
'b: 'a,
179
'c: 'b,
180
{
181
type Context = IsleContext<'a, 'b, 'c>;
182
type Output = (Type, Value);
183
type IntoIter = Self;
184
185
fn into_context_iter(self) -> Self {
186
self
187
}
188
}
189
190
impl<'a, 'b, 'c> generated_code::Context for IsleContext<'a, 'b, 'c> {
191
isle_common_prelude_methods!();
192
193
type inst_data_value_etor_returns = InstDataEtorIter<'a, 'b, 'c>;
194
195
fn inst_data_value_etor(&mut self, eclass: Value, returns: &mut InstDataEtorIter<'a, 'b, 'c>) {
196
*returns = InstDataEtorIter::new(eclass);
197
}
198
199
type inst_data_value_tupled_etor_returns = InstDataEtorIter<'a, 'b, 'c>;
200
201
fn inst_data_value_tupled_etor(
202
&mut self,
203
eclass: Value,
204
returns: &mut InstDataEtorIter<'a, 'b, 'c>,
205
) {
206
// Literally identical to `inst_data_value_etor`, just a different nominal type in ISLE
207
self.inst_data_value_etor(eclass, returns);
208
}
209
210
fn make_inst_ctor(&mut self, ty: Type, op: &InstructionData) -> Value {
211
trace!("make_inst_ctor: creating {:?}", op);
212
let value = self.ctx.insert_pure_enode(NewOrExistingInst::New(*op, ty));
213
trace!("make_inst_ctor: {:?} -> {}", op, value);
214
value
215
}
216
217
fn make_skeleton_inst_ctor(&mut self, data: &InstructionData) -> Inst {
218
let inst = self.ctx.func.dfg.make_inst(*data);
219
self.ctx
220
.func
221
.dfg
222
.make_inst_results(inst, Default::default());
223
inst
224
}
225
226
fn inst_data_etor(&mut self, inst: Inst) -> Option<InstructionData> {
227
Some(self.ctx.func.dfg.insts[inst])
228
}
229
230
fn value_array_2_ctor(&mut self, arg0: Value, arg1: Value) -> ValueArray2 {
231
[arg0, arg1]
232
}
233
234
fn value_array_3_ctor(&mut self, arg0: Value, arg1: Value, arg2: Value) -> ValueArray3 {
235
[arg0, arg1, arg2]
236
}
237
238
#[inline]
239
fn value_type(&mut self, val: Value) -> Type {
240
self.ctx.func.dfg.value_type(val)
241
}
242
243
fn iconst_sextend_etor(
244
&mut self,
245
(ty, inst_data): (Type, InstructionData),
246
) -> Option<(Type, i64)> {
247
if let InstructionData::UnaryImm {
248
opcode: Opcode::Iconst,
249
imm,
250
} = inst_data
251
{
252
Some((ty, self.i64_sextend_imm64(ty, imm)))
253
} else {
254
None
255
}
256
}
257
258
fn remat(&mut self, value: Value) -> Value {
259
trace!("remat: {}", value);
260
self.ctx.remat_values.insert(value);
261
self.ctx.stats.remat += 1;
262
value
263
}
264
265
fn subsume(&mut self, value: Value) -> Value {
266
trace!("subsume: {}", value);
267
self.ctx.subsume_values.insert(value);
268
self.ctx.stats.subsume += 1;
269
value
270
}
271
272
fn splat64(&mut self, val: u64) -> Constant {
273
let val = u128::from(val);
274
let val = val | (val << 64);
275
let imm = V128Imm(val.to_le_bytes());
276
self.ctx.func.dfg.constants.insert(imm.into())
277
}
278
279
type sextend_maybe_etor_returns = MaybeUnaryEtorIter<'a, 'b, 'c>;
280
fn sextend_maybe_etor(&mut self, value: Value, returns: &mut Self::sextend_maybe_etor_returns) {
281
*returns = MaybeUnaryEtorIter::new(Opcode::Sextend, value);
282
}
283
284
type uextend_maybe_etor_returns = MaybeUnaryEtorIter<'a, 'b, 'c>;
285
fn uextend_maybe_etor(&mut self, value: Value, returns: &mut Self::uextend_maybe_etor_returns) {
286
*returns = MaybeUnaryEtorIter::new(Opcode::Uextend, value);
287
}
288
289
// NB: Cranelift's defined semantics for `fcvt_from_{s,u}int` match Rust's
290
// own semantics for converting an integer to a float, so these are all
291
// implemented with `as` conversions in Rust.
292
fn f32_from_uint(&mut self, n: u64) -> Ieee32 {
293
Ieee32::with_float(n as f32)
294
}
295
296
fn f64_from_uint(&mut self, n: u64) -> Ieee64 {
297
Ieee64::with_float(n as f64)
298
}
299
300
fn f32_from_sint(&mut self, n: i64) -> Ieee32 {
301
Ieee32::with_float(n as f32)
302
}
303
304
fn f64_from_sint(&mut self, n: i64) -> Ieee64 {
305
Ieee64::with_float(n as f64)
306
}
307
308
fn u64_bswap16(&mut self, n: u64) -> u64 {
309
(n as u16).swap_bytes() as u64
310
}
311
312
fn u64_bswap32(&mut self, n: u64) -> u64 {
313
(n as u32).swap_bytes() as u64
314
}
315
316
fn u64_bswap64(&mut self, n: u64) -> u64 {
317
n.swap_bytes()
318
}
319
320
fn ieee128_constant_extractor(&mut self, n: Constant) -> Option<Ieee128> {
321
self.ctx.func.dfg.constants.get(n).try_into().ok()
322
}
323
324
fn ieee128_constant(&mut self, n: Ieee128) -> Constant {
325
self.ctx.func.dfg.constants.insert(n.into())
326
}
327
328
fn div_const_magic_u32(&mut self, d: u32) -> generated_code::DivConstMagicU32 {
329
let div_const::MU32 {
330
mul_by,
331
do_add,
332
shift_by,
333
} = div_const::magic_u32(d);
334
generated_code::DivConstMagicU32::U32 {
335
mul_by,
336
do_add,
337
shift_by: shift_by.try_into().unwrap(),
338
}
339
}
340
341
fn div_const_magic_u64(&mut self, d: u64) -> generated_code::DivConstMagicU64 {
342
let div_const::MU64 {
343
mul_by,
344
do_add,
345
shift_by,
346
} = div_const::magic_u64(d);
347
generated_code::DivConstMagicU64::U64 {
348
mul_by,
349
do_add,
350
shift_by: shift_by.try_into().unwrap(),
351
}
352
}
353
354
fn div_const_magic_s32(&mut self, d: i32) -> generated_code::DivConstMagicS32 {
355
let div_const::MS32 { mul_by, shift_by } = div_const::magic_s32(d);
356
generated_code::DivConstMagicS32::S32 {
357
mul_by,
358
shift_by: shift_by.try_into().unwrap(),
359
}
360
}
361
362
fn div_const_magic_s64(&mut self, d: i64) -> generated_code::DivConstMagicS64 {
363
let div_const::MS64 { mul_by, shift_by } = div_const::magic_s64(d);
364
generated_code::DivConstMagicS64::S64 {
365
mul_by,
366
shift_by: shift_by.try_into().unwrap(),
367
}
368
}
369
}
370
371