package com.seaofnodes.simple; import com.seaofnodes.simple.node.*; import com.seaofnodes.simple.type.*; import java.util.*; public abstract class GlobalCodeMotion { // Arrange that the existing isCFG() Nodes form a valid CFG. The // Node.use(0) is always a block tail (either IfNode or head of the // following block). There are no unreachable infinite loops. public static void buildCFG( StopNode stop ) { fixLoops(stop); schedEarly(); Parser.SCHEDULED = true; schedLate(Parser.START); } // ------------------------------------------------------------------------ // Backwards walk on the CFG only, looking for unreachable code - which has // to be an infinite loop. Insert a bogus never-taken exit to Stop, so the // loop becomes reachable. Also, set loop nesting depth private static void fixLoops(StopNode stop) { // Backwards walk from Stop, looking for unreachable code BitSet visit = new BitSet(); HashSet unreach = new HashSet<>(); unreach.add(Parser.START); for( Node ret : stop._inputs ) ((ReturnNode)ret).walkUnreach(visit,unreach); if( unreach.isEmpty() ) return; // Forwards walk from unreachable, looking for loops with no exit test. visit.clear(); for( CFGNode cfg : unreach ) walkInfinite(cfg,visit,stop); // Set loop depth on remaining graph unreach.clear(); visit.clear(); for( Node ret : stop._inputs ) ((ReturnNode)ret).walkUnreach(visit,unreach); assert unreach.isEmpty(); } // Forwards walk over previously unreachable, looking for loops with no // exit test. static private void walkInfinite( CFGNode n, BitSet visit, StopNode stop ) { if( visit.get(n._nid) ) return; // Been there, done that visit.set(n._nid); if( n instanceof LoopNode loop ) loop.forceExit(stop); for( Node use : n._outputs ) if( use instanceof CFGNode cfg ) walkInfinite(cfg,visit,stop); } // ------------------------------------------------------------------------ private static void schedEarly() { ArrayList rpo = new ArrayList<>(); BitSet visit = new BitSet(); _rpo_cfg(Parser.START, visit, rpo); // Reverse Post-Order on CFG for( int j=rpo.size()-1; j>=0; j-- ) { CFGNode cfg = rpo.get(j); cfg.loopDepth(); for( Node n : cfg._inputs ) _schedEarly(n,visit); // Strictly for dead infinite loops, we can have entire code blocks // not reachable from below - so we reach down, from above, one // step. Since _schedEarly modifies the output arrays, the normal // region._outputs ArrayList iterator throws CME. The extra edges // are always *added* after any Phis, so just walk the Phi prefix. if( cfg instanceof RegionNode region ) { int len = region.nOuts(); for( int i=0; i rpo) { if( !(n instanceof CFGNode cfg) || visit.get(cfg._nid) ) return; // Been there, done that visit.set(cfg._nid); for( Node use : cfg._outputs ) _rpo_cfg(use,visit,rpo); rpo.add(cfg); } private static void _schedEarly(Node n, BitSet visit) { if( n==null || visit.get(n._nid) ) return; // Been there, done that visit.set(n._nid); // Schedule not-pinned not-CFG inputs before self. Since skipping // Pinned, this never walks the backedge of Phis (and thus spins around // a data-only loop, eventually attempting relying on some pre-visited- // not-post-visited data op with no scheduled control. for( Node def : n._inputs ) if( def!=null && !def.isPinned() ) _schedEarly(def,visit); // If not-pinned (e.g. constants, projections, phi) and not-CFG if( !n.isPinned() ) { // Schedule at deepest input CFGNode early = Parser.START; // Maximally early, lowest idepth for( int i=1; i early.idepth() ) early = n.in(i).cfg0(); // Latest/deepest input n.setDef(0,early); // First place this can go } } // ------------------------------------------------------------------------ private static void schedLate(StartNode start) { CFGNode[] late = new CFGNode[Node.UID()]; Node[] ns = new Node[Node.UID()]; _schedLate(start,ns,late); for( int i=0; i best.idepth() || best instanceof IfNode); } // Skip iteration if a backedge private static boolean isForwardsEdge(Node use, Node def) { return use != null && def != null && !(use.nIns()>2 && use.in(2)==def && (use instanceof LoopNode || (use instanceof PhiNode phi && phi.region() instanceof LoopNode))); } private static CFGNode find_anti_dep(CFGNode lca, LoadNode load, CFGNode early, CFGNode[] late) { // We could skip final-field loads here. // Walk LCA->early, flagging Load's block location choices for( CFGNode cfg=lca; early!=null && cfg!=early.idom(); cfg = cfg.idom() ) cfg._anti = load._nid; // Walk load->mem uses, looking for Stores causing an anti-dep for( Node mem : load.mem()._outputs ) { switch( mem ) { case StoreNode st: lca = anti_dep(load,late[st._nid],st.cfg0(),lca,st); break; case PhiNode phi: // Repeat anti-dep for matching Phi inputs. // No anti-dep edges but may raise the LCA. for( int i=1; i