source: src/Concurrency/Waitfor.cpp@ c9d36b1

stuck-waitfor-destruct
Last change on this file since c9d36b1 was c9d36b1, checked in by Matthew Au-Yeung <mw2auyeu@…>, 3 days ago

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

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