summaryrefslogtreecommitdiff
path: root/src/main/java/com/seaofnodes/simple/node/LoopNode.java
blob: df686bcf81f59378afe853e7e041749c5f36b1bc (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
package com.seaofnodes.simple.node;

import com.seaofnodes.simple.Parser;
import com.seaofnodes.simple.codegen.CodeGen;
import com.seaofnodes.simple.type.Type;
import com.seaofnodes.simple.type.TypeMem;
import com.seaofnodes.simple.util.BAOS;

import java.util.HashMap;
import java.util.IdentityHashMap;

public class LoopNode extends RegionNode {
    public LoopNode( Parser.Lexer loc, Node xctrl, Node entry ) { super(loc,xctrl,entry,null); }
    public LoopNode( LoopNode loop ) { super(loop); }
    @Override public Tag serialTag() { return Tag.Loop; }
    public void packed( BAOS baos, HashMap<String,Integer> strs, HashMap<Type,Integer> types, IdentityHashMap<Node, Integer> anodes ) { }

    public CFGNode entry() { return cfg(1); }
    public CFGNode back () { return cfg(2); }

    @Override
    public Type compute() {
        if( inProgress() ) return Type.CONTROL;
        return entry()._type;
    }

    // Bypass Region idom, same as the default idom() using use in(1) instead of in(0)
    @Override public int idepth() { return validIDepth() ? _idepth : cacheIDepth(entry().idepth()+1); }
    // Bypass Region idom, same as the default idom() using use in(1) instead of in(0)
    @Override public CFGNode idom(Node dep) { return entry(); }

    // If this is an unreachable loop, it may not have an exit.  If it does not
    // (i.e., infinite loop), force an exit to make it reachable.
    public FunNode forceExit( FunNode fun, StopNode stop ) {
        // Walk the backedge, then immediate dominator tree util we hit this
        // Loop again.  If we ever hit a CProj from an If (as opposed to
        // directly on the If) we found our exit.
        CFGNode x = back();
        while( x != this ) {
            if( x instanceof CProjNode exit && exit.in(0) instanceof IfNode iff ) {
                CFGNode other = iff.cproj(1-exit._idx);
                if( other!=null && other._ltree != _ltree && nested(_ltree,other._ltree) )
                    return fun; // Found an exit, not an infinite loop
            }
            x = x.idom();
        }
        // Found a no-exit loop.  Insert an exit
        NeverNode iff = (NeverNode)new NeverNode(1.2f,back()).peephole(); // Ideal never-branch
        CProjNode t = new CProjNode(iff,0,"True" ).init();
        CProjNode f = new CProjNode(iff,1,"False").init();
        setDef(2,t);            // True continues loop, False (never) exits loop
        ReturnNode ret = fun.ret();
        iff._ltree = t._ltree = _ltree;
        ret._ltree = f._ltree = fun._ltree;

        // Now fold control into the exit.  Might have 1 valid exit, or an
        // XCtrl or a bunch of prior NeverNode exits.
        Node top = ConstantNode.make(Type.TOP).peephole();
        Node memout = new MemMergeNode();
        memout.addDef(f); // placeholder for control
        memout.addDef(top);
        for( Node u : _outputs )
            if( u instanceof PhiNode phi && phi._type.isa(TypeMem.BOT) )
                memout.addDef(phi);
        memout.init();

        Node ctrl = ret.ctrl(), mem = ret.mem(), expr = ret.expr();
        if( ctrl!=null && ctrl._type != Type.XCONTROL ) {
            // Perfect aligned exit?
            if( !(ctrl instanceof RegionNode r &&
                  mem  instanceof PhiNode pmem && pmem.region()==r &&
                  expr instanceof PhiNode prez && prez.region()==r ) ) {
                // Nope, insert an aligned exit layer
                RegionNode r = new RegionNode(_loc,null,ctrl).init();
                ctrl = r;  r._ltree = fun._ltree;
                mem  = new BulkMemPhiNode(r,mem).init();
                expr = new PhiNode(r,expr).init();
            }
            // Append new Never exit
            ctrl.keep().addDef(f  );
            mem .keep().addDef(memout);
            expr.keep().addDef(top);
        } else {
            ctrl = f     .keep();
            mem  = memout.keep();
            expr = top   .keep();
        }
        ret.setDef(0,ctrl.unkeep());
        ret.setDef(1,mem .unkeep());
        ret.setDef(2,expr.unkeep());
        // Force sane types to exit; might locally break monotonicity
        ret.init();
        ret.out(0).init();
        stop.init();
        CodeGen.CODE.add(CodeGen.CODE._start);
        CodeGen.CODE.add(mem);

        return fun;
    }

    private static boolean nested(LoopTree inner, LoopTree outer) {
        for( ; inner!=null; inner = inner._par )
            if( inner == outer )
                return true;
        return false;
    }

}