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
|
package com.seaofnodes.simple.parser;
import com.seaofnodes.simple.node.*;
import com.seaofnodes.simple.parser.Parser.ParseException;
import com.seaofnodes.simple.type.*;
import static com.seaofnodes.simple.util.Utils.TODO;
/** Shared guarded branch/merge construction for if, ?:, && and ||. */
final class ControlFlow {
private final Parser P;
ControlFlow(Parser P) { this.P = P; }
// Parse a conditional expression, merging results.
Node conditional( Node pred, String fside ) {
// IfNode takes current control and predicate
pred.keep();
Node ifNode = new IfNode(P.ctrl(), pred).peephole();
// Setup projection nodes
CFGNode ifT = new CProjNode(ifNode. keep(), 0, "True" ).peephole().keep();
CFGNode ifF = new CProjNode(ifNode.unkeep(), 1, "False").peephole().keep();
// In if true branch, the ifT proj node becomes the ctrl
// But first clone the scope and set it as current
ScopeNode fScope = P._scope.dup(); // Duplicate current scope
fScope.ctrl(ifF); // The pending arm already starts at False
P._xScopes.push(fScope); // For graph visualization we need all scopes
// Parse the true side
P.ctrl(ifT.unkeep()); // set ctrl token to ifTrue projection
P._scope.addGuards(ifT,pred,false); // Up-cast predicate
// Parse true-side flavor
Node lhs = switch( fside ) {
case "else" -> P.statements.parseStatement(); // if( pred ) stmts;
case ":" -> P.expressions.parseAssignment(); // pred ? asgn;
case "&&" -> P.expressions.parseLogical(); // pred && expr
case "||" -> pred; // pred || expr_is_ignored
default -> throw TODO();
};
lhs.keep();
P._scope.removeGuards(ifT);
// See if a one-sided def was made: "if(pred) int x = 1;" and throw.
// See if any forward-refs were made, and copy them to the other side:
// "pred ? n*fact(n-1) : 1"
fScope.balanceIf(P._scope);
ScopeNode tScope = P._scope;
// Parse the false side
P._scope = fScope; // Restore scope, then parse else block if any
ifF.unkeep(); // fScope already owns the false control
// Up-cast predicate, even if not else clause, because predicate can
// remain true if the true clause exits: `if( !ptr ) return 0; return ptr.fld;`
P._scope.addGuards(ifF,pred,true);
// Parse false-side flavor
boolean doRHS = false; // RHS is optional for if/else and trinary
Node rhs = switch( fside ) {
case "else" -> (doRHS = P.match(fside)) ? P.statements.parseStatement() : P.con(lhs._type.makeZero());
case ":" -> (doRHS = P.match(fside)) ? P.expressions.parseAssignment() : P.con(lhs._type.makeZero());
case "&&" -> P.con(lhs._type.makeZero()); // pred && expr_is_ignored
case "||" -> P.expressions.parseLogical(); // pred || expr
default -> throw TODO();
};
rhs.keep();
P._scope.removeGuards(ifF);
// The RHS of `||` is parsed on the false side, so retain its updated
// control, memory and locals just as an explicit else arm does.
if( doRHS || fside.equals("||") )
fScope = P._scope;
pred.unkeep();
// Check the trinary widening int/flt
if( !fside.equals("else") ) {
rhs = P.conversions.widenInt( rhs.unkeep(), lhs._type ).keep();
lhs = P.conversions.widenInt( lhs.unkeep(), rhs._type ).keep();
}
P._scope = tScope;
P._xScopes.pop(); // Discard pushed from graph display
// See if a one-sided def was made: "if(pred) int x = 1;" and throw.
// See if any forward-refs were made, and copy them to the other side:
// "pred ? n*fact(n-1) : 1"
tScope.balanceIf(fScope);
// Merge results
RegionNode r = P.ctrl(tScope.mergeScopes(fScope,P.loc()));
Node ret = P.peep(new PhiNode("",r,lhs.unkeep(),rhs.unkeep()));
// Immediately fail e.g. `arg ? 7 : ptr`
ParseException err;
if( !fside.equals("else") && (err = ret.err()) !=null ) throw err;
ret.keep();
r.peephole();
return ret.unkeep();
}
}
|