package com.seaofnodes.simple.node; import com.seaofnodes.simple.codegen.CodeGen; import com.seaofnodes.simple.type.*; import com.seaofnodes.simple.util.Ary; import java.util.BitSet; import java.util.IdentityHashMap; /** Control Flow Graph Nodes *
* CFG nodes have a immediate dominator depth (idepth) and a loop nesting * depth(loop_depth). *
* idepth is computed lazily upon first request, and is valid even in the * Parser, and is used by peepholes during parsing and afterward. *
* loop_depth is computed after optimization as part of scheduling. * * Start - Block head; has constants and trailing Funs * Region - Block head; has Phis. Includes Fun and Parms. * CallEnd - Block head; followed by CProj and Proj; also linked RPC Parms * CProj - Block head * * If - Block tail; followed by CProj same block * Call - Block tail; followed by CallEnd and linked Funs * New/intrinsic - If followed by CProj: block tail else mid-block; followed by Proj * */ public abstract class CFGNode extends Node { public CFGNode(Node... nodes) { super(nodes); } public CFGNode(CFGNode cfg) { super(cfg); if( cfg != null ) { _idepth = cfg._idepth; _idepthVersion = cfg._idepthVersion; _ltree = cfg._ltree; _pre = cfg._pre; } } public CFGNode cfg(int idx) { return (CFGNode)in(idx); } // Block head is Start, Region, CProj, but not e.g. If, Return, Stop public boolean blockHead() { return false; } // Get the one control following; error to call with more than one e.g. an // IfNode or other multi-way branch. public CFGNode uctrl() { CFGNode c = null; for( Node n : _outputs ) if( n instanceof CFGNode cfg ) { assert c==null; c = cfg; } return c; } // Used by the encoding / final BB layout public CFGNode uctrlSkipEmpty() { CFGNode x = this, y; while( x.nOuts() == 1 && (y=x.uctrl())!=null && !(y instanceof CallNode) ) // Skip empty blocks x = y; return x; } // ------------------------------------------------------------------------ /** * Immediate dominator tree depth, used to approximate a real IDOM during * parsing where we do not have the whole program, and also peepholes * change the CFG incrementally. *
* See {@link ...}
*/
public char _idepth;
public char _idepthVersion;
public int idepth() { return validIDepth() ? _idepth : cacheIDepth(idom().idepth()+1); }
// Zero depth means uncached. Check before narrowing so overflow cannot wrap.
final int cacheIDepth(int depth) {
assert 0 <= depth && depth <= Character.MAX_VALUE : "Dominator depth exceeds 65535";
_idepthVersion = CodeGen.CODE.iDepthVersion();
return _idepth = (char)depth;
}
final boolean validIDepth() { return _idepth!=0 && _idepthVersion==CodeGen.CODE.iDepthVersion(); }
// Return the immediate dominator of this Node and compute dom tree depth.
public CFGNode idom(Node dep) { return cfg(0); }
public final CFGNode idom() { return idom(null); }
// Return the LCA of two idoms
public CFGNode domLCA(CFGNode rhs, Node dep) {
if( rhs==null ) return this;
CFGNode lhs = this;
while( lhs != rhs ) {
if( lhs==null || rhs==null ) return null;
int comp = lhs.idepth() - rhs.idepth();
if( comp >= 0 ) lhs = (dep==null ? lhs : dep.addDep(lhs)).idom();
if( comp <= 0 ) rhs = (dep==null ? rhs : dep.addDep(rhs)).idom();
}
return lhs;
}
// Safe read of the control input of a data node.
public static CFGNode safeCFG(Node n) {
if( n==null ) return null;
if( !(n.in(0) instanceof CFGNode cfg) ) return null;
return cfg._safeCFG();
}
private CFGNode _safeCFG() {
return switch(this) {
case CallNode call when call.cend()._folding -> call.cfg0()._safeCFG();
case CallEndNode cend when cend._folding -> cend.cfg(1)._safeCFG();
case FunNode fun when fun._folding -> fun.cfg(1)._safeCFG();
case ReturnNode ret when ret.fun()._folding -> ret.cfg0()._safeCFG();
default -> this;
};
}
// True when this node dominates `sub` in the current idom tree.
public boolean dominates(CFGNode sub, Node dep) {
for( CFGNode cfg=sub; cfg!=null; cfg=cfg.idom() )
if( cfg == this )
return true;
else if( dep != null ) dep.addDep(cfg);
return false;
}
public static CFGNode deepest(CFGNode a, CFGNode b) {
if( a == null ) return b;
if( b == null ) return a;
return b.idepth() > a.idepth() ? b : a;
}
public static CFGNode earlyCFG(Node n) {
return earlyCFG(n,new IdentityHashMap<>(),new BitSet());
}
public static CFGNode earlyCFG(Node n, IdentityHashMap