source: src/InitTweak/GenInit.cc@ f5212ca

Last change on this file since f5212ca was 4e2f1b2, checked in by Andrew Beach <ajbeach@…>, 19 months ago

Clean-up of GenImplicitCall module. Changing the return type for consistency spilled out into some other files, but that should also saves some operations. The other big one is the template instances were reduced to one and then the templates removed.

  • Property mode set to 100644
File size: 14.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// GenInit.cc -- Generate initializers, and other stuff.
8//
9// Author : Rob Schluntz
10// Created On : Mon May 18 07:44:20 2015
11// Last Modified By : Andrew Beach
12// Last Modified On : Mon Oct 25 13:53:00 2021
13// Update Count : 186
14//
15#include "GenInit.h"
16
17#include <stddef.h> // for NULL
18#include <algorithm> // for any_of
19#include <cassert> // for assert, strict_dynamic_cast, assertf
20#include <deque>
21#include <iterator> // for back_inserter, inserter, back_inse...
22#include <list> // for _List_iterator, list
23
24#include "AST/Decl.hpp"
25#include "AST/Init.hpp"
26#include "AST/Pass.hpp"
27#include "AST/Node.hpp"
28#include "AST/Stmt.hpp"
29#include "CompilationState.h"
30#include "CodeGen/OperatorTable.h"
31#include "Common/SemanticError.h" // for SemanticError
32#include "Common/ToString.hpp" // for toCString
33#include "Common/UniqueName.h" // for UniqueName
34#include "Common/utility.h" // for ValueGuard, maybeClone
35#include "GenPoly/GenPoly.h" // for getFunctionType, isPolyType
36#include "GenPoly/ScopedSet.h" // for ScopedSet, ScopedSet<>::const_iter...
37#include "InitTweak.h" // for isConstExpr, InitExpander, checkIn...
38#include "ResolvExpr/Resolver.h"
39#include "SymTab/GenImplicitCall.hpp" // for genImplicitCall
40#include "SymTab/Mangler.h" // for Mangler
41#include "Tuples/Tuples.h" // for maybeImpure
42#include "Validate/FindSpecialDecls.h" // for SizeType
43
44namespace InitTweak {
45
46namespace {
47
48 // Outer pass finds declarations, for their type could wrap a type that needs hoisting
49 struct HoistArrayDimension_NoResolve final :
50 public ast::WithDeclsToAdd<>, public ast::WithShortCircuiting,
51 public ast::WithGuards, public ast::WithConstTranslationUnit,
52 public ast::WithVisitorRef<HoistArrayDimension_NoResolve>,
53 public ast::WithSymbolTableX<ast::SymbolTable::ErrorDetection::IgnoreErrors> {
54
55 // Inner pass looks within a type, for a part that depends on an expression
56 struct HoistDimsFromTypes final :
57 public ast::WithShortCircuiting, public ast::WithGuards {
58
59 HoistArrayDimension_NoResolve * outer;
60 HoistDimsFromTypes( HoistArrayDimension_NoResolve * outer ) : outer(outer) {}
61
62 // Only intended for visiting through types.
63 // Tolerate, and short-circuit at, the dimension expression of an array type.
64 // (We'll operate on the dimension expression of an array type directly
65 // from the parent type, not by visiting through it)
66 // Look inside type exprs.
67 void previsit( const ast::Node * ) {
68 assert( false && "unsupported node type" );
69 };
70 const ast::Expr * allowedExpr = nullptr;
71 void previsit( const ast::Type * ) {
72 GuardValue( allowedExpr ) = nullptr;
73 }
74 void previsit( const ast::ArrayType * t ) {
75 GuardValue( allowedExpr ) = t->dimension.get();
76 }
77 void previsit( const ast::PointerType * t ) {
78 GuardValue( allowedExpr ) = t->dimension.get();
79 }
80 void previsit( const ast::TypeofType * t ) {
81 GuardValue( allowedExpr ) = t->expr.get();
82 }
83 void previsit( const ast::Expr * e ) {
84 assert( e == allowedExpr &&
85 "only expecting to visit exprs that are dimension exprs or typeof(-) inner exprs" );
86
87 // Skip the tolerated expressions
88 visit_children = false;
89 }
90 void previsit( const ast::TypeExpr * ) {}
91
92 const ast::Type * postvisit(
93 const ast::ArrayType * arrayType ) {
94 static UniqueName dimensionName( "_array_dim" );
95
96 if ( nullptr == arrayType->dimension ) { // if no dimension is given, don't presume to invent one
97 return arrayType;
98 }
99
100 // find size_t; use it as the type for a dim expr
101 ast::ptr<ast::Type> dimType = outer->transUnit().global.sizeType;
102 assert( dimType );
103 add_qualifiers( dimType, ast::CV::Qualifiers( ast::CV::Const ) );
104
105 // Special-case handling: leave the user's dimension expression alone
106 // - requires the user to have followed a careful convention
107 // - may apply to extremely simple applications, but only as windfall
108 // - users of advanced applications will be following the convention on purpose
109 // - CFA maintainers must protect the criteria against leaving too much alone
110
111 // Actual leave-alone cases following are conservative approximations of "cannot vary"
112
113 // Leave alone: literals and enum constants
114 if ( dynamic_cast< const ast::ConstantExpr * >( arrayType->dimension.get() ) ) {
115 return arrayType;
116 }
117
118 // Leave alone: direct use of an object declared to be const
119 const ast::NameExpr * dimn = dynamic_cast< const ast::NameExpr * >( arrayType->dimension.get() );
120 if ( dimn ) {
121 std::vector<ast::SymbolTable::IdData> dimnDefs = outer->symtab.lookupId( dimn->name );
122 if ( dimnDefs.size() == 1 ) {
123 const ast::DeclWithType * dimnDef = dimnDefs[0].id.get();
124 assert( dimnDef && "symbol table binds a name to nothing" );
125 const ast::ObjectDecl * dimOb = dynamic_cast< const ast::ObjectDecl * >( dimnDef );
126 if( dimOb ) {
127 const ast::Type * dimTy = dimOb->type.get();
128 assert( dimTy && "object declaration bearing no type" );
129 // must not hoist some: size_t
130 // must hoist all: pointers and references
131 // the analysis is conservative; BasicType is a simple approximation
132 if ( dynamic_cast< const ast::BasicType * >( dimTy ) ||
133 dynamic_cast< const ast::SueInstType<ast::EnumDecl> * >( dimTy ) ) {
134 if ( dimTy->is_const() ) {
135 // The dimension is certainly re-evaluable, giving the same answer each time.
136 // Our user might be hoping to write the array type in multiple places, having them unify.
137 // Leave the type alone.
138
139 // We believe the new criterion leaves less alone than the old criterion.
140 // Thus, the old criterion should have left the current case alone.
141 // Catch cases that weren't thought through.
142 assert( !Tuples::maybeImpure( arrayType->dimension ) );
143
144 return arrayType;
145 }
146 };
147 }
148 }
149 }
150
151 // Leave alone: any sizeof expression (answer cannot vary during current lexical scope)
152 const ast::SizeofExpr * sz = dynamic_cast< const ast::SizeofExpr * >( arrayType->dimension.get() );
153 if ( sz ) {
154 return arrayType;
155 }
156
157 // General-case handling: change the array-type's dim expr (hoist the user-given content out of the type)
158 // - always safe
159 // - user-unnoticeable in common applications (benign noise in -CFA output)
160 // - may annoy a responsible user of advanced applications (but they can work around)
161 // - protects against misusing advanced features
162 //
163 // The hoist, by example, is:
164 // FROM USER: float a[ rand() ];
165 // TO GCC: const size_t __len_of_a = rand(); float a[ __len_of_a ];
166
167 ast::ObjectDecl * arrayDimension = new ast::ObjectDecl(
168 arrayType->dimension->location,
169 dimensionName.newName(),
170 dimType,
171 new ast::SingleInit(
172 arrayType->dimension->location,
173 arrayType->dimension
174 )
175 );
176
177 ast::ArrayType * mutType = ast::mutate( arrayType );
178 mutType->dimension = new ast::VariableExpr(
179 arrayDimension->location, arrayDimension );
180 outer->declsToAddBefore.push_back( arrayDimension );
181
182 return mutType;
183 } // postvisit( const ast::ArrayType * )
184 }; // struct HoistDimsFromTypes
185
186 ast::Storage::Classes storageClasses;
187 void previsit(
188 const ast::ObjectDecl * decl ) {
189 GuardValue( storageClasses ) = decl->storage;
190 }
191
192 const ast::DeclWithType * postvisit(
193 const ast::ObjectDecl * objectDecl ) {
194
195 if ( !isInFunction() || storageClasses.is_static ) {
196 return objectDecl;
197 }
198
199 const ast::Type * mid = objectDecl->type;
200
201 ast::Pass<HoistDimsFromTypes> hoist{this};
202 const ast::Type * result = mid->accept( hoist );
203
204 return mutate_field( objectDecl, &ast::ObjectDecl::type, result );
205 }
206 };
207
208 struct ReturnFixer final :
209 public ast::WithStmtsToAdd<>, ast::WithGuards, ast::WithShortCircuiting {
210 void previsit( const ast::FunctionDecl * decl );
211 const ast::ReturnStmt * previsit( const ast::ReturnStmt * stmt );
212 private:
213 const ast::FunctionDecl * funcDecl = nullptr;
214 };
215
216 void ReturnFixer::previsit( const ast::FunctionDecl * decl ) {
217 if (decl->linkage == ast::Linkage::Intrinsic) visit_children = false;
218 GuardValue( funcDecl ) = decl;
219 }
220
221 const ast::ReturnStmt * ReturnFixer::previsit(
222 const ast::ReturnStmt * stmt ) {
223 auto & returns = funcDecl->returns;
224 assert( returns.size() < 2 );
225 // Hands off if the function returns a reference.
226 // Don't allocate a temporary if the address is returned.
227 if ( stmt->expr && 1 == returns.size() ) {
228 ast::ptr<ast::DeclWithType> retDecl = returns.front();
229 if ( isConstructable( retDecl->get_type() ) ) {
230 // Explicitly construct the return value using the return
231 // expression and the retVal object.
232 assertf( "" != retDecl->name,
233 "Function %s has unnamed return value.\n",
234 funcDecl->name.c_str() );
235
236 auto retVal = retDecl.strict_as<ast::ObjectDecl>();
237 if ( auto varExpr = stmt->expr.as<ast::VariableExpr>() ) {
238 // Check if the return statement is already set up.
239 if ( varExpr->var == retVal ) return stmt;
240 }
241 const ast::Stmt * ctorStmt = genCtorDtor(
242 retVal->location, "?{}", retVal, stmt->expr );
243 assertf( ctorStmt,
244 "ReturnFixer: genCtorDtor returned nullptr: %s / %s",
245 toString( retVal ).c_str(),
246 toString( stmt->expr ).c_str() );
247 stmtsToAddBefore.push_back( ctorStmt );
248
249 // Return the retVal object.
250 ast::ReturnStmt * mutStmt = ast::mutate( stmt );
251 mutStmt->expr = new ast::VariableExpr(
252 stmt->location, retDecl );
253 return mutStmt;
254 }
255 }
256 return stmt;
257 }
258
259} // namespace
260
261void genInit( ast::TranslationUnit & transUnit ) {
262 ast::Pass<HoistArrayDimension_NoResolve>::run( transUnit );
263 ast::Pass<ReturnFixer>::run( transUnit );
264}
265
266void fixReturnStatements( ast::TranslationUnit & transUnit ) {
267 ast::Pass<ReturnFixer>::run( transUnit );
268}
269
270bool ManagedTypes::isManaged( const ast::Type * type ) const {
271 // references are never constructed
272 if ( dynamic_cast< const ast::ReferenceType * >( type ) ) return false;
273 if ( auto tupleType = dynamic_cast< const ast::TupleType * > ( type ) ) {
274 // tuple is also managed if any of its components are managed
275 for (auto & component : tupleType->types) {
276 if (isManaged(component)) return true;
277 }
278 }
279 // need to clear and reset qualifiers when determining if a type is managed
280 // ValueGuard< Type::Qualifiers > qualifiers( type->get_qualifiers() );
281 auto tmp = shallowCopy(type);
282 tmp->qualifiers = {};
283 // delete tmp at return
284 ast::ptr<ast::Type> guard = tmp;
285 // a type is managed if it appears in the map of known managed types, or if it contains any polymorphism (is a type variable or generic type containing a type variable)
286 return managedTypes.find( Mangle::mangle( tmp, {Mangle::NoOverrideable | Mangle::NoGenericParams | Mangle::Type} ) ) != managedTypes.end() || GenPoly::isPolyType( tmp );
287}
288
289bool ManagedTypes::isManaged( const ast::ObjectDecl * objDecl ) const {
290 const ast::Type * type = objDecl->type;
291 while ( auto at = dynamic_cast< const ast::ArrayType * >( type ) ) {
292 // must always construct VLAs with an initializer, since this is an error in C
293 if ( at->isVarLen && objDecl->init ) return true;
294 type = at->base;
295 }
296 return isManaged( type );
297}
298
299void ManagedTypes::handleDWT( const ast::DeclWithType * dwt ) {
300 // if this function is a user-defined constructor or destructor, mark down the type as "managed"
301 if ( ! dwt->linkage.is_overrideable && CodeGen::isCtorDtor( dwt->name ) ) {
302 auto & params = GenPoly::getFunctionType( dwt->get_type())->params;
303 assert( ! params.empty() );
304 // Type * type = InitTweak::getPointerBase( params.front() );
305 // assert( type );
306 managedTypes.insert( Mangle::mangle( params.front(), {Mangle::NoOverrideable | Mangle::NoGenericParams | Mangle::Type} ) );
307 }
308}
309
310void ManagedTypes::handleStruct( const ast::StructDecl * aggregateDecl ) {
311 // don't construct members, but need to take note if there is a managed member,
312 // because that means that this type is also managed
313 for ( auto & member : aggregateDecl->members ) {
314 if ( auto field = member.as<ast::ObjectDecl>() ) {
315 if ( isManaged( field ) ) {
316 // generic parameters should not play a role in determining whether a generic type is constructed - construct all generic types, so that
317 // polymorphic constructors make generic types managed types
318 ast::StructInstType inst( aggregateDecl );
319 managedTypes.insert( Mangle::mangle( &inst, {Mangle::NoOverrideable | Mangle::NoGenericParams | Mangle::Type} ) );
320 break;
321 }
322 }
323 }
324}
325
326void ManagedTypes::beginScope() { managedTypes.beginScope(); }
327void ManagedTypes::endScope() { managedTypes.endScope(); }
328
329const ast::Stmt * genCtorDtor( const CodeLocation & loc, const std::string & fname, const ast::ObjectDecl * objDecl, const ast::Expr * arg ) {
330 assertf(objDecl, "genCtorDtor passed null objDecl");
331 InitExpander srcParam(arg);
332 return SymTab::genImplicitCall(srcParam, new ast::VariableExpr(loc, objDecl), loc, fname, objDecl);
333}
334
335ast::ConstructorInit * genCtorInit( const CodeLocation & loc, const ast::ObjectDecl * objDecl ) {
336 // call into genImplicitCall from Autogen.h to generate calls to ctor/dtor for each
337 // constructable object
338 InitExpander srcParam{ objDecl->init }, nullParam{ (const ast::Init *)nullptr };
339 ast::ptr< ast::Expr > dstParam = new ast::VariableExpr(loc, objDecl);
340
341 ast::ptr< ast::Stmt > ctor = SymTab::genImplicitCall(
342 srcParam, dstParam, loc, "?{}", objDecl );
343 ast::ptr< ast::Stmt > dtor = SymTab::genImplicitCall(
344 nullParam, dstParam, loc, "^?{}", objDecl,
345 SymTab::LoopBackward );
346
347 // check that either both ctor and dtor are present, or neither
348 assert( (bool)ctor == (bool)dtor );
349
350 if ( ctor ) {
351 // need to remember init expression, in case no ctors exist. If ctor does exist, want to
352 // use ctor expression instead of init.
353 ctor.strict_as< ast::ImplicitCtorDtorStmt >();
354 dtor.strict_as< ast::ImplicitCtorDtorStmt >();
355
356 return new ast::ConstructorInit{ loc, ctor, dtor, objDecl->init };
357 }
358
359 return nullptr;
360}
361
362} // namespace InitTweak
363
364// Local Variables: //
365// tab-width: 4 //
366// mode: c++ //
367// compile-command: "make install" //
368// End: //
Note: See TracBrowser for help on using the repository browser.