Skip to content

Commit

Permalink
Add the MainFunction and the Lower_Control_Flow pass.
Browse files Browse the repository at this point in the history
MainFunction moves all global code into one function, __MAIN__.
Lower_Control_Flow lowers ifs and whiles to branch/label instructions.
Foreach is currently unsupported.
  • Loading branch information
criemen committed Apr 6, 2010
1 parent 45783d0 commit aa2ea21
Show file tree
Hide file tree
Showing 13 changed files with 488 additions and 35 deletions.
2 changes: 2 additions & 0 deletions compiler/analysis/CMakeLists.txt
Expand Up @@ -90,6 +90,8 @@ set(PARSER_SRC_FILES
passes/Early_Lower_Loops.cpp
passes/Lower_Binary_Op.cpp
passes/Lower_Conditional_Expr.cpp
passes/Lower_Control_Flow.cpp
passes/MainFunction.cpp
)

# PASSES
Expand Down
9 changes: 9 additions & 0 deletions compiler/analysis/passes/DumpAST.cpp
Expand Up @@ -426,5 +426,14 @@ bool DumpAST::visit_children_formalParam(formalParam* n) {
return true;
}

void DumpAST::visit_pre_label(label* n) {
currentElement_->SetAttribute("id", n->labelNo());
}

void DumpAST::visit_pre_branch(branch* n) {
currentElement_->SetAttribute("trueLabel", n->trueLabel());
currentElement_->SetAttribute("falseLabel", n->falseLabel());
}

} } } // namespace

3 changes: 3 additions & 0 deletions compiler/analysis/passes/Early_Lower_Loops.cpp
Expand Up @@ -46,6 +46,9 @@ namespace rphp { namespace AST { namespace Pass {
* }
*/
stmt* Early_Lower_Loops::transform_post_whileStmt(whileStmt* n) {
if(literalBool* cond = dyn_cast<literalBool>(n->condition()))
if(cond->getBoolVal() == true)
return n;
// break
breakStmt* breakLoop = new (C_) breakStmt(NULL);

Expand Down
162 changes: 162 additions & 0 deletions compiler/analysis/passes/Lower_Control_Flow.cpp
@@ -0,0 +1,162 @@
/* ***** BEGIN LICENSE BLOCK *****
;; Roadsend PHP Compiler
;;
;; Copyright (c) 2010 Cornelius Riemenschneider <c.r1@gmx.de>
;;
;; This program is free software; you can redistribute it and/or
;; modify it under the terms of the GNU General Public License
;; as published by the Free Software Foundation; either version 2
;; of the License, or (at your option) any later version.
;;
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;;
;; You should have received a copy of the GNU General Public License
;; along with this program; if not, write to the Free Software
;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
***** END LICENSE BLOCK *****
*/

#include "rphp/analysis/passes/Lower_Control_Flow.h"
#include "rphp/analysis/pSourceModule.h"

namespace rphp { namespace AST { namespace Pass {

/**
* TODO: foreach lowering, dynamic break/continues.
*/

/**
* Foreach lowering is not implemented yet, thus reject foreachs.
*/
stmt* Lower_Control_Flow::transform_pre_forEach(forEach* n) {
assert(0 && "Foreach lowering is not implemented yet, sorry.");
return n;
}

/* Transform
* if ($x) { y (); }
* else { z (); }
* into
* if ($x) goto L1;
* else goto L2;
* L1:
* y ();
* goto L3;
* L2:
* z ();
* goto L3;
* L3:
*
* We only need two labels, since the first edge can be an implict
* fall-through edge if we negate the condition, but this is more
* readable and understandable, as it keeps the structure of the original
* if-else statement. */
stmt* Lower_Control_Flow::transform_post_ifStmt(ifStmt* n) {
label* L1 = currentFunction_->getNewLabel(C_);
label* L2 = currentFunction_->getNewLabel(C_);
label* L3 = currentFunction_->getNewLabel(C_);

branch* br = new (C_) branch(n->condition()->retain(), L1, L2);
statementList stmts;
stmts.push_back(br);
stmts.push_back(L1);
if(n->trueBlock())
stmts.push_back(n->trueBlock()->retain());
stmts.push_back(new (C_) branch(L3));
stmts.push_back(L2);
if(n->falseBlock())
stmts.push_back(n->falseBlock()->retain());
stmts.push_back(new (C_) branch(L3));
stmts.push_back(L3);

return new (C_) block(C_, &stmts);
}

/**
* Remove Lbreak and Lcontinue from the respective stacks.
*/
stmt* Lower_Control_Flow::transform_post_whileStmt(whileStmt* n) {
breakLabels_.pop_back();
continueLabels_.pop_back();
return n;
}

/* Transform
* while ($x) { y (); }
* into
* Lcontinue:
* y ();
* goto Lcontinue
* Lbreak:
*
* Lcontinue is the label used if you use continue in the loop, Lbreak is the label used if you use break in the loop.
*/
stmt* Lower_Control_Flow::transform_pre_whileStmt(whileStmt* n) {
assert(cast<literalBool>(n->condition())->getBoolVal() == true && "non-lowered while loop found!");

statementList stmts;

// continue label
label* Lcontinue = currentFunction_->getNewLabel(C_);
continueLabels_.push_back(Lcontinue);

stmts.push_back(Lcontinue);
stmts.push_back(n->body()->retain());
stmts.push_back(new (C_) branch(Lcontinue));

label* Lbreak = currentFunction_->getNewLabel(C_);
breakLabels_.push_back(Lbreak);
stmts.push_back(Lbreak);

return new (C_) block(C_, &stmts);
}

/**
* This function lowers an exit (continue/break) from a loop and generates a branch to the appropriate label.
* Currently dynamic continue/breaks aren't supported.
*/
stmt* Lower_Control_Flow::lower_exit(std::vector<label*>& labels, expr* depthExpr) {
pInt depth = -1;
if(depthExpr == NULL)
depth = 0;
else {
//TODO: what's about literalStrings? normally they evaluate to 0, but to what evaluates '1'?
assert(!isa<literalString>(depthExpr));
if(literalInt* depthInt = dyn_cast<literalInt>(depthExpr)) {
// This possibly throws an exception if int>2^31-1, but that's okay, we don't support breaking on that values anyways :D
depth = depthInt->getInt();
//TODO: error/warning on depth < 1
// negative/zero break is always 1
if(depth < 1)
depth = 1;
}
//TODO: Dynamic depth
}
if(depth == 0)
depth = 1;
if(depth != -1)
//TODO: replace by a better error!
assert(depth <= (pInt)labels.size() && "you're trying to break too much!");
assert(depth != -1 && "dynamic depth currently not supported!");
return new (C_) branch(labels[labels.size() - depth]);
}

stmt* Lower_Control_Flow::transform_post_breakStmt(breakStmt* n) {
return lower_exit(breakLabels_, n->rVal());
}

stmt* Lower_Control_Flow::transform_post_continueStmt(continueStmt* n) {
return lower_exit(continueLabels_, n->rVal());
}

// This is needed to get the current function.
stmt* Lower_Control_Flow::transform_pre_functionDecl(functionDecl* n) {
currentFunction_ = n;
return n;
}


} } } // namespace
52 changes: 52 additions & 0 deletions compiler/analysis/passes/MainFunction.cpp
@@ -0,0 +1,52 @@
/* ***** BEGIN LICENSE BLOCK *****
;; Roadsend PHP Compiler
;;
;; Copyright (c) 2010 Cornelius Riemenschneider <c.r1@gmx.de>
;;
;; This program is free software; you can redistribute it and/or
;; modify it under the terms of the GNU General Public License
;; as published by the Free Software Foundation; either version 2
;; of the License, or (at your option) any later version.
;;
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;;
;; You should have received a copy of the GNU General Public License
;; along with this program; if not, write to the Free Software
;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
***** END LICENSE BLOCK *****
*/

#include "rphp/analysis/passes/MainFunction.h"
#include "rphp/analysis/pSourceModule.h"

namespace rphp { namespace AST { namespace Pass {

/**
* Moves all global code except of function and class declarations into a __MAIN__ function.
* __MAIN__ will later hold the global variables as well.
*/
stmt* MainFunction::transform_pre_block(block *n) {
// Just run on the first block.
if(firstBlock)
return n;
firstBlock = true;
statementList mainFunction;
statementList globalScope;
foreach(stmt* child, n->children()) {
if(isa<functionDecl>(child) || isa<classDecl>(child))
globalScope.push_back(child->retain());
else
mainFunction.push_back(child->retain());
}

block* mainFunctionBody = new (C_) block(C_, &mainFunction);
signature* mainFunctionSig = new (C_) signature("__MAIN__", C_, NULL);
functionDecl* mainFunctionDecl = new (C_) functionDecl(mainFunctionSig, mainFunctionBody);
globalScope.push_back(mainFunctionDecl);
return new (C_) block(C_, &globalScope);
}

} } } // namespace
14 changes: 13 additions & 1 deletion frontend/cli/rphp-analyzer.cpp
Expand Up @@ -43,6 +43,8 @@
#include "rphp/analysis/passes/Early_Lower_Loops.h"
#include "rphp/analysis/passes/Lower_Binary_Op.h"
#include "rphp/analysis/passes/Lower_Conditional_Expr.h"
#include "rphp/analysis/passes/Lower_Control_Flow.h"
#include "rphp/analysis/passes/MainFunction.h"


using namespace llvm;
Expand Down Expand Up @@ -104,11 +106,13 @@ int main( int argc, char* argv[] )
else if(runPasses) {
// make all variables etc in strings accessible to passes which need them
passManager.addPass<AST::Pass::SimplifyStrings>();
passManager.addPass<AST::Pass::CheckMemoryManagement>();

// TODO: get variable names here or use variable names which the zend engine
// doesn't allow for internal variables.

//TODO: take care of conditional function definitions.
passManager.addPass<AST::Pass::MainFunction>();

// uses boolean && and ||
passManager.addPass<AST::Pass::Split_Builtins>();
// uses boolean !
Expand All @@ -121,6 +125,8 @@ int main( int argc, char* argv[] )
// do this one late in case other passes are creating return's for whatever reason...
passManager.addPass<AST::Pass::Desugar>();

passManager.addPass<AST::Pass::Lower_Control_Flow>();

// debug output
passManager.addPass<AST::Pass::DumpAST>();
passManager.addPass<AST::Pass::DumpStats>();
Expand Down Expand Up @@ -154,6 +160,12 @@ int main( int argc, char* argv[] )
else if (*i == "desugar") {
passManager.addPass<AST::Pass::Desugar>();
}
else if (*i == "lower-control-flow") {
passManager.addPass<AST::Pass::Lower_Control_Flow>();
}
else if (*i == "add-main-function") {
passManager.addPass<AST::Pass::MainFunction>();
}
}
}
else {
Expand Down
6 changes: 6 additions & 0 deletions include/rphp/analysis/astNodes.def
Expand Up @@ -52,6 +52,12 @@ STMT(propertyDecl, stmt)
STMT(functionDecl, stmt)
// LAST DECL

//FIRST MIR NODE
STMT(label, stmt)
STMT(branch, stmt)
//LAST MIR NODE


// expr (see expr::first and last kind constants)
// FIRST EXPR
EXPR(exprReduce, expr)
Expand Down

0 comments on commit aa2ea21

Please sign in to comment.