source: src/Concurrency/Waituntil.cpp @ 5e81a9c

Last change on this file since 5e81a9c was ed1a7ab8, checked in by caparsons <caparson@…>, 16 months ago

fixed two bugs with breaks in waituntils, required reordering of waituntil pass to set correct labels for breaks

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