source: src/Concurrency/Waituntil.cpp@ 97453ce

Last change on this file since 97453ce was fc0996a, checked in by caparsons <caparson@…>, 2 years ago

refactores to account for removal of ret val from on_selected

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