- Timestamp:
- May 10, 2019, 2:47:32 PM (6 years ago)
- Branches:
- ADT, arm-eh, ast-experimental, 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
- Location:
- src/AST
- Files:
-
- 3 edited
Legend:
- Unmodified
- Added
- Removed
-
src/AST/Pass.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.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 // IWYU pragma: private, include " Common/PassVisitor.h"17 // IWYU pragma: private, include "AST/Pass.hpp" 3 18 4 19 #include <functional> … … 6 21 #include <stack> 7 22 8 #include "Fwd.hpp" 9 #include "Node.hpp" 23 #include "AST/Fwd.hpp" 24 #include "AST/Node.hpp" 25 #include "AST/Decl.hpp" 26 #include "AST/Visitor.hpp" 27 28 #include "SymTab/Indexer.h" 10 29 11 30 // Private prelude header, needed for some of the magic tricks this class pulls off 12 #include " Pass.proto.hpp"31 #include "AST/Pass.proto.hpp" 13 32 14 33 namespace ast { … … 20 39 // 21 40 // Several additional features are available through inheritance 22 // | WithTypeSubstitution - provides polymorphic TypeSubstitution * env for the current expression 41 // | WithTypeSubstitution - provides polymorphic const TypeSubstitution * env for the 42 // current expression 23 43 // | WithStmtsToAdd - provides the ability to insert statements before or after the current 24 44 // statement by adding new statements into stmtsToAddBefore or 25 45 // stmtsToAddAfter respectively. 46 // | WithDeclsToAdd - provides the ability to insert declarations before or after the current 47 // declarations by adding new DeclStmt into declsToAddBefore or 48 // declsToAddAfter respectively. 26 49 // | WithShortCircuiting - provides the ability to skip visiting child nodes; set visit_children 27 50 // to false in pre{visit,visit} to skip visiting children … … 30 53 // automatically be restored to its previous value after the corresponding 31 54 // postvisit/postmutate teminates. 55 // | WithVisitorRef - provides an pointer to the templated visitor wrapper 56 // | WithIndexer - provides indexer functionality (i.e. up-to-date symbol table) 32 57 //------------------------------------------------------------------------------------------------- 33 58 template< typename pass_t > 34 59 class Pass final : public ast::Visitor { 35 60 public: 61 /// Forward any arguments to the pass constructor 62 /// Propagate 'this' if necessary 36 63 template< typename... Args > 37 64 Pass( Args &&... args) 38 : m_pass( std::forward<Args>( args )... )65 : pass( std::forward<Args>( args )... ) 39 66 { 40 67 // After the pass is constructed, check if it wants the have a pointer to the wrapping visitor 41 68 typedef Pass<pass_t> this_t; 42 this_t * const * visitor = __pass::visitor( m_pass, 0);69 this_t * const * visitor = __pass::visitor(pass, 0); 43 70 if(visitor) { 44 71 *const_cast<this_t **>( visitor ) = this; … … 48 75 virtual ~Pass() = default; 49 76 50 pass_t m_pass; 51 77 /// Storage for the actual pass 78 pass_t pass; 79 80 /// Visit function declarations 52 81 virtual DeclWithType * visit( const ObjectDecl * ) override final; 53 82 virtual DeclWithType * visit( const FunctionDecl * ) override final; … … 145 174 virtual TypeSubstitution * visit( const TypeSubstitution * ) override final; 146 175 176 friend void acceptAll( std::list< ptr<Decl> > & decls, Pass<pass_t>& visitor ); 147 177 private: 148 178 149 bool __visit_children() { __pass::bool_ref * ptr = __pass::visit_children( m_pass, 0); return ptr ? *ptr : true; }179 bool __visit_children() { __pass::bool_ref * ptr = __pass::visit_children(pass, 0); return ptr ? *ptr : true; } 150 180 151 181 private: 182 /// Logic to call the accept and mutate the parent if needed, delegates call to accept 152 183 template<typename parent_t, typename child_t> 153 184 void maybe_accept(parent_t * & , typename parent_t::child_t *); 154 185 155 ast::Statement * call_accept( const ast::Statement* );156 ast::Expression * call_accept( const ast::Expression* );186 Stmt * call_accept( const Stmt * ); 187 Expr * call_accept( const Expr * ); 157 188 158 189 template< template <class> class container_t > 159 container_t< ast::ptr<ast::Statement> > call_accept( const container_t< ast::ptr<ast::Statement> > & );190 container_t< ptr<Stmt> > call_accept( const container_t< ptr<Stmt> > & ); 160 191 161 192 template< template <class> class container_t, typename node_t > 162 container_t< ast::ptr<node_t> > call_accept( const container_t< ast::ptr<node_t> > & container );193 container_t< ptr<node_t> > call_accept( const container_t< ptr<node_t> > & container ); 163 194 164 195 private: 165 struct indexer_guard { 196 /// Internal RAII guard for indexer features 197 struct guard_indexer { 198 guard_indexer( Pass<pass_t> & pass ): pass( pass ) { __pass::indexer::enter(pass, 0); } 199 ~guard_indexer() { __pass::indexer::leave(pass, 0); } 166 200 Pass<pass_t> & pass; 167 168 indexer_guard( Pass<pass_t> & pass ) : pass( pass ) { __pass::indexer::enter(pass, 0); }169 ~indexer_guard() { __pass::indexer::leave(pass, 0); }170 201 }; 171 202 172 indexer_guard make_indexer_guard() { return { *this }; }173 174 private: 175 struct scope_guard {203 /// Internal RAII guard for scope features 204 struct guard_scope { 205 guard_scope( Pass<pass_t> & pass ): pass( pass ) { __pass::scope::enter(pass, 0); } 206 ~guard_scope() { __pass::scope::leave(pass, 0); } 176 207 Pass<pass_t> & pass; 177 178 scope_guard( Pass<pass_t> & pass ) : pass( pass ) { __pass::scope::enter(pass, 0); }179 ~scope_guard() { __pass::scope::leave(pass, 0); }180 208 }; 181 182 scope_guard make_scope_guard() { return { *this }; } 183 }; 184 185 //------------------------------------------------------------------------------------------------------------------------------------------------------------------------- 186 // Guard value : RAII type to restore a value when the Pass finishes visiting this node 187 template<typename pass_t, typename T> 188 void GuardValue( pass_t * pass, T& val ) { 189 pass->at_cleanup( [ val ]( void * newVal ) { 190 * static_cast< T * >( newVal ) = val; 191 }, static_cast< void * >( & val ) ); 192 } 193 194 //------------------------------------------------------------------------------------------------------------------------------------------------------------------------- 209 }; 210 211 template<typename pass_t> 212 void acceptAll( std::list< ptr<Decl> >, Pass<pass_t>& visitor ); 213 214 //------------------------------------------------------------------------------------------------- 195 215 // PASS ACCESSORIES 196 //------------------------------------------------------------------------------------------------------------------------------------------------------------------------- 197 198 // Keep track of the type substitution 199 struct WithConstTypeSubstitution { 200 const ast::TypeSubstitution * env = nullptr; 201 }; 216 //------------------------------------------------------------------------------------------------- 202 217 203 218 template<typename T> 204 219 using std_list = std::list<T>; 205 220 206 // Used if visitor requires added statements before or after the current node. 207 // The Pass template handles what *before* and *after* means automatically 221 /// Keep track of the polymorphic const TypeSubstitution * env for the current expression 222 struct WithConstTypeSubstitution { 223 const TypeSubstitution * env = nullptr; 224 }; 225 226 /// Used if visitor requires added statements before or after the current node. 227 /// The Pass template handles what *before* and *after* means automatically 208 228 template< template<class> class container_t = std_list > 209 229 struct WithStmtsToAdd { 210 container_t< ast::ptr< ast::Statement> > stmtsToAddBefore;211 container_t< ast::ptr< ast::Statement> > stmtsToAddAfter;212 }; 213 214 // Used if visitor requires added declarations before or after the current node.215 // The Pass template handles what *before* and *after* means automatically230 container_t< ptr<Stmt> > stmtsToAddBefore; 231 container_t< ptr<Stmt> > stmtsToAddAfter; 232 }; 233 234 /// Used if visitor requires added declarations before or after the current node. 235 /// The Pass template handles what *before* and *after* means automatically 216 236 template< template<class> class container_t = std_list > 217 237 struct WithDeclsToAdd { 218 ~WithDeclsToAdd() { 219 assert( declsToAddBefore.empty() ); 220 } 221 222 container_t< ast::ptr< ast::Declaration > > declsToAddBefore; 223 container_t< ast::ptr< ast::Declaration > > declsToAddAfter; 224 }; 225 226 // Use if visitation should stop at certain levels 227 // set visit_children false of all child nodes should be ignored 238 container_t< ptr<Decl> > declsToAddBefore; 239 container_t< ptr<Decl> > declsToAddAfter; 240 }; 241 242 /// Use if visitation should stop at certain levels 243 /// set visit_children false of all child nodes should be ignored 228 244 struct WithShortCircuiting { 229 245 __pass::bool_ref visit_children; 230 246 }; 231 247 232 // class WithGuards { 233 // protected: 234 // WithGuards() = default; 235 // ~WithGuards() = default; 236 237 // public: 238 // at_cleanup_t at_cleanup; 239 240 // template< typename T > 241 // void GuardValue( T& val ) { 242 // at_cleanup( [ val ]( void * newVal ) { 243 // * static_cast< T * >( newVal ) = val; 244 // }, static_cast< void * >( & val ) ); 245 // } 246 247 // template< typename T > 248 // void GuardScope( T& val ) { 249 // val.beginScope(); 250 // at_cleanup( []( void * val ) { 251 // static_cast< T * >( val )->endScope(); 252 // }, static_cast< void * >( & val ) ); 253 // } 254 255 // template< typename Func > 256 // void GuardAction( Func func ) { 257 // at_cleanup( [func](__attribute__((unused)) void *) { func(); }, nullptr ); 258 // } 259 // }; 260 261 // template<typename pass_type> 262 // class WithVisitorRef { 263 // protected: 264 // WithVisitorRef() {} 265 // ~WithVisitorRef() {} 266 267 // public: 268 // PassVisitor<pass_type> * const visitor = nullptr; 269 // }; 270 248 /// Used to restore values/functions/etc. when the Pass finishes visiting this node 249 class WithGuards { 250 __pass::at_cleanup_t at_cleanup; 251 252 public: 253 /// When this node is finished being visited, restore the value of a variable 254 template< typename T > 255 void GuardValue( T& val ) { 256 at_cleanup( [ val ]( void * newVal ) { 257 * static_cast< T * >( newVal ) = val; 258 }, static_cast< void * >( & val ) ); 259 } 260 261 /// On the object, all beginScope now and endScope when the current node is finished being visited 262 template< typename T > 263 void GuardScope( T& val ) { 264 val.beginScope(); 265 at_cleanup( []( void * val ) { 266 static_cast< T * >( val )->endScope(); 267 }, static_cast< void * >( & val ) ); 268 } 269 270 /// When this node is finished being visited, call a function 271 template< typename Func > 272 void GuardAction( Func func ) { 273 at_cleanup( [func](void *) { func(); }, nullptr ); 274 } 275 }; 276 277 /// Used to get a pointer to the pass with its wrapped type 278 template<typename pass_t> 279 struct WithVisitorRef { 280 Pass<pass_t> * const visitor = nullptr; 281 }; 282 283 /// Use when the templated visitor should update the indexer 271 284 struct WithIndexer { 272 285 SymTab::Indexer indexer; -
src/AST/Pass.impl.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 // IWYU pragma: private, include " Pass.hpp"17 // IWYU pragma: private, include "AST/Pass.hpp" 3 18 4 19 #define VISIT_START( node ) \ 20 using namespace ast; \ 5 21 /* back-up the visit children */ \ 6 __attribute__((unused)) ast::__pass::visit_children_guard guard1( ast::__pass::visit_children( m_pass, 0) ); \22 __attribute__((unused)) ast::__pass::visit_children_guard guard1( ast::__pass::visit_children(pass, 0) ); \ 7 23 /* setup the scope for passes that want to run code at exit */ \ 8 __attribute__((unused)) ast::__pass::guard_value guard2( ast::__pass::at_cleanup ( m_pass, 0) ); \24 __attribute__((unused)) ast::__pass::guard_value guard2( ast::__pass::at_cleanup (pass, 0) ); \ 9 25 /* call the implementation of the previsit of this pass */ \ 10 __pass::previsit( m_pass, node, 0 );26 __pass::previsit( pass, node, 0 ); 11 27 12 28 #define VISIT( code ) \ … … 98 114 99 115 template< typename pass_t > 100 ast::Expr ession * Pass< pass_t >::call_accept( const ast::Expression* expr ) {116 ast::Expr * Pass< pass_t >::call_accept( const ast::Expr * expr ) { 101 117 __pedantic_pass_assert( __visit_children() ); 102 118 __pedantic_pass_assert( expr ); 103 119 104 const ast::TypeSubstitution ** env_ptr = __pass::env( m_pass, 0);120 const ast::TypeSubstitution ** env_ptr = __pass::env( pass, 0); 105 121 if ( env_ptr && expr->env ) { 106 122 *env_ptr = expr->env; … … 111 127 112 128 template< typename pass_t > 113 ast::Statement * Pass< pass_t >::call_accept( const ast::Statement * stmt ) {129 Stmt * Pass< pass_t >::call_accept( const Stmt * stmt ) { 114 130 __pedantic_pass_assert( __visit_children() ); 115 131 __pedantic_pass_assert( stmt ); 116 117 // add a few useful symbols to the scope118 using __pass::empty;119 using decls_t = typename std::remove_pointer< decltype(__decls_before()) >::type;120 using stmts_t = typename std::remove_pointer< decltype(__stmts_before()) >::type;121 122 // get the stmts/decls that will need to be spliced in123 auto stmts_before = __pass::stmtsToAddBefore( m_pass, 0);124 auto stmts_after = __pass::stmtsToAddAfter ( m_pass, 0);125 auto decls_before = __pass::declsToAddBefore( m_pass, 0);126 auto decls_after = __pass::declsToAddAfter ( m_pass, 0);127 128 // These may be modified by subnode but most be restored once we exit this statemnet.129 ValueGuardPtr< const ast::TypeSubstitution * > __old_env ( __pass::env( m_pass, 0); );130 ValueGuardPtr< typename std::remove_pointer< decltype(stmts_before) > __old_decls_before( stmts_before );131 ValueGuardPtr< typename std::remove_pointer< decltype(stmts_after ) > __old_decls_after ( stmts_after );132 ValueGuardPtr< typename std::remove_pointer< decltype(decls_before) > __old_stmts_before( decls_before );133 ValueGuardPtr< typename std::remove_pointer< decltype(decls_after ) > __old_stmts_after ( decls_after );134 135 // Now is the time to actually visit the node136 ast::Statement * nstmt = stmt->accept( *this );137 138 // If the pass doesn't want to add anything then we are done139 if( empty(stmts_before) && empty(stmts_after) && empty(decls_before) && empty(decls_after) ) {140 return nstmt;141 }142 143 // Make sure that it is either adding statements or declartions but not both144 // this is because otherwise the order would be awkward to predict145 assert(( empty( stmts_before ) && empty( stmts_after ))146 || ( empty( decls_before ) && empty( decls_after )) );147 148 // Create a new Compound Statement to hold the new decls/stmts149 ast::CompoundStmt * compound = new ast::CompoundStmt( parent->*child.location );150 151 // Take all the declarations that go before152 __pass::take_all( std::back_inserter( compound->kids ), decls_before );153 __pass::take_all( std::back_inserter( compound->kids ), stmts_before );154 155 // Insert the original declaration156 compound->kids.push_back( nstmt );157 158 // Insert all the declarations that go before159 __pass::take_all( std::back_inserter( compound->kids ), decls_after );160 __pass::take_all( std::back_inserter( compound->kids ), stmts_after );161 162 return compound;163 }164 165 template< typename pass_t >166 template< template <class> class container_t >167 container_t< ast::ptr<ast::Statement> > Pass< pass_t >::call_accept( const container_t< ast::ptr<ast::Statement> > & statements ) {168 __pedantic_pass_assert( __visit_children() );169 if( statements.empty() ) return {};170 171 // We are going to aggregate errors for all these statements172 SemanticErrorException errors;173 132 174 133 // add a few useful symbols to the scope … … 182 141 183 142 // These may be modified by subnode but most be restored once we exit this statemnet. 143 ValueGuardPtr< const ast::TypeSubstitution * > __old_env ( __pass::env( pass, 0); ); 184 144 ValueGuardPtr< typename std::remove_pointer< decltype(stmts_before) > __old_decls_before( stmts_before ); 185 145 ValueGuardPtr< typename std::remove_pointer< decltype(stmts_after ) > __old_decls_after ( stmts_after ); … … 187 147 ValueGuardPtr< typename std::remove_pointer< decltype(decls_after ) > __old_stmts_after ( decls_after ); 188 148 149 // Now is the time to actually visit the node 150 ast::Statement * nstmt = stmt->accept( *this ); 151 152 // If the pass doesn't want to add anything then we are done 153 if( empty(stmts_before) && empty(stmts_after) && empty(decls_before) && empty(decls_after) ) { 154 return nstmt; 155 } 156 157 // Make sure that it is either adding statements or declartions but not both 158 // this is because otherwise the order would be awkward to predict 159 assert(( empty( stmts_before ) && empty( stmts_after )) 160 || ( empty( decls_before ) && empty( decls_after )) ); 161 162 // Create a new Compound Statement to hold the new decls/stmts 163 ast::CompoundStmt * compound = new ast::CompoundStmt( parent->*child.location ); 164 165 // Take all the declarations that go before 166 __pass::take_all( std::back_inserter( compound->kids ), decls_before ); 167 __pass::take_all( std::back_inserter( compound->kids ), stmts_before ); 168 169 // Insert the original declaration 170 compound->kids.push_back( nstmt ); 171 172 // Insert all the declarations that go before 173 __pass::take_all( std::back_inserter( compound->kids ), decls_after ); 174 __pass::take_all( std::back_inserter( compound->kids ), stmts_after ); 175 176 return compound; 177 } 178 179 template< typename pass_t > 180 template< template <class> class container_t > 181 container_t< ptr<Stmt> > Pass< pass_t >::call_accept( const container_t< ptr<Stmt> > & statements ) { 182 __pedantic_pass_assert( __visit_children() ); 183 if( statements.empty() ) return {}; 184 185 // We are going to aggregate errors for all these statements 186 SemanticErrorException errors; 187 188 // add a few useful symbols to the scope 189 using __pass::empty; 190 191 // get the stmts/decls that will need to be spliced in 192 auto stmts_before = __pass::stmtsToAddBefore( pass, 0); 193 auto stmts_after = __pass::stmtsToAddAfter ( pass, 0); 194 auto decls_before = __pass::declsToAddBefore( pass, 0); 195 auto decls_after = __pass::declsToAddAfter ( pass, 0); 196 197 // These may be modified by subnode but most be restored once we exit this statemnet. 198 ValueGuardPtr< typename std::remove_pointer< decltype(stmts_before) > __old_decls_before( stmts_before ); 199 ValueGuardPtr< typename std::remove_pointer< decltype(stmts_after ) > __old_decls_after ( stmts_after ); 200 ValueGuardPtr< typename std::remove_pointer< decltype(decls_before) > __old_stmts_before( decls_before ); 201 ValueGuardPtr< typename std::remove_pointer< decltype(decls_after ) > __old_stmts_after ( decls_after ); 202 189 203 // update pass statitistics 190 204 pass_visitor_stats.depth++; … … 193 207 194 208 bool mutated = false; 195 container_t< ast::ptr< ast::Statement >> new_kids;196 for( const ast::Statement * stmt : statements ) {209 container_t< ptr<Stmt> > new_kids; 210 for( const Stmt * stmt : statements ) { 197 211 try { 198 212 __pedantic_pass_assert( stmt ); … … 269 283 //------------------------------------------------------------------------------------------------------------------------------------------------------------------------ 270 284 285 template< typename pass_t > 286 inline void ast::acceptAll( std::list< ast::ptr<ast::Decl> > & decls, ast::Pass< pass_t > & visitor ) { 287 // We are going to aggregate errors for all these statements 288 SemanticErrorException errors; 289 290 // add a few useful symbols to the scope 291 using __pass::empty; 292 293 // get the stmts/decls that will need to be spliced in 294 auto decls_before = __pass::declsToAddBefore( pass, 0); 295 auto decls_after = __pass::declsToAddAfter ( pass, 0); 296 297 // update pass statitistics 298 pass_visitor_stats.depth++; 299 pass_visitor_stats.max->push(pass_visitor_stats.depth); 300 pass_visitor_stats.avg->push(pass_visitor_stats.depth); 301 302 for ( std::list< ast::ptr<ast::Decl> >::iterator i = decls.begin(); ; ++i ) { 303 // splice in new declarations after previous decl 304 if ( !empty( decls_after ) ) { decls.splice( i, *decls_after ); } 305 306 if ( i == decls.end() ) break; 307 308 try { 309 // run visitor on declaration 310 ast::ptr<ast::Decl> & node = *i; 311 assert( node ); 312 node = node->accept( visitor ); 313 } 314 catch( SemanticErrorException &e ) { 315 errors.append( e ); 316 } 317 318 // splice in new declarations before current decl 319 if ( !empty( decls_before ) ) { decls.splice( i, *decls_before ); } 320 } 321 pass_visitor_stats.depth--; 322 if ( !errors.isEmpty() ) { throw errors; } 323 } 324 271 325 // A NOTE ON THE ORDER OF TRAVERSAL 272 326 // … … 289 343 // ObjectDecl 290 344 template< typename pass_t > 291 ast::Decl arationWithType * Pass< pass_t >::mutate(ast::ObjectDecl * node ) {345 ast::DeclWithType * ast::Pass< pass_t >::visit( const ast::ObjectDecl * node ) { 292 346 VISIT_START( node ); 293 347 294 348 VISIT( 295 349 { 296 auto guard = make_indexer_guard();297 maybe_accept( node, ast::ObjectDecl::type );298 } 299 maybe_accept( node, ast::ObjectDecl::init );300 maybe_accept( node, ast::ObjectDecl::bitfieldWidth );301 maybe_accept( node, ast::ObjectDecl::attributes );350 indexer_guard guard { *this }; 351 maybe_accept( node, ObjectDecl::type ); 352 } 353 maybe_accept( node, ObjectDecl::init ); 354 maybe_accept( node, ObjectDecl::bitfieldWidth ); 355 maybe_accept( node, ObjectDecl::attributes ); 302 356 ) 303 357 304 __pass::indexer::AddId( m_pass, 0, node );305 306 VISIT_END( Decl arationWithType, node );358 __pass::indexer::AddId( pass, 0, node ); 359 360 VISIT_END( DeclWithType, node ); 307 361 } 308 362 … … 310 364 // Attribute 311 365 template< typename pass_type > 312 ast::Attribute * ast::Pass< pass_type >::visit( ast::ptr<ast::Attribute> &node ) {366 ast::Attribute * ast::Pass< pass_type >::visit( const ast::Attribute * node ) { 313 367 VISIT_START(node); 314 368 … … 323 377 // TypeSubstitution 324 378 template< typename pass_type > 325 TypeSubstitution * PassVisitor< pass_type >::mutate( TypeSubstitution * node ) {379 TypeSubstitution * PassVisitor< pass_type >::mutate( const TypeSubstitution * node ) { 326 380 MUTATE_START( node ); 327 381 -
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.