package com.seaofnodes.simple.parser; import com.seaofnodes.simple.node.*; import com.seaofnodes.simple.node.ScopeNode.Kind; import com.seaofnodes.simple.type.*; import com.seaofnodes.simple.util.Ary; import java.util.BitSet; import static com.seaofnodes.simple.util.Utils.TODO; /** Function expressions, parameters, calls, and function entry/exit graphs. */ final class Functions { private final Parser P; // Merge all function exits here. ScopeNode _returnScope; Functions(Parser P) { this.P = P; } /** * Parse a function body; the caller will parse the surrounding "{}" * *
     *     { [type arg,]* -> expr }
     *     { -> statements } // The no-argument function
     * 
*/ Node parseFunction() { Ary ts = new Ary<>(Type.class); Ary ids = new Ary<>(String.class); BitSet finals = new BitSet(); Type self = P.types.implicitSelfType(); ts.push(self); ids.push(self == TypePtr.PTR ? "#self" : "self"); // Parse other arguments P._lexer.skipWhiteSpace(); Lexer loc = P.loc(); // First argument location parameters(ts,ids,finals); // Make a concrete function type, with a fidx TypeFunPtr tfp = TypeFunPtr.make1((byte)2,true,ts.asAry(),Type.BOTTOM,P._code.fidx(P._ref._cname)); ReturnNode ret = parseFunctionBody(tfp,loc,finals,ids.asAry()); return new FunPtrNode(tfp,P._code._start,ret).peephole(); } Node parseConstructor(String name) { P.require("{"); Ary ts = new Ary<>(Type.class); Ary ids = new Ary<>(String.class); BitSet finals = new BitSet(); TypeStruct self = P._scope.constructorSelf(); assert self != null; ts.push(TypeMemPtr.make(Structs.constructorRecv(self))); ids.push("self"); // Explicit constructors use the same hidden allocation arguments as // : public memory is the call memory edge (index 1), followed by // self (index 2) and the private, unescaped object memory (index 3). ts.push(TypeMem.makePrivate(self)); ids.push("#selfMem"); P._lexer.skipWhiteSpace(); Lexer loc = P.loc(); parameters(ts,ids,finals); TypeFunPtr tfp = TypeFunPtr.make1((byte)2,true,ts.asAry(),TypeMem.makePrivate(self),P._code.fidx(P._ref._cname)); Node[] preParse = P._scope.save(); ReturnNode ret = parseBody((self._name+"."+name).intern(),tfp,loc,finals,ids.asAry()); P._scope.restore(preParse); P.require("}"); return new FunPtrNode(tfp,P._code._start,ret).peephole(); } // parameterList followed by the function arrow; callers seed hidden parameters. private void parameters(Ary ts, Ary ids, BitSet finals) { while( true ) { Type t = P.types.parseType(); // Arg type if( t==null ) break; finals.set(ids.size(),P.declarations.bindingFinal(t)); String id = P.requireId(); ts .push(t ); // Push type/arg pairs ids.push(id); P.match(","); } P.require("->"); } /** * Parse function call arguments; caller will parse the surrounding "()" *
     *   ( arg* )
     * 
*/ Node functionCall(Node fcn, Node self) { Lexer site = P.loc(); site._position--; // Opening '('; arguments can contain nested calls. if( fcn._type == Type.NIL ) throw P.error("Calling a null function pointer"); fcn.keep(); // Keep while parsing args Ary args = new Ary<>( Node.class ); args.push(null); // Space for ctrl,mem args.push(null); if( self != null ) // Method call has a self args.push(self.keep()); arguments(args); // Control & memory after parsing args args.set(0,P.ctrl().keep()); args.set(1,P.mem().keep()); args.push(fcn); // Function pointer // Unkeep them all for( Node arg : args ) arg.unkeep(); return functionCall(args,site); } Node functionCall( Ary args ) { return functionCall(args,P.loc()); } Node functionCall( Ary args, Lexer site ) { // Dead into the call? Skip all the node gen if( P.ctrl()._type == Type.XCONTROL ) { for( Node arg : args ) if( arg.isUnused() ) arg.kill(); return P.con(Type.TOP); } // Into the call CallNode call = (CallNode)new CallNode(site, args.asAry()).peephole(); // Post-call setup CallEndNode cend = (CallEndNode)new CallEndNode(call,TypeRPC.constant(P._code.rpc(P._ref._cname))).peephole(); call.peephole(); // Rerun peeps after CallEnd, allows early inlining cend = (CallEndNode)cend.keep().peephole(); // TODO: Might inline // Control from CallEnd P.ctrl(new CProjNode(cend,0,ScopeNode.CTRL).peephole()); // Memory from CallEnd Node mem = new ProjNode(cend,1,ScopeNode.MEM0).peephole(); P.mem(mem); // Call result return new ProjNode(cend.unkeep(),2,"#2").peephole(); } // argumentList; returns kept values and leaves the closing parenthesis to the caller. void arguments(Ary args) { while( !P.peek(')') ) { Node arg = P.expressions.parseAssignment(); if( arg==null ) break; args.push(arg.keep()); if( !P.match(",") ) break; } } /** * Parses a function body, assuming the header is parsed. */ private ReturnNode parseFunctionBody( TypeFunPtr sig, Lexer loc, BitSet finals, String[] ids) { // Record & restore the existing scope vars. The function parse might // update fields, so when the parse is done reset back to the pre-parse // state. Node[] preParse = P._scope.save(); // Parse function body normally ReturnNode ret = parseBody(null, sig,loc,finals,ids); // Reset all fields to pre-parse days P._scope.restore(preParse); return ret; } // Parse a function body // Stack the {break,continue,return} scopes and a new lexical scope. // Build a FunNode and parameters, extending scope by parm names. // Parse the function body // - while( not end-of-function ) // - - parseStatement // - - If isInit, also incrementally extend the self-type. // If isInit // - upgrade and close the self-type // - Gather fields and store values // - returns self, not last // Unstack the {break,continue,return} scopes. ReturnNode parseBody( String funName, TypeFunPtr sig, Lexer loc, String[] ids) { return parseBody(funName, sig, loc, null, ids); } ReturnNode parseBody( String funName, TypeFunPtr sig, Lexer loc, BitSet finals, String[] ids) { Structs.Constructor outerCtor = P.structs._pendingCtor; P.structs._pendingCtor = null; boolean isInit = FunNode.isInit(funName); boolean isCtor = isInit || P.structs.isExplicitConstructorName(funName); String typeName = isCtor ? ((TypeMemPtr)sig._sig[0])._obj._name : null; // Stack parser state on the local Java stack, and unstack it later P._scope.push(new Kind.Func(funName,isCtor && !funName.endsWith("."))); ScopeNode breakScope = P.statements._breakScope; P.statements._breakScope = null; ScopeNode continueScope = P.statements._continueScope; P.statements._continueScope = null; ScopeNode returnScope = _returnScope; _returnScope = null; int oldUID = P._code.UID(); // Used to approximate function size FunNode fun = enterFunction(funName,sig,loc,finals,ids); Node rpc = fun.parm(0); if( isCtor && !isInit ) P.structs.constructorDefaults(typeName,fun,loc); if( fun.isClz() ) P.structs.beginClassInit(fun,loc); // Parse the function body. Node last = P._code.ZERO; // Last statement as the default result int nvar = 0; while (!P.peek('}') && !P._lexer.isEOF()) { // Parse a statement; record last statement as default return last = P._code.add(P.statements.parseStatement()); // For and , local vars are really struct fields and // get exposed as discovered. This allows late local functions to // use early declared fields. if( isInit ) nvar = P.structs.updateSelfAsFieldsDiscovered( typeName, nvar ); } if( isInit ) P.structs.finishConstructor(); P.structs._pendingCtor = outerCtor; ParmNode self = fun.parm(2); // Last expression is the return if( P.ctrl()._type==Type.CONTROL ) { // Constructors get special return types if( fun.isClz() ) { assert last!=null; } else if( isCtor ) { last = fun.parm(3); } addReturn(last); // exit path is now dead (nothing live after a "return") } if( isCtor ) P.structs.upgradeSelfTypeAndStoreFields( typeName, fun.isClz(), self, fun.parm(3), isInit ); // Build a return from the _returnScope. // Can be no returns for never-exit functions Node rctl = _returnScope==null ? P._code.XCTRL : _returnScope.ctrl().peephole(); Node rmem = rctl==P._code.XCTRL ? P.con(TypeMem.TOP) : _returnScope.mem(); Node expr = rctl==P._code.XCTRL ? P.con(Type.TOP) : _returnScope._inputs.last().peephole(); ReturnNode ret = (ReturnNode)P.peep(new ReturnNode(rctl, rmem, expr, rpc, fun)); fun.setRet(ret); // Approximate function size, for inlining heuristics fun._approxUIDs = P._code.UID() - oldUID; for( Node cend : ret.outs() ) if( cend instanceof CallEndNode ) throw TODO(); //_code.add(cend); // have call ends ALREADY? just barely made the Return // Export (and keep-alive) functions until prove dead P._ref.addFun(P._code,fun); // Unstack parser state if( _returnScope != null ) _returnScope.kill(); _returnScope = returnScope; P.statements._continueScope = continueScope; P.statements._breakScope = breakScope; P._scope.pop(); return ret; } private FunNode enterFunction(String funName, TypeFunPtr sig, Lexer loc, BitSet finals, String[] ids) { FunNode fun = (FunNode)P.peep(new FunNode(P.loc(),sig, funName, P._ref, null, P._ref._start)); // Once the function header is available, install in linker table - // allowing recursive functions. Linker matches on declared args and // exact fidx, and ignores the return (because the fidx will only match // the exact single function). P._code.link(fun); new ParmNode("$rpc",0,TypeRPC.BOT,fun,ConstantNode.seed(TypeRPC.BOT).peephole()).peephole(); // Pre-call the function from Start, with worse-case arguments. This // represents all the future, yet-to-be-parsed functions calls and // external calls. P._scope.ctrl(fun); // Scope control from function // Private mem alias tracking per function Node defaultMem = new ProjNode(P._ref._start,1,ScopeNode.MEM0).peephole(); Node privMem = new ParmNode(ScopeNode.MEM0,1,TypeMem.BOT,fun,defaultMem).peephole(); P.mem(privMem); // All args, "as-if" called externally for( int i = 0; i and - all fields in scope will be live-on-exit // and stored into `self` and need to have lazy-phi's inserted. int lexN = P._scope.enclosingFuncOrDecl(); Kind.Func k = (Kind.Func)P._scope._kinds.at(lexN); if( FunNode.isInit(k._name) ) { int nestedLexSize = lexN+1 < P._scope.depth() ? P._scope._kinds.at(lexN+1)._lexSize : P._scope.nIns(); for( int i = k._lexSize; i and - ALL FIELDS IN LAST SCOPE are live // and need to have lazy-phi's inserted. // And fields might not match.... will need matching Var/define with default values int rlen = _returnScope.nIns()-1; RegionNode r = P.ctrl(new RegionNode(null, null,_returnScope.ctrl(), P._scope.ctrl()).init().keep()); _returnScope._merge(P._scope, r, rlen); Node oldExpr = _returnScope.in(rlen); _returnScope.setDef(rlen,new PhiNode("$expr", r, oldExpr, expr).peephole()); P._code.add(r); _returnScope.ctrl(r.unkeep()); } P.ctrl(P._code.XCTRL); // Kill control return expr.unkeep(); } }