source: src/Parser/StatementNode.cc @ 4c0b674

Last change on this file since 4c0b674 was 3d9d017, checked in by caparson <caparson@…>, 7 months ago

added cofor implementation

  • Property mode set to 100644
File size: 18.7 KB
Line 
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//
7// StatementNode.cc -- Transform from parse data-structures to AST data-structures, usually deleting the parse
8//     data-structure after the transformation.
9//
10// Author           : Rodolfo G. Esteves
11// Created On       : Sat May 16 14:59:41 2015
12// Last Modified By : Peter A. Buhr
13// Last Modified On : Fri Aug 11 11:44:15 2023
14// Update Count     : 429
15//
16
17#include "StatementNode.h"
18
19#include <cassert>                 // for assert, strict_dynamic_cast, assertf
20#include <memory>                  // for unique_ptr
21#include <string>                  // for string
22
23#include "AST/Label.hpp"           // for Label
24#include "AST/Stmt.hpp"            // for Stmt, AsmStmt, BranchStmt, CaseCla...
25#include "Common/SemanticError.h"  // for SemanticError
26#include "Common/utility.h"        // for maybeMoveBuild, maybeBuild
27#include "DeclarationNode.h"       // for DeclarationNode
28#include "ExpressionNode.h"        // for ExpressionNode
29#include "parserutility.h"         // for notZeroExpr
30
31class Declaration;
32
33using namespace std;
34
35// Some helpers for cases that really want a single node but check for lists.
36static const ast::Stmt * buildMoveSingle( StatementNode * node ) {
37        std::vector<ast::ptr<ast::Stmt>> list;
38        buildMoveList( node, list );
39        assertf( list.size() == 1, "CFA Internal Error: Extra/Missing Nodes" );
40        return list.front().release();
41}
42
43static const ast::Stmt * buildMoveOptional( StatementNode * node ) {
44        std::vector<ast::ptr<ast::Stmt>> list;
45        buildMoveList( node, list );
46        assertf( list.size() <= 1, "CFA Internal Error: Extra Nodes" );
47        return list.empty() ? nullptr : list.front().release();
48}
49
50StatementNode::StatementNode( DeclarationNode * decl ) {
51        assert( decl );
52        DeclarationNode * agg = decl->extractAggregate();
53        if ( agg ) {
54                StatementNode * nextStmt = new StatementNode(
55                        new ast::DeclStmt( decl->location, maybeBuild( decl ) ) );
56                set_next( nextStmt );
57                if ( decl->get_next() ) {
58                        get_next()->set_next( new StatementNode( dynamic_cast< DeclarationNode * >(decl->get_next()) ) );
59                        decl->set_next( 0 );
60                } // if
61        } else {
62                if ( decl->get_next() ) {
63                        set_next( new StatementNode( dynamic_cast< DeclarationNode * >( decl->get_next() ) ) );
64                        decl->set_next( 0 );
65                } // if
66                agg = decl;
67        } // if
68        // Local copy to avoid accessing the pointer after it is moved from.
69        CodeLocation declLocation = agg->location;
70        stmt.reset( new ast::DeclStmt( declLocation, maybeMoveBuild( agg ) ) );
71} // StatementNode::StatementNode
72
73StatementNode * StatementNode::add_label(
74                const CodeLocation & location,
75                const std::string * name,
76                DeclarationNode * attr ) {
77        stmt->labels.emplace_back( location,
78                *name,
79                attr ? std::move( attr->attributes )
80                        : std::vector<ast::ptr<ast::Attribute>>{} );
81        delete attr;
82        delete name;
83        return this;
84}
85
86ClauseNode * ClauseNode::append_last_case( StatementNode * stmt ) {
87        ClauseNode * prev = this;
88        // find end of list and maintain previous pointer
89        for ( ClauseNode * curr = prev; curr != nullptr; curr = (ClauseNode *)curr->get_next() ) {
90                ClauseNode * node = strict_dynamic_cast< ClauseNode * >(curr);
91                assert( dynamic_cast<ast::CaseClause *>( node->clause.get() ) );
92                prev = curr;
93        } // for
94        ClauseNode * node = dynamic_cast< ClauseNode * >(prev);
95        // convert from StatementNode list to Statement list
96        std::vector<ast::ptr<ast::Stmt>> stmts;
97        buildMoveList( stmt, stmts );
98        // splice any new Statements to end of current Statements
99        auto caseStmt = strict_dynamic_cast<ast::CaseClause *>( node->clause.get() );
100        for ( auto const & newStmt : stmts ) {
101                caseStmt->stmts.emplace_back( newStmt );
102        }
103        stmts.clear();
104        return this;
105} // ClauseNode::append_last_case
106
107ast::Stmt * build_expr( CodeLocation const & location, ExpressionNode * ctl ) {
108        if ( ast::Expr * e = maybeMoveBuild( ctl ) ) {
109                return new ast::ExprStmt( location, e );
110        } else {
111                return new ast::NullStmt( location );
112        }
113} // build_expr
114
115static ast::Expr * build_if_control( CondCtl * ctl,
116                std::vector<ast::ptr<ast::Stmt>> & inits ) {
117        assert( inits.empty() );
118        if ( nullptr != ctl->init ) {
119                buildMoveList( ctl->init, inits );
120        } // if
121
122        ast::Expr * cond = nullptr;
123        if ( ctl->condition ) {
124                // compare the provided condition against 0
125                cond = notZeroExpr( maybeMoveBuild( ctl->condition ) );
126        } else {
127                for ( ast::ptr<ast::Stmt> & stmt : inits ) {
128                        // build the && of all of the declared variables compared against 0
129                        auto declStmt = stmt.strict_as<ast::DeclStmt>();
130                        auto dwt = declStmt->decl.strict_as<ast::DeclWithType>();
131                        ast::Expr * nze = notZeroExpr( new ast::VariableExpr( dwt->location, dwt ) );
132                        cond = cond ? new ast::LogicalExpr( dwt->location, cond, nze, ast::AndExpr ) : nze;
133                }
134        }
135        delete ctl;
136        return cond;
137} // build_if_control
138
139ast::Stmt * build_if( const CodeLocation & location, CondCtl * ctl, StatementNode * then, StatementNode * else_ ) {
140        std::vector<ast::ptr<ast::Stmt>> astinit;                                               // maybe empty
141        ast::Expr * astcond = build_if_control( ctl, astinit ); // ctl deleted, cond/init set
142
143        ast::Stmt const * astthen = buildMoveSingle( then );
144        ast::Stmt const * astelse = buildMoveOptional( else_ );
145
146        return new ast::IfStmt( location, astcond, astthen, astelse,
147                std::move( astinit )
148        );
149} // build_if
150
151ast::Stmt * build_switch( const CodeLocation & location, bool isSwitch, ExpressionNode * ctl, ClauseNode * stmt ) {
152        std::vector<ast::ptr<ast::CaseClause>> aststmt;
153        buildMoveList( stmt, aststmt );
154        // If it is not a switch it is a choose statement.
155        if ( ! isSwitch ) {
156                for ( ast::ptr<ast::CaseClause> & stmt : aststmt ) {
157                        // Code after "case" is the end of case list.
158                        if ( !stmt->stmts.empty() ) {
159                                auto mutStmt = ast::mutate( stmt.get() );
160                                // I believe the stmts are actually always one block.
161                                auto stmts = mutStmt->stmts.front().get_and_mutate();
162                                auto block = strict_dynamic_cast<ast::CompoundStmt *>( stmts );
163                                block->kids.push_back( new ast::BranchStmt( block->location,
164                                        ast::BranchStmt::Break,
165                                        ast::Label( block->location ) ) );
166                                stmt = mutStmt;
167                        } // if
168                } // for
169        } // if
170        // aststmt.size() == 0 for switch (...) {}, i.e., no declaration or statements
171        return new ast::SwitchStmt( location,
172                maybeMoveBuild( ctl ), std::move( aststmt ) );
173} // build_switch
174
175ast::CaseClause * build_case( const CodeLocation & location, ExpressionNode * ctl ) {
176        // stmt starts empty and then added to
177        auto expr = maybeMoveBuild( ctl );
178        return new ast::CaseClause( location, expr, {} );
179} // build_case
180
181ast::CaseClause * build_default( const CodeLocation & location ) {
182        // stmt starts empty and then added to
183        return new ast::CaseClause( location, nullptr, {} );
184} // build_default
185
186ast::Stmt * build_while( const CodeLocation & location, CondCtl * ctl, StatementNode * stmt, StatementNode * else_ ) {
187        std::vector<ast::ptr<ast::Stmt>> astinit;                                               // maybe empty
188        ast::Expr * astcond = build_if_control( ctl, astinit ); // ctl deleted, cond/init set
189
190        return new ast::WhileDoStmt( location,
191                astcond,
192                buildMoveSingle( stmt ),
193                buildMoveOptional( else_ ),
194                std::move( astinit ),
195                ast::While
196        );
197} // build_while
198
199ast::Stmt * build_do_while( const CodeLocation & location, ExpressionNode * ctl, StatementNode * stmt, StatementNode * else_ ) {
200        // do-while cannot have declarations in the contitional, so init is always empty
201        return new ast::WhileDoStmt( location,
202                notZeroExpr( maybeMoveBuild( ctl ) ),
203                buildMoveSingle( stmt ),
204                buildMoveOptional( else_ ),
205                {},
206                ast::DoWhile
207        );
208} // build_do_while
209
210ast::Stmt * build_for( const CodeLocation & location, ForCtrl * forctl, StatementNode * stmt, StatementNode * else_ ) {
211        std::vector<ast::ptr<ast::Stmt>> astinit;                                               // maybe empty
212        buildMoveList( forctl->init, astinit );
213
214        ast::Expr * astcond = nullptr;                                          // maybe empty
215        astcond = notZeroExpr( maybeMoveBuild( forctl->condition ) );
216
217        ast::Expr * astincr = nullptr;                                          // maybe empty
218        astincr = maybeMoveBuild( forctl->change );
219        delete forctl;
220
221        return new ast::ForStmt( location,
222                std::move( astinit ),
223                astcond,
224                astincr,
225                buildMoveSingle( stmt ),
226                buildMoveOptional( else_ )
227        );
228} // build_for
229
230ast::Stmt * build_branch( const CodeLocation & location, ast::BranchStmt::Kind kind ) {
231        return new ast::BranchStmt( location,
232                kind,
233                ast::Label( location )
234        );
235} // build_branch
236
237ast::Stmt * build_branch( const CodeLocation & location, string * identifier, ast::BranchStmt::Kind kind ) {
238        ast::Stmt * ret = new ast::BranchStmt( location,
239                kind,
240                ast::Label( location, *identifier )
241        );
242        delete identifier;                                                                      // allocated by lexer
243        return ret;
244} // build_branch
245
246ast::Stmt * build_computedgoto( ExpressionNode * ctl ) {
247        ast::Expr * expr = maybeMoveBuild( ctl );
248        return new ast::BranchStmt( expr->location, expr );
249} // build_computedgoto
250
251ast::Stmt * build_return( const CodeLocation & location, ExpressionNode * ctl ) {
252        std::vector<ast::ptr<ast::Expr>> exps;
253        buildMoveList( ctl, exps );
254        return new ast::ReturnStmt( location,
255                exps.size() > 0 ? exps.back().release() : nullptr
256        );
257} // build_return
258
259static ast::Stmt * build_throw_stmt(
260                const CodeLocation & location,
261                ExpressionNode * ctl,
262                ast::ExceptionKind kind ) {
263        std::vector<ast::ptr<ast::Expr>> exps;
264        buildMoveList( ctl, exps );
265        assertf( exps.size() < 2, "CFA internal error: leaking memory" );
266        return new ast::ThrowStmt( location,
267                kind,
268                !exps.empty() ? exps.back().release() : nullptr,
269                (ast::Expr *)nullptr
270        );
271}
272
273ast::Stmt * build_throw( const CodeLocation & loc, ExpressionNode * ctl ) {
274        return build_throw_stmt( loc, ctl, ast::Terminate );
275} // build_throw
276
277ast::Stmt * build_resume( const CodeLocation & loc, ExpressionNode * ctl ) {
278        return build_throw_stmt( loc, ctl, ast::Resume );
279} // build_resume
280
281ast::Stmt * build_resume_at( ExpressionNode * ctl, ExpressionNode * target ) {
282        (void)ctl;
283        (void)target;
284        assertf( false, "resume at (non-local throw) is not yet supported," );
285} // build_resume_at
286
287ast::Stmt * build_try( const CodeLocation & location, StatementNode * try_, ClauseNode * catch_, ClauseNode * finally_ ) {
288        std::vector<ast::ptr<ast::CatchClause>> aststmt;
289        buildMoveList( catch_, aststmt );
290        ast::CompoundStmt * tryBlock = strict_dynamic_cast<ast::CompoundStmt *>( maybeMoveBuild( try_ ) );
291        ast::FinallyClause * finallyBlock = nullptr;
292        if ( finally_ ) {
293                finallyBlock = dynamic_cast<ast::FinallyClause *>( finally_->clause.release() );
294        }
295        return new ast::TryStmt( location,
296                tryBlock,
297                std::move( aststmt ),
298                finallyBlock
299        );
300} // build_try
301
302ast::CatchClause * build_catch( const CodeLocation & location, ast::ExceptionKind kind, DeclarationNode * decl, ExpressionNode * cond, StatementNode * body ) {
303        return new ast::CatchClause( location,
304                kind,
305                maybeMoveBuild( decl ),
306                maybeMoveBuild( cond ),
307                buildMoveSingle( body )
308        );
309} // build_catch
310
311ast::FinallyClause * build_finally( const CodeLocation & location, StatementNode * stmt ) {
312        return new ast::FinallyClause( location,
313                strict_dynamic_cast<const ast::CompoundStmt *>(
314                        buildMoveSingle( stmt )
315                )
316        );
317} // build_finally
318
319ast::SuspendStmt * build_suspend( const CodeLocation & location, StatementNode * then, ast::SuspendStmt::Kind kind ) {
320        return new ast::SuspendStmt( location,
321                strict_dynamic_cast<const ast::CompoundStmt *, nullptr>(
322                        buildMoveOptional( then )
323                ),
324                kind
325        );
326} // build_suspend
327
328ast::WaitForStmt * build_waitfor( const CodeLocation & location, ast::WaitForStmt * existing, ExpressionNode * when, ExpressionNode * targetExpr, StatementNode * stmt ) {
329        auto clause = new ast::WaitForClause( location );
330        clause->target = maybeBuild( targetExpr );
331        clause->stmt = maybeMoveBuild( stmt );
332        clause->when_cond = notZeroExpr( maybeMoveBuild( when ) );
333
334        ExpressionNode * next = dynamic_cast<ExpressionNode *>( targetExpr->get_next() );
335        targetExpr->set_next( nullptr );
336        buildMoveList( next, clause->target_args );
337
338        delete targetExpr;
339
340        existing->clauses.insert( existing->clauses.begin(), clause );
341
342        return existing;
343} // build_waitfor
344
345ast::WaitForStmt * build_waitfor_else( const CodeLocation & location, ast::WaitForStmt * existing, ExpressionNode * when, StatementNode * stmt ) {
346        existing->else_stmt = maybeMoveBuild( stmt );
347        existing->else_cond = notZeroExpr( maybeMoveBuild( when ) );
348
349        (void)location;
350        return existing;
351} // build_waitfor_else
352
353ast::WaitForStmt * build_waitfor_timeout( const CodeLocation & location, ast::WaitForStmt * existing, ExpressionNode * when, ExpressionNode * timeout, StatementNode * stmt ) {
354        existing->timeout_time = maybeMoveBuild( timeout );
355        existing->timeout_stmt = maybeMoveBuild( stmt );
356        existing->timeout_cond = notZeroExpr( maybeMoveBuild( when ) );
357
358        (void)location;
359        return existing;
360} // build_waitfor_timeout
361
362ast::WaitUntilStmt::ClauseNode * build_waituntil_clause( const CodeLocation & loc, ExpressionNode * when, ExpressionNode * targetExpr, StatementNode * stmt ) {
363        ast::WhenClause * clause = new ast::WhenClause( loc );
364        clause->when_cond = notZeroExpr( maybeMoveBuild( when ) );
365        clause->stmt = maybeMoveBuild( stmt );
366        clause->target = maybeMoveBuild( targetExpr );
367        return new ast::WaitUntilStmt::ClauseNode( clause );
368}
369ast::WaitUntilStmt::ClauseNode * build_waituntil_else( const CodeLocation & loc, ExpressionNode * when, StatementNode * stmt ) {
370        ast::WhenClause * clause = new ast::WhenClause( loc );
371        clause->when_cond = notZeroExpr( maybeMoveBuild( when ) );
372        clause->stmt = maybeMoveBuild( stmt );
373        return new ast::WaitUntilStmt::ClauseNode( ast::WaitUntilStmt::ClauseNode::Op::ELSE, clause );
374}
375
376ast::WaitUntilStmt * build_waituntil_stmt( const CodeLocation & loc, ast::WaitUntilStmt::ClauseNode * root ) {
377        ast::WaitUntilStmt * retStmt = new ast::WaitUntilStmt( loc );
378        retStmt->predicateTree = root;
379
380        // iterative tree traversal
381        std::vector<ast::WaitUntilStmt::ClauseNode *> nodeStack; // stack needed for iterative traversal
382        ast::WaitUntilStmt::ClauseNode * currNode = nullptr;
383        ast::WaitUntilStmt::ClauseNode * lastInternalNode = nullptr;
384        ast::WaitUntilStmt::ClauseNode * cleanup = nullptr; // used to cleanup removed else/timeout
385        nodeStack.push_back(root);
386
387        do {
388                currNode = nodeStack.back();
389                nodeStack.pop_back(); // remove node since it will be processed
390
391                switch (currNode->op) {
392                case ast::WaitUntilStmt::ClauseNode::LEAF:
393                        retStmt->clauses.push_back(currNode->leaf);
394                        break;
395                case ast::WaitUntilStmt::ClauseNode::ELSE:
396                        retStmt->else_stmt = currNode->leaf->stmt
397                                ? ast::deepCopy( currNode->leaf->stmt )
398                                : nullptr;
399                        retStmt->else_cond = currNode->leaf->when_cond
400                                ? ast::deepCopy( currNode->leaf->when_cond )
401                                : nullptr;
402
403                        delete currNode->leaf;
404                        break;
405                case ast::WaitUntilStmt::ClauseNode::TIMEOUT:
406                        retStmt->timeout_time = currNode->leaf->target
407                                ? ast::deepCopy( currNode->leaf->target )
408                                : nullptr;
409                        retStmt->timeout_stmt = currNode->leaf->stmt
410                                ? ast::deepCopy( currNode->leaf->stmt )
411                                : nullptr;
412                        retStmt->timeout_cond = currNode->leaf->when_cond
413                                ? ast::deepCopy( currNode->leaf->when_cond )
414                                : nullptr;
415
416                        delete currNode->leaf;
417                        break;
418                default:
419                        nodeStack.push_back( currNode->right ); // process right after left
420                        nodeStack.push_back( currNode->left );
421
422                        // Cut else/timeout out of the tree
423                        if ( currNode->op == ast::WaitUntilStmt::ClauseNode::LEFT_OR ) {
424                                if ( lastInternalNode )
425                                        lastInternalNode->right = currNode->left;
426                                else // if not set then root is LEFT_OR
427                                        retStmt->predicateTree = currNode->left;
428
429                                currNode->left = nullptr;
430                                cleanup = currNode;
431                        }
432
433                        lastInternalNode = currNode;
434                        break;
435                }
436        } while ( !nodeStack.empty() );
437
438        if ( cleanup ) delete cleanup;
439
440        return retStmt;
441}
442
443ast::Stmt * build_with( const CodeLocation & location, ExpressionNode * exprs, StatementNode * stmt ) {
444        std::vector<ast::ptr<ast::Expr>> e;
445        buildMoveList( exprs, e );
446        ast::Stmt * s = maybeMoveBuild( stmt );
447        return new ast::DeclStmt( location, new ast::WithStmt( location, std::move( e ), s ) );
448} // build_with
449
450ast::Stmt * build_compound( const CodeLocation & location, StatementNode * first ) {
451        auto cs = new ast::CompoundStmt( location );
452        buildMoveList( first, cs->kids );
453        return cs;
454} // build_compound
455
456// A single statement in a control structure is always converted to a compound statement so subsequent generated code
457// can be placed within this compound statement. Otherwise, code generation has to constantly check for a single
458// statement and wrap it into a compound statement to insert additional code. Hence, all control structures have a
459// conical form for code generation.
460StatementNode * maybe_build_compound( const CodeLocation & location, StatementNode * first ) {
461        // Optimization: if the control-structure statement is a compound statement, do not wrap it.
462        // e.g., if (...) {...} do not wrap the existing compound statement.
463        if ( !dynamic_cast<ast::CompoundStmt *>( first->stmt.get() ) ) { // unique_ptr
464                return new StatementNode( build_compound( location, first ) );
465        } // if
466        return first;
467} // maybe_build_compound
468
469// Question
470ast::Stmt * build_asm( const CodeLocation & location, bool is_volatile, ExpressionNode * instruction, ExpressionNode * output, ExpressionNode * input, ExpressionNode * clobber, LabelNode * gotolabels ) {
471        std::vector<ast::ptr<ast::Expr>> out, in;
472        std::vector<ast::ptr<ast::ConstantExpr>> clob;
473
474        buildMoveList( output, out );
475        buildMoveList( input, in );
476        buildMoveList( clobber, clob );
477        return new ast::AsmStmt( location,
478                is_volatile,
479                maybeMoveBuild( instruction ),
480                std::move( out ),
481                std::move( in ),
482                std::move( clob ),
483                gotolabels ? gotolabels->labels : std::vector<ast::Label>()
484        );
485} // build_asm
486
487ast::Stmt * build_directive( const CodeLocation & location, string * directive ) {
488        auto stmt = new ast::DirectiveStmt( location, *directive );
489        delete directive;
490        return stmt;
491} // build_directive
492
493ast::Stmt * build_mutex( const CodeLocation & location, ExpressionNode * exprs, StatementNode * stmt ) {
494        std::vector<ast::ptr<ast::Expr>> expList;
495        buildMoveList( exprs, expList );
496        ast::Stmt * body = maybeMoveBuild( stmt );
497        return new ast::MutexStmt( location, body, std::move( expList ) );
498} // build_mutex
499
500ast::Stmt * build_corun( const CodeLocation & location, StatementNode * stmt ) {
501        ast::Stmt * body = maybeMoveBuild( stmt );
502        return new ast::CorunStmt( location, body );
503} // build_corun
504
505ast::Stmt * build_cofor( const CodeLocation & location, ForCtrl * forctl, StatementNode * stmt ) {
506        std::vector<ast::ptr<ast::Stmt>> astinit;                                               // maybe empty
507        buildMoveList( forctl->init, astinit );
508
509        ast::Expr * astcond = nullptr;                                          // maybe empty
510        astcond = notZeroExpr( maybeMoveBuild( forctl->condition ) );
511
512        ast::Expr * astincr = nullptr;                                          // maybe empty
513        astincr = maybeMoveBuild( forctl->change );
514        delete forctl;
515
516        return new ast::CoforStmt( location,
517                std::move( astinit ),
518                astcond,
519                astincr,
520                buildMoveSingle( stmt )
521        );
522} // build_cofor
523
524// Local Variables: //
525// tab-width: 4 //
526// mode: c++ //
527// compile-command: "make install" //
528// End: //
Note: See TracBrowser for help on using the repository browser.