source: src/AST/Pass.proto.hpp @ 634cb80

Last change on this file since 634cb80 was e0069bd, checked in by Andrew Beach <ajbeach@…>, 12 months ago

Renaming of some SymbolTable? functions because they are different and should be tracked. Removed an extra forward declaration and modified some indentation in the area.

  • Property mode set to 100644
File size: 19.1 KB
RevLine 
[04124c4]1//
2// Cforall Version 1.0.0 Copyright (C) 2019 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// Pass.impl.hpp --
8//
9// Author           : Thierry Delisle
10// Created On       : Thu May 09 15::37::05 2019
11// Last Modified By :
12// Last Modified On :
13// Update Count     :
14//
15
[f47f887]16#pragma once
17// IWYU pragma: private, include "Pass.hpp"
18
[2d0f918]19#include "Common/Iterate.hpp"
[c15085d]20#include "Common/Stats/Heap.h"
[f47f887]21namespace ast {
[26e6d88]22        template<typename core_t> class Pass;
23        class TranslationUnit;
24        struct PureVisitor;
25        template<typename node_t> node_t * deepCopy( const node_t * );
[cad9edb]26}
[d3aa64f1]27
[2d0f918]28#ifdef PEDANTIC_PASS_ASSERT
[efe89894]29#define __pedantic_pass_assert(...) assert(__VA_ARGS__)
[2d0f918]30#define __pedantic_pass_assertf(...) assertf(__VA_ARGS__)
31#else
32#define __pedantic_pass_assert(...)
33#define __pedantic_pass_assertf(...)
34#endif
35
[cad9edb]36namespace ast::__pass {
[4ec9513]37
[26e6d88]38typedef std::function<void( void * )> cleanup_func_t;
39typedef std::function<void( cleanup_func_t, void * )> at_cleanup_t;
[f47f887]40
[26e6d88]41// boolean reference that may be null
42// either refers to a boolean value or is null and returns true
43class bool_ref {
44public:
45        bool_ref() = default;
46        ~bool_ref() = default;
[f47f887]47
[26e6d88]48        operator bool() { return m_ref ? *m_ref : true; }
49        bool operator=( bool val ) { assert(m_ref); return *m_ref = val; }
[f47f887]50
[26e6d88]51private:
[f47f887]52
[26e6d88]53        friend class visit_children_guard;
[f47f887]54
[26e6d88]55        bool * set( bool * val ) {
56                bool * prev = m_ref;
57                m_ref = val;
58                return prev;
59        }
[f47f887]60
[26e6d88]61        bool * m_ref = nullptr;
62};
63
64// Implementation of the guard value
65// Created inside the visit scope
66class guard_value {
67public:
68        /// Push onto the cleanup
69        guard_value( at_cleanup_t * at_cleanup ) {
70                if( at_cleanup ) {
71                        *at_cleanup = [this]( cleanup_func_t && func, void* val ) {
72                                push( std::move( func ), val );
73                        };
[04124c4]74                }
[26e6d88]75        }
[f47f887]76
[26e6d88]77        ~guard_value() {
78                while( !cleanups.empty() ) {
79                        auto& cleanup = cleanups.top();
80                        cleanup.func( cleanup.val );
81                        cleanups.pop();
[04124c4]82                }
[26e6d88]83        }
[f47f887]84
[26e6d88]85        void push( cleanup_func_t && func, void* val ) {
86                cleanups.emplace( std::move(func), val );
87        }
[f47f887]88
[26e6d88]89private:
90        struct cleanup_t {
91                cleanup_func_t func;
92                void * val;
[f47f887]93
[26e6d88]94                cleanup_t( cleanup_func_t&& func, void * val ) : func(func), val(val) {}
[04124c4]95        };
[f47f887]96
[26e6d88]97        std::stack< cleanup_t, std::vector<cleanup_t> > cleanups;
98};
[04124c4]99
[26e6d88]100// Guard structure implementation for whether or not children should be visited
101class visit_children_guard {
102public:
[dff6452]103
[26e6d88]104        visit_children_guard( bool_ref * ref )
105                : m_val ( true )
106                , m_prev( ref ? ref->set( &m_val ) : nullptr )
107                , m_ref ( ref )
108        {}
[dff6452]109
[26e6d88]110        ~visit_children_guard() {
111                if( m_ref ) {
112                        m_ref->set( m_prev );
113                }
114        }
[eb211bf]115
[26e6d88]116        operator bool() { return m_val; }
117
118private:
119        bool       m_val;
120        bool     * m_prev;
121        bool_ref * m_ref;
122};
123
124/// "Short hand" to check if this is a valid previsit function
125/// Mostly used to make the static_assert look (and print) prettier
126template<typename core_t, typename node_t>
127struct is_valid_previsit {
128        using ret_t = decltype( std::declval<core_t*>()->previsit( std::declval<const node_t *>() ) );
129
130        static constexpr bool value = std::is_void< ret_t >::value ||
131                std::is_base_of<const node_t, typename std::remove_pointer<ret_t>::type >::value;
132};
133
134/// The result is a single node.
135template< typename node_t >
136struct result1 {
137        bool differs = false;
138        const node_t * value = nullptr;
139
140        template< typename object_t, typename super_t, typename field_t >
[2d0f918]141        void apply( object_t * object, field_t super_t::* field ) {
142                object->*field = value;
143        }
[26e6d88]144};
145
146/// The result is a container of statements.
147template< template<class...> class container_t >
148struct resultNstmt {
149        /// The delta/change on a single node.
150        struct delta {
151                ptr<Stmt> new_val;
152                ssize_t old_idx;
153                bool is_old;
154
155                delta(const Stmt * s, ssize_t i, bool old) :
156                        new_val(s), old_idx(i), is_old(old) {}
[eb211bf]157        };
158
[26e6d88]159        bool differs = false;
160        container_t< delta > values;
[eb211bf]161
[26e6d88]162        template< typename object_t, typename super_t, typename field_t >
[2d0f918]163        void apply( object_t * object, field_t super_t::* field ) {
164                field_t & container = object->*field;
165                __pedantic_pass_assert( container.size() <= values.size() );
166
167                auto cit = enumerate(container).begin();
168
169                container_t<ptr<Stmt>> nvals;
170                for ( delta & d : values ) {
171                        if ( d.is_old ) {
172                                __pedantic_pass_assert( cit.idx <= d.old_idx );
173                                std::advance( cit, d.old_idx - cit.idx );
174                                nvals.push_back( std::move( (*cit).val ) );
175                        } else {
176                                nvals.push_back( std::move( d.new_val ) );
177                        }
178                }
179
180                container = std::move(nvals);
181        }
[eb211bf]182
[26e6d88]183        template< template<class...> class incontainer_t >
[2d0f918]184        void take_all( incontainer_t<ptr<Stmt>> * stmts ) {
185                if ( !stmts || stmts->empty() ) return;
186
187                std::transform( stmts->begin(), stmts->end(), std::back_inserter( values ),
188                        [](ast::ptr<ast::Stmt>& stmt) -> delta {
189                                return delta( stmt.release(), -1, false );
190                        });
191                stmts->clear();
192                differs = true;
193        }
[eb211bf]194
[26e6d88]195        template< template<class...> class incontainer_t >
[2d0f918]196        void take_all( incontainer_t<ptr<Decl>> * decls ) {
197                if ( !decls || decls->empty() ) return;
198
199                std::transform( decls->begin(), decls->end(), std::back_inserter( values ),
200                        [](ast::ptr<ast::Decl>& decl) -> delta {
201                                ast::Decl const * d = decl.release();
202                                return delta( new DeclStmt( d->location, d ), -1, false );
203                        });
204                decls->clear();
205                differs = true;
206        }
[26e6d88]207};
[eb211bf]208
[26e6d88]209/// The result is a container of nodes.
210template< template<class...> class container_t, typename node_t >
211struct resultN {
212        bool differs = false;
213        container_t<ptr<node_t>> values;
[0b8bf27]214
[26e6d88]215        template< typename object_t, typename super_t, typename field_t >
[2d0f918]216        void apply( object_t * object, field_t super_t::* field ) {
217                field_t & container = object->*field;
218                __pedantic_pass_assert( container.size() == values.size() );
219
220                for ( size_t i = 0; i < container.size(); ++i ) {
221                        // Take all the elements that are different in 'values'
222                        // and swap them into 'container'
223                        if ( values[i] != nullptr ) swap(container[i], values[i]);
224                }
225                // Now the original containers should still have the unchanged values
226                // but also contain the new values.
227        }
[26e6d88]228};
[e4b6cf78]229
[26e6d88]230/// Used by previsit implementation
231/// We need to reassign the result to 'node', unless the function
232/// returns void, then we just leave 'node' unchanged
233template<bool is_void>
234struct __assign;
[e4b6cf78]235
[26e6d88]236template<>
237struct __assign<true> {
[7ff3e522]238        template<typename core_t, typename node_t>
[26e6d88]239        static inline void result( core_t & core, const node_t * & node ) {
240                core.previsit( node );
[04124c4]241        }
[26e6d88]242};
[04124c4]243
[26e6d88]244template<>
245struct __assign<false> {
[7ff3e522]246        template<typename core_t, typename node_t>
[26e6d88]247        static inline void result( core_t & core, const node_t * & node ) {
248                node = core.previsit( node );
249                assertf(node, "Previsit must not return NULL");
250        }
251};
[04124c4]252
[26e6d88]253/// Used by postvisit implementation
254/// We need to return the result unless the function
255/// returns void, then we just return the original node
256template<bool is_void>
257struct __return;
258
259template<>
260struct __return<true> {
[7ff3e522]261        template<typename core_t, typename node_t>
[26e6d88]262        static inline const node_t * result( core_t & core, const node_t * & node ) {
263                core.postvisit( node );
264                return node;
[04124c4]265        }
[26e6d88]266};
[04124c4]267
[26e6d88]268template<>
269struct __return<false> {
[7ff3e522]270        template<typename core_t, typename node_t>
[26e6d88]271        static inline auto result( core_t & core, const node_t * & node ) {
272                return core.postvisit( node );
[c15085d]273        }
[26e6d88]274};
275
276//-------------------------------------------------------------------------------------------------------------------------------------------------------------------------
277// Deep magic (a.k.a template meta programming) to make the templated visitor work
278// Basically the goal is to make 2 previsit
279// 1 - Use when a pass implements a valid previsit. This uses overloading which means the any overload of
280//     'pass.previsit( node )' that compiles will be used for that node for that type
281//     This requires that this option only compile for passes that actually define an appropriate visit.
282//     SFINAE will make sure the compilation errors in this function don't halt the build.
283//     See http://en.cppreference.com/w/cpp/language/sfinae for details on SFINAE
284// 2 - Since the first implementation might not be specilizable, the second implementation exists and does nothing.
285//     This is needed only to eliminate the need for passes to specify any kind of handlers.
286//     The second implementation only works because it has a lower priority. This is due to the bogus last parameter.
287//     The second implementation takes a long while the first takes an int. Since the caller always passes an literal 0
288//     the first implementation takes priority in regards to overloading.
289//-------------------------------------------------------------------------------------------------------------------------------------------------------------------------
290// PreVisit : may mutate the pointer passed in if the node is mutated in the previsit call
291template<typename core_t, typename node_t>
292static inline auto previsit( core_t & core, const node_t * & node, int ) -> decltype( core.previsit( node ), void() ) {
293        static_assert(
294                is_valid_previsit<core_t, node_t>::value,
295                "Previsit may not change the type of the node. It must return its paremeter or void."
296        );
297
298        __assign<
299                std::is_void<
300                        decltype( core.previsit( node ) )
301                >::value
302        >::result( core, node );
303}
[c15085d]304
[26e6d88]305template<typename core_t, typename node_t>
306static inline auto previsit( core_t &, const node_t *, long ) {}
307
308// PostVisit : never mutates the passed pointer but may return a different node
309template<typename core_t, typename node_t>
310static inline auto postvisit( core_t & core, const node_t * node, int ) ->
311        decltype( core.postvisit( node ), node->accept( *(Visitor*)nullptr ) )
312{
313        return __return<
314                std::is_void<
315                        decltype( core.postvisit( node ) )
316                >::value
317        >::result( core, node );
318}
[c15085d]319
[26e6d88]320template<typename core_t, typename node_t>
321static inline const node_t * postvisit( core_t &, const node_t * node, long ) { return node; }
322
323//-------------------------------------------------------------------------------------------------------------------------------------------------------------------------
324// Deep magic (a.k.a template meta programming) continued
325// To make the templated visitor be more expressive, we allow 'accessories' : classes/structs the implementation can inherit
326// from in order to get extra functionallity for example
327// class ErrorChecker : WithShortCircuiting { ... };
328// Pass<ErrorChecker> checker;
329// this would define a pass that uses the templated visitor with the additionnal feature that it has short circuiting
330// Note that in all cases the accessories are not required but guarantee the requirements of the feature is matched
331//-------------------------------------------------------------------------------------------------------------------------------------------------------------------------
332// For several accessories, the feature is enabled by detecting that a specific field is present
333// Use a macro the encapsulate the logic of detecting a particular field
334// The type is not strictly enforced but does match the accessory
335#define FIELD_PTR( name, default_type ) \
336template< typename core_t > \
337static inline auto name( core_t & core, int ) -> decltype( &core.name ) { return &core.name; } \
338\
339template< typename core_t > \
340static inline default_type * name( core_t &, long ) { return nullptr; }
341
342// List of fields and their expected types
343FIELD_PTR( typeSubs, const ast::TypeSubstitution * )
344FIELD_PTR( stmtsToAddBefore, std::list< ast::ptr< ast::Stmt > > )
345FIELD_PTR( stmtsToAddAfter , std::list< ast::ptr< ast::Stmt > > )
346FIELD_PTR( declsToAddBefore, std::list< ast::ptr< ast::Decl > > )
347FIELD_PTR( declsToAddAfter , std::list< ast::ptr< ast::Decl > > )
348FIELD_PTR( visit_children, __pass::bool_ref )
349FIELD_PTR( at_cleanup, __pass::at_cleanup_t )
350FIELD_PTR( visitor, ast::Pass<core_t> * const )
351
352// Remove the macro to make sure we don't clash
353#undef FIELD_PTR
354
355template< typename core_t >
356static inline auto beginTrace(core_t &, int) -> decltype( core_t::traceId, void() ) {
357        // Stats::Heap::stacktrace_push(core_t::traceId);
358}
[c15085d]359
[26e6d88]360template< typename core_t >
361static inline auto endTrace(core_t &, int) -> decltype( core_t::traceId, void() ) {
362        // Stats::Heap::stacktrace_pop();
363}
[c15085d]364
[26e6d88]365template< typename core_t >
366static void beginTrace(core_t &, long) {}
[0dd9a5e]367
[26e6d88]368template< typename core_t >
369static void endTrace(core_t &, long) {}
[0dd9a5e]370
[26e6d88]371// Allows visitor to handle an error on top-level declarations, and possibly suppress the error.
372// If on_error() returns false, the error will be ignored. By default, it returns true.
[0dd9a5e]373
[26e6d88]374template< typename core_t >
375static bool on_error (core_t &, ptr<Decl> &, long) { return true; }
[95e5018]376
[26e6d88]377template< typename core_t >
378static auto on_error (core_t & core, ptr<Decl> & decl, int) -> decltype(core.on_error(decl)) {
379        return core.on_error(decl);
380}
[a9762dc]381
[26e6d88]382template< typename core_t, typename node_t >
383static auto make_location_guard( core_t & core, node_t * node, int )
384                -> decltype( node->location, ValueGuardPtr<const CodeLocation *>( &core.location ) ) {
385        ValueGuardPtr<const CodeLocation *> guard( &core.location );
386        core.location = &node->location;
387        return guard;
388}
[f47f887]389
[26e6d88]390template< typename core_t, typename node_t >
391static auto make_location_guard( core_t &, node_t *, long ) -> int {
392        return 0;
393}
[f47f887]394
[26e6d88]395// Another feature of the templated visitor is that it calls beginScope()/endScope() for compound statement.
396// All passes which have such functions are assumed desire this behaviour
397// detect it using the same strategy
398namespace scope {
399        template<typename core_t>
400        static inline auto enter( core_t & core, int ) -> decltype( core.beginScope(), void() ) {
401                core.beginScope();
402        }
[f47f887]403
[26e6d88]404        template<typename core_t>
405        static inline void enter( core_t &, long ) {}
[f47f887]406
[26e6d88]407        template<typename core_t>
408        static inline auto leave( core_t & core, int ) -> decltype( core.endScope(), void() ) {
409                core.endScope();
410        }
[f47f887]411
[26e6d88]412        template<typename core_t>
413        static inline void leave( core_t &, long ) {}
414} // namespace scope
415
416// Certain passes desire an up to date symbol table automatically
417// detect the presence of a member name `symtab` and call all the members appropriately
418namespace symtab {
419        // Some simple scoping rules
420        template<typename core_t>
421        static inline auto enter( core_t & core, int ) -> decltype( core.symtab, void() ) {
422                core.symtab.enterScope();
423        }
[f47f887]424
[26e6d88]425        template<typename core_t>
426        static inline auto enter( core_t &, long ) {}
427
428        template<typename core_t>
429        static inline auto leave( core_t & core, int ) -> decltype( core.symtab, void() ) {
430                core.symtab.leaveScope();
431        }
[6d51bd7]432
[26e6d88]433        template<typename core_t>
434        static inline auto leave( core_t &, long ) {}
[6d51bd7]435
[26e6d88]436        // The symbol table has 2 kind of functions mostly, 1 argument and 2 arguments
437        // Create macro to condense these common patterns
438        #define SYMTAB_FUNC1( func, type ) \
439        template<typename core_t> \
440        static inline auto func( core_t & core, int, type arg ) -> decltype( core.symtab.func( arg ), void() ) {\
441                core.symtab.func( arg ); \
442        } \
443        \
444        template<typename core_t> \
445        static inline void func( core_t &, long, type ) {}
446
447        #define SYMTAB_FUNC2( func, type1, type2 ) \
448        template<typename core_t> \
449        static inline auto func( core_t & core, int, type1 arg1, type2 arg2 ) -> decltype( core.symtab.func( arg1, arg2 ), void () ) {\
450                core.symtab.func( arg1, arg2 ); \
451        } \
452        \
453        template<typename core_t> \
454        static inline void func( core_t &, long, type1, type2 ) {}
455
456        SYMTAB_FUNC1( addId     , const DeclWithType *  );
457        SYMTAB_FUNC1( addType   , const NamedTypeDecl * );
458        SYMTAB_FUNC1( addStruct , const StructDecl *    );
459        SYMTAB_FUNC1( addEnum   , const EnumDecl *      );
460        SYMTAB_FUNC1( addUnion  , const UnionDecl *     );
461        SYMTAB_FUNC1( addTrait  , const TraitDecl *     );
462        SYMTAB_FUNC2( addWith   , const std::vector< ptr<Expr> > &, const Decl * );
463
464        // A few extra functions have more complicated behaviour, they are hand written
465        template<typename core_t>
466        static inline auto addStructFwd( core_t & core, int, const ast::StructDecl * decl ) -> decltype( core.symtab.addStruct( decl ), void() ) {
467                ast::StructDecl * fwd = new ast::StructDecl( decl->location, decl->name );
468                for ( const auto & param : decl->params ) {
469                        fwd->params.push_back( deepCopy( param.get() ) );
[6d51bd7]470                }
[26e6d88]471                core.symtab.addStruct( fwd );
472        }
[6d51bd7]473
[26e6d88]474        template<typename core_t>
475        static inline void addStructFwd( core_t &, long, const ast::StructDecl * ) {}
[6d51bd7]476
[26e6d88]477        template<typename core_t>
478        static inline auto addUnionFwd( core_t & core, int, const ast::UnionDecl * decl ) -> decltype( core.symtab.addUnion( decl ), void() ) {
479                ast::UnionDecl * fwd = new ast::UnionDecl( decl->location, decl->name );
480                for ( const auto & param : decl->params ) {
481                        fwd->params.push_back( deepCopy( param.get() ) );
[6d51bd7]482                }
[26e6d88]483                core.symtab.addUnion( fwd );
484        }
[6d51bd7]485
[26e6d88]486        template<typename core_t>
487        static inline void addUnionFwd( core_t &, long, const ast::UnionDecl * ) {}
[6d51bd7]488
[26e6d88]489        template<typename core_t>
[e0069bd]490        static inline auto addStructId( core_t & core, int, const std::string & str ) -> decltype( core.symtab.addStructId( str ), void() ) {
[26e6d88]491                if ( ! core.symtab.lookupStruct( str ) ) {
[e0069bd]492                        core.symtab.addStructId( str );
[6d51bd7]493                }
[26e6d88]494        }
[6d51bd7]495
[26e6d88]496        template<typename core_t>
[e0069bd]497        static inline void addStructId( core_t &, long, const std::string & ) {}
[26e6d88]498
499        template<typename core_t>
[e0069bd]500        static inline auto addUnionId( core_t & core, int, const std::string & str ) -> decltype( core.symtab.addUnionId( str ), void() ) {
[26e6d88]501                if ( ! core.symtab.lookupUnion( str ) ) {
[e0069bd]502                        core.symtab.addUnionId( str );
[e0e9a0b]503                }
[26e6d88]504        }
[e0e9a0b]505
[26e6d88]506        template<typename core_t>
[e0069bd]507        static inline void addUnionId( core_t &, long, const std::string & ) {}
[26e6d88]508
509        #undef SYMTAB_FUNC1
510        #undef SYMTAB_FUNC2
511} // namespace symtab
512
513// Some passes need to mutate TypeDecl and properly update their pointing TypeInstType.
514// Detect the presence of a member name `subs` and call all members appropriately
515namespace forall {
516        // Some simple scoping rules
517        template<typename core_t>
518        static inline auto enter( core_t & core, int, const ast::FunctionType * type )
519                        -> decltype( core.subs, void() ) {
520                if ( ! type->forall.empty() ) core.subs.beginScope();
521        }
[e0e9a0b]522
[26e6d88]523        template<typename core_t>
524        static inline auto enter( core_t &, long, const ast::FunctionType * ) {}
[e0e9a0b]525
[26e6d88]526        template<typename core_t>
527        static inline auto leave( core_t & core, int, const ast::FunctionType * type )
528                        -> decltype( core.subs, void() ) {
529                if ( ! type->forall.empty() ) { core.subs.endScope(); }
530        }
[e0e9a0b]531
[26e6d88]532        template<typename core_t>
533        static inline auto leave( core_t &, long, const ast::FunctionType * ) {}
[e0e9a0b]534
[26e6d88]535        // Replaces a TypeInstType's base TypeDecl according to the table
536        template<typename core_t>
537        static inline auto replace( core_t & core, int, const ast::TypeInstType *& inst )
538                        -> decltype( core.subs, void() ) {
539                inst = ast::mutate_field(
540                        inst, &ast::TypeInstType::base, core.subs.replace( inst->base ) );
541        }
[e6b42e7]542
[26e6d88]543        template<typename core_t>
544        static inline auto replace( core_t &, long, const ast::TypeInstType *& ) {}
545} // namespace forall
[c600df1]546
[26e6d88]547// For passes that need access to the global context. Searches `translationUnit`
548namespace translation_unit {
549        template<typename core_t>
550        static inline auto get_cptr( core_t & core, int )
551                        -> decltype( &core.translationUnit ) {
552                return &core.translationUnit;
[c600df1]553        }
554
[26e6d88]555        template<typename core_t>
556        static inline const TranslationUnit ** get_cptr( core_t &, long ) {
557                return nullptr;
558        }
559}
[e6b42e7]560
[26e6d88]561// For passes, usually utility passes, that have a result.
562namespace result {
563        template<typename core_t>
564        static inline auto get( core_t & core, char ) -> decltype( core.result() ) {
565                return core.result();
566        }
[e6b42e7]567
[26e6d88]568        template<typename core_t>
569        static inline auto get( core_t & core, int ) -> decltype( core.result ) {
570                return core.result;
[66a89e7]571        }
[cad9edb]572
[26e6d88]573        template<typename core_t>
574        static inline void get( core_t &, long ) {}
575}
576
[cad9edb]577} // namespace ast::__pass
[2d0f918]578
579#undef __pedantic_pass_assertf
580#undef __pedantic_pass_assert
Note: See TracBrowser for help on using the repository browser.