source: src/InitTweak/InitTweak.cc @ 4a89b52

Last change on this file since 4a89b52 was 37273c8, checked in by Andrew Beach <ajbeach@…>, 11 months ago

Removed the old-ast-compatable FunctionDecl? constructor. However, enough cases pass nothing polymorphic along some of the uses of the constructor now go to a new monomorphic function constructor.

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