source: src/Concurrency/Waituntil.cpp @ cf34e82

Last change on this file since cf34e82 was 1d66a91, checked in by caparsons <caparson@…>, 16 months ago

added support for general channel operators and cleaned up some cruft

  • Property mode set to 100644
File size: 57.9 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 & 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 IfStmt( cLoc,
630                genSelectTraitCall( clause, data, "on_selected" ),
631                ast::deepCopy( clause->stmt )
632            )
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 & 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, runName ) );
837
838    return new CompoundStmt( loc,
839        {
840            new WhileDoStmt( loc,
841                genNotSatExpr( stmt, runName, arrName ),
842                whileBody,  // body
843                {}          // no inits
844            )
845        }
846    );
847}
848
849// generates the following decls for each clause to ensure the target expr and when_cond is only evaluated once
850// typeof(target) & __clause_target_0 = target;
851// bool __when_cond_0 = when_cond; // only generated if when_cond defined
852// select_node clause1;
853void GenerateWaitUntilCore::genClauseInits( const WaitUntilStmt * stmt, vector<ClauseData *> & clauseData, CompoundStmt * body, string & statusName, string & elseWhenName ) {
854    ClauseData * currClause;
855    for ( vector<ClauseData*>::size_type i = 0; i < stmt->clauses.size(); i++ ) {
856        currClause = new ClauseData( i, statusName );
857        currClause->nodeName = namer_node.newName();
858        currClause->targetName = namer_target.newName();
859        currClause->whenName = namer_when.newName();
860        clauseData.push_back(currClause);
861        const CodeLocation & cLoc = stmt->clauses.at(i)->location;
862
863        // typeof(target) & __clause_target_0 = target;
864        body->push_back(
865            new DeclStmt( cLoc,
866                new ObjectDecl( cLoc,
867                    currClause->targetName,
868                    new ReferenceType( 
869                        new TypeofType( new UntypedExpr( cLoc,
870                            new NameExpr( cLoc, "__CFA_select_get_type" ),
871                            { ast::deepCopy( stmt->clauses.at(i)->target ) }
872                        ))
873                    ),
874                    new SingleInit( cLoc, ast::deepCopy( stmt->clauses.at(i)->target ) )
875                )
876            )
877        );
878
879        // bool __when_cond_0 = when_cond; // only generated if when_cond defined
880        if ( stmt->clauses.at(i)->when_cond )
881            body->push_back(
882                new DeclStmt( cLoc,
883                    new ObjectDecl( cLoc,
884                        currClause->whenName,
885                        new BasicType( BasicType::Kind::Bool ),
886                        new SingleInit( cLoc, ast::deepCopy( stmt->clauses.at(i)->when_cond ) )
887                    )
888                )
889            );
890       
891        // select_node clause1;
892        body->push_back(
893            new DeclStmt( cLoc,
894                new ObjectDecl( cLoc,
895                    currClause->nodeName,
896                    new StructInstType( selectNodeDecl )
897                )
898            )
899        );
900    }
901
902    if ( stmt->else_stmt && stmt->else_cond ) {
903        body->push_back(
904            new DeclStmt( stmt->else_cond->location,
905                new ObjectDecl( stmt->else_cond->location,
906                    elseWhenName,
907                    new BasicType( BasicType::Kind::Bool ),
908                    new SingleInit( stmt->else_cond->location, ast::deepCopy( stmt->else_cond ) )
909                )
910            )
911        );
912    }
913}
914
915/*
916if ( clause_status == &clause1 ) ... clause 1 body ...
917...
918elif ( clause_status == &clausen ) ... clause n body ...
919*/
920Stmt * GenerateWaitUntilCore::buildOrCaseSwitch( const WaitUntilStmt * stmt, string & statusName, vector<ClauseData *> & data ) {
921    const CodeLocation & loc = stmt->location;
922
923    IfStmt * outerIf = nullptr;
924        IfStmt * lastIf = nullptr;
925
926        //adds an if/elif clause for each select clause address to run the corresponding clause stmt
927        for ( long unsigned int i = 0; i < data.size(); i++ ) {
928        const CodeLocation & cLoc = stmt->clauses.at(i)->location;
929
930                IfStmt * currIf = new IfStmt( cLoc,
931                        new UntypedExpr( cLoc, 
932                new NameExpr( cLoc, "?==?" ), 
933                {
934                    new NameExpr( cLoc, statusName ),
935                    new CastExpr( cLoc, 
936                        new AddressExpr( cLoc, new NameExpr( cLoc, data.at(i)->nodeName ) ),
937                        new BasicType( BasicType::Kind::LongUnsignedInt ), GeneratedFlag::ExplicitCast
938                    )
939                }
940            ),
941            genStmtBlock( stmt->clauses.at(i), data.at(i) )
942                );
943               
944                if ( i == 0 ) {
945                        outerIf = currIf;
946                } else {
947                        // add ifstmt to else of previous stmt
948                        lastIf->else_ = currIf;
949                }
950
951                lastIf = currIf;
952        }
953
954    return new CompoundStmt( loc,
955        {
956            new ExprStmt( loc, new UntypedExpr( loc, new NameExpr( loc, "park" ) ) ),
957            outerIf
958        }
959    );
960}
961
962Stmt * GenerateWaitUntilCore::recursiveOrIfGen( const WaitUntilStmt * stmt, vector<ClauseData *> & data, vector<ClauseData*>::size_type idx, string & elseWhenName ) {
963    if ( idx == data.size() ) {   // base case, gen last else
964        const CodeLocation & cLoc = stmt->else_stmt->location;
965        if ( !stmt->else_stmt ) // normal non-else gen
966            return buildOrCaseSwitch( stmt, data.at(0)->statusName, data );
967
968        Expr * raceFnCall = new UntypedExpr( stmt->location,
969            new NameExpr( stmt->location, "__select_node_else_race" ),
970            { new NameExpr( stmt->location, data.at(0)->nodeName ) }
971        );
972
973        if ( stmt->else_stmt && stmt->else_cond ) { // return else conditional on both when and race
974            return new IfStmt( cLoc,
975                new LogicalExpr( cLoc,
976                    new CastExpr( cLoc,
977                        new NameExpr( cLoc, elseWhenName ),
978                        new BasicType( BasicType::Kind::Bool ), GeneratedFlag::ExplicitCast
979                    ),
980                    new CastExpr( cLoc,
981                        raceFnCall,
982                        new BasicType( BasicType::Kind::Bool ), GeneratedFlag::ExplicitCast
983                    ),
984                    LogicalFlag::AndExpr
985                ),
986                ast::deepCopy( stmt->else_stmt ),
987                buildOrCaseSwitch( stmt, data.at(0)->statusName, data )
988            );
989        }
990
991        // return else conditional on race
992        return new IfStmt( stmt->else_stmt->location,
993            raceFnCall,
994            ast::deepCopy( stmt->else_stmt ),
995            buildOrCaseSwitch( stmt, data.at(0)->statusName, data )
996        );
997    }
998    const CodeLocation & cLoc = stmt->clauses.at(idx)->location;
999
1000    Expr * baseCond = genSelectTraitCall( stmt->clauses.at(idx), data.at(idx), "register_select" );
1001    Expr * ifCond;
1002
1003    // If we have a when_cond make the register call conditional on it
1004    if ( stmt->clauses.at(idx)->when_cond ) {
1005        ifCond = new LogicalExpr( cLoc,
1006            new CastExpr( cLoc,
1007                new NameExpr( cLoc, data.at(idx)->whenName ), 
1008                new BasicType( BasicType::Kind::Bool ), GeneratedFlag::ExplicitCast
1009            ),
1010            new CastExpr( cLoc,
1011                baseCond,
1012                new BasicType( BasicType::Kind::Bool ), GeneratedFlag::ExplicitCast
1013            ),
1014            LogicalFlag::AndExpr
1015        );
1016    } else ifCond = baseCond;
1017
1018    return new CompoundStmt( cLoc,
1019        {   // gens: setup_clause( clause1, &status, 0p );
1020            new ExprStmt( cLoc,
1021                new UntypedExpr ( cLoc,
1022                    new NameExpr( cLoc, "setup_clause" ),
1023                    {
1024                        new NameExpr( cLoc, data.at(idx)->nodeName ),
1025                        new AddressExpr( cLoc, new NameExpr( cLoc, data.at(idx)->statusName ) ),
1026                        ConstantExpr::null( cLoc, new PointerType( new BasicType( BasicType::Kind::SignedInt ) ) )
1027                    }
1028                )
1029            ),
1030            // gens: if (__when_cond && register_select()) { clause body } else { ... recursiveOrIfGen ... }
1031            new IfStmt( cLoc,
1032                ifCond,
1033                genStmtBlock( stmt->clauses.at(idx), data.at(idx) ),
1034                recursiveOrIfGen( stmt, data, idx + 1, elseWhenName )
1035            )
1036        }
1037    );
1038}
1039
1040// This gens the special case of an all OR waituntil:
1041/*
1042int status = 0;
1043
1044typeof(target) & __clause_target_0 = target;
1045bool __when_cond_0 = when_cond; // only generated if when_cond defined
1046select_node clause1;
1047... generate above for rest of clauses ...
1048
1049try {
1050    setup_clause( clause1, &status, 0p );
1051    if ( __when_cond_0 && register_select( 1 ) ) {
1052        ... clause 1 body ...
1053    } else {
1054        ... recursively gen for each of n clauses ...
1055        setup_clause( clausen, &status, 0p );
1056        if ( __when_cond_n-1 && register_select( n ) ) {
1057            ... clause n body ...
1058        } else {
1059            if ( else_when ) ... else clause body ...
1060            else {
1061                park();
1062
1063                // after winning the race and before unpark() clause_status is set to be the winning clause index + 1
1064                if ( clause_status == &clause1) ... clause 1 body ...
1065                ...
1066                elif ( clause_status == &clausen ) ... clause n body ...
1067            }
1068        }
1069    }
1070}
1071finally {
1072    if ( __when_cond_1 && clause1.status != 0p) unregister_select( 1 ); // if registered unregister
1073    ...
1074    if ( __when_cond_n && clausen.status != 0p) unregister_select( n );
1075}
1076*/
1077Stmt * GenerateWaitUntilCore::genAllOr( const WaitUntilStmt * stmt ) {
1078    const CodeLocation & loc = stmt->location;
1079    string statusName = namer_status.newName();
1080    string elseWhenName = namer_when.newName();
1081    int numClauses = stmt->clauses.size();
1082    CompoundStmt * body = new CompoundStmt( stmt->location );
1083
1084    // Generates: unsigned long int status = 0;
1085    body->push_back( new DeclStmt( loc,
1086        new ObjectDecl( loc,
1087            statusName,
1088            new BasicType( BasicType::Kind::LongUnsignedInt ),
1089            new SingleInit( loc, ConstantExpr::from_int( loc, 0 ) )
1090        )
1091    ));
1092
1093    vector<ClauseData *> clauseData;
1094    genClauseInits( stmt, clauseData, body, statusName, elseWhenName );
1095
1096    vector<int> whenIndices; // track which clauses have whens
1097
1098    CompoundStmt * unregisters = new CompoundStmt( loc );
1099    Expr * ifCond;
1100    for ( int i = 0; i < numClauses; i++ ) {
1101        const CodeLocation & cLoc = stmt->clauses.at(i)->location;
1102        // Gens: node.status != 0p
1103        UntypedExpr * statusPtrCheck = new UntypedExpr( cLoc, 
1104            new NameExpr( cLoc, "?!=?" ), 
1105            {
1106                ConstantExpr::null( cLoc, new PointerType( new BasicType( BasicType::Kind::LongUnsignedInt ) ) ),
1107                new UntypedExpr( cLoc, 
1108                    new NameExpr( cLoc, "__get_clause_status" ), 
1109                    { new NameExpr( cLoc, clauseData.at(i)->nodeName ) } 
1110                ) 
1111            }
1112        );
1113
1114        // If we have a when_cond make the unregister call conditional on it
1115        if ( stmt->clauses.at(i)->when_cond ) {
1116            whenIndices.push_back(i);
1117            ifCond = new LogicalExpr( cLoc,
1118                new CastExpr( cLoc,
1119                    new NameExpr( cLoc, clauseData.at(i)->whenName ), 
1120                    new BasicType( BasicType::Kind::Bool ), GeneratedFlag::ExplicitCast
1121                ),
1122                new CastExpr( cLoc,
1123                    statusPtrCheck,
1124                    new BasicType( BasicType::Kind::Bool ), GeneratedFlag::ExplicitCast
1125                ),
1126                LogicalFlag::AndExpr
1127            );
1128        } else ifCond = statusPtrCheck;
1129       
1130        unregisters->push_back(
1131            new IfStmt( cLoc,
1132                ifCond,
1133                new ExprStmt( cLoc, genSelectTraitCall( stmt->clauses.at(i), clauseData.at(i), "unregister_select" ) ) 
1134            )
1135        );
1136    }
1137
1138    if ( whenIndices.empty() || whenIndices.size() != stmt->clauses.size() ) {
1139        body->push_back(
1140                new ast::TryStmt( loc,
1141                new CompoundStmt( loc, { recursiveOrIfGen( stmt, clauseData, 0, elseWhenName ) } ),
1142                {},
1143                new ast::FinallyClause( loc, unregisters )
1144            )
1145        );
1146    } else { // If all clauses have whens, we need to skip the waituntil if they are all false
1147        Expr * outerIfCond = new NameExpr( loc, clauseData.at( whenIndices.at(0) )->whenName );
1148        Expr * lastExpr = outerIfCond;
1149
1150        for ( vector<int>::size_type i = 1; i < whenIndices.size(); i++ ) {
1151            outerIfCond = new LogicalExpr( loc,
1152                new CastExpr( loc,
1153                    new NameExpr( loc, clauseData.at( whenIndices.at(i) )->whenName ), 
1154                    new BasicType( BasicType::Kind::Bool ), GeneratedFlag::ExplicitCast
1155                ),
1156                new CastExpr( loc,
1157                    lastExpr,
1158                    new BasicType( BasicType::Kind::Bool ), GeneratedFlag::ExplicitCast
1159                ),
1160                LogicalFlag::OrExpr
1161            );
1162            lastExpr = outerIfCond;
1163        }
1164
1165        body->push_back(
1166                new ast::TryStmt( loc,
1167                new CompoundStmt( loc, 
1168                    {
1169                        new IfStmt( loc,
1170                            outerIfCond,
1171                            recursiveOrIfGen( stmt, clauseData, 0, elseWhenName )
1172                        )
1173                    }
1174                ),
1175                {},
1176                new ast::FinallyClause( loc, unregisters )
1177            )
1178        );
1179    }
1180
1181    for ( ClauseData * datum : clauseData )
1182        delete datum;
1183
1184    return body;
1185}
1186
1187Stmt * GenerateWaitUntilCore::postvisit( const WaitUntilStmt * stmt ) {
1188    if ( !selectNodeDecl )
1189        SemanticError( stmt, "waituntil statement requires #include <waituntil.hfa>" );
1190
1191    // Prep clause tree to figure out how to set initial statuses
1192    // setTreeSizes( stmt->predicateTree );
1193    if ( paintWhenTree( stmt->predicateTree ) ) // if this returns true we can special case since tree is all OR's
1194        return genAllOr( stmt );
1195
1196    CompoundStmt * tryBody = new CompoundStmt( stmt->location );
1197    CompoundStmt * body = new CompoundStmt( stmt->location );
1198    string statusArrName = namer_status.newName();
1199    string pCountName = namer_park.newName();
1200    string satName = namer_sat.newName();
1201    string runName = namer_run.newName();
1202    string elseWhenName = namer_when.newName();
1203    int numClauses = stmt->clauses.size();
1204    addPredicates( stmt, satName, runName );
1205
1206    const CodeLocation & loc = stmt->location;
1207
1208    // Generates: int park_counter = 0;
1209    body->push_back( new DeclStmt( loc,
1210        new ObjectDecl( loc,
1211            pCountName,
1212            new BasicType( BasicType::Kind::SignedInt ),
1213            new SingleInit( loc, ConstantExpr::from_int( loc, 0 ) )
1214        )
1215    ));
1216
1217    // Generates: int clause_statuses[3] = { 0 };
1218    body->push_back( new DeclStmt( loc,
1219        new ObjectDecl( loc,
1220            statusArrName,
1221            new ArrayType( new BasicType( BasicType::Kind::LongUnsignedInt ), ConstantExpr::from_int( loc, numClauses ), LengthFlag::FixedLen, DimensionFlag::DynamicDim ),
1222            new ListInit( loc,
1223                {
1224                    new SingleInit( loc, ConstantExpr::from_int( loc, 0 ) )
1225                }
1226            )
1227        )
1228    ));
1229
1230    vector<ClauseData *> clauseData;
1231    genClauseInits( stmt, clauseData, body, statusArrName, elseWhenName );
1232
1233    vector<pair<int, WaitUntilStmt::ClauseNode *>> ambiguousClauses;       // list of ambiguous clauses
1234    vector<int> andWhenClauses;    // list of clauses that have an AND op as a direct parent and when_cond defined
1235
1236    collectWhens( stmt->predicateTree, ambiguousClauses, andWhenClauses );
1237
1238    // This is only needed for clauses that have AND as a parent and a when_cond defined
1239    // generates: if ( ! when_cond_0 ) clause_statuses_0 = __SELECT_RUN;
1240    for ( int idx : andWhenClauses ) {
1241        const CodeLocation & cLoc = stmt->clauses.at(idx)->location;
1242        body->push_back( 
1243            new IfStmt( cLoc,
1244                new UntypedExpr ( cLoc,
1245                    new NameExpr( cLoc, "!?" ),
1246                    { new NameExpr( cLoc, clauseData.at(idx)->whenName ) }
1247                ),  // IfStmt cond
1248                new ExprStmt( cLoc,
1249                    new UntypedExpr ( cLoc,
1250                        new NameExpr( cLoc, "?=?" ),
1251                        {
1252                            new UntypedExpr ( cLoc, 
1253                                new NameExpr( cLoc, "?[?]" ),
1254                                {
1255                                    new NameExpr( cLoc, statusArrName ),
1256                                    ConstantExpr::from_int( cLoc, idx )
1257                                }
1258                            ),
1259                            new NameExpr( cLoc, "__SELECT_RUN" )
1260                        }
1261                    )
1262                )  // IfStmt then
1263            )
1264        );
1265    }
1266
1267    // Only need to generate conditional initial state setting for ambiguous when clauses
1268    if ( !ambiguousClauses.empty() ) {
1269        body->push_back( genWhenStateConditions( stmt, clauseData, ambiguousClauses, 0 ) );
1270    }
1271
1272    // generates the following for each clause:
1273    // setup_clause( clause1, &clause_statuses[0], &park_counter );
1274    // register_select(A, clause1);
1275    for ( int i = 0; i < numClauses; i++ ) {
1276        setUpClause( stmt->clauses.at(i), clauseData.at(i), pCountName, tryBody );
1277    }
1278
1279    // generate satisfy logic based on if there is an else clause and if it is conditional
1280    if ( stmt->else_stmt && stmt->else_cond ) { // gen both else/non else branches
1281        tryBody->push_back(
1282            new IfStmt( stmt->else_cond->location,
1283                new NameExpr( stmt->else_cond->location, elseWhenName ),
1284                genElseClauseBranch( stmt, runName, statusArrName, clauseData ),
1285                genNoElseClauseBranch( stmt, runName, statusArrName, pCountName, clauseData )
1286            )
1287        );
1288    } else if ( !stmt->else_stmt ) { // normal gen
1289        tryBody->push_back( genNoElseClauseBranch( stmt, runName, statusArrName, pCountName, clauseData ) );
1290    } else { // generate just else
1291        tryBody->push_back( genElseClauseBranch( stmt, runName, statusArrName, clauseData ) );
1292    }
1293
1294    // Collection of unregister calls on resources to be put in finally clause
1295    // for each clause:
1296    // if ( !__CFA_has_clause_run( clause_statuses[i] )) && unregister_select( ... , clausei ) ) { ... clausei stmt ... }
1297    // OR if when( ... ) defined on resource
1298    // if ( when_cond_i && (!__CFA_has_clause_run( clause_statuses[i] )) && unregister_select( ... , clausei ) ) { ... clausei stmt ... }
1299    CompoundStmt * unregisters = new CompoundStmt( loc );
1300
1301    Expr * statusExpr; // !__CFA_has_clause_run( clause_statuses[i] )
1302    for ( int i = 0; i < numClauses; i++ ) {
1303        const CodeLocation & cLoc = stmt->clauses.at(i)->location;
1304
1305        // Generates: !__CFA_has_clause_run( clause_statuses[i] )
1306        statusExpr = new UntypedExpr ( cLoc,
1307            new NameExpr( cLoc, "!?" ),
1308            {
1309                new UntypedExpr ( cLoc, 
1310                    new NameExpr( cLoc, "__CFA_has_clause_run" ),
1311                    {
1312                        genArrAccessExpr( cLoc, i, statusArrName )
1313                    }
1314                )
1315            }
1316        );
1317       
1318        // Generates:
1319        // (!__CFA_has_clause_run( clause_statuses[i] )) && unregister_select( ... , clausei );
1320        statusExpr = new LogicalExpr( cLoc,
1321            new CastExpr( cLoc,
1322                statusExpr, 
1323                new BasicType( BasicType::Kind::Bool ), GeneratedFlag::ExplicitCast
1324            ),
1325            new CastExpr( cLoc,
1326                genSelectTraitCall( stmt->clauses.at(i), clauseData.at(i), "unregister_select" ),
1327                new BasicType( BasicType::Kind::Bool ), GeneratedFlag::ExplicitCast
1328            ),
1329            LogicalFlag::AndExpr
1330        );
1331       
1332        // if when cond defined generates:
1333        // when_cond_i && (!__CFA_has_clause_run( clause_statuses[i] )) && unregister_select( ... , clausei );
1334        if ( stmt->clauses.at(i)->when_cond )
1335            statusExpr = new LogicalExpr( cLoc,
1336                new CastExpr( cLoc,
1337                    new NameExpr( cLoc, clauseData.at(i)->whenName ), 
1338                    new BasicType( BasicType::Kind::Bool ), GeneratedFlag::ExplicitCast
1339                ),
1340                new CastExpr( cLoc,
1341                    statusExpr,
1342                    new BasicType( BasicType::Kind::Bool ), GeneratedFlag::ExplicitCast
1343                ),
1344                LogicalFlag::AndExpr
1345            );
1346
1347        // generates:
1348        // if ( statusExpr ) { ... clausei stmt ... }
1349        unregisters->push_back( 
1350            new IfStmt( cLoc,
1351                statusExpr,
1352                new CompoundStmt( cLoc,
1353                    {
1354                        new IfStmt( cLoc,
1355                            genSelectTraitCall( stmt->clauses.at(i), clauseData.at(i), "on_selected" ),
1356                            ast::deepCopy( stmt->clauses.at(i)->stmt )
1357                        )
1358                    }
1359                )
1360            )
1361        );
1362
1363        // // generates:
1364        // // if ( statusExpr ) { ... clausei stmt ... }
1365        // unregisters->push_back(
1366        //     new IfStmt( cLoc,
1367        //         statusExpr,
1368        //         genStmtBlock( stmt->clauses.at(i), clauseData.at(i) )
1369        //     )
1370        // );
1371    }
1372
1373    body->push_back( 
1374        new ast::TryStmt(
1375            loc,
1376            tryBody,
1377            {},
1378            new ast::FinallyClause( loc, unregisters )
1379        )
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.