source: src/Concurrency/Waituntil.cpp@ f5212ca

Last change on this file since f5212ca was 37273c8, checked in by Andrew Beach <ajbeach@…>, 22 months ago

Removed the old-ast-compatable FunctionDecl constructor. However, enough cases pass nothing polymorphic along some of the uses of the constructor now go to a new monomorphic function constructor.

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