source: src/InitTweak/FixInit.cc @ 845cedc

aaron-thesisarm-ehcleanup-dtorsctordeferred_resndemanglergc_noraiijacob/cs343-translationjenkins-sandboxmemorynew-astnew-ast-unique-exprnew-envno_listpersistent-indexerresolv-newwith_gc
Last change on this file since 845cedc was 845cedc, checked in by Rob Schluntz <rschlunt@…>, 6 years ago

don't generate copy constructor calls for arguments to intrinsic functions, copy constructors, assignment operators, and destructors, fix problem with copying result type of ImplicitCopyCtorExpr? which caused expression resolution to fail

  • Property mode set to 100644
File size: 16.7 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// FixInit.h --
8//
9// Author           : Rob Schluntz
10// Created On       : Wed Jan 13 16:29:30 2016
11// Last Modified By : Rob Schluntz
12// Last Modified On : Mon Apr 25 14:44:21 2016
13// Update Count     : 30
14//
15
16#include <stack>
17#include <list>
18#include "RemoveInit.h"
19#include "ResolvExpr/Resolver.h"
20#include "ResolvExpr/typeops.h"
21#include "SynTree/Declaration.h"
22#include "SynTree/Type.h"
23#include "SynTree/Expression.h"
24#include "SynTree/Statement.h"
25#include "SynTree/Initializer.h"
26#include "SynTree/Mutator.h"
27#include "SymTab/Indexer.h"
28#include "GenPoly/PolyMutator.h"
29
30bool ctordtorp = false;
31#define PRINT( text ) if ( ctordtorp ) { text }
32
33namespace InitTweak {
34        namespace {
35                const std::list<Label> noLabels;
36                const std::list<Expression*> noDesignators;
37        }
38
39        class InsertImplicitCalls : public Mutator {
40        public:
41                /// wrap function application expressions as ImplicitCopyCtorExpr nodes
42                /// so that it is easy to identify which function calls need their parameters
43                /// to be copy constructed
44                static void insert( std::list< Declaration * > & translationUnit );
45
46                virtual Expression * mutate( ApplicationExpr * appExpr );
47        };
48
49        class ResolveCopyCtors : public SymTab::Indexer {
50        public:
51                /// generate temporary ObjectDecls for each argument and return value of each
52                /// ImplicitCopyCtorExpr, generate/resolve copy construction expressions for each,
53                /// and generate/resolve destructors for both arguments and return value temporaries
54                static void resolveImplicitCalls( std::list< Declaration * > & translationUnit );
55
56                virtual void visit( ImplicitCopyCtorExpr * impCpCtorExpr );
57
58                /// create and resolve ctor/dtor expression: fname(var, [cpArg])
59                ApplicationExpr * makeCtorDtor( const std::string & fname, ObjectDecl * var, Expression * cpArg = NULL );
60        };
61
62        class FixInit : public GenPoly::PolyMutator {
63          public:
64                /// expand each object declaration to use its constructor after it is declared.
65                /// insert destructor calls at the appropriate places
66                static void fixInitializers( std::list< Declaration * > &translationUnit );
67
68                virtual DeclarationWithType * mutate( ObjectDecl *objDecl );
69
70                virtual CompoundStmt * mutate( CompoundStmt * compoundStmt );
71                virtual Statement * mutate( ReturnStmt * returnStmt );
72                virtual Statement * mutate( BranchStmt * branchStmt );
73
74          private:
75                // stack of list of statements - used to differentiate scopes
76                std::list< std::list< Statement * > > dtorStmts;
77        };
78
79        class FixCopyCtors : public GenPoly::PolyMutator {
80          public:
81                /// expand ImplicitCopyCtorExpr nodes into the temporary declarations, copy constructors,
82                /// call expression, and destructors
83                static void fixCopyCtors( std::list< Declaration * > &translationUnit );
84
85                virtual Expression * mutate( ImplicitCopyCtorExpr * impCpCtorExpr );
86
87          private:
88                // stack of list of statements - used to differentiate scopes
89                std::list< std::list< Statement * > > dtorStmts;
90        };
91
92        void fix( std::list< Declaration * > & translationUnit ) {
93                InsertImplicitCalls::insert( translationUnit );
94                ResolveCopyCtors::resolveImplicitCalls( translationUnit );
95                FixInit::fixInitializers( translationUnit );
96                // FixCopyCtors must happen after FixInit, so that destructors are placed correctly
97                FixCopyCtors::fixCopyCtors( translationUnit );
98        }
99
100        void InsertImplicitCalls::insert( std::list< Declaration * > & translationUnit ) {
101                InsertImplicitCalls inserter;
102                mutateAll( translationUnit, inserter );
103        }
104
105        void ResolveCopyCtors::resolveImplicitCalls( std::list< Declaration * > & translationUnit ) {
106                ResolveCopyCtors resolver;
107                acceptAll( translationUnit, resolver );
108        }
109
110        void FixInit::fixInitializers( std::list< Declaration * > & translationUnit ) {
111                FixInit fixer;
112                mutateAll( translationUnit, fixer );
113        }
114
115        void FixCopyCtors::fixCopyCtors( std::list< Declaration * > & translationUnit ) {
116                FixCopyCtors fixer;
117                mutateAll( translationUnit, fixer );
118        }
119
120        Expression * InsertImplicitCalls::mutate( ApplicationExpr * appExpr ) {
121                appExpr = dynamic_cast< ApplicationExpr * >( Mutator::mutate( appExpr ) );
122                assert( appExpr );
123
124                if ( VariableExpr * function = dynamic_cast< VariableExpr * > ( appExpr->get_function() ) ) {
125                        if ( function->get_var()->get_linkage() == LinkageSpec::Intrinsic ) {
126                                // optimization: don't need to copy construct in order to call intrinsic functions
127                                return appExpr;
128                        } else if ( FunctionDecl * funcDecl = dynamic_cast< FunctionDecl * > ( function->get_var() ) ) {
129                                FunctionType * ftype = funcDecl->get_functionType();
130                                if ( (funcDecl->get_name() == "?{}" || funcDecl->get_name() == "?=?") && ftype->get_parameters().size() == 2 ) {
131                                        Type * t1 = ftype->get_parameters().front()->get_type();
132                                        Type * t2 = ftype->get_parameters().back()->get_type();
133                                        PointerType * ptrType = dynamic_cast< PointerType * > ( t1 );
134                                        assert( ptrType );
135                                        if ( ResolvExpr::typesCompatible( ptrType->get_base(), t2, SymTab::Indexer() ) ) {
136                                                // optimization: don't need to copy construct in order to call a copy constructor or
137                                                // assignment operator
138                                                return appExpr;
139                                        }
140                                } else if ( funcDecl->get_name() == "^?{}" ) {
141                                        // correctness: never copy construct arguments to a destructor
142                                        return appExpr;
143                                }
144                        }
145                }
146                PRINT( std::cerr << "InsertImplicitCalls: adding a wrapper " << appExpr << std::endl; )
147
148                // wrap each function call so that it is easy to identify nodes that have to be copy constructed
149                return new ImplicitCopyCtorExpr( appExpr );
150        }
151
152        ApplicationExpr * ResolveCopyCtors::makeCtorDtor( const std::string & fname, ObjectDecl * var, Expression * cpArg ) {
153                assert( var );
154                UntypedExpr * untyped = new UntypedExpr( new NameExpr( fname ) );
155                untyped->get_args().push_back( new AddressExpr( new VariableExpr( var ) ) );
156                if (cpArg) untyped->get_args().push_back( cpArg );
157
158                // resolve copy constructor
159                // should only be one alternative for copy ctor and dtor expressions, since
160                // all arguments are fixed (VariableExpr and already resolved expression)
161                PRINT( std::cerr << "ResolvingCtorDtor " << untyped << std::endl; )
162                ApplicationExpr * resolved = dynamic_cast< ApplicationExpr * >( ResolvExpr::findVoidExpression( untyped, *this ) );
163
164                assert( resolved );
165                delete untyped;
166                return resolved;
167        }
168
169        void ResolveCopyCtors::visit( ImplicitCopyCtorExpr *impCpCtorExpr ) {
170                static UniqueName tempNamer("_tmp_cp");
171                static UniqueName retNamer("_tmp_cp_ret");
172
173                PRINT( std::cerr << "ResolveCopyCtors: " << impCpCtorExpr << std::endl; )
174                Visitor::visit( impCpCtorExpr );
175
176                ApplicationExpr * appExpr = impCpCtorExpr->get_callExpr();
177
178                // take each argument and attempt to copy construct it.
179                for ( Expression * & arg : appExpr->get_args() ) {
180                        // xxx - need to handle tuple arguments
181                        assert( ! arg->get_results().empty() );
182                        ObjectDecl * tmp = new ObjectDecl( tempNamer.newName(), DeclarationNode::NoStorageClass, LinkageSpec::C, 0, arg->get_results().front()->clone(), 0 );
183                        tmp->get_type()->set_isConst( false );
184
185                        // create and resolve copy constructor
186                        PRINT( std::cerr << "makeCtorDtor for an argument" << std::endl; )
187                        ApplicationExpr * cpCtor = makeCtorDtor( "?{}", tmp, arg );
188
189                        // if the chosen constructor is intrinsic, the copy is unnecessary, so
190                        // don't create the temporary and don't call the copy constructor
191                        VariableExpr * function = dynamic_cast< VariableExpr * >( cpCtor->get_function() );
192                        assert( function );
193                        if ( function->get_var()->get_linkage() != LinkageSpec::Intrinsic ) {
194                                // replace argument to function call with temporary
195                                arg = new VariableExpr( tmp );
196                                impCpCtorExpr->get_tempDecls().push_back( tmp );
197                                impCpCtorExpr->get_copyCtors().push_back( cpCtor );
198                                impCpCtorExpr->get_dtors().push_front( makeCtorDtor( "^?{}", tmp ) );
199                        }
200                }
201
202                // each return value from the call needs to be connected with an ObjectDecl
203                // at the call site, which is initialized with the return value and is destructed
204                // later
205                // xxx - handle multiple return values
206                ApplicationExpr * callExpr = impCpCtorExpr->get_callExpr();
207                for ( Type * result : appExpr->get_results() ) {
208                        ObjectDecl * ret = new ObjectDecl( retNamer.newName(), DeclarationNode::NoStorageClass, LinkageSpec::C, 0, result->clone(), new SingleInit( callExpr ) );
209                        ret->get_type()->set_isConst( false );
210                        impCpCtorExpr->get_returnDecls().push_back( ret );
211                        PRINT( std::cerr << "makeCtorDtor for a return" << std::endl; )
212                        impCpCtorExpr->get_dtors().push_front( makeCtorDtor( "^?{}", ret ) );
213                }
214                PRINT( std::cerr << "after Resolving: " << impCpCtorExpr << std::endl; )
215        }
216
217
218        Expression * FixCopyCtors::mutate( ImplicitCopyCtorExpr * impCpCtorExpr ) {
219                PRINT( std::cerr << "FixCopyCtors: " << impCpCtorExpr << std::endl; )
220
221                // assert( impCpCtorExpr->get_callExpr()->get_env() );
222                impCpCtorExpr = dynamic_cast< ImplicitCopyCtorExpr * >( Mutator::mutate( impCpCtorExpr ) );
223                assert( impCpCtorExpr );
224
225                std::list< Expression * > & copyCtors = impCpCtorExpr->get_copyCtors();
226                std::list< ObjectDecl * > & tempDecls = impCpCtorExpr->get_tempDecls();
227                std::list< ObjectDecl * > & returnDecls = impCpCtorExpr->get_returnDecls();
228                std::list< Expression * > & dtors = impCpCtorExpr->get_dtors();
229
230                // add all temporary declarations and their constructors
231                for ( ObjectDecl * obj : tempDecls ) {
232                        assert( ! copyCtors.empty() );
233                        stmtsToAdd.push_back( new DeclStmt( noLabels, obj ) );
234                        stmtsToAdd.push_back( new ExprStmt( noLabels, copyCtors.front() ) );
235                        copyCtors.pop_front();
236                }
237
238                // add destructors after current statement
239                for ( Expression * dtor : dtors ) {
240                        stmtsToAddAfter.push_back( new ExprStmt( noLabels, dtor ) );
241                }
242
243                // xxx - update to work with multiple return values
244                ObjectDecl * returnDecl = returnDecls.empty() ? NULL : returnDecls.front();
245                Expression * callExpr = impCpCtorExpr->get_callExpr();
246
247                PRINT( std::cerr << "Coming out the back..." << impCpCtorExpr << std::endl; )
248
249                // xxx - some of these aren't necessary, and can be removed once this is stable
250                copyCtors.clear();
251                dtors.clear();
252                tempDecls.clear();
253                returnDecls.clear();
254                impCpCtorExpr->set_callExpr( NULL );
255                delete impCpCtorExpr;
256
257                if ( returnDecl ) {
258                        // call is currently attached to first returnDecl
259                        stmtsToAdd.push_back( new DeclStmt( noLabels, returnDecl ) );
260                        return new VariableExpr( returnDecl );
261                } else {
262                        // add call expression - if no return values, can call directly
263                        assert( callExpr );
264                        return callExpr;
265                }
266        }
267
268        DeclarationWithType *FixInit::mutate( ObjectDecl *objDecl ) {
269                // first recursively handle pieces of ObjectDecl so that they aren't missed by other visitors
270                // when the init is removed from the ObjectDecl
271                objDecl = dynamic_cast< ObjectDecl * >( Mutator::mutate( objDecl ) );
272
273                if ( ConstructorInit * ctorInit = dynamic_cast< ConstructorInit * >( objDecl->get_init() ) ) {
274                        // a decision should have been made by the resolver, so ctor and init are not both non-NULL
275                        assert( ! ctorInit->get_ctor() || ! ctorInit->get_init() );
276                        if ( Statement * ctor = ctorInit->get_ctor() ) {
277                                if ( objDecl->get_storageClass() == DeclarationNode::Static ) {
278                                        // generate:
279                                        // static bool __objName_uninitialized = true;
280                                        // if (__objName_uninitialized) {
281                                        //   __ctor(__objName);
282                                        //   void dtor_atexit() {
283                                        //     __dtor(__objName);
284                                        //   }
285                                        //   on_exit(dtorOnExit, &__objName);
286                                        //   __objName_uninitialized = false;
287                                        // }
288
289                                        // generate first line
290                                        BasicType * boolType = new BasicType( Type::Qualifiers(), BasicType::Bool );
291                                        SingleInit * boolInitExpr = new SingleInit( new ConstantExpr( Constant( boolType->clone(), "1" ) ), noDesignators );
292                                        ObjectDecl * isUninitializedVar = new ObjectDecl( objDecl->get_mangleName() + "_uninitialized", DeclarationNode::Static, LinkageSpec::Cforall, 0, boolType, boolInitExpr );
293                                        isUninitializedVar->fixUniqueId();
294
295                                        // void dtor_atexit(...) {...}
296                                        FunctionDecl * dtorCaller = new FunctionDecl( objDecl->get_mangleName() + "_dtor_atexit", DeclarationNode::NoStorageClass, LinkageSpec::C, new FunctionType( Type::Qualifiers(), false ), new CompoundStmt( noLabels ), false, false );
297                                        dtorCaller->fixUniqueId();
298                                        dtorCaller->get_statements()->get_kids().push_back( ctorInit->get_dtor() );
299
300                                        // on_exit(dtor_atexit);
301                                        UntypedExpr * callAtexit = new UntypedExpr( new NameExpr( "atexit" ) );
302                                        callAtexit->get_args().push_back( new VariableExpr( dtorCaller ) );
303
304                                        // __objName_uninitialized = false;
305                                        UntypedExpr * setTrue = new UntypedExpr( new NameExpr( "?=?" ) );
306                                        setTrue->get_args().push_back( new VariableExpr( isUninitializedVar ) );
307                                        setTrue->get_args().push_back( new ConstantExpr( Constant( boolType->clone(), "0" ) ) );
308
309                                        // generate body of if
310                                        CompoundStmt * initStmts = new CompoundStmt( noLabels );
311                                        std::list< Statement * > & body = initStmts->get_kids();
312                                        body.push_back( ctor );
313                                        body.push_back( new DeclStmt( noLabels, dtorCaller ) );
314                                        body.push_back( new ExprStmt( noLabels, callAtexit ) );
315                                        body.push_back( new ExprStmt( noLabels, setTrue ) );
316
317                                        // put it all together
318                                        IfStmt * ifStmt = new IfStmt( noLabels, new VariableExpr( isUninitializedVar ), initStmts, 0 );
319                                        stmtsToAddAfter.push_back( new DeclStmt( noLabels, isUninitializedVar ) );
320                                        stmtsToAddAfter.push_back( ifStmt );
321                                } else {
322                                        stmtsToAddAfter.push_back( ctor );
323                                        dtorStmts.back().push_front( ctorInit->get_dtor() );
324                                }
325                                objDecl->set_init( NULL );
326                                ctorInit->set_ctor( NULL );
327                                ctorInit->set_dtor( NULL );  // xxx - only destruct when constructing? Probably not?
328                        } else if ( Initializer * init = ctorInit->get_init() ) {
329                                objDecl->set_init( init );
330                                ctorInit->set_init( NULL );
331                        } else {
332                                // no constructor and no initializer, which is okay
333                                objDecl->set_init( NULL );
334                        }
335                        delete ctorInit;
336                }
337                return objDecl;
338        }
339
340        template<typename Iterator, typename OutputIterator>
341        void insertDtors( Iterator begin, Iterator end, OutputIterator out ) {
342                for ( Iterator it = begin ; it != end ; ++it ) {
343                        // remove if instrinsic destructor statement
344                        // xxx - test user manually calling intrinsic functions - what happens?
345                        if ( ExprStmt * exprStmt = dynamic_cast< ExprStmt * >( *it ) ) {
346                                ApplicationExpr * appExpr = dynamic_cast< ApplicationExpr * >( exprStmt->get_expr() );
347                                assert( appExpr );
348                                VariableExpr * function = dynamic_cast< VariableExpr * >( appExpr->get_function() );
349                                assert( function );
350                                // check for Intrinsic only - don't want to remove all overridable dtors because autogenerated dtor
351                                // will call all member dtors, and some members may have a user defined dtor.
352                                if ( function->get_var()->get_linkage() == LinkageSpec::Intrinsic ) {
353                                        // don't need to call intrinsic dtor, because it does nothing
354                                } else {
355                                        // non-intrinsic dtors must be called
356                                        *out++ = (*it)->clone();
357                                }
358                        } else {
359                                // could also be a compound statement with a loop, in the case of an array
360                                *out++ = (*it)->clone();
361                        }
362                }
363        }
364
365
366        CompoundStmt * FixInit::mutate( CompoundStmt * compoundStmt ) {
367                // mutate statements - this will also populate dtorStmts list.
368                // don't want to dump all destructors when block is left,
369                // just the destructors associated with variables defined in this block,
370                // so push a new list to the top of the stack so that we can differentiate scopes
371                dtorStmts.push_back( std::list<Statement *>() );
372
373                compoundStmt = PolyMutator::mutate( compoundStmt );
374                std::list< Statement * > & statements = compoundStmt->get_kids();
375
376                insertDtors( dtorStmts.back().begin(), dtorStmts.back().end(), back_inserter( statements ) );
377
378                deleteAll( dtorStmts.back() );
379                dtorStmts.pop_back();
380                return compoundStmt;
381        }
382
383        Statement * FixInit::mutate( ReturnStmt * returnStmt ) {
384                for ( std::list< std::list< Statement * > >::reverse_iterator list = dtorStmts.rbegin(); list != dtorStmts.rend(); ++list ) {
385                        insertDtors( list->begin(), list->end(), back_inserter( stmtsToAdd ) );
386                }
387                return Mutator::mutate( returnStmt );
388        }
389
390        Statement * FixInit::mutate( BranchStmt * branchStmt ) {
391                // TODO: adding to the end of a block isn't sufficient, since
392                // return/break/goto should trigger destructor when block is left.
393                switch( branchStmt->get_type() ) {
394                        case BranchStmt::Continue:
395                        case BranchStmt::Break:
396                                insertDtors( dtorStmts.back().begin(), dtorStmts.back().end(), back_inserter( stmtsToAdd ) );
397                                break;
398                        case BranchStmt::Goto:
399                                // xxx
400                                // if goto leaves a block, generate dtors for every block it leaves
401                                // if goto is in same block but earlier statement, destruct every object that was defined after the statement
402                                break;
403                        default:
404                                assert( false );
405                }
406                return Mutator::mutate( branchStmt );
407        }
408
409
410} // namespace InitTweak
411
412// Local Variables: //
413// tab-width: 4 //
414// mode: c++ //
415// compile-command: "make install" //
416// End: //
Note: See TracBrowser for help on using the repository browser.