source: src/Concurrency/Waituntil.cpp @ 8705a11

Last change on this file since 8705a11 was d923fca, checked in by Andrew Beach <ajbeach@…>, 6 weeks ago

Clean-up the warnings of the concurrency tests. A lot of little test level fixes, the most interesting repeated one is some formally redundent fallthough statements. pthread_attr_test had to be rewritten because of library restrictions. Changed some types so they would always be pointer sized. There was a library change, there was a function that could not be implemented; I trust that it is included for a reason so I just put it in a comment. There is a change to the compiler, wait-until now uses goto. The labelled breaks were code generated as unlabelled breaks and although it worked out slipped through some checks. Finally, there is one warning that I cannot solve at this time so tests that produce it have been put in their own lax group.

  • Property mode set to 100644
File size: 50.0 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.hpp"
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        Expr * leftExpr, * rightExpr;
495        switch (currNode->op) {
496                case WaitUntilStmt::ClauseNode::AND:
497                        leftExpr = genPredExpr( loc, currNode->left, idx, genLeaf );
498                        rightExpr = genPredExpr( loc, currNode->right, idx, genLeaf );
499                        return new LogicalExpr( loc,
500                                new CastExpr( loc, leftExpr, new BasicType( BasicKind::Bool ), GeneratedFlag::ExplicitCast ),
501                                new CastExpr( loc, rightExpr, new BasicType( BasicKind::Bool ), GeneratedFlag::ExplicitCast ),
502                                LogicalFlag::AndExpr
503                        );
504                        break;
505                case WaitUntilStmt::ClauseNode::OR:
506                        leftExpr = genPredExpr( loc, currNode->left, idx, genLeaf );
507                        rightExpr = genPredExpr( loc, currNode->right, idx, genLeaf );
508                        return new LogicalExpr( loc,
509                                new CastExpr( loc, leftExpr, new BasicType( BasicKind::Bool ), GeneratedFlag::ExplicitCast ),
510                                new CastExpr( loc, rightExpr, new BasicType( BasicKind::Bool ), GeneratedFlag::ExplicitCast ),
511                                LogicalFlag::OrExpr );
512                        break;
513                case WaitUntilStmt::ClauseNode::LEAF:
514                        return genLeaf( loc, idx );
515                        break;
516                default:
517                        assertf(false, "Unreachable waituntil clause node type. How did you get here???");\
518                        return nullptr;
519                        break;
520        }
521        return nullptr;
522}
523
524
525// Builds the predicate functions used to check the status of the waituntil statement
526/* Ex:
527{
528        waituntil( A ){ doA(); }
529        or waituntil( B ){ doB(); }
530        and waituntil( C ) { doC(); }
531}
532generates =>
533static inline bool is_full_sat_1( int * clause_statuses ) {
534        return clause_statuses[0]
535                || clause_statuses[1]
536                && clause_statuses[2];
537}
538
539static inline bool is_done_sat_1( int * clause_statuses ) {
540        return has_run(clause_statuses[0])
541                || has_run(clause_statuses[1])
542                && has_run(clause_statuses[2]);
543}
544*/
545// Returns a predicate function decl
546// predName and genLeaf determine if this generates an is_done or an is_full predicate
547FunctionDecl * buildPredicate( const WaitUntilStmt * stmt, GenLeafExpr genLeaf, string & predName ) {
548        int arrIdx = 0;
549        const CodeLocation & loc = stmt->location;
550        CompoundStmt * body = new CompoundStmt( loc );
551        body->push_back( new ReturnStmt( loc, genPredExpr( loc,  stmt->predicateTree, arrIdx, genLeaf ) ) );
552
553        return new FunctionDecl( loc,
554                predName,
555                {
556                        new ObjectDecl( loc,
557                                "clause_statuses",
558                                new PointerType( new BasicType( BasicKind::LongUnsignedInt ) )
559                        )
560                },
561                {
562                        new ObjectDecl( loc,
563                                "sat_ret",
564                                new BasicType( BasicKind::Bool )
565                        )
566                },
567                body,               // body
568                { Storage::Static },    // storage
569                Linkage::Cforall,       // linkage
570                {},                     // attributes
571                { Function::Inline }
572        );
573}
574
575// Creates is_done and is_full predicates
576void GenerateWaitUntilCore::addPredicates( const WaitUntilStmt * stmt, string & satName, string & runName ) {
577        if ( !stmt->else_stmt || stmt->else_cond ) // don't need SAT predicate when else variation with no else_cond
578                satFns.push_back( Concurrency::buildPredicate( stmt, genSatExpr, satName ) );
579        satFns.push_back( Concurrency::buildPredicate( stmt, genRunExpr, runName ) );
580}
581
582// Adds the following to body:
583// if ( when_cond ) { // this if is omitted if no when() condition
584//      setup_clause( clause1, &clause_statuses[0], &park_counter );
585//      register_select(A, clause1);
586// }
587void GenerateWaitUntilCore::setUpClause( const WhenClause * clause, ClauseData * data, string & pCountName, CompoundStmt * body ) {
588        CompoundStmt * currBody = body;
589        const CodeLocation & loc = clause->location;
590
591        // If we have a when_cond make the initialization conditional
592        if ( clause->when_cond )
593                currBody = new CompoundStmt( loc );
594
595        // Generates: setup_clause( clause1, &clause_statuses[0], &park_counter );
596        currBody->push_back( new ExprStmt( loc,
597                new UntypedExpr ( loc,
598                        new NameExpr( loc, "setup_clause" ),
599                        {
600                                new NameExpr( loc, data->nodeName ),
601                                new AddressExpr( loc, genArrAccessExpr( loc, data->index, data->statusName ) ),
602                                new AddressExpr( loc, new NameExpr( loc, pCountName ) )
603                        }
604                )
605        ));
606
607        // Generates: register_select(A, clause1);
608        currBody->push_back( new ExprStmt( loc, genSelectTraitCall( clause, data, "register_select" ) ) );
609
610        // generates: if ( when_cond ) { ... currBody ... }
611        if ( clause->when_cond )
612                body->push_back(
613                        new IfStmt( loc,
614                                new NameExpr( loc, data->whenName ),
615                                currBody
616                        )
617                );
618}
619
620// Used to generate a call to one of the select trait routines
621Expr * GenerateWaitUntilCore::genSelectTraitCall( const WhenClause * clause, const ClauseData * data, string fnName ) {
622        const CodeLocation & loc = clause->location;
623        return new UntypedExpr ( loc,
624                new NameExpr( loc, fnName ),
625                {
626                        new NameExpr( loc, data->targetName ),
627                        new NameExpr( loc, data->nodeName )
628                }
629        );
630}
631
632// Generates:
633/* on_selected( target_1, node_1 ); ... corresponding body of target_1 ...
634*/
635CompoundStmt * GenerateWaitUntilCore::genStmtBlock( const WhenClause * clause, const ClauseData * data ) {
636        const CodeLocation & cLoc = clause->location;
637        return new CompoundStmt( cLoc,
638                {
639                        new IfStmt( cLoc,
640                                genSelectTraitCall( clause, data, "on_selected" ),
641                                ast::deepCopy( clause->stmt )
642                        )
643                }
644        );
645}
646
647// this routine generates and returns the following
648/*for ( int i = 0; i < numClauses; i++ ) {
649        if ( predName(clause_statuses) ) break;
650        if (clause_statuses[i] == __SELECT_SAT) {
651                switch (i) {
652                        case 0:
653                                try {
654                                    on_selected( target1, clause1 );
655                                    dotarget1stmt();
656                                }
657                                finally { clause_statuses[i] = __SELECT_RUN; unregister_select(target1, clause1); }
658                                break;
659                        ...
660                        case N:
661                                ...
662                                break;
663                }
664        }
665}*/
666CompoundStmt * GenerateWaitUntilCore::genStatusCheckFor( const WaitUntilStmt * stmt, vector<ClauseData *> & clauseData, string & predName ) {
667        CompoundStmt * ifBody = new CompoundStmt( stmt->location );
668        const CodeLocation & loc = stmt->location;
669
670        string switchLabel = namer_label.newName();
671
672        /* generates:
673        switch (i) {
674                case 0:
675                        try {
676                                on_selected( target1, clause1 );
677                                dotarget1stmt();
678                        }
679                        finally { clause_statuses[i] = __SELECT_RUN; unregister_select(target1, clause1); }
680                        break;
681                        ...
682                case N:
683                        ...
684                        break;
685        }*/
686        std::vector<ptr<CaseClause>> switchCases;
687        int idx = 0;
688        for ( const auto & clause: stmt->clauses ) {
689                const CodeLocation & cLoc = clause->location;
690                switchCases.push_back(
691                        new CaseClause( cLoc,
692                                ConstantExpr::from_int( cLoc, idx ),
693                                {
694                                    new CompoundStmt( cLoc,
695                                        {
696                                            new ast::TryStmt( cLoc,
697                                                genStmtBlock( clause, clauseData.at(idx) ),
698                                                {},
699                                                new ast::FinallyClause( cLoc,
700                                                    new CompoundStmt( cLoc,
701                                                        {
702                                                            new ExprStmt( loc,
703                                                                new UntypedExpr ( loc,
704                                                                    new NameExpr( loc, "?=?" ),
705                                                                    {
706                                                                        new UntypedExpr ( loc,
707                                                                            new NameExpr( loc, "?[?]" ),
708                                                                            {
709                                                                                new NameExpr( loc, clauseData.at(0)->statusName ),
710                                                                                new NameExpr( loc, idxName )
711                                                                            }
712                                                                        ),
713                                                                        new NameExpr( loc, "__SELECT_RUN" )
714                                                                    }
715                                                                )
716                                                            ),
717                                                            new ExprStmt( loc, genSelectTraitCall( clause, clauseData.at(idx), "unregister_select" ) )
718                                                        }
719                                                    )
720                                                )
721                                            ),
722                                            new BranchStmt( cLoc, BranchStmt::Goto, Label( cLoc, switchLabel ) )
723                                        }
724                                    )
725                                }
726                        )
727                );
728                idx++;
729        }
730
731        ifBody->push_back(
732                new SwitchStmt( loc,
733                        new NameExpr( loc, idxName ),
734                        std::move( switchCases ),
735                        {}
736                )
737        );
738
739        ifBody->push_back(
740                new NullStmt( loc,
741                        { Label( loc, switchLabel ) }
742                )
743        );
744
745        // gens:
746        // if (clause_statuses[i] == __SELECT_SAT) {
747        //      ... ifBody  ...
748        // }
749        IfStmt * ifSwitch = new IfStmt( loc,
750                new UntypedExpr ( loc,
751                        new NameExpr( loc, "?==?" ),
752                        {
753                                new UntypedExpr ( loc,
754                                    new NameExpr( loc, "?[?]" ),
755                                    {
756                                        new NameExpr( loc, clauseData.at(0)->statusName ),
757                                        new NameExpr( loc, idxName )
758                                    }
759                                ),
760                                new NameExpr( loc, "__SELECT_SAT" )
761                        }
762                ),      // condition
763                ifBody  // body
764        );
765
766        string forLabel = namer_label.newName();
767
768        // we hoist init here so that this pass can happen after hoistdecls pass
769        return new CompoundStmt( loc,
770                {
771                        new DeclStmt( loc,
772                                new ObjectDecl( loc,
773                                    idxName,
774                                    new BasicType( BasicKind::SignedInt ),
775                                    new SingleInit( loc, ConstantExpr::from_int( loc, 0 ) )
776                                )
777                        ),
778                        new ForStmt( loc,
779                                {},  // inits
780                                new UntypedExpr ( loc,
781                                    new NameExpr( loc, "?<?" ),
782                                    {
783                                        new NameExpr( loc, idxName ),
784                                        ConstantExpr::from_int( loc, stmt->clauses.size() )
785                                    }
786                                ),  // cond
787                                new UntypedExpr ( loc,
788                                    new NameExpr( loc, "?++" ),
789                                    { new NameExpr( loc, idxName ) }
790                                ),  // inc
791                                new CompoundStmt( loc,
792                                    {
793                                        new IfStmt( loc,
794                                            new UntypedExpr ( loc,
795                                                new NameExpr( loc, predName ),
796                                                { new NameExpr( loc, clauseData.at(0)->statusName ) }
797                                            ),
798                                            new BranchStmt( loc, BranchStmt::Goto, Label( loc, forLabel ) )
799                                        ),
800                                        ifSwitch
801                                    }
802                                ),   // body
803                                {}
804                        ),
805                        new NullStmt( loc,
806                                { Label( loc, forLabel ) }
807                        )
808                }
809        );
810}
811
812// Generates: !is_full_sat_n() / !is_run_sat_n()
813Expr * genNotSatExpr( const WaitUntilStmt * stmt, string & satName, string & arrName ) {
814        const CodeLocation & loc = stmt->location;
815        return new UntypedExpr ( loc,
816                new NameExpr( loc, "!?" ),
817                {
818                        new UntypedExpr ( loc,
819                                new NameExpr( loc, satName ),
820                                { new NameExpr( loc, arrName ) }
821                        )
822                }
823        );
824}
825
826// Generates the code needed for waituntils with an else ( ... )
827// Checks clauses once after registering for completion and runs them if completes
828// If not enough have run to satisfy predicate after one pass then the else is run
829Stmt * GenerateWaitUntilCore::genElseClauseBranch( const WaitUntilStmt * stmt, string & runName, string & arrName, vector<ClauseData *> & clauseData ) {
830        return new CompoundStmt( stmt->else_stmt->location,
831                {
832                        genStatusCheckFor( stmt, clauseData, runName ),
833                        new IfStmt( stmt->else_stmt->location,
834                                genNotSatExpr( stmt, runName, arrName ),
835                                ast::deepCopy( stmt->else_stmt )
836                        )
837                }
838        );
839}
840
841Stmt * GenerateWaitUntilCore::genNoElseClauseBranch( const WaitUntilStmt * stmt, string & runName, string & arrName, string & pCountName, vector<ClauseData *> & clauseData ) {
842        CompoundStmt * whileBody = new CompoundStmt( stmt->location );
843        const CodeLocation & loc = stmt->location;
844
845        // generates: __CFA_maybe_park( &park_counter );
846        whileBody->push_back(
847                new ExprStmt( loc,
848                        new UntypedExpr ( loc,
849                                new NameExpr( loc, "__CFA_maybe_park" ),
850                                { new AddressExpr( loc, new NameExpr( loc, pCountName ) ) }
851                        )
852                )
853        );
854
855        whileBody->push_back( genStatusCheckFor( stmt, clauseData, runName ) );
856
857        return new CompoundStmt( loc,
858                {
859                        new WhileDoStmt( loc,
860                                genNotSatExpr( stmt, runName, arrName ),
861                                whileBody,  // body
862                                {}          // no inits
863                        )
864                }
865        );
866}
867
868// generates the following decls for each clause to ensure the target expr and when_cond is only evaluated once
869// typeof(target) & __clause_target_0 = target;
870// bool __when_cond_0 = when_cond; // only generated if when_cond defined
871// select_node clause1;
872void GenerateWaitUntilCore::genClauseInits( const WaitUntilStmt * stmt, vector<ClauseData *> & clauseData, CompoundStmt * body, string & statusName, string & elseWhenName ) {
873        ClauseData * currClause;
874        for ( vector<ClauseData*>::size_type i = 0; i < stmt->clauses.size(); i++ ) {
875                currClause = new ClauseData( i, statusName );
876                currClause->nodeName = namer_node.newName();
877                currClause->targetName = namer_target.newName();
878                currClause->whenName = namer_when.newName();
879                clauseData.push_back(currClause);
880                const CodeLocation & cLoc = stmt->clauses.at(i)->location;
881
882                // typeof(target) & __clause_target_0 = target;
883                body->push_back(
884                        new DeclStmt( cLoc,
885                                new ObjectDecl( cLoc,
886                                    currClause->targetName,
887                                    new ReferenceType(
888                                        new TypeofType( new UntypedExpr( cLoc,
889                                            new NameExpr( cLoc, "__CFA_select_get_type" ),
890                                            { ast::deepCopy( stmt->clauses.at(i)->target ) }
891                                        ))
892                                    ),
893                                    new SingleInit( cLoc, ast::deepCopy( stmt->clauses.at(i)->target ) )
894                                )
895                        )
896                );
897
898                // bool __when_cond_0 = when_cond; // only generated if when_cond defined
899                if ( stmt->clauses.at(i)->when_cond )
900                        body->push_back(
901                                new DeclStmt( cLoc,
902                                    new ObjectDecl( cLoc,
903                                        currClause->whenName,
904                                        new BasicType( BasicKind::Bool ),
905                                        new SingleInit( cLoc, ast::deepCopy( stmt->clauses.at(i)->when_cond ) )
906                                    )
907                                )
908                        );
909
910                // select_node clause1;
911                body->push_back(
912                        new DeclStmt( cLoc,
913                                new ObjectDecl( cLoc,
914                                    currClause->nodeName,
915                                    new StructInstType( selectNodeDecl )
916                                )
917                        )
918                );
919        }
920
921        if ( stmt->else_stmt && stmt->else_cond ) {
922                body->push_back(
923                        new DeclStmt( stmt->else_cond->location,
924                                new ObjectDecl( stmt->else_cond->location,
925                                    elseWhenName,
926                                    new BasicType( BasicKind::Bool ),
927                                    new SingleInit( stmt->else_cond->location, ast::deepCopy( stmt->else_cond ) )
928                                )
929                        )
930                );
931        }
932}
933
934/*
935if ( clause_status == &clause1 ) ... clause 1 body ...
936...
937elif ( clause_status == &clausen ) ... clause n body ...
938*/
939Stmt * GenerateWaitUntilCore::buildOrCaseSwitch( const WaitUntilStmt * stmt, string & statusName, vector<ClauseData *> & data ) {
940        const CodeLocation & loc = stmt->location;
941
942        IfStmt * outerIf = nullptr;
943        IfStmt * lastIf = nullptr;
944
945        //adds an if/elif clause for each select clause address to run the corresponding clause stmt
946        for ( long unsigned int i = 0; i < data.size(); i++ ) {
947                const CodeLocation & cLoc = stmt->clauses.at(i)->location;
948
949                IfStmt * currIf = new IfStmt( cLoc,
950                        new UntypedExpr( cLoc,
951                                new NameExpr( cLoc, "?==?" ),
952                                {
953                                    new NameExpr( cLoc, statusName ),
954                                    new CastExpr( cLoc,
955                                        new AddressExpr( cLoc, new NameExpr( cLoc, data.at(i)->nodeName ) ),
956                                        new BasicType( BasicKind::LongUnsignedInt ), GeneratedFlag::ExplicitCast
957                                    )
958                                }
959                        ),
960                        genStmtBlock( stmt->clauses.at(i), data.at(i) )
961                );
962
963                if ( i == 0 ) {
964                        outerIf = currIf;
965                } else {
966                        // add ifstmt to else of previous stmt
967                        lastIf->else_ = currIf;
968                }
969
970                lastIf = currIf;
971        }
972
973        return new CompoundStmt( loc,
974                {
975                        new ExprStmt( loc, new UntypedExpr( loc, new NameExpr( loc, "park" ) ) ),
976                        outerIf
977                }
978        );
979}
980
981Stmt * GenerateWaitUntilCore::recursiveOrIfGen( const WaitUntilStmt * stmt, vector<ClauseData *> & data, vector<ClauseData*>::size_type idx, string & elseWhenName ) {
982        if ( idx == data.size() ) {   // base case, gen last else
983                const CodeLocation & cLoc = stmt->else_stmt->location;
984                if ( !stmt->else_stmt ) // normal non-else gen
985                        return buildOrCaseSwitch( stmt, data.at(0)->statusName, data );
986
987                Expr * raceFnCall = new UntypedExpr( stmt->location,
988                        new NameExpr( stmt->location, "__select_node_else_race" ),
989                        { new NameExpr( stmt->location, data.at(0)->nodeName ) }
990                );
991
992                if ( stmt->else_stmt && stmt->else_cond ) { // return else conditional on both when and race
993                        return new IfStmt( cLoc,
994                                new LogicalExpr( cLoc,
995                                    new CastExpr( cLoc,
996                                        new NameExpr( cLoc, elseWhenName ),
997                                        new BasicType( BasicKind::Bool ), GeneratedFlag::ExplicitCast
998                                    ),
999                                    new CastExpr( cLoc,
1000                                        raceFnCall,
1001                                        new BasicType( BasicKind::Bool ), GeneratedFlag::ExplicitCast
1002                                    ),
1003                                    LogicalFlag::AndExpr
1004                                ),
1005                                ast::deepCopy( stmt->else_stmt ),
1006                                buildOrCaseSwitch( stmt, data.at(0)->statusName, data )
1007                        );
1008                }
1009
1010                // return else conditional on race
1011                return new IfStmt( stmt->else_stmt->location,
1012                        raceFnCall,
1013                        ast::deepCopy( stmt->else_stmt ),
1014                        buildOrCaseSwitch( stmt, data.at(0)->statusName, data )
1015                );
1016        }
1017        const CodeLocation & cLoc = stmt->clauses.at(idx)->location;
1018
1019        Expr * baseCond = genSelectTraitCall( stmt->clauses.at(idx), data.at(idx), "register_select" );
1020        Expr * ifCond;
1021
1022        // If we have a when_cond make the register call conditional on it
1023        if ( stmt->clauses.at(idx)->when_cond ) {
1024                ifCond = new LogicalExpr( cLoc,
1025                        new CastExpr( cLoc,
1026                                new NameExpr( cLoc, data.at(idx)->whenName ),
1027                                new BasicType( BasicKind::Bool ), GeneratedFlag::ExplicitCast
1028                        ),
1029                        new CastExpr( cLoc,
1030                                baseCond,
1031                                new BasicType( BasicKind::Bool ), GeneratedFlag::ExplicitCast
1032                        ),
1033                        LogicalFlag::AndExpr
1034                );
1035        } else ifCond = baseCond;
1036
1037        return new CompoundStmt( cLoc,
1038                {   // gens: setup_clause( clause1, &status, 0p );
1039                        new ExprStmt( cLoc,
1040                                new UntypedExpr ( cLoc,
1041                                    new NameExpr( cLoc, "setup_clause" ),
1042                                    {
1043                                        new NameExpr( cLoc, data.at(idx)->nodeName ),
1044                                        new AddressExpr( cLoc, new NameExpr( cLoc, data.at(idx)->statusName ) ),
1045                                        ConstantExpr::null( cLoc, new PointerType( new BasicType( BasicKind::SignedInt ) ) )
1046                                    }
1047                                )
1048                        ),
1049                        // gens: if (__when_cond && register_select()) { clause body } else { ... recursiveOrIfGen ... }
1050                        new IfStmt( cLoc,
1051                                ifCond,
1052                                genStmtBlock( stmt->clauses.at(idx), data.at(idx) ),
1053                                recursiveOrIfGen( stmt, data, idx + 1, elseWhenName )
1054                        )
1055                }
1056        );
1057}
1058
1059// This gens the special case of an all OR waituntil:
1060/*
1061int status = 0;
1062
1063typeof(target) & __clause_target_0 = target;
1064bool __when_cond_0 = when_cond; // only generated if when_cond defined
1065select_node clause1;
1066... generate above for rest of clauses ...
1067
1068try {
1069        setup_clause( clause1, &status, 0p );
1070        if ( __when_cond_0 && register_select( 1 ) ) {
1071                ... clause 1 body ...
1072        } else {
1073                ... recursively gen for each of n clauses ...
1074                setup_clause( clausen, &status, 0p );
1075                if ( __when_cond_n-1 && register_select( n ) ) {
1076                        ... clause n body ...
1077                } else {
1078                        if ( else_when ) ... else clause body ...
1079                        else {
1080                                park();
1081
1082                                // after winning the race and before unpark() clause_status is set to be the winning clause index + 1
1083                                if ( clause_status == &clause1) ... clause 1 body ...
1084                                ...
1085                                elif ( clause_status == &clausen ) ... clause n body ...
1086                        }
1087                }
1088        }
1089}
1090finally {
1091        if ( __when_cond_1 && clause1.status != 0p) unregister_select( 1 ); // if registered unregister
1092        ...
1093        if ( __when_cond_n && clausen.status != 0p) unregister_select( n );
1094}
1095*/
1096Stmt * GenerateWaitUntilCore::genAllOr( const WaitUntilStmt * stmt ) {
1097        const CodeLocation & loc = stmt->location;
1098        string statusName = namer_status.newName();
1099        string elseWhenName = namer_when.newName();
1100        int numClauses = stmt->clauses.size();
1101        CompoundStmt * body = new CompoundStmt( stmt->location );
1102
1103        // Generates: unsigned long int status = 0;
1104        body->push_back( new DeclStmt( loc,
1105                new ObjectDecl( loc,
1106                        statusName,
1107                        new BasicType( BasicKind::LongUnsignedInt ),
1108                        new SingleInit( loc, ConstantExpr::from_int( loc, 0 ) )
1109                )
1110        ));
1111
1112        vector<ClauseData *> clauseData;
1113        genClauseInits( stmt, clauseData, body, statusName, elseWhenName );
1114
1115        vector<int> whenIndices; // track which clauses have whens
1116
1117        CompoundStmt * unregisters = new CompoundStmt( loc );
1118        Expr * ifCond;
1119        for ( int i = 0; i < numClauses; i++ ) {
1120                const CodeLocation & cLoc = stmt->clauses.at(i)->location;
1121                // Gens: node.status != 0p
1122                UntypedExpr * statusPtrCheck = new UntypedExpr( cLoc,
1123                        new NameExpr( cLoc, "?!=?" ),
1124                        {
1125                                ConstantExpr::null( cLoc, new PointerType( new BasicType( BasicKind::LongUnsignedInt ) ) ),
1126                                new UntypedExpr( cLoc,
1127                                    new NameExpr( cLoc, "__get_clause_status" ),
1128                                    { new NameExpr( cLoc, clauseData.at(i)->nodeName ) }
1129                                )
1130                        }
1131                );
1132
1133                // If we have a when_cond make the unregister call conditional on it
1134                if ( stmt->clauses.at(i)->when_cond ) {
1135                        whenIndices.push_back(i);
1136                        ifCond = new LogicalExpr( cLoc,
1137                                new CastExpr( cLoc,
1138                                    new NameExpr( cLoc, clauseData.at(i)->whenName ),
1139                                    new BasicType( BasicKind::Bool ), GeneratedFlag::ExplicitCast
1140                                ),
1141                                new CastExpr( cLoc,
1142                                    statusPtrCheck,
1143                                    new BasicType( BasicKind::Bool ), GeneratedFlag::ExplicitCast
1144                                ),
1145                                LogicalFlag::AndExpr
1146                        );
1147                } else ifCond = statusPtrCheck;
1148
1149                unregisters->push_back(
1150                        new IfStmt( cLoc,
1151                                ifCond,
1152                                new ExprStmt( cLoc, genSelectTraitCall( stmt->clauses.at(i), clauseData.at(i), "unregister_select" ) )
1153                        )
1154                );
1155        }
1156
1157        if ( whenIndices.empty() || whenIndices.size() != stmt->clauses.size() ) {
1158                body->push_back(
1159                                new ast::TryStmt( loc,
1160                                new CompoundStmt( loc, { recursiveOrIfGen( stmt, clauseData, 0, elseWhenName ) } ),
1161                                {},
1162                                new ast::FinallyClause( loc, unregisters )
1163                        )
1164                );
1165        } else { // If all clauses have whens, we need to skip the waituntil if they are all false
1166                Expr * outerIfCond = new NameExpr( loc, clauseData.at( whenIndices.at(0) )->whenName );
1167                Expr * lastExpr = outerIfCond;
1168
1169                for ( vector<int>::size_type i = 1; i < whenIndices.size(); i++ ) {
1170                        outerIfCond = new LogicalExpr( loc,
1171                                new CastExpr( loc,
1172                                    new NameExpr( loc, clauseData.at( whenIndices.at(i) )->whenName ),
1173                                    new BasicType( BasicKind::Bool ), GeneratedFlag::ExplicitCast
1174                                ),
1175                                new CastExpr( loc,
1176                                    lastExpr,
1177                                    new BasicType( BasicKind::Bool ), GeneratedFlag::ExplicitCast
1178                                ),
1179                                LogicalFlag::OrExpr
1180                        );
1181                        lastExpr = outerIfCond;
1182                }
1183
1184                body->push_back(
1185                                new ast::TryStmt( loc,
1186                                new CompoundStmt( loc,
1187                                    {
1188                                        new IfStmt( loc,
1189                                            outerIfCond,
1190                                            recursiveOrIfGen( stmt, clauseData, 0, elseWhenName )
1191                                        )
1192                                    }
1193                                ),
1194                                {},
1195                                new ast::FinallyClause( loc, unregisters )
1196                        )
1197                );
1198        }
1199
1200        for ( ClauseData * datum : clauseData )
1201                delete datum;
1202
1203        return body;
1204}
1205
1206Stmt * GenerateWaitUntilCore::postvisit( const WaitUntilStmt * stmt ) {
1207        if ( !selectNodeDecl )
1208                SemanticError( stmt, "waituntil statement requires #include <waituntil.hfa>" );
1209
1210        // Prep clause tree to figure out how to set initial statuses
1211        // setTreeSizes( stmt->predicateTree );
1212        if ( paintWhenTree( stmt->predicateTree ) ) // if this returns true we can special case since tree is all OR's
1213                return genAllOr( stmt );
1214
1215        CompoundStmt * tryBody = new CompoundStmt( stmt->location );
1216        CompoundStmt * body = new CompoundStmt( stmt->location );
1217        string statusArrName = namer_status.newName();
1218        string pCountName = namer_park.newName();
1219        string satName = namer_sat.newName();
1220        string runName = namer_run.newName();
1221        string elseWhenName = namer_when.newName();
1222        int numClauses = stmt->clauses.size();
1223        addPredicates( stmt, satName, runName );
1224
1225        const CodeLocation & loc = stmt->location;
1226
1227        // Generates: int park_counter = 0;
1228        body->push_back( new DeclStmt( loc,
1229                new ObjectDecl( loc,
1230                        pCountName,
1231                        new BasicType( BasicKind::SignedInt ),
1232                        new SingleInit( loc, ConstantExpr::from_int( loc, 0 ) )
1233                )
1234        ));
1235
1236        // Generates: int clause_statuses[3] = { 0 };
1237        body->push_back( new DeclStmt( loc,
1238                new ObjectDecl( loc,
1239                        statusArrName,
1240                        new ArrayType( new BasicType( BasicKind::LongUnsignedInt ), ConstantExpr::from_int( loc, numClauses ), LengthFlag::FixedLen, DimensionFlag::DynamicDim ),
1241                        new ListInit( loc,
1242                                {
1243                                    new SingleInit( loc, ConstantExpr::from_int( loc, 0 ) )
1244                                }
1245                        )
1246                )
1247        ));
1248
1249        vector<ClauseData *> clauseData;
1250        genClauseInits( stmt, clauseData, body, statusArrName, elseWhenName );
1251
1252        vector<pair<int, WaitUntilStmt::ClauseNode *>> ambiguousClauses;       // list of ambiguous clauses
1253        vector<int> andWhenClauses;    // list of clauses that have an AND op as a direct parent and when_cond defined
1254
1255        collectWhens( stmt->predicateTree, ambiguousClauses, andWhenClauses );
1256
1257        // This is only needed for clauses that have AND as a parent and a when_cond defined
1258        // generates: if ( ! when_cond_0 ) clause_statuses_0 = __SELECT_RUN;
1259        for ( int idx : andWhenClauses ) {
1260                const CodeLocation & cLoc = stmt->clauses.at(idx)->location;
1261                body->push_back(
1262                        new IfStmt( cLoc,
1263                                new UntypedExpr ( cLoc,
1264                                    new NameExpr( cLoc, "!?" ),
1265                                    { new NameExpr( cLoc, clauseData.at(idx)->whenName ) }
1266                                ),  // IfStmt cond
1267                                new ExprStmt( cLoc,
1268                                    new UntypedExpr ( cLoc,
1269                                        new NameExpr( cLoc, "?=?" ),
1270                                        {
1271                                            new UntypedExpr ( cLoc,
1272                                                new NameExpr( cLoc, "?[?]" ),
1273                                                {
1274                                                    new NameExpr( cLoc, statusArrName ),
1275                                                    ConstantExpr::from_int( cLoc, idx )
1276                                                }
1277                                            ),
1278                                            new NameExpr( cLoc, "__SELECT_RUN" )
1279                                        }
1280                                    )
1281                                )  // IfStmt then
1282                        )
1283                );
1284        }
1285
1286        // Only need to generate conditional initial state setting for ambiguous when clauses
1287        if ( !ambiguousClauses.empty() ) {
1288                body->push_back( genWhenStateConditions( stmt, clauseData, ambiguousClauses, 0 ) );
1289        }
1290
1291        // generates the following for each clause:
1292        // setup_clause( clause1, &clause_statuses[0], &park_counter );
1293        // register_select(A, clause1);
1294        for ( int i = 0; i < numClauses; i++ ) {
1295                setUpClause( stmt->clauses.at(i), clauseData.at(i), pCountName, tryBody );
1296        }
1297
1298        // generate satisfy logic based on if there is an else clause and if it is conditional
1299        if ( stmt->else_stmt && stmt->else_cond ) { // gen both else/non else branches
1300                tryBody->push_back(
1301                        new IfStmt( stmt->else_cond->location,
1302                                new NameExpr( stmt->else_cond->location, elseWhenName ),
1303                                genElseClauseBranch( stmt, runName, statusArrName, clauseData ),
1304                                genNoElseClauseBranch( stmt, runName, statusArrName, pCountName, clauseData )
1305                        )
1306                );
1307        } else if ( !stmt->else_stmt ) { // normal gen
1308                tryBody->push_back( genNoElseClauseBranch( stmt, runName, statusArrName, pCountName, clauseData ) );
1309        } else { // generate just else
1310                tryBody->push_back( genElseClauseBranch( stmt, runName, statusArrName, clauseData ) );
1311        }
1312
1313        // Collection of unregister calls on resources to be put in finally clause
1314        // for each clause:
1315        // if ( !__CFA_has_clause_run( clause_statuses[i] )) && unregister_select( ... , clausei ) ) { ... clausei stmt ... }
1316        // OR if when( ... ) defined on resource
1317        // if ( when_cond_i && (!__CFA_has_clause_run( clause_statuses[i] )) && unregister_select( ... , clausei ) ) { ... clausei stmt ... }
1318        CompoundStmt * unregisters = new CompoundStmt( loc );
1319
1320        Expr * statusExpr; // !__CFA_has_clause_run( clause_statuses[i] )
1321        for ( int i = 0; i < numClauses; i++ ) {
1322                const CodeLocation & cLoc = stmt->clauses.at(i)->location;
1323
1324                // Generates: !__CFA_has_clause_run( clause_statuses[i] )
1325                statusExpr = new UntypedExpr ( cLoc,
1326                        new NameExpr( cLoc, "!?" ),
1327                        {
1328                                new UntypedExpr ( cLoc,
1329                                    new NameExpr( cLoc, "__CFA_has_clause_run" ),
1330                                    {
1331                                        genArrAccessExpr( cLoc, i, statusArrName )
1332                                    }
1333                                )
1334                        }
1335                );
1336
1337                // Generates:
1338                // (!__CFA_has_clause_run( clause_statuses[i] )) && unregister_select( ... , clausei );
1339                statusExpr = new LogicalExpr( cLoc,
1340                        new CastExpr( cLoc,
1341                                statusExpr,
1342                                new BasicType( BasicKind::Bool ), GeneratedFlag::ExplicitCast
1343                        ),
1344                        new CastExpr( cLoc,
1345                                genSelectTraitCall( stmt->clauses.at(i), clauseData.at(i), "unregister_select" ),
1346                                new BasicType( BasicKind::Bool ), GeneratedFlag::ExplicitCast
1347                        ),
1348                        LogicalFlag::AndExpr
1349                );
1350
1351                // if when cond defined generates:
1352                // when_cond_i && (!__CFA_has_clause_run( clause_statuses[i] )) && unregister_select( ... , clausei );
1353                if ( stmt->clauses.at(i)->when_cond )
1354                        statusExpr = new LogicalExpr( cLoc,
1355                                new CastExpr( cLoc,
1356                                    new NameExpr( cLoc, clauseData.at(i)->whenName ),
1357                                    new BasicType( BasicKind::Bool ), GeneratedFlag::ExplicitCast
1358                                ),
1359                                new CastExpr( cLoc,
1360                                    statusExpr,
1361                                    new BasicType( BasicKind::Bool ), GeneratedFlag::ExplicitCast
1362                                ),
1363                                LogicalFlag::AndExpr
1364                        );
1365
1366                // generates:
1367                // if ( statusExpr ) { ... clausei stmt ... }
1368                unregisters->push_back(
1369                        new IfStmt( cLoc,
1370                                statusExpr,
1371                                new CompoundStmt( cLoc,
1372                                    {
1373                                        new IfStmt( cLoc,
1374                                            genSelectTraitCall( stmt->clauses.at(i), clauseData.at(i), "on_selected" ),
1375                                            ast::deepCopy( stmt->clauses.at(i)->stmt )
1376                                        )
1377                                    }
1378                                )
1379                        )
1380                );
1381
1382                // // generates:
1383                // // if ( statusExpr ) { ... clausei stmt ... }
1384                // unregisters->push_back(
1385                //     new IfStmt( cLoc,
1386                //         statusExpr,
1387                //         genStmtBlock( stmt->clauses.at(i), clauseData.at(i) )
1388                //     )
1389                // );
1390        }
1391
1392        body->push_back(
1393                new ast::TryStmt(
1394                        loc,
1395                        tryBody,
1396                        {},
1397                        new ast::FinallyClause( loc, unregisters )
1398                )
1399        );
1400
1401        for ( ClauseData * datum : clauseData )
1402                delete datum;
1403
1404        return body;
1405}
1406
1407// To add the predicates at global scope we need to do it in a second pass
1408// Predicates are added after "struct select_node { ... };"
1409class AddPredicateDecls final : public WithDeclsToAdd {
1410        vector<FunctionDecl *> & satFns;
1411        const StructDecl * selectNodeDecl = nullptr;
1412
1413  public:
1414        void previsit( const StructDecl * decl ) {
1415                if ( !decl->body ) {
1416                        return;
1417                } else if ( "select_node" == decl->name ) {
1418                        assert( !selectNodeDecl );
1419                        selectNodeDecl = decl;
1420                        for ( FunctionDecl * fn : satFns )
1421                                declsToAddAfter.push_back(fn);
1422                }
1423        }
1424        AddPredicateDecls( vector<FunctionDecl *> & satFns ): satFns(satFns) {}
1425};
1426
1427void generateWaitUntil( TranslationUnit & translationUnit ) {
1428        vector<FunctionDecl *> satFns;
1429        Pass<GenerateWaitUntilCore>::run( translationUnit, satFns );
1430        Pass<AddPredicateDecls>::run( translationUnit, satFns );
1431}
1432
1433} // namespace Concurrency
1434
1435// Local Variables: //
1436// tab-width: 4 //
1437// mode: c++ //
1438// compile-command: "make install" //
1439// End: //
Note: See TracBrowser for help on using the repository browser.