source: src/Concurrency/Keywords.cpp@ 0dffe91

Last change on this file since 0dffe91 was db19e1d, checked in by Andrew Beach <ajbeach@…>, 13 months ago

Changed the interpritation of () to be no parameters instead of any parameters. This had a lot of little changes because of this and some nearby clean-up. This includes some changes, including changing some generated functions to be fixed-args instead of variable-args, stripping out the place holder void parameter earlier, but it still shows up earlier in some cases that examine the parser directly. Also had to update the function generation tools. Have only tested with one --arch. Hopefully this all works out.

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