Changeset 04124c4 for src/AST/Pass.proto.hpp
- Timestamp:
- May 10, 2019, 2:47:32 PM (4 years ago)
- Branches:
- arm-eh, cleanup-dtors, enum, forall-pointer-decay, jacob/cs343-translation, jenkins-sandbox, master, new-ast, new-ast-unique-expr, pthread-emulation, qualifiedEnum
- Children:
- 37e3af4
- Parents:
- 7f3f63c
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
src/AST/Pass.proto.hpp
r7f3f63c r04124c4 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 1 16 #pragma once 2 17 // IWYU pragma: private, include "Pass.hpp" 3 18 4 19 namespace ast { 5 template<typename pass_type> 6 class Pass; 7 8 namespace __pass { 9 typedef std::function<void( void * )> cleanup_func_t; 10 typedef std::function<void( cleanup_func_t, void * )> at_cleanup_t; 11 12 13 // boolean reference that may be null 14 // either refers to a boolean value or is null and returns true 15 class bool_ref { 16 public: 17 bool_ref() = default; 18 ~bool_ref() = default; 19 20 operator bool() { return m_ref ? *m_ref : true; } 21 bool operator=( bool val ) { assert(m_ref); return *m_ref = val; } 22 23 private: 24 25 friend class visit_children_guard; 26 27 bool * set( bool * val ) { 28 bool * prev = m_ref; 29 m_ref = val; 30 return prev; 20 template<typename pass_type> 21 class Pass; 22 23 namespace __pass { 24 typedef std::function<void( void * )> cleanup_func_t; 25 typedef std::function<void( cleanup_func_t, void * )> at_cleanup_t; 26 27 28 // boolean reference that may be null 29 // either refers to a boolean value or is null and returns true 30 class bool_ref { 31 public: 32 bool_ref() = default; 33 ~bool_ref() = default; 34 35 operator bool() { return m_ref ? *m_ref : true; } 36 bool operator=( bool val ) { assert(m_ref); return *m_ref = val; } 37 38 private: 39 40 friend class visit_children_guard; 41 42 bool * set( bool * val ) { 43 bool * prev = m_ref; 44 m_ref = val; 45 return prev; 46 } 47 48 bool * m_ref = nullptr; 49 }; 50 51 // Implementation of the guard value 52 // Created inside the visit scope 53 class guard_value { 54 public: 55 /// Push onto the cleanup 56 guard_value( at_cleanup_t * at_cleanup ) { 57 if( at_cleanup ) { 58 *at_cleanup = [this]( cleanup_func_t && func, void* val ) { 59 push( std::move( func ), val ); 60 }; 31 61 } 32 33 bool * m_ref = nullptr; 62 } 63 64 ~guard_value() { 65 while( !cleanups.empty() ) { 66 auto& cleanup = cleanups.top(); 67 cleanup.func( cleanup.val ); 68 cleanups.pop(); 69 } 70 } 71 72 void push( cleanup_func_t && func, void* val ) { 73 cleanups.emplace( std::move(func), val ); 74 } 75 76 private: 77 struct cleanup_t { 78 cleanup_func_t func; 79 void * val; 80 81 cleanup_t( cleanup_func_t&& func, void * val ) : func(func), val(val) {} 34 82 }; 35 83 36 // Implementation of the guard value 37 // Created inside the visit scope 38 class guard_value { 39 public: 40 guard_value( at_cleanup_t * at_cleanup ) { 41 if( at_cleanup ) { 42 *at_cleanup = [this]( cleanup_func_t && func, void* val ) { 43 push( std::move( func ), val ); 44 }; 45 } 84 std::stack< cleanup_t > cleanups; 85 }; 86 87 // Guard structure implementation for whether or not children should be visited 88 class visit_children_guard { 89 public: 90 91 visit_children_guard( bool_ref * ref ) 92 : m_val ( true ) 93 , m_prev( ref ? ref->set( &m_val ) : nullptr ) 94 , m_ref ( ref ) 95 {} 96 97 ~visit_children_guard() { 98 if( m_ref ) { 99 m_ref->set( m_prev ); 46 100 } 47 48 ~guard_value() { 49 while( !cleanups.empty() ) { 50 auto& cleanup = cleanups.top(); 51 cleanup.func( cleanup.val ); 52 cleanups.pop(); 53 } 54 } 55 56 void push( cleanup_func_t && func, void* val ) { 57 cleanups.emplace( std::move(func), val ); 58 } 59 60 private: 61 struct cleanup_t { 62 cleanup_func_t func; 63 void * val; 64 65 cleanup_t( cleanup_func_t&& func, void * val ) : func(func), val(val) {} 66 }; 67 68 std::stack< cleanup_t > cleanups; 69 }; 70 71 // Guard structure implementation for whether or not children should be visited 72 class visit_children_guard { 73 public: 74 75 visit_children_guard( bool_ref * ref ) 76 : m_val ( true ) 77 , m_prev( ref ? ref->set( &m_val ) : nullptr ) 78 , m_ref ( ref ) 79 {} 80 81 ~visit_children_guard() { 82 if( m_ref ) { 83 m_ref->set( m_prev ); 84 } 85 } 86 87 operator bool() { return m_val; } 88 89 private: 90 bool m_val; 91 bool * m_prev; 92 bool_ref * m_ref; 93 }; 94 95 //------------------------------------------------------------------------------------------------------------------------------------------------------------------------- 96 // Deep magic (a.k.a template meta programming) to make the templated visitor work 97 // Basically the goal is to make 2 previsit 98 // 1 - Use when a pass implements a valid previsit. This uses overloading which means the any overload of 99 // 'pass.previsit( node )' that compiles will be used for that node for that type 100 // This requires that this option only compile for passes that actually define an appropriate visit. 101 // SFINAE will make sure the compilation errors in this function don't halt the build. 102 // See http://en.cppreference.com/w/cpp/language/sfinae for details on SFINAE 103 // 2 - Since the first implementation might not be specilizable, the second implementation exists and does nothing. 104 // This is needed only to eliminate the need for passes to specify any kind of handlers. 105 // The second implementation only works because it has a lower priority. This is due to the bogus last parameter. 106 // The second implementation takes a long while the first takes an int. Since the caller always passes an literal 0 107 // the first implementation takes priority in regards to overloading. 108 //------------------------------------------------------------------------------------------------------------------------------------------------------------------------- 109 // PreVisit : may mutate the pointer passed in if the node is mutated in the previsit call 110 template<typename pass_t, typename node_t> 111 static inline auto previsit( pass_t & pass, const node_t * & node, int ) -> decltype( pass.previsit( node ), void() ) { 112 node = pass.previsit( node ); 113 assert(node); 114 } 115 116 template<typename pass_t, typename node_t> 117 static inline auto previsit( pass_t &, const node_t *, long ) {} 118 119 // PostVisit : never mutates the passed pointer but may return a different node 120 template<typename pass_t, typename node_t> 121 static inline auto postvisit( pass_t & pass, const node_t * node, int ) -> decltype( pass.postvisit( node ), (const node_t *)nullptr ) { 122 return pass.postvisit( node ); 123 } 124 125 template<typename pass_t, typename node_t> 126 static inline const node_t * postvisit( pass_t &, const node_t * node, long ) { return node; } 127 128 //------------------------------------------------------------------------------------------------------------------------------------------------------------------------- 129 // Deep magic (a.k.a template meta programming) continued 130 // To make the templated visitor be more expressive, we allow 'accessories' : classes/structs the implementation can inherit 131 // from in order to get extra functionallity for example 132 // class ErrorChecker : WithShortCircuiting { ... }; 133 // Pass<ErrorChecker> checker; 134 // this would define a pass that uses the templated visitor with the additionnal feature that it has short circuiting 135 // Note that in all cases the accessories are not required but guarantee the requirements of the feature is matched 136 //------------------------------------------------------------------------------------------------------------------------------------------------------------------------- 137 // For several accessories, the feature is enabled by detecting that a specific field is present 138 // Use a macro the encapsulate the logic of detecting a particular field 139 // The type is not strictly enforced but does match the accessory 140 #define FIELD_PTR( name, default_type ) \ 141 template< typename pass_t > \ 142 static inline auto name( pass_t & pass, int ) -> decltype( &pass.name ) { return &pass.name; } \ 101 } 102 103 operator bool() { return m_val; } 104 105 private: 106 bool m_val; 107 bool * m_prev; 108 bool_ref * m_ref; 109 }; 110 111 //------------------------------------------------------------------------------------------------------------------------------------------------------------------------- 112 // Deep magic (a.k.a template meta programming) to make the templated visitor work 113 // Basically the goal is to make 2 previsit 114 // 1 - Use when a pass implements a valid previsit. This uses overloading which means the any overload of 115 // 'pass.previsit( node )' that compiles will be used for that node for that type 116 // This requires that this option only compile for passes that actually define an appropriate visit. 117 // SFINAE will make sure the compilation errors in this function don't halt the build. 118 // See http://en.cppreference.com/w/cpp/language/sfinae for details on SFINAE 119 // 2 - Since the first implementation might not be specilizable, the second implementation exists and does nothing. 120 // This is needed only to eliminate the need for passes to specify any kind of handlers. 121 // The second implementation only works because it has a lower priority. This is due to the bogus last parameter. 122 // The second implementation takes a long while the first takes an int. Since the caller always passes an literal 0 123 // the first implementation takes priority in regards to overloading. 124 //------------------------------------------------------------------------------------------------------------------------------------------------------------------------- 125 // PreVisit : may mutate the pointer passed in if the node is mutated in the previsit call 126 template<typename pass_t, typename node_t> 127 static inline auto previsit( pass_t & pass, const node_t * & node, int ) -> decltype( pass.previsit( node ), void() ) { 128 node = pass.previsit( node ); 129 assert(node); 130 } 131 132 template<typename pass_t, typename node_t> 133 static inline auto previsit( pass_t &, const node_t *, long ) {} 134 135 // PostVisit : never mutates the passed pointer but may return a different node 136 template<typename pass_t, typename node_t> 137 static inline auto postvisit( pass_t & pass, const node_t * node, int ) -> decltype( pass.postvisit( node ), (const node_t *)nullptr ) { 138 return pass.postvisit( node ); 139 } 140 141 template<typename pass_t, typename node_t> 142 static inline const node_t * postvisit( pass_t &, const node_t * node, long ) { return node; } 143 144 //------------------------------------------------------------------------------------------------------------------------------------------------------------------------- 145 // Deep magic (a.k.a template meta programming) continued 146 // To make the templated visitor be more expressive, we allow 'accessories' : classes/structs the implementation can inherit 147 // from in order to get extra functionallity for example 148 // class ErrorChecker : WithShortCircuiting { ... }; 149 // Pass<ErrorChecker> checker; 150 // this would define a pass that uses the templated visitor with the additionnal feature that it has short circuiting 151 // Note that in all cases the accessories are not required but guarantee the requirements of the feature is matched 152 //------------------------------------------------------------------------------------------------------------------------------------------------------------------------- 153 // For several accessories, the feature is enabled by detecting that a specific field is present 154 // Use a macro the encapsulate the logic of detecting a particular field 155 // The type is not strictly enforced but does match the accessory 156 #define FIELD_PTR( name, default_type ) \ 157 template< typename pass_t > \ 158 static inline auto name( pass_t & pass, int ) -> decltype( &pass.name ) { return &pass.name; } \ 159 \ 160 template< typename pass_t > \ 161 static inline default_type * name( pass_t &, long ) { return nullptr; } 162 163 // List of fields and their expected types 164 FIELD_PTR( env, const ast::TypeSubstitution ) 165 FIELD_PTR( stmtsToAddBefore, std::list< ast::ptr< ast::Stmt > > ) 166 FIELD_PTR( stmtsToAddAfter , std::list< ast::ptr< ast::Stmt > > ) 167 FIELD_PTR( declsToAddBefore, std::list< ast::ptr< ast::Decl > > ) 168 FIELD_PTR( declsToAddAfter , std::list< ast::ptr< ast::Decl > > ) 169 FIELD_PTR( visit_children, __pass::bool_ref ) 170 FIELD_PTR( at_cleanup, __pass::at_cleanup_t ) 171 FIELD_PTR( visitor, ast::Pass<pass_t> * const ) 172 173 // Remove the macro to make sure we don't clash 174 #undef FIELD_PTR 175 176 // Another feature of the templated visitor is that it calls beginScope()/endScope() for compound statement. 177 // All passes which have such functions are assumed desire this behaviour 178 // detect it using the same strategy 179 namespace scope { 180 template<typename pass_t> 181 static inline auto enter( pass_t & pass, int ) -> decltype( pass.beginScope(), void() ) { 182 pass.beginScope(); 183 } 184 185 template<typename pass_t> 186 static inline void enter( pass_t &, long ) {} 187 188 template<typename pass_t> 189 static inline auto leave( pass_t & pass, int ) -> decltype( pass.endScope(), void() ) { 190 pass.endScope(); 191 } 192 193 template<typename pass_t> 194 static inline void leave( pass_t &, long ) {} 195 }; 196 197 // Finally certain pass desire an up to date indexer automatically 198 // detect the presence of a member name indexer and call all the members appropriately 199 namespace indexer { 200 // Some simple scoping rules 201 template<typename pass_t> 202 static inline auto enter( pass_t & pass, int ) -> decltype( pass.indexer.enterScope(), void() ) { 203 pass.indexer.enterScope(); 204 } 205 206 template<typename pass_t> 207 static inline auto enter( pass_t &, long ) {} 208 209 template<typename pass_t> 210 static inline auto leave( pass_t & pass, int ) -> decltype( pass.indexer.leaveScope(), void() ) { 211 pass.indexer.leaveScope(); 212 } 213 214 template<typename pass_t> 215 static inline auto leave( pass_t &, long ) {} 216 217 // The indexer has 2 kind of functions mostly, 1 argument and 2 arguments 218 // Create macro to condense these common patterns 219 #define INDEXER_FUNC1( func, type ) \ 220 template<typename pass_t> \ 221 static inline auto func( pass_t & pass, int, type arg ) -> decltype( pass.indexer.func( arg ), void() ) {\ 222 pass.indexer.func( arg ); \ 223 } \ 143 224 \ 144 template< typename pass_t > \ 145 static inline default_type * name( pass_t &, long ) { return nullptr; } 146 147 // List of fields and their expected types 148 FIELD_PTR( env, const ast::TypeSubstitution ) 149 FIELD_PTR( stmtsToAddBefore, std::list< ast::ptr< ast::Stmt > > ) 150 FIELD_PTR( stmtsToAddAfter , std::list< ast::ptr< ast::Stmt > > ) 151 FIELD_PTR( declsToAddBefore, std::list< ast::ptr< ast::Decl > > ) 152 FIELD_PTR( declsToAddAfter , std::list< ast::ptr< ast::Decl > > ) 153 FIELD_PTR( visit_children, __pass::bool_ref ) 154 FIELD_PTR( at_cleanup, __pass::at_cleanup_t ) 155 FIELD_PTR( visitor, ast::Pass<pass_t> * const ) 156 157 // Remove the macro to make sure we don't clash 158 #undef FIELD_PTR 159 160 // Another feature of the templated visitor is that it calls beginScope()/endScope() for compound statement. 161 // All passes which have such functions are assumed desire this behaviour 162 // detect it using the same strategy 163 namespace scope { 164 template<typename pass_t> 165 static inline auto enter( pass_t & pass, int ) -> decltype( pass.beginScope(), void() ) { 166 pass.beginScope(); 167 } 168 169 template<typename pass_t> 170 static inline void enter( pass_t &, long ) {} 171 172 template<typename pass_t> 173 static inline auto leave( pass_t & pass, int ) -> decltype( pass.endScope(), void() ) { 174 pass.endScope(); 175 } 176 177 template<typename pass_t> 178 static inline void leave( pass_t &, long ) {} 179 }; 180 181 // Finally certain pass desire an up to date indexer automatically 182 // detect the presence of a member name indexer and call all the members appropriately 183 namespace indexer { 184 // Some simple scoping rules 185 template<typename pass_t> 186 static inline auto enter( pass_t & pass, int ) -> decltype( pass.indexer.enterScope(), void() ) { 187 pass.indexer.enterScope(); 188 } 189 190 template<typename pass_t> 191 static inline auto enter( pass_t &, long ) {} 192 193 template<typename pass_t> 194 static inline auto leave( pass_t & pass, int ) -> decltype( pass.indexer.leaveScope(), void() ) { 195 pass.indexer.leaveScope(); 196 } 197 198 template<typename pass_t> 199 static inline auto leave( pass_t &, long ) {} 200 201 // The indexer has 2 kind of functions mostly, 1 argument and 2 arguments 202 // Create macro to condense these common patterns 203 #define INDEXER_FUNC1( func, type ) \ 204 template<typename pass_t> \ 205 static inline auto func( pass_t & pass, int, type arg ) -> decltype( pass.indexer.func( arg ), void() ) {\ 206 pass.indexer.func( arg ); \ 207 } \ 225 template<typename pass_t> \ 226 static inline void func( pass_t &, long, type ) {} 227 228 #define INDEXER_FUNC2( func, type1, type2 ) \ 229 template<typename pass_t> \ 230 static inline auto func( pass_t & pass, int, type1 arg1, type2 arg2 ) -> decltype( pass.indexer.func( arg1, arg2 ), void () ) {\ 231 pass.indexer.func( arg1, arg2 ); \ 232 } \ 208 233 \ 209 template<typename pass_t> \ 210 static inline void func( pass_t &, long, type ) {} 211 212 #define INDEXER_FUNC2( func, type1, type2 ) \ 213 template<typename pass_t> \ 214 static inline auto func( pass_t & pass, int, type1 arg1, type2 arg2 ) -> decltype( pass.indexer.func( arg1, arg2 ), void () ) {\ 215 pass.indexer.func( arg1, arg2 ); \ 216 } \ 217 \ 218 template<typename pass_t> \ 219 static inline void func( pass_t &, long, type1, type2 ) {} 220 221 INDEXER_FUNC1( addId , DeclarationWithType * ); 222 INDEXER_FUNC1( addType , NamedTypeDecl * ); 223 INDEXER_FUNC1( addStruct , StructDecl * ); 224 INDEXER_FUNC1( addEnum , EnumDecl * ); 225 INDEXER_FUNC1( addUnion , UnionDecl * ); 226 INDEXER_FUNC1( addTrait , TraitDecl * ); 227 INDEXER_FUNC2( addWith , std::list< Expression * > &, BaseSyntaxNode * ); 228 229 // A few extra functions have more complicated behaviour, they are hand written 230 template<typename pass_t> 231 static inline auto addStructFwd( pass_t & pass, int, ast::StructDecl * decl ) -> decltype( pass.indexer.addStruct( decl ), void() ) { 232 ast::StructDecl * fwd = new ast::StructDecl( decl->location, decl->name ); 233 fwd->parameters = decl->parameters; 234 pass.indexer.addStruct( fwd ); 235 } 236 237 template<typename pass_t> 238 static inline void addStructFwd( pass_t &, long, ast::StructDecl * ) {} 239 240 template<typename pass_t> 241 static inline auto addUnionFwd( pass_t & pass, int, ast::UnionDecl * decl ) -> decltype( pass.indexer.addUnion( decl ), void() ) { 242 UnionDecl * fwd = new UnionDecl( decl->name ); 243 fwd->parameters = decl->parameters; 244 pass.indexer.addUnion( fwd ); 245 } 246 247 template<typename pass_t> 248 static inline void addUnionFwd( pass_t &, long, ast::UnionDecl * ) {} 249 250 template<typename pass_t> 251 static inline auto addStruct( pass_t & pass, int, const std::string & str ) -> decltype( pass.indexer.addStruct( str ), void() ) { 252 if ( ! pass.indexer.lookupStruct( str ) ) { 253 pass.indexer.addStruct( str ); 254 } 255 } 256 257 template<typename pass_t> 258 static inline void addStruct( pass_t &, long, const std::string & ) {} 259 260 template<typename pass_t> 261 static inline auto addUnion( pass_t & pass, int, const std::string & str ) -> decltype( pass.indexer.addUnion( str ), void() ) { 262 if ( ! pass.indexer.lookupUnion( str ) ) { 263 pass.indexer.addUnion( str ); 264 } 265 } 266 267 template<typename pass_t> 268 static inline void addUnion( pass_t &, long, const std::string & ) {} 269 270 #undef INDEXER_FUNC1 271 #undef INDEXER_FUNC2 272 }; 234 template<typename pass_t> \ 235 static inline void func( pass_t &, long, type1, type2 ) {} 236 237 INDEXER_FUNC1( addId , DeclWithType * ); 238 INDEXER_FUNC1( addType , NamedTypeDecl * ); 239 INDEXER_FUNC1( addStruct , StructDecl * ); 240 INDEXER_FUNC1( addEnum , EnumDecl * ); 241 INDEXER_FUNC1( addUnion , UnionDecl * ); 242 INDEXER_FUNC1( addTrait , TraitDecl * ); 243 INDEXER_FUNC2( addWith , std::list< Expression * > &, Node * ); 244 245 // A few extra functions have more complicated behaviour, they are hand written 246 // template<typename pass_t> 247 // static inline auto addStructFwd( pass_t & pass, int, ast::StructDecl * decl ) -> decltype( pass.indexer.addStruct( decl ), void() ) { 248 // ast::StructDecl * fwd = new ast::StructDecl( decl->location, decl->name ); 249 // fwd->parameters = decl->parameters; 250 // pass.indexer.addStruct( fwd ); 251 // } 252 253 // template<typename pass_t> 254 // static inline void addStructFwd( pass_t &, long, ast::StructDecl * ) {} 255 256 // template<typename pass_t> 257 // static inline auto addUnionFwd( pass_t & pass, int, ast::UnionDecl * decl ) -> decltype( pass.indexer.addUnion( decl ), void() ) { 258 // UnionDecl * fwd = new UnionDecl( decl->name ); 259 // fwd->parameters = decl->parameters; 260 // pass.indexer.addUnion( fwd ); 261 // } 262 263 // template<typename pass_t> 264 // static inline void addUnionFwd( pass_t &, long, ast::UnionDecl * ) {} 265 266 // template<typename pass_t> 267 // static inline auto addStruct( pass_t & pass, int, const std::string & str ) -> decltype( pass.indexer.addStruct( str ), void() ) { 268 // if ( ! pass.indexer.lookupStruct( str ) ) { 269 // pass.indexer.addStruct( str ); 270 // } 271 // } 272 273 // template<typename pass_t> 274 // static inline void addStruct( pass_t &, long, const std::string & ) {} 275 276 // template<typename pass_t> 277 // static inline auto addUnion( pass_t & pass, int, const std::string & str ) -> decltype( pass.indexer.addUnion( str ), void() ) { 278 // if ( ! pass.indexer.lookupUnion( str ) ) { 279 // pass.indexer.addUnion( str ); 280 // } 281 // } 282 283 // template<typename pass_t> 284 // static inline void addUnion( pass_t &, long, const std::string & ) {} 285 286 #undef INDEXER_FUNC1 287 #undef INDEXER_FUNC2 273 288 }; 274 289 }; 290 };
Note: See TracChangeset
for help on using the changeset viewer.