@@ -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 {
@@ -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