package com.seaofnodes.simple; import com.seaofnodes.simple.node.*; import java.lang.StringBuilder; import java.util.ArrayList; import java.util.BitSet; import java.util.HashMap; import java.util.IdentityHashMap; public class IRPrinter { // Print a node on 1 line, columnar aligned, as: // NNID NNAME DDEF DDEF [[ UUSE UUSE ]] TYPE // 1234 sssss 1234 1234 1234 1234 1234 1234 tttttt public static void _printLine( Node n, StringBuilder sb ) { sb.append("%4d %-7.7s ".formatted(n._nid,n.label())); if( n.isDead() ) { sb.append("DEAD\n"); return; } for( Node def : n._inputs ) sb.append(def==null ? "____ " : "%4d ".formatted(def._nid)); for( int i = n._inputs.size(); i<4; i++ ) sb.append(" "); sb.append(" [[ "); for( Node use : n._outputs ) sb.append(use==null ? "____ " : "%4d ".formatted(use._nid)); int lim = 6 - Math.max(n._inputs.size(),4); for( int i = n._outputs.size(); i 0) sb.append(", "); if (def == null) sb.append("_"); else nodeId(sb, def); } sb.append(")").append("\n"); } public static void printLine( Node n, StringBuilder sb, boolean llvmFormat ) { if (llvmFormat) _printLineLlvmFormat( n, sb ); else _printLine ( n, sb ); } public static String prettyPrint(Node node, int depth) { return Parser.SCHEDULED ? prettyPrintScheduled( node, depth, false ) : prettyPrint( node, depth, false ); } // Another bulk pretty-printer. Makes more effort at basic-block grouping. public static String prettyPrint( Node node, int depth, boolean llvmFormat ) { // First, a Breadth First Search at a fixed depth. BFS bfs = new BFS(node,depth); // Convert just that set to a post-order ArrayList rpos = new ArrayList<>(); BitSet visit = new BitSet(); for( int i=bfs._lim; i< bfs._bfs.size(); i++ ) postOrd( bfs._bfs.get(i), rpos, visit, bfs._bs); // Reverse the post-order walk StringBuilder sb = new StringBuilder(); boolean gap=false; for( int i=rpos.size()-1; i>=0; i-- ) { Node n = rpos.get(i); if( n.isCFG() || n.isMultiHead() ) { if( !gap ) sb.append("\n"); // Blank before multihead printLine( n, sb, llvmFormat ); // Print head while( --i >= 0 ) { Node t = rpos.get(i); if( !t.isMultiTail() ) { i++; break; } printLine( t, sb, llvmFormat ); } sb.append("\n"); // Blank after multitail gap = true; } else { printLine( n, sb, llvmFormat ); gap = false; } } return sb.toString(); } private static void postOrd(Node n, ArrayList rpos, BitSet visit, BitSet bfs) { if( !bfs.get(n._nid) ) return; // Not in the BFS visit if( visit.get(n._nid) ) return; // Already post-order walked visit.set(n._nid); // First walk the CFG, then everything if( n.isCFG() ) { for( Node use : n._outputs ) if( use!=null && use.isCFG() && use.nOuts()>=1 && !(use._outputs.get(0) instanceof LoopNode) ) postOrd(use, rpos,visit,bfs); for( Node use : n._outputs ) if( use!=null && use.isCFG() ) postOrd(use,rpos,visit,bfs); } for( Node use : n._outputs ) if( use != null ) postOrd(use, rpos,visit,bfs); // Post-order rpos.add(n); } // Breadth-first search, broken out in a class to keep in more independent. // Maintains a root-set of Nodes at the limit (or past by 1 if MultiHead). public static class BFS { // A breadth first search, plus MultiHeads for any MultiTails public final ArrayList _bfs; public final BitSet _bs; // Visited members by node id public final int _depth; // Depth limit public final int _lim; // From here to _bfs._len can be roots for a reverse search public BFS( Node base, int d ) { _depth = d; _bfs = new ArrayList<>(); _bs = new BitSet(); add(base); // Prime the pump int idx=0, lim=1; // Limit is where depth counter changes while( idx < _bfs.size() ) { // Ran out of nodes below depth Node n = _bfs.get(idx++); for( Node def : n._inputs ) if( def!=null && !_bs.get(def._nid) ) add(def); if( idx==lim ) { // Depth counter changes at limit if( --d < 0 ) break; // Ran out of depth lim = _bfs.size(); // New depth limit } } // Toss things past the limit except multi-heads while( idx < _bfs.size() ) { Node n = _bfs.get(idx); if( n.isMultiHead() ) idx++; else del(idx); } // Root set is any node with no inputs in the visited set lim = _bfs.size(); for( int i=_bfs.size()-1; i>=0; i-- ) if( !any_visited(_bfs.get(i)) ) swap( i,--lim); _lim = lim; } void swap( int x, int y ) { if( x==y ) return; Node tx = _bfs.get(x); Node ty = _bfs.get(y); _bfs.set(x,ty); _bfs.set(y,tx); } void add(Node n) { _bfs.add(n); _bs.set(n._nid); } void del(int idx) { Node n = Utils.del(_bfs, idx); _bs.clear(n._nid); } boolean any_visited( Node n ) { for( Node def : n._inputs ) if( def!=null && _bs.get(def._nid) ) return true; return false; } } // Diagnostic block ordering from raw control edges. Do not call idepth(): // it fills compiler caches (and can walk lazy dominator accessors). private static int _idepth(CFGNode cfg, HashMap depths) { if( cfg == null || cfg instanceof StartNode ) return 0; Integer old = depths.get(cfg._nid); if( old != null ) return old; depths.put(cfg._nid,0); // Break cycles in partially constructed graphs. int d = 0; if( cfg instanceof LoopNode ) { d = _idepth((CFGNode)cfg.in(1),depths)+1; } else if( cfg instanceof RegionNode || cfg instanceof StopNode ) { for( Node n : cfg._inputs ) if( n instanceof CFGNode pred ) d = Math.max(d,_idepth(pred,depths)+1); } else if( cfg.in(0) instanceof CFGNode pred ) { d = _idepth(pred,depths)+1; } depths.put(cfg._nid,d); return d; } // Bulk pretty printer, knowing scheduling information is available public static String prettyPrintScheduled( Node node, int depth, boolean llvmFormat ) { // Backwards DFS walk to depth. IdentityHashMap ds = new IdentityHashMap<>(); _walk(ds,node,depth); // Print by block with least diagnostic depth HashMap depths = new HashMap<>(); StringBuilder sb = new StringBuilder(); ArrayList bns = new ArrayList<>(); while( !ds.isEmpty() ) { CFGNode blk = null; for( Node n : ds.keySet() ) { CFGNode cfg = n instanceof CFGNode cfg0 && cfg0.blockHead() ? cfg0 : (CFGNode)n.in(0); if( blk==null || _idepth(cfg,depths) < _idepth(blk,depths) ) blk = cfg; } ds.remove(blk); // Print block header sb.append("%-13.13s".formatted(label(blk)+":")); sb.append( " ".repeat(4) ).append(" [[ "); if( blk instanceof RegionNode || blk instanceof StopNode ) for( int i=(blk instanceof StopNode ? 0 : 1); i ds, Node node, int d ) { Integer nd = ds.get(node); if( nd!=null && d <= nd ) return; // Been there, done that ds.put(node,d); if( d == 0 ) return; // Depth cutoff for( Node def : node._inputs ) if( def != null ) _walk(ds,def,d-1); } static String label( CFGNode blk ) { if( blk instanceof StartNode ) return "START"; return (blk instanceof LoopNode ? "LOOP" : "L")+blk._nid; } static void label( StringBuilder sb, CFGNode blk ) { if( !blk.blockHead() ) blk = blk.cfg(0); sb.append( "%-9.9s ".formatted( label( blk ) ) ); } static void printLine( Node n, StringBuilder sb, boolean llvmFormat, ArrayList bns, int i, IdentityHashMap ds ) { printLine( n, sb, llvmFormat ); Utils.del(bns,i); ds.remove(n); } }