package com.seaofnodes.simple.node; import com.seaofnodes.print.ExprPrinter; import com.seaofnodes.simple.Parser; import com.seaofnodes.simple.Var; import com.seaofnodes.simple.codegen.CodeGen; import com.seaofnodes.simple.codegen.ParseAll; import com.seaofnodes.simple.codegen.CompUnit; import com.seaofnodes.simple.type.*; import com.seaofnodes.simple.util.Ary; import com.seaofnodes.simple.util.Utils; import java.util.*; /** * The Scope node is purely a parser helper - it tracks names to nodes with a * stack of variables. */ public class ScopeNode extends MemMergeNode { /** * The control is a name that binds to the currently active control * node in the graph */ public static final String CTRL = "$ctrl"; public static final String ARG0 = "arg"; public static final String MEM0 = "$mem"; // All active/live variables in all nested scopes, all run together. // Maps 1-to-1 to the _inputs array. public final Ary _vars; // Lexical scope is typed: public static class Kind { // Basic block scoping public static class Block extends Kind { } // Constructor/initializer block for an allocated struct. public static class Constructor extends Block { public Constructor(TypeStruct self) { _self = self; } public final TypeStruct _self; } // Function scope public static class Func extends Kind { public Func(String name) { _name=name;} public final String _name; } public int _lexSize; // Number of Vars in this scope } public final Ary _kinds; // Lexical scope nesting depth public int depth() { return _kinds._len; } public Kind klast() { return _kinds.last(); } // Extra guards; tested predicates and cast results private final Ary _guards; // A new ScopeNode public ScopeNode() { super(); _vars = new Ary<>(Var .class); _kinds = new Ary<>(Kind.class); _guards = new Ary<>(Node.class); } @Override public String label() { return "Scope"; } @Override protected ExprPrinter _print1(ExprPrinter p) { p.p("Scope[ "); int j=1; for( int i=0; i N ctrl(N n) { return setDef(0,n); } public Node mem(Node n) { return setDef(1,n); } public void push( Kind kind ) { kind._lexSize = _vars.size(); _kinds.push(kind); } // Pop a lexical scope public void pop() { promote(null); // Promote forward references to the next outer scope _pop(); } // Pop a scope, no promote FRefs public void _pop() { int n = _kinds.pop()._lexSize; popUntil(n); // Pop off inputs going out of scope _vars.setLen(n); // Pop off variables going out of scope } // Look for forward references in the last lexical scope and promote to the // next outer lexical scope. At the last scope declare them an error. public void promote(CodeGen code) { Kind kind = klast(); int n = kind._lexSize; for( int i=n; i=0; i-- ) if( _kinds.at(i) instanceof Kind.Constructor ctor ) return ctor._self; else if( _kinds.at(i) instanceof Kind.Func func && FunNode.isInstance(func._name) ) return ((TypeMemPtr)var(_kinds.at(i)._lexSize).type())._obj; return null; } // Find nearest enclosing function scope public int enclosingFunction() { for( int i=depth()-1; i>=0; i-- ) if( _kinds.at(i) instanceof Kind.Func func && !FunNode.isInstance(func._name) ) return i; throw Utils.TODO("Should not reach here"); } // Find nearest enclosing function or declaration scope public int enclosingFuncOrDecl() { for( int i=depth()-1; i>=0; i-- ) if( _kinds.at(i) instanceof Kind.Func func ) return i; throw Utils.TODO("Should not reach here"); } // Which lexical scope contains this Var? public int kindx( Var v ) { for( int i=depth()-1; i>=0; i-- ) if( v._idx >= _kinds.at(i)._lexSize ) return i; return -1; } // Which lexical scope contains this Var? public Kind kind( Var v ) { int kx = kindx(v); return kx == -1 ? null : _kinds.at(kx); } public int kindFcnx( Var v ) { for( int i=depth()-1; i>=0; i-- ) if( v._idx >= _kinds.at(i)._lexSize && _kinds.at(i) instanceof Kind.Func ) return i; throw Utils.TODO("Should not reach here"); } // Which function scope contains this Var? public Kind kindFcn( Var v ) { int kx = kindFcnx(v); return kx == -1 ? null : _kinds.at(kx); } // Find name in reverse, return an index into _vars or -1. Linear scan // instead of hashtable, but probably doesn't matter until the scan // typically hits many dozens of variables. public int find( String name ) { for( int i=_vars.size()-1; i>=0; i-- ) if( var(i)._name.equals(name) ) return i; return -1; } /** * Create a new variable name in the current scope */ public boolean define( String name, Type declaredType, boolean xfinal, Node init, Parser.Lexer loc ) { assert _kinds.isEmpty() || name!=MEM0 ; // Later scopes do not define memory if( depth() > 0 ) for( int i=_vars.size()-1; i>=klast()._lexSize; i-- ) { Var n = var(i); if( n._name.equals(name) ) { if( !n.isFRef() ) return false; // Double define FRefNode fref = (FRefNode)in(n._idx); // Get forward ref if( !xfinal || !declaredType.isConstant() ) throw fref.err(); // Must be a final constant n.defFRef(declaredType,xfinal,loc); // Declare full correct type, final, source location setDef(n._idx,fref.addDef(init)); // Set FRef to defined; tell parser also return true; } } Var v = new Var(nIns(),name,declaredType,xfinal,loc,init==CodeGen.CODE.XCTRL); _vars.add(v); // Creating a forward reference if( init==CodeGen.CODE.XCTRL ) //init = new FRefNode(v).init(); throw Utils.TODO(); addDef(init); return true; } // Normal lookup was tried and failed. Append a forward ref in the current // lexical scope. Scope pops promote it outwards "as if" the final constant // had been declared before the using scope. public Var defineFRef( String id, Type t, boolean xfinal, Parser.Lexer loc ) { Var var = new Var(nIns(),id,t,xfinal,loc,true); FRefNode fref = new FRefNode(id,loc).init(); fref._type = fref._con = t; _vars.add(var); addDef(fref); return var; } // Parser state carries one complete, conservative memory value. Precise // alias partitions belong to the graph and are introduced by peepholes. public Node mem( int alias ) { assert alias == 1; return in(update(var(1),null)); } public void mem( int alias, Node st ) { assert alias == 1; update(var(1),st); } /** * Lookup a name in all scopes starting from most deeply nested. * * @param name Name to be looked up * @return null if not found, or the implementing Node */ public Var lookup( String name ) { int idx = find(name); // -1 is missed in all scopes, not found return idx == -1 ? null : update(var(idx),null); } /** * Redefine an existing name * * @param name Name being redefined * @param n The node to bind to the name */ public void update( String name, Node n ) { int idx = find(name); assert idx>=0; update(var(idx),n); } public Var update( Var v, Node st ) { Node old = in(v._idx); if( old instanceof ScopeNode loop ) { // Lazy Phi! Node def = loop.in(v._idx); old = def instanceof PhiNode phi && loop.ctrl()==phi.region() // Loop already has a real Phi, use it ? def // Set real Phi in the loop head // The phi takes its one input (no backedge yet) from a recursive // lookup, which might have insert a Phi in every loop nest. : loop.setDef(v._idx,PhiNode.make(v._name, v.type(), loop.ctrl(), loop.in(loop.update(v,null)._idx),null).init()); setDef(v._idx,old); } //assert !v._final || st==null; if( st!=null ) setDef(v._idx,st); // Set new value return v; } /** * Duplicate a ScopeNode; including all levels, up to Nodes. So this is * neither shallow (would dup the Scope but not the internal HashMap * tables), nor deep (would dup the Scope, the HashMap tables, but then * also the program Nodes). *

* If the {@code loop} flag is set, the edges are filled in as the original * Scope, as an indication of Lazy Phis at loop heads. The goal here is to * not make Phis at loop heads for variables which are never touched in the * loop body. *

* The new Scope is a full-fledged Node with proper use<->def edges. */ public ScopeNode dup() { return dup(false); } public ScopeNode dup(boolean loop) { ScopeNode dup = new ScopeNode(); // Our goals are: // 1) duplicate the name bindings of the ScopeNode across all stack levels // 2) Make the new ScopeNode a user of all the nodes bound // 3) Ensure that the order of defs is the same to allow easy merging dup._vars .addAll(_vars ); dup._kinds .addAll(_kinds ); dup._guards .addAll(_guards ); // The dup'd guards all need dup'd keepers, to keep proper accounting // when later removing all guards for( Node n : _guards ) if( !(n instanceof CFGNode) ) n.keep(); dup.addDef(ctrl()); // Control input is just copied // Memory is one ordinary parser-state value. At loop heads the // Scope sentinel triggers the same lazy-Phi machinery as variables. dup.addDef(loop ? this : mem()); // Copy of other inputs for( int i=2; i 0 ) { Node cast = _guards.at(--i); if( cast instanceof CFGNode ) continue; // Marker between guard sets Node xpred = _guards.at(--i); if( xpred == pred ) return cast; } return pred; } // Kill guards also @Override public void kill() { for( Node n : _guards ) if( !(n instanceof CFGNode) ) n.unkill(); _guards.clear(); // Can have lazy uses remaining if( isUnused() ) super.kill(); } private void replace( Node old, Node cast, boolean nonZero ) { assert old!=null && old!=cast; for( int i=0; i