@@ -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+
534542impl 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