source: src/Concurrency/Waituntil.cpp@ f3c02ea

stuck-waitfor-destruct
Last change on this file since f3c02ea was 9cb2742, checked in by caparsons <caparson@…>, 3 years ago

refactored some waituntil code gen to be more concise

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