source: src/InitTweak/GenInit.cc @ c5c123f

Last change on this file since c5c123f was 4e2f1b2, checked in by Andrew Beach <ajbeach@…>, 9 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.