source: src/ControlStruct/MultiLevelExit.cpp@ 154672d

Last change on this file since 154672d was 4a40fca7, checked in by Andrew Beach <ajbeach@…>, 2 years ago

Clean-up in MultiLevelExit. Primary purpose is to remove some uneeded fill code.

  • Property mode set to 100644
File size: 21.5 KB
RevLine 
[b8ab91a]1//
2// Cforall Version 1.0.0 Copyright (C) 2015 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//
[817bb3c]7// MultiLevelExit.cpp -- Replaces CFA's local control flow with C's versions.
[b8ab91a]8//
9// Author : Andrew Beach
10// Created On : Mon Nov 1 13:48:00 2021
[400b8be]11// Last Modified By : Andrew Beach
[4a40fca7]12// Last Modified On : Wed Sep 6 12:00:00 2023
13// Update Count : 35
[b8ab91a]14//
15
16#include "MultiLevelExit.hpp"
17
18#include "AST/Pass.hpp"
19#include "AST/Stmt.hpp"
[66daee4]20#include "LabelGeneratorNew.hpp"
[b8ab91a]21
22#include <set>
[66daee4]23using namespace std;
24using namespace ast;
[b8ab91a]25
26namespace ControlStruct {
[4a40fca7]27
28namespace {
29
[b8ab91a]30class Entry {
[66daee4]31 public:
32 const Stmt * stmt;
33 private:
[b8ab91a]34 // Organized like a manual ADT. Avoids creating a bunch of dead data.
35 struct Target {
[66daee4]36 Label label;
[b8ab91a]37 bool used = false;
[66daee4]38 Target( const Label & label ) : label( label ) {}
[4a40fca7]39 Target() : label( CodeLocation(), "" ) {}
[b8ab91a]40 };
41 Target firstTarget;
42 Target secondTarget;
43
44 enum Kind {
[400b8be]45 ForStmtK, WhileDoStmtK, CompoundStmtK, IfStmtK, CaseClauseK, SwitchStmtK, TryStmtK
[b8ab91a]46 } kind;
47
48 bool fallDefaultValid = true;
49
[66daee4]50 static Label & useTarget( Target & target ) {
[b8ab91a]51 target.used = true;
52 return target.label;
53 }
[66daee4]54 public:
55 Entry( const ForStmt * stmt, Label breakExit, Label contExit ) :
56 stmt( stmt ), firstTarget( breakExit ), secondTarget( contExit ), kind( ForStmtK ) {}
[3b0bc16]57 Entry( const WhileDoStmt * stmt, Label breakExit, Label contExit ) :
58 stmt( stmt ), firstTarget( breakExit ), secondTarget( contExit ), kind( WhileDoStmtK ) {}
[66daee4]59 Entry( const CompoundStmt *stmt, Label breakExit ) :
60 stmt( stmt ), firstTarget( breakExit ), secondTarget(), kind( CompoundStmtK ) {}
61 Entry( const IfStmt *stmt, Label breakExit ) :
62 stmt( stmt ), firstTarget( breakExit ), secondTarget(), kind( IfStmtK ) {}
[400b8be]63 Entry( const CaseClause *, const CompoundStmt *stmt, Label fallExit ) :
64 stmt( stmt ), firstTarget( fallExit ), secondTarget(), kind( CaseClauseK ) {}
[66daee4]65 Entry( const SwitchStmt *stmt, Label breakExit, Label fallDefaultExit ) :
66 stmt( stmt ), firstTarget( breakExit ), secondTarget( fallDefaultExit ), kind( SwitchStmtK ) {}
67 Entry( const TryStmt *stmt, Label breakExit ) :
68 stmt( stmt ), firstTarget( breakExit ), secondTarget(), kind( TryStmtK ) {}
69
[3b0bc16]70 bool isContTarget() const { return kind <= WhileDoStmtK; }
[400b8be]71 bool isBreakTarget() const { return kind != CaseClauseK; }
72 bool isFallTarget() const { return kind == CaseClauseK; }
[66daee4]73 bool isFallDefaultTarget() const { return kind == SwitchStmtK; }
[b8ab91a]74
[3e5db5b4]75 // These routines set a target as being "used" by a BranchStmt
[3b0bc16]76 Label useContExit() { assert( kind <= WhileDoStmtK ); return useTarget(secondTarget); }
[400b8be]77 Label useBreakExit() { assert( kind != CaseClauseK ); return useTarget(firstTarget); }
78 Label useFallExit() { assert( kind == CaseClauseK ); return useTarget(firstTarget); }
[66daee4]79 Label useFallDefaultExit() { assert( kind == SwitchStmtK ); return useTarget(secondTarget); }
[b8ab91a]80
[3e5db5b4]81 // These routines check if a specific label for a statement is used by a BranchStmt
[3b0bc16]82 bool isContUsed() const { assert( kind <= WhileDoStmtK ); return secondTarget.used; }
[400b8be]83 bool isBreakUsed() const { assert( kind != CaseClauseK ); return firstTarget.used; }
84 bool isFallUsed() const { assert( kind == CaseClauseK ); return firstTarget.used; }
[66daee4]85 bool isFallDefaultUsed() const { assert( kind == SwitchStmtK ); return secondTarget.used; }
[b8ab91a]86 void seenDefault() { fallDefaultValid = false; }
87 bool isFallDefaultValid() const { return fallDefaultValid; }
88};
89
[66daee4]90// Helper predicates used in find_if calls (it doesn't take methods):
[b8ab91a]91bool isBreakTarget( const Entry & entry ) {
92 return entry.isBreakTarget();
93}
94
95bool isContinueTarget( const Entry & entry ) {
96 return entry.isContTarget();
97}
98
99bool isFallthroughTarget( const Entry & entry ) {
100 return entry.isFallTarget();
101}
102
103bool isFallthroughDefaultTarget( const Entry & entry ) {
104 return entry.isFallDefaultTarget();
105}
106
[817bb3c]107struct MultiLevelExitCore final :
[66daee4]108 public WithVisitorRef<MultiLevelExitCore>,
109 public WithShortCircuiting, public WithGuards {
[cb921d4]110 MultiLevelExitCore( const LabelToStmt & lt );
[b8ab91a]111
[66daee4]112 void previsit( const FunctionDecl * );
113
114 const CompoundStmt * previsit( const CompoundStmt * );
115 const BranchStmt * postvisit( const BranchStmt * );
[3b0bc16]116 void previsit( const WhileDoStmt * );
117 const WhileDoStmt * postvisit( const WhileDoStmt * );
[66daee4]118 void previsit( const ForStmt * );
119 const ForStmt * postvisit( const ForStmt * );
[400b8be]120 const CaseClause * previsit( const CaseClause * );
[66daee4]121 void previsit( const IfStmt * );
122 const IfStmt * postvisit( const IfStmt * );
123 void previsit( const SwitchStmt * );
124 const SwitchStmt * postvisit( const SwitchStmt * );
125 void previsit( const ReturnStmt * );
126 void previsit( const TryStmt * );
127 void postvisit( const TryStmt * );
[400b8be]128 void previsit( const FinallyClause * );
[66daee4]129
130 const Stmt * mutateLoop( const Stmt * body, Entry& );
[b8ab91a]131
[817bb3c]132 const LabelToStmt & target_table;
[66daee4]133 set<Label> fallthrough_labels;
134 vector<Entry> enclosing_control_structures;
135 Label break_label;
[b8ab91a]136 bool inFinally;
137
138 template<typename LoopNode>
139 void prehandleLoopStmt( const LoopNode * loopStmt );
140 template<typename LoopNode>
141 const LoopNode * posthandleLoopStmt( const LoopNode * loopStmt );
142
[66daee4]143 list<ptr<Stmt>> fixBlock(
144 const list<ptr<Stmt>> & kids, bool caseClause );
[b8ab91a]145
[817bb3c]146 template<typename UnaryPredicate>
147 auto findEnclosingControlStructure( UnaryPredicate pred ) {
[66daee4]148 return find_if( enclosing_control_structures.rbegin(),
149 enclosing_control_structures.rend(), pred );
[817bb3c]150 }
151};
[b8ab91a]152
[0577df2]153NullStmt * labelledNullStmt( const CodeLocation & cl, const Label & label ) {
[66daee4]154 return new NullStmt( cl, vector<Label>{ label } );
[b8ab91a]155}
156
[cb921d4]157MultiLevelExitCore::MultiLevelExitCore( const LabelToStmt & lt ) :
158 target_table( lt ), break_label( CodeLocation(), "" ),
[b8ab91a]159 inFinally( false )
160{}
161
[66daee4]162void MultiLevelExitCore::previsit( const FunctionDecl * ) {
[b8ab91a]163 visit_children = false;
164}
165
[66daee4]166const CompoundStmt * MultiLevelExitCore::previsit(
[0577df2]167 const CompoundStmt * stmt ) {
[b8ab91a]168 visit_children = false;
[3e5db5b4]169
170 // if the stmt is labelled then generate a label to check in postvisit if the label is used
[2f52b18]171 bool isLabeled = ! stmt->labels.empty();
[b8ab91a]172 if ( isLabeled ) {
[66daee4]173 Label breakLabel = newLabel( "blockBreak", stmt );
[b8ab91a]174 enclosing_control_structures.emplace_back( stmt, breakLabel );
175 GuardAction( [this]() { enclosing_control_structures.pop_back(); } );
176 }
177
[66daee4]178 auto mutStmt = mutate( stmt );
[b8ab91a]179 // A child statement may set the break label.
[891f707]180 mutStmt->kids = fixBlock( stmt->kids, false );
[b8ab91a]181
182 if ( isLabeled ) {
[2f52b18]183 assert( ! enclosing_control_structures.empty() );
[b8ab91a]184 Entry & entry = enclosing_control_structures.back();
[2f52b18]185 if ( ! entry.useBreakExit().empty() ) {
[b8ab91a]186 break_label = entry.useBreakExit();
187 }
188 }
189 return mutStmt;
190}
191
[0577df2]192size_t getUnusedIndex( const Stmt * stmt, const Label & originalTarget ) {
[b8ab91a]193 const size_t size = stmt->labels.size();
194
[66daee4]195 // If the label is empty, do not add unused attribute.
196 if ( originalTarget.empty() ) return size;
[b8ab91a]197
198 // Search for a label that matches the originalTarget.
199 for ( size_t i = 0 ; i < size ; ++i ) {
[66daee4]200 const Label & label = stmt->labels[i];
[b8ab91a]201 if ( label == originalTarget ) {
[66daee4]202 for ( const Attribute * attr : label.attributes ) {
[b8ab91a]203 if ( attr->name == "unused" ) return size;
204 }
205 return i;
206 }
207 }
[6180274]208 assertf( false, "CFA internal error: could not find label '%s' on statement %s",
[66daee4]209 originalTarget.name.c_str(), toString( stmt ).c_str() );
[b8ab91a]210}
211
[0577df2]212const Stmt * addUnused( const Stmt * stmt, const Label & originalTarget ) {
[b8ab91a]213 size_t i = getUnusedIndex( stmt, originalTarget );
214 if ( i == stmt->labels.size() ) {
215 return stmt;
216 }
[66daee4]217 Stmt * mutStmt = mutate( stmt );
218 mutStmt->labels[i].attributes.push_back( new Attribute( "unused" ) );
[b8ab91a]219 return mutStmt;
220}
221
[3e5db5b4]222// This routine updates targets on enclosing control structures to indicate which
223// label is used by the BranchStmt that is passed
[66daee4]224const BranchStmt * MultiLevelExitCore::postvisit( const BranchStmt * stmt ) {
225 vector<Entry>::reverse_iterator targetEntry =
[b8ab91a]226 enclosing_control_structures.rend();
[3e5db5b4]227
228 // Labels on different stmts require different approaches to access
[b8ab91a]229 switch ( stmt->kind ) {
[491bb81]230 case BranchStmt::Goto:
[b8ab91a]231 return stmt;
[491bb81]232 case BranchStmt::Continue:
233 case BranchStmt::Break: {
234 bool isContinue = stmt->kind == BranchStmt::Continue;
235 // Handle unlabeled break and continue.
236 if ( stmt->target.empty() ) {
237 if ( isContinue ) {
238 targetEntry = findEnclosingControlStructure( isContinueTarget );
239 } else {
240 if ( enclosing_control_structures.empty() ) {
[66daee4]241 SemanticError( stmt->location,
242 "'break' outside a loop, 'switch', or labelled block" );
[491bb81]243 }
244 targetEntry = findEnclosingControlStructure( isBreakTarget );
245 }
246 // Handle labeled break and continue.
247 } else {
248 // Lookup label in table to find attached control structure.
249 targetEntry = findEnclosingControlStructure(
250 [ targetStmt = target_table.at(stmt->target) ](auto entry){
[66daee4]251 return entry.stmt == targetStmt;
[491bb81]252 } );
253 }
254 // Ensure that selected target is valid.
255 if ( targetEntry == enclosing_control_structures.rend() || ( isContinue && ! isContinueTarget( *targetEntry ) ) ) {
256 SemanticError( stmt->location, toString( (isContinue ? "'continue'" : "'break'"),
[66daee4]257 " target must be an enclosing ", (isContinue ? "loop: " : "control structure: "),
258 stmt->originalTarget ) );
[491bb81]259 }
260 break;
261 }
262 // handle fallthrough in case/switch stmts
263 case BranchStmt::FallThrough: {
264 targetEntry = findEnclosingControlStructure( isFallthroughTarget );
265 // Check that target is valid.
266 if ( targetEntry == enclosing_control_structures.rend() ) {
267 SemanticError( stmt->location, "'fallthrough' must be enclosed in a 'switch' or 'choose'" );
268 }
269 if ( ! stmt->target.empty() ) {
270 // Labelled fallthrough: target must be a valid fallthough label.
271 if ( ! fallthrough_labels.count( stmt->target ) ) {
272 SemanticError( stmt->location, toString( "'fallthrough' target must be a later case statement: ",
[66daee4]273 stmt->originalTarget ) );
[491bb81]274 }
275 return new BranchStmt( stmt->location, BranchStmt::Goto, stmt->originalTarget );
276 }
277 break;
278 }
279 case BranchStmt::FallThroughDefault: {
280 targetEntry = findEnclosingControlStructure( isFallthroughDefaultTarget );
281
282 // Check if in switch or choose statement.
283 if ( targetEntry == enclosing_control_structures.rend() ) {
284 SemanticError( stmt->location, "'fallthrough' must be enclosed in a 'switch' or 'choose'" );
285 }
286
287 // Check if switch or choose has default clause.
288 auto switchStmt = strict_dynamic_cast< const SwitchStmt * >( targetEntry->stmt );
289 bool foundDefault = false;
290 for ( auto caseStmt : switchStmt->cases ) {
291 if ( caseStmt->isDefault() ) {
292 foundDefault = true;
293 break;
294 }
295 }
296 if ( ! foundDefault ) {
297 SemanticError( stmt->location, "'fallthrough default' must be enclosed in a 'switch' or 'choose'"
298 "control structure with a 'default' clause" );
299 }
300 break;
301 }
302 default:
[b8ab91a]303 assert( false );
304 }
305
[2f52b18]306 // Branch error checks: get the appropriate label name, which is always replaced.
[66daee4]307 Label exitLabel( CodeLocation(), "" );
[b8ab91a]308 switch ( stmt->kind ) {
[491bb81]309 case BranchStmt::Break:
[2f52b18]310 assert( ! targetEntry->useBreakExit().empty() );
[b8ab91a]311 exitLabel = targetEntry->useBreakExit();
312 break;
[491bb81]313 case BranchStmt::Continue:
[2f52b18]314 assert( ! targetEntry->useContExit().empty() );
[b8ab91a]315 exitLabel = targetEntry->useContExit();
316 break;
[491bb81]317 case BranchStmt::FallThrough:
[2f52b18]318 assert( ! targetEntry->useFallExit().empty() );
[b8ab91a]319 exitLabel = targetEntry->useFallExit();
320 break;
[491bb81]321 case BranchStmt::FallThroughDefault:
[2f52b18]322 assert( ! targetEntry->useFallDefaultExit().empty() );
[b8ab91a]323 exitLabel = targetEntry->useFallDefaultExit();
324 // Check that fallthrough default comes before the default clause.
[2f52b18]325 if ( ! targetEntry->isFallDefaultValid() ) {
[66daee4]326 SemanticError( stmt->location, "'fallthrough default' must precede the 'default' clause" );
[b8ab91a]327 }
328 break;
[491bb81]329 default:
[b8ab91a]330 assert(0);
331 }
332
333 // Add unused attribute to silence warnings.
334 targetEntry->stmt = addUnused( targetEntry->stmt, stmt->originalTarget );
335
[66daee4]336 // Replace with goto to make later passes more uniform.
337 return new BranchStmt( stmt->location, BranchStmt::Goto, exitLabel );
[b8ab91a]338}
339
[3b0bc16]340void MultiLevelExitCore::previsit( const WhileDoStmt * stmt ) {
[b8ab91a]341 return prehandleLoopStmt( stmt );
342}
343
[3b0bc16]344const WhileDoStmt * MultiLevelExitCore::postvisit( const WhileDoStmt * stmt ) {
[b8ab91a]345 return posthandleLoopStmt( stmt );
346}
347
[66daee4]348void MultiLevelExitCore::previsit( const ForStmt * stmt ) {
[b8ab91a]349 return prehandleLoopStmt( stmt );
350}
351
[66daee4]352const ForStmt * MultiLevelExitCore::postvisit( const ForStmt * stmt ) {
[b8ab91a]353 return posthandleLoopStmt( stmt );
354}
355
356// Mimic what the built-in push_front would do anyways. It is O(n).
[0577df2]357void push_front( vector<ptr<Stmt>> & vec, const Stmt * element ) {
[b8ab91a]358 vec.emplace_back( nullptr );
359 for ( size_t i = vec.size() - 1 ; 0 < i ; --i ) {
[0bd46fd]360 vec[ i ] = std::move( vec[ i - 1 ] );
[b8ab91a]361 }
362 vec[ 0 ] = element;
363}
364
[400b8be]365const CaseClause * MultiLevelExitCore::previsit( const CaseClause * stmt ) {
[b8ab91a]366 visit_children = false;
367
[66daee4]368 // If default, mark seen.
[b8ab91a]369 if ( stmt->isDefault() ) {
[2f52b18]370 assert( ! enclosing_control_structures.empty() );
[b8ab91a]371 enclosing_control_structures.back().seenDefault();
372 }
373
374 // The cond may not exist, but if it does update it now.
[400b8be]375 visitor->maybe_accept( stmt, &CaseClause::cond );
[b8ab91a]376
377 // Just save the mutated node for simplicity.
[400b8be]378 CaseClause * mutStmt = mutate( stmt );
[b8ab91a]379
[400b8be]380 Label fallLabel = newLabel( "fallThrough", stmt->location );
[66daee4]381 if ( ! mutStmt->stmts.empty() ) {
[400b8be]382 // These should already be in a block.
383 auto first = mutStmt->stmts.front().get_and_mutate();
384 auto block = strict_dynamic_cast<CompoundStmt *>( first );
385
[b8ab91a]386 // Ensure that the stack isn't corrupted by exceptions in fixBlock.
387 auto guard = makeFuncGuard(
[400b8be]388 [&](){ enclosing_control_structures.emplace_back( mutStmt, block, fallLabel ); },
[b8ab91a]389 [this](){ enclosing_control_structures.pop_back(); }
[66daee4]390 );
[b8ab91a]391
392 block->kids = fixBlock( block->kids, true );
393
394 // Add fallthrough label if necessary.
[66daee4]395 assert( ! enclosing_control_structures.empty() );
[b8ab91a]396 Entry & entry = enclosing_control_structures.back();
397 if ( entry.isFallUsed() ) {
[400b8be]398 mutStmt->stmts.push_back( labelledNullStmt( block->location, entry.useFallExit() ) );
[b8ab91a]399 }
400 }
[66daee4]401 assert( ! enclosing_control_structures.empty() );
[b8ab91a]402 Entry & entry = enclosing_control_structures.back();
[66daee4]403 assertf( dynamic_cast< const SwitchStmt * >( entry.stmt ),
[6180274]404 "CFA internal error: control structure enclosing a case clause must be a switch, but is: %s",
[66daee4]405 toString( entry.stmt ).c_str() );
[b8ab91a]406 if ( mutStmt->isDefault() ) {
407 if ( entry.isFallDefaultUsed() ) {
408 // Add fallthrough default label if necessary.
[2f52b18]409 push_front( mutStmt->stmts, labelledNullStmt( stmt->location, entry.useFallDefaultExit() ) );
[b8ab91a]410 }
411 }
412 return mutStmt;
413}
414
[66daee4]415void MultiLevelExitCore::previsit( const IfStmt * stmt ) {
[2f52b18]416 bool labeledBlock = ! stmt->labels.empty();
[b8ab91a]417 if ( labeledBlock ) {
[66daee4]418 Label breakLabel = newLabel( "blockBreak", stmt );
[b8ab91a]419 enclosing_control_structures.emplace_back( stmt, breakLabel );
420 GuardAction( [this](){ enclosing_control_structures.pop_back(); } );
421 }
422}
423
[66daee4]424const IfStmt * MultiLevelExitCore::postvisit( const IfStmt * stmt ) {
[2f52b18]425 bool labeledBlock = ! stmt->labels.empty();
[b8ab91a]426 if ( labeledBlock ) {
427 auto this_label = enclosing_control_structures.back().useBreakExit();
[2f52b18]428 if ( ! this_label.empty() ) {
[b8ab91a]429 break_label = this_label;
430 }
431 }
432 return stmt;
433}
434
[400b8be]435static bool isDefaultCase( const ptr<CaseClause> & caseClause ) {
436 return caseClause->isDefault();
[b8ab91a]437}
438
[66daee4]439void MultiLevelExitCore::previsit( const SwitchStmt * stmt ) {
440 Label label = newLabel( "switchBreak", stmt );
[400b8be]441 auto it = find_if( stmt->cases.rbegin(), stmt->cases.rend(), isDefaultCase );
[b8ab91a]442
[400b8be]443 const CaseClause * defaultCase = it != stmt->cases.rend() ? (*it) : nullptr;
444 Label defaultLabel = defaultCase ? newLabel( "fallThroughDefault", defaultCase->location ) : Label( stmt->location, "" );
[b8ab91a]445 enclosing_control_structures.emplace_back( stmt, label, defaultLabel );
446 GuardAction( [this]() { enclosing_control_structures.pop_back(); } );
447
[2f52b18]448 // Collect valid labels for fallthrough. It starts with all labels at this level, then remove as each is seen during
449 // traversal.
[400b8be]450 for ( const CaseClause * caseStmt : stmt->cases ) {
[b8ab91a]451 if ( caseStmt->stmts.empty() ) continue;
[66daee4]452 auto block = caseStmt->stmts.front().strict_as<CompoundStmt>();
453 for ( const Stmt * stmt : block->kids ) {
454 for ( const Label & l : stmt->labels ) {
[b8ab91a]455 fallthrough_labels.insert( l );
456 }
457 }
458 }
459}
460
[66daee4]461const SwitchStmt * MultiLevelExitCore::postvisit( const SwitchStmt * stmt ) {
[2f52b18]462 assert( ! enclosing_control_structures.empty() );
[b8ab91a]463 Entry & entry = enclosing_control_structures.back();
464 assert( entry.stmt == stmt );
465
[66daee4]466 // Only run to generate the break label.
[b8ab91a]467 if ( entry.isBreakUsed() ) {
[2f52b18]468 // To keep the switch statements uniform (all direct children of a SwitchStmt should be CastStmts), append the
469 // exit label and break to the last case, create a default case if no cases.
[66daee4]470 SwitchStmt * mutStmt = mutate( stmt );
[400b8be]471 if ( mutStmt->cases.empty() ) {
472 mutStmt->cases.push_back( new CaseClause( mutStmt->location, nullptr, {} ) );
[b8ab91a]473 }
474
[400b8be]475 auto caseStmt = mutStmt->cases.back().get();
[66daee4]476 auto mutCase = mutate( caseStmt );
[400b8be]477 mutStmt->cases.back() = mutCase;
[b8ab91a]478
[66daee4]479 Label label( mutCase->location, "breakLabel" );
480 auto branch = new BranchStmt( mutCase->location, BranchStmt::Break, label );
[b8ab91a]481 branch->labels.push_back( entry.useBreakExit() );
482 mutCase->stmts.push_back( branch );
483
484 return mutStmt;
485 }
486 return stmt;
487}
488
[66daee4]489void MultiLevelExitCore::previsit( const ReturnStmt * stmt ) {
[b8ab91a]490 if ( inFinally ) {
491 SemanticError( stmt->location, "'return' may not appear in a finally clause" );
492 }
493}
494
[66daee4]495void MultiLevelExitCore::previsit( const TryStmt * stmt ) {
[2f52b18]496 bool isLabeled = ! stmt->labels.empty();
[b8ab91a]497 if ( isLabeled ) {
[66daee4]498 Label breakLabel = newLabel( "blockBreak", stmt );
[b8ab91a]499 enclosing_control_structures.emplace_back( stmt, breakLabel );
500 GuardAction([this](){ enclosing_control_structures.pop_back(); } );
501 }
502}
503
[66daee4]504void MultiLevelExitCore::postvisit( const TryStmt * stmt ) {
[2f52b18]505 bool isLabeled = ! stmt->labels.empty();
[b8ab91a]506 if ( isLabeled ) {
507 auto this_label = enclosing_control_structures.back().useBreakExit();
[2f52b18]508 if ( ! this_label.empty() ) {
[b8ab91a]509 break_label = this_label;
510 }
511 }
512}
513
[400b8be]514void MultiLevelExitCore::previsit( const FinallyClause * ) {
[0bd46fd]515 GuardAction([this, old = std::move( enclosing_control_structures)](){ enclosing_control_structures = std::move(old); });
[66daee4]516 enclosing_control_structures = vector<Entry>();
[b8ab91a]517 GuardValue( inFinally ) = true;
518}
519
[66daee4]520const Stmt * MultiLevelExitCore::mutateLoop(
521 const Stmt * body, Entry & entry ) {
[b8ab91a]522 if ( entry.isBreakUsed() ) {
523 break_label = entry.useBreakExit();
524 }
525
[3e5db5b4]526 // if continue is used insert a continue label into the back of the body of the loop
[b8ab91a]527 if ( entry.isContUsed() ) {
[3e5db5b4]528 // {
529 // body
[4a40fca7]530 // ContinueLabel: ;
[3e5db5b4]531 // }
[4a40fca7]532 return new CompoundStmt( body->location, {
533 body,
534 labelledNullStmt( body->location, entry.useContExit() ),
535 } );
[b8ab91a]536 }
537
538 return body;
539}
540
541template<typename LoopNode>
542void MultiLevelExitCore::prehandleLoopStmt( const LoopNode * loopStmt ) {
543 // Remember is loop before going onto mutate the body.
544 // The labels will be folded in if they are used.
[66daee4]545 Label breakLabel = newLabel( "loopBreak", loopStmt );
546 Label contLabel = newLabel( "loopContinue", loopStmt );
[b8ab91a]547 enclosing_control_structures.emplace_back( loopStmt, breakLabel, contLabel );
[3e5db5b4]548 // labels are added temporarily to see if they are used and then added permanently in postvisit if ther are used
549 // children will tag labels as being used during their traversal which occurs before postvisit
550
551 // GuardAction calls the lambda after the node is done being visited
[b8ab91a]552 GuardAction( [this](){ enclosing_control_structures.pop_back(); } );
553}
554
555template<typename LoopNode>
556const LoopNode * MultiLevelExitCore::posthandleLoopStmt( const LoopNode * loopStmt ) {
[2f52b18]557 assert( ! enclosing_control_structures.empty() );
[b8ab91a]558 Entry & entry = enclosing_control_structures.back();
559 assert( entry.stmt == loopStmt );
560
[66daee4]561 // Now check if the labels are used and add them if so.
[2f52b18]562 return mutate_field( loopStmt, &LoopNode::body, mutateLoop( loopStmt->body, entry ) );
[3e5db5b4]563 // this call to mutate_field compares loopStmt->body and the result of mutateLoop
564 // if they are the same the node isn't mutated, if they differ then the new mutated node is returned
565 // the stmts will only differ if a label is used
[b8ab91a]566}
567
[66daee4]568list<ptr<Stmt>> MultiLevelExitCore::fixBlock(
569 const list<ptr<Stmt>> & kids, bool is_case_clause ) {
570 // Unfortunately cannot use automatic error collection.
[b8ab91a]571 SemanticErrorException errors;
572
[66daee4]573 list<ptr<Stmt>> ret;
[b8ab91a]574
575 // Manually visit each child.
[66daee4]576 for ( const ptr<Stmt> & kid : kids ) {
[b8ab91a]577 if ( is_case_clause ) {
578 // Once a label is seen, it's no longer a valid for fallthrough.
[66daee4]579 for ( const Label & l : kid->labels ) {
[b8ab91a]580 fallthrough_labels.erase( l );
581 }
582 }
583
[f75e25b]584 ptr<Stmt> else_stmt = nullptr;
[0577df2]585 const Stmt * loop_kid = nullptr;
[f75e25b]586 // check if loop node and if so add else clause if it exists
[0577df2]587 const WhileDoStmt * whilePtr = kid.as<WhileDoStmt>();
588 if ( whilePtr && whilePtr->else_ ) {
[f75e25b]589 else_stmt = whilePtr->else_;
[0577df2]590 loop_kid = mutate_field( whilePtr, &WhileDoStmt::else_, nullptr );
[f75e25b]591 }
[0577df2]592 const ForStmt * forPtr = kid.as<ForStmt>();
593 if ( forPtr && forPtr->else_ ) {
[f75e25b]594 else_stmt = forPtr->else_;
[0577df2]595 loop_kid = mutate_field( forPtr, &ForStmt::else_, nullptr );
[f75e25b]596 }
597
[b8ab91a]598 try {
[f75e25b]599 if (else_stmt) ret.push_back( loop_kid->accept( *visitor ) );
600 else ret.push_back( kid->accept( *visitor ) );
[b8ab91a]601 } catch ( SemanticErrorException & e ) {
602 errors.append( e );
603 }
604
[f75e25b]605 if (else_stmt) ret.push_back(else_stmt);
[7ad47df]606
[2f52b18]607 if ( ! break_label.empty() ) {
608 ret.push_back( labelledNullStmt( ret.back()->location, break_label ) );
[66daee4]609 break_label = Label( CodeLocation(), "" );
[b8ab91a]610 }
611 }
612
[2f52b18]613 if ( ! errors.isEmpty() ) {
[b8ab91a]614 throw errors;
615 }
616 return ret;
617}
618
[4a40fca7]619} // namespace
620
[66daee4]621const CompoundStmt * multiLevelExitUpdate(
[4a40fca7]622 const CompoundStmt * stmt, const LabelToStmt & labelTable ) {
[b8ab91a]623 // Must start in the body, so FunctionDecls can be a stopping point.
[66daee4]624 Pass<MultiLevelExitCore> visitor( labelTable );
[4a40fca7]625 return stmt->accept( visitor );
[b8ab91a]626}
[4a40fca7]627
[b8ab91a]628} // namespace ControlStruct
629
630// Local Variables: //
631// tab-width: 4 //
632// mode: c++ //
633// compile-command: "make install" //
634// End: //
Note: See TracBrowser for help on using the repository browser.