source: src/InitTweak/FixInit.cc@ add371c

ADT aaron-thesis arm-eh ast-experimental cleanup-dtors ctor deferred_resn demangler enum forall-pointer-decay gc_noraii jacob/cs343-translation jenkins-sandbox memory new-ast new-ast-unique-expr new-env no_list persistent-indexer pthread-emulation qualifiedEnum resolv-new with_gc
Last change on this file since add371c was 845cedc, checked in by Rob Schluntz <rschlunt@…>, 10 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.