Skip to content

Commit d7c296b

Browse files
committed
Fix typos, restructure algorithm sketch
1 parent 16f4059 commit d7c296b

2 files changed

Lines changed: 92 additions & 76 deletions

File tree

zjit/src/hir.rs

Lines changed: 91 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -5287,81 +5287,97 @@ impl Function {
52875287
}
52885288
}
52895289

5290-
/// Convert maximal SSA (constructed from YARV with `add_iseq_to_hir`) to minimal SSA
5291-
/// We implement the SSA construction described by Braun et al.
5292-
/// SSA Paper reference: <https://c9x.me/compile/bib/braun13cc.pdf>
5293-
fn minify_ssa(&mut self) {
5294-
// This PR is _very_ much an early draft. I've spent some time reading the paper, and isolating some of the concepts.
5295-
// There will be lots of lengthy comments that get removed and reworked. There are concepts in ZJIT that are identical but named different things.
5296-
// We need to map these to each other. It probably makes sense to keep some reference comments performing this mapping for the future too.
5297-
//
5298-
// Important difference: Braun et al construct SSA from scratch. We already have a maximal SSA representation. We may be able to get away with only the pruning parts of the paper
5299-
// Alternatively, we may just need to overhaul our entire SSA construction. I'm not sure what makes sense yet.
5300-
//
5301-
// TODO: Make tests for single blocks and multiple block constructions of minimal SSA
5302-
// It would be great to faithfully recreate examples from the paper but with ZJIT specifics. If there's a way to construct ruby code that maps to this HIR, that would be awesome but jury is still out
5303-
//
5304-
// First Test Idea for LVN (page 3):
5305-
// This first version isn't entirely implementable because `d` and `v?` are intentionally unspecified but it's a good starting point
5306-
// source (make into ruby) SSA (make into ZJIT)
5307-
// a <- 42 v1: 42
5308-
// b <- a
5309-
// c <- a + b v2: v1 + v1
5310-
// v3: 23
5311-
// a <- c + 23 v4: v2 + v3
5312-
// c <- a + d v5: v4 + v?
5313-
5314-
// ------------ SINGLE BLOCK ----------------
5315-
// This section contains all the tech we need for single blocks. For implementation, let's pass tests for this part first before adding in the rest
5316-
// These two functions handle local value numbering. I don't know if this is something that Max landed in a PR, nor what can / should be repurposed vs written from scratch
5317-
fn read_variable() {
5318-
// TODO: Specify and define arguments
5319-
// TODO: Figure out if this already exists in ZJIT. Likely this uses find or chase_insn?
5320-
}
5321-
5322-
fn write_variable() {
5323-
// TODO: Specify and define arguments
5324-
// TODO: Figure out if this already exists in ZJIT. Likely this uses find or chase_insn?
5325-
}
5326-
//
5327-
// ------------ MULTIPLE BLOCKS ----------------
5328-
// When we get to multiple blocks, we need to deal with phi nodes. It seems that in ZJIT, these are referred to as block arguments or block params
5329-
// It would be really nice to check my knowledge on this, and potentially rename them if so
5330-
fn read_variable_recursive() {
5331-
// TODO: fill out
5332-
// This function is the global value numbering version of the single block variant
5333-
}
5334-
5335-
fn add_phi_operands() {
5336-
// See comments in try_remove_triival_phi
5337-
}
5290+
fn try_remove_trivial_phi(BlockId: block_id) {
53385291

5339-
fn try_remove_trivial_phi() {
5340-
// TODO: fill out
5341-
// This function cleans up the opportunistic phis that get added in Braun's algorithm.
5342-
// If we didn't add such phis, we run into all sorts of recursion issues. This is a key idea in the algorithm we are implementing
5343-
}
5344-
5345-
// ---------- INCOMPLETE CFGS --------------
5346-
// While technically part of the multiple blocks section, the next functions are even more complicated so they get their own section
5347-
fn seal_block() {
5348-
// We call a block "sealed" when we know there will not be predecessors added to the block.
5349-
// This function adds all necessary phis to incomplete blocks and adds this block to the sealed list
5350-
}
5351-
5352-
// I don't yet know which optimizations we want to include during construction. Pages 8 and 9 of the paper show why this is non-trivial and important for us to figure out
5353-
// However, the following tools are definitely necessary
5354-
fn remove_redundant_phis() {
5355-
// In the paper, they innovate by considering irreducible control flow from the perspective of the strongly connected component that _must_ exist if there are redundant phis
5356-
// By computing this strongly connected component, we can figure out what to remove (with the following function)
5357-
}
5358-
5359-
fn process_strongly_connected_component() {
5360-
// Do some fancy logic to iterate across all the phis in the SCC, tracking "inner" and "outer" operations
5361-
}
5292+
}
53625293

5363-
// TODO: Close reading of page 11 after implementing everything else to see if there are further optimizations to be made
5364-
// Key issue: we construct a lot of phis and then remove a lot of phis. Can we construct fewer in the first place?
5294+
fn remove_trivial_phis(&mut self) {
5295+
let mut fixpoint = False;
5296+
while not fixpoint {
5297+
for block in self.reverse_post_order() {
5298+
// TODO: Figure out where the predecessors come from
5299+
predecessors = vec![];
5300+
try_remove_trivial_phi(block);
5301+
}
5302+
}
5303+
}
5304+
5305+
// TODO: Probably throw this next big comment away
5306+
// /// Convert maximal SSA (constructed from YARV with `add_iseq_to_hir`) to minimal SSA
5307+
// /// We implement the SSA construction described by Braun et al.
5308+
// /// SSA Paper reference: <https://c9x.me/compile/bib/braun13cc.pdf>
5309+
// fn minify_ssa(&mut self) {
5310+
// // This PR is _very_ much an early draft. I've spent some time reading the paper, and isolating some of the concepts.
5311+
// // There will be lots of lengthy comments that get removed and reworked. There are concepts in ZJIT that are identical but named different things.
5312+
// // We need to map these to each other. It probably makes sense to keep some reference comments performing this mapping for the future too.
5313+
// //
5314+
// // Important difference: Braun et al construct SSA from scratch. We already have a maximal SSA representation. We may be able to get away with only the pruning parts of the paper
5315+
// // Alternatively, we may just need to overhaul our entire SSA construction. I'm not sure what makes sense yet.
5316+
// //
5317+
// // TODO: Make tests for single blocks and multiple block constructions of minimal SSA
5318+
// // It would be great to faithfully recreate examples from the paper but with ZJIT specifics. If there's a way to construct ruby code that maps to this HIR, that would be awesome but jury is still out
5319+
// //
5320+
// // First Test Idea for LVN (page 3):
5321+
// // This first version isn't entirely implementable because `d` and `v?` are intentionally unspecified but it's a good starting point
5322+
// // source (make into ruby) SSA (make into ZJIT)
5323+
// // a <- 42 v1: 42
5324+
// // b <- a
5325+
// // c <- a + b v2: v1 + v1
5326+
// // v3: 23
5327+
// // a <- c + 23 v4: v2 + v3
5328+
// // c <- a + d v5: v4 + v?
5329+
5330+
// // ------------ SINGLE BLOCK ----------------
5331+
// // This section contains all the tech we need for single blocks. For implementation, let's pass tests for this part first before adding in the rest
5332+
// // These two functions handle local value numbering. I don't know if this is something that Max landed in a PR, nor what can / should be repurposed vs written from scratch
5333+
// fn read_variable() {
5334+
// // TODO: Specify and define arguments
5335+
// // TODO: Figure out if this already exists in ZJIT. Likely this uses find or chase_insn?
5336+
// }
5337+
5338+
// fn write_variable() {
5339+
// // TODO: Specify and define arguments
5340+
// // TODO: Figure out if this already exists in ZJIT. Likely this uses find or chase_insn?
5341+
// }
5342+
// //
5343+
// // ------------ MULTIPLE BLOCKS ----------------
5344+
// // When we get to multiple blocks, we need to deal with phi nodes. It seems that in ZJIT, these are referred to as block arguments or block params
5345+
// // It would be really nice to check my knowledge on this, and potentially rename them if so
5346+
// fn read_variable_recursive() {
5347+
// // TODO: fill out
5348+
// // This function is the global value numbering version of the single block variant
5349+
// }
5350+
5351+
// fn add_phi_operands() {
5352+
// // See comments in try_remove_triival_phi
5353+
// }
5354+
5355+
// fn try_remove_trivial_phi() {
5356+
// // TODO: fill out
5357+
// // This function cleans up the opportunistic phis that get added in Braun's algorithm.
5358+
// // If we didn't add such phis, we run into all sorts of recursion issues. This is a key idea in the algorithm we are implementing
5359+
// }
5360+
5361+
// // ---------- INCOMPLETE CFGS --------------
5362+
// // While technically part of the multiple blocks section, the next functions are even more complicated so they get their own section
5363+
// fn seal_block() {
5364+
// // We call a block "sealed" when we know there will not be predecessors added to the block.
5365+
// // This function adds all necessary phis to incomplete blocks and adds this block to the sealed list
5366+
// }
5367+
5368+
// // I don't yet know which optimizations we want to include during construction. Pages 8 and 9 of the paper show why this is non-trivial and important for us to figure out
5369+
// // However, the following tools are definitely necessary
5370+
// fn remove_redundant_phis() {
5371+
// // In the paper, they innovate by considering irreducible control flow from the perspective of the strongly connected component that _must_ exist if there are redundant phis
5372+
// // By computing this strongly connected component, we can figure out what to remove (with the following function)
5373+
// }
5374+
5375+
// fn process_strongly_connected_component() {
5376+
// // Do some fancy logic to iterate across all the phis in the SCC, tracking "inner" and "outer" operations
5377+
// }
5378+
5379+
// // TODO: Close reading of page 11 after implementing everything else to see if there are further optimizations to be made
5380+
// // Key issue: we construct a lot of phis and then remove a lot of phis. Can we construct fewer in the first place?
53655381

53665382
}
53675383

@@ -6170,7 +6186,7 @@ impl Function {
61706186
(convert_no_profile_sends) => { Counter::compile_hir_strength_reduce_time_ns };
61716187
// End strength reduction bucket
61726188
(inline_methods) => { Counter::compile_hir_inline_methods_time_ns };
6173-
(minify_ssa) => { Counter::compile_hir_minify_ssa_time_ns };
6189+
(remove_trivial_phis) => { Counter::compile_hir_remove_trivial_phis_time_ns };
61746190
(optimize_load_store) => { Counter::compile_hir_optimize_load_store_time_ns };
61756191
(canonicalize) => { Counter::compile_hir_canonicalize_time_ns };
61766192
(fold_constants) => { Counter::compile_hir_fold_constants_time_ns };
@@ -6224,7 +6240,7 @@ impl Function {
62246240
// TODO: Figure out where the pass should go and remove these comments
62256241
// It's not clear where converting to minimal SSA should occur
62266242
// We need it for a global optimize_load_store, so this is a good starting point
6227-
run_pass!(minify_ssa);
6243+
run_pass!(remove_trivial_phis);
62286244
run_pass!(optimize_load_store);
62296245
run_pass!(canonicalize);
62306246
run_pass!(fold_constants);

zjit/src/stats.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,7 @@ make_counters! {
171171
compile_hir_build_time_ns,
172172
compile_hir_strength_reduce_time_ns,
173173
compile_hir_inline_methods_time_ns,
174-
compile_hir_minify_ssa_time_ns,
174+
compile_hir_remove_trivial_phis_time_ns,
175175
compile_hir_optimize_load_store_time_ns,
176176
compile_hir_canonicalize_time_ns,
177177
compile_hir_fold_constants_time_ns,

0 commit comments

Comments
 (0)