source: src/Concurrency/Waituntil.cpp @ da4a570

Last change on this file since da4a570 was fc0996a, checked in by caparsons <caparson@…>, 15 months ago

refactores to account for removal of ret val from on_selected

  • Property mode set to 100644
File size: 56.3 KB
Line 
1//
2// Cforall Version 1.0.0 Copyright (C) 2016 University of Waterloo
3//
4// The contents of this file are covered under the licence agreement in the
5// file "LICENCE" distributed with Cforall.
6//
7// WaitforNew.cpp -- Expand waitfor clauses into code.
8//
9// Author           : Andrew Beach
10// Created On       : Fri May 27 10:31:00 2022
11// Last Modified By : Andrew Beach
12// Last Modified On : Tue Jun 13 13:30:00 2022
13// Update Count     : 0
14//
15
16#include "Waituntil.hpp"
17
18#include <string>
19
20#include "AST/Copy.hpp"
21#include "AST/Expr.hpp"
22#include "AST/Pass.hpp"
23#include "AST/Print.hpp"
24#include "AST/Stmt.hpp"
25#include "AST/Type.hpp"
26#include "Common/UniqueName.h"
27
28using namespace ast;
29using namespace std;
30
31/* So this is what this pass dones:
32{
33    when ( condA ) waituntil( A ){ doA(); }
34    or when ( condB ) waituntil( B ){ doB(); }
35    and when ( condC ) waituntil( C ) { doC(); }
36}
37                 ||
38                 ||
39                \||/
40                 \/
41
42Generates these two routines:
43static inline bool is_full_sat_1( int * clause_statuses ) {
44    return clause_statuses[0]
45        || clause_statuses[1]
46        && clause_statuses[2];
47}
48
49static inline bool is_done_sat_1( int * clause_statuses ) {
50    return has_run(clause_statuses[0])
51        || has_run(clause_statuses[1])
52        && has_run(clause_statuses[2]);
53}
54
55Replaces the waituntil statement above with the following code:
56{
57    // used with atomic_dec/inc to get binary semaphore behaviour
58    int park_counter = 0;
59
60    // status (one for each clause)
61    int clause_statuses[3] = { 0 };
62
63    bool whenA = condA;
64    bool whenB = condB;
65    bool whenC = condC;
66
67    if ( !whenB ) clause_statuses[1] = __SELECT_RUN;
68    if ( !whenC ) clause_statuses[2] = __SELECT_RUN;
69
70    // some other conditional settors for clause_statuses are set here, see genSubtreeAssign and related routines
71
72    // three blocks
73    // for each block, create, setup, then register select_node
74    select_node clause1;
75    select_node clause2;
76    select_node clause3;
77
78    try {
79        if ( whenA ) { register_select(A, clause1); setup_clause( clause1, &clause_statuses[0], &park_counter ); }
80        ... repeat ^ for B and C ...
81
82        // if else clause is defined a separate branch can occur here to set initial values, see genWhenStateConditions
83
84        // loop & park until done
85        while( !is_full_sat_1( clause_statuses ) ) {
86           
87            // binary sem P();
88            if ( __atomic_sub_fetch( &park_counter, 1, __ATOMIC_SEQ_CST) < 0 )
89                park();
90           
91            // execute any blocks available with status set to 0
92            for ( int i = 0; i < 3; i++ ) {
93                if (clause_statuses[i] == __SELECT_SAT) {
94                    switch (i) {
95                        case 0:
96                            try {
97                                    on_selected( A, clause1 );
98                                    doA();
99                            }
100                            finally { clause_statuses[i] = __SELECT_RUN; unregister_select(A, clause1); }
101                            break;
102                        case 1:
103                            ... same gen as A but for B and clause2 ...
104                            break;
105                        case 2:
106                            ... same gen as A but for C and clause3 ...
107                            break;
108                    }
109                }
110            }
111        }
112
113        // ensure that the blocks that triggered is_full_sat_1 are run
114        // by running every un-run block that is SAT from the start until
115        // the predicate is SAT when considering RUN status = true
116        for ( int i = 0; i < 3; i++ ) {
117            if (is_done_sat_1( clause_statuses )) break;
118            if (clause_statuses[i] == __SELECT_SAT)
119                ... Same if body here as in loop above ...
120        }
121    } finally {
122        // the unregister and on_selected calls are needed to support primitives where the acquire has side effects
123        // so the corresponding block MUST be run for those primitives to not lose state (example is channels)
124        if ( !has_run(clause_statuses[0]) && whenA && unregister_select(A, clause1) )
125            on_selected( A, clause1 )
126            doA();
127        ... repeat if above for B and C ...
128    }
129}
130
131*/
132
133namespace Concurrency {
134
135class GenerateWaitUntilCore final {
136    vector<FunctionDecl *> & satFns;
137        UniqueName namer_sat = "__is_full_sat_"s;
138    UniqueName namer_run = "__is_run_sat_"s;
139        UniqueName namer_park = "__park_counter_"s;
140        UniqueName namer_status = "__clause_statuses_"s;
141        UniqueName namer_node = "__clause_"s;
142    UniqueName namer_target = "__clause_target_"s;
143    UniqueName namer_when = "__when_cond_"s;
144
145    string idxName = "__CFA_clause_idx_";
146
147    struct ClauseData {
148        string nodeName;
149        string targetName;
150        string whenName;
151        int index;
152        string & statusName;
153        ClauseData( int index, string & statusName ) : index(index), statusName(statusName) {}
154    };
155
156    const StructDecl * selectNodeDecl = nullptr;
157
158    // This first set of routines are all used to do the complicated job of
159    //    dealing with how to set predicate statuses with certain when_conds T/F
160    //    so that the when_cond == F effectively makes that clause "disappear"
161    void updateAmbiguousWhen( WaitUntilStmt::ClauseNode * currNode, bool andAbove, bool orAbove, bool andBelow, bool orBelow );
162    void paintWhenTree( WaitUntilStmt::ClauseNode * currNode, bool andAbove, bool orAbove, bool & andBelow, bool & orBelow );
163    bool paintWhenTree( WaitUntilStmt::ClauseNode * currNode );
164    void collectWhens( WaitUntilStmt::ClauseNode * currNode, vector<pair<int, WaitUntilStmt::ClauseNode *>> & ambigIdxs, vector<int> & andIdxs, int & index, bool parentAmbig, bool parentAnd );
165    void collectWhens( WaitUntilStmt::ClauseNode * currNode, vector<pair<int, WaitUntilStmt::ClauseNode *>> & ambigIdxs, vector<int> & andIdxs );
166    void updateWhenState( WaitUntilStmt::ClauseNode * currNode );
167    void genSubtreeAssign( const WaitUntilStmt * stmt, WaitUntilStmt::ClauseNode * currNode, bool status, int & idx, CompoundStmt * retStmt, vector<ClauseData *> & clauseData );
168    void genStatusAssign( const WaitUntilStmt * stmt, WaitUntilStmt::ClauseNode * currNode, int & idx, CompoundStmt * retStmt, vector<ClauseData *> & clauseData );
169    CompoundStmt * getStatusAssignment( const WaitUntilStmt * stmt, vector<ClauseData *> & clauseData );
170    Stmt * genWhenStateConditions( const WaitUntilStmt * stmt, vector<ClauseData *> & clauseData, vector<pair<int, WaitUntilStmt::ClauseNode *>> & ambigClauses, vector<pair<int, WaitUntilStmt::ClauseNode *>>::size_type ambigIdx );
171
172    // These routines are just code-gen helpers
173    void addPredicates( const WaitUntilStmt * stmt, string & satName, string & runName );
174    void setUpClause( const WhenClause * clause, ClauseData * data, string & pCountName, CompoundStmt * body );
175    ForStmt * genStatusCheckFor( const WaitUntilStmt * stmt, vector<ClauseData *> & clauseData, string & predName );
176    Expr * genSelectTraitCall( const WhenClause * clause, const ClauseData * data, string fnName );
177    CompoundStmt * genStmtBlock( const WhenClause * clause, const ClauseData * data );
178    Stmt * genElseClauseBranch( const WaitUntilStmt * stmt, string & runName, string & arrName, vector<ClauseData *> & clauseData );
179    Stmt * genNoElseClauseBranch( const WaitUntilStmt * stmt, string & satName, string & runName, string & arrName, string & pCountName, vector<ClauseData *> & clauseData );
180    void genClauseInits( const WaitUntilStmt * stmt, vector<ClauseData *> & clauseData, CompoundStmt * body, string & statusName, string & elseWhenName );
181    Stmt * recursiveOrIfGen( const WaitUntilStmt * stmt, vector<ClauseData *> & data, vector<ClauseData*>::size_type idx, string & elseWhenName );
182    Stmt * buildOrCaseSwitch( const WaitUntilStmt * stmt, string & statusName, vector<ClauseData *> & data );
183    Stmt * genAllOr( const WaitUntilStmt * stmt );
184
185  public:
186    void previsit( const StructDecl * decl );
187        Stmt * postvisit( const WaitUntilStmt * stmt );
188    GenerateWaitUntilCore( vector<FunctionDecl *> & satFns ): satFns(satFns) {}
189};
190
191// Finds select_node decl
192void GenerateWaitUntilCore::previsit( const StructDecl * decl ) {
193    if ( !decl->body ) {
194                return;
195        } else if ( "select_node" == decl->name ) {
196                assert( !selectNodeDecl );
197                selectNodeDecl = decl;
198        }
199}
200
201void GenerateWaitUntilCore::updateAmbiguousWhen( WaitUntilStmt::ClauseNode * currNode, bool andAbove, bool orAbove, bool andBelow, bool orBelow ) {
202    // all children when-ambiguous
203    if ( currNode->left->ambiguousWhen && currNode->right->ambiguousWhen )
204        // true iff an ancestor/descendant has a different operation
205        currNode->ambiguousWhen = (orAbove || orBelow) && (andBelow || andAbove);
206    // ambiguousWhen is initially false so theres no need to set it here
207}
208
209// Traverses ClauseNode tree and paints each AND/OR node as when-ambiguous true or false
210// This tree painting is needed to generate the if statements that set the initial state
211//    of the clause statuses when some clauses are turned off via when_cond
212// An internal AND/OR node is when-ambiguous if it satisfies all of the following:
213// - It has an ancestor or descendant that is a different operation, i.e. (AND has an OR ancestor or vice versa)
214// - All of its descendent clauses are optional, i.e. they have a when_cond defined on the WhenClause
215void GenerateWaitUntilCore::paintWhenTree( WaitUntilStmt::ClauseNode * currNode, bool andAbove, bool orAbove, bool & andBelow, bool & orBelow ) {
216    bool aBelow = false; // updated by child nodes
217    bool oBelow = false; // updated by child nodes
218    switch (currNode->op) {
219        case WaitUntilStmt::ClauseNode::AND:
220            paintWhenTree( currNode->left, true, orAbove, aBelow, oBelow );
221            paintWhenTree( currNode->right, true, orAbove, aBelow, oBelow );
222
223            // update currNode's when flag based on conditions listed in fn signature comment above
224            updateAmbiguousWhen(currNode, true, orAbove, aBelow, oBelow );
225
226            // set return flags to tell parents which decendant ops have been seen
227            andBelow = true;
228            orBelow = oBelow;
229            return;
230        case WaitUntilStmt::ClauseNode::OR:
231            paintWhenTree( currNode->left, andAbove, true, aBelow, oBelow );
232            paintWhenTree( currNode->right, andAbove, true, aBelow, oBelow );
233
234            // update currNode's when flag based on conditions listed in fn signature comment above
235            updateAmbiguousWhen(currNode, andAbove, true, aBelow, oBelow );
236
237            // set return flags to tell parents which decendant ops have been seen
238            andBelow = aBelow;
239            orBelow = true;
240            return;
241        case WaitUntilStmt::ClauseNode::LEAF:
242            if ( currNode->leaf->when_cond )
243                currNode->ambiguousWhen = true;
244            return;
245        default:
246            assertf(false, "Unreachable waituntil clause node type. How did you get here???");
247    }
248}
249
250// overloaded wrapper for paintWhenTree that sets initial values
251// returns true if entire tree is OR's (special case)
252bool GenerateWaitUntilCore::paintWhenTree( WaitUntilStmt::ClauseNode * currNode ) {
253    bool aBelow = false, oBelow = false; // unused by initial call
254    paintWhenTree( currNode, false, false, aBelow, oBelow );
255    return !aBelow;
256}
257
258// Helper: returns Expr that represents arrName[index]
259Expr * genArrAccessExpr( const CodeLocation & loc, int index, string arrName ) {
260    return new UntypedExpr ( loc, 
261        new NameExpr( loc, "?[?]" ),
262        {
263            new NameExpr( loc, arrName ),
264            ConstantExpr::from_int( loc, index )
265        }
266    );
267}
268
269// After the ClauseNode AND/OR nodes are painted this routine is called to traverses the tree and does the following:
270// - collects a set of indices in the clause arr that refer whenclauses that can have ambiguous status assignments (ambigIdxs)
271// - collects a set of indices in the clause arr that refer whenclauses that have a when() defined and an AND node as a parent (andIdxs)
272// - updates LEAF nodes to be when-ambiguous if their direct parent is when-ambiguous.
273void GenerateWaitUntilCore::collectWhens( WaitUntilStmt::ClauseNode * currNode, vector<pair<int, WaitUntilStmt::ClauseNode *>> & ambigIdxs, vector<int> & andIdxs, int & index, bool parentAmbig, bool parentAnd ) {
274    switch (currNode->op) {
275        case WaitUntilStmt::ClauseNode::AND:
276            collectWhens( currNode->left, ambigIdxs, andIdxs, index, currNode->ambiguousWhen, true );
277            collectWhens( currNode->right,  ambigIdxs, andIdxs, index, currNode->ambiguousWhen, true );
278            return;
279        case WaitUntilStmt::ClauseNode::OR:
280            collectWhens( currNode->left,  ambigIdxs, andIdxs, index, currNode->ambiguousWhen, false );
281            collectWhens( currNode->right,  ambigIdxs, andIdxs, index, currNode->ambiguousWhen, false );
282            return;
283        case WaitUntilStmt::ClauseNode::LEAF:
284            if ( parentAmbig ) {
285                ambigIdxs.push_back(make_pair(index, currNode));
286            }
287            if ( parentAnd && currNode->leaf->when_cond ) {
288                currNode->childOfAnd = true;
289                andIdxs.push_back(index);
290            }
291            index++;
292            return;
293        default:
294            assertf(false, "Unreachable waituntil clause node type. How did you get here???");
295    }
296}
297
298// overloaded wrapper for collectWhens that sets initial values
299void GenerateWaitUntilCore::collectWhens( WaitUntilStmt::ClauseNode * currNode, vector<pair<int, WaitUntilStmt::ClauseNode *>> & ambigIdxs, vector<int> & andIdxs ) {
300    int idx = 0;
301    collectWhens( currNode, ambigIdxs, andIdxs, idx, false, false );
302}
303
304// recursively updates ClauseNode whenState on internal nodes so that next pass can see which
305//    subtrees are "turned off"
306// sets whenState = false iff both children have whenState == false.
307// similar to paintWhenTree except since paintWhenTree also filtered out clauses we don't need to consider based on op
308// since the ambiguous clauses were filtered in paintWhenTree we don't need to worry about that here
309void GenerateWaitUntilCore::updateWhenState( WaitUntilStmt::ClauseNode * currNode ) {
310    if ( currNode->op == WaitUntilStmt::ClauseNode::LEAF ) return;
311    updateWhenState( currNode->left );
312    updateWhenState( currNode->right );
313    if ( !currNode->left->whenState && !currNode->right->whenState )
314        currNode->whenState = false;
315    else 
316        currNode->whenState = true;
317}
318
319// generates the minimal set of status assignments to ensure predicate subtree passed as currNode evaluates to status
320// assumes that this will only be called on subtrees that are entirely whenState == false
321void GenerateWaitUntilCore::genSubtreeAssign( const WaitUntilStmt * stmt, WaitUntilStmt::ClauseNode * currNode, bool status, int & idx, CompoundStmt * retStmt, vector<ClauseData *> & clauseData ) {
322    if ( ( currNode->op == WaitUntilStmt::ClauseNode::AND && status )
323        || ( currNode->op == WaitUntilStmt::ClauseNode::OR && !status ) ) {
324        // need to recurse on both subtrees if && subtree needs to be true or || subtree needs to be false
325        genSubtreeAssign( stmt, currNode->left, status, idx, retStmt, clauseData );
326        genSubtreeAssign( stmt, currNode->right, status, idx, retStmt, clauseData );
327    } else if ( ( currNode->op == WaitUntilStmt::ClauseNode::OR && status )
328        || ( currNode->op == WaitUntilStmt::ClauseNode::AND && !status ) ) {
329        // only one subtree needs to evaluate to status if && subtree needs to be true or || subtree needs to be false
330        CompoundStmt * leftStmt = new CompoundStmt( stmt->location );
331        CompoundStmt * rightStmt = new CompoundStmt( stmt->location );
332
333        // only one side needs to evaluate to status so we recurse on both subtrees
334        //    but only keep the statements from the subtree with minimal statements
335        genSubtreeAssign( stmt, currNode->left, status, idx, leftStmt, clauseData );
336        genSubtreeAssign( stmt, currNode->right, status, idx, rightStmt, clauseData );
337       
338        // append minimal statements to retStmt
339        if ( leftStmt->kids.size() < rightStmt->kids.size() ) {
340            retStmt->kids.splice( retStmt->kids.end(), leftStmt->kids );
341        } else {
342            retStmt->kids.splice( retStmt->kids.end(), rightStmt->kids );
343        }
344       
345        delete leftStmt;
346        delete rightStmt;
347    } else if ( currNode->op == WaitUntilStmt::ClauseNode::LEAF ) {
348        const CodeLocation & loc = stmt->location;
349        if ( status && !currNode->childOfAnd ) {
350            retStmt->push_back(
351                new ExprStmt( loc, 
352                    UntypedExpr::createAssign( loc,
353                        genArrAccessExpr( loc, idx, clauseData.at(idx)->statusName ),
354                        new NameExpr( loc, "__SELECT_RUN" )
355                    )
356                )
357            );
358        } else if ( !status && currNode->childOfAnd ) {
359            retStmt->push_back(
360                new ExprStmt( loc, 
361                    UntypedExpr::createAssign( loc,
362                        genArrAccessExpr( loc, idx, clauseData.at(idx)->statusName ),
363                        new NameExpr( loc, "__SELECT_UNSAT" )
364                    )
365                )
366            );
367        }
368
369        // No need to generate statements for the following cases since childOfAnd are always set to true
370        //    and !childOfAnd are always false
371        // - status && currNode->childOfAnd
372        // - !status && !currNode->childOfAnd
373        idx++;
374    }
375}
376
377void GenerateWaitUntilCore::genStatusAssign( const WaitUntilStmt * stmt, WaitUntilStmt::ClauseNode * currNode, int & idx, CompoundStmt * retStmt, vector<ClauseData *> & clauseData ) {
378    switch (currNode->op) {
379        case WaitUntilStmt::ClauseNode::AND:
380            // check which subtrees have all whenState == false (disabled)
381            if (!currNode->left->whenState && !currNode->right->whenState) {
382                // this case can only occur when whole tree is disabled since otherwise
383                //    genStatusAssign( ... ) isn't called on nodes with whenState == false
384                assert( !currNode->whenState ); // paranoidWWW
385                // whole tree disabled so pass true so that select is SAT vacuously
386                genSubtreeAssign( stmt, currNode, true, idx, retStmt, clauseData );
387            } else if ( !currNode->left->whenState ) {
388                // pass true since x && true === x
389                genSubtreeAssign( stmt, currNode->left, true, idx, retStmt, clauseData );
390                genStatusAssign( stmt, currNode->right, idx, retStmt, clauseData );
391            } else if ( !currNode->right->whenState ) {
392                genStatusAssign( stmt, currNode->left, idx, retStmt, clauseData );
393                genSubtreeAssign( stmt, currNode->right, true, idx, retStmt, clauseData );
394            } else { 
395                // if no children with whenState == false recurse normally via break
396                break;
397            }
398            return;
399        case WaitUntilStmt::ClauseNode::OR:
400            if (!currNode->left->whenState && !currNode->right->whenState) {
401                assert( !currNode->whenState ); // paranoid
402                genSubtreeAssign( stmt, currNode, true, idx, retStmt, clauseData );
403            } else if ( !currNode->left->whenState ) {
404                // pass false since x || false === x
405                genSubtreeAssign( stmt, currNode->left, false, idx, retStmt, clauseData );
406                genStatusAssign( stmt, currNode->right, idx, retStmt, clauseData );
407            } else if ( !currNode->right->whenState ) {
408                genStatusAssign( stmt, currNode->left, idx, retStmt, clauseData );
409                genSubtreeAssign( stmt, currNode->right, false, idx, retStmt, clauseData );
410            } else { 
411                break;
412            }
413            return;
414        case WaitUntilStmt::ClauseNode::LEAF:
415            idx++;
416            return;
417        default:
418            assertf(false, "Unreachable waituntil clause node type. How did you get here???");
419    }
420    genStatusAssign( stmt, currNode->left, idx, retStmt, clauseData );
421    genStatusAssign( stmt, currNode->right, idx, retStmt, clauseData );
422}
423
424// generates a minimal set of assignments for status arr based on which whens are toggled on/off
425CompoundStmt * GenerateWaitUntilCore::getStatusAssignment( const WaitUntilStmt * stmt, vector<ClauseData *> & clauseData ) {
426    updateWhenState( stmt->predicateTree );
427    CompoundStmt * retval = new CompoundStmt( stmt->location );
428    int idx = 0;
429    genStatusAssign( stmt, stmt->predicateTree, idx, retval, clauseData );
430    return retval;
431}
432
433// generates nested if/elses for all possible assignments of ambiguous when_conds
434// exponential size of code gen but linear runtime O(n), where n is number of ambiguous whens()
435Stmt * GenerateWaitUntilCore::genWhenStateConditions( const WaitUntilStmt * stmt, vector<ClauseData *> & clauseData, 
436    vector<pair<int, WaitUntilStmt::ClauseNode *>> & ambigClauses, vector<pair<int, WaitUntilStmt::ClauseNode *>>::size_type ambigIdx ) {
437    // I hate C++ sometimes, using vector<pair<int, WaitUntilStmt::ClauseNode *>>::size_type for size() comparison seems silly.
438    //    Why is size_type parameterized on the type stored in the vector?????
439
440    const CodeLocation & loc = stmt->location;
441    int clauseIdx = ambigClauses.at(ambigIdx).first;
442    WaitUntilStmt::ClauseNode * currNode = ambigClauses.at(ambigIdx).second;
443    Stmt * thenStmt;
444    Stmt * elseStmt;
445   
446    if ( ambigIdx == ambigClauses.size() - 1 ) { // base case
447        currNode->whenState = true;
448        thenStmt = getStatusAssignment( stmt, clauseData );
449        currNode->whenState = false;
450        elseStmt = getStatusAssignment( stmt, clauseData );
451    } else {
452        // recurse both with when enabled and disabled to generate all possible cases
453        currNode->whenState = true;
454        thenStmt = genWhenStateConditions( stmt, clauseData, ambigClauses, ambigIdx + 1 );
455        currNode->whenState = false;
456        elseStmt = genWhenStateConditions( stmt, clauseData, ambigClauses, ambigIdx + 1 );
457    }
458
459    // insert first recursion result in if ( __when_cond_ ) { ... }
460    // insert second recursion result in else { ... }
461    return new CompoundStmt ( loc,
462        {
463            new IfStmt( loc,
464                new NameExpr( loc, clauseData.at(clauseIdx)->whenName ),
465                thenStmt,
466                elseStmt
467            )
468        }
469    );
470}
471
472// typedef a fn ptr so that we can reuse genPredExpr
473// genLeafExpr is used to refer to one of the following two routines
474typedef Expr * (*GenLeafExpr)( const CodeLocation & loc, int & index );
475
476// return Expr that represents clause_statuses[index]
477// mutates index to be index + 1
478Expr * genSatExpr( const CodeLocation & loc, int & index ) {
479    return genArrAccessExpr( loc, index++, "clause_statuses" );
480}
481
482// return Expr that represents has_run(clause_statuses[index])
483Expr * genRunExpr( const CodeLocation & loc, int & index ) {
484    return new UntypedExpr ( loc, 
485        new NameExpr( loc, "__CFA_has_clause_run" ),
486        { genSatExpr( loc, index ) }
487    );
488}
489
490// Takes in the ClauseNode tree and recursively generates
491// the predicate expr used inside the predicate functions
492Expr * genPredExpr( const CodeLocation & loc, WaitUntilStmt::ClauseNode * currNode, int & idx, GenLeafExpr genLeaf ) {
493    switch (currNode->op) {
494        case WaitUntilStmt::ClauseNode::AND:
495            return new LogicalExpr( loc, 
496                new CastExpr( loc, genPredExpr( loc, currNode->left, idx, genLeaf ), new BasicType( BasicType::Kind::Bool ), GeneratedFlag::ExplicitCast ),
497                new CastExpr( loc, genPredExpr( loc, currNode->right, idx, genLeaf ), new BasicType( BasicType::Kind::Bool ), GeneratedFlag::ExplicitCast ), 
498                LogicalFlag::AndExpr
499            );
500        case WaitUntilStmt::ClauseNode::OR:
501            return new LogicalExpr( loc,
502                new CastExpr( loc, genPredExpr( loc, currNode->left, idx, genLeaf ), new BasicType( BasicType::Kind::Bool ), GeneratedFlag::ExplicitCast ),
503                new CastExpr( loc, genPredExpr( loc, currNode->right, idx, genLeaf ), new BasicType( BasicType::Kind::Bool ), GeneratedFlag::ExplicitCast ), 
504                LogicalFlag::OrExpr );
505        case WaitUntilStmt::ClauseNode::LEAF:
506            return genLeaf( loc, idx );
507        default:
508            assertf(false, "Unreachable waituntil clause node type. How did you get here???");
509    }
510}
511
512
513// Builds the predicate functions used to check the status of the waituntil statement
514/* Ex:
515{
516    waituntil( A ){ doA(); }
517    or waituntil( B ){ doB(); }
518    and waituntil( C ) { doC(); }
519}
520generates =>
521static inline bool is_full_sat_1( int * clause_statuses ) {
522    return clause_statuses[0]
523        || clause_statuses[1]
524        && clause_statuses[2];
525}
526
527static inline bool is_done_sat_1( int * clause_statuses ) {
528    return has_run(clause_statuses[0])
529        || has_run(clause_statuses[1])
530        && has_run(clause_statuses[2]);
531}
532*/
533// Returns a predicate function decl
534// predName and genLeaf determine if this generates an is_done or an is_full predicate
535FunctionDecl * buildPredicate( const WaitUntilStmt * stmt, GenLeafExpr genLeaf, string & predName ) {
536    int arrIdx = 0;
537    const CodeLocation & loc = stmt->location;
538    CompoundStmt * body = new CompoundStmt( loc );
539    body->push_back( new ReturnStmt( loc, genPredExpr( loc,  stmt->predicateTree, arrIdx, genLeaf ) ) );
540
541    return new FunctionDecl( loc,
542        predName,
543        {},                     // forall
544        {
545            new ObjectDecl( loc,
546                "clause_statuses",
547                new PointerType( new BasicType( BasicType::Kind::LongUnsignedInt ) )
548            )
549        },
550        { 
551            new ObjectDecl( loc,
552                "sat_ret",
553                new BasicType( BasicType::Kind::Bool )
554            )
555        },
556        body,               // body
557        { Storage::Static },    // storage
558        Linkage::Cforall,       // linkage
559        {},                     // attributes
560        { Function::Inline }
561    );
562}
563
564// Creates is_done and is_full predicates
565void GenerateWaitUntilCore::addPredicates( const WaitUntilStmt * stmt, string & satName, string & runName ) {
566    if ( !stmt->else_stmt || stmt->else_cond ) // don't need SAT predicate when else variation with no else_cond
567        satFns.push_back( Concurrency::buildPredicate( stmt, genSatExpr, satName ) ); 
568    satFns.push_back( Concurrency::buildPredicate( stmt, genRunExpr, runName ) );
569}
570
571// Adds the following to body:
572// if ( when_cond ) { // this if is omitted if no when() condition
573//      setup_clause( clause1, &clause_statuses[0], &park_counter );
574//      register_select(A, clause1);
575// }
576void GenerateWaitUntilCore::setUpClause( const WhenClause * clause, ClauseData * data, string & pCountName, CompoundStmt * body ) {   
577    CompoundStmt * currBody = body;
578    const CodeLocation & loc = clause->location;
579
580    // If we have a when_cond make the initialization conditional
581    if ( clause->when_cond )
582        currBody = new CompoundStmt( loc );
583
584    // Generates: setup_clause( clause1, &clause_statuses[0], &park_counter );
585    currBody->push_back( new ExprStmt( loc,
586        new UntypedExpr ( loc,
587            new NameExpr( loc, "setup_clause" ),
588            {
589                new NameExpr( loc, data->nodeName ),
590                new AddressExpr( loc, genArrAccessExpr( loc, data->index, data->statusName ) ),
591                new AddressExpr( loc, new NameExpr( loc, pCountName ) )
592            }
593        )
594    ));
595
596    // Generates: register_select(A, clause1);
597    currBody->push_back( new ExprStmt( loc, genSelectTraitCall( clause, data, "register_select" ) ) );
598
599    // generates: if ( when_cond ) { ... currBody ... }
600    if ( clause->when_cond )
601        body->push_back( 
602            new IfStmt( loc,
603                new NameExpr( loc, data->whenName ),
604                currBody
605            )
606        );
607}
608
609// Used to generate a call to one of the select trait routines
610Expr * GenerateWaitUntilCore::genSelectTraitCall( const WhenClause * clause, const ClauseData * data, string fnName ) {
611    const CodeLocation & loc = clause->location;
612    return new UntypedExpr ( loc,
613        new NameExpr( loc, fnName ),
614        {
615            new NameExpr( loc, data->targetName ),
616            new NameExpr( loc, data->nodeName )
617        }
618    );
619}
620
621// Generates:
622/* on_selected( target_1, node_1 ); ... corresponding body of target_1 ...
623*/
624CompoundStmt * GenerateWaitUntilCore::genStmtBlock( const WhenClause * clause, const ClauseData * data ) {
625    const CodeLocation & cLoc = clause->location;
626    return new CompoundStmt( cLoc,
627        {
628            new ExprStmt( cLoc,
629                genSelectTraitCall( clause, data, "on_selected" )
630            ),
631            ast::deepCopy( clause->stmt )
632        }
633    );
634}
635
636// this routine generates and returns the following
637/*for ( int i = 0; i < numClauses; i++ ) {
638    if ( predName(clause_statuses) ) break;
639    if (clause_statuses[i] == __SELECT_SAT) {
640        switch (i) {
641            case 0:
642                try {
643                    on_selected( target1, clause1 );
644                    dotarget1stmt();
645                }
646                finally { clause_statuses[i] = __SELECT_RUN; unregister_select(target1, clause1); }
647                break;
648            ...
649            case N:
650                ...
651                break;
652        }
653    }
654}*/
655ForStmt * GenerateWaitUntilCore::genStatusCheckFor( const WaitUntilStmt * stmt, vector<ClauseData *> & clauseData, string & predName ) {
656    CompoundStmt * ifBody = new CompoundStmt( stmt->location );
657    const CodeLocation & loc = stmt->location;
658
659    /* generates:
660    switch (i) {
661        case 0:
662            try {
663                on_selected( target1, clause1 );
664                dotarget1stmt();
665            }
666            finally { clause_statuses[i] = __SELECT_RUN; unregister_select(target1, clause1); }
667            break;
668            ...
669        case N:
670            ...
671            break;
672    }*/
673    std::vector<ptr<CaseClause>> switchCases;
674    int idx = 0;
675    for ( const auto & clause: stmt->clauses ) {
676        const CodeLocation & cLoc = clause->location;
677        switchCases.push_back(
678            new CaseClause( cLoc,
679                ConstantExpr::from_int( cLoc, idx ),
680                {
681                    new CompoundStmt( cLoc,
682                        {
683                            new ast::TryStmt( cLoc,
684                                genStmtBlock( clause, clauseData.at(idx) ),
685                                {},
686                                new ast::FinallyClause( cLoc, 
687                                    new CompoundStmt( cLoc,
688                                        {
689                                            new ExprStmt( loc,
690                                                new UntypedExpr ( loc,
691                                                    new NameExpr( loc, "?=?" ),
692                                                    {
693                                                        new UntypedExpr ( loc, 
694                                                            new NameExpr( loc, "?[?]" ),
695                                                            {
696                                                                new NameExpr( loc, clauseData.at(0)->statusName ),
697                                                                new NameExpr( loc, idxName )
698                                                            }
699                                                        ),
700                                                        new NameExpr( loc, "__SELECT_RUN" )
701                                                    }
702                                                )
703                                            ),
704                                            new ExprStmt( loc, genSelectTraitCall( clause, clauseData.at(idx), "unregister_select" ) )
705                                        }
706                                    )
707                                )
708                            ),
709                            new BranchStmt( cLoc, BranchStmt::Kind::Break, Label( cLoc ) )
710                        }
711                    )
712                }
713            )
714        );
715        idx++;
716    }
717
718    ifBody->push_back(
719        new SwitchStmt( loc,
720            new NameExpr( loc, idxName ),
721            std::move( switchCases )
722        )
723    );
724
725    // gens:
726    // if (clause_statuses[i] == __SELECT_SAT) {
727    //      ... ifBody  ...
728    // }
729    IfStmt * ifSwitch = new IfStmt( loc,
730        new UntypedExpr ( loc,
731            new NameExpr( loc, "?==?" ),
732            {
733                new UntypedExpr ( loc, 
734                    new NameExpr( loc, "?[?]" ),
735                    {
736                        new NameExpr( loc, clauseData.at(0)->statusName ),
737                        new NameExpr( loc, idxName )
738                    }
739                ),
740                new NameExpr( loc, "__SELECT_SAT" )
741            }
742        ),      // condition
743        ifBody  // body
744    );
745
746    return new ForStmt( loc,
747        {
748            new DeclStmt( loc,
749                new ObjectDecl( loc,
750                    idxName,
751                    new BasicType( BasicType::Kind::SignedInt ),
752                    new SingleInit( loc, ConstantExpr::from_int( loc, 0 ) )
753                )
754            )
755        },  // inits
756        new UntypedExpr ( loc,
757            new NameExpr( loc, "?<?" ),
758            {
759                new NameExpr( loc, idxName ),
760                ConstantExpr::from_int( loc, stmt->clauses.size() )
761            }
762        ),  // cond
763        new UntypedExpr ( loc,
764            new NameExpr( loc, "?++" ),
765            { new NameExpr( loc, idxName ) }
766        ),  // inc
767        new CompoundStmt( loc,
768            {
769                new IfStmt( loc,
770                    new UntypedExpr ( loc,
771                        new NameExpr( loc, predName ),
772                        { new NameExpr( loc, clauseData.at(0)->statusName ) }
773                    ),
774                    new BranchStmt( loc, BranchStmt::Kind::Break, Label( loc ) )
775                ),
776                ifSwitch
777            }
778        )   // body
779    );
780}
781
782// Generates: !is_full_sat_n() / !is_run_sat_n()
783Expr * genNotSatExpr( const WaitUntilStmt * stmt, string & satName, string & arrName ) {
784    const CodeLocation & loc = stmt->location;
785    return new UntypedExpr ( loc,
786        new NameExpr( loc, "!?" ),
787        {
788            new UntypedExpr ( loc,
789                new NameExpr( loc, satName ),
790                { new NameExpr( loc, arrName ) }
791            )
792        }
793    );
794}
795
796// Generates the code needed for waituntils with an else ( ... )
797// Checks clauses once after registering for completion and runs them if completes
798// If not enough have run to satisfy predicate after one pass then the else is run
799Stmt * GenerateWaitUntilCore::genElseClauseBranch( const WaitUntilStmt * stmt, string & runName, string & arrName, vector<ClauseData *> & clauseData ) {
800    return new CompoundStmt( stmt->else_stmt->location,
801        {
802            genStatusCheckFor( stmt, clauseData, runName ),
803            new IfStmt( stmt->else_stmt->location,
804                genNotSatExpr( stmt, runName, arrName ),
805                ast::deepCopy( stmt->else_stmt )
806            )
807        }
808    );
809}
810
811Stmt * GenerateWaitUntilCore::genNoElseClauseBranch( const WaitUntilStmt * stmt, string & satName, string & runName, string & arrName, string & pCountName, vector<ClauseData *> & clauseData ) {
812    CompoundStmt * whileBody = new CompoundStmt( stmt->location );
813    const CodeLocation & loc = stmt->location;
814
815    // generates: __CFA_maybe_park( &park_counter );
816    whileBody->push_back(
817        new ExprStmt( loc,
818            new UntypedExpr ( loc,
819                new NameExpr( loc, "__CFA_maybe_park" ),
820                { new AddressExpr( loc, new NameExpr( loc, pCountName ) ) }
821            )
822        )
823    );
824
825    whileBody->push_back( genStatusCheckFor( stmt, clauseData, satName ) );
826
827    return new CompoundStmt( loc,
828        {
829            new WhileDoStmt( loc,
830                genNotSatExpr( stmt, satName, arrName ),
831                whileBody,  // body
832                {}          // no inits
833            ),
834            genStatusCheckFor( stmt, clauseData, runName )
835        }
836    );
837}
838
839// generates the following decls for each clause to ensure the target expr and when_cond is only evaluated once
840// typeof(target) & __clause_target_0 = target;
841// bool __when_cond_0 = when_cond; // only generated if when_cond defined
842// select_node clause1;
843void GenerateWaitUntilCore::genClauseInits( const WaitUntilStmt * stmt, vector<ClauseData *> & clauseData, CompoundStmt * body, string & statusName, string & elseWhenName ) {
844    ClauseData * currClause;
845    for ( vector<ClauseData*>::size_type i = 0; i < stmt->clauses.size(); i++ ) {
846        currClause = new ClauseData( i, statusName );
847        currClause->nodeName = namer_node.newName();
848        currClause->targetName = namer_target.newName();
849        currClause->whenName = namer_when.newName();
850        clauseData.push_back(currClause);
851        const CodeLocation & cLoc = stmt->clauses.at(i)->location;
852
853        // typeof(target) & __clause_target_0 = target;
854        body->push_back(
855            new DeclStmt( cLoc,
856                new ObjectDecl( cLoc,
857                    currClause->targetName,
858                    new ReferenceType( new TypeofType( ast::deepCopy( stmt->clauses.at(i)->target ) ) ),
859                    new SingleInit( cLoc, ast::deepCopy( stmt->clauses.at(i)->target ) )
860                )
861            )
862        );
863
864        // bool __when_cond_0 = when_cond; // only generated if when_cond defined
865        if ( stmt->clauses.at(i)->when_cond )
866            body->push_back(
867                new DeclStmt( cLoc,
868                    new ObjectDecl( cLoc,
869                        currClause->whenName,
870                        new BasicType( BasicType::Kind::Bool ),
871                        new SingleInit( cLoc, ast::deepCopy( stmt->clauses.at(i)->when_cond ) )
872                    )
873                )
874            );
875       
876        // select_node clause1;
877        body->push_back(
878            new DeclStmt( cLoc,
879                new ObjectDecl( cLoc,
880                    currClause->nodeName,
881                    new StructInstType( selectNodeDecl )
882                )
883            )
884        );
885    }
886
887    if ( stmt->else_stmt && stmt->else_cond ) {
888        body->push_back(
889            new DeclStmt( stmt->else_cond->location,
890                new ObjectDecl( stmt->else_cond->location,
891                    elseWhenName,
892                    new BasicType( BasicType::Kind::Bool ),
893                    new SingleInit( stmt->else_cond->location, ast::deepCopy( stmt->else_cond ) )
894                )
895            )
896        );
897    }
898}
899
900/*
901if ( clause_status == &clause1 ) ... clause 1 body ...
902...
903elif ( clause_status == &clausen ) ... clause n body ...
904*/
905Stmt * GenerateWaitUntilCore::buildOrCaseSwitch( const WaitUntilStmt * stmt, string & statusName, vector<ClauseData *> & data ) {
906    const CodeLocation & loc = stmt->location;
907
908    IfStmt * outerIf = nullptr;
909        IfStmt * lastIf = nullptr;
910
911        //adds an if/elif clause for each select clause address to run the corresponding clause stmt
912        for ( long unsigned int i = 0; i < data.size(); i++ ) {
913        const CodeLocation & cLoc = stmt->clauses.at(i)->location;
914
915                IfStmt * currIf = new IfStmt( cLoc,
916                        new UntypedExpr( cLoc, 
917                new NameExpr( cLoc, "?==?" ), 
918                {
919                    new NameExpr( cLoc, statusName ),
920                    new CastExpr( cLoc, 
921                        new AddressExpr( cLoc, new NameExpr( cLoc, data.at(i)->nodeName ) ),
922                        new BasicType( BasicType::Kind::LongUnsignedInt ), GeneratedFlag::ExplicitCast
923                    )
924                }
925            ),
926            genStmtBlock( stmt->clauses.at(i), data.at(i) )
927                );
928               
929                if ( i == 0 ) {
930                        outerIf = currIf;
931                } else {
932                        // add ifstmt to else of previous stmt
933                        lastIf->else_ = currIf;
934                }
935
936                lastIf = currIf;
937        }
938
939    return new CompoundStmt( loc,
940        {
941            new ExprStmt( loc, new UntypedExpr( loc, new NameExpr( loc, "park" ) ) ),
942            outerIf
943        }
944    );
945}
946
947Stmt * GenerateWaitUntilCore::recursiveOrIfGen( const WaitUntilStmt * stmt, vector<ClauseData *> & data, vector<ClauseData*>::size_type idx, string & elseWhenName ) {
948    if ( idx == data.size() ) {   // base case, gen last else
949        const CodeLocation & cLoc = stmt->else_stmt->location;
950        if ( !stmt->else_stmt ) // normal non-else gen
951            return buildOrCaseSwitch( stmt, data.at(0)->statusName, data );
952
953        Expr * raceFnCall = new UntypedExpr( stmt->location,
954            new NameExpr( stmt->location, "__select_node_else_race" ),
955            { new NameExpr( stmt->location, data.at(0)->nodeName ) }
956        );
957
958        if ( stmt->else_stmt && stmt->else_cond ) { // return else conditional on both when and race
959            return new IfStmt( cLoc,
960                new LogicalExpr( cLoc,
961                    new CastExpr( cLoc,
962                        new NameExpr( cLoc, elseWhenName ),
963                        new BasicType( BasicType::Kind::Bool ), GeneratedFlag::ExplicitCast
964                    ),
965                    new CastExpr( cLoc,
966                        raceFnCall,
967                        new BasicType( BasicType::Kind::Bool ), GeneratedFlag::ExplicitCast
968                    ),
969                    LogicalFlag::AndExpr
970                ),
971                ast::deepCopy( stmt->else_stmt ),
972                buildOrCaseSwitch( stmt, data.at(0)->statusName, data )
973            );
974        }
975
976        // return else conditional on race
977        return new IfStmt( stmt->else_stmt->location,
978            raceFnCall,
979            ast::deepCopy( stmt->else_stmt ),
980            buildOrCaseSwitch( stmt, data.at(0)->statusName, data )
981        );
982    }
983    const CodeLocation & cLoc = stmt->clauses.at(idx)->location;
984
985    Expr * baseCond = genSelectTraitCall( stmt->clauses.at(idx), data.at(idx), "register_select" );
986    Expr * ifCond;
987
988    // If we have a when_cond make the register call conditional on it
989    if ( stmt->clauses.at(idx)->when_cond ) {
990        ifCond = new LogicalExpr( cLoc,
991            new CastExpr( cLoc,
992                new NameExpr( cLoc, data.at(idx)->whenName ), 
993                new BasicType( BasicType::Kind::Bool ), GeneratedFlag::ExplicitCast
994            ),
995            new CastExpr( cLoc,
996                baseCond,
997                new BasicType( BasicType::Kind::Bool ), GeneratedFlag::ExplicitCast
998            ),
999            LogicalFlag::AndExpr
1000        );
1001    } else ifCond = baseCond;
1002
1003    return new CompoundStmt( cLoc,
1004        {   // gens: setup_clause( clause1, &status, 0p );
1005            new ExprStmt( cLoc,
1006                new UntypedExpr ( cLoc,
1007                    new NameExpr( cLoc, "setup_clause" ),
1008                    {
1009                        new NameExpr( cLoc, data.at(idx)->nodeName ),
1010                        new AddressExpr( cLoc, new NameExpr( cLoc, data.at(idx)->statusName ) ),
1011                        ConstantExpr::null( cLoc, new PointerType( new BasicType( BasicType::Kind::SignedInt ) ) )
1012                    }
1013                )
1014            ),
1015            // gens: if (__when_cond && register_select()) { clause body } else { ... recursiveOrIfGen ... }
1016            new IfStmt( cLoc,
1017                ifCond,
1018                genStmtBlock( stmt->clauses.at(idx), data.at(idx) ),
1019                recursiveOrIfGen( stmt, data, idx + 1, elseWhenName )
1020            )
1021        }
1022    );
1023}
1024
1025// This gens the special case of an all OR waituntil:
1026/*
1027int status = 0;
1028
1029typeof(target) & __clause_target_0 = target;
1030bool __when_cond_0 = when_cond; // only generated if when_cond defined
1031select_node clause1;
1032... generate above for rest of clauses ...
1033
1034try {
1035    setup_clause( clause1, &status, 0p );
1036    if ( __when_cond_0 && register_select( 1 ) ) {
1037        ... clause 1 body ...
1038    } else {
1039        ... recursively gen for each of n clauses ...
1040        setup_clause( clausen, &status, 0p );
1041        if ( __when_cond_n-1 && register_select( n ) ) {
1042            ... clause n body ...
1043        } else {
1044            if ( else_when ) ... else clause body ...
1045            else {
1046                park();
1047
1048                // after winning the race and before unpark() clause_status is set to be the winning clause index + 1
1049                if ( clause_status == &clause1) ... clause 1 body ...
1050                ...
1051                elif ( clause_status == &clausen ) ... clause n body ...
1052            }
1053        }
1054    }
1055}
1056finally {
1057    if ( __when_cond_1 && clause1.status != 0p) unregister_select( 1 ); // if registered unregister
1058    ...
1059    if ( __when_cond_n && clausen.status != 0p) unregister_select( n );
1060}
1061*/
1062Stmt * GenerateWaitUntilCore::genAllOr( const WaitUntilStmt * stmt ) {
1063    const CodeLocation & loc = stmt->location;
1064    string statusName = namer_status.newName();
1065    string elseWhenName = namer_when.newName();
1066    int numClauses = stmt->clauses.size();
1067    CompoundStmt * body = new CompoundStmt( stmt->location );
1068
1069    // Generates: unsigned long int status = 0;
1070    body->push_back( new DeclStmt( loc,
1071        new ObjectDecl( loc,
1072            statusName,
1073            new BasicType( BasicType::Kind::LongUnsignedInt ),
1074            new SingleInit( loc, ConstantExpr::from_int( loc, 0 ) )
1075        )
1076    ));
1077
1078    vector<ClauseData *> clauseData;
1079    genClauseInits( stmt, clauseData, body, statusName, elseWhenName );
1080
1081    vector<int> whenIndices; // track which clauses have whens
1082
1083    CompoundStmt * unregisters = new CompoundStmt( loc );
1084    Expr * ifCond;
1085    for ( int i = 0; i < numClauses; i++ ) {
1086        const CodeLocation & cLoc = stmt->clauses.at(i)->location;
1087        // Gens: node.status != 0p
1088        UntypedExpr * statusPtrCheck = new UntypedExpr( cLoc, 
1089            new NameExpr( cLoc, "?!=?" ), 
1090            {
1091                ConstantExpr::null( cLoc, new PointerType( new BasicType( BasicType::Kind::LongUnsignedInt ) ) ),
1092                new UntypedExpr( cLoc, 
1093                    new NameExpr( cLoc, "__get_clause_status" ), 
1094                    { new NameExpr( cLoc, clauseData.at(i)->nodeName ) } 
1095                ) 
1096            }
1097        );
1098
1099        // If we have a when_cond make the unregister call conditional on it
1100        if ( stmt->clauses.at(i)->when_cond ) {
1101            whenIndices.push_back(i);
1102            ifCond = new LogicalExpr( cLoc,
1103                new CastExpr( cLoc,
1104                    new NameExpr( cLoc, clauseData.at(i)->whenName ), 
1105                    new BasicType( BasicType::Kind::Bool ), GeneratedFlag::ExplicitCast
1106                ),
1107                new CastExpr( cLoc,
1108                    statusPtrCheck,
1109                    new BasicType( BasicType::Kind::Bool ), GeneratedFlag::ExplicitCast
1110                ),
1111                LogicalFlag::AndExpr
1112            );
1113        } else ifCond = statusPtrCheck;
1114       
1115        unregisters->push_back(
1116            new IfStmt( cLoc,
1117                ifCond,
1118                new ExprStmt( cLoc, genSelectTraitCall( stmt->clauses.at(i), clauseData.at(i), "unregister_select" ) ) 
1119            )
1120        );
1121    }
1122
1123    if ( whenIndices.empty() || whenIndices.size() != stmt->clauses.size() ) {
1124        body->push_back(
1125                new ast::TryStmt( loc,
1126                new CompoundStmt( loc, { recursiveOrIfGen( stmt, clauseData, 0, elseWhenName ) } ),
1127                {},
1128                new ast::FinallyClause( loc, unregisters )
1129            )
1130        );
1131    } else { // If all clauses have whens, we need to skip the waituntil if they are all false
1132        Expr * outerIfCond = new NameExpr( loc, clauseData.at( whenIndices.at(0) )->whenName );
1133        Expr * lastExpr = outerIfCond;
1134
1135        for ( vector<int>::size_type i = 1; i < whenIndices.size(); i++ ) {
1136            outerIfCond = new LogicalExpr( loc,
1137                new CastExpr( loc,
1138                    new NameExpr( loc, clauseData.at( whenIndices.at(i) )->whenName ), 
1139                    new BasicType( BasicType::Kind::Bool ), GeneratedFlag::ExplicitCast
1140                ),
1141                new CastExpr( loc,
1142                    lastExpr,
1143                    new BasicType( BasicType::Kind::Bool ), GeneratedFlag::ExplicitCast
1144                ),
1145                LogicalFlag::OrExpr
1146            );
1147            lastExpr = outerIfCond;
1148        }
1149
1150        body->push_back(
1151                new ast::TryStmt( loc,
1152                new CompoundStmt( loc, 
1153                    {
1154                        new IfStmt( loc,
1155                            outerIfCond,
1156                            recursiveOrIfGen( stmt, clauseData, 0, elseWhenName )
1157                        )
1158                    }
1159                ),
1160                {},
1161                new ast::FinallyClause( loc, unregisters )
1162            )
1163        );
1164    }
1165
1166    for ( ClauseData * datum : clauseData )
1167        delete datum;
1168
1169    return body;
1170}
1171
1172Stmt * GenerateWaitUntilCore::postvisit( const WaitUntilStmt * stmt ) {
1173    if ( !selectNodeDecl )
1174        SemanticError( stmt, "waituntil statement requires #include <waituntil.hfa>" );
1175
1176    // Prep clause tree to figure out how to set initial statuses
1177    // setTreeSizes( stmt->predicateTree );
1178    if ( paintWhenTree( stmt->predicateTree ) ) // if this returns true we can special case since tree is all OR's
1179        return genAllOr( stmt );
1180
1181    CompoundStmt * tryBody = new CompoundStmt( stmt->location );
1182    CompoundStmt * body = new CompoundStmt( stmt->location );
1183    string statusArrName = namer_status.newName();
1184    string pCountName = namer_park.newName();
1185    string satName = namer_sat.newName();
1186    string runName = namer_run.newName();
1187    string elseWhenName = namer_when.newName();
1188    int numClauses = stmt->clauses.size();
1189    addPredicates( stmt, satName, runName );
1190
1191    const CodeLocation & loc = stmt->location;
1192
1193    // Generates: int park_counter = 0;
1194    body->push_back( new DeclStmt( loc,
1195        new ObjectDecl( loc,
1196            pCountName,
1197            new BasicType( BasicType::Kind::SignedInt ),
1198            new SingleInit( loc, ConstantExpr::from_int( loc, 0 ) )
1199        )
1200    ));
1201
1202    // Generates: int clause_statuses[3] = { 0 };
1203    body->push_back( new DeclStmt( loc,
1204        new ObjectDecl( loc,
1205            statusArrName,
1206            new ArrayType( new BasicType( BasicType::Kind::LongUnsignedInt ), ConstantExpr::from_int( loc, numClauses ), LengthFlag::FixedLen, DimensionFlag::DynamicDim ),
1207            new ListInit( loc,
1208                {
1209                    new SingleInit( loc, ConstantExpr::from_int( loc, 0 ) )
1210                }
1211            )
1212        )
1213    ));
1214
1215    vector<ClauseData *> clauseData;
1216    genClauseInits( stmt, clauseData, body, statusArrName, elseWhenName );
1217
1218    vector<pair<int, WaitUntilStmt::ClauseNode *>> ambiguousClauses;       // list of ambiguous clauses
1219    vector<int> andWhenClauses;    // list of clauses that have an AND op as a direct parent and when_cond defined
1220
1221    collectWhens( stmt->predicateTree, ambiguousClauses, andWhenClauses );
1222
1223    // This is only needed for clauses that have AND as a parent and a when_cond defined
1224    // generates: if ( ! when_cond_0 ) clause_statuses_0 = __SELECT_RUN;
1225    for ( int idx : andWhenClauses ) {
1226        const CodeLocation & cLoc = stmt->clauses.at(idx)->location;
1227        body->push_back( 
1228            new IfStmt( cLoc,
1229                new UntypedExpr ( cLoc,
1230                    new NameExpr( cLoc, "!?" ),
1231                    { new NameExpr( cLoc, clauseData.at(idx)->whenName ) }
1232                ),  // IfStmt cond
1233                new ExprStmt( cLoc,
1234                    new UntypedExpr ( cLoc,
1235                        new NameExpr( cLoc, "?=?" ),
1236                        {
1237                            new UntypedExpr ( cLoc, 
1238                                new NameExpr( cLoc, "?[?]" ),
1239                                {
1240                                    new NameExpr( cLoc, statusArrName ),
1241                                    ConstantExpr::from_int( cLoc, idx )
1242                                }
1243                            ),
1244                            new NameExpr( cLoc, "__SELECT_RUN" )
1245                        }
1246                    )
1247                )  // IfStmt then
1248            )
1249        );
1250    }
1251
1252    // Only need to generate conditional initial state setting for ambiguous when clauses
1253    if ( !ambiguousClauses.empty() ) {
1254        body->push_back( genWhenStateConditions( stmt, clauseData, ambiguousClauses, 0 ) );
1255    }
1256
1257    // generates the following for each clause:
1258    // setup_clause( clause1, &clause_statuses[0], &park_counter );
1259    // register_select(A, clause1);
1260    for ( int i = 0; i < numClauses; i++ ) {
1261        setUpClause( stmt->clauses.at(i), clauseData.at(i), pCountName, tryBody );
1262    }
1263
1264    // generate satisfy logic based on if there is an else clause and if it is conditional
1265    if ( stmt->else_stmt && stmt->else_cond ) { // gen both else/non else branches
1266        tryBody->push_back(
1267            new IfStmt( stmt->else_cond->location,
1268                new NameExpr( stmt->else_cond->location, elseWhenName ),
1269                genElseClauseBranch( stmt, runName, statusArrName, clauseData ),
1270                genNoElseClauseBranch( stmt, satName, runName, statusArrName, pCountName, clauseData )
1271            )
1272        );
1273    } else if ( !stmt->else_stmt ) { // normal gen
1274        tryBody->push_back( genNoElseClauseBranch( stmt, satName, runName, statusArrName, pCountName, clauseData ) );
1275    } else { // generate just else
1276        tryBody->push_back( genElseClauseBranch( stmt, runName, statusArrName, clauseData ) );
1277    }
1278
1279    CompoundStmt * unregisters = new CompoundStmt( loc );
1280    // generates for each clause:
1281    // if ( !has_run( clause_statuses[i] ) )
1282    // OR if when_cond defined
1283    // if ( when_cond_i && !has_run( clause_statuses[i] ) )
1284    // body of if is:
1285    // { if (unregister_select(A, clause1) && on_selected(A, clause1)) clause1->stmt; } // this conditionally runs the block unregister_select returns true (needed by some primitives)
1286    Expr * ifCond;
1287    UntypedExpr * statusExpr; // !clause_statuses[i]
1288    for ( int i = 0; i < numClauses; i++ ) {
1289        const CodeLocation & cLoc = stmt->clauses.at(i)->location;
1290
1291        statusExpr = new UntypedExpr ( cLoc,
1292            new NameExpr( cLoc, "!?" ),
1293            {
1294                new UntypedExpr ( cLoc, 
1295                    new NameExpr( cLoc, "__CFA_has_clause_run" ),
1296                    {
1297                        genArrAccessExpr( cLoc, i, statusArrName )
1298                    }
1299                )
1300            }
1301        );
1302
1303        if ( stmt->clauses.at(i)->when_cond ) {
1304            // generates: if( when_cond_i && !has_run(clause_statuses[i]) )
1305            ifCond = new LogicalExpr( cLoc,
1306                new CastExpr( cLoc,
1307                    new NameExpr( cLoc, clauseData.at(i)->whenName ), 
1308                    new BasicType( BasicType::Kind::Bool ), GeneratedFlag::ExplicitCast
1309                ),
1310                new CastExpr( cLoc,
1311                    statusExpr,
1312                    new BasicType( BasicType::Kind::Bool ), GeneratedFlag::ExplicitCast
1313                ),
1314                LogicalFlag::AndExpr
1315            );
1316        } else // generates: if( !clause_statuses[i] )
1317            ifCond = statusExpr;
1318       
1319        unregisters->push_back( 
1320            new IfStmt( cLoc,
1321                ifCond,
1322                new CompoundStmt( cLoc,
1323                    {
1324                        new IfStmt( cLoc,
1325                            genSelectTraitCall( stmt->clauses.at(i), clauseData.at(i), "unregister_select" ),
1326                            // ast::deepCopy( stmt->clauses.at(i)->stmt )
1327                            genStmtBlock( stmt->clauses.at(i), clauseData.at(i) )
1328                        )
1329                    }
1330                )
1331               
1332            )
1333        );
1334    }
1335
1336    body->push_back( 
1337        new ast::TryStmt(
1338            loc,
1339            tryBody,
1340            {},
1341            new ast::FinallyClause( loc, unregisters )
1342        )
1343    );
1344
1345    for ( ClauseData * datum : clauseData )
1346        delete datum;
1347
1348    return body;
1349}
1350
1351// To add the predicates at global scope we need to do it in a second pass
1352// Predicates are added after "struct select_node { ... };"
1353class AddPredicateDecls final : public WithDeclsToAdd<> {
1354    vector<FunctionDecl *> & satFns;
1355    const StructDecl * selectNodeDecl = nullptr;
1356
1357  public:
1358    void previsit( const StructDecl * decl ) {
1359        if ( !decl->body ) {
1360            return;
1361        } else if ( "select_node" == decl->name ) {
1362            assert( !selectNodeDecl );
1363            selectNodeDecl = decl;
1364            for ( FunctionDecl * fn : satFns )
1365                declsToAddAfter.push_back(fn);           
1366        }
1367    }
1368    AddPredicateDecls( vector<FunctionDecl *> & satFns ): satFns(satFns) {}
1369};
1370
1371void generateWaitUntil( TranslationUnit & translationUnit ) {
1372    vector<FunctionDecl *> satFns;
1373        Pass<GenerateWaitUntilCore>::run( translationUnit, satFns );
1374    Pass<AddPredicateDecls>::run( translationUnit, satFns );
1375}
1376
1377} // namespace Concurrency
1378
1379// Local Variables: //
1380// tab-width: 4 //
1381// mode: c++ //
1382// compile-command: "make install" //
1383// End: //
Note: See TracBrowser for help on using the repository browser.