Skip to content

Commit c5eb637

Browse files
committed
Create initial scaffolding for escape analysis and scalar replacement
1 parent 130c37d commit c5eb637

File tree

1 file changed

+50
-0
lines changed

1 file changed

+50
-0
lines changed

zjit/src/hir.rs

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -531,6 +531,14 @@ pub enum MethodType {
531531
Null,
532532
}
533533

534+
// TODO(Jacob): collapse methodescape and globalescape. Potentially add some type of sideexit escape.
535+
#[derive(Debug, Clone, Copy)]
536+
pub enum EscapeType {
537+
NoEscape,
538+
MethodEscape,
539+
GlobalEscape,
540+
}
541+
534542
impl From<u32> for MethodType {
535543
fn from(value: u32) -> Self {
536544
match value {
@@ -3837,6 +3845,47 @@ impl Function {
38373845
})
38383846
}
38393847

3848+
fn escape_type_of(instruction: Insn) -> EscapeType {
3849+
match instruction {
3850+
// TODO(Jacob): Fill out each of the values here with our paper annotations from the burst
3851+
// TODO(Jacob): Eventually, this should not use any wildcard and should match for every single HIR instruction
3852+
_ => EscapeType::NoEscape,
3853+
}
3854+
}
3855+
3856+
// TODO(Jacob): We probably need a function to do rewriting of escapable allocations here. TBD
3857+
3858+
// TODO(Jacob): Make some escape analysis tests. Ask someone else to make sure the examples I'm using are correct for ruby and matter
3859+
fn scalar_replace(&mut self) {
3860+
// Design sketch
3861+
// 1. Walk the basic block and tag each Insn with an escape value. The escape values are part of a lattice. While this lattice is
3862+
// much simpler than effects or types, it is definitely a lattice. Instructions are lifted to higher escape types, but not lowered.
3863+
// 2. Union find the Insns by escape type to normalize the overall escape type. (ie. NoEscape connected to GlobalEscape turns both
3864+
// into GlobalEscape. We use Union-Find because it's fast, matches the academic paper, and is already implemented in ZJIT HIR. But
3865+
// other set connectivity algorithms are also fine.
3866+
// 3. Replace things that don't escape. (Read: I don't fully understand how this part works yet...)
3867+
//
3868+
// Notes: escape analysis as an algorithm is likely applicable outside of just scalar replacement. We will create separate functions
3869+
// for escape analysis that scalar_replacement makes use of.
3870+
// Notes: While the escape values form a lattice, it's simple enough that we aren't using fancy lattice functions to start with.
3871+
// Perhaps over time this will expand, and we should develop an implementation, or consolidatee effects and types and escapes for a unified
3872+
// lattice / abstract-interpretation sets of data types and functions for use in ZJIT.
3873+
3874+
// TODO: See if there's a way we can collapse the union find to be in one pass here rather than 2
3875+
3876+
for block in self.rpo() {
3877+
let old_insns = std::mem::take(&mut self.blocks[block.0].insns);
3878+
assert!(self.blocks[block.0].insns.is_empty());
3879+
// Tag each instruction with its escape value.
3880+
for insn_id in old_insns {
3881+
// Use the `escape_type_of` function to union together the escape types of each instruction
3882+
// Then we perform replacement somehow
3883+
// Yes this is O(n) and doing nothing when we could just do nothing in constant time but it's scaffolding. *shrug*
3884+
self.push_insn_id(block, insn_id);
3885+
}
3886+
}
3887+
}
3888+
38403889
fn optimize_getivar(&mut self) {
38413890
for block in self.rpo() {
38423891
let old_insns = std::mem::take(&mut self.blocks[block.0].insns);
@@ -5226,6 +5275,7 @@ impl Function {
52265275
// Function is assumed to have types inferred already
52275276
run_pass!(type_specialize);
52285277
run_pass!(inline);
5278+
run_pass!(scalar_replace);
52295279
run_pass!(optimize_getivar);
52305280
run_pass!(optimize_c_calls);
52315281
run_pass!(fold_constants);

0 commit comments

Comments
 (0)