source: src/Concurrency/KeywordsNew.cpp@ 436bbe5

ADT ast-experimental enum forall-pointer-decay pthread-emulation qualifiedEnum
Last change on this file since 436bbe5 was 56f519b, checked in by Andrew Beach <ajbeach@…>, 4 years ago

Clean-up of the last pass. Added a test for the constructor mutex argument test.

  • Property mode set to 100644
File size: 17.0 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// KeywordsNew.cpp -- Implement concurrency constructs from their keywords.
8//
9// Author : Andrew Beach
10// Created On : Tue Nov 16 9:53:00 2021
11// Last Modified By : Andrew Beach
12// Last Modified On : Wed Dec 1 11:24:00 2021
13// Update Count : 1
14//
15
16#include "Concurrency/Keywords.h"
17
18#include "AST/Copy.hpp"
19#include "AST/Decl.hpp"
20#include "AST/Pass.hpp"
21#include "AST/Stmt.hpp"
22#include "AST/TranslationUnit.hpp"
23#include "CodeGen/OperatorTable.h"
24#include "Common/utility.h"
25#include "InitTweak/InitTweak.h"
26
27namespace Concurrency {
28
29namespace {
30
31inline static bool isThread( const ast::DeclWithType * decl ) {
32 auto baseType = decl->get_type()->stripDeclarator();
33 auto instType = dynamic_cast<const ast::StructInstType *>( baseType );
34 if ( nullptr == instType ) { return false; }
35 return instType->base->is_thread();
36}
37
38// --------------------------------------------------------------------------
39struct MutexKeyword final {
40 const ast::FunctionDecl * postvisit( const ast::FunctionDecl * decl );
41 void postvisit( const ast::StructDecl * decl );
42 const ast::Stmt * postvisit( const ast::MutexStmt * stmt );
43
44 static std::vector<const ast::DeclWithType *> findMutexArgs(
45 const ast::FunctionDecl * decl, bool & first );
46 static void validate( const ast::DeclWithType * decl );
47
48 ast::CompoundStmt * addDtorStatements( const ast::FunctionDecl* func, const ast::CompoundStmt *, const std::vector<const ast::DeclWithType *> &);
49 ast::CompoundStmt * addStatements( const ast::FunctionDecl* func, const ast::CompoundStmt *, const std::vector<const ast::DeclWithType *> &);
50 ast::CompoundStmt * addStatements( const ast::CompoundStmt * body, const std::vector<ast::ptr<ast::Expr>> & args );
51 ast::CompoundStmt * addThreadDtorStatements( const ast::FunctionDecl* func, const ast::CompoundStmt * body, const std::vector<const ast::DeclWithType *> & args );
52
53private:
54 const ast::StructDecl * monitor_decl = nullptr;
55 const ast::StructDecl * guard_decl = nullptr;
56 const ast::StructDecl * dtor_guard_decl = nullptr;
57 const ast::StructDecl * thread_guard_decl = nullptr;
58 const ast::StructDecl * lock_guard_decl = nullptr;
59
60 static ast::ptr<ast::Type> generic_func;
61};
62
63const ast::FunctionDecl * MutexKeyword::postvisit(
64 const ast::FunctionDecl * decl ) {
65 bool is_first_argument_mutex = false;
66 const std::vector<const ast::DeclWithType *> mutexArgs =
67 findMutexArgs( decl, is_first_argument_mutex );
68 bool const isDtor = CodeGen::isDestructor( decl->name );
69
70 // Does this function have any mutex arguments that connect to monitors?
71 if ( mutexArgs.empty() ) {
72 // If this is the destructor for a monitor it must be mutex.
73 if ( isDtor ) {
74 // This reflects MutexKeyword::validate, but no error messages.
75 const ast::Type * type = decl->type->params.front();
76
77 // If it's a copy, it's not a mutex.
78 const ast::ReferenceType * refType = dynamic_cast<const ast::ReferenceType *>( type );
79 if ( nullptr == refType ) {
80 return decl;
81 }
82
83 // If it is not pointing directly to a type, it's not a mutex.
84 auto base = refType->base;
85 if ( base.as<ast::ReferenceType>() ) return decl;
86 if ( base.as<ast::PointerType>() ) return decl;
87
88 // If it is not a struct, it's not a mutex.
89 auto baseStruct = base.as<ast::StructInstType>();
90 if ( nullptr == baseStruct ) return decl;
91
92 // If it is a monitor, then it is a monitor.
93 if( baseStruct->base->is_monitor() || baseStruct->base->is_thread() ) {
94 SemanticError( decl, "destructors for structures declared as \"monitor\" must use mutex parameters\n" );
95 }
96 }
97 return decl;
98 }
99
100 // Monitors can't be constructed with mutual exclusion.
101 if ( CodeGen::isConstructor( decl->name ) && is_first_argument_mutex ) {
102 SemanticError( decl, "constructors cannot have mutex parameters\n" );
103 }
104
105 // It makes no sense to have multiple mutex parameters for the destructor.
106 if ( isDtor && mutexArgs.size() != 1 ) {
107 SemanticError( decl, "destructors can only have 1 mutex argument\n" );
108 }
109
110 // Make sure all the mutex arguments are monitors.
111 for ( auto arg : mutexArgs ) {
112 validate( arg );
113 }
114
115 // Check to see if the body needs to be instrument the body.
116 const ast::CompoundStmt * body = decl->stmts;
117 if ( !body ) return decl;
118
119 // Check to if the required headers have been seen.
120 if ( !monitor_decl || !guard_decl || !dtor_guard_decl ) {
121 SemanticError( decl, "mutex keyword requires monitors to be in scope, add #include <monitor.hfa>\n" );
122 }
123
124 // Instrument the body.
125 ast::CompoundStmt * newBody = nullptr;
126 if ( isDtor && isThread( mutexArgs.front() ) ) {
127 if ( !thread_guard_decl ) {
128 SemanticError( decl, "thread destructor requires threads to be in scope, add #include <thread.hfa>\n" );
129 }
130 newBody = addThreadDtorStatements( decl, body, mutexArgs );
131 } else if ( isDtor ) {
132 newBody = addDtorStatements( decl, body, mutexArgs );
133 } else {
134 newBody = addStatements( decl, body, mutexArgs );
135 }
136 assert( newBody );
137 return ast::mutate_field( decl, &ast::FunctionDecl::stmts, newBody );
138}
139
140void MutexKeyword::postvisit( const ast::StructDecl * decl ) {
141 if ( !decl->body ) {
142 return;
143 } else if ( decl->name == "monitor$" ) {
144 assert( !monitor_decl );
145 monitor_decl = decl;
146 } else if ( decl->name == "monitor_guard_t" ) {
147 assert( !guard_decl );
148 guard_decl = decl;
149 } else if ( decl->name == "monitor_dtor_guard_t" ) {
150 assert( !dtor_guard_decl );
151 dtor_guard_decl = decl;
152 } else if ( decl->name == "thread_dtor_guard_t" ) {
153 assert( !thread_guard_decl );
154 thread_guard_decl = decl;
155 } else if ( decl->name == "__mutex_stmt_lock_guard" ) {
156 assert( !lock_guard_decl );
157 lock_guard_decl = decl;
158 }
159}
160
161const ast::Stmt * MutexKeyword::postvisit( const ast::MutexStmt * stmt ) {
162 ast::CompoundStmt * body =
163 new ast::CompoundStmt( stmt->location, { stmt->stmt } );
164 addStatements( body, stmt->mutexObjs );
165 return body;
166}
167
168std::vector<const ast::DeclWithType *> MutexKeyword::findMutexArgs(
169 const ast::FunctionDecl * decl, bool & first ) {
170 std::vector<const ast::DeclWithType *> mutexArgs;
171
172 bool once = true;
173 for ( auto arg : decl->params ) {
174 const ast::Type * type = arg->get_type();
175 if ( type->is_mutex() ) {
176 if ( once ) first = true;
177 mutexArgs.push_back( arg.get() );
178 }
179 once = false;
180 }
181 return mutexArgs;
182}
183
184void MutexKeyword::validate( const ast::DeclWithType * decl ) {
185 const ast::Type * type = decl->get_type();
186
187 // If it's a copy, it's not a mutex.
188 const ast::ReferenceType * refType = dynamic_cast<const ast::ReferenceType *>( type );
189 if ( nullptr == refType ) {
190 SemanticError( decl, "Mutex argument must be of reference type " );
191 }
192
193 // If it is not pointing directly to a type, it's not a mutex.
194 auto base = refType->base;
195 if ( base.as<ast::ReferenceType>() || base.as<ast::PointerType>() ) {
196 SemanticError( decl, "Mutex argument have exactly one level of indirection " );
197 }
198
199 // If it is not a struct, it's not a mutex.
200 auto baseStruct = base.as<ast::StructInstType>();
201 if ( nullptr == baseStruct ) return;
202
203 // Make sure that only the outer reference is mutex.
204 if( baseStruct->is_mutex() ) {
205 SemanticError( decl, "mutex keyword may only appear once per argument " );
206 }
207}
208
209ast::CompoundStmt * MutexKeyword::addDtorStatements(
210 const ast::FunctionDecl* func, const ast::CompoundStmt * body,
211 const std::vector<const ast::DeclWithType *> & args ) {
212 ast::Type * argType = ast::shallowCopy( args.front()->get_type() );
213 argType->set_mutex( false );
214
215 ast::CompoundStmt * mutBody = ast::mutate( body );
216
217 // Generated code goes near the beginning of body:
218 const CodeLocation & location = mutBody->location;
219
220 const ast::ObjectDecl * monitor = new ast::ObjectDecl(
221 location,
222 "__monitor",
223 new ast::PointerType( new ast::StructInstType( monitor_decl ) ),
224 new ast::SingleInit(
225 location,
226 new ast::UntypedExpr(
227 location,
228 new ast::NameExpr( location, "get_monitor" ),
229 { new ast::CastExpr(
230 location,
231 new ast::VariableExpr( location, args.front() ),
232 argType, ast::ExplicitCast
233 ) }
234 )
235 ),
236 ast::Storage::Classes(),
237 ast::Linkage::Cforall
238 );
239
240 assert( generic_func );
241
242 // In reverse order:
243 // monitor_dtor_guard_t __guard = { __monitor, func, false };
244 mutBody->push_front(
245 new ast::DeclStmt( location, new ast::ObjectDecl(
246 location,
247 "__guard",
248 new ast::StructInstType( dtor_guard_decl ),
249 new ast::ListInit(
250 location,
251 {
252 new ast::SingleInit( location,
253 new ast::AddressExpr(
254 new ast::VariableExpr( location, monitor ) ) ),
255 new ast::SingleInit( location,
256 new ast::CastExpr( location,
257 new ast::VariableExpr( location, func ),
258 generic_func,
259 ast::ExplicitCast ) ),
260 new ast::SingleInit( location,
261 ast::ConstantExpr::from_bool( location, false ) ),
262 },
263 {},
264 ast::MaybeConstruct
265 ),
266 ast::Storage::Classes(),
267 ast::Linkage::Cforall
268 ))
269 );
270
271 // monitor$ * __monitor = get_monitor(a);
272 mutBody->push_front( new ast::DeclStmt( location, monitor ) );
273
274 return mutBody;
275}
276
277ast::CompoundStmt * MutexKeyword::addStatements(
278 const ast::FunctionDecl* func, const ast::CompoundStmt * body,
279 const std::vector<const ast::DeclWithType * > & args ) {
280 ast::CompoundStmt * mutBody = ast::mutate( body );
281
282 // Code is generated near the beginning of the compound statement.
283 const CodeLocation & location = mutBody->location;
284
285 // Make pointer to the monitors.
286 ast::ObjectDecl * monitors = new ast::ObjectDecl(
287 location,
288 "__monitors",
289 new ast::ArrayType(
290 new ast::PointerType(
291 new ast::StructInstType( monitor_decl )
292 ),
293 ast::ConstantExpr::from_ulong( location, args.size() ),
294 ast::FixedLen,
295 ast::DynamicDim
296 ),
297 new ast::ListInit(
298 location,
299 map_range<std::vector<ast::ptr<ast::Init>>>(
300 args,
301 []( const ast::DeclWithType * decl ) {
302 return new ast::SingleInit(
303 decl->location,
304 new ast::UntypedExpr(
305 decl->location,
306 new ast::NameExpr( decl->location, "get_monitor" ),
307 {
308 new ast::CastExpr(
309 decl->location,
310 new ast::VariableExpr( decl->location, decl ),
311 decl->get_type(),
312 ast::ExplicitCast
313 )
314 }
315 )
316 );
317 }
318 )
319 ),
320 ast::Storage::Classes(),
321 ast::Linkage::Cforall
322 );
323
324 assert( generic_func );
325
326 // In Reverse Order:
327 mutBody->push_front(
328 new ast::DeclStmt( location, new ast::ObjectDecl(
329 location,
330 "__guard",
331 new ast::StructInstType( guard_decl ),
332 new ast::ListInit(
333 location,
334 {
335 new ast::SingleInit( location,
336 new ast::VariableExpr( location, monitors ) ),
337 new ast::SingleInit( location,
338 ast::ConstantExpr::from_ulong( location, args.size() ) ),
339 new ast::SingleInit( location, new ast::CastExpr(
340 location,
341 new ast::VariableExpr( location, func ),
342 generic_func,
343 ast::ExplicitCast
344 ) ),
345 },
346 {},
347 ast::MaybeConstruct
348 ),
349 ast::Storage::Classes(),
350 ast::Linkage::Cforall
351 ))
352 );
353
354 // monitor$ * __monitors[] = { get_monitor(a), get_monitor(b) };
355 mutBody->push_front( new ast::DeclStmt( location, monitors ) );
356
357 return mutBody;
358}
359
360ast::CompoundStmt * MutexKeyword::addStatements(
361 const ast::CompoundStmt * body,
362 const std::vector<ast::ptr<ast::Expr>> & args ) {
363 ast::CompoundStmt * mutBody = ast::mutate( body );
364
365 // Code is generated near the beginning of the compound statement.
366 const CodeLocation & location = mutBody->location;
367
368 // Make pointer to the monitors.
369 ast::ObjectDecl * monitors = new ast::ObjectDecl(
370 location,
371 "__monitors",
372 new ast::ArrayType(
373 new ast::PointerType(
374 new ast::TypeofType(
375 new ast::UntypedExpr(
376 location,
377 new ast::NameExpr( location, "__get_type" ),
378 { args.front() }
379 )
380 )
381 ),
382 ast::ConstantExpr::from_ulong( location, args.size() ),
383 ast::FixedLen,
384 ast::DynamicDim
385 ),
386 new ast::ListInit(
387 location,
388 map_range<std::vector<ast::ptr<ast::Init>>>(
389 args, [](const ast::Expr * expr) {
390 return new ast::SingleInit(
391 expr->location,
392 new ast::UntypedExpr(
393 expr->location,
394 new ast::NameExpr( expr->location, "__get_ptr" ),
395 { expr }
396 )
397 );
398 }
399 )
400 ),
401 ast::Storage::Classes(),
402 ast::Linkage::Cforall
403 );
404
405 ast::StructInstType * lock_guard_struct =
406 new ast::StructInstType( lock_guard_decl );
407 ast::TypeExpr * lock_type_expr = new ast::TypeExpr(
408 location,
409 new ast::TypeofType(
410 new ast::UntypedExpr(
411 location,
412 new ast::NameExpr( location, "__get_type" ),
413 { args.front() }
414 )
415 )
416 );
417
418 lock_guard_struct->params.push_back( lock_type_expr );
419
420 // In reverse order:
421 // monitor_guard_t __guard = { __monitors, # };
422 mutBody->push_front(
423 new ast::DeclStmt(
424 location,
425 new ast::ObjectDecl(
426 location,
427 "__guard",
428 lock_guard_struct,
429 new ast::ListInit(
430 location,
431 {
432 new ast::SingleInit(
433 location,
434 new ast::VariableExpr( location, monitors ) ),
435 new ast::SingleInit(
436 location,
437 ast::ConstantExpr::from_ulong( location, args.size() ) ),
438 },
439 {},
440 ast::MaybeConstruct
441 ),
442 ast::Storage::Classes(),
443 ast::Linkage::Cforall
444 )
445 )
446 );
447
448 // monitor$ * __monitors[] = { get_monitor(a), get_monitor(b) };
449 mutBody->push_front( new ast::DeclStmt( location, monitors ) );
450
451 return mutBody;
452}
453
454ast::CompoundStmt * MutexKeyword::addThreadDtorStatements(
455 const ast::FunctionDecl*, const ast::CompoundStmt * body,
456 const std::vector<const ast::DeclWithType * > & args ) {
457 assert( args.size() == 1 );
458 const ast::DeclWithType * arg = args.front();
459 const ast::Type * argType = arg->get_type();
460 assert( argType->is_mutex() );
461
462 ast::CompoundStmt * mutBody = ast::mutate( body );
463
464 // The code is generated near the front of the body.
465 const CodeLocation & location = mutBody->location;
466
467 // thread_dtor_guard_t __guard = { this, intptr( 0 ) };
468 mutBody->push_front( new ast::DeclStmt(
469 location,
470 new ast::ObjectDecl(
471 location,
472 "__guard",
473 new ast::StructInstType( thread_guard_decl ),
474 new ast::ListInit(
475 location,
476 {
477 new ast::SingleInit( location,
478 new ast::CastExpr( location,
479 new ast::VariableExpr( location, arg ), argType ) ),
480 new ast::SingleInit(
481 location,
482 new ast::UntypedExpr(
483 location,
484 new ast::NameExpr( location, "intptr" ), {
485 ast::ConstantExpr::from_int( location, 0 ),
486 }
487 ) ),
488 },
489 {},
490 ast::MaybeConstruct
491 ),
492 ast::Storage::Classes(),
493 ast::Linkage::Cforall
494 )
495 ));
496
497 return mutBody;
498}
499
500ast::ptr<ast::Type> MutexKeyword::generic_func =
501 new ast::FunctionType( ast::VariableArgs );
502
503// --------------------------------------------------------------------------
504struct ThreadStarter final {
505 void previsit( const ast::StructDecl * decl );
506 const ast::FunctionDecl * postvisit( const ast::FunctionDecl * decl );
507
508private:
509 bool thread_ctor_seen = false;
510 const ast::StructDecl * thread_decl = nullptr;
511};
512
513void ThreadStarter::previsit( const ast::StructDecl * decl ) {
514 if ( decl->body && decl->name == "thread$" ) {
515 assert( !thread_decl );
516 thread_decl = decl;
517 }
518}
519
520const ast::FunctionDecl * ThreadStarter::postvisit( const ast::FunctionDecl * decl ) {
521 if ( !CodeGen::isConstructor( decl->name ) ) return decl;
522
523 // Seach for the thread constructor.
524 // (Are the "prefixes" of these to blocks the same?)
525 const ast::Type * typeof_this = InitTweak::getTypeofThis( decl->type );
526 auto ctored_type = dynamic_cast<const ast::StructInstType *>( typeof_this );
527 if ( ctored_type && ctored_type->base == thread_decl ) {
528 thread_ctor_seen = true;
529 }
530
531 // Modify this declaration, the extra checks to see if we will are first.
532 const ast::ptr<ast::DeclWithType> & param = decl->params.front();
533 auto type = dynamic_cast<const ast::StructInstType *>(
534 InitTweak::getPointerBase( param->get_type() ) );
535 if ( nullptr == type ) return decl;
536 if ( !type->base->is_thread() ) return decl;
537 if ( !thread_decl || !thread_ctor_seen ) {
538 SemanticError( type->base->location, "thread keyword requires threads to be in scope, add #include <thread.hfa>" );
539 }
540 const ast::CompoundStmt * stmt = decl->stmts;
541 if ( nullptr == stmt ) return decl;
542
543 // Now do the actual modification:
544 ast::CompoundStmt * mutStmt = ast::mutate( stmt );
545 const CodeLocation & location = mutStmt->location;
546 mutStmt->push_back(
547 new ast::ExprStmt(
548 location,
549 new ast::UntypedExpr(
550 location,
551 new ast::NameExpr( location, "__thrd_start" ),
552 {
553 new ast::VariableExpr( location, param ),
554 new ast::NameExpr( location, "main" ),
555 }
556 )
557 )
558 );
559
560 return ast::mutate_field( decl, &ast::FunctionDecl::stmts, mutStmt );
561}
562
563} // namespace
564
565// --------------------------------------------------------------------------
566
567void implementKeywords( ast::TranslationUnit & translationUnit ) {
568 (void)translationUnit;
569 assertf(false, "Apply Keywords not implemented." );
570}
571
572void implementMutex( ast::TranslationUnit & translationUnit ) {
573 ast::Pass<MutexKeyword>::run( translationUnit );
574}
575
576void implementThreadStarter( ast::TranslationUnit & translationUnit ) {
577 ast::Pass<ThreadStarter>::run( translationUnit );
578}
579
580}
581
582// Local Variables: //
583// tab-width: 4 //
584// mode: c++ //
585// compile-command: "make install" //
586// End: //
Note: See TracBrowser for help on using the repository browser.