source: src/AST/Pass.impl.hpp@ be567e9

ADT arm-eh ast-experimental cleanup-dtors enum forall-pointer-decay jacob/cs343-translation jenkins-sandbox new-ast new-ast-unique-expr pthread-emulation qualifiedEnum
Last change on this file since be567e9 was 6d51bd7, checked in by Thierry Delisle <tdelisle@…>, 6 years ago

Fixes to the new templated pass and started on conversions

  • Property mode set to 100644
File size: 17.5 KB
RevLine 
[04124c4]1//
2// Cforall Version 1.0.0 Copyright (C) 2019 University of Waterloo
3//
4// The contents of this file are covered under the licence agreement in the
5// file "LICENCE" distributed with Cforall.
6//
7// Pass.impl.hpp --
8//
9// Author : Thierry Delisle
10// Created On : Thu May 09 15::37::05 2019
11// Last Modified By :
12// Last Modified On :
13// Update Count :
14//
15
[f47f887]16#pragma once
[04124c4]17// IWYU pragma: private, include "AST/Pass.hpp"
[f47f887]18
[6d51bd7]19#include <type_traits>
20#include <unordered_map>
21
[f47f887]22#define VISIT_START( node ) \
[04124c4]23 using namespace ast; \
[f47f887]24 /* back-up the visit children */ \
[04124c4]25 __attribute__((unused)) ast::__pass::visit_children_guard guard1( ast::__pass::visit_children(pass, 0) ); \
[f47f887]26 /* setup the scope for passes that want to run code at exit */ \
[04124c4]27 __attribute__((unused)) ast::__pass::guard_value guard2( ast::__pass::at_cleanup (pass, 0) ); \
[f47f887]28 /* call the implementation of the previsit of this pass */ \
[04124c4]29 __pass::previsit( pass, node, 0 );
[f47f887]30
[6d51bd7]31#define VISIT( code... ) \
[f47f887]32 /* if this node should visit its children */ \
33 if ( __visit_children() ) { \
34 /* visit the children */ \
35 code \
36 }
37
38#define VISIT_END( type, node ) \
39 /* call the implementation of the postvisit of this pass */ \
[6d51bd7]40 auto __return = __pass::postvisit( pass, node, 0 ); \
[f47f887]41 assertf(__return, "post visit should never return null"); \
42 return __return;
43
44#ifdef PEDANTIC_PASS_ASSERT
[6d51bd7]45#define __pedantic_pass_assert(...) assert (__VA_ARGS__)
46#define __pedantic_pass_assertf(...) assertf(__VA_ARGS__)
[f47f887]47#else
[6d51bd7]48#define __pedantic_pass_assert(...)
[f47f887]49#define __pedantic_pass_assertf(...)
50#endif
51
52namespace ast {
53 namespace __pass {
54 // Check if this is either a null pointer or a pointer to an empty container
55 template<typename T>
56 static inline bool empty( T * ptr ) {
57 return !ptr || ptr->empty();
58 }
59
[6d51bd7]60 //------------------------------
61 template<typename it_t, template <class...> class container_t>
62 static inline void take_all( it_t it, container_t<ast::ptr<ast::Decl>> * decls, bool * mutated = nullptr ) {
[f47f887]63 if(empty(decls)) return;
64
[6d51bd7]65 std::transform(decls->begin(), decls->end(), it, [](const ast::Decl * decl) -> auto {
[f47f887]66 return new DeclStmt( decl );
67 });
68 decls->clear();
69 if(mutated) *mutated = true;
70 }
71
[6d51bd7]72 template<typename it_t, template <class...> class container_t>
73 static inline void take_all( it_t it, container_t<ast::ptr<ast::Stmt>> * decls, bool * mutated = nullptr ) {
[f47f887]74 if(empty(decls)) return;
75
76 std::move(decls->begin(), decls->end(), it);
77 decls->clear();
78 if(mutated) *mutated = true;
79 }
80
[6d51bd7]81 //------------------------------
82 /// Check if should be skipped, different for pointers and containers
[f47f887]83 template<typename node_t>
[6d51bd7]84 bool skip( const ast::ptr<node_t> & val) {
85 return !val;
[f47f887]86 }
87
[6d51bd7]88 template< template <class...> class container_t, typename node_t >
89 bool skip( const container_t<ast::ptr< node_t >> & val ) {
90 return val.empty();
[f47f887]91 }
92
[6d51bd7]93 //------------------------------
94 /// Get the value to visit, different for pointers and containers
95 template<typename node_t>
96 auto get( const ast::ptr<node_t> & val, int ) -> decltype(val.get()) {
97 return val.get();
98 }
[f47f887]99
[6d51bd7]100 template<typename node_t>
101 const node_t & get( const node_t & val, long) {
102 return val;
103 }
[f47f887]104
[6d51bd7]105
106 //------------------------------
107 /// Check if value was mutated, different for pointers and containers
108 template<typename lhs_t, typename rhs_t>
109 bool differs( const lhs_t * old_val, const rhs_t * new_val ) {
110 return old_val != new_val;
111 }
112
113 template< template <class...> class container_t, typename node_t >
114 bool differs( const container_t<ast::ptr< node_t >> &, const container_t<ast::ptr< node_t >> & new_val ) {
115 return !new_val.empty();
[f47f887]116 }
117 }
118
119 template< typename pass_t >
120 template< typename node_t >
[6d51bd7]121 auto Pass< pass_t >::call_accept( const node_t * node ) -> decltype( node->accept(*this) ) {
[f47f887]122 __pedantic_pass_assert( __visit_children() );
123 __pedantic_pass_assert( expr );
124
[6d51bd7]125 static_assert( !std::is_base_of<ast::Expr, node_t>::value, "ERROR");
126 static_assert( !std::is_base_of<ast::Stmt, node_t>::value, "ERROR");
127
[f47f887]128 return node->accept( *this );
129 }
130
131 template< typename pass_t >
[6d51bd7]132 const ast::Expr * Pass< pass_t >::call_accept( const ast::Expr * expr ) {
[f47f887]133 __pedantic_pass_assert( __visit_children() );
134 __pedantic_pass_assert( expr );
135
[04124c4]136 const ast::TypeSubstitution ** env_ptr = __pass::env( pass, 0);
[f47f887]137 if ( env_ptr && expr->env ) {
138 *env_ptr = expr->env;
139 }
140
141 return expr->accept( *this );
142 }
143
144 template< typename pass_t >
[6d51bd7]145 const ast::Stmt * Pass< pass_t >::call_accept( const ast::Stmt * stmt ) {
[f47f887]146 __pedantic_pass_assert( __visit_children() );
147 __pedantic_pass_assert( stmt );
148
149 // add a few useful symbols to the scope
150 using __pass::empty;
151
152 // get the stmts/decls that will need to be spliced in
[04124c4]153 auto stmts_before = __pass::stmtsToAddBefore( pass, 0);
154 auto stmts_after = __pass::stmtsToAddAfter ( pass, 0);
155 auto decls_before = __pass::declsToAddBefore( pass, 0);
156 auto decls_after = __pass::declsToAddAfter ( pass, 0);
[f47f887]157
158 // These may be modified by subnode but most be restored once we exit this statemnet.
[6d51bd7]159 ValueGuardPtr< const ast::TypeSubstitution * > __old_env ( __pass::env( pass, 0) );
160 ValueGuardPtr< typename std::remove_pointer< decltype(stmts_before) > > __old_decls_before( stmts_before );
161 ValueGuardPtr< typename std::remove_pointer< decltype(stmts_after ) > > __old_decls_after ( stmts_after );
162 ValueGuardPtr< typename std::remove_pointer< decltype(decls_before) > > __old_stmts_before( decls_before );
163 ValueGuardPtr< typename std::remove_pointer< decltype(decls_after ) > > __old_stmts_after ( decls_after );
[f47f887]164
165 // Now is the time to actually visit the node
[6d51bd7]166 const ast::Stmt * nstmt = stmt->accept( *this );
[f47f887]167
168 // If the pass doesn't want to add anything then we are done
169 if( empty(stmts_before) && empty(stmts_after) && empty(decls_before) && empty(decls_after) ) {
170 return nstmt;
171 }
172
173 // Make sure that it is either adding statements or declartions but not both
174 // this is because otherwise the order would be awkward to predict
175 assert(( empty( stmts_before ) && empty( stmts_after ))
176 || ( empty( decls_before ) && empty( decls_after )) );
177
178 // Create a new Compound Statement to hold the new decls/stmts
[6d51bd7]179 ast::CompoundStmt * compound = new ast::CompoundStmt( stmt->location );
[f47f887]180
181 // Take all the declarations that go before
182 __pass::take_all( std::back_inserter( compound->kids ), decls_before );
183 __pass::take_all( std::back_inserter( compound->kids ), stmts_before );
184
185 // Insert the original declaration
[6d51bd7]186 compound->kids.emplace_back( nstmt );
[f47f887]187
188 // Insert all the declarations that go before
189 __pass::take_all( std::back_inserter( compound->kids ), decls_after );
190 __pass::take_all( std::back_inserter( compound->kids ), stmts_after );
191
192 return compound;
193 }
194
195 template< typename pass_t >
[6d51bd7]196 template< template <class...> class container_t >
[04124c4]197 container_t< ptr<Stmt> > Pass< pass_t >::call_accept( const container_t< ptr<Stmt> > & statements ) {
[f47f887]198 __pedantic_pass_assert( __visit_children() );
199 if( statements.empty() ) return {};
200
201 // We are going to aggregate errors for all these statements
202 SemanticErrorException errors;
203
204 // add a few useful symbols to the scope
205 using __pass::empty;
206
207 // get the stmts/decls that will need to be spliced in
208 auto stmts_before = __pass::stmtsToAddBefore( pass, 0);
209 auto stmts_after = __pass::stmtsToAddAfter ( pass, 0);
210 auto decls_before = __pass::declsToAddBefore( pass, 0);
211 auto decls_after = __pass::declsToAddAfter ( pass, 0);
212
213 // These may be modified by subnode but most be restored once we exit this statemnet.
[6d51bd7]214 ValueGuardPtr< typename std::remove_pointer< decltype(stmts_before) > > __old_decls_before( stmts_before );
215 ValueGuardPtr< typename std::remove_pointer< decltype(stmts_after ) > > __old_decls_after ( stmts_after );
216 ValueGuardPtr< typename std::remove_pointer< decltype(decls_before) > > __old_stmts_before( decls_before );
217 ValueGuardPtr< typename std::remove_pointer< decltype(decls_after ) > > __old_stmts_after ( decls_after );
[f47f887]218
219 // update pass statitistics
220 pass_visitor_stats.depth++;
221 pass_visitor_stats.max->push(pass_visitor_stats.depth);
222 pass_visitor_stats.avg->push(pass_visitor_stats.depth);
223
224 bool mutated = false;
[04124c4]225 container_t< ptr<Stmt> > new_kids;
226 for( const Stmt * stmt : statements ) {
[f47f887]227 try {
228 __pedantic_pass_assert( stmt );
[6d51bd7]229 const ast::Stmt * new_stmt = stmt->accept( *this );
[f47f887]230 assert( new_stmt );
231 if(new_stmt != stmt ) mutated = true;
232
233 // Make sure that it is either adding statements or declartions but not both
234 // this is because otherwise the order would be awkward to predict
235 assert(( empty( stmts_before ) && empty( stmts_after ))
236 || ( empty( decls_before ) && empty( decls_after )) );
237
238
239
240 // Take all the statements which should have gone after, N/A for first iteration
241 __pass::take_all( std::back_inserter( new_kids ), decls_before, &mutated );
242 __pass::take_all( std::back_inserter( new_kids ), stmts_before, &mutated );
243
244 // Now add the statement if there is one
245 new_kids.emplace_back( new_stmt );
246
247 // Take all the declarations that go before
248 __pass::take_all( std::back_inserter( new_kids ), decls_after, &mutated );
249 __pass::take_all( std::back_inserter( new_kids ), stmts_after, &mutated );
250 }
251 catch ( SemanticErrorException &e ) {
252 errors.append( e );
253 }
254 }
255 pass_visitor_stats.depth--;
256 if ( !errors.isEmpty() ) { throw errors; }
257
[6d51bd7]258 return mutated ? new_kids : container_t< ptr<Stmt> >();
[f47f887]259 }
260
261 template< typename pass_t >
[6d51bd7]262 template< template <class...> class container_t, typename node_t >
[f47f887]263 container_t< ast::ptr<node_t> > Pass< pass_t >::call_accept( const container_t< ast::ptr<node_t> > & container ) {
264 __pedantic_pass_assert( __visit_children() );
265 if( container.empty() ) return {};
266 SemanticErrorException errors;
267
268 pass_visitor_stats.depth++;
269 pass_visitor_stats.max->push(pass_visitor_stats.depth);
270 pass_visitor_stats.avg->push(pass_visitor_stats.depth);
271
272 bool mutated = false;
273 container_t< ast::ptr<node_t> > new_kids;
274 for ( const node_t * node : container ) {
275 try {
276 __pedantic_pass_assert( node );
[6d51bd7]277 const node_t * new_stmt = strict_dynamic_cast< const node_t * >( node->accept( *this ) );
278 if(new_stmt != node ) mutated = true;
[f47f887]279
280 new_kids.emplace_back( new_stmt );
281 }
282 catch( SemanticErrorException &e ) {
283 errors.append( e );
284 }
285 }
286 pass_visitor_stats.depth--;
287 if ( ! errors.isEmpty() ) { throw errors; }
288
[6d51bd7]289 return mutated ? new_kids : container_t< ast::ptr<node_t> >();
290 }
291
292 template< typename pass_t >
293 template<typename node_t, typename parent_t, typename child_t>
294 void Pass< pass_t >::maybe_accept(
295 const node_t * & parent,
296 child_t parent_t::*child
297 ) {
298 static_assert( std::is_base_of<parent_t, node_t>::value, "Error deductiing member object" );
299
300 if(__pass::skip(parent->*child)) return;
301 const auto & old_val = __pass::get(parent->*child, 0);
302
303 static_assert( !std::is_same<const ast::Node * &, decltype(old_val)>::value, "ERROR");
304
305 auto new_val = call_accept( old_val );
306
307 static_assert( !std::is_same<const ast::Node *, decltype(new_val)>::value || std::is_same<int, decltype(old_val)>::value, "ERROR");
308
309 if( __pass::differs(old_val, new_val) ) {
310 auto new_parent = mutate(parent);
311 new_parent->*child = new_val;
312 parent = new_parent;
313 }
[f47f887]314 }
[6d51bd7]315
[f47f887]316}
317
318//------------------------------------------------------------------------------------------------------------------------------------------------------------------------
319//========================================================================================================================================================================
320//========================================================================================================================================================================
321//========================================================================================================================================================================
322//========================================================================================================================================================================
323//========================================================================================================================================================================
324//------------------------------------------------------------------------------------------------------------------------------------------------------------------------
325
[04124c4]326template< typename pass_t >
[6d51bd7]327inline void ast::accept_all( std::list< ast::ptr<ast::Decl> > & decls, ast::Pass< pass_t > & visitor ) {
[04124c4]328 // We are going to aggregate errors for all these statements
329 SemanticErrorException errors;
330
331 // add a few useful symbols to the scope
332 using __pass::empty;
333
334 // get the stmts/decls that will need to be spliced in
[6d51bd7]335 auto decls_before = __pass::declsToAddBefore( visitor.pass, 0);
336 auto decls_after = __pass::declsToAddAfter ( visitor.pass, 0);
[04124c4]337
338 // update pass statitistics
339 pass_visitor_stats.depth++;
340 pass_visitor_stats.max->push(pass_visitor_stats.depth);
341 pass_visitor_stats.avg->push(pass_visitor_stats.depth);
342
343 for ( std::list< ast::ptr<ast::Decl> >::iterator i = decls.begin(); ; ++i ) {
344 // splice in new declarations after previous decl
345 if ( !empty( decls_after ) ) { decls.splice( i, *decls_after ); }
346
347 if ( i == decls.end() ) break;
348
349 try {
350 // run visitor on declaration
351 ast::ptr<ast::Decl> & node = *i;
352 assert( node );
353 node = node->accept( visitor );
354 }
355 catch( SemanticErrorException &e ) {
356 errors.append( e );
357 }
358
359 // splice in new declarations before current decl
360 if ( !empty( decls_before ) ) { decls.splice( i, *decls_before ); }
361 }
362 pass_visitor_stats.depth--;
363 if ( !errors.isEmpty() ) { throw errors; }
364}
365
[f47f887]366// A NOTE ON THE ORDER OF TRAVERSAL
367//
368// Types and typedefs have their base types visited before they are added to the type table. This is ok, since there is
369// no such thing as a recursive type or typedef.
370//
371// typedef struct { T *x; } T; // never allowed
372//
373// for structs/unions, it is possible to have recursion, so the decl should be added as if it's incomplete to begin, the
374// members are traversed, and then the complete type should be added (assuming the type is completed by this particular
375// declaration).
376//
377// struct T { struct T *x; }; // allowed
378//
379// It is important to add the complete type to the symbol table *after* the members/base has been traversed, since that
380// traversal may modify the definition of the type and these modifications should be visible when the symbol table is
381// queried later in this pass.
382
383//--------------------------------------------------------------------------
384// ObjectDecl
385template< typename pass_t >
[6d51bd7]386const ast::DeclWithType * ast::Pass< pass_t >::visit( const ast::ObjectDecl * node ) {
[f47f887]387 VISIT_START( node );
388
389 VISIT(
390 {
[6d51bd7]391 guard_indexer guard { *this };
392 maybe_accept( node, &ast::ObjectDecl::type );
[f47f887]393 }
[6d51bd7]394 maybe_accept( node, &ast::ObjectDecl::init );
395 maybe_accept( node, &ast::ObjectDecl::bitfieldWidth );
396 maybe_accept( node, &ast::ObjectDecl::attributes );
[f47f887]397 )
398
[6d51bd7]399 __pass::indexer::addId( pass, 0, node );
[f47f887]400
[04124c4]401 VISIT_END( DeclWithType, node );
[f47f887]402}
403
[6d51bd7]404//--------------------------------------------------------------------------
405// SingleInit
406template< typename pass_t >
407const ast::Init * ast::Pass< pass_t >::visit( const ast::SingleInit * node ) {
408 VISIT_START( node );
409
410 VISIT(
411 maybe_accept( node, &SingleInit::value );
412 )
413
414 VISIT_END( Init, node );
415}
416
417//--------------------------------------------------------------------------
418// ListInit
419template< typename pass_t >
420const ast::Init * ast::Pass< pass_t >::visit( const ast::ListInit * node ) {
421 VISIT_START( node );
422
423 VISIT(
424 maybe_accept( node, &ListInit::designations );
425 maybe_accept( node, &ListInit::initializers );
426 )
427
428 VISIT_END( Init, node );
429}
430
431//--------------------------------------------------------------------------
432// ConstructorInit
433template< typename pass_t >
434const ast::Init * ast::Pass< pass_t >::visit( const ast::ConstructorInit * node ) {
435 VISIT_START( node );
436
437 VISIT(
438 maybe_accept( node, &ConstructorInit::ctor );
439 maybe_accept( node, &ConstructorInit::dtor );
440 maybe_accept( node, &ConstructorInit::init );
441 )
442
443 VISIT_END( Init, node );
444}
445
[f47f887]446//--------------------------------------------------------------------------
447// Attribute
[6d51bd7]448template< typename pass_t >
449const ast::Attribute * ast::Pass< pass_t >::visit( const ast::Attribute * node ) {
450 VISIT_START( node );
[f47f887]451
452 VISIT(
[6d51bd7]453 maybe_accept( node, &Attribute::parameters );
[f47f887]454 )
455
[6d51bd7]456 VISIT_END( Attribute *, node );
[f47f887]457}
458
459//--------------------------------------------------------------------------
460// TypeSubstitution
[6d51bd7]461template< typename pass_t >
462const ast::TypeSubstitution * ast::Pass< pass_t >::visit( const ast::TypeSubstitution * node ) {
463 VISIT_START( node );
[f47f887]464
[6d51bd7]465 VISIT(
466 {
467 bool mutated = false;
468 std::unordered_map< std::string, ast::ptr< ast::Type > > new_map;
469 for ( const auto & p : node->typeEnv ) {
470 guard_indexer guard { *this };
471 auto new_node = p.second->accept( *this );
472 if (new_node != p.second) mutated = false;
473 new_map.insert({ p.first, new_node });
474 }
475 if (mutated) {
476 auto new_node = mutate( node );
477 new_node->typeEnv.swap( new_map );
478 node = new_node;
479 }
480 }
[f47f887]481
[6d51bd7]482 {
483 bool mutated = false;
484 std::unordered_map< std::string, ast::ptr< ast::Expr > > new_map;
485 for ( const auto & p : node->varEnv ) {
486 guard_indexer guard { *this };
487 auto new_node = p.second->accept( *this );
488 if (new_node != p.second) mutated = false;
489 new_map.insert({ p.first, new_node });
490 }
491 if (mutated) {
492 auto new_node = mutate( node );
493 new_node->varEnv.swap( new_map );
494 node = new_node;
495 }
496 }
497 )
[f47f887]498
[6d51bd7]499 VISIT_END( TypeSubstitution, node );
[f47f887]500}
501
502#undef VISIT_START
503#undef VISIT
504#undef VISIT_END
Note: See TracBrowser for help on using the repository browser.