Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bytecodealliance
GitHub Repository: bytecodealliance/wasmtime
Path: blob/main/cranelift/codegen/src/context.rs
1693 views
1
//! Cranelift compilation context and main entry point.
2
//!
3
//! When compiling many small functions, it is important to avoid repeatedly allocating and
4
//! deallocating the data structures needed for compilation. The `Context` struct is used to hold
5
//! on to memory allocations between function compilations.
6
//!
7
//! The context does not hold a `TargetIsa` instance which has to be provided as an argument
8
//! instead. This is because an ISA instance is immutable and can be used by multiple compilation
9
//! contexts concurrently. Typically, you would have one context per compilation thread and only a
10
//! single ISA instance.
11
12
use crate::alias_analysis::AliasAnalysis;
13
use crate::dominator_tree::DominatorTree;
14
use crate::egraph::EgraphPass;
15
use crate::flowgraph::ControlFlowGraph;
16
use crate::inline::{Inline, do_inlining};
17
use crate::ir::Function;
18
use crate::isa::TargetIsa;
19
use crate::legalizer::simple_legalize;
20
use crate::loop_analysis::LoopAnalysis;
21
use crate::machinst::{CompiledCode, CompiledCodeStencil};
22
use crate::nan_canonicalization::do_nan_canonicalization;
23
use crate::remove_constant_phis::do_remove_constant_phis;
24
use crate::result::{CodegenResult, CompileResult};
25
use crate::settings::{FlagsOrIsa, OptLevel};
26
use crate::trace;
27
use crate::unreachable_code::eliminate_unreachable_code;
28
use crate::verifier::{VerifierErrors, VerifierResult, verify_context};
29
use crate::{CompileError, timing};
30
#[cfg(feature = "souper-harvest")]
31
use alloc::string::String;
32
use alloc::vec::Vec;
33
use cranelift_control::ControlPlane;
34
use target_lexicon::Architecture;
35
36
#[cfg(feature = "souper-harvest")]
37
use crate::souper_harvest::do_souper_harvest;
38
39
/// Persistent data structures and compilation pipeline.
40
pub struct Context {
41
/// The function we're compiling.
42
pub func: Function,
43
44
/// The control flow graph of `func`.
45
pub cfg: ControlFlowGraph,
46
47
/// Dominator tree for `func`.
48
pub domtree: DominatorTree,
49
50
/// Loop analysis of `func`.
51
pub loop_analysis: LoopAnalysis,
52
53
/// Result of MachBackend compilation, if computed.
54
pub(crate) compiled_code: Option<CompiledCode>,
55
56
/// Flag: do we want a disassembly with the CompiledCode?
57
pub want_disasm: bool,
58
}
59
60
impl Context {
61
/// Allocate a new compilation context.
62
///
63
/// The returned instance should be reused for compiling multiple functions in order to avoid
64
/// needless allocator thrashing.
65
pub fn new() -> Self {
66
Self::for_function(Function::new())
67
}
68
69
/// Allocate a new compilation context with an existing Function.
70
///
71
/// The returned instance should be reused for compiling multiple functions in order to avoid
72
/// needless allocator thrashing.
73
pub fn for_function(func: Function) -> Self {
74
Self {
75
func,
76
cfg: ControlFlowGraph::new(),
77
domtree: DominatorTree::new(),
78
loop_analysis: LoopAnalysis::new(),
79
compiled_code: None,
80
want_disasm: false,
81
}
82
}
83
84
/// Clear all data structures in this context.
85
pub fn clear(&mut self) {
86
self.func.clear();
87
self.cfg.clear();
88
self.domtree.clear();
89
self.loop_analysis.clear();
90
self.compiled_code = None;
91
self.want_disasm = false;
92
}
93
94
/// Returns the compilation result for this function, available after any `compile` function
95
/// has been called.
96
pub fn compiled_code(&self) -> Option<&CompiledCode> {
97
self.compiled_code.as_ref()
98
}
99
100
/// Returns the compilation result for this function, available after any `compile` function
101
/// has been called.
102
pub fn take_compiled_code(&mut self) -> Option<CompiledCode> {
103
self.compiled_code.take()
104
}
105
106
/// Set the flag to request a disassembly when compiling with a
107
/// `MachBackend` backend.
108
pub fn set_disasm(&mut self, val: bool) {
109
self.want_disasm = val;
110
}
111
112
/// Compile the function, and emit machine code into a `Vec<u8>`.
113
#[deprecated = "use Context::compile"]
114
pub fn compile_and_emit(
115
&mut self,
116
isa: &dyn TargetIsa,
117
mem: &mut Vec<u8>,
118
ctrl_plane: &mut ControlPlane,
119
) -> CompileResult<'_, &CompiledCode> {
120
let compiled_code = self.compile(isa, ctrl_plane)?;
121
mem.extend_from_slice(compiled_code.code_buffer());
122
Ok(compiled_code)
123
}
124
125
/// Internally compiles the function into a stencil.
126
///
127
/// Public only for testing and fuzzing purposes.
128
pub fn compile_stencil(
129
&mut self,
130
isa: &dyn TargetIsa,
131
ctrl_plane: &mut ControlPlane,
132
) -> CodegenResult<CompiledCodeStencil> {
133
let result;
134
trace!("****** START compiling {}", self.func.display_spec());
135
{
136
let _tt = timing::compile();
137
138
self.verify_if(isa)?;
139
self.optimize(isa, ctrl_plane)?;
140
result = isa.compile_function(&self.func, &self.domtree, self.want_disasm, ctrl_plane);
141
}
142
trace!("****** DONE compiling {}\n", self.func.display_spec());
143
result
144
}
145
146
/// Optimize the function, performing all compilation steps up to
147
/// but not including machine-code lowering and register
148
/// allocation.
149
///
150
/// Public only for testing purposes.
151
pub fn optimize(
152
&mut self,
153
isa: &dyn TargetIsa,
154
ctrl_plane: &mut ControlPlane,
155
) -> CodegenResult<()> {
156
log::debug!(
157
"Number of CLIF instructions to optimize: {}",
158
self.func.dfg.num_insts()
159
);
160
log::debug!(
161
"Number of CLIF blocks to optimize: {}",
162
self.func.dfg.num_blocks()
163
);
164
165
let opt_level = isa.flags().opt_level();
166
crate::trace!(
167
"Optimizing (opt level {:?}):\n{}",
168
opt_level,
169
self.func.display()
170
);
171
172
if isa.flags().enable_nan_canonicalization() {
173
self.canonicalize_nans(isa)?;
174
}
175
176
self.legalize(isa)?;
177
178
self.compute_cfg();
179
self.compute_domtree();
180
self.eliminate_unreachable_code(isa)?;
181
self.remove_constant_phis(isa)?;
182
183
self.func.dfg.resolve_all_aliases();
184
185
if opt_level != OptLevel::None {
186
self.egraph_pass(isa, ctrl_plane)?;
187
}
188
189
Ok(())
190
}
191
192
/// Perform function call inlining.
193
///
194
/// Returns `true` if any function call was inlined, `false` otherwise.
195
pub fn inline(&mut self, inliner: impl Inline) -> CodegenResult<bool> {
196
do_inlining(&mut self.func, inliner)
197
}
198
199
/// Compile the function,
200
///
201
/// Run the function through all the passes necessary to generate
202
/// code for the target ISA represented by `isa`. The generated
203
/// machine code is not relocated. Instead, any relocations can be
204
/// obtained from `compiled_code.buffer.relocs()`.
205
///
206
/// Performs any optimizations that are enabled, unless
207
/// `optimize()` was already invoked.
208
///
209
/// Returns the generated machine code as well as information about
210
/// the function's code and read-only data.
211
pub fn compile(
212
&mut self,
213
isa: &dyn TargetIsa,
214
ctrl_plane: &mut ControlPlane,
215
) -> CompileResult<'_, &CompiledCode> {
216
let stencil = self
217
.compile_stencil(isa, ctrl_plane)
218
.map_err(|error| CompileError {
219
inner: error,
220
func: &self.func,
221
})?;
222
Ok(self
223
.compiled_code
224
.insert(stencil.apply_params(&self.func.params)))
225
}
226
227
/// If available, return information about the code layout in the
228
/// final machine code: the offsets (in bytes) of each basic-block
229
/// start, and all basic-block edges.
230
#[deprecated = "use CompiledCode::get_code_bb_layout"]
231
pub fn get_code_bb_layout(&self) -> Option<(Vec<usize>, Vec<(usize, usize)>)> {
232
self.compiled_code().map(CompiledCode::get_code_bb_layout)
233
}
234
235
/// Creates unwind information for the function.
236
///
237
/// Returns `None` if the function has no unwind information.
238
#[cfg(feature = "unwind")]
239
#[deprecated = "use CompiledCode::create_unwind_info"]
240
pub fn create_unwind_info(
241
&self,
242
isa: &dyn TargetIsa,
243
) -> CodegenResult<Option<crate::isa::unwind::UnwindInfo>> {
244
self.compiled_code().unwrap().create_unwind_info(isa)
245
}
246
247
/// Run the verifier on the function.
248
///
249
/// Also check that the dominator tree and control flow graph are consistent with the function.
250
///
251
/// TODO: rename to "CLIF validate" or similar.
252
pub fn verify<'a, FOI: Into<FlagsOrIsa<'a>>>(&self, fisa: FOI) -> VerifierResult<()> {
253
let mut errors = VerifierErrors::default();
254
let _ = verify_context(&self.func, &self.cfg, &self.domtree, fisa, &mut errors);
255
256
if errors.is_empty() {
257
Ok(())
258
} else {
259
Err(errors)
260
}
261
}
262
263
/// Run the verifier only if the `enable_verifier` setting is true.
264
pub fn verify_if<'a, FOI: Into<FlagsOrIsa<'a>>>(&self, fisa: FOI) -> CodegenResult<()> {
265
let fisa = fisa.into();
266
if fisa.flags.enable_verifier() {
267
self.verify(fisa)?;
268
}
269
Ok(())
270
}
271
272
/// Perform constant-phi removal on the function.
273
pub fn remove_constant_phis<'a, FOI: Into<FlagsOrIsa<'a>>>(
274
&mut self,
275
fisa: FOI,
276
) -> CodegenResult<()> {
277
do_remove_constant_phis(&mut self.func, &mut self.domtree);
278
self.verify_if(fisa)?;
279
Ok(())
280
}
281
282
/// Perform NaN canonicalizing rewrites on the function.
283
pub fn canonicalize_nans(&mut self, isa: &dyn TargetIsa) -> CodegenResult<()> {
284
// Currently only RiscV64 is the only arch that may not have vector support.
285
let has_vector_support = match isa.triple().architecture {
286
Architecture::Riscv64(_) => match isa.isa_flags().iter().find(|f| f.name == "has_v") {
287
Some(value) => value.as_bool().unwrap_or(false),
288
None => false,
289
},
290
_ => true,
291
};
292
do_nan_canonicalization(&mut self.func, has_vector_support);
293
self.verify_if(isa)
294
}
295
296
/// Run the legalizer for `isa` on the function.
297
pub fn legalize(&mut self, isa: &dyn TargetIsa) -> CodegenResult<()> {
298
// Legalization invalidates the domtree and loop_analysis by mutating the CFG.
299
// TODO: Avoid doing this when legalization doesn't actually mutate the CFG.
300
self.domtree.clear();
301
self.loop_analysis.clear();
302
self.cfg.clear();
303
304
// Run some specific legalizations only.
305
simple_legalize(&mut self.func, isa);
306
self.verify_if(isa)
307
}
308
309
/// Compute the control flow graph.
310
pub fn compute_cfg(&mut self) {
311
self.cfg.compute(&self.func)
312
}
313
314
/// Compute dominator tree.
315
pub fn compute_domtree(&mut self) {
316
self.domtree.compute(&self.func, &self.cfg);
317
}
318
319
/// Compute the loop analysis.
320
pub fn compute_loop_analysis(&mut self) {
321
self.loop_analysis
322
.compute(&self.func, &self.cfg, &self.domtree)
323
}
324
325
/// Compute the control flow graph and dominator tree.
326
pub fn flowgraph(&mut self) {
327
self.compute_cfg();
328
self.compute_domtree()
329
}
330
331
/// Perform unreachable code elimination.
332
pub fn eliminate_unreachable_code<'a, FOI>(&mut self, fisa: FOI) -> CodegenResult<()>
333
where
334
FOI: Into<FlagsOrIsa<'a>>,
335
{
336
eliminate_unreachable_code(&mut self.func, &mut self.cfg, &self.domtree);
337
self.verify_if(fisa)
338
}
339
340
/// Replace all redundant loads with the known values in
341
/// memory. These are loads whose values were already loaded by
342
/// other loads earlier, as well as loads whose values were stored
343
/// by a store instruction to the same instruction (so-called
344
/// "store-to-load forwarding").
345
pub fn replace_redundant_loads(&mut self) -> CodegenResult<()> {
346
let mut analysis = AliasAnalysis::new(&self.func, &self.domtree);
347
analysis.compute_and_update_aliases(&mut self.func);
348
Ok(())
349
}
350
351
/// Harvest candidate left-hand sides for superoptimization with Souper.
352
#[cfg(feature = "souper-harvest")]
353
pub fn souper_harvest(
354
&mut self,
355
out: &mut std::sync::mpsc::Sender<String>,
356
) -> CodegenResult<()> {
357
do_souper_harvest(&self.func, out);
358
Ok(())
359
}
360
361
/// Run optimizations via the egraph infrastructure.
362
pub fn egraph_pass<'a, FOI>(
363
&mut self,
364
fisa: FOI,
365
ctrl_plane: &mut ControlPlane,
366
) -> CodegenResult<()>
367
where
368
FOI: Into<FlagsOrIsa<'a>>,
369
{
370
let _tt = timing::egraph();
371
372
trace!(
373
"About to optimize with egraph phase:\n{}",
374
self.func.display()
375
);
376
let fisa = fisa.into();
377
self.compute_loop_analysis();
378
let mut alias_analysis = AliasAnalysis::new(&self.func, &self.domtree);
379
let mut pass = EgraphPass::new(
380
&mut self.func,
381
&self.domtree,
382
&self.loop_analysis,
383
&mut alias_analysis,
384
&fisa.flags,
385
ctrl_plane,
386
);
387
pass.run();
388
log::debug!("egraph stats: {:?}", pass.stats);
389
trace!("After egraph optimization:\n{}", self.func.display());
390
391
self.verify_if(fisa)
392
}
393
}
394
395