Coverage Report

Created: 2026-08-02 07:35

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