source: src/Concurrency/Keywords.cpp@ c9d36b1

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

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

  • Property mode set to 100644
File size: 49.7 KB
RevLine 
[2cf3b87]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//
[83fd57d]7// Keywords.cpp -- Implement concurrency constructs from their keywords.
[2cf3b87]8//
9// Author : Andrew Beach
10// Created On : Tue Nov 16 9:53:00 2021
[ca9d65e]11// Last Modified By : Peter A. Buhr
[c3d0182a]12// Last Modified On : Fri Jan 24 18:33:02 2025
13// Update Count : 14
[2cf3b87]14//
15
[c92bdcc]16#include "Concurrency/Keywords.hpp"
[2cf3b87]17
[83fd57d]18#include <iostream>
19
[2cf3b87]20#include "AST/Copy.hpp"
21#include "AST/Decl.hpp"
[4f6dda0]22#include "AST/Expr.hpp"
[e01eb4a]23#include "AST/Inspect.hpp"
[2cf3b87]24#include "AST/Pass.hpp"
25#include "AST/Stmt.hpp"
[4f6dda0]26#include "AST/DeclReplacer.hpp"
[2cf3b87]27#include "AST/TranslationUnit.hpp"
[c92bdcc]28#include "CodeGen/OperatorTable.hpp"
29#include "Common/Examine.hpp"
30#include "Common/Utility.hpp"
[c9d36b1]31#include "Concurrency/MutexFuncHash.hpp"
[c92bdcc]32#include "Common/UniqueName.hpp"
[83fd57d]33#include "ControlStruct/LabelGenerator.hpp"
[c92bdcc]34#include "InitTweak/InitTweak.hpp"
35#include "Virtual/Tables.hpp"
[2cf3b87]36
37namespace Concurrency {
38
39namespace {
40
[4f6dda0]41// --------------------------------------------------------------------------
42// Loose Helper Functions:
43
44/// Detect threads constructed with the keyword thread.
45bool isThread( const ast::DeclWithType * decl ) {
[2cf3b87]46 auto baseType = decl->get_type()->stripDeclarator();
47 auto instType = dynamic_cast<const ast::StructInstType *>( baseType );
48 if ( nullptr == instType ) { return false; }
49 return instType->base->is_thread();
50}
51
[4f6dda0]52/// Get the virtual type id if given a type name.
53std::string typeIdType( std::string const & exception_name ) {
54 return exception_name.empty() ? std::string()
55 : Virtual::typeIdType( exception_name );
56}
57
58/// Get the vtable type name if given a type name.
59std::string vtableTypeName( std::string const & exception_name ) {
60 return exception_name.empty() ? std::string()
61 : Virtual::vtableTypeName( exception_name );
62}
63
64static ast::Type * mutate_under_references( ast::ptr<ast::Type>& type ) {
65 ast::Type * mutType = type.get_and_mutate();
66 for ( ast::ReferenceType * mutRef
67 ; (mutRef = dynamic_cast<ast::ReferenceType *>( mutType ))
68 ; mutType = mutRef->base.get_and_mutate() );
69 return mutType;
70}
71
[35eef3b]72// Describe that it adds the generic parameters and the uses of the generic parameters on the
73// function and first "this" argument.
[4f6dda0]74ast::FunctionDecl * fixupGenerics(
75 const ast::FunctionDecl * func, const ast::StructDecl * decl ) {
76 const CodeLocation & location = decl->location;
77 // We have to update both the declaration
78 auto mutFunc = ast::mutate( func );
79 auto mutType = mutFunc->type.get_and_mutate();
80
81 if ( decl->params.empty() ) {
82 return mutFunc;
83 }
84
85 assert( 0 != mutFunc->params.size() );
86 assert( 0 != mutType->params.size() );
87
88 // Add the "forall" clause information.
89 for ( const ast::ptr<ast::TypeDecl> & typeParam : decl->params ) {
90 auto typeDecl = ast::deepCopy( typeParam );
91 mutFunc->type_params.push_back( typeDecl );
[b230091]92 mutType->forall.push_back( new ast::TypeInstType( typeDecl ) );
[4f6dda0]93 for ( auto & assertion : typeDecl->assertions ) {
94 mutFunc->assertions.push_back( assertion );
95 mutType->assertions.emplace_back(
96 new ast::VariableExpr( location, assertion ) );
97 }
98 typeDecl->assertions.clear();
99 }
100
101 // Even chain_mutate is not powerful enough for this:
102 ast::ptr<ast::Type>& paramType = strict_dynamic_cast<ast::ObjectDecl *>(
103 mutFunc->params[0].get_and_mutate() )->type;
104 auto paramTypeInst = strict_dynamic_cast<ast::StructInstType *>(
105 mutate_under_references( paramType ) );
106 auto typeParamInst = strict_dynamic_cast<ast::StructInstType *>(
107 mutate_under_references( mutType->params[0] ) );
108
109 for ( const ast::ptr<ast::TypeDecl> & typeDecl : mutFunc->type_params ) {
110 paramTypeInst->params.push_back(
[b230091]111 new ast::TypeExpr( location, new ast::TypeInstType( typeDecl ) ) );
[4f6dda0]112 typeParamInst->params.push_back(
[b230091]113 new ast::TypeExpr( location, new ast::TypeInstType( typeDecl ) ) );
[4f6dda0]114 }
115
116 return mutFunc;
117}
118
119// --------------------------------------------------------------------------
[35eef3b]120
121// This type describes the general information used to transform a generator, coroutine, monitor, or
122// thread aggregate kind. A pass is made over the AST in ConcurrentSueKeyword::postvisit looking
123// for each of these kinds. When found, this data structure is filled in with the information from
124// the specific structures below, and handleStruct is called to perform the common changes that
125// augment the aggregate kind with fields and generate appropriate companion routines. The location
126// of any extra fields is specified in addField.
127
[ed96731]128struct ConcurrentSueKeyword : public ast::WithDeclsToAdd {
[4f6dda0]129 ConcurrentSueKeyword(
130 std::string&& type_name, std::string&& field_name,
131 std::string&& getter_name, std::string&& context_error,
132 std::string&& exception_name,
133 bool needs_main, ast::AggregateDecl::Aggregate cast_target
134 ) :
135 type_name( type_name ), field_name( field_name ),
136 getter_name( getter_name ), context_error( context_error ),
137 exception_name( exception_name ),
138 typeid_name( typeIdType( exception_name ) ),
139 vtable_name( vtableTypeName( exception_name ) ),
140 needs_main( needs_main ), cast_target( cast_target )
141 {}
142
143 virtual ~ConcurrentSueKeyword() {}
144
145 const ast::Decl * postvisit( const ast::StructDecl * decl );
146 const ast::DeclWithType * postvisit( const ast::FunctionDecl * decl );
147 const ast::Expr * postvisit( const ast::KeywordCastExpr * expr );
148
149 struct StructAndField {
150 const ast::StructDecl * decl;
151 const ast::ObjectDecl * field;
152 };
153
154 const ast::StructDecl * handleStruct( const ast::StructDecl * );
155 void handleMain( const ast::FunctionDecl *, const ast::StructInstType * );
156 void addTypeId( const ast::StructDecl * );
157 void addVtableForward( const ast::StructDecl * );
158 const ast::FunctionDecl * forwardDeclare( const ast::StructDecl * );
159 StructAndField addField( const ast::StructDecl * );
160 void addGetRoutines( const ast::ObjectDecl *, const ast::FunctionDecl * );
161 void addLockUnlockRoutines( const ast::StructDecl * );
162
163private:
164 const std::string type_name;
165 const std::string field_name;
166 const std::string getter_name;
167 const std::string context_error;
168 const std::string exception_name;
169 const std::string typeid_name;
170 const std::string vtable_name;
171 const bool needs_main;
172 const ast::AggregateDecl::Aggregate cast_target;
173
174 const ast::StructDecl * type_decl = nullptr;
175 const ast::FunctionDecl * dtor_decl = nullptr;
176 const ast::StructDecl * except_decl = nullptr;
177 const ast::StructDecl * typeid_decl = nullptr;
178 const ast::StructDecl * vtable_decl = nullptr;
[080d2d7]179
[4f6dda0]180};
181
[35eef3b]182// Handles generator type declarations:
[4f6dda0]183//
[35eef3b]184// generator MyGenerator { struct MyGenerator {
185// => int __generator_state;
186// int data; int data;
187// a_struct_t more_data; a_struct_t more_data;
[4f6dda0]188// }; };
189//
[35eef3b]190struct GeneratorKeyword final : public ConcurrentSueKeyword {
191 GeneratorKeyword() : ConcurrentSueKeyword(
192 "generator$",
193 "__generator_state",
194 "get_generator",
195 "Unable to find builtin type generator$\n",
196 "",
[4f6dda0]197 true,
[35eef3b]198 ast::AggregateDecl::Generator )
[4f6dda0]199 {}
200
[35eef3b]201 virtual ~GeneratorKeyword() {}
[4f6dda0]202};
203
204// Handles coroutine type declarations:
205//
206// coroutine MyCoroutine { struct MyCoroutine {
207// => coroutine$ __cor_d;
[35eef3b]208// int data; int data;
209// a_struct_t more_data; a_struct_t more_data;
[4f6dda0]210// }; };
211// static inline coroutine$ * get_coroutine( MyCoroutine * this ) { return &this->__cor_d; }
212//
213struct CoroutineKeyword final : public ConcurrentSueKeyword {
214 CoroutineKeyword() : ConcurrentSueKeyword(
215 "coroutine$",
216 "__cor",
217 "get_coroutine",
218 "coroutine keyword requires coroutines to be in scope, add #include <coroutine.hfa>\n",
219 "CoroutineCancelled",
220 true,
221 ast::AggregateDecl::Coroutine )
222 {}
223
224 virtual ~CoroutineKeyword() {}
225};
226
227// Handles monitor type declarations:
228//
229// monitor MyMonitor { struct MyMonitor {
230// => monitor$ __mon_d;
[35eef3b]231// int data; int data;
232// a_struct_t more_data; a_struct_t more_data;
[4f6dda0]233// }; };
234// static inline monitor$ * get_coroutine( MyMonitor * this ) {
235// return &this->__cor_d;
236// }
237// void lock(MyMonitor & this) {
238// lock(get_monitor(this));
239// }
240// void unlock(MyMonitor & this) {
241// unlock(get_monitor(this));
242// }
243//
244struct MonitorKeyword final : public ConcurrentSueKeyword {
245 MonitorKeyword() : ConcurrentSueKeyword(
246 "monitor$",
247 "__mon",
248 "get_monitor",
249 "monitor keyword requires monitors to be in scope, add #include <monitor.hfa>\n",
250 "",
251 false,
252 ast::AggregateDecl::Monitor )
253 {}
254
255 virtual ~MonitorKeyword() {}
256};
257
[35eef3b]258// Handles thread type declarations:
[4f6dda0]259//
[35eef3b]260// thread Mythread { struct MyThread {
261// => thread$ __thrd_d;
262// int data; int data;
263// a_struct_t more_data; a_struct_t more_data;
[4f6dda0]264// }; };
[35eef3b]265// static inline thread$ * get_thread( MyThread * this ) { return &this->__thrd_d; }
[4f6dda0]266//
[35eef3b]267struct ThreadKeyword final : public ConcurrentSueKeyword {
268 ThreadKeyword() : ConcurrentSueKeyword(
269 "thread$",
270 "__thrd",
271 "get_thread",
272 "thread keyword requires threads to be in scope, add #include <thread.hfa>\n",
273 "ThreadCancelled",
[4f6dda0]274 true,
[35eef3b]275 ast::AggregateDecl::Thread )
[4f6dda0]276 {}
277
[35eef3b]278 virtual ~ThreadKeyword() {}
[4f6dda0]279};
280
281const ast::Decl * ConcurrentSueKeyword::postvisit(
282 const ast::StructDecl * decl ) {
283 if ( !decl->body ) {
284 return decl;
285 } else if ( cast_target == decl->kind ) {
286 return handleStruct( decl );
287 } else if ( type_name == decl->name ) {
288 assert( !type_decl );
289 type_decl = decl;
290 } else if ( exception_name == decl->name ) {
291 assert( !except_decl );
292 except_decl = decl;
293 } else if ( typeid_name == decl->name ) {
294 assert( !typeid_decl );
295 typeid_decl = decl;
296 } else if ( vtable_name == decl->name ) {
297 assert( !vtable_decl );
298 vtable_decl = decl;
299 }
300 return decl;
301}
302
303// Try to get the full definition, but raise an error on conflicts.
304const ast::FunctionDecl * getDefinition(
305 const ast::FunctionDecl * old_decl,
306 const ast::FunctionDecl * new_decl ) {
307 if ( !new_decl->stmts ) {
308 return old_decl;
309 } else if ( !old_decl->stmts ) {
310 return new_decl;
311 } else {
312 assert( !old_decl->stmts || !new_decl->stmts );
313 return nullptr;
314 }
315}
316
317const ast::DeclWithType * ConcurrentSueKeyword::postvisit(
318 const ast::FunctionDecl * decl ) {
319 if ( type_decl && isDestructorFor( decl, type_decl ) ) {
320 // Check for forward declarations, try to get the full definition.
321 dtor_decl = (dtor_decl) ? getDefinition( dtor_decl, decl ) : decl;
322 } else if ( !vtable_name.empty() && decl->has_body() ) {
323 if (const ast::DeclWithType * param = isMainFor( decl, cast_target )) {
324 if ( !vtable_decl ) {
325 SemanticError( decl, context_error );
326 }
327 // Should be safe because of isMainFor.
328 const ast::StructInstType * struct_type =
329 static_cast<const ast::StructInstType *>(
330 static_cast<const ast::ReferenceType *>(
331 param->get_type() )->base.get() );
332
333 handleMain( decl, struct_type );
334 }
335 }
336 return decl;
337}
338
339const ast::Expr * ConcurrentSueKeyword::postvisit(
340 const ast::KeywordCastExpr * expr ) {
341 if ( cast_target == expr->target ) {
342 // Convert `(thread &)ex` to `(thread$ &)*get_thread(ex)`, etc.
343 if ( !type_decl || !dtor_decl ) {
344 SemanticError( expr, context_error );
345 }
346 assert( nullptr == expr->result );
347 auto cast = ast::mutate( expr );
348 cast->result = new ast::ReferenceType( new ast::StructInstType( type_decl ) );
349 cast->concrete_target.field = field_name;
350 cast->concrete_target.getter = getter_name;
351 return cast;
352 }
353 return expr;
354}
355
356const ast::StructDecl * ConcurrentSueKeyword::handleStruct(
357 const ast::StructDecl * decl ) {
358 assert( decl->body );
359
360 if ( !type_decl || !dtor_decl ) {
361 SemanticError( decl, context_error );
362 }
363
364 if ( !exception_name.empty() ) {
[35eef3b]365 if ( !typeid_decl || !vtable_decl ) {
[4f6dda0]366 SemanticError( decl, context_error );
367 }
368 addTypeId( decl );
369 addVtableForward( decl );
370 }
371
372 const ast::FunctionDecl * func = forwardDeclare( decl );
373 StructAndField addFieldRet = addField( decl );
374 decl = addFieldRet.decl;
375 const ast::ObjectDecl * field = addFieldRet.field;
376
377 addGetRoutines( field, func );
378 // Add routines to monitors for use by mutex stmt.
379 if ( ast::AggregateDecl::Monitor == cast_target ) {
380 addLockUnlockRoutines( decl );
381 }
382
383 return decl;
384}
385
386void ConcurrentSueKeyword::handleMain(
387 const ast::FunctionDecl * decl, const ast::StructInstType * type ) {
388 assert( vtable_decl );
389 assert( except_decl );
390
391 const CodeLocation & location = decl->location;
392
393 std::vector<ast::ptr<ast::Expr>> poly_args = {
394 new ast::TypeExpr( location, type ),
395 };
396 ast::ObjectDecl * vtable_object = Virtual::makeVtableInstance(
397 location,
398 "_default_vtable_object_declaration",
399 new ast::StructInstType( vtable_decl, copy( poly_args ) ),
400 type,
401 nullptr
402 );
403 declsToAddAfter.push_back( vtable_object );
404 declsToAddAfter.push_back(
405 new ast::ObjectDecl(
406 location,
407 Virtual::concurrentDefaultVTableName(),
408 new ast::ReferenceType( vtable_object->type, ast::CV::Const ),
409 new ast::SingleInit( location,
[e8616b6]410 new ast::VariableExpr( location, vtable_object ) )
[4f6dda0]411 )
412 );
413 declsToAddAfter.push_back( Virtual::makeGetExceptionFunction(
414 location,
415 vtable_object,
416 new ast::StructInstType( except_decl, copy( poly_args ) )
417 ) );
418}
419
420void ConcurrentSueKeyword::addTypeId( const ast::StructDecl * decl ) {
421 assert( typeid_decl );
422 const CodeLocation & location = decl->location;
423
424 ast::StructInstType * typeid_type =
425 new ast::StructInstType( typeid_decl, ast::CV::Const );
426 typeid_type->params.push_back(
427 new ast::TypeExpr( location, new ast::StructInstType( decl ) ) );
428 declsToAddBefore.push_back(
429 Virtual::makeTypeIdInstance( location, typeid_type ) );
[35eef3b]430 // If the typeid_type is going to be kept, the other reference will have been made by now, but
431 // we also get to avoid extra mutates.
[4f6dda0]432 ast::ptr<ast::StructInstType> typeid_cleanup = typeid_type;
433}
434
435void ConcurrentSueKeyword::addVtableForward( const ast::StructDecl * decl ) {
436 assert( vtable_decl );
437 const CodeLocation& location = decl->location;
438
439 std::vector<ast::ptr<ast::Expr>> poly_args = {
440 new ast::TypeExpr( location, new ast::StructInstType( decl ) ),
441 };
442 declsToAddBefore.push_back( Virtual::makeGetExceptionForward(
443 location,
444 new ast::StructInstType( vtable_decl, copy( poly_args ) ),
445 new ast::StructInstType( except_decl, copy( poly_args ) )
446 ) );
447 ast::ObjectDecl * vtable_object = Virtual::makeVtableForward(
448 location,
449 "_default_vtable_object_declaration",
450 new ast::StructInstType( vtable_decl, std::move( poly_args ) )
451 );
452 declsToAddBefore.push_back( vtable_object );
453 declsToAddBefore.push_back(
454 new ast::ObjectDecl(
455 location,
456 Virtual::concurrentDefaultVTableName(),
457 new ast::ReferenceType( vtable_object->type, ast::CV::Const ),
458 nullptr,
459 ast::Storage::Extern,
460 ast::Linkage::Cforall
461 )
462 );
463}
464
465const ast::FunctionDecl * ConcurrentSueKeyword::forwardDeclare(
466 const ast::StructDecl * decl ) {
467 const CodeLocation & location = decl->location;
468
469 ast::StructDecl * forward = ast::deepCopy( decl );
470 {
471 // If removing members makes ref-count go to zero, do not free.
472 ast::ptr<ast::StructDecl> forward_ptr = forward;
473 forward->body = false;
474 forward->members.clear();
475 forward_ptr.release();
476 }
477
478 ast::ObjectDecl * this_decl = new ast::ObjectDecl(
479 location,
480 "this",
[e8616b6]481 new ast::ReferenceType( new ast::StructInstType( decl ) )
[4f6dda0]482 );
483
484 ast::ObjectDecl * ret_decl = new ast::ObjectDecl(
485 location,
486 "ret",
[e8616b6]487 new ast::PointerType( new ast::StructInstType( type_decl ) )
[4f6dda0]488 );
489
490 ast::FunctionDecl * get_decl = new ast::FunctionDecl(
491 location,
492 getter_name,
493 { this_decl }, // params
494 { ret_decl }, // returns
495 nullptr, // stmts
496 ast::Storage::Static,
497 ast::Linkage::Cforall,
498 { new ast::Attribute( "const" ) },
499 ast::Function::Inline
500 );
501 get_decl = fixupGenerics( get_decl, decl );
502
503 ast::FunctionDecl * main_decl = nullptr;
504 if ( needs_main ) {
505 // `this_decl` is copied here because the original was used above.
506 main_decl = new ast::FunctionDecl(
507 location,
508 "main",
509 { ast::deepCopy( this_decl ) },
510 {},
511 nullptr,
512 ast::Storage::Classes(),
513 ast::Linkage::Cforall
514 );
515 main_decl = fixupGenerics( main_decl, decl );
516 }
517
518 declsToAddBefore.push_back( forward );
519 if ( needs_main ) declsToAddBefore.push_back( main_decl );
520 declsToAddBefore.push_back( get_decl );
521
522 return get_decl;
523}
524
525ConcurrentSueKeyword::StructAndField ConcurrentSueKeyword::addField(
526 const ast::StructDecl * decl ) {
527 const CodeLocation & location = decl->location;
528
529 ast::ObjectDecl * field = new ast::ObjectDecl(
530 location,
531 field_name,
[e8616b6]532 new ast::StructInstType( type_decl )
[4f6dda0]533 );
534
535 auto mutDecl = ast::mutate( decl );
[35eef3b]536 // Insert at start of special aggregate structures => front of vector
[c3d0182a]537 mutDecl->members.insert( mutDecl->members.begin(), field );
[4f6dda0]538
539 return {mutDecl, field};
540}
541
542void ConcurrentSueKeyword::addGetRoutines(
543 const ast::ObjectDecl * field, const ast::FunctionDecl * forward ) {
[b2ecd48]544 // Clone the signature and then build the body.
545 ast::FunctionDecl * decl = ast::deepCopy( forward );
546
[4f6dda0]547 // Say it is generated at the "same" places as the forward declaration.
[b2ecd48]548 const CodeLocation & location = decl->location;
[4f6dda0]549
[b2ecd48]550 const ast::DeclWithType * param = decl->params.front();
[4f6dda0]551 ast::Stmt * stmt = new ast::ReturnStmt( location,
552 new ast::AddressExpr( location,
553 new ast::MemberExpr( location,
554 field,
555 new ast::CastExpr( location,
556 new ast::VariableExpr( location, param ),
557 ast::deepCopy( param->get_type()->stripReferences() ),
558 ast::ExplicitCast
559 )
560 )
561 )
562 );
563
564 decl->stmts = new ast::CompoundStmt( location, { stmt } );
565 declsToAddAfter.push_back( decl );
566}
567
568void ConcurrentSueKeyword::addLockUnlockRoutines(
569 const ast::StructDecl * decl ) {
570 // This should only be used on monitors.
571 assert( ast::AggregateDecl::Monitor == cast_target );
572
573 const CodeLocation & location = decl->location;
574
575 // The parameter for both routines.
576 ast::ObjectDecl * this_decl = new ast::ObjectDecl(
577 location,
578 "this",
[e8616b6]579 new ast::ReferenceType( new ast::StructInstType( decl ) )
[4f6dda0]580 );
581
582 ast::FunctionDecl * lock_decl = new ast::FunctionDecl(
583 location,
584 "lock",
585 {
586 // Copy the declaration of this.
587 ast::deepCopy( this_decl ),
588 },
589 { /* returns */ },
590 nullptr,
591 ast::Storage::Static,
592 ast::Linkage::Cforall,
593 { /* attributes */ },
594 ast::Function::Inline
595 );
596 lock_decl = fixupGenerics( lock_decl, decl );
597
598 lock_decl->stmts = new ast::CompoundStmt( location, {
599 new ast::ExprStmt( location,
600 new ast::UntypedExpr( location,
601 new ast::NameExpr( location, "lock" ),
602 {
603 new ast::UntypedExpr( location,
604 new ast::NameExpr( location, "get_monitor" ),
605 { new ast::VariableExpr( location,
606 InitTweak::getParamThis( lock_decl ) ) }
607 )
608 }
609 )
610 )
611 } );
612
613 ast::FunctionDecl * unlock_decl = new ast::FunctionDecl(
614 location,
615 "unlock",
616 {
617 // Last use, consume the declaration of this.
618 this_decl,
619 },
620 { /* returns */ },
621 nullptr,
622 ast::Storage::Static,
623 ast::Linkage::Cforall,
624 { /* attributes */ },
625 ast::Function::Inline
626 );
627 unlock_decl = fixupGenerics( unlock_decl, decl );
628
629 unlock_decl->stmts = new ast::CompoundStmt( location, {
630 new ast::ExprStmt( location,
631 new ast::UntypedExpr( location,
632 new ast::NameExpr( location, "unlock" ),
633 {
634 new ast::UntypedExpr( location,
635 new ast::NameExpr( location, "get_monitor" ),
636 { new ast::VariableExpr( location,
637 InitTweak::getParamThis( unlock_decl ) ) }
638 )
639 }
640 )
641 )
642 } );
643
644 declsToAddAfter.push_back( lock_decl );
645 declsToAddAfter.push_back( unlock_decl );
646}
647
648
649// --------------------------------------------------------------------------
650struct SuspendKeyword final :
[ed96731]651 public ast::WithStmtsToAdd, public ast::WithGuards {
[4f6dda0]652 SuspendKeyword() = default;
653 virtual ~SuspendKeyword() = default;
654
655 void previsit( const ast::FunctionDecl * );
656 const ast::DeclWithType * postvisit( const ast::FunctionDecl * );
657 const ast::Stmt * postvisit( const ast::SuspendStmt * );
658
659private:
660 bool is_real_suspend( const ast::FunctionDecl * );
661
662 const ast::Stmt * make_generator_suspend( const ast::SuspendStmt * );
663 const ast::Stmt * make_coroutine_suspend( const ast::SuspendStmt * );
664
665 struct LabelPair {
666 ast::Label obj;
667 int idx;
668 };
669
670 LabelPair make_label(const ast::Stmt * stmt ) {
671 labels.push_back( ControlStruct::newLabel( "generator", stmt ) );
672 return { labels.back(), int(labels.size()) };
673 }
674
675 const ast::DeclWithType * in_generator = nullptr;
676 const ast::FunctionDecl * decl_suspend = nullptr;
677 std::vector<ast::Label> labels;
678};
679
680void SuspendKeyword::previsit( const ast::FunctionDecl * decl ) {
681 GuardValue( in_generator ); in_generator = nullptr;
682
683 // If it is the real suspend, grab it if we don't have one already.
684 if ( is_real_suspend( decl ) ) {
685 decl_suspend = decl_suspend ? decl_suspend : decl;
686 return;
687 }
688
689 // Otherwise check if this is a generator main and, if so, handle it.
690 auto param = isMainFor( decl, ast::AggregateDecl::Generator );
691 if ( !param ) return;
692
693 if ( 0 != decl->returns.size() ) {
[ca9d65e]694 SemanticError( decl->location, "Generator main must return void." );
[4f6dda0]695 }
696
697 in_generator = param;
698 GuardValue( labels ); labels.clear();
699}
700
701const ast::DeclWithType * SuspendKeyword::postvisit(
702 const ast::FunctionDecl * decl ) {
703 // Only modify a full definition of a generator with states.
704 if ( !decl->stmts || !in_generator || labels.empty() ) return decl;
705
706 const CodeLocation & location = decl->location;
707
708 // Create a new function body:
709 // static void * __generator_labels[] = {&&s0, &&s1, ...};
710 // void * __generator_label = __generator_labels[GEN.__generator_state];
711 // goto * __generator_label;
712 // s0: ;
713 // OLD_BODY
714
715 // This is the null statement inserted right before the body.
716 ast::NullStmt * noop = new ast::NullStmt( location );
717 noop->labels.push_back( ControlStruct::newLabel( "generator", noop ) );
718 const ast::Label & first_label = noop->labels.back();
719
720 // Add each label to the init, starting with the first label.
721 std::vector<ast::ptr<ast::Init>> inits = {
722 new ast::SingleInit( location,
723 new ast::LabelAddressExpr( location, copy( first_label ) ) ) };
724 // Then go through all the stored labels, and clear the store.
725 for ( auto && label : labels ) {
726 inits.push_back( new ast::SingleInit( label.location,
727 new ast::LabelAddressExpr( label.location, std::move( label )
728 ) ) );
729 }
730 labels.clear();
731 // Then construct the initializer itself.
732 auto init = new ast::ListInit( location, std::move( inits ) );
733
734 ast::ObjectDecl * generatorLabels = new ast::ObjectDecl(
735 location,
736 "__generator_labels",
737 new ast::ArrayType(
738 new ast::PointerType( new ast::VoidType() ),
739 nullptr,
740 ast::FixedLen,
741 ast::DynamicDim
742 ),
743 init,
744 ast::Storage::Classes(),
745 ast::Linkage::AutoGen
746 );
747
748 ast::ObjectDecl * generatorLabel = new ast::ObjectDecl(
749 location,
750 "__generator_label",
751 new ast::PointerType( new ast::VoidType() ),
752 new ast::SingleInit( location,
753 new ast::UntypedExpr( location,
754 new ast::NameExpr( location, "?[?]" ),
755 {
756 // TODO: Could be a variable expr.
757 new ast::NameExpr( location, "__generator_labels" ),
758 new ast::UntypedMemberExpr( location,
759 new ast::NameExpr( location, "__generator_state" ),
760 new ast::VariableExpr( location, in_generator )
761 )
762 }
763 )
764 ),
765 ast::Storage::Classes(),
766 ast::Linkage::AutoGen
767 );
768
769 ast::BranchStmt * theGoTo = new ast::BranchStmt(
770 location, new ast::VariableExpr( location, generatorLabel )
771 );
772
773 // The noop goes here in order.
774
775 ast::CompoundStmt * body = new ast::CompoundStmt( location, {
776 { new ast::DeclStmt( location, generatorLabels ) },
777 { new ast::DeclStmt( location, generatorLabel ) },
778 { theGoTo },
779 { noop },
780 { decl->stmts },
781 } );
782
783 auto mutDecl = ast::mutate( decl );
784 mutDecl->stmts = body;
785 return mutDecl;
786}
787
788const ast::Stmt * SuspendKeyword::postvisit( const ast::SuspendStmt * stmt ) {
[835d6e8]789 switch ( stmt->kind ) {
[4f6dda0]790 case ast::SuspendStmt::None:
791 // Use the context to determain the implicit target.
792 if ( in_generator ) {
793 return make_generator_suspend( stmt );
794 } else {
795 return make_coroutine_suspend( stmt );
796 }
797 case ast::SuspendStmt::Coroutine:
798 return make_coroutine_suspend( stmt );
799 case ast::SuspendStmt::Generator:
800 // Generator suspends must be directly in a generator.
[ca9d65e]801 if ( !in_generator ) SemanticError( stmt->location, "\"suspend generator\" must be used inside main of generator type." );
[4f6dda0]802 return make_generator_suspend( stmt );
803 }
804 assert( false );
805 return stmt;
806}
807
808/// Find the real/official suspend declaration.
809bool SuspendKeyword::is_real_suspend( const ast::FunctionDecl * decl ) {
810 return ( !decl->linkage.is_mangled
811 && 0 == decl->params.size()
812 && 0 == decl->returns.size()
813 && "__cfactx_suspend" == decl->name );
814}
815
816const ast::Stmt * SuspendKeyword::make_generator_suspend(
817 const ast::SuspendStmt * stmt ) {
818 assert( in_generator );
819 // Target code is:
820 // GEN.__generator_state = X;
821 // THEN
822 // return;
823 // __gen_X:;
824
825 const CodeLocation & location = stmt->location;
826
827 LabelPair label = make_label( stmt );
828
829 // This is the context saving statement.
830 stmtsToAddBefore.push_back( new ast::ExprStmt( location,
831 new ast::UntypedExpr( location,
832 new ast::NameExpr( location, "?=?" ),
833 {
834 new ast::UntypedMemberExpr( location,
835 new ast::NameExpr( location, "__generator_state" ),
836 new ast::VariableExpr( location, in_generator )
837 ),
838 ast::ConstantExpr::from_int( location, label.idx ),
839 }
840 )
841 ) );
842
843 // The THEN component is conditional (return is not).
844 if ( stmt->then ) {
845 stmtsToAddBefore.push_back( stmt->then.get() );
846 }
847 stmtsToAddBefore.push_back( new ast::ReturnStmt( location, nullptr ) );
848
849 // The null statement replaces the old suspend statement.
850 return new ast::NullStmt( location, { label.obj } );
851}
852
853const ast::Stmt * SuspendKeyword::make_coroutine_suspend(
854 const ast::SuspendStmt * stmt ) {
855 // The only thing we need from the old statement is the location.
856 const CodeLocation & location = stmt->location;
857
858 if ( !decl_suspend ) {
[ca9d65e]859 SemanticError( location, "suspend keyword applied to coroutines requires coroutines to be in scope, add #include <coroutine.hfa>." );
[4f6dda0]860 }
861 if ( stmt->then ) {
862 SemanticError( location, "Compound statement following coroutines is not implemented." );
863 }
864
865 return new ast::ExprStmt( location,
866 new ast::UntypedExpr( location,
867 ast::VariableExpr::functionPointer( location, decl_suspend ) )
868 );
869}
870
[2cf3b87]871// --------------------------------------------------------------------------
[ed96731]872struct MutexKeyword final : public ast::WithDeclsToAdd {
[2cf3b87]873 const ast::FunctionDecl * postvisit( const ast::FunctionDecl * decl );
874 void postvisit( const ast::StructDecl * decl );
875 const ast::Stmt * postvisit( const ast::MutexStmt * stmt );
876
[56f519b]877 static std::vector<const ast::DeclWithType *> findMutexArgs(
[2cf3b87]878 const ast::FunctionDecl * decl, bool & first );
[56f519b]879 static void validate( const ast::DeclWithType * decl );
880
[2cf3b87]881 ast::CompoundStmt * addDtorStatements( const ast::FunctionDecl* func, const ast::CompoundStmt *, const std::vector<const ast::DeclWithType *> &);
882 ast::CompoundStmt * addStatements( const ast::FunctionDecl* func, const ast::CompoundStmt *, const std::vector<const ast::DeclWithType *> &);
883 ast::CompoundStmt * addStatements( const ast::CompoundStmt * body, const std::vector<ast::ptr<ast::Expr>> & args );
884 ast::CompoundStmt * addThreadDtorStatements( const ast::FunctionDecl* func, const ast::CompoundStmt * body, const std::vector<const ast::DeclWithType *> & args );
[080d2d7]885 ast::ExprStmt * genVirtLockUnlockExpr( const std::string & fnName, ast::ptr<ast::Expr> expr, const CodeLocation & location, ast::Expr * param);
886 ast::IfStmt * genTypeDiscrimLockUnlock( const std::string & fnName, const std::vector<ast::ptr<ast::Expr>> & args, const CodeLocation & location, ast::UntypedExpr * thisParam );
[56f519b]887private:
[2cf3b87]888 const ast::StructDecl * monitor_decl = nullptr;
889 const ast::StructDecl * guard_decl = nullptr;
890 const ast::StructDecl * dtor_guard_decl = nullptr;
891 const ast::StructDecl * thread_guard_decl = nullptr;
892 const ast::StructDecl * lock_guard_decl = nullptr;
893
894 static ast::ptr<ast::Type> generic_func;
[080d2d7]895
896 UniqueName mutex_func_namer = UniqueName("__lock_unlock_curr");
[2cf3b87]897};
898
899const ast::FunctionDecl * MutexKeyword::postvisit(
900 const ast::FunctionDecl * decl ) {
901 bool is_first_argument_mutex = false;
902 const std::vector<const ast::DeclWithType *> mutexArgs =
903 findMutexArgs( decl, is_first_argument_mutex );
904 bool const isDtor = CodeGen::isDestructor( decl->name );
905
906 // Does this function have any mutex arguments that connect to monitors?
907 if ( mutexArgs.empty() ) {
908 // If this is the destructor for a monitor it must be mutex.
909 if ( isDtor ) {
910 // This reflects MutexKeyword::validate, but no error messages.
911 const ast::Type * type = decl->type->params.front();
912
913 // If it's a copy, it's not a mutex.
914 const ast::ReferenceType * refType = dynamic_cast<const ast::ReferenceType *>( type );
915 if ( nullptr == refType ) {
916 return decl;
917 }
918
919 // If it is not pointing directly to a type, it's not a mutex.
920 auto base = refType->base;
921 if ( base.as<ast::ReferenceType>() ) return decl;
922 if ( base.as<ast::PointerType>() ) return decl;
923
924 // If it is not a struct, it's not a mutex.
925 auto baseStruct = base.as<ast::StructInstType>();
926 if ( nullptr == baseStruct ) return decl;
927
928 // If it is a monitor, then it is a monitor.
[35eef3b]929 if ( baseStruct->base->is_monitor() || baseStruct->base->is_thread() ) {
[ca9d65e]930 SemanticError( decl, "destructors for structures declared as \"monitor\" must use mutex parameters " );
[2cf3b87]931 }
932 }
933 return decl;
934 }
935
936 // Monitors can't be constructed with mutual exclusion.
[56f519b]937 if ( CodeGen::isConstructor( decl->name ) && is_first_argument_mutex ) {
[ca9d65e]938 SemanticError( decl, "constructors cannot have mutex parameters " );
[2cf3b87]939 }
940
941 // It makes no sense to have multiple mutex parameters for the destructor.
942 if ( isDtor && mutexArgs.size() != 1 ) {
[ca9d65e]943 SemanticError( decl, "destructors can only have 1 mutex argument " );
[2cf3b87]944 }
945
946 // Make sure all the mutex arguments are monitors.
947 for ( auto arg : mutexArgs ) {
948 validate( arg );
949 }
950
951 // Check to see if the body needs to be instrument the body.
952 const ast::CompoundStmt * body = decl->stmts;
953 if ( !body ) return decl;
954
955 // Check to if the required headers have been seen.
956 if ( !monitor_decl || !guard_decl || !dtor_guard_decl ) {
[ca9d65e]957 SemanticError( decl, "mutex keyword requires monitors to be in scope, add #include <monitor.hfa>." );
[2cf3b87]958 }
959
960 // Instrument the body.
961 ast::CompoundStmt * newBody = nullptr;
962 if ( isDtor && isThread( mutexArgs.front() ) ) {
963 if ( !thread_guard_decl ) {
[ca9d65e]964 SemanticError( decl, "thread destructor requires threads to be in scope, add #include <thread.hfa>." );
[2cf3b87]965 }
966 newBody = addThreadDtorStatements( decl, body, mutexArgs );
967 } else if ( isDtor ) {
968 newBody = addDtorStatements( decl, body, mutexArgs );
969 } else {
970 newBody = addStatements( decl, body, mutexArgs );
971 }
972 assert( newBody );
973 return ast::mutate_field( decl, &ast::FunctionDecl::stmts, newBody );
974}
975
976void MutexKeyword::postvisit( const ast::StructDecl * decl ) {
977 if ( !decl->body ) {
978 return;
979 } else if ( decl->name == "monitor$" ) {
980 assert( !monitor_decl );
981 monitor_decl = decl;
982 } else if ( decl->name == "monitor_guard_t" ) {
983 assert( !guard_decl );
984 guard_decl = decl;
985 } else if ( decl->name == "monitor_dtor_guard_t" ) {
986 assert( !dtor_guard_decl );
987 dtor_guard_decl = decl;
988 } else if ( decl->name == "thread_dtor_guard_t" ) {
989 assert( !thread_guard_decl );
990 thread_guard_decl = decl;
991 } else if ( decl->name == "__mutex_stmt_lock_guard" ) {
992 assert( !lock_guard_decl );
993 lock_guard_decl = decl;
994 }
995}
996
997const ast::Stmt * MutexKeyword::postvisit( const ast::MutexStmt * stmt ) {
[41d3c8d]998 if ( !lock_guard_decl ) {
[ca9d65e]999 SemanticError( stmt->location, "mutex stmt requires a header, add #include <mutex_stmt.hfa>." );
[41d3c8d]1000 }
[2cf3b87]1001 ast::CompoundStmt * body =
1002 new ast::CompoundStmt( stmt->location, { stmt->stmt } );
[fc1a3e2]1003
[080d2d7]1004 return addStatements( body, stmt->mutexObjs );;
[2cf3b87]1005}
1006
1007std::vector<const ast::DeclWithType *> MutexKeyword::findMutexArgs(
1008 const ast::FunctionDecl * decl, bool & first ) {
1009 std::vector<const ast::DeclWithType *> mutexArgs;
1010
1011 bool once = true;
1012 for ( auto arg : decl->params ) {
1013 const ast::Type * type = arg->get_type();
[56f519b]1014 if ( type->is_mutex() ) {
1015 if ( once ) first = true;
1016 mutexArgs.push_back( arg.get() );
[2cf3b87]1017 }
[56f519b]1018 once = false;
[2cf3b87]1019 }
1020 return mutexArgs;
1021}
1022
1023void MutexKeyword::validate( const ast::DeclWithType * decl ) {
1024 const ast::Type * type = decl->get_type();
1025
1026 // If it's a copy, it's not a mutex.
1027 const ast::ReferenceType * refType = dynamic_cast<const ast::ReferenceType *>( type );
1028 if ( nullptr == refType ) {
1029 SemanticError( decl, "Mutex argument must be of reference type " );
1030 }
1031
1032 // If it is not pointing directly to a type, it's not a mutex.
1033 auto base = refType->base;
1034 if ( base.as<ast::ReferenceType>() || base.as<ast::PointerType>() ) {
1035 SemanticError( decl, "Mutex argument have exactly one level of indirection " );
1036 }
1037
1038 // If it is not a struct, it's not a mutex.
1039 auto baseStruct = base.as<ast::StructInstType>();
1040 if ( nullptr == baseStruct ) return;
1041
1042 // Make sure that only the outer reference is mutex.
[35eef3b]1043 if ( baseStruct->is_mutex() ) {
[2cf3b87]1044 SemanticError( decl, "mutex keyword may only appear once per argument " );
1045 }
1046}
1047
1048ast::CompoundStmt * MutexKeyword::addDtorStatements(
1049 const ast::FunctionDecl* func, const ast::CompoundStmt * body,
1050 const std::vector<const ast::DeclWithType *> & args ) {
1051 ast::Type * argType = ast::shallowCopy( args.front()->get_type() );
1052 argType->set_mutex( false );
1053
1054 ast::CompoundStmt * mutBody = ast::mutate( body );
1055
1056 // Generated code goes near the beginning of body:
1057 const CodeLocation & location = mutBody->location;
1058
1059 const ast::ObjectDecl * monitor = new ast::ObjectDecl(
1060 location,
1061 "__monitor",
1062 new ast::PointerType( new ast::StructInstType( monitor_decl ) ),
1063 new ast::SingleInit(
1064 location,
1065 new ast::UntypedExpr(
1066 location,
1067 new ast::NameExpr( location, "get_monitor" ),
1068 { new ast::CastExpr(
1069 location,
1070 new ast::VariableExpr( location, args.front() ),
1071 argType, ast::ExplicitCast
1072 ) }
1073 )
[e8616b6]1074 )
[2cf3b87]1075 );
1076
1077 assert( generic_func );
1078
1079 // In reverse order:
[c9d36b1]1080 // monitor_dtor_guard_t __guard = { __monitor, func, func_id, false };
[2cf3b87]1081 mutBody->push_front(
1082 new ast::DeclStmt( location, new ast::ObjectDecl(
1083 location,
1084 "__guard",
1085 new ast::StructInstType( dtor_guard_decl ),
1086 new ast::ListInit(
1087 location,
1088 {
1089 new ast::SingleInit( location,
[4f6dda0]1090 new ast::AddressExpr( location,
[2cf3b87]1091 new ast::VariableExpr( location, monitor ) ) ),
1092 new ast::SingleInit( location,
1093 new ast::CastExpr( location,
1094 new ast::VariableExpr( location, func ),
1095 generic_func,
1096 ast::ExplicitCast ) ),
[c9d36b1]1097 new ast::SingleInit( location,
1098 Concurrency::hashMangleExpr( location, func ) ),
[2cf3b87]1099 new ast::SingleInit( location,
1100 ast::ConstantExpr::from_bool( location, false ) ),
1101 },
1102 {},
1103 ast::MaybeConstruct
[e8616b6]1104 )
[2cf3b87]1105 ))
1106 );
1107
1108 // monitor$ * __monitor = get_monitor(a);
1109 mutBody->push_front( new ast::DeclStmt( location, monitor ) );
1110
1111 return mutBody;
1112}
1113
1114ast::CompoundStmt * MutexKeyword::addStatements(
1115 const ast::FunctionDecl* func, const ast::CompoundStmt * body,
1116 const std::vector<const ast::DeclWithType * > & args ) {
1117 ast::CompoundStmt * mutBody = ast::mutate( body );
1118
1119 // Code is generated near the beginning of the compound statement.
1120 const CodeLocation & location = mutBody->location;
1121
1122 // Make pointer to the monitors.
1123 ast::ObjectDecl * monitors = new ast::ObjectDecl(
1124 location,
1125 "__monitors",
1126 new ast::ArrayType(
1127 new ast::PointerType(
1128 new ast::StructInstType( monitor_decl )
1129 ),
1130 ast::ConstantExpr::from_ulong( location, args.size() ),
1131 ast::FixedLen,
1132 ast::DynamicDim
1133 ),
1134 new ast::ListInit(
1135 location,
1136 map_range<std::vector<ast::ptr<ast::Init>>>(
1137 args,
1138 []( const ast::DeclWithType * decl ) {
1139 return new ast::SingleInit(
1140 decl->location,
1141 new ast::UntypedExpr(
1142 decl->location,
1143 new ast::NameExpr( decl->location, "get_monitor" ),
1144 {
1145 new ast::CastExpr(
1146 decl->location,
1147 new ast::VariableExpr( decl->location, decl ),
1148 decl->get_type(),
1149 ast::ExplicitCast
1150 )
1151 }
1152 )
1153 );
1154 }
1155 )
[e8616b6]1156 )
[2cf3b87]1157 );
1158
1159 assert( generic_func );
1160
1161 // In Reverse Order:
1162 mutBody->push_front(
1163 new ast::DeclStmt( location, new ast::ObjectDecl(
1164 location,
1165 "__guard",
1166 new ast::StructInstType( guard_decl ),
1167 new ast::ListInit(
1168 location,
1169 {
1170 new ast::SingleInit( location,
1171 new ast::VariableExpr( location, monitors ) ),
1172 new ast::SingleInit( location,
1173 ast::ConstantExpr::from_ulong( location, args.size() ) ),
1174 new ast::SingleInit( location, new ast::CastExpr(
1175 location,
1176 new ast::VariableExpr( location, func ),
1177 generic_func,
1178 ast::ExplicitCast
1179 ) ),
[c9d36b1]1180 new ast::SingleInit( location,
1181 Concurrency::hashMangleExpr( location, func ) ),
[2cf3b87]1182 },
1183 {},
1184 ast::MaybeConstruct
[e8616b6]1185 )
[2cf3b87]1186 ))
1187 );
1188
1189 // monitor$ * __monitors[] = { get_monitor(a), get_monitor(b) };
1190 mutBody->push_front( new ast::DeclStmt( location, monitors ) );
1191
1192 return mutBody;
1193}
1194
[35eef3b]1195// Generates a cast to the void ptr to the appropriate lock type and dereferences it before calling
1196// lock or unlock on it used to undo the type erasure done by storing all the lock pointers as void.
[080d2d7]1197ast::ExprStmt * MutexKeyword::genVirtLockUnlockExpr( const std::string & fnName, ast::ptr<ast::Expr> expr, const CodeLocation & location, ast::Expr * param ) {
1198 return new ast::ExprStmt( location,
1199 new ast::UntypedExpr( location,
1200 new ast::NameExpr( location, fnName ), {
1201 ast::UntypedExpr::createDeref(
1202 location,
[fc1a3e2]1203 new ast::CastExpr( location,
[080d2d7]1204 param,
1205 new ast::PointerType( new ast::TypeofType( new ast::UntypedExpr(
1206 expr->location,
1207 new ast::NameExpr( expr->location, "__get_mutexstmt_lock_type" ),
1208 { expr }
1209 ) ) ),
1210 ast::GeneratedFlag::ExplicitCast
1211 )
1212 )
1213 }
1214 )
1215 );
1216}
1217
1218ast::IfStmt * MutexKeyword::genTypeDiscrimLockUnlock( const std::string & fnName, const std::vector<ast::ptr<ast::Expr>> & args, const CodeLocation & location, ast::UntypedExpr * thisParam ) {
1219 ast::IfStmt * outerLockIf = nullptr;
1220 ast::IfStmt * lastLockIf = nullptr;
1221
1222 //adds an if/elif clause for each lock to assign type from void ptr based on ptr address
1223 for ( long unsigned int i = 0; i < args.size(); i++ ) {
[fc1a3e2]1224
[080d2d7]1225 ast::UntypedExpr * ifCond = new ast::UntypedExpr( location,
1226 new ast::NameExpr( location, "?==?" ), {
1227 ast::deepCopy( thisParam ),
1228 new ast::CastExpr( location, new ast::AddressExpr( location, args.at(i) ), new ast::PointerType( new ast::VoidType() ))
1229 }
1230 );
1231
[fc1a3e2]1232 ast::IfStmt * currLockIf = new ast::IfStmt(
[080d2d7]1233 location,
1234 ifCond,
1235 genVirtLockUnlockExpr( fnName, args.at(i), location, ast::deepCopy( thisParam ) )
1236 );
[fc1a3e2]1237
[080d2d7]1238 if ( i == 0 ) {
1239 outerLockIf = currLockIf;
1240 } else {
1241 // add ifstmt to else of previous stmt
1242 lastLockIf->else_ = currLockIf;
1243 }
1244
1245 lastLockIf = currLockIf;
1246 }
1247 return outerLockIf;
1248}
1249
[d464b79]1250void flattenTuple( const ast::UntypedTupleExpr * tuple, std::vector<ast::ptr<ast::Expr>> & output ) {
[fc1a3e2]1251 for ( auto & expr : tuple->exprs ) {
1252 const ast::UntypedTupleExpr * innerTuple = dynamic_cast<const ast::UntypedTupleExpr *>(expr.get());
1253 if ( innerTuple ) flattenTuple( innerTuple, output );
1254 else output.emplace_back( ast::deepCopy( expr ));
1255 }
[d464b79]1256}
1257
[2cf3b87]1258ast::CompoundStmt * MutexKeyword::addStatements(
1259 const ast::CompoundStmt * body,
1260 const std::vector<ast::ptr<ast::Expr>> & args ) {
1261
1262 // Code is generated near the beginning of the compound statement.
[080d2d7]1263 const CodeLocation & location = body->location;
1264
1265 // final body to return
1266 ast::CompoundStmt * newBody = new ast::CompoundStmt( location );
1267
1268 // std::string lockFnName = mutex_func_namer.newName();
1269 // std::string unlockFnName = mutex_func_namer.newName();
[2cf3b87]1270
[fc1a3e2]1271 // If any arguments to the mutex stmt are tuples, flatten them
1272 std::vector<ast::ptr<ast::Expr>> flattenedArgs;
1273 for ( auto & arg : args ) {
1274 const ast::UntypedTupleExpr * tuple = dynamic_cast<const ast::UntypedTupleExpr *>(args.at(0).get());
1275 if ( tuple ) flattenTuple( tuple, flattenedArgs );
1276 else flattenedArgs.emplace_back( ast::deepCopy( arg ));
1277 }
[d464b79]1278
[2cf3b87]1279 // Make pointer to the monitors.
1280 ast::ObjectDecl * monitors = new ast::ObjectDecl(
1281 location,
1282 "__monitors",
1283 new ast::ArrayType(
1284 new ast::PointerType(
[080d2d7]1285 new ast::VoidType()
[2cf3b87]1286 ),
[d464b79]1287 ast::ConstantExpr::from_ulong( location, flattenedArgs.size() ),
[2cf3b87]1288 ast::FixedLen,
1289 ast::DynamicDim
1290 ),
1291 new ast::ListInit(
1292 location,
1293 map_range<std::vector<ast::ptr<ast::Init>>>(
[d464b79]1294 flattenedArgs, [](const ast::Expr * expr) {
[2cf3b87]1295 return new ast::SingleInit(
1296 expr->location,
1297 new ast::UntypedExpr(
1298 expr->location,
[080d2d7]1299 new ast::NameExpr( expr->location, "__get_mutexstmt_lock_ptr" ),
[2cf3b87]1300 { expr }
1301 )
1302 );
1303 }
1304 )
[e8616b6]1305 )
[2cf3b87]1306 );
1307
1308 ast::StructInstType * lock_guard_struct =
1309 new ast::StructInstType( lock_guard_decl );
1310
[080d2d7]1311 // use try stmts to lock and finally to unlock
1312 ast::TryStmt * outerTry = nullptr;
1313 ast::TryStmt * currentTry;
1314 ast::CompoundStmt * lastBody = nullptr;
1315
1316 // adds a nested try stmt for each lock we are locking
[d464b79]1317 for ( long unsigned int i = 0; i < flattenedArgs.size(); i++ ) {
[fc1a3e2]1318 ast::UntypedExpr * innerAccess = new ast::UntypedExpr(
[080d2d7]1319 location,
1320 new ast::NameExpr( location,"?[?]" ), {
1321 new ast::NameExpr( location, "__monitors" ),
1322 ast::ConstantExpr::from_int( location, i )
1323 }
1324 );
1325
1326 // make the try body
1327 ast::CompoundStmt * currTryBody = new ast::CompoundStmt( location );
[d464b79]1328 ast::IfStmt * lockCall = genTypeDiscrimLockUnlock( "lock", flattenedArgs, location, innerAccess );
[080d2d7]1329 currTryBody->push_back( lockCall );
1330
1331 // make the finally stmt
1332 ast::CompoundStmt * currFinallyBody = new ast::CompoundStmt( location );
[d464b79]1333 ast::IfStmt * unlockCall = genTypeDiscrimLockUnlock( "unlock", flattenedArgs, location, innerAccess );
[080d2d7]1334 currFinallyBody->push_back( unlockCall );
1335
1336 // construct the current try
[400b8be]1337 currentTry = new ast::TryStmt(
1338 location,
1339 currTryBody,
1340 {},
1341 new ast::FinallyClause( location, currFinallyBody )
[080d2d7]1342 );
1343 if ( i == 0 ) outerTry = currentTry;
1344 else {
1345 // pushback try into the body of the outer try
1346 lastBody->push_back( currentTry );
1347 }
1348 lastBody = currTryBody;
1349 }
1350
1351 // push body into innermost try body
1352 if ( lastBody != nullptr ) {
1353 lastBody->push_back( body );
1354 newBody->push_front( outerTry );
[400b8be]1355 }
[2cf3b87]1356
1357 // monitor_guard_t __guard = { __monitors, # };
[080d2d7]1358 newBody->push_front(
[2cf3b87]1359 new ast::DeclStmt(
1360 location,
1361 new ast::ObjectDecl(
1362 location,
1363 "__guard",
1364 lock_guard_struct,
1365 new ast::ListInit(
1366 location,
1367 {
1368 new ast::SingleInit(
1369 location,
1370 new ast::VariableExpr( location, monitors ) ),
1371 new ast::SingleInit(
1372 location,
[d464b79]1373 ast::ConstantExpr::from_ulong( location, flattenedArgs.size() ) ),
[2cf3b87]1374 },
1375 {},
1376 ast::MaybeConstruct
[e8616b6]1377 )
[2cf3b87]1378 )
1379 )
1380 );
1381
1382 // monitor$ * __monitors[] = { get_monitor(a), get_monitor(b) };
[080d2d7]1383 newBody->push_front( new ast::DeclStmt( location, monitors ) );
1384
1385 // // The parameter for both __lock_curr/__unlock_curr routines.
1386 // ast::ObjectDecl * this_decl = new ast::ObjectDecl(
1387 // location,
1388 // "this",
1389 // new ast::PointerType( new ast::VoidType() ),
1390 // nullptr,
1391 // {},
1392 // ast::Linkage::Cforall
1393 // );
1394
1395 // ast::FunctionDecl * lock_decl = new ast::FunctionDecl(
1396 // location,
1397 // lockFnName,
1398 // { /* forall */ },
1399 // {
1400 // // Copy the declaration of this.
1401 // this_decl,
1402 // },
1403 // { /* returns */ },
1404 // nullptr,
1405 // 0,
1406 // ast::Linkage::Cforall,
1407 // { /* attributes */ },
1408 // ast::Function::Inline
1409 // );
1410
1411 // ast::FunctionDecl * unlock_decl = new ast::FunctionDecl(
1412 // location,
1413 // unlockFnName,
1414 // { /* forall */ },
1415 // {
1416 // // Copy the declaration of this.
1417 // ast::deepCopy( this_decl ),
1418 // },
1419 // { /* returns */ },
1420 // nullptr,
1421 // 0,
1422 // ast::Linkage::Cforall,
1423 // { /* attributes */ },
1424 // ast::Function::Inline
1425 // );
1426
1427 // ast::IfStmt * outerLockIf = nullptr;
1428 // ast::IfStmt * outerUnlockIf = nullptr;
1429 // ast::IfStmt * lastLockIf = nullptr;
1430 // ast::IfStmt * lastUnlockIf = nullptr;
1431
1432 // //adds an if/elif clause for each lock to assign type from void ptr based on ptr address
1433 // for ( long unsigned int i = 0; i < args.size(); i++ ) {
1434 // ast::VariableExpr * thisParam = new ast::VariableExpr( location, InitTweak::getParamThis( lock_decl ) );
1435 // ast::UntypedExpr * ifCond = new ast::UntypedExpr( location,
1436 // new ast::NameExpr( location, "?==?" ), {
1437 // thisParam,
1438 // new ast::CastExpr( location, new ast::AddressExpr( location, args.at(i) ), new ast::PointerType( new ast::VoidType() ))
1439 // }
1440 // );
1441
[fc1a3e2]1442 // ast::IfStmt * currLockIf = new ast::IfStmt(
[080d2d7]1443 // location,
1444 // ast::deepCopy( ifCond ),
1445 // genVirtLockUnlockExpr( "lock", args.at(i), location, ast::deepCopy( thisParam ) )
1446 // );
1447
[fc1a3e2]1448 // ast::IfStmt * currUnlockIf = new ast::IfStmt(
[080d2d7]1449 // location,
1450 // ifCond,
1451 // genVirtLockUnlockExpr( "unlock", args.at(i), location, ast::deepCopy( thisParam ) )
1452 // );
[fc1a3e2]1453
[080d2d7]1454 // if ( i == 0 ) {
1455 // outerLockIf = currLockIf;
1456 // outerUnlockIf = currUnlockIf;
1457 // } else {
1458 // // add ifstmt to else of previous stmt
1459 // lastLockIf->else_ = currLockIf;
1460 // lastUnlockIf->else_ = currUnlockIf;
1461 // }
1462
1463 // lastLockIf = currLockIf;
1464 // lastUnlockIf = currUnlockIf;
1465 // }
[fc1a3e2]1466
[080d2d7]1467 // // add pointer typing if/elifs to body of routines
1468 // lock_decl->stmts = new ast::CompoundStmt( location, { outerLockIf } );
1469 // unlock_decl->stmts = new ast::CompoundStmt( location, { outerUnlockIf } );
1470
1471 // // add routines to scope
1472 // declsToAddBefore.push_back( lock_decl );
1473 // declsToAddBefore.push_back( unlock_decl );
1474
1475 // newBody->push_front(new ast::DeclStmt( location, lock_decl ));
1476 // newBody->push_front(new ast::DeclStmt( location, unlock_decl ));
1477
1478 return newBody;
[2cf3b87]1479}
1480
1481ast::CompoundStmt * MutexKeyword::addThreadDtorStatements(
[c9d36b1]1482 const ast::FunctionDecl* func, const ast::CompoundStmt * body,
[2cf3b87]1483 const std::vector<const ast::DeclWithType * > & args ) {
1484 assert( args.size() == 1 );
1485 const ast::DeclWithType * arg = args.front();
1486 const ast::Type * argType = arg->get_type();
1487 assert( argType->is_mutex() );
1488
1489 ast::CompoundStmt * mutBody = ast::mutate( body );
1490
1491 // The code is generated near the front of the body.
1492 const CodeLocation & location = mutBody->location;
1493
[c9d36b1]1494 // thread_dtor_guard_t __guard = { this, func_id, intptr( 0 ) };
[2cf3b87]1495 mutBody->push_front( new ast::DeclStmt(
1496 location,
1497 new ast::ObjectDecl(
1498 location,
1499 "__guard",
1500 new ast::StructInstType( thread_guard_decl ),
1501 new ast::ListInit(
1502 location,
1503 {
1504 new ast::SingleInit( location,
1505 new ast::CastExpr( location,
1506 new ast::VariableExpr( location, arg ), argType ) ),
[c9d36b1]1507 new ast::SingleInit( location,
1508 Concurrency::hashMangleExpr( location, func ) ),
[2cf3b87]1509 new ast::SingleInit(
1510 location,
1511 new ast::UntypedExpr(
1512 location,
1513 new ast::NameExpr( location, "intptr" ), {
1514 ast::ConstantExpr::from_int( location, 0 ),
1515 }
1516 ) ),
1517 },
1518 {},
1519 ast::MaybeConstruct
[e8616b6]1520 )
[2cf3b87]1521 )
1522 ));
1523
1524 return mutBody;
1525}
1526
1527ast::ptr<ast::Type> MutexKeyword::generic_func =
[db19e1d]1528 new ast::FunctionType( ast::FixedArgs );
[2cf3b87]1529
1530// --------------------------------------------------------------------------
1531struct ThreadStarter final {
1532 void previsit( const ast::StructDecl * decl );
1533 const ast::FunctionDecl * postvisit( const ast::FunctionDecl * decl );
1534
1535private:
1536 bool thread_ctor_seen = false;
1537 const ast::StructDecl * thread_decl = nullptr;
1538};
1539
1540void ThreadStarter::previsit( const ast::StructDecl * decl ) {
1541 if ( decl->body && decl->name == "thread$" ) {
1542 assert( !thread_decl );
1543 thread_decl = decl;
1544 }
1545}
1546
1547const ast::FunctionDecl * ThreadStarter::postvisit( const ast::FunctionDecl * decl ) {
1548 if ( !CodeGen::isConstructor( decl->name ) ) return decl;
1549
1550 // Seach for the thread constructor.
1551 // (Are the "prefixes" of these to blocks the same?)
1552 const ast::Type * typeof_this = InitTweak::getTypeofThis( decl->type );
1553 auto ctored_type = dynamic_cast<const ast::StructInstType *>( typeof_this );
1554 if ( ctored_type && ctored_type->base == thread_decl ) {
1555 thread_ctor_seen = true;
1556 }
1557
1558 // Modify this declaration, the extra checks to see if we will are first.
1559 const ast::ptr<ast::DeclWithType> & param = decl->params.front();
1560 auto type = dynamic_cast<const ast::StructInstType *>(
[e01eb4a]1561 ast::getPointerBase( param->get_type() ) );
[2cf3b87]1562 if ( nullptr == type ) return decl;
1563 if ( !type->base->is_thread() ) return decl;
1564 if ( !thread_decl || !thread_ctor_seen ) {
[ca9d65e]1565 SemanticError( type->base->location, "thread keyword requires threads to be in scope, add #include <thread.hfa>." );
[2cf3b87]1566 }
1567 const ast::CompoundStmt * stmt = decl->stmts;
1568 if ( nullptr == stmt ) return decl;
1569
1570 // Now do the actual modification:
1571 ast::CompoundStmt * mutStmt = ast::mutate( stmt );
1572 const CodeLocation & location = mutStmt->location;
1573 mutStmt->push_back(
1574 new ast::ExprStmt(
1575 location,
1576 new ast::UntypedExpr(
1577 location,
1578 new ast::NameExpr( location, "__thrd_start" ),
1579 {
1580 new ast::VariableExpr( location, param ),
1581 new ast::NameExpr( location, "main" ),
1582 }
1583 )
1584 )
1585 );
1586
1587 return ast::mutate_field( decl, &ast::FunctionDecl::stmts, mutStmt );
1588}
1589
1590} // namespace
1591
1592// --------------------------------------------------------------------------
[4f6dda0]1593// Interface Functions:
[2cf3b87]1594
1595void implementKeywords( ast::TranslationUnit & translationUnit ) {
[4f6dda0]1596 ast::Pass<ThreadKeyword>::run( translationUnit );
1597 ast::Pass<CoroutineKeyword>::run( translationUnit );
1598 ast::Pass<MonitorKeyword>::run( translationUnit );
1599 ast::Pass<GeneratorKeyword>::run( translationUnit );
1600 ast::Pass<SuspendKeyword>::run( translationUnit );
[2cf3b87]1601}
1602
1603void implementMutex( ast::TranslationUnit & translationUnit ) {
1604 ast::Pass<MutexKeyword>::run( translationUnit );
1605}
1606
1607void implementThreadStarter( ast::TranslationUnit & translationUnit ) {
1608 ast::Pass<ThreadStarter>::run( translationUnit );
1609}
1610
1611}
1612
1613// Local Variables: //
1614// tab-width: 4 //
1615// mode: c++ //
1616// compile-command: "make install" //
1617// End: //
Note: See TracBrowser for help on using the repository browser.