source: src/Virtual/VirtualDtor.cpp @ bef2245

Last change on this file since bef2245 was 4bae7b4, checked in by caparsons <caparson@…>, 16 months ago

fixed bug where virtual dtor would not work if dtors or ctors of the type had unnamed params

  • Property mode set to 100644
File size: 15.2 KB
Line 
1//
2// Cforall Version 1.0.0 Copyright (C) 2015 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// VirtualDtor.cpp -- generate code needed by the actor system
8//
9// Author           : Colby Parsons
10// Created On       : Tues Mar 14 15:16:42 2023
11// Last Modified By : Colby Parsons
12// Last Modified On : Tues Mar 14 15:16:42 2023
13// Update Count     : 0
14//
15
16#include "AST/Print.hpp"
17#include "AST/Decl.hpp"
18#include "AST/Pass.hpp"
19#include "AST/Type.hpp"
20#include "AST/Stmt.hpp"
21#include "AST/TranslationUnit.hpp"
22#include "AST/Expr.hpp"
23#include <algorithm>
24using namespace ast;
25using namespace std;
26
27namespace Virtual {
28
29struct CtorDtor {
30    FunctionDecl * dtorSetup;  // dtor init routine to add after last dtor for a struct
31    FunctionDecl * deleteFn;
32    FunctionDecl * lastDtor;    // pointer to last occurence of dtor to know where to insert after
33
34    CtorDtor() : dtorSetup(nullptr), deleteFn(nullptr), lastDtor(nullptr) {}
35};
36
37class CtorDtorTable {
38    unordered_map<const StructDecl *, CtorDtor> & structMap;
39
40  public:
41    // if dtor is last dtor for this decl return the routine to add afterwards
42    // otherwise return nullptr
43    FunctionDecl * getToAddLater( const StructDecl * decl, FunctionDecl * dtor, FunctionDecl ** retDeleteFn ) {
44        auto iter = structMap.find( decl );
45        if ( iter == structMap.end() || iter->second.lastDtor != dtor ) return nullptr; // check if this is needed
46        *retDeleteFn = iter->second.deleteFn;
47        return iter->second.dtorSetup;
48    }
49
50    // return if the dtorSetup field has been defined for this decl
51    bool inTable( const StructDecl * decl ) {
52        auto iter = structMap.find( decl );
53        return iter->second.dtorSetup != nullptr;
54    }
55
56    void addLater( const StructDecl * decl, FunctionDecl * dtorSetup, FunctionDecl * deleteFn ) {
57        auto iter = structMap.find( decl );
58        iter->second.dtorSetup = dtorSetup;
59        iter->second.deleteFn = deleteFn;
60    }
61
62    void addDtor( const StructDecl * decl, FunctionDecl * dtor ) {
63        auto iter = structMap.find( decl );
64        iter->second.lastDtor = dtor;
65    }
66
67    CtorDtorTable( unordered_map<const StructDecl *, CtorDtor> & structMap ) : structMap(structMap) {}
68};
69
70struct CollectStructDecls : public ast::WithGuards {
71    unordered_map<const StructDecl *, CtorDtor> & structDecls;
72    StructDecl * parentDecl;
73    bool insideStruct = false;
74    bool namedDecl = false;
75
76    const StructDecl ** virtualDtor;
77
78    // finds and sets a ptr to the actor, message, and request structs, which are needed in the next pass
79    void previsit( const StructDecl * decl ) {
80        if ( !decl->body ) return;
81        if( decl->name == "virtual_dtor" ) {
82            structDecls.emplace( make_pair( decl, CtorDtor() ) );
83            *virtualDtor = decl;
84        } else {
85            GuardValue(insideStruct);
86            insideStruct = true;
87            parentDecl = mutate( decl );
88        }
89        }
90
91    // this catches structs of the form:
92    //     struct derived_type { virtual_dtor a; };
93    // since they should be:
94    //     struct derived_type { inline virtual_dtor; };
95    void previsit ( const ObjectDecl * decl ) {
96        if ( insideStruct && ! decl->name.empty() ) {
97            GuardValue(namedDecl);
98            namedDecl = true;
99        }
100    }
101
102    // this collects the derived actor and message struct decl ptrs
103    void postvisit( const StructInstType * node ) {
104        if ( ! *virtualDtor ) return;
105        if ( insideStruct && !namedDecl ) {
106            auto structIter = structDecls.find( node->aggr() );   
107            if ( structIter != structDecls.end() )
108                structDecls.emplace( make_pair( parentDecl, CtorDtor() ) );
109        }
110        }
111
112  public:
113    CollectStructDecls( unordered_map<const StructDecl *, CtorDtor> & structDecls, const StructDecl ** virtualDtor ):
114        structDecls( structDecls ), virtualDtor(virtualDtor) {}
115};
116
117// generates the forward decl of virtual dtor setting routine and delete routine
118// generates the call to the virtual dtor routine in each appropriate ctor
119// collects data needed for next pass that does the circular defn resolution
120//     for dtor setters and delete fns (via table above)
121struct GenFuncsCreateTables : public ast::WithDeclsToAdd<> {
122    unordered_map<const StructDecl *, CtorDtor> & structDecls;
123    CtorDtorTable & torDecls;
124    const StructDecl ** virtualDtor;
125
126    // collects the dtor info for actors/messages
127    // gens the dtor fwd decl and dtor call in ctor
128    void previsit( const FunctionDecl * decl ) {
129        if ( (decl->name != "?{}" && decl->name != "^?{}") || decl->params.size() == 0 
130            || !decl->stmts || (decl->name == "^?{}" && decl->params.size() != 1)) return;
131
132        // the first param should be a reference
133        const ReferenceType * ref = dynamic_cast<const ReferenceType *>(decl->params.at(0)->get_type());
134        if ( !ref ) return;
135
136        // the reference should be to a struct instance
137        const StructInstType * instType = dynamic_cast<const StructInstType *>(ref->base.get());
138        if ( !instType ) return;
139
140        // return if not ctor/dtor for an actor or message
141        auto structIter = structDecls.find( instType->aggr() );
142        if ( structIter == structDecls.end() ) return;
143
144        // If first param not named we need to name it to use it
145        if ( decl->params.at(0)->name == "" )
146            mutate( decl->params.at(0).get() )->name = "__CFA_Virt_Dtor_param";
147
148        if ( decl->name == "^?{}") {
149            torDecls.addDtor( structIter->first, mutate( decl ) );
150
151            CompoundStmt * dtorBody = mutate( decl->stmts.get() );
152            // Adds the following to the start of any actor/message dtor:
153            //  __CFA_dtor_shutdown( this );
154            dtorBody->push_front( 
155                new IfStmt( decl->location,
156                    new UntypedExpr (
157                        decl->location,
158                        new NameExpr( decl->location, "__CFA_dtor_shutdown" ),
159                        {
160                            new NameExpr( decl->location, decl->params.at(0)->name )
161                        }
162                    ),
163                    new ReturnStmt( decl->location, nullptr )
164                )
165            );
166            return;
167        }
168
169        // not dtor by this point so must be ctor
170        CompoundStmt * ctorBody = mutate( decl->stmts.get() );
171        // Adds the following to the end of any actor/message ctor:
172        //  __CFA_set_dtor( this );
173        ctorBody->push_back( new ExprStmt(
174            decl->location,
175            new UntypedExpr (
176                decl->location,
177                new NameExpr( decl->location, "__CFA_set_dtor" ),
178                {
179                    new NameExpr( decl->location, decl->params.at(0)->name )
180                }
181            )
182        ));
183       
184        if ( torDecls.inTable( structIter->first ) ) return;
185
186        // Generates the following:
187        // void __CFA_set_dtor( Derived_type & this ){
188        //     void (*__my_dtor)( Derived_type & ) = ^?{};
189        //     this.__virtual_dtor = (void (*)( Base_type & ))__my_dtor;
190        //     this.__virtual_obj_start = (void *)(&this);
191        // }
192        CompoundStmt * setDtorBody = new CompoundStmt( decl->location );
193
194        // Function type is: (void (*)(Derived_type &))
195        FunctionType * derivedDtor = new FunctionType();
196        derivedDtor->params.push_back( ast::deepCopy( ref ) );
197
198        // Generates:
199        //      void (*__my_dtor)( Derived_type & ) = ^?{};
200        setDtorBody->push_back( new DeclStmt(
201            decl->location,
202            new ObjectDecl(
203                decl->location,
204                "__my_dtor",
205                new PointerType( derivedDtor ),
206                new SingleInit( decl->location, new NameExpr( decl->location, "^?{}" ) )
207            )
208        ));
209
210        // Function type is: (void (*)( Base_type & ))
211        FunctionType * baseDtor = new FunctionType();
212        baseDtor->params.push_back( new ReferenceType( new StructInstType( *virtualDtor ) ) );
213
214        // Generates:
215        //     __CFA_set_virt_dtor( this, (void (*)( Base_type & ))__my_dtor )
216        setDtorBody->push_back( new ExprStmt(
217            decl->location,
218            new UntypedExpr (
219                decl->location,
220                new NameExpr( decl->location, "__CFA_set_virt_dtor" ),
221                {
222                    new NameExpr( decl->location, "this" ),
223                    new CastExpr( decl->location, new NameExpr( decl->location, "__my_dtor" ), new PointerType( baseDtor ), ExplicitCast )
224                }
225            )
226        ));
227
228        // Generates:
229        //     __CFA_set_virt_start( (void *)(&this) );
230        setDtorBody->push_back( new ExprStmt(
231            decl->location,
232            new UntypedExpr (
233                decl->location, 
234                new NameExpr( decl->location, "__CFA_set_virt_start" ),
235                {
236                    new NameExpr( decl->location, "this" ),
237                    new CastExpr(
238                        decl->location, 
239                        new AddressExpr( decl->location, new NameExpr( decl->location, "this" )), 
240                        new PointerType( new ast::VoidType() ), ExplicitCast
241                        )
242                }
243            )
244        ));
245
246        // put it all together into the complete function decl from above
247        FunctionDecl * setDtorFunction = new FunctionDecl(
248            decl->location,
249            "__CFA_set_dtor",
250            {},                     // forall
251            {
252                new ObjectDecl(
253                    decl->location,
254                    "this",
255                    ast::deepCopy( ref )
256                ),
257            },                      // params
258            {},
259            nullptr,               // body
260            { Storage::Static },    // storage
261            Linkage::Cforall,       // linkage
262            {},                     // attributes
263            { Function::Inline }
264        );
265
266        declsToAddBefore.push_back( ast::deepCopy( setDtorFunction ) );
267
268        setDtorFunction->stmts = setDtorBody;
269
270        // The following generates the following specialized delete routine:
271        // static inline void delete( derived_type * ptr ) {
272        //     if ( ptr )
273        //         ^(*ptr){};
274        //     __CFA_virt_free( *ptr );
275        // }
276        CompoundStmt * deleteFnBody = new CompoundStmt( decl->location );
277
278        // Generates:
279        //     if ( ptr )
280        //         ^(*ptr){};
281        deleteFnBody->push_back(
282            new IfStmt(
283                decl->location,
284                UntypedExpr::createCall(
285                    decl->location,
286                    "?!=?",
287                    {
288                        new NameExpr( decl->location, "ptr" ),
289                        ConstantExpr::null( decl->location, new PointerType( ast::deepCopy( instType ) ) )
290                    }
291                ),
292                new ExprStmt(
293                    decl->location,
294                    UntypedExpr::createCall( 
295                        decl->location, 
296                        "^?{}",
297                        {
298                            UntypedExpr::createDeref( decl->location, new NameExpr( decl->location, "ptr" ))
299                        }
300                    )
301                )
302            )
303        );
304
305        // Generates:
306        //     __CFA_virt_free( *ptr );
307        deleteFnBody->push_back( new ExprStmt(
308                decl->location,
309                UntypedExpr::createCall( 
310                    decl->location, 
311                    "__CFA_virt_free",
312                    {
313                        UntypedExpr::createDeref( decl->location, new NameExpr( decl->location, "ptr" ))
314                    }
315                )
316            )
317        );
318
319        FunctionDecl * deleteFn = new FunctionDecl(
320            decl->location,
321            "delete",
322            {},                     // forall
323            {
324                new ObjectDecl(
325                    decl->location,
326                    "ptr",
327                    new PointerType( ast::deepCopy( instType ) )
328                ),
329            },                      // params
330            {},
331            nullptr,               // body
332            { Storage::Static },    // storage
333            Linkage::Cforall,       // linkage
334            {},                     // attributes
335            { Function::Inline }
336        );
337
338        declsToAddBefore.push_back( ast::deepCopy( deleteFn ) );
339
340        deleteFn->stmts = deleteFnBody;
341
342        torDecls.addLater( structIter->first, setDtorFunction, deleteFn );
343    }
344
345  public:
346    GenFuncsCreateTables( unordered_map<const StructDecl *, CtorDtor> & structDecls, CtorDtorTable & torDecls, const StructDecl ** virtualDtor ):
347    structDecls(structDecls), torDecls(torDecls), virtualDtor(virtualDtor) {}
348};
349
350
351// generates the trailing definitions of dtor setting routines for virtual dtors on messages and actors
352// generates the function defns of __CFA_set_dtor
353// separate pass is needed since  __CFA_set_dtor needs to be defined after
354//   the last dtor defn which is found in prior pass
355struct GenSetDtor : public ast::WithDeclsToAdd<> {
356    unordered_map<const StructDecl *, CtorDtor> & structDecls; // set of decls that inherit from virt dtor
357    CtorDtorTable & torDecls;
358
359    // handles adding the declaration of the dtor init routine after the last dtor detected
360    void postvisit( const FunctionDecl * decl ) {
361        if ( decl->name != "^?{}" || !decl->stmts || decl->params.size() != 1 ) return;
362
363        // the one param should be a reference
364        const ReferenceType * ref = dynamic_cast<const ReferenceType *>(decl->params.at(0)->get_type());
365        if ( !ref ) return;
366
367        // the reference should be to a struct instance
368        const StructInstType * instType = dynamic_cast<const StructInstType *>(ref->base.get());
369        if ( !instType ) return;
370
371        FunctionDecl * deleteRtn;
372
373        // returns nullptr if not in table
374        FunctionDecl * maybeAdd = torDecls.getToAddLater( instType->aggr(), mutate( decl ), &deleteRtn );
375        if ( maybeAdd ) {
376            declsToAddAfter.push_back( maybeAdd );
377            declsToAddAfter.push_back( deleteRtn );
378        }
379    }
380
381  public:
382    GenSetDtor( unordered_map<const StructDecl *, CtorDtor> & structDecls, CtorDtorTable & torDecls ):
383        structDecls(structDecls), torDecls(torDecls) {}
384};
385
386void implementVirtDtors( TranslationUnit & translationUnit ) {
387    // unordered_map to collect all derived types and associated data
388    unordered_map<const StructDecl *, CtorDtor> structDecls;
389    CtorDtorTable torDecls( structDecls );
390
391    const StructDecl * virtualDtorPtr = nullptr;
392    const StructDecl ** virtualDtor = &virtualDtorPtr;
393
394    // first pass collects all structs that inherit from virtual_dtor
395    Pass<CollectStructDecls>::run( translationUnit, structDecls, virtualDtor );
396
397    // second pass locates all dtor/ctor routines that need modifying or need fns inserted before/after
398    Pass<GenFuncsCreateTables>::run( translationUnit, structDecls, torDecls, virtualDtor );
399
400    // The third pass adds the forward decls needed to resolve circular defn problems
401    Pass<GenSetDtor>::run( translationUnit, structDecls, torDecls );
402}
403
404
405} // namespace Virtual
406
407// Local Variables: //
408// tab-width: 4 //
409// mode: c++ //
410// compile-command: "make install" //
411// End: //
412
Note: See TracBrowser for help on using the repository browser.