Move logic out of cton-util wasm.
Give LoopAnalysis `is_valid` and `ensure` functions similar to DominatorTree and others, so that it can be computed on demand in the same way. This removes the last need for src/wasm.rs to have embedded knowledge of the dependencies of the passes it's running.
This commit is contained in:
@@ -6,9 +6,6 @@
|
|||||||
|
|
||||||
use cton_wasm::{translate_module, DummyRuntime, WasmRuntime};
|
use cton_wasm::{translate_module, DummyRuntime, WasmRuntime};
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use cretonne::loop_analysis::LoopAnalysis;
|
|
||||||
use cretonne::flowgraph::ControlFlowGraph;
|
|
||||||
use cretonne::dominator_tree::DominatorTree;
|
|
||||||
use cretonne::Context;
|
use cretonne::Context;
|
||||||
use cretonne::verifier;
|
use cretonne::verifier;
|
||||||
use cretonne::settings::{self, Configurable};
|
use cretonne::settings::{self, Configurable};
|
||||||
@@ -156,18 +153,8 @@ fn handle_module(
|
|||||||
vprint!(flag_verbose, "Optimizing... ");
|
vprint!(flag_verbose, "Optimizing... ");
|
||||||
terminal.reset().unwrap();
|
terminal.reset().unwrap();
|
||||||
for func in &translation.functions {
|
for func in &translation.functions {
|
||||||
let mut il = func.clone();
|
|
||||||
let mut loop_analysis = LoopAnalysis::new();
|
|
||||||
let mut cfg = ControlFlowGraph::new();
|
|
||||||
cfg.compute(&il);
|
|
||||||
let mut domtree = DominatorTree::new();
|
|
||||||
domtree.compute(&mut il, &cfg);
|
|
||||||
loop_analysis.compute(&mut il, &mut cfg, &mut domtree);
|
|
||||||
let mut context = Context::new();
|
let mut context = Context::new();
|
||||||
context.func = il;
|
context.func = func.clone();
|
||||||
context.cfg = cfg;
|
|
||||||
context.domtree = domtree;
|
|
||||||
context.loop_analysis = loop_analysis;
|
|
||||||
verifier::verify_context(&context.func, &context.cfg, &context.domtree, None)
|
verifier::verify_context(&context.func, &context.cfg, &context.domtree, None)
|
||||||
.map_err(|err| pretty_verifier_error(&context.func, None, err))?;
|
.map_err(|err| pretty_verifier_error(&context.func, None, err))?;
|
||||||
context.licm();
|
context.licm();
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ pub fn do_licm(
|
|||||||
) {
|
) {
|
||||||
cfg.ensure(func);
|
cfg.ensure(func);
|
||||||
domtree.ensure(func, cfg);
|
domtree.ensure(func, cfg);
|
||||||
loop_analysis.compute(func, cfg, domtree);
|
loop_analysis.ensure(func, cfg, domtree);
|
||||||
for lp in loop_analysis.loops() {
|
for lp in loop_analysis.loops() {
|
||||||
// For each loop that we want to optimize we determine the set of loop-invariant
|
// For each loop that we want to optimize we determine the set of loop-invariant
|
||||||
// instructions
|
// instructions
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ entity_impl!(Loop, "loop");
|
|||||||
/// Loops are referenced by the Loop object, and for each loop you can access its header EBB,
|
/// Loops are referenced by the Loop object, and for each loop you can access its header EBB,
|
||||||
/// its eventual parent in the loop tree and all the EBB belonging to the loop.
|
/// its eventual parent in the loop tree and all the EBB belonging to the loop.
|
||||||
pub struct LoopAnalysis {
|
pub struct LoopAnalysis {
|
||||||
|
valid: bool,
|
||||||
loops: PrimaryMap<Loop, LoopData>,
|
loops: PrimaryMap<Loop, LoopData>,
|
||||||
ebb_loop_map: EntityMap<Ebb, PackedOption<Loop>>,
|
ebb_loop_map: EntityMap<Ebb, PackedOption<Loop>>,
|
||||||
}
|
}
|
||||||
@@ -43,6 +44,7 @@ impl LoopAnalysis {
|
|||||||
/// a function.
|
/// a function.
|
||||||
pub fn new() -> LoopAnalysis {
|
pub fn new() -> LoopAnalysis {
|
||||||
LoopAnalysis {
|
LoopAnalysis {
|
||||||
|
valid: false,
|
||||||
loops: PrimaryMap::new(),
|
loops: PrimaryMap::new(),
|
||||||
ebb_loop_map: EntityMap::new(),
|
ebb_loop_map: EntityMap::new(),
|
||||||
}
|
}
|
||||||
@@ -100,7 +102,25 @@ impl LoopAnalysis {
|
|||||||
self.ebb_loop_map.clear();
|
self.ebb_loop_map.clear();
|
||||||
self.ebb_loop_map.resize(func.dfg.num_ebbs());
|
self.ebb_loop_map.resize(func.dfg.num_ebbs());
|
||||||
self.find_loop_headers(cfg, domtree, &func.layout);
|
self.find_loop_headers(cfg, domtree, &func.layout);
|
||||||
self.discover_loop_blocks(cfg, domtree, &func.layout)
|
self.discover_loop_blocks(cfg, domtree, &func.layout);
|
||||||
|
self.valid = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check if the loop analysis is in a valid state.
|
||||||
|
///
|
||||||
|
/// Note that this doesn't perform any kind of validity checks. It simply checks if the
|
||||||
|
/// `compute()` method has been called since the last `clear()`. It does not check that the
|
||||||
|
/// loop analysis is consistent with the CFG.
|
||||||
|
pub fn is_valid(&self) -> bool {
|
||||||
|
self.valid
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Conveneince function to call `compute` if `compute` hasn't been called
|
||||||
|
/// since the last `clear()`.
|
||||||
|
pub fn ensure(&mut self, func: &Function, cfg: &ControlFlowGraph, domtree: &DominatorTree) {
|
||||||
|
if !self.is_valid() {
|
||||||
|
self.compute(func, cfg, domtree)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Traverses the CFG in reverse postorder and create a loop object for every EBB having a
|
// Traverses the CFG in reverse postorder and create a loop object for every EBB having a
|
||||||
|
|||||||
Reference in New Issue
Block a user