Skip to content

Commit b3e102a

Browse files
committed
Create initial scaffolding for escape analysis and scalar replacement
1 parent 55ecc9c commit b3e102a

1 file changed

Lines changed: 50 additions & 0 deletions

File tree

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 {
@@ -3737,6 +3745,47 @@ impl Function {
37373745
})
37383746
}
37393747

3748+
fn escape_type_of(instruction: Insn) -> EscapeType {
3749+
match instruction {
3750+
// TODO(Jacob): Fill out each of the values here with our paper annotations from the burst
3751+
// TODO(Jacob): Eventually, this should not use any wildcard and should match for every single HIR instruction
3752+
_ => EscapeType::NoEscape,
3753+
}
3754+
}
3755+
3756+
// TODO(Jacob): We probably need a function to do rewriting of escapable allocations here. TBD
3757+
3758+
// TODO(Jacob): Make some escape analysis tests. Ask someone else to make sure the examples I'm using are correct for ruby and matter
3759+
fn scalar_replace(&mut self) {
3760+
// Design sketch
3761+
// 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
3762+
// much simpler than effects or types, it is definitely a lattice. Instructions are lifted to higher escape types, but not lowered.
3763+
// 2. Union find the Insns by escape type to normalize the overall escape type. (ie. NoEscape connected to GlobalEscape turns both
3764+
// into GlobalEscape. We use Union-Find because it's fast, matches the academic paper, and is already implemented in ZJIT HIR. But
3765+
// other set connectivity algorithms are also fine.
3766+
// 3. Replace things that don't escape. (Read: I don't fully understand how this part works yet...)
3767+
//
3768+
// Notes: escape analysis as an algorithm is likely applicable outside of just scalar replacement. We will create separate functions
3769+
// for escape analysis that scalar_replacement makes use of.
3770+
// Notes: While the escape values form a lattice, it's simple enough that we aren't using fancy lattice functions to start with.
3771+
// Perhaps over time this will expand, and we should develop an implementation, or consolidatee effects and types and escapes for a unified
3772+
// lattice / abstract-interpretation sets of data types and functions for use in ZJIT.
3773+
3774+
// TODO: See if there's a way we can collapse the union find to be in one pass here rather than 2
3775+
3776+
for block in self.rpo() {
3777+
let old_insns = std::mem::take(&mut self.blocks[block.0].insns);
3778+
assert!(self.blocks[block.0].insns.is_empty());
3779+
// Tag each instruction with its escape value.
3780+
for insn_id in old_insns {
3781+
// Use the `escape_type_of` function to union together the escape types of each instruction
3782+
// Then we perform replacement somehow
3783+
// Yes this is O(n) and doing nothing when we could just do nothing in constant time but it's scaffolding. *shrug*
3784+
self.push_insn_id(block, insn_id);
3785+
}
3786+
}
3787+
}
3788+
37403789
fn optimize_getivar(&mut self) {
37413790
for block in self.rpo() {
37423791
let old_insns = std::mem::take(&mut self.blocks[block.0].insns);
@@ -5134,6 +5183,7 @@ impl Function {
51345183
// Function is assumed to have types inferred already
51355184
run_pass!(type_specialize);
51365185
run_pass!(inline);
5186+
run_pass!(scalar_replace);
51375187
run_pass!(optimize_getivar);
51385188
run_pass!(optimize_c_calls);
51395189
run_pass!(fold_constants);

0 commit comments

Comments
 (0)