source: src/AST/Pass.proto.hpp @ 4a89b52

Last change on this file since 4a89b52 was e0069bd, checked in by Andrew Beach <ajbeach@…>, 11 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
Line 
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
16#pragma once
17// IWYU pragma: private, include "Pass.hpp"
18
19#include "Common/Iterate.hpp"
20#include "Common/Stats/Heap.h"
21namespace ast {
22        template<typename core_t> class Pass;
23        class TranslationUnit;
24        struct PureVisitor;
25        template<typename node_t> node_t * deepCopy( const node_t * );
26}
27
28#ifdef PEDANTIC_PASS_ASSERT
29#define __pedantic_pass_assert(...) assert(__VA_ARGS__)
30#define __pedantic_pass_assertf(...) assertf(__VA_ARGS__)
31#else
32#define __pedantic_pass_assert(...)
33#define __pedantic_pass_assertf(...)
34#endif
35
36namespace ast::__pass {
37
38typedef std::function<void( void * )> cleanup_func_t;
39typedef std::function<void( cleanup_func_t, void * )> at_cleanup_t;
40
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;
47
48        operator bool() { return m_ref ? *m_ref : true; }
49        bool operator=( bool val ) { assert(m_ref); return *m_ref = val; }
50
51private:
52
53        friend class visit_children_guard;
54
55        bool * set( bool * val ) {
56                bool * prev = m_ref;
57                m_ref = val;
58                return prev;
59        }
60
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                        };
74                }
75        }
76
77        ~guard_value() {
78                while( !cleanups.empty() ) {
79                        auto& cleanup = cleanups.top();
80                        cleanup.func( cleanup.val );
81                        cleanups.pop();
82                }
83        }
84
85        void push( cleanup_func_t && func, void* val ) {
86                cleanups.emplace( std::move(func), val );
87        }
88
89private:
90        struct cleanup_t {
91                cleanup_func_t func;
92                void * val;
93
94                cleanup_t( cleanup_func_t&& func, void * val ) : func(func), val(val) {}
95        };
96
97        std::stack< cleanup_t, std::vector<cleanup_t> > cleanups;
98};
99
100// Guard structure implementation for whether or not children should be visited
101class visit_children_guard {
102public:
103
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        {}
109
110        ~visit_children_guard() {
111                if( m_ref ) {
112                        m_ref->set( m_prev );
113                }
114        }
115
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 >
141        void apply( object_t * object, field_t super_t::* field ) {
142                object->*field = value;
143        }
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) {}
157        };
158
159        bool differs = false;
160        container_t< delta > values;
161
162        template< typename object_t, typename super_t, typename field_t >
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        }
182
183        template< template<class...> class incontainer_t >
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        }
194
195        template< template<class...> class incontainer_t >
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        }
207};
208
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;
214
215        template< typename object_t, typename super_t, typename field_t >
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        }
228};
229
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;
235
236template<>
237struct __assign<true> {
238        template<typename core_t, typename node_t>
239        static inline void result( core_t & core, const node_t * & node ) {
240                core.previsit( node );
241        }
242};
243
244template<>
245struct __assign<false> {
246        template<typename core_t, typename node_t>
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};
252
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> {
261        template<typename core_t, typename node_t>
262        static inline const node_t * result( core_t & core, const node_t * & node ) {
263                core.postvisit( node );
264                return node;
265        }
266};
267
268template<>
269struct __return<false> {
270        template<typename core_t, typename node_t>
271        static inline auto result( core_t & core, const node_t * & node ) {
272                return core.postvisit( node );
273        }
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}
304
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}
319
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}
359
360template< typename core_t >
361static inline auto endTrace(core_t &, int) -> decltype( core_t::traceId, void() ) {
362        // Stats::Heap::stacktrace_pop();
363}
364
365template< typename core_t >
366static void beginTrace(core_t &, long) {}
367
368template< typename core_t >
369static void endTrace(core_t &, long) {}
370
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.
373
374template< typename core_t >
375static bool on_error (core_t &, ptr<Decl> &, long) { return true; }
376
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}
381
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}
389
390template< typename core_t, typename node_t >
391static auto make_location_guard( core_t &, node_t *, long ) -> int {
392        return 0;
393}
394
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        }
403
404        template<typename core_t>
405        static inline void enter( core_t &, long ) {}
406
407        template<typename core_t>
408        static inline auto leave( core_t & core, int ) -> decltype( core.endScope(), void() ) {
409                core.endScope();
410        }
411
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        }
424
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        }
432
433        template<typename core_t>
434        static inline auto leave( core_t &, long ) {}
435
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() ) );
470                }
471                core.symtab.addStruct( fwd );
472        }
473
474        template<typename core_t>
475        static inline void addStructFwd( core_t &, long, const ast::StructDecl * ) {}
476
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() ) );
482                }
483                core.symtab.addUnion( fwd );
484        }
485
486        template<typename core_t>
487        static inline void addUnionFwd( core_t &, long, const ast::UnionDecl * ) {}
488
489        template<typename core_t>
490        static inline auto addStructId( core_t & core, int, const std::string & str ) -> decltype( core.symtab.addStructId( str ), void() ) {
491                if ( ! core.symtab.lookupStruct( str ) ) {
492                        core.symtab.addStructId( str );
493                }
494        }
495
496        template<typename core_t>
497        static inline void addStructId( core_t &, long, const std::string & ) {}
498
499        template<typename core_t>
500        static inline auto addUnionId( core_t & core, int, const std::string & str ) -> decltype( core.symtab.addUnionId( str ), void() ) {
501                if ( ! core.symtab.lookupUnion( str ) ) {
502                        core.symtab.addUnionId( str );
503                }
504        }
505
506        template<typename core_t>
507        static inline void addUnionId( core_t &, long, const std::string & ) {}
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        }
522
523        template<typename core_t>
524        static inline auto enter( core_t &, long, const ast::FunctionType * ) {}
525
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        }
531
532        template<typename core_t>
533        static inline auto leave( core_t &, long, const ast::FunctionType * ) {}
534
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        }
542
543        template<typename core_t>
544        static inline auto replace( core_t &, long, const ast::TypeInstType *& ) {}
545} // namespace forall
546
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;
553        }
554
555        template<typename core_t>
556        static inline const TranslationUnit ** get_cptr( core_t &, long ) {
557                return nullptr;
558        }
559}
560
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        }
567
568        template<typename core_t>
569        static inline auto get( core_t & core, int ) -> decltype( core.result ) {
570                return core.result;
571        }
572
573        template<typename core_t>
574        static inline void get( core_t &, long ) {}
575}
576
577} // namespace ast::__pass
578
579#undef __pedantic_pass_assertf
580#undef __pedantic_pass_assert
Note: See TracBrowser for help on using the repository browser.