source: src/Concurrency/Waitfor.cpp@ 44940ee

stuck-waitfor-destruct
Last change on this file since 44940ee was 44940ee, checked in by Matthew Au-Yeung <mw2auyeu@…>, 38 hours ago

Revert "Add a generated hash to fix stuck waitfor comparing static inline mutex destructors"

This reverts commit a30fceb1a73c4ef2bbee39a2b5406da881f51111.

  • Property mode set to 100644
File size: 15.9 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// Waitfor.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 "Waitfor.hpp"
17
18#include <string>
19
20#include "AST/Pass.hpp"
21#include "Common/UniqueName.hpp"
22#include "InitTweak/InitTweak.hpp"
23#include "ResolvExpr/Resolver.hpp"
24
25#include "AST/Print.hpp"
26
27using namespace std::string_literals;
28using ResolvExpr::ResolveContext;
29
30/* So this is what this file dones:
31
32void f(int i, float f, A & mutex b, struct foo * );
33void f(int );
34
35...{
36 when ( a < 1 ) waitfor( f : a ) { fee(); }
37 or timeout( getWaitTime() ) { fy(); }
38 or waitfor( g : a ) { foe(); }
39 or waitfor( ^?{} : a ) { break; }
40 or waitfor( ^?{} ) { break; }
41 or when ( a < 1 ) else { fum(); }
42}...
43
44 ||
45 ||
46 \||/
47 \/
48
49...{
50 {
51 __acceptable_t __acceptables_#[4 <num-clauses>];
52 bool __do_run_# = false;
53
54 monitor$ * __monitors_#[1 <num-monitors>] = { a };
55 if ( a < 1) {
56 void (*__function_#)() = <casts> f;
57 __acceptables_#[0].is_dtor = false;
58 __acceptables_#[0].func = __function_#;
59 __acceptables_#[0].data = __monitors_#;
60 __acceptables_#[0].size = 1;
61 __do_run_# = true;
62 }
63
64 // Remaining waitfor clauses go here.
65
66 long long unsigned int __timeout_# = -1;
67 if ( true ) {
68 __timeout_# = getWaitTime();
69 __do_run_# = true;
70 }
71
72 if ( a < 1 ) {
73 __timeout_# = 0
74 __do_run_# = true;
75 }
76
77 short int __index_# = -1;
78 __waitfor_mask_t __mask_# = {&__index_#, {__acceptables_#, ?}};
79 __waitfor_internal((__waitfor_mask_t&)__mask_#, __timeout_#);
80
81 switch (__index_#) {
82 case 0:
83 { { fee(); } break; }
84 case 1:
85 { { foe(); } break; }
86 case 2:
87 { <modified-break> break; }
88 case 3:
89 { <modified-break> break; }
90 case -2:
91 { { fy(); } break; }
92 case -1:
93 { { foe(); } break; }
94 }
95 }
96}...
97*/
98
99namespace Concurrency {
100
101namespace {
102
103class GenerateWaitForCore final :
104 public ast::WithSymbolTable, public ast::WithConstTranslationUnit {
105 const ast::FunctionDecl * decl_waitfor = nullptr;
106 const ast::StructDecl * decl_mask = nullptr;
107 const ast::StructDecl * decl_acceptable = nullptr;
108 const ast::StructDecl * decl_monitor = nullptr;
109
110 UniqueName namer_acc = "__acceptables_"s;
111 UniqueName namer_idx = "__index_"s;
112 UniqueName namer_flg = "__do_run_"s;
113 UniqueName namer_msk = "__mask_"s;
114 UniqueName namer_mon = "__monitors_"s;
115 UniqueName namer_tim = "__timeout_"s;
116 UniqueName namer_fun = "__function_"s;
117
118 ast::ObjectDecl * declareAcceptables( ast::CompoundStmt * out,
119 const CodeLocation & location, unsigned long numClauses );
120 ast::ObjectDecl * declareFlag(
121 ast::CompoundStmt * out, const CodeLocation & location );
122 ast::ExprStmt * makeSetter(
123 const CodeLocation & location, ast::ObjectDecl * flag );
124 ast::ObjectDecl * declMonitors(
125 ast::CompoundStmt * out, const ast::WaitForClause * clause );
126 void init_clause( ast::CompoundStmt * out, ast::ObjectDecl * acceptables,
127 int index, const ast::WaitForClause * clause, ast::Stmt * setter );
128 ast::Expr * init_timeout(
129 ast::CompoundStmt * out, const CodeLocation & topLocation,
130 const ast::Expr * timeout_time, const ast::Expr * timeout_cond,
131 const ast::Stmt * else_stmt, const ast::Expr * else_cond,
132 const ast::Stmt * setter );
133 ast::Expr * call(
134 ast::CompoundStmt * out, const CodeLocation & location,
135 size_t numClauses, ast::ObjectDecl * acceptables,
136 ast::Expr * timeout );
137public:
138 void previsit( const ast::FunctionDecl * decl );
139 void previsit( const ast::StructDecl * decl );
140 ast::Stmt * postvisit( const ast::WaitForStmt * stmt );
141};
142
143ast::Expr * makeOpIndex( const CodeLocation & location,
144 const ast::DeclWithType * array, unsigned long index ) {
145 return new ast::UntypedExpr( location,
146 new ast::NameExpr( location, "?[?]" ),
147 {
148 new ast::VariableExpr( location, array ),
149 ast::ConstantExpr::from_ulong( location, index ),
150 }
151 );
152}
153
154ast::Expr * makeOpAssign( const CodeLocation & location,
155 const ast::Expr * lhs, const ast::Expr * rhs ) {
156 return new ast::UntypedExpr( location,
157 new ast::NameExpr( location, "?=?" ),
158 { lhs, rhs }
159 );
160}
161
162ast::Expr * makeOpMember( const CodeLocation & location,
163 const std::string & mem, const ast::Expr * sue ) {
164 return new ast::UntypedMemberExpr( location,
165 new ast::NameExpr( location, mem ),
166 sue
167 );
168}
169
170ast::Stmt * makeAccStmt(
171 const CodeLocation & location, ast::DeclWithType * object,
172 unsigned long index, const std::string & member,
173 const ast::Expr * value, const ResolveContext & context
174) {
175 ast::Expr * expr = makeOpAssign( location,
176 makeOpMember( location,
177 member,
178 makeOpIndex( location,
179 object,
180 index
181 )
182 ),
183 value
184 );
185
186 auto result = ResolvExpr::findVoidExpression( expr, context );
187 return new ast::ExprStmt( location, result.get() );
188}
189
190const ast::Stmt * maybeCond( const CodeLocation & location,
191 const ast::Expr * cond, std::list<ast::ptr<ast::Stmt>> && stmts ) {
192 ast::Stmt * block = new ast::CompoundStmt( location, std::move( stmts ) );
193 return (cond) ? new ast::IfStmt( location, cond, block ) : block;
194}
195
196const ast::VariableExpr * extractVariable( const ast::Expr * func ) {
197 if ( auto var = dynamic_cast<const ast::VariableExpr *>( func ) ) {
198 return var;
199 }
200 auto cast = strict_dynamic_cast<const ast::CastExpr *>( func );
201 return cast->arg.strict_as<ast::VariableExpr>();
202}
203
204const ast::Expr * detectIsDtor(
205 const CodeLocation & location, const ast::Expr * func ) {
206 const ast::VariableExpr * typed_func = extractVariable( func );
207 bool is_dtor = InitTweak::isDestructor(
208 typed_func->var.strict_as<ast::FunctionDecl>() );
209 return ast::ConstantExpr::from_bool( location, is_dtor );
210}
211
212ast::ObjectDecl * GenerateWaitForCore::declareAcceptables(
213 ast::CompoundStmt * out,
214 const CodeLocation & location, unsigned long numClauses ) {
215 ast::ObjectDecl * acceptables = new ast::ObjectDecl( location,
216 namer_acc.newName(),
217 new ast::ArrayType(
218 new ast::StructInstType( decl_acceptable ),
219 ast::ConstantExpr::from_ulong( location, numClauses ),
220 ast::FixedLen,
221 ast::DynamicDim
222 )
223 );
224 out->push_back( new ast::DeclStmt( location, acceptables ) );
225
226 ast::Expr * set = new ast::UntypedExpr( location,
227 new ast::NameExpr( location, "__builtin_memset" ),
228 {
229 new ast::VariableExpr( location, acceptables ),
230 ast::ConstantExpr::from_int( location, 0 ),
231 new ast::SizeofExpr( location,
232 new ast::TypeofType(
233 new ast::VariableExpr( location, acceptables ) ) ),
234 }
235 );
236 ResolveContext context{ symtab, transUnit().global };
237 auto result = ResolvExpr::findVoidExpression( set, context );
238 out->push_back( new ast::ExprStmt( location, result.get() ) );
239
240 return acceptables;
241}
242
243ast::ObjectDecl * GenerateWaitForCore::declareFlag(
244 ast::CompoundStmt * out, const CodeLocation & location ) {
245 ast::ObjectDecl * flag = new ast::ObjectDecl( location,
246 namer_flg.newName(),
247 new ast::BasicType( ast::BasicKind::Bool ),
248 new ast::SingleInit( location,
249 ast::ConstantExpr::from_ulong( location, 0 )
250 )
251 );
252 out->push_back( new ast::DeclStmt( location, flag ) );
253 return flag;
254}
255
256ast::ExprStmt * GenerateWaitForCore::makeSetter(
257 const CodeLocation & location, ast::ObjectDecl * flag ) {
258 ast::Expr * expr = new ast::UntypedExpr( location,
259 new ast::NameExpr( location, "?=?" ),
260 {
261 new ast::VariableExpr( location, flag ),
262 ast::ConstantExpr::from_ulong( location, 1 ),
263 }
264 );
265 ResolveContext context{ symtab, transUnit().global };
266 auto result = ResolvExpr::findVoidExpression( expr, context );
267 return new ast::ExprStmt( location, result.get() );
268}
269
270ast::ObjectDecl * GenerateWaitForCore::declMonitors(
271 ast::CompoundStmt * out,
272 const ast::WaitForClause * clause ) {
273 const CodeLocation & location = clause->location;
274 ast::ObjectDecl * monitor = new ast::ObjectDecl( location,
275 namer_mon.newName(),
276 new ast::ArrayType(
277 new ast::PointerType(
278 new ast::StructInstType( decl_monitor )
279 ),
280 ast::ConstantExpr::from_ulong( location, clause->target_args.size() ),
281 ast::FixedLen,
282 ast::DynamicDim
283 ),
284 new ast::ListInit( location,
285 map_range<std::vector<ast::ptr<ast::Init>>>(
286 clause->target_args,
287 []( const ast::Expr * expr ){
288 return new ast::SingleInit( expr->location, expr ); }
289 )
290 )
291 );
292 out->push_back( new ast::DeclStmt( location, monitor ) );
293 return monitor;
294}
295
296void GenerateWaitForCore::init_clause(
297 ast::CompoundStmt * out,
298 ast::ObjectDecl * acceptables,
299 int index,
300 const ast::WaitForClause * clause,
301 ast::Stmt * setter ) {
302 const CodeLocation & location = clause->location;
303 const ast::ObjectDecl * monitors = declMonitors( out, clause );
304 ast::Type * fptr_t = new ast::PointerType(
305 new ast::FunctionType( ast::FixedArgs ) );
306
307 const ast::VariableExpr * variableExpr =
308 clause->target.as<ast::VariableExpr>();
309 ast::Expr * castExpr = new ast::CastExpr(
310 location,
311 new ast::CastExpr(
312 location,
313 clause->target,
314 ast::deepCopy( variableExpr->result.get() ),
315 ast::GeneratedCast ),
316 fptr_t,
317 ast::GeneratedCast );
318
319 ast::ObjectDecl * funcDecl = new ast::ObjectDecl( location,
320 namer_fun.newName(),
321 ast::deepCopy( fptr_t ),
322 new ast::SingleInit( location, castExpr )
323 );
324 ast::Expr * funcExpr = new ast::VariableExpr( location, funcDecl );
325 out->push_back( new ast::DeclStmt( location, funcDecl ) );
326
327 ResolveContext context{ symtab, transUnit().global };
328 out->push_back( maybeCond( location, clause->when_cond.get(), {
329 makeAccStmt( location, acceptables, index, "is_dtor",
330 detectIsDtor( location, clause->target ), context ),
331 makeAccStmt( location, acceptables, index, "func",
332 funcExpr, context ),
333 makeAccStmt( location, acceptables, index, "data",
334 new ast::VariableExpr( location, monitors ), context ),
335 makeAccStmt( location, acceptables, index, "size",
336 ast::ConstantExpr::from_ulong( location,
337 clause->target_args.size() ), context ),
338 ast::deepCopy( setter ),
339 } ) );
340}
341
342ast::Expr * GenerateWaitForCore::init_timeout(
343 ast::CompoundStmt * out,
344 const CodeLocation & topLocation,
345 const ast::Expr * timeout_time,
346 const ast::Expr * timeout_cond,
347 const ast::Stmt * else_stmt,
348 const ast::Expr * else_cond,
349 const ast::Stmt * setter ) {
350 ast::ObjectDecl * timeout = new ast::ObjectDecl( topLocation,
351 namer_tim.newName(),
352 new ast::BasicType( ast::BasicKind::LongLongUnsignedInt ),
353 new ast::SingleInit( topLocation,
354 ast::ConstantExpr::from_int( topLocation, -1 )
355 )
356 );
357 out->push_back( new ast::DeclStmt( topLocation, timeout ) );
358
359 if ( timeout_time ) {
360 const CodeLocation & location = timeout_time->location;
361 out->push_back( maybeCond( location, timeout_cond, {
362 new ast::ExprStmt( location,
363 makeOpAssign(
364 location,
365 new ast::VariableExpr( location, timeout ),
366 timeout_time
367 )
368 ),
369 ast::deepCopy( setter ),
370 } ) );
371 }
372
373 // We only care about the else_stmt's presence and location.
374 if ( else_stmt ) {
375 const CodeLocation & location = else_stmt->location;
376 out->push_back( maybeCond( location, else_cond, {
377 new ast::ExprStmt( location,
378 makeOpAssign(
379 location,
380 new ast::VariableExpr( location, timeout ),
381 ast::ConstantExpr::from_ulong( location, 0 )
382 )
383 ),
384 ast::deepCopy( setter ),
385 } ) );
386 }
387
388 return new ast::VariableExpr( topLocation, timeout );
389}
390
391ast::Expr * GenerateWaitForCore::call(
392 ast::CompoundStmt * out,
393 const CodeLocation & location,
394 size_t numClauses,
395 ast::ObjectDecl * acceptables,
396 ast::Expr * timeout
397) {
398 ast::ObjectDecl * index = new ast::ObjectDecl( location,
399 namer_idx.newName(),
400 new ast::BasicType( ast::BasicKind::ShortSignedInt ),
401 new ast::SingleInit( location,
402 ast::ConstantExpr::from_int( location, -1 )
403 )
404 );
405 out->push_back( new ast::DeclStmt( location, index ) );
406
407 ast::ObjectDecl * mask = new ast::ObjectDecl( location,
408 namer_msk.newName(),
409 new ast::StructInstType( decl_mask ),
410 new ast::ListInit( location, {
411 new ast::SingleInit( location,
412 new ast::AddressExpr( location,
413 new ast::VariableExpr( location, index )
414 )
415 ),
416 new ast::ListInit( location, {
417 new ast::SingleInit( location,
418 new ast::VariableExpr( location, acceptables )
419 ),
420 new ast::SingleInit( location,
421 ast::ConstantExpr::from_ulong( location, numClauses )
422 ),
423 }),
424 })
425 );
426 out->push_back( new ast::DeclStmt( location, mask ) );
427
428 ast::ApplicationExpr * waitforMask = new ast::ApplicationExpr( location,
429 ast::VariableExpr::functionPointer( location, decl_waitfor ),
430 {
431 new ast::CastExpr(
432 new ast::VariableExpr( location, mask ),
433 new ast::ReferenceType(
434 new ast::StructInstType( decl_mask )
435 )
436 ),
437 timeout
438 }
439 );
440 out->push_back( new ast::ExprStmt( location, waitforMask ) );
441
442 return new ast::VariableExpr( location, index );
443}
444
445ast::Stmt * choose( const ast::WaitForStmt * waitfor, ast::Expr * result ) {
446 const CodeLocation & location = waitfor->location;
447
448 ast::SwitchStmt * theSwitch = new ast::SwitchStmt( location,
449 result,
450 std::vector<ast::ptr<ast::CaseClause>>()
451 );
452
453 for ( const auto & [i, clause] : enumerate( waitfor->clauses ) ) {
454 theSwitch->cases.push_back(
455 new ast::CaseClause( location,
456 ast::ConstantExpr::from_ulong( location, i ),
457 {
458 new ast::CompoundStmt( location, {
459 clause->stmt,
460 new ast::BranchStmt( location,
461 ast::BranchStmt::Break,
462 ast::Label( location )
463 )
464 })
465 }
466 )
467 );
468 }
469
470 if ( waitfor->timeout_stmt ) {
471 theSwitch->cases.push_back(
472 new ast::CaseClause( location,
473 ast::ConstantExpr::from_int( location, -2 ),
474 {
475 new ast::CompoundStmt( location, {
476 waitfor->timeout_stmt,
477 new ast::BranchStmt( location,
478 ast::BranchStmt::Break,
479 ast::Label( location )
480 )
481 })
482 }
483 )
484 );
485 }
486
487 if ( waitfor->else_stmt ) {
488 theSwitch->cases.push_back(
489 new ast::CaseClause( location,
490 ast::ConstantExpr::from_int( location, -1 ),
491 {
492 new ast::CompoundStmt( location, {
493 waitfor->else_stmt,
494 new ast::BranchStmt( location,
495 ast::BranchStmt::Break,
496 ast::Label( location )
497 )
498 })
499 }
500 )
501 );
502 }
503
504 return theSwitch;
505}
506
507void GenerateWaitForCore::previsit( const ast::FunctionDecl * decl ) {
508 if ( "__waitfor_internal" == decl->name ) {
509 decl_waitfor = decl;
510 }
511}
512
513void GenerateWaitForCore::previsit( const ast::StructDecl * decl ) {
514 if ( !decl->body ) {
515 return;
516 } else if ( "__acceptable_t" == decl->name ) {
517 assert( !decl_acceptable );
518 decl_acceptable = decl;
519 } else if ( "__waitfor_mask_t" == decl->name ) {
520 assert( !decl_mask );
521 decl_mask = decl;
522 } else if ( "monitor$" == decl->name ) {
523 assert( !decl_monitor );
524 decl_monitor = decl;
525 }
526}
527
528ast::Stmt * GenerateWaitForCore::postvisit( const ast::WaitForStmt * stmt ) {
529 if ( !decl_monitor || !decl_acceptable || !decl_mask ) {
530 SemanticError( stmt, "waitfor keyword requires monitors to be in scope, add #include <monitor.hfa>" );
531 }
532
533 const CodeLocation & location = stmt->location;
534 ast::CompoundStmt * comp = new ast::CompoundStmt( location );
535
536 ast::ObjectDecl * acceptables = declareAcceptables( comp, location, stmt->clauses.size() );
537 ast::ObjectDecl * flag = declareFlag( comp, location );
538 ast::Stmt * setter = makeSetter( location, flag );
539
540 for ( const auto & [i, clause] : enumerate( stmt->clauses ) ) {
541 init_clause( comp, acceptables, i, clause, setter );
542 }
543
544 ast::Expr * timeout = init_timeout(
545 comp,
546 location,
547 stmt->timeout_time,
548 stmt->timeout_cond,
549 stmt->else_stmt,
550 stmt->else_cond,
551 setter
552 );
553
554 ast::CompoundStmt * compound = new ast::CompoundStmt( location );
555 comp->push_back( new ast::IfStmt( location,
556 new ast::VariableExpr( location, flag ),
557 compound,
558 nullptr
559 ));
560
561 ast::Expr * result = call(
562 compound, location, stmt->clauses.size(), acceptables, timeout );
563 compound->push_back( choose( stmt, result ) );
564 return comp;
565}
566
567} // namespace
568
569void generateWaitFor( ast::TranslationUnit & translationUnit ) {
570 ast::Pass<GenerateWaitForCore>::run( translationUnit );
571}
572
573} // namespace Concurrency
574
575// Local Variables: //
576// tab-width: 4 //
577// mode: c++ //
578// compile-command: "make install" //
579// End: //
Note: See TracBrowser for help on using the repository browser.