source: src/Concurrency/Keywords.cc@ c2a9d88

ADT ast-experimental enum forall-pointer-decay jacob/cs343-translation pthread-emulation qualifiedEnum
Last change on this file since c2a9d88 was 6cebfef, checked in by caparsons <caparson@…>, 4 years ago

added mutex stmt monitor

  • Property mode set to 100644
File size: 38.1 KB
RevLine 
[64adb03]1//
2// Cforall Version 1.0.0 Copyright (C) 2016 University of Waterloo
3//
4// The contents of this file are covered under the licence agreement in the
5// file "LICENCE" distributed with Cforall.
6//
7// Keywords.cc --
8//
9// Author : Thierry Delisle
10// Created On : Mon Mar 13 12:41:22 2017
11// Last Modified By :
12// Last Modified On :
[07de76b]13// Update Count : 10
[64adb03]14//
15
16#include "Concurrency/Keywords.h"
17
[427854b]18#include <cassert> // for assert
19#include <string> // for string, operator==
20
[1c01c58]21#include <iostream>
22
23#include "Common/Examine.h" // for isMainFor
[427854b]24#include "Common/PassVisitor.h" // for PassVisitor
25#include "Common/SemanticError.h" // for SemanticError
26#include "Common/utility.h" // for deleteAll, map_range
27#include "CodeGen/OperatorTable.h" // for isConstructor
28#include "ControlStruct/LabelGenerator.h" // for LebelGenerator
29#include "InitTweak/InitTweak.h" // for getPointerBase
30#include "SynTree/LinkageSpec.h" // for Cforall
31#include "SynTree/Constant.h" // for Constant
32#include "SynTree/Declaration.h" // for StructDecl, FunctionDecl, ObjectDecl
33#include "SynTree/Expression.h" // for VariableExpr, ConstantExpr, Untype...
34#include "SynTree/Initializer.h" // for SingleInit, ListInit, Initializer ...
35#include "SynTree/Label.h" // for Label
36#include "SynTree/Statement.h" // for CompoundStmt, DeclStmt, ExprStmt
37#include "SynTree/Type.h" // for StructInstType, Type, PointerType
38#include "SynTree/Visitor.h" // for Visitor, acceptAll
[1c01c58]39#include "Virtual/Tables.h"
[bf2438c]40
41class Attribute;
[64adb03]42
43namespace Concurrency {
[ecfd758]44 inline static std::string getTypeIdName( std::string const & exception_name ) {
45 return exception_name.empty() ? std::string() : Virtual::typeIdType( exception_name );
46 }
[1c01c58]47 inline static std::string getVTableName( std::string const & exception_name ) {
[ecfd758]48 return exception_name.empty() ? std::string() : Virtual::vtableTypeName( exception_name );
[1c01c58]49 }
50
[ab8c6a6]51 // Only detects threads constructed with the keyword thread.
52 inline static bool isThread( DeclarationWithType * decl ) {
53 Type * baseType = decl->get_type()->stripDeclarator();
54 StructInstType * instType = dynamic_cast<StructInstType *>( baseType );
55 if ( nullptr == instType ) { return false; }
56 return instType->baseStruct->is_thread();
57 }
58
[64adb03]59 //=============================================================================================
[2065609]60 // Pass declarations
[64adb03]61 //=============================================================================================
62
[bcda04c]63 //-----------------------------------------------------------------------------
64 //Handles sue type declarations :
65 // sue MyType { struct MyType {
66 // int data; int data;
67 // a_struct_t more_data; a_struct_t more_data;
68 // => NewField_t newField;
69 // }; };
70 // static inline NewField_t * getter_name( MyType * this ) { return &this->newField; }
71 //
[2065609]72 class ConcurrentSueKeyword : public WithDeclsToAdd {
[bcda04c]73 public:
74
[1c01c58]75 ConcurrentSueKeyword( std::string&& type_name, std::string&& field_name,
76 std::string&& getter_name, std::string&& context_error, std::string&& exception_name,
77 bool needs_main, AggregateDecl::Aggregate cast_target ) :
78 type_name( type_name ), field_name( field_name ), getter_name( getter_name ),
[69c5c00]79 context_error( context_error ), exception_name( exception_name ),
[ecfd758]80 typeid_name( getTypeIdName( exception_name ) ),
[69c5c00]81 vtable_name( getVTableName( exception_name ) ),
[1c01c58]82 needs_main( needs_main ), cast_target( cast_target ) {}
[bcda04c]83
84 virtual ~ConcurrentSueKeyword() {}
85
[9a705dc8]86 Declaration * postmutate( StructDecl * decl );
[6c3a5ac1]87 DeclarationWithType * postmutate( FunctionDecl * decl );
[bcda04c]88
89 void handle( StructDecl * );
[ecfd758]90 void addTypeId( StructDecl * );
[1c01c58]91 void addVtableForward( StructDecl * );
[bcda04c]92 FunctionDecl * forwardDeclare( StructDecl * );
93 ObjectDecl * addField( StructDecl * );
[2f9a722]94 void addRoutines( ObjectDecl *, FunctionDecl * );
[bcda04c]95
96 virtual bool is_target( StructDecl * decl ) = 0;
97
[9a705dc8]98 Expression * postmutate( KeywordCastExpr * cast );
99
[bcda04c]100 private:
101 const std::string type_name;
102 const std::string field_name;
103 const std::string getter_name;
104 const std::string context_error;
[69c5c00]105 const std::string exception_name;
[ecfd758]106 const std::string typeid_name;
[1c01c58]107 const std::string vtable_name;
[bd4d011]108 bool needs_main;
[312029a]109 AggregateDecl::Aggregate cast_target;
[bcda04c]110
[6c3a5ac1]111 StructDecl * type_decl = nullptr;
112 FunctionDecl * dtor_decl = nullptr;
[69c5c00]113 StructDecl * except_decl = nullptr;
[ecfd758]114 StructDecl * typeid_decl = nullptr;
[1c01c58]115 StructDecl * vtable_decl = nullptr;
[bcda04c]116 };
117
118
[64adb03]119 //-----------------------------------------------------------------------------
120 //Handles thread type declarations :
121 // thread Mythread { struct MyThread {
122 // int data; int data;
123 // a_struct_t more_data; a_struct_t more_data;
[e84ab3d]124 // => thread$ __thrd_d;
[64adb03]125 // }; };
[e84ab3d]126 // static inline thread$ * get_thread( MyThread * this ) { return &this->__thrd_d; }
[64adb03]127 //
[bcda04c]128 class ThreadKeyword final : public ConcurrentSueKeyword {
[64adb03]129 public:
130
[bcda04c]131 ThreadKeyword() : ConcurrentSueKeyword(
[e84ab3d]132 "thread$",
[bcda04c]133 "__thrd",
134 "get_thread",
[6c3a5ac1]135 "thread keyword requires threads to be in scope, add #include <thread.hfa>\n",
[ab8c6a6]136 "ThreadCancelled",
[9a705dc8]137 true,
[312029a]138 AggregateDecl::Thread
[bcda04c]139 )
140 {}
141
142 virtual ~ThreadKeyword() {}
143
144 virtual bool is_target( StructDecl * decl ) override final { return decl->is_thread(); }
145
146 static void implement( std::list< Declaration * > & translationUnit ) {
[2065609]147 PassVisitor< ThreadKeyword > impl;
[9a705dc8]148 mutateAll( translationUnit, impl );
[bcda04c]149 }
[64adb03]150 };
151
152 //-----------------------------------------------------------------------------
153 //Handles coroutine type declarations :
154 // coroutine MyCoroutine { struct MyCoroutine {
155 // int data; int data;
156 // a_struct_t more_data; a_struct_t more_data;
[e84ab3d]157 // => coroutine$ __cor_d;
[64adb03]158 // }; };
[e84ab3d]159 // static inline coroutine$ * get_coroutine( MyCoroutine * this ) { return &this->__cor_d; }
[64adb03]160 //
[bcda04c]161 class CoroutineKeyword final : public ConcurrentSueKeyword {
[64adb03]162 public:
163
[bcda04c]164 CoroutineKeyword() : ConcurrentSueKeyword(
[e84ab3d]165 "coroutine$",
[bcda04c]166 "__cor",
167 "get_coroutine",
[6c3a5ac1]168 "coroutine keyword requires coroutines to be in scope, add #include <coroutine.hfa>\n",
[1c01c58]169 "CoroutineCancelled",
[9a705dc8]170 true,
[312029a]171 AggregateDecl::Coroutine
[bcda04c]172 )
173 {}
[b32ada31]174
[bcda04c]175 virtual ~CoroutineKeyword() {}
176
177 virtual bool is_target( StructDecl * decl ) override final { return decl->is_coroutine(); }
[b32ada31]178
179 static void implement( std::list< Declaration * > & translationUnit ) {
[2065609]180 PassVisitor< CoroutineKeyword > impl;
[9a705dc8]181 mutateAll( translationUnit, impl );
[b32ada31]182 }
[64adb03]183 };
184
[427854b]185
186
[64adb03]187 //-----------------------------------------------------------------------------
188 //Handles monitor type declarations :
189 // monitor MyMonitor { struct MyMonitor {
190 // int data; int data;
191 // a_struct_t more_data; a_struct_t more_data;
[e84ab3d]192 // => monitor$ __mon_d;
[64adb03]193 // }; };
[e84ab3d]194 // static inline monitor$ * get_coroutine( MyMonitor * this ) { return &this->__cor_d; }
[64adb03]195 //
[bcda04c]196 class MonitorKeyword final : public ConcurrentSueKeyword {
[64adb03]197 public:
198
[bcda04c]199 MonitorKeyword() : ConcurrentSueKeyword(
[e84ab3d]200 "monitor$",
[bcda04c]201 "__mon",
202 "get_monitor",
[6c3a5ac1]203 "monitor keyword requires monitors to be in scope, add #include <monitor.hfa>\n",
[1c01c58]204 "",
[9a705dc8]205 false,
[312029a]206 AggregateDecl::Monitor
[bcda04c]207 )
208 {}
209
210 virtual ~MonitorKeyword() {}
211
212 virtual bool is_target( StructDecl * decl ) override final { return decl->is_monitor(); }
213
214 static void implement( std::list< Declaration * > & translationUnit ) {
[2065609]215 PassVisitor< MonitorKeyword > impl;
[9a705dc8]216 mutateAll( translationUnit, impl );
[bcda04c]217 }
[64adb03]218 };
219
[427854b]220 //-----------------------------------------------------------------------------
221 //Handles generator type declarations :
222 // generator MyGenerator { struct MyGenerator {
223 // int data; int data;
224 // a_struct_t more_data; a_struct_t more_data;
225 // => int __gen_next;
226 // }; };
227 //
228 class GeneratorKeyword final : public ConcurrentSueKeyword {
229 public:
230
231 GeneratorKeyword() : ConcurrentSueKeyword(
[e84ab3d]232 "generator$",
[427854b]233 "__generator_state",
234 "get_generator",
[e84ab3d]235 "Unable to find builtin type generator$\n",
[1c01c58]236 "",
[427854b]237 true,
238 AggregateDecl::Generator
239 )
240 {}
241
242 virtual ~GeneratorKeyword() {}
243
244 virtual bool is_target( StructDecl * decl ) override final { return decl->is_generator(); }
245
246 static void implement( std::list< Declaration * > & translationUnit ) {
247 PassVisitor< GeneratorKeyword > impl;
248 mutateAll( translationUnit, impl );
249 }
250 };
251
252
253 //-----------------------------------------------------------------------------
254 class SuspendKeyword final : public WithStmtsToAdd, public WithGuards {
255 public:
256 SuspendKeyword() = default;
257 virtual ~SuspendKeyword() = default;
258
259 void premutate( FunctionDecl * );
260 DeclarationWithType * postmutate( FunctionDecl * );
261
262 Statement * postmutate( SuspendStmt * );
263
264 static void implement( std::list< Declaration * > & translationUnit ) {
265 PassVisitor< SuspendKeyword > impl;
266 mutateAll( translationUnit, impl );
267 }
268
269 private:
270 bool is_real_suspend( FunctionDecl * );
271
272 Statement * make_generator_suspend( SuspendStmt * );
273 Statement * make_coroutine_suspend( SuspendStmt * );
274
275 struct LabelPair {
276 Label obj;
277 int idx;
278 };
279
280 LabelPair make_label() {
281 labels.push_back( gen.newLabel("generator") );
282 return { labels.back(), int(labels.size()) };
283 }
284
285 DeclarationWithType * in_generator = nullptr;
286 FunctionDecl * decl_suspend = nullptr;
287 std::vector<Label> labels;
288 ControlStruct::LabelGenerator & gen = *ControlStruct::LabelGenerator::getGenerator();
289 };
290
[64adb03]291 //-----------------------------------------------------------------------------
292 //Handles mutex routines definitions :
293 // void foo( A * mutex a, B * mutex b, int i ) { void foo( A * a, B * b, int i ) {
[e84ab3d]294 // monitor$ * __monitors[] = { get_monitor(a), get_monitor(b) };
[64adb03]295 // monitor_guard_t __guard = { __monitors, 2 };
296 // /*Some code*/ => /*Some code*/
297 // } }
298 //
[2065609]299 class MutexKeyword final {
[64adb03]300 public:
301
[2065609]302 void postvisit( FunctionDecl * decl );
303 void postvisit( StructDecl * decl );
[6cebfef]304 Statement * postmutate( MutexStmt * stmt );
[64adb03]305
[db4d8e3]306 std::list<DeclarationWithType*> findMutexArgs( FunctionDecl*, bool & first );
[64adb03]307 void validate( DeclarationWithType * );
[ab8c6a6]308 void addDtorStatements( FunctionDecl* func, CompoundStmt *, const std::list<DeclarationWithType * > &);
309 void addStatements( FunctionDecl* func, CompoundStmt *, const std::list<DeclarationWithType * > &);
[6cebfef]310 void addStatements( CompoundStmt * body, const std::list<Expression * > & args );
[ab8c6a6]311 void addThreadDtorStatements( FunctionDecl* func, CompoundStmt * body, const std::list<DeclarationWithType * > & args );
[64adb03]312
313 static void implement( std::list< Declaration * > & translationUnit ) {
[2065609]314 PassVisitor< MutexKeyword > impl;
[64adb03]315 acceptAll( translationUnit, impl );
[6cebfef]316 mutateAll( translationUnit, impl );
[64adb03]317 }
[9243cc91]318
319 private:
320 StructDecl* monitor_decl = nullptr;
[ef42b143]321 StructDecl* guard_decl = nullptr;
[549c006]322 StructDecl* dtor_guard_decl = nullptr;
[ab8c6a6]323 StructDecl* thread_guard_decl = nullptr;
[97e3296]324
325 static std::unique_ptr< Type > generic_func;
[64adb03]326 };
327
[97e3296]328 std::unique_ptr< Type > MutexKeyword::generic_func = std::unique_ptr< Type >(
329 new FunctionType(
330 noQualifiers,
331 true
332 )
333 );
334
[bd4d011]335 //-----------------------------------------------------------------------------
336 //Handles mutex routines definitions :
337 // void foo( A * mutex a, B * mutex b, int i ) { void foo( A * a, B * b, int i ) {
[e84ab3d]338 // monitor$ * __monitors[] = { get_monitor(a), get_monitor(b) };
[bd4d011]339 // monitor_guard_t __guard = { __monitors, 2 };
340 // /*Some code*/ => /*Some code*/
341 // } }
342 //
[2065609]343 class ThreadStarter final {
[bd4d011]344 public:
345
[2065609]346 void postvisit( FunctionDecl * decl );
[549c006]347 void previsit ( StructDecl * decl );
[bd4d011]348
349 void addStartStatement( FunctionDecl * decl, DeclarationWithType * param );
350
351 static void implement( std::list< Declaration * > & translationUnit ) {
[2065609]352 PassVisitor< ThreadStarter > impl;
[bd4d011]353 acceptAll( translationUnit, impl );
354 }
[549c006]355
356 private :
357 bool thread_ctor_seen = false;
358 StructDecl * thread_decl = nullptr;
[bd4d011]359 };
360
[64adb03]361 //=============================================================================================
362 // General entry routine
363 //=============================================================================================
364 void applyKeywords( std::list< Declaration * > & translationUnit ) {
365 ThreadKeyword ::implement( translationUnit );
366 CoroutineKeyword ::implement( translationUnit );
367 MonitorKeyword ::implement( translationUnit );
[427854b]368 GeneratorKeyword ::implement( translationUnit );
369 SuspendKeyword ::implement( translationUnit );
[bcda04c]370 }
371
372 void implementMutexFuncs( std::list< Declaration * > & translationUnit ) {
[64adb03]373 MutexKeyword ::implement( translationUnit );
374 }
375
[bcda04c]376 void implementThreadStarter( std::list< Declaration * > & translationUnit ) {
[bd4d011]377 ThreadStarter ::implement( translationUnit );
[bcda04c]378 }
379
[b32ada31]380 //=============================================================================================
[bcda04c]381 // Generic keyword implementation
[b32ada31]382 //=============================================================================================
[2db79e5]383 void fixupGenerics(FunctionType * func, StructDecl * decl) {
384 cloneAll(decl->parameters, func->forall);
385 for ( TypeDecl * td : func->forall ) {
386 strict_dynamic_cast<StructInstType*>(
387 func->parameters.front()->get_type()->stripReferences()
388 )->parameters.push_back(
389 new TypeExpr( new TypeInstType( noQualifiers, td->name, td ) )
390 );
391 }
392 }
393
[9a705dc8]394 Declaration * ConcurrentSueKeyword::postmutate(StructDecl * decl) {
[9f5ecf5]395 if( decl->name == type_name && decl->body ) {
[bcda04c]396 assert( !type_decl );
397 type_decl = decl;
[b32ada31]398 }
[bcda04c]399 else if ( is_target(decl) ) {
[b32ada31]400 handle( decl );
401 }
[69c5c00]402 else if ( !except_decl && exception_name == decl->name && decl->body ) {
403 except_decl = decl;
404 }
[ecfd758]405 else if ( !typeid_decl && typeid_name == decl->name && decl->body ) {
406 typeid_decl = decl;
407 }
[1c01c58]408 else if ( !vtable_decl && vtable_name == decl->name && decl->body ) {
409 vtable_decl = decl;
410 }
411 // Might be able to get ride of is target.
412 assert( is_target(decl) == (cast_target == decl->kind) );
[9a705dc8]413 return decl;
[b32ada31]414 }
415
[6c3a5ac1]416 DeclarationWithType * ConcurrentSueKeyword::postmutate( FunctionDecl * decl ) {
[1c01c58]417 if ( type_decl && isDestructorFor( decl, type_decl ) )
418 dtor_decl = decl;
[b583113]419 else if ( vtable_name.empty() || !decl->has_body() )
[43cedfb1]420 ;
[1c01c58]421 else if ( auto param = isMainFor( decl, cast_target ) ) {
422 // This should never trigger.
423 assert( vtable_decl );
424 // Should be safe because of isMainFor.
425 StructInstType * struct_type = static_cast<StructInstType *>(
426 static_cast<ReferenceType *>( param->get_type() )->base );
427 assert( struct_type );
428
[69c5c00]429 std::list< Expression * > poly_args = { new TypeExpr( struct_type->clone() ) };
430 ObjectDecl * vtable_object = Virtual::makeVtableInstance(
[b583113]431 "_default_vtable_object_declaration",
[69c5c00]432 vtable_decl->makeInst( poly_args ), struct_type, nullptr );
433 declsToAddAfter.push_back( vtable_object );
[b583113]434 declsToAddAfter.push_back(
435 new ObjectDecl(
436 Virtual::concurrentDefaultVTableName(),
[67b421c]437 noStorageClasses,
[b583113]438 LinkageSpec::Cforall,
439 /* bitfieldWidth */ nullptr,
440 new ReferenceType( Type::Const, vtable_object->type->clone() ),
441 new SingleInit( new VariableExpr( vtable_object ) )
442 )
443 );
[69c5c00]444 declsToAddAfter.push_back( Virtual::makeGetExceptionFunction(
445 vtable_object, except_decl->makeInst( std::move( poly_args ) )
446 ) );
[1c01c58]447 }
[6c3a5ac1]448
449 return decl;
450 }
451
[9a705dc8]452 Expression * ConcurrentSueKeyword::postmutate( KeywordCastExpr * cast ) {
453 if ( cast_target == cast->target ) {
[e84ab3d]454 // convert (thread &)t to (thread$ &)*get_thread(t), etc.
[9a705dc8]455 if( !type_decl ) SemanticError( cast, context_error );
[6c3a5ac1]456 if( !dtor_decl ) SemanticError( cast, context_error );
[3b0c8cb]457 assert( cast->result == nullptr );
458 cast->set_result( new ReferenceType( noQualifiers, new StructInstType( noQualifiers, type_decl ) ) );
459 cast->concrete_target.field = field_name;
460 cast->concrete_target.getter = getter_name;
[9a705dc8]461 }
462 return cast;
463 }
464
465
[bcda04c]466 void ConcurrentSueKeyword::handle( StructDecl * decl ) {
[9f5ecf5]467 if( ! decl->body ) return;
[b32ada31]468
[a16764a6]469 if( !type_decl ) SemanticError( decl, context_error );
[6c3a5ac1]470 if( !dtor_decl ) SemanticError( decl, context_error );
[b32ada31]471
[ecfd758]472 if ( !exception_name.empty() ) {
473 if( !typeid_decl ) SemanticError( decl, context_error );
474 if( !vtable_decl ) SemanticError( decl, context_error );
475
476 addTypeId( decl );
477 addVtableForward( decl );
478 }
[bcda04c]479 FunctionDecl * func = forwardDeclare( decl );
480 ObjectDecl * field = addField( decl );
[2f9a722]481 addRoutines( field, func );
[b32ada31]482 }
483
[ecfd758]484 void ConcurrentSueKeyword::addTypeId( StructDecl * decl ) {
485 assert( typeid_decl );
486 StructInstType typeid_type( Type::Const, typeid_decl );
487 typeid_type.parameters.push_back( new TypeExpr(
488 new StructInstType( noQualifiers, decl )
[69c5c00]489 ) );
[ecfd758]490 declsToAddBefore.push_back( Virtual::makeTypeIdInstance( &typeid_type ) );
491 }
492
493 void ConcurrentSueKeyword::addVtableForward( StructDecl * decl ) {
494 assert( vtable_decl );
495 std::list< Expression * > poly_args = {
496 new TypeExpr( new StructInstType( noQualifiers, decl ) ),
497 };
498 declsToAddBefore.push_back( Virtual::makeGetExceptionForward(
499 vtable_decl->makeInst( poly_args ),
500 except_decl->makeInst( poly_args )
501 ) );
[b583113]502 ObjectDecl * vtable_object = Virtual::makeVtableForward(
503 "_default_vtable_object_declaration",
504 vtable_decl->makeInst( move( poly_args ) ) );
505 declsToAddBefore.push_back( vtable_object );
[8edbe40]506 declsToAddBefore.push_back(
[b583113]507 new ObjectDecl(
508 Virtual::concurrentDefaultVTableName(),
[67b421c]509 Type::StorageClasses( Type::Extern ),
[b583113]510 LinkageSpec::Cforall,
511 /* bitfieldWidth */ nullptr,
512 new ReferenceType( Type::Const, vtable_object->type->clone() ),
513 /* init */ nullptr
514 )
515 );
[1c01c58]516 }
517
[bcda04c]518 FunctionDecl * ConcurrentSueKeyword::forwardDeclare( StructDecl * decl ) {
519
520 StructDecl * forward = decl->clone();
521 forward->set_body( false );
522 deleteAll( forward->get_members() );
523 forward->get_members().clear();
524
[bd4d011]525 FunctionType * get_type = new FunctionType( noQualifiers, false );
[bcda04c]526 ObjectDecl * this_decl = new ObjectDecl(
527 "this",
[ba3706f]528 noStorageClasses,
[b32ada31]529 LinkageSpec::Cforall,
530 nullptr,
[83a071f9]531 new ReferenceType(
[b32ada31]532 noQualifiers,
[bcda04c]533 new StructInstType(
534 noQualifiers,
535 decl
536 )
[b32ada31]537 ),
538 nullptr
539 );
540
[2db79e5]541 get_type->get_parameters().push_back( this_decl->clone() );
[bd4d011]542 get_type->get_returnVals().push_back(
[e04b636]543 new ObjectDecl(
544 "ret",
[ba3706f]545 noStorageClasses,
[e04b636]546 LinkageSpec::Cforall,
547 nullptr,
548 new PointerType(
549 noQualifiers,
550 new StructInstType(
551 noQualifiers,
[bcda04c]552 type_decl
[e04b636]553 )
554 ),
555 nullptr
556 )
557 );
[2db79e5]558 fixupGenerics(get_type, decl);
[b32ada31]559
[bcda04c]560 FunctionDecl * get_decl = new FunctionDecl(
561 getter_name,
562 Type::Static,
563 LinkageSpec::Cforall,
[bd4d011]564 get_type,
[bcda04c]565 nullptr,
[a8078eef]566 { new Attribute("const") },
[bcda04c]567 Type::Inline
568 );
569
[bd4d011]570 FunctionDecl * main_decl = nullptr;
571
572 if( needs_main ) {
573 FunctionType * main_type = new FunctionType( noQualifiers, false );
[bf2438c]574
[bd4d011]575 main_type->get_parameters().push_back( this_decl->clone() );
576
577 main_decl = new FunctionDecl(
578 "main",
[ba3706f]579 noStorageClasses,
[bd4d011]580 LinkageSpec::Cforall,
581 main_type,
582 nullptr
583 );
[2db79e5]584 fixupGenerics(main_type, decl);
[bd4d011]585 }
586
[2db79e5]587 delete this_decl;
588
[2065609]589 declsToAddBefore.push_back( forward );
590 if( needs_main ) declsToAddBefore.push_back( main_decl );
591 declsToAddBefore.push_back( get_decl );
[bcda04c]592
593 return get_decl;
594 }
595
596 ObjectDecl * ConcurrentSueKeyword::addField( StructDecl * decl ) {
597 ObjectDecl * field = new ObjectDecl(
598 field_name,
[ba3706f]599 noStorageClasses,
[bcda04c]600 LinkageSpec::Cforall,
601 nullptr,
602 new StructInstType(
603 noQualifiers,
604 type_decl
605 ),
606 nullptr
607 );
608
609 decl->get_members().push_back( field );
610
611 return field;
612 }
613
[2f9a722]614 void ConcurrentSueKeyword::addRoutines( ObjectDecl * field, FunctionDecl * func ) {
[ba3706f]615 CompoundStmt * statement = new CompoundStmt();
[bf2438c]616 statement->push_back(
[b32ada31]617 new ReturnStmt(
[e04b636]618 new AddressExpr(
[bcda04c]619 new MemberExpr(
620 field,
[2db79e5]621 new CastExpr(
622 new VariableExpr( func->get_functionType()->get_parameters().front() ),
[b81fd95]623 func->get_functionType()->get_parameters().front()->get_type()->stripReferences()->clone(),
624 false
[2db79e5]625 )
[e04b636]626 )
[b32ada31]627 )
628 )
629 );
630
[bcda04c]631 FunctionDecl * get_decl = func->clone();
632
633 get_decl->set_statements( statement );
[e04b636]634
635 declsToAddAfter.push_back( get_decl );
[427854b]636 }
637
638 //=============================================================================================
639 // Suspend keyword implementation
640 //=============================================================================================
641 bool SuspendKeyword::is_real_suspend( FunctionDecl * func ) {
642 if(isMangled(func->linkage)) return false; // the real suspend isn't mangled
643 if(func->name != "__cfactx_suspend") return false; // the real suspend has a specific name
644 if(func->type->parameters.size() != 0) return false; // Too many parameters
645 if(func->type->returnVals.size() != 0) return false; // Too many return values
646
647 return true;
[b32ada31]648 }
649
[427854b]650 void SuspendKeyword::premutate( FunctionDecl * func ) {
651 GuardValue(in_generator);
652 in_generator = nullptr;
653
654 // Is this the real suspend?
655 if(is_real_suspend(func)) {
656 decl_suspend = decl_suspend ? decl_suspend : func;
657 return;
658 }
659
660 // Is this the main of a generator?
[1c01c58]661 auto param = isMainFor( func, AggregateDecl::Aggregate::Generator );
[427854b]662 if(!param) return;
663
664 if(func->type->returnVals.size() != 0) SemanticError(func->location, "Generator main must return void");
665
666 in_generator = param;
667 GuardValue(labels);
668 labels.clear();
669 }
670
671 DeclarationWithType * SuspendKeyword::postmutate( FunctionDecl * func ) {
672 if( !func->statements ) return func; // Not the actual definition, don't do anything
673 if( !in_generator ) return func; // Not in a generator, don't do anything
674 if( labels.empty() ) return func; // Generator has no states, nothing to do, could throw a warning
675
676 // This is a generator main, we need to add the following code to the top
677 // static void * __generator_labels[] = {&&s0, &&s1, ...};
678 // goto * __generator_labels[gen.__generator_state];
679 const auto & loc = func->location;
680
681 const auto first_label = gen.newLabel("generator");
682
683 // for each label add to declaration
684 std::list<Initializer*> inits = { new SingleInit( new LabelAddressExpr( first_label ) ) };
685 for(const auto & label : labels) {
686 inits.push_back(
687 new SingleInit(
688 new LabelAddressExpr( label )
689 )
690 );
691 }
692 auto init = new ListInit(std::move(inits), noDesignators, true);
693 labels.clear();
694
695 // create decl
696 auto decl = new ObjectDecl(
697 "__generator_labels",
698 Type::StorageClasses( Type::Static ),
699 LinkageSpec::AutoGen,
700 nullptr,
701 new ArrayType(
702 Type::Qualifiers(),
703 new PointerType(
704 Type::Qualifiers(),
705 new VoidType( Type::Qualifiers() )
706 ),
707 nullptr,
708 false, false
709 ),
710 init
711 );
712
713 // create the goto
714 assert(in_generator);
715
716 auto go_decl = new ObjectDecl(
717 "__generator_label",
718 noStorageClasses,
719 LinkageSpec::AutoGen,
720 nullptr,
721 new PointerType(
722 Type::Qualifiers(),
723 new VoidType( Type::Qualifiers() )
724 ),
725 new SingleInit(
726 new UntypedExpr(
727 new NameExpr("?[?]"),
728 {
729 new NameExpr("__generator_labels"),
730 new UntypedMemberExpr(
731 new NameExpr("__generator_state"),
732 new VariableExpr( in_generator )
733 )
734 }
735 )
736 )
737 );
738 go_decl->location = loc;
739
740 auto go = new BranchStmt(
741 new VariableExpr( go_decl ),
742 BranchStmt::Goto
743 );
744 go->location = loc;
745 go->computedTarget->location = loc;
746
747 auto noop = new NullStmt({ first_label });
748 noop->location = loc;
749
750 // wrap everything in a nice compound
751 auto body = new CompoundStmt({
752 new DeclStmt( decl ),
753 new DeclStmt( go_decl ),
754 go,
755 noop,
756 func->statements
757 });
758 body->location = loc;
759 func->statements = body;
760
761 return func;
762 }
763
764 Statement * SuspendKeyword::postmutate( SuspendStmt * stmt ) {
765 SuspendStmt::Type type = stmt->type;
766 if(type == SuspendStmt::None) {
767 // This suspend has a implicit target, find it
768 type = in_generator ? SuspendStmt::Generator : SuspendStmt::Coroutine;
769 }
770
771 // Check that the target makes sense
772 if(!in_generator && type == SuspendStmt::Generator) SemanticError( stmt->location, "'suspend generator' must be used inside main of generator type.");
773
774 // Act appropriately
775 switch(type) {
776 case SuspendStmt::Generator: return make_generator_suspend(stmt);
777 case SuspendStmt::Coroutine: return make_coroutine_suspend(stmt);
778 default: abort();
779 }
780 }
781
782 Statement * SuspendKeyword::make_generator_suspend( SuspendStmt * stmt ) {
783 assert(in_generator);
784 // Target code is :
785 // gen.__generator_state = X;
786 // { THEN }
787 // return;
788 // __gen_X:;
789
790 // Save the location and delete the old statement, we only need the location from this point on
791 auto loc = stmt->location;
792
793 // Build the label and get its index
794 auto label = make_label();
795
796 // Create the context saving statement
797 auto save = new ExprStmt( new UntypedExpr(
798 new NameExpr( "?=?" ),
799 {
800 new UntypedMemberExpr(
801 new NameExpr("__generator_state"),
802 new VariableExpr( in_generator )
803 ),
804 new ConstantExpr(
805 Constant::from_int( label.idx )
806 )
807 }
808 ));
809 assert(save->expr);
810 save->location = loc;
811 stmtsToAddBefore.push_back( save );
812
813 // if we have a then add it here
814 auto then = stmt->then;
815 stmt->then = nullptr;
816 delete stmt;
817 if(then) stmtsToAddBefore.push_back( then );
818
819 // Create the return statement
820 auto ret = new ReturnStmt( nullptr );
821 ret->location = loc;
822 stmtsToAddBefore.push_back( ret );
823
824 // Create the null statement with the created label
825 auto noop = new NullStmt({ label.obj });
826 noop->location = loc;
827
828 // Return the null statement to take the place of the previous statement
829 return noop;
830 }
831
832 Statement * SuspendKeyword::make_coroutine_suspend( SuspendStmt * stmt ) {
833 if(stmt->then) SemanticError( stmt->location, "Compound statement following coroutines is not implemented.");
834
835 // Save the location and delete the old statement, we only need the location from this point on
836 auto loc = stmt->location;
837 delete stmt;
838
839 // Create the call expression
840 if(!decl_suspend) SemanticError( loc, "suspend keyword applied to coroutines requires coroutines to be in scope, add #include <coroutine.hfa>\n");
841 auto expr = new UntypedExpr( VariableExpr::functionPointer( decl_suspend ) );
[e6cfa8ff]842 expr->location = loc;
[427854b]843
844 // Change this statement into a regular expr
845 assert(expr);
846 auto nstmt = new ExprStmt( expr );
[e6cfa8ff]847 nstmt->location = loc;
[427854b]848 return nstmt;
849 }
850
851
[64adb03]852 //=============================================================================================
853 // Mutex keyword implementation
854 //=============================================================================================
[97e3296]855
[2065609]856 void MutexKeyword::postvisit(FunctionDecl* decl) {
[102a58b]857
[db4d8e3]858 bool first = false;
859 std::list<DeclarationWithType*> mutexArgs = findMutexArgs( decl, first );
[ab8c6a6]860 bool const isDtor = CodeGen::isDestructor( decl->name );
[64adb03]861
[ceedde6]862 // Is this function relevant to monitors
863 if( mutexArgs.empty() ) {
864 // If this is the destructor for a monitor it must be mutex
865 if(isDtor) {
866 Type* ty = decl->get_functionType()->get_parameters().front()->get_type();
867
868 // If it's a copy, it's not a mutex
869 ReferenceType* rty = dynamic_cast< ReferenceType * >( ty );
870 if( ! rty ) return;
871
872 // If we are not pointing directly to a type, it's not a mutex
873 Type* base = rty->get_base();
874 if( dynamic_cast< ReferenceType * >( base ) ) return;
875 if( dynamic_cast< PointerType * >( base ) ) return;
876
877 // Check if its a struct
878 StructInstType * baseStruct = dynamic_cast< StructInstType * >( base );
879 if( !baseStruct ) return;
880
881 // Check if its a monitor
882 if(baseStruct->baseStruct->is_monitor() || baseStruct->baseStruct->is_thread())
883 SemanticError( decl, "destructors for structures declared as \"monitor\" must use mutex parameters\n" );
884 }
885 return;
886 }
[549c006]887
[ceedde6]888 // Monitors can't be constructed with mutual exclusion
889 if( CodeGen::isConstructor(decl->name) && !first ) SemanticError( decl, "constructors cannot have mutex parameters" );
[549c006]890
[ceedde6]891 // It makes no sense to have multiple mutex parameters for the destructor
[a16764a6]892 if( isDtor && mutexArgs.size() != 1 ) SemanticError( decl, "destructors can only have 1 mutex argument" );
[549c006]893
[ceedde6]894 // Make sure all the mutex arguments are monitors
[64adb03]895 for(auto arg : mutexArgs) {
896 validate( arg );
897 }
898
[ceedde6]899 // Check if we need to instrument the body
[64adb03]900 CompoundStmt* body = decl->get_statements();
901 if( ! body ) return;
902
[ceedde6]903 // Do we have the required headers
[a16764a6]904 if( !monitor_decl || !guard_decl || !dtor_guard_decl )
[73abe95]905 SemanticError( decl, "mutex keyword requires monitors to be in scope, add #include <monitor.hfa>\n" );
[b32ada31]906
[ceedde6]907 // Instrument the body
[ab8c6a6]908 if ( isDtor && isThread( mutexArgs.front() ) ) {
909 if( !thread_guard_decl ) {
910 SemanticError( decl, "thread destructor requires threads to be in scope, add #include <thread.hfa>\n" );
911 }
912 addThreadDtorStatements( decl, body, mutexArgs );
913 }
914 else if ( isDtor ) {
915 addDtorStatements( decl, body, mutexArgs );
[549c006]916 }
917 else {
[ab8c6a6]918 addStatements( decl, body, mutexArgs );
[549c006]919 }
[64adb03]920 }
921
[2065609]922 void MutexKeyword::postvisit(StructDecl* decl) {
[102a58b]923
[e84ab3d]924 if( decl->name == "monitor$" && decl->body ) {
[9243cc91]925 assert( !monitor_decl );
926 monitor_decl = decl;
927 }
[88d955f]928 else if( decl->name == "monitor_guard_t" && decl->body ) {
[ef42b143]929 assert( !guard_decl );
930 guard_decl = decl;
931 }
[88d955f]932 else if( decl->name == "monitor_dtor_guard_t" && decl->body ) {
[549c006]933 assert( !dtor_guard_decl );
934 dtor_guard_decl = decl;
935 }
[ab8c6a6]936 else if( decl->name == "thread_dtor_guard_t" && decl->body ) {
937 assert( !thread_guard_decl );
938 thread_guard_decl = decl;
939 }
[9243cc91]940 }
941
[6cebfef]942 Statement * MutexKeyword::postmutate( MutexStmt * stmt ) {
943 std::list<Statement *> stmtsForCtor;
944 stmtsForCtor.push_back(stmt->stmt);
945 CompoundStmt * body = new CompoundStmt( stmtsForCtor );
946 addStatements( body, stmt->mutexObjs);
947 return body;
948 }
949
[db4d8e3]950 std::list<DeclarationWithType*> MutexKeyword::findMutexArgs( FunctionDecl* decl, bool & first ) {
[64adb03]951 std::list<DeclarationWithType*> mutexArgs;
952
[db4d8e3]953 bool once = true;
[64adb03]954 for( auto arg : decl->get_functionType()->get_parameters()) {
955 //Find mutex arguments
956 Type* ty = arg->get_type();
[615a096]957 if( ! ty->get_mutex() ) continue;
[64adb03]958
[db4d8e3]959 if(once) {first = true;}
960 once = false;
961
[64adb03]962 //Append it to the list
963 mutexArgs.push_back( arg );
964 }
965
966 return mutexArgs;
967 }
968
969 void MutexKeyword::validate( DeclarationWithType * arg ) {
970 Type* ty = arg->get_type();
971
972 //Makes sure it's not a copy
[870d1f0]973 ReferenceType* rty = dynamic_cast< ReferenceType * >( ty );
[a16764a6]974 if( ! rty ) SemanticError( arg, "Mutex argument must be of reference type " );
[64adb03]975
976 //Make sure the we are pointing directly to a type
[83a071f9]977 Type* base = rty->get_base();
[a16764a6]978 if( dynamic_cast< ReferenceType * >( base ) ) SemanticError( arg, "Mutex argument have exactly one level of indirection " );
979 if( dynamic_cast< PointerType * >( base ) ) SemanticError( arg, "Mutex argument have exactly one level of indirection " );
[64adb03]980
981 //Make sure that typed isn't mutex
[a16764a6]982 if( base->get_mutex() ) SemanticError( arg, "mutex keyword may only appear once per argument " );
[64adb03]983 }
984
[ab8c6a6]985 void MutexKeyword::addDtorStatements( FunctionDecl* func, CompoundStmt * body, const std::list<DeclarationWithType * > & args ) {
[549c006]986 Type * arg_type = args.front()->get_type()->clone();
987 arg_type->set_mutex( false );
988
989 ObjectDecl * monitors = new ObjectDecl(
990 "__monitor",
[ba3706f]991 noStorageClasses,
[549c006]992 LinkageSpec::Cforall,
993 nullptr,
994 new PointerType(
995 noQualifiers,
996 new StructInstType(
997 noQualifiers,
998 monitor_decl
999 )
1000 ),
1001 new SingleInit( new UntypedExpr(
1002 new NameExpr( "get_monitor" ),
[b81fd95]1003 { new CastExpr( new VariableExpr( args.front() ), arg_type, false ) }
[549c006]1004 ))
1005 );
1006
1007 assert(generic_func);
1008
1009 //in reverse order :
[2bf7ef6]1010 // monitor_dtor_guard_t __guard = { __monitors, func };
[549c006]1011 body->push_front(
[ba3706f]1012 new DeclStmt( new ObjectDecl(
[549c006]1013 "__guard",
[ba3706f]1014 noStorageClasses,
[549c006]1015 LinkageSpec::Cforall,
1016 nullptr,
1017 new StructInstType(
1018 noQualifiers,
1019 dtor_guard_decl
1020 ),
1021 new ListInit(
1022 {
1023 new SingleInit( new AddressExpr( new VariableExpr( monitors ) ) ),
[77a2994]1024 new SingleInit( new CastExpr( new VariableExpr( func ), generic_func->clone(), false ) ),
1025 new SingleInit( new ConstantExpr( Constant::from_bool( false ) ) )
[549c006]1026 },
1027 noDesignators,
1028 true
1029 )
1030 ))
1031 );
1032
[e84ab3d]1033 //monitor$ * __monitors[] = { get_monitor(a), get_monitor(b) };
[ab8c6a6]1034 body->push_front( new DeclStmt( monitors ) );
1035 }
1036
1037 void MutexKeyword::addThreadDtorStatements(
1038 FunctionDecl*, CompoundStmt * body,
1039 const std::list<DeclarationWithType * > & args ) {
1040 assert( args.size() == 1 );
1041 DeclarationWithType * arg = args.front();
1042 Type * arg_type = arg->get_type()->clone();
1043 assert( arg_type->get_mutex() );
1044 arg_type->set_mutex( false );
1045
1046 // thread_dtor_guard_t __guard = { this, intptr( 0 ) };
1047 body->push_front(
1048 new DeclStmt( new ObjectDecl(
1049 "__guard",
1050 noStorageClasses,
1051 LinkageSpec::Cforall,
1052 nullptr,
1053 new StructInstType(
1054 noQualifiers,
1055 thread_guard_decl
1056 ),
1057 new ListInit(
1058 {
1059 new SingleInit( new CastExpr( new VariableExpr( arg ), arg_type ) ),
1060 new SingleInit( new UntypedExpr(
1061 new NameExpr( "intptr" ), {
1062 new ConstantExpr( Constant::from_int( 0 ) ),
1063 }
1064 ) ),
1065 },
1066 noDesignators,
1067 true
1068 )
1069 ))
1070 );
[549c006]1071 }
1072
[6cebfef]1073 void MutexKeyword::addStatements( CompoundStmt * body, const std::list<Expression * > & args ) {
1074 ObjectDecl * monitors = new ObjectDecl(
1075 "__monitors",
1076 noStorageClasses,
1077 LinkageSpec::Cforall,
1078 nullptr,
1079 new ArrayType(
1080 noQualifiers,
1081 new PointerType(
1082 noQualifiers,
1083 new StructInstType(
1084 noQualifiers,
1085 monitor_decl
1086 )
1087 ),
1088 new ConstantExpr( Constant::from_ulong( args.size() ) ),
1089 false,
1090 false
1091 ),
1092 new ListInit(
1093 map_range < std::list<Initializer*> > ( args, [](Expression * var ){
1094 return new SingleInit( new UntypedExpr(
1095 new NameExpr( "get_monitor" ),
1096 { var }
1097 ) );
1098 })
1099 )
1100 );
1101
1102 // in reverse order :
1103 // monitor_guard_t __guard = { __monitors, # };
1104 body->push_front(
1105 new DeclStmt( new ObjectDecl(
1106 "__guard",
1107 noStorageClasses,
1108 LinkageSpec::Cforall,
1109 nullptr,
1110 new StructInstType(
1111 noQualifiers,
1112 guard_decl
1113 ),
1114 new ListInit(
1115 {
1116 new SingleInit( new VariableExpr( monitors ) ),
1117 new SingleInit( new ConstantExpr( Constant::from_ulong( args.size() ) ) )
1118 },
1119 noDesignators,
1120 true
1121 )
1122 ))
1123 );
1124
1125 //monitor$ * __monitors[] = { get_monitor(a), get_monitor(b) };
1126 body->push_front( new DeclStmt( monitors) );
1127 }
1128
[ab8c6a6]1129 void MutexKeyword::addStatements( FunctionDecl* func, CompoundStmt * body, const std::list<DeclarationWithType * > & args ) {
[9243cc91]1130 ObjectDecl * monitors = new ObjectDecl(
1131 "__monitors",
[ba3706f]1132 noStorageClasses,
[9243cc91]1133 LinkageSpec::Cforall,
1134 nullptr,
1135 new ArrayType(
1136 noQualifiers,
1137 new PointerType(
1138 noQualifiers,
1139 new StructInstType(
1140 noQualifiers,
1141 monitor_decl
1142 )
1143 ),
1144 new ConstantExpr( Constant::from_ulong( args.size() ) ),
1145 false,
1146 false
1147 ),
1148 new ListInit(
[bd41764]1149 map_range < std::list<Initializer*> > ( args, [](DeclarationWithType * var ){
[cb0e6de]1150 Type * type = var->get_type()->clone();
1151 type->set_mutex( false );
[9243cc91]1152 return new SingleInit( new UntypedExpr(
1153 new NameExpr( "get_monitor" ),
[b81fd95]1154 { new CastExpr( new VariableExpr( var ), type, false ) }
[9243cc91]1155 ) );
1156 })
1157 )
1158 );
1159
[97e3296]1160 assert(generic_func);
1161
[2bf7ef6]1162 // in reverse order :
[97e3296]1163 // monitor_guard_t __guard = { __monitors, #, func };
[64adb03]1164 body->push_front(
[ba3706f]1165 new DeclStmt( new ObjectDecl(
[64adb03]1166 "__guard",
[ba3706f]1167 noStorageClasses,
[64adb03]1168 LinkageSpec::Cforall,
1169 nullptr,
1170 new StructInstType(
1171 noQualifiers,
[ef42b143]1172 guard_decl
[64adb03]1173 ),
1174 new ListInit(
1175 {
[9243cc91]1176 new SingleInit( new VariableExpr( monitors ) ),
[97e3296]1177 new SingleInit( new ConstantExpr( Constant::from_ulong( args.size() ) ) ),
[b81fd95]1178 new SingleInit( new CastExpr( new VariableExpr( func ), generic_func->clone(), false ) )
[ef42b143]1179 },
1180 noDesignators,
1181 true
[64adb03]1182 )
1183 ))
1184 );
1185
[e84ab3d]1186 //monitor$ * __monitors[] = { get_monitor(a), get_monitor(b) };
[ba3706f]1187 body->push_front( new DeclStmt( monitors) );
[64adb03]1188 }
[bd4d011]1189
1190 //=============================================================================================
1191 // General entry routine
1192 //=============================================================================================
[549c006]1193 void ThreadStarter::previsit( StructDecl * decl ) {
[e84ab3d]1194 if( decl->name == "thread$" && decl->body ) {
[549c006]1195 assert( !thread_decl );
1196 thread_decl = decl;
1197 }
1198 }
1199
[2065609]1200 void ThreadStarter::postvisit(FunctionDecl * decl) {
[9f5ecf5]1201 if( ! CodeGen::isConstructor(decl->name) ) return;
[bd4d011]1202
[549c006]1203 Type * typeof_this = InitTweak::getTypeofThis(decl->type);
1204 StructInstType * ctored_type = dynamic_cast< StructInstType * >( typeof_this );
1205 if( ctored_type && ctored_type->baseStruct == thread_decl ) {
1206 thread_ctor_seen = true;
1207 }
1208
[bd4d011]1209 DeclarationWithType * param = decl->get_functionType()->get_parameters().front();
[ce8c12f]1210 auto type = dynamic_cast< StructInstType * >( InitTweak::getPointerBase( param->get_type() ) );
[bd4d011]1211 if( type && type->get_baseStruct()->is_thread() ) {
[549c006]1212 if( !thread_decl || !thread_ctor_seen ) {
[73abe95]1213 SemanticError( type->get_baseStruct()->location, "thread keyword requires threads to be in scope, add #include <thread.hfa>");
[549c006]1214 }
1215
[bd4d011]1216 addStartStatement( decl, param );
1217 }
1218 }
1219
1220 void ThreadStarter::addStartStatement( FunctionDecl * decl, DeclarationWithType * param ) {
1221 CompoundStmt * stmt = decl->get_statements();
1222
1223 if( ! stmt ) return;
1224
[bf2438c]1225 stmt->push_back(
[bd4d011]1226 new ExprStmt(
1227 new UntypedExpr(
1228 new NameExpr( "__thrd_start" ),
[09f357ec]1229 { new VariableExpr( param ), new NameExpr("main") }
[bd4d011]1230 )
1231 )
1232 );
1233 }
[68fe077a]1234};
[6b0b624]1235
1236// Local Variables: //
1237// mode: c //
1238// tab-width: 4 //
1239// End: //
[1c01c58]1240
Note: See TracBrowser for help on using the repository browser.