source: src/InitTweak/InitTweak.cc @ fc12f05

Last change on this file since fc12f05 was c6b4432, checked in by Andrew Beach <ajbeach@…>, 8 months ago

Remove BaseSyntaxNode? and clean-up.

  • Property mode set to 100644
File size: 16.4 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// InitTweak.cc --
8//
9// Author           : Rob Schluntz
10// Created On       : Fri May 13 11:26:36 2016
11// Last Modified By : Andrew Beach
12// Last Modified On : Wed Sep 22  9:50:00 2022
13// Update Count     : 21
14//
15
16#include <algorithm>               // for find, all_of
17#include <cassert>                 // for assertf, assert, strict_dynamic_cast
18#include <iostream>                // for ostream, cerr, endl
19#include <iterator>                // for back_insert_iterator, back_inserter
20#include <memory>                  // for __shared_ptr
21#include <vector>
22
23#include "AST/Expr.hpp"
24#include "AST/Init.hpp"
25#include "AST/Inspect.hpp"
26#include "AST/Node.hpp"
27#include "AST/Pass.hpp"
28#include "AST/Stmt.hpp"
29#include "AST/Type.hpp"
30#include "CodeGen/OperatorTable.h" // for isConstructor, isDestructor, isCto...
31#include "Common/SemanticError.h"  // for SemanticError
32#include "Common/UniqueName.h"     // for UniqueName
33#include "Common/utility.h"        // for toString, deleteAll, maybeClone
34#include "GenPoly/GenPoly.h"       // for getFunctionType
35#include "InitTweak.h"
36#include "ResolvExpr/Unify.h"      // for typesCompatibleIgnoreQualifiers
37#include "Tuples/Tuples.h"         // for Tuples::isTtype
38
39namespace InitTweak {
40        namespace {
41                struct HasDesignations_new : public ast::WithShortCircuiting {
42                        bool result = false;
43
44                        void previsit( const ast::Node * ) {
45                                // short circuit if we already know there are designations
46                                if ( result ) visit_children = false;
47                        }
48
49                        void previsit( const ast::Designation * des ) {
50                                // short circuit if we already know there are designations
51                                if ( result ) visit_children = false;
52                                else if ( ! des->designators.empty() ) {
53                                        result = true;
54                                        visit_children = false;
55                                }
56                        }
57                };
58
59                struct InitDepthChecker_new {
60                        bool result = true;
61                        const ast::Type * type;
62                        int curDepth = 0, maxDepth = 0;
63                        InitDepthChecker_new( const ast::Type * type ) : type( type ) {
64                                const ast::Type * t = type;
65                                while ( auto at = dynamic_cast< const ast::ArrayType * >( t ) ) {
66                                        maxDepth++;
67                                        t = at->base;
68                                }
69                                maxDepth++;
70                        }
71                        void previsit( ast::ListInit const * ) {
72                                curDepth++;
73                                if ( curDepth > maxDepth ) result = false;
74                        }
75                        void postvisit( ast::ListInit const * ) {
76                                curDepth--;
77                        }
78                };
79
80                struct InitFlattener_new : public ast::WithShortCircuiting {
81                        std::vector< ast::ptr< ast::Expr > > argList;
82
83                        void previsit( const ast::SingleInit * singleInit ) {
84                                visit_children = false;
85                                argList.emplace_back( singleInit->value );
86                        }
87                };
88
89        } // anonymous namespace
90
91        bool isDesignated( const ast::Init * init ) {
92                ast::Pass<HasDesignations_new> finder;
93                maybe_accept( init, finder );
94                return finder.core.result;
95        }
96
97        bool checkInitDepth( const ast::ObjectDecl * objDecl ) {
98                ast::Pass<InitDepthChecker_new> checker( objDecl->type );
99                maybe_accept( objDecl->init.get(), checker );
100                return checker.core.result;
101        }
102
103std::vector< ast::ptr< ast::Expr > > makeInitList( const ast::Init * init ) {
104        ast::Pass< InitFlattener_new > flattener;
105        maybe_accept( init, flattener );
106        return std::move( flattener.core.argList );
107}
108
109class InitExpander_new::ExpanderImpl {
110public:
111        virtual ~ExpanderImpl() = default;
112        virtual std::vector< ast::ptr< ast::Expr > > next( IndexList & indices ) = 0;
113        virtual ast::ptr< ast::Stmt > buildListInit(
114                ast::UntypedExpr * callExpr, IndexList & indices ) = 0;
115};
116
117namespace {
118        template< typename Out >
119        void buildCallExpr(
120                ast::UntypedExpr * callExpr, const ast::Expr * index, const ast::Expr * dimension,
121                const ast::Init * init, Out & out
122        ) {
123                const CodeLocation & loc = init->location;
124
125                auto cond = new ast::UntypedExpr{
126                        loc, new ast::NameExpr{ loc, "?<?" }, { index, dimension } };
127
128                std::vector< ast::ptr< ast::Expr > > args = makeInitList( init );
129                splice( callExpr->args, args );
130
131                out.emplace_back( new ast::IfStmt{ loc, cond, new ast::ExprStmt{ loc, callExpr } } );
132
133                out.emplace_back( new ast::ExprStmt{
134                        loc, new ast::UntypedExpr{ loc, new ast::NameExpr{ loc, "++?" }, { index } } } );
135        }
136
137        template< typename Out >
138        void build(
139                ast::UntypedExpr * callExpr, const InitExpander_new::IndexList & indices,
140                const ast::Init * init, Out & out
141        ) {
142                if ( indices.empty() ) return;
143
144                unsigned idx = 0;
145
146                const ast::Expr * index = indices[idx++];
147                assert( idx != indices.size() );
148                const ast::Expr * dimension = indices[idx++];
149
150                if ( idx == indices.size() ) {
151                        if ( auto listInit = dynamic_cast< const ast::ListInit * >( init ) ) {
152                                for ( const ast::Init * init : *listInit ) {
153                                        buildCallExpr( shallowCopy(callExpr), index, dimension, init, out );
154                                }
155                        } else {
156                                buildCallExpr( shallowCopy(callExpr), index, dimension, init, out );
157                        }
158                } else {
159                        const CodeLocation & loc = init->location;
160
161                        unsigned long cond = 0;
162                        auto listInit = dynamic_cast< const ast::ListInit * >( init );
163                        if ( ! listInit ) { SemanticError( loc, "unbalanced list initializers" ); }
164
165                        static UniqueName targetLabel( "L__autogen__" );
166                        ast::Label switchLabel{
167                                loc, targetLabel.newName(), { new ast::Attribute{ "unused" } } };
168
169                        std::vector< ast::ptr< ast::CaseClause > > branches;
170                        for ( const ast::Init * init : *listInit ) {
171                                auto condition = ast::ConstantExpr::from_ulong( loc, cond );
172                                ++cond;
173
174                                std::vector< ast::ptr< ast::Stmt > > stmts;
175                                build( callExpr, indices, init, stmts );
176                                stmts.emplace_back(
177                                        new ast::BranchStmt{ loc, ast::BranchStmt::Break, switchLabel } );
178                                branches.emplace_back( new ast::CaseClause{ loc, condition, std::move( stmts ) } );
179                        }
180                        out.emplace_back( new ast::SwitchStmt{ loc, index, std::move( branches ) } );
181                        out.emplace_back( new ast::NullStmt{ loc, { switchLabel } } );
182                }
183        }
184
185        class InitImpl_new final : public InitExpander_new::ExpanderImpl {
186                ast::ptr< ast::Init > init;
187        public:
188                InitImpl_new( const ast::Init * i ) : init( i ) {}
189
190                std::vector< ast::ptr< ast::Expr > > next( InitExpander_new::IndexList & ) override {
191                        return makeInitList( init );
192                }
193
194                ast::ptr< ast::Stmt > buildListInit(
195                        ast::UntypedExpr * callExpr, InitExpander_new::IndexList & indices
196                ) override {
197                        // If array came with an initializer list, initialize each element. We may have more
198                        // initializers than elements of the array; need to check at each index that we have
199                        // not exceeded size. We may have fewer initializers than elements in the array; need
200                        // to default-construct remaining elements. To accomplish this, generate switch
201                        // statement consuming all of expander's elements
202
203                        if ( ! init ) return {};
204
205                        std::list< ast::ptr< ast::Stmt > > stmts;
206                        build( callExpr, indices, init, stmts );
207                        if ( stmts.empty() ) {
208                                return {};
209                        } else {
210                                auto block = new ast::CompoundStmt{ init->location, std::move( stmts ) };
211                                init = nullptr;  // consumed in creating the list init
212                                return block;
213                        }
214                }
215        };
216
217        class ExprImpl_new final : public InitExpander_new::ExpanderImpl {
218                ast::ptr< ast::Expr > arg;
219        public:
220                ExprImpl_new( const ast::Expr * a ) : arg( a ) {}
221
222                std::vector< ast::ptr< ast::Expr > > next(
223                        InitExpander_new::IndexList & indices
224                ) override {
225                        if ( ! arg ) return {};
226
227                        const CodeLocation & loc = arg->location;
228                        const ast::Expr * expr = arg;
229                        for ( auto it = indices.rbegin(); it != indices.rend(); ++it ) {
230                                // go through indices and layer on subscript exprs ?[?]
231                                ++it;
232                                expr = new ast::UntypedExpr{
233                                        loc, new ast::NameExpr{ loc, "?[?]" }, { expr, *it } };
234                        }
235                        return { expr };
236                }
237
238                ast::ptr< ast::Stmt > buildListInit(
239                        ast::UntypedExpr *, InitExpander_new::IndexList &
240                ) override {
241                        return {};
242                }
243        };
244} // anonymous namespace
245
246InitExpander_new::InitExpander_new( const ast::Init * init )
247: expander( new InitImpl_new{ init } ), crnt(), indices() {}
248
249InitExpander_new::InitExpander_new( const ast::Expr * expr )
250: expander( new ExprImpl_new{ expr } ), crnt(), indices() {}
251
252std::vector< ast::ptr< ast::Expr > > InitExpander_new::operator* () { return crnt; }
253
254InitExpander_new & InitExpander_new::operator++ () {
255        crnt = expander->next( indices );
256        return *this;
257}
258
259/// builds statement which has the same semantics as a C-style list initializer (for array
260/// initializers) using callExpr as the base expression to perform initialization
261ast::ptr< ast::Stmt > InitExpander_new::buildListInit( ast::UntypedExpr * callExpr ) {
262        return expander->buildListInit( callExpr, indices );
263}
264
265void InitExpander_new::addArrayIndex( const ast::Expr * index, const ast::Expr * dimension ) {
266        indices.emplace_back( index );
267        indices.emplace_back( dimension );
268}
269
270void InitExpander_new::clearArrayIndices() { indices.clear(); }
271
272bool InitExpander_new::addReference() {
273        for ( ast::ptr< ast::Expr > & expr : crnt ) {
274                expr = new ast::AddressExpr{ expr };
275        }
276        return ! crnt.empty();
277}
278
279        const ast::Type * getTypeofThis( const ast::FunctionType * ftype ) {
280                assertf( ftype, "getTypeofThis: nullptr ftype" );
281                const std::vector<ast::ptr<ast::Type>> & params = ftype->params;
282                assertf( !params.empty(), "getTypeofThis: ftype with 0 parameters: %s",
283                                toString( ftype ).c_str() );
284                const ast::ReferenceType * refType =
285                        params.front().strict_as<ast::ReferenceType>();
286                return refType->base;
287        }
288
289        const ast::ObjectDecl * getParamThis(const ast::FunctionDecl * func) {
290                assertf( func, "getParamThis: nullptr ftype" );
291                auto & params = func->params;
292                assertf( ! params.empty(), "getParamThis: ftype with 0 parameters: %s", toString( func ).c_str());
293                return params.front().strict_as<ast::ObjectDecl>();
294        }
295
296        bool tryConstruct( const ast::DeclWithType * dwt ) {
297                auto objDecl = dynamic_cast< const ast::ObjectDecl * >( dwt );
298                if ( ! objDecl ) return false;
299                return (objDecl->init == nullptr ||
300                                ( objDecl->init != nullptr && objDecl->init->maybeConstructed ))
301                        && ! objDecl->storage.is_extern
302                        && isConstructable( objDecl->type );
303        }
304
305        bool isConstructable( const ast::Type * type ) {
306                return ! dynamic_cast< const ast::VarArgsType * >( type ) && ! dynamic_cast< const ast::ReferenceType * >( type )
307                && ! dynamic_cast< const ast::FunctionType * >( type ) && ! Tuples::isTtype( type );
308        }
309
310        struct CallFinder_new final {
311                std::vector< const ast::Expr * > matches;
312                const std::vector< std::string > names;
313
314                CallFinder_new( std::vector< std::string > && ns ) : matches(), names( std::move(ns) ) {}
315
316                void handleCallExpr( const ast::Expr * expr ) {
317                        std::string fname = getFunctionName( expr );
318                        if ( std::find( names.begin(), names.end(), fname ) != names.end() ) {
319                                matches.emplace_back( expr );
320                        }
321                }
322
323                void postvisit( const ast::ApplicationExpr * expr ) { handleCallExpr( expr ); }
324                void postvisit( const ast::UntypedExpr *     expr ) { handleCallExpr( expr ); }
325        };
326
327        std::vector< const ast::Expr * > collectCtorDtorCalls( const ast::Stmt * stmt ) {
328                ast::Pass< CallFinder_new > finder{ std::vector< std::string >{ "?{}", "^?{}" } };
329                maybe_accept( stmt, finder );
330                return std::move( finder.core.matches );
331        }
332
333        namespace {
334                template <typename Predicate>
335                bool allofCtorDtor( const ast::Stmt * stmt, const Predicate & pred ) {
336                        std::vector< const ast::Expr * > callExprs = collectCtorDtorCalls( stmt );
337                        return std::all_of( callExprs.begin(), callExprs.end(), pred );
338                }
339        }
340
341        bool isIntrinsicSingleArgCallStmt( const ast::Stmt * stmt ) {
342                return allofCtorDtor( stmt, []( const ast::Expr * callExpr ){
343                        if ( const ast::ApplicationExpr * appExpr = isIntrinsicCallExpr( callExpr ) ) {
344                                const ast::FunctionType * funcType =
345                                        GenPoly::getFunctionType( appExpr->func->result );
346                                assert( funcType );
347                                return funcType->params.size() == 1;
348                        }
349                        return false;
350                });
351        }
352
353        // looks like some other such codegen uses UntypedExpr and does not create fake function. should revisit afterwards
354        // following passes may accidentally resolve this expression if returned as untyped...
355        ast::Expr * createBitwiseAssignment (const ast::Expr * dst, const ast::Expr * src) {
356                static ast::ptr<ast::FunctionDecl> assign = nullptr;
357                if (!assign) {
358                        auto td = new ast::TypeDecl(CodeLocation(), "T", {}, nullptr, ast::TypeDecl::Dtype, true);
359                        assign = new ast::FunctionDecl(CodeLocation(), "?=?", {td},
360                        { new ast::ObjectDecl(CodeLocation(), "_dst", new ast::ReferenceType(new ast::TypeInstType("T", td))),
361                          new ast::ObjectDecl(CodeLocation(), "_src", new ast::TypeInstType("T", td))},
362                        { new ast::ObjectDecl(CodeLocation(), "_ret", new ast::TypeInstType("T", td))}, nullptr, {}, ast::Linkage::Intrinsic);
363                }
364                if (dst->result.as<ast::ReferenceType>()) {
365                        for (int depth = dst->result->referenceDepth(); depth > 0; depth--) {
366                                dst = new ast::AddressExpr(dst);
367                        }
368                } else {
369                        dst = new ast::CastExpr(dst, new ast::ReferenceType(dst->result, {}));
370                }
371                if (src->result.as<ast::ReferenceType>()) {
372                        for (int depth = src->result->referenceDepth(); depth > 0; depth--) {
373                                src = new ast::AddressExpr(src);
374                        }
375                }
376                auto var = ast::VariableExpr::functionPointer(dst->location, assign);
377                auto app = new ast::ApplicationExpr(dst->location, var, {dst, src});
378                // Skip the resolver, just set the result to the correct type.
379                app->result = ast::deepCopy( src->result );
380                return app;
381        }
382
383        struct ConstExprChecker_new : public ast::WithShortCircuiting {
384                // most expressions are not const expr
385                void previsit( const ast::Expr * ) { result = false; visit_children = false; }
386
387                void previsit( const ast::AddressExpr *addressExpr ) {
388                        visit_children = false;
389                        const ast::Expr * arg = addressExpr->arg;
390
391                        // address of a variable or member expression is constexpr
392                        if ( ! dynamic_cast< const ast::NameExpr * >( arg )
393                        && ! dynamic_cast< const ast::VariableExpr * >( arg )
394                        && ! dynamic_cast< const ast::MemberExpr * >( arg )
395                        && ! dynamic_cast< const ast::UntypedMemberExpr * >( arg ) ) result = false;
396                }
397
398                // these expressions may be const expr, depending on their children
399                void previsit( const ast::SizeofExpr * ) {}
400                void previsit( const ast::AlignofExpr * ) {}
401                void previsit( const ast::UntypedOffsetofExpr * ) {}
402                void previsit( const ast::OffsetofExpr * ) {}
403                void previsit( const ast::OffsetPackExpr * ) {}
404                void previsit( const ast::CommaExpr * ) {}
405                void previsit( const ast::LogicalExpr * ) {}
406                void previsit( const ast::ConditionalExpr * ) {}
407                void previsit( const ast::CastExpr * ) {}
408                void previsit( const ast::ConstantExpr * ) {}
409
410                void previsit( const ast::VariableExpr * varExpr ) {
411                        visit_children = false;
412
413                        if ( auto inst = varExpr->result.as<ast::EnumInstType>() ) {
414                                long long int value;
415                                if ( inst->base->valueOf( varExpr->var, value ) ) {
416                                        // enumerators are const expr
417                                        return;
418                                }
419                        }
420                        result = false;
421                }
422
423                bool result = true;
424        };
425
426        bool isConstExpr( const ast::Expr * expr ) {
427                if ( expr ) {
428                        ast::Pass<ConstExprChecker_new> checker;
429                        expr->accept( checker );
430                        return checker.core.result;
431                }
432                return true;
433        }
434
435        bool isConstExpr( const ast::Init * init ) {
436                if ( init ) {
437                        ast::Pass<ConstExprChecker_new> checker;
438                        init->accept( checker );
439                        return checker.core.result;
440                } // if
441                // for all intents and purposes, no initializer means const expr
442                return true;
443        }
444
445bool isAssignment( const ast::FunctionDecl * decl ) {
446        return CodeGen::isAssignment( decl->name ) && isCopyFunction( decl );
447}
448
449bool isDestructor( const ast::FunctionDecl * decl ) {
450        return CodeGen::isDestructor( decl->name );
451}
452
453bool isDefaultConstructor( const ast::FunctionDecl * decl ) {
454        return CodeGen::isConstructor( decl->name ) && 1 == decl->params.size();
455}
456
457bool isCopyConstructor( const ast::FunctionDecl * decl ) {
458        return CodeGen::isConstructor( decl->name ) && 2 == decl->params.size();
459}
460
461bool isCopyFunction( const ast::FunctionDecl * decl ) {
462        const ast::FunctionType * ftype = decl->type;
463        if ( ftype->params.size() != 2 ) return false;
464
465        const ast::Type * t1 = ast::getPointerBase( ftype->params.front() );
466        if ( ! t1 ) return false;
467        const ast::Type * t2 = ftype->params.back();
468
469        return ResolvExpr::typesCompatibleIgnoreQualifiers( t1, t2 );
470}
471
472        #if defined( __x86_64 ) || defined( __i386 ) // assembler comment to prevent assembler warning message
473                #define ASM_COMMENT "#"
474        #else // defined( __ARM_ARCH )
475                #define ASM_COMMENT "//"
476        #endif
477        static const char * const data_section =  ".data" ASM_COMMENT;
478        static const char * const tlsd_section = ".tdata" ASM_COMMENT;
479
480        void addDataSectionAttribute( ast::ObjectDecl * objDecl ) {
481                const bool is_tls = objDecl->storage.is_threadlocal_any();
482                const char * section = is_tls ? tlsd_section : data_section;
483                objDecl->attributes.push_back(new ast::Attribute("section", {
484                        ast::ConstantExpr::from_string(objDecl->location, section)
485                }));
486        }
487
488}
Note: See TracBrowser for help on using the repository browser.