source: src/InitTweak/InitTweak.cpp @ 26d40a1

Last change on this file since 26d40a1 was c92bdcc, checked in by Andrew Beach <ajbeach@…>, 6 months ago

Updated the rest of the names in src/ (except for the generated files).

  • Property mode set to 100644
File size: 16.1 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.cpp --
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.hpp"
17
18#include <algorithm>                  // for find, all_of
19#include <cassert>                    // for assertf, assert, strict_dynamic_...
20#include <iostream>                   // for ostream, cerr, endl
21#include <iterator>                   // for back_insert_iterator, back_inser...
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.hpp"  // for isConstructor, isDestructor, isC...
33#include "Common/SemanticError.hpp"   // for SemanticError
34#include "Common/ToString.hpp"        // for toCString
35#include "Common/UniqueName.hpp"      // for UniqueName
36#include "GenPoly/GenPoly.hpp"        // for getFunctionType
37#include "ResolvExpr/Unify.hpp"       // for typesCompatibleIgnoreQualifiers
38#include "Tuples/Tuples.hpp"          // 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 if ( 1 == stmts.size() ) {
192                                return std::move( stmts.front() );
193                        } else {
194                                auto block = new ast::CompoundStmt{ init->location, std::move( stmts ) };
195                                init = nullptr;  // consumed in creating the list init
196                                return block;
197                        }
198                }
199        };
200
201        class ExprImpl final : public InitExpander::ExpanderImpl {
202                ast::ptr< ast::Expr > arg;
203        public:
204                ExprImpl( const ast::Expr * a ) : arg( a ) {}
205
206                std::vector< ast::ptr< ast::Expr > > next(
207                        InitExpander::IndexList & indices
208                ) override {
209                        if ( !arg ) return {};
210
211                        const CodeLocation & loc = arg->location;
212                        const ast::Expr * expr = arg;
213                        for ( auto it = indices.rbegin(); it != indices.rend(); ++it ) {
214                                // Go through indices and layer on subscript exprs ?[?].
215                                ++it;
216                                expr = new ast::UntypedExpr{
217                                        loc, new ast::NameExpr{ loc, "?[?]" }, { expr, *it } };
218                        }
219                        return { expr };
220                }
221
222                ast::ptr< ast::Stmt > buildListInit(
223                        ast::UntypedExpr *, InitExpander::IndexList &
224                ) override {
225                        return {};
226                }
227        };
228
229        struct CallFinder final {
230                std::vector< const ast::Expr * > matches;
231                const std::vector< std::string > names;
232
233                CallFinder( std::vector< std::string > && ns ) : matches(), names( std::move(ns) ) {}
234
235                void handleCallExpr( const ast::Expr * expr ) {
236                        std::string fname = getFunctionName( expr );
237                        if ( std::find( names.begin(), names.end(), fname ) != names.end() ) {
238                                matches.emplace_back( expr );
239                        }
240                }
241
242                void postvisit( const ast::ApplicationExpr * expr ) { handleCallExpr( expr ); }
243                void postvisit( const ast::UntypedExpr *     expr ) { handleCallExpr( expr ); }
244        };
245
246        template <typename Predicate>
247        bool allofCtorDtor( const ast::Stmt * stmt, const Predicate & pred ) {
248                std::vector< const ast::Expr * > callExprs = collectCtorDtorCalls( stmt );
249                return std::all_of( callExprs.begin(), callExprs.end(), pred );
250        }
251
252        struct ConstExprChecker : public ast::WithShortCircuiting {
253                // Most expressions are not const-expr.
254                void previsit( const ast::Expr * ) { result = false; visit_children = false; }
255
256                void previsit( const ast::AddressExpr *addressExpr ) {
257                        visit_children = false;
258                        const ast::Expr * arg = addressExpr->arg;
259
260                        // Address of a variable or member expression is const-expr.
261                        if ( !dynamic_cast< const ast::NameExpr * >( arg )
262                                && !dynamic_cast< const ast::VariableExpr * >( arg )
263                                && !dynamic_cast< const ast::MemberExpr * >( arg )
264                                && !dynamic_cast< const ast::UntypedMemberExpr * >( arg ) ) result = false;
265                }
266
267                // These expressions may be const expr, depending on their children.
268                void previsit( const ast::SizeofExpr * ) {}
269                void previsit( const ast::AlignofExpr * ) {}
270                void previsit( const ast::UntypedOffsetofExpr * ) {}
271                void previsit( const ast::OffsetofExpr * ) {}
272                void previsit( const ast::OffsetPackExpr * ) {}
273                void previsit( const ast::CommaExpr * ) {}
274                void previsit( const ast::LogicalExpr * ) {}
275                void previsit( const ast::ConditionalExpr * ) {}
276                void previsit( const ast::CastExpr * ) {}
277                void previsit( const ast::ConstantExpr * ) {}
278
279                void previsit( const ast::VariableExpr * varExpr ) {
280                        visit_children = false;
281
282                        if ( auto inst = varExpr->result.as<ast::EnumInstType>() ) {
283                                long long int value;
284                                if ( inst->base->valueOf( varExpr->var, value ) ) {
285                                        // enumerators are const expr
286                                        return;
287                                }
288                        }
289                        result = false;
290                }
291
292                bool result = true;
293        };
294} // namespace
295
296bool isAssignment( const ast::FunctionDecl * decl ) {
297        return CodeGen::isAssignment( decl->name ) && isCopyFunction( decl );
298}
299
300bool isDestructor( const ast::FunctionDecl * decl ) {
301        return CodeGen::isDestructor( decl->name );
302}
303
304bool isDefaultConstructor( const ast::FunctionDecl * decl ) {
305        return CodeGen::isConstructor( decl->name ) && 1 == decl->params.size();
306}
307
308bool isCopyConstructor( const ast::FunctionDecl * decl ) {
309        return CodeGen::isConstructor( decl->name ) && 2 == decl->params.size();
310}
311
312bool isCopyFunction( const ast::FunctionDecl * decl ) {
313        const ast::FunctionType * ftype = decl->type;
314        if ( ftype->params.size() != 2 ) return false;
315
316        const ast::Type * t1 = ast::getPointerBase( ftype->params.front() );
317        if ( ! t1 ) return false;
318        const ast::Type * t2 = ftype->params.back();
319
320        return ResolvExpr::typesCompatibleIgnoreQualifiers( t1, t2 );
321}
322
323const ast::Type * getTypeofThis( const ast::FunctionType * ftype ) {
324        assertf( ftype, "getTypeofThis: nullptr ftype" );
325        const std::vector<ast::ptr<ast::Type>> & params = ftype->params;
326        assertf( !params.empty(), "getTypeofThis: ftype with 0 parameters: %s",
327                        toCString( ftype ) );
328        const ast::ReferenceType * refType =
329                params.front().strict_as<ast::ReferenceType>();
330        return refType->base;
331}
332
333const ast::ObjectDecl * getParamThis(const ast::FunctionDecl * func) {
334        assertf( func, "getParamThis: nullptr ftype" );
335        auto & params = func->params;
336        assertf( !params.empty(), "getParamThis: ftype with 0 parameters: %s", toCString( func ));
337        return params.front().strict_as<ast::ObjectDecl>();
338}
339
340// looks like some other such codegen uses UntypedExpr and does not create fake function. should revisit afterwards
341// following passes may accidentally resolve this expression if returned as untyped...
342ast::Expr * createBitwiseAssignment(const ast::Expr * dst, const ast::Expr * src) {
343        static ast::ptr<ast::FunctionDecl> assign = nullptr;
344        if (!assign) {
345                auto td = new ast::TypeDecl(CodeLocation(), "T", {}, nullptr, ast::TypeDecl::Dtype, true);
346                assign = new ast::FunctionDecl(CodeLocation(), "?=?", {td}, {},
347                { new ast::ObjectDecl(CodeLocation(), "_dst", new ast::ReferenceType(new ast::TypeInstType("T", td))),
348                  new ast::ObjectDecl(CodeLocation(), "_src", new ast::TypeInstType("T", td))},
349                { new ast::ObjectDecl(CodeLocation(), "_ret", new ast::TypeInstType("T", td))}, nullptr, {}, ast::Linkage::Intrinsic);
350        }
351        if (dst->result.as<ast::ReferenceType>()) {
352                for (int depth = dst->result->referenceDepth(); depth > 0; depth--) {
353                        dst = new ast::AddressExpr(dst);
354                }
355        } else {
356                dst = new ast::CastExpr(dst, new ast::ReferenceType(dst->result, {}));
357        }
358        if (src->result.as<ast::ReferenceType>()) {
359                for (int depth = src->result->referenceDepth(); depth > 0; depth--) {
360                        src = new ast::AddressExpr(src);
361                }
362        }
363        auto var = ast::VariableExpr::functionPointer(dst->location, assign);
364        auto app = new ast::ApplicationExpr(dst->location, var, {dst, src});
365        // Skip the resolver, just set the result to the correct type.
366        app->result = ast::deepCopy( src->result );
367        return app;
368}
369
370std::vector< ast::ptr< ast::Expr > > makeInitList( const ast::Init * init ) {
371        ast::Pass< InitFlattener > flattener;
372        maybe_accept( init, flattener );
373        return std::move( flattener.core.argList );
374}
375
376bool tryConstruct( const ast::DeclWithType * dwt ) {
377        auto objDecl = dynamic_cast< const ast::ObjectDecl * >( dwt );
378        if ( !objDecl ) return false;
379        return (objDecl->init == nullptr ||
380                        ( objDecl->init != nullptr && objDecl->init->maybeConstructed ))
381                && !objDecl->storage.is_extern
382                && isConstructable( objDecl->type );
383}
384
385bool isConstructable( const ast::Type * type ) {
386        return !dynamic_cast< const ast::VarArgsType * >( type ) && !dynamic_cast< const ast::ReferenceType * >( type )
387                && !dynamic_cast< const ast::FunctionType * >( type ) && !Tuples::isTtype( type );
388}
389
390bool isDesignated( const ast::Init * init ) {
391        return ( init ) ? ast::Pass<HasDesignations>::read( init ) : false;
392}
393
394bool checkInitDepth( const ast::ObjectDecl * objDecl ) {
395        return ( objDecl->init ) ? ast::Pass<InitDepthChecker>::read(
396                objDecl->init.get(), objDecl->type.get() ) : true;
397}
398
399bool isIntrinsicSingleArgCallStmt( const ast::Stmt * stmt ) {
400        return allofCtorDtor( stmt, []( const ast::Expr * callExpr ){
401                if ( const ast::ApplicationExpr * appExpr = isIntrinsicCallExpr( callExpr ) ) {
402                        const ast::FunctionType * funcType =
403                                GenPoly::getFunctionType( appExpr->func->result );
404                        assert( funcType );
405                        return funcType->params.size() == 1;
406                }
407                return false;
408        });
409}
410
411std::vector< const ast::Expr * > collectCtorDtorCalls( const ast::Stmt * stmt ) {
412        ast::Pass< CallFinder > finder{ std::vector< std::string >{ "?{}", "^?{}" } };
413        maybe_accept( stmt, finder );
414        return std::move( finder.core.matches );
415}
416
417bool isConstExpr( const ast::Expr * expr ) {
418        return ( expr ) ? ast::Pass<ConstExprChecker>::read( expr ) : true;
419}
420
421bool isConstExpr( const ast::Init * init ) {
422        // for all intents and purposes, no initializer means const expr
423        return ( init ) ? ast::Pass<ConstExprChecker>::read( init ) : true;
424}
425
426#if defined( __x86_64 ) || defined( __i386 ) // assembler comment to prevent assembler warning message
427        #define ASM_COMMENT "#"
428#else // defined( __ARM_ARCH )
429        #define ASM_COMMENT "//"
430#endif
431static const char * const data_section =  ".data" ASM_COMMENT;
432static const char * const tlsd_section = ".tdata" ASM_COMMENT;
433
434void addDataSectionAttribute( ast::ObjectDecl * objDecl ) {
435        const bool is_tls = objDecl->storage.is_threadlocal_any();
436        const char * section = is_tls ? tlsd_section : data_section;
437        objDecl->attributes.push_back(new ast::Attribute("section", {
438                ast::ConstantExpr::from_string(objDecl->location, section)
439        }));
440}
441
442InitExpander::InitExpander( const ast::Init * init )
443: expander( new InitImpl{ init } ), crnt(), indices() {}
444
445InitExpander::InitExpander( const ast::Expr * expr )
446: expander( new ExprImpl{ expr } ), crnt(), indices() {}
447
448std::vector< ast::ptr< ast::Expr > > InitExpander::operator* () { return crnt; }
449
450InitExpander & InitExpander::operator++ () {
451        crnt = expander->next( indices );
452        return *this;
453}
454
455/// builds statement which has the same semantics as a C-style list initializer (for array
456/// initializers) using callExpr as the base expression to perform initialization
457ast::ptr< ast::Stmt > InitExpander::buildListInit( ast::UntypedExpr * callExpr ) {
458        return expander->buildListInit( callExpr, indices );
459}
460
461void InitExpander::addArrayIndex( const ast::Expr * index, const ast::Expr * dimension ) {
462        indices.emplace_back( index );
463        indices.emplace_back( dimension );
464}
465
466void InitExpander::clearArrayIndices() { indices.clear(); }
467
468bool InitExpander::addReference() {
469        for ( ast::ptr< ast::Expr > & expr : crnt ) {
470                expr = new ast::AddressExpr{ expr };
471        }
472        return !crnt.empty();
473}
474
475} // namespace InitTweak
Note: See TracBrowser for help on using the repository browser.