source: src/Concurrency/Waituntil.cpp@ b28ce93

Last change on this file since b28ce93 was d923fca, checked in by Andrew Beach <ajbeach@…>, 7 months 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.