package com.seaofnodes.simple.codegen; import com.seaofnodes.simple.node.*; import com.seaofnodes.simple.util.Ary; import com.seaofnodes.simple.util.SB; import com.seaofnodes.simple.util.Utils; import java.util.IdentityHashMap; /** A Live Range *

* A live range is a set of nodes and edges which must get the same register. * Live ranges form an interconnected web with almost no limits on their * shape. Live ranges also gather the set of register constraints from all * their parts. *

* Most of the fields in this class are for spill heuristics, but there are a * few key ones that define what a LRG is. LRGs have a unique dense integer * `_lrg` number which names the LRG. New `_lrg` numbers come from the * `RegAlloc._lrg_num` counter. LRGs can be unioned together - * (this is the Union-Find algorithm) * - and when this happens the lower numbered `_lrg` wins. Unioning only * happens during `BuildLRG` and happens because either a `Phi` is forcing all * its inputs and outputs into the same register, or because of a 2-address * instruction. LRGs have matching `union` and `find` calls, and a set * `_leader` field. */ public class LRG { // Dense live range numbers final short _lrg; // U-F leader; null if leader LRG _leader; // Choosen register short _reg; // Count of single-register defs and uses short _1regDefCnt, _1regUseCnt; // Count of all defs, uses. Mostly interested 1 vs many boolean _multiDef, _multiUse; // A sample MachNode def in the live range public MachNode _machDef, _machUse; short _uidx; // _machUse input // Some splits used in biased coloring MachConcreteNode _splitDef, _splitUse; // All the self-conflicting defs for this live range IdentityHashMap _selfConflicts; // AND of all masks involved; null if none have been applied yet RegMask _mask; // Adjacent Live Range neighbors. Only valid during coloring Ary _adj; // Function fully containing the live range; used to find the stack frame // layout, for spill op offsets. final public FunNode _fun; public int nadj() { return _adj==null ? 0 : _adj._len; } void addNeighbor(LRG lrg) { if( _adj==null ) _adj = new Ary<>(LRG.class); _adj.push(lrg); } // Remove and compress neighbor list; store ex-neighbor past // end for unwinding Simplify. True if exactly going low-degree boolean removeCompress( LRG lrg ) { _adj.swap(_adj.find(lrg),_adj._len-1); return _adj._len-- == _mask.size(); } void reinsert( LRG lrg ) { assert _adj._es[_adj._len]==lrg; _adj._len++; } // More registers than neighbors boolean lowDegree() { return nadj() < _mask.size(); } LRG( short lrg, FunNode fun ) { _lrg = lrg; _reg = -1; _fun = fun; } boolean leader() { return _leader == null; } // Fast-path Find from the Union-Find algorithm LRG find() { if( _leader==null ) return this; // I am the leader if( _leader._leader==null ) // I point to the leader return _leader; return _rollup(); } // Slow-path rollup of U-F private LRG _rollup() { LRG ldr = _leader._leader; // Roll-up while( ldr._leader!=null ) ldr = ldr._leader; LRG l2 = this; while( l2 != ldr ) { LRG l3 = l2._leader; l2._leader = ldr; l2 = l3; } return ldr; } // Union `this` and `lrg`, keeping the lower numbered _lrg. // Includes a number of fast-path cutouts. LRG union( LRG lrg ) { assert leader(); if( lrg==null ) return this; lrg = lrg.find(); if( lrg==this ) return this; return _lrg < lrg._lrg ? _union(lrg) : lrg._union(this); } // Union `this` and `lrg`, folding together all stats. private LRG _union( LRG lrg ) { assert _fun == lrg._fun; // Set U-F leader lrg._leader = this; // Fold together stats if( _machDef==null ) { _machDef = lrg._machDef; } else if( lrg._machDef!=null ) { if( _machDef != lrg._machDef ) _multiDef=true; if( _1regDefCnt==0 ) _machDef = lrg._machDef; else if( _machDef==lrg._machDef ) _1regDefCnt--; } _1regDefCnt += lrg._1regDefCnt; _multiDef |= lrg._multiDef; if( _machUse==null ) { _machUse = lrg._machUse; _uidx = lrg._uidx; } else if( lrg._machUse!=null ) { if( _machUse != lrg._machUse ) _multiUse=true; if( _1regUseCnt==0 ) { _machUse = lrg._machUse; _uidx = lrg._uidx; } else if( _machUse==lrg._machUse ) _1regUseCnt--; } _1regUseCnt += lrg._1regUseCnt; _multiUse |= lrg._multiUse; // Fold deepest Split _splitDef = deepSplit(_splitDef,lrg._splitDef); _splitUse = deepSplit(_splitUse,lrg._splitUse); // Fold together masks RegMask mask = _mask.and(lrg._mask); if( mask==null ) mask = _mask.copy().and(lrg._mask); _mask = mask; return this; } private static MachConcreteNode deepSplit( MachConcreteNode s0, MachConcreteNode s1 ) { return s0==null || (s1!=null && s0.cfg0().loopDepth() < s1.cfg0().loopDepth()) ? s1 : s0; } // Record any Mach def for spilling heuristics LRG machDef( MachNode def, boolean size1 ) { if( _machDef!=null && _machDef!=def ) _multiDef = true; if( _machDef==null || size1 ) _machDef = def; if( size1 ) _1regDefCnt++; if( def instanceof SplitNode split ) _splitDef = deepSplit(_splitDef,split); return this; } // Record any Mach use for spilling heuristics LRG machUse( MachNode use, short uidx, boolean size1 ) { if( _machUse!=null && _machUse!=use ) _multiUse = true; if( _machUse==null || size1 ) { _machUse = use; _uidx = uidx; } if( size1 ) _1regUseCnt++; if( use instanceof SplitNode split ) _splitUse = deepSplit(_splitUse,split); return this; } boolean hasSplit() { return _splitDef != null || _splitUse != null; } short size() { return _mask.size(); } boolean size1() { return _mask.size1(); } void selfConflict( Node def ) { if( _selfConflicts == null ) _selfConflicts = new IdentityHashMap<>(); _selfConflicts.put(def,""); } // Record intersection of all register masks. // True if still has registers boolean and( RegMask mask ) { RegMask mask2 = mask.and(_mask); if( mask2==null ) mask2 = _mask.copy().and(mask); _mask = mask2; return !_mask.isEmpty(); } // Remove this singular register // True if still has registers boolean clr( int reg ) { if( _mask.clr(reg) ) return true; _mask = _mask.copy(); // Need a mutable copy return _mask.clr(reg); } // Subtract mask (AND complement) // True if still has registers boolean sub( RegMask mask ) { RegMask mask2 = _mask.sub(mask); if( mask2==null ) mask2 = _mask.copy().sub(mask); _mask = mask2; return !_mask.isEmpty(); } @Override public String toString() { return toString(new SB()).toString(); } public SB toString( SB sb ) { sb.p("V").p(_lrg); if( _mask!=null ) _mask.toString(sb); return sb; } }