[flang-commits] [flang] ae37bb9 - [Flang] Add support for lowering the goto statement
Kiran Chandramohan via flang-commits
flang-commits at lists.llvm.org
Wed Feb 9 01:48:34 PST 2022
Author: Kiran Chandramohan
Date: 2022-02-09T09:48:13Z
New Revision: ae37bb9804c7b6ee7e6d1c070889c30f74be1001
URL: https://github.com/llvm/llvm-project/commit/ae37bb9804c7b6ee7e6d1c070889c30f74be1001
DIFF: https://github.com/llvm/llvm-project/commit/ae37bb9804c7b6ee7e6d1c070889c30f74be1001.diff
LOG: [Flang] Add support for lowering the goto statement
This patch adds support for lowering the Fortran goto statement from
parse-tree to MLIR. The goto statement in Fortran is a form of
unstructured control flow. The statement transfers control to the
code starting at the label specified in the statement. This can be
faithfully represented in MLIR by a branch instruction.
To assist the lowering of code with unstructured control flow, blocks
are created in advance and associated with the relevant pre-fir tree
evaluations.
This is part of the upstreaming effort from the fir-dev branch in [1].
[1] https://github.com/flang-compiler/f18-llvm-project
Reviewed By: clementval, vdonaldson, schweitz, awarzynski
Differential Revision: https://reviews.llvm.org/D118983
Co-authored-by: V Donaldson <vdonaldson at nvidia.com>
Co-authored-by: Jean Perier <jperier at nvidia.com>
Co-authored-by: Eric Schweitz <eschweitz at nvidia.com>
Added:
flang/test/Lower/goto-statement.f90
Modified:
flang/lib/Lower/Bridge.cpp
Removed:
################################################################################
diff --git a/flang/lib/Lower/Bridge.cpp b/flang/lib/Lower/Bridge.cpp
index 82666df0b0615..ffaec4df07051 100644
--- a/flang/lib/Lower/Bridge.cpp
+++ b/flang/lib/Lower/Bridge.cpp
@@ -22,6 +22,7 @@
#include "flang/Lower/SymbolMap.h"
#include "flang/Lower/Todo.h"
#include "flang/Optimizer/Support/FIRContext.h"
+#include "mlir/Dialect/ControlFlow/IR/ControlFlowOps.h"
#include "mlir/IR/PatternMatch.h"
#include "mlir/Transforms/RegionUtils.h"
#include "llvm/Support/CommandLine.h"
@@ -132,6 +133,10 @@ class FirConverter : public Fortran::lower::AbstractConverter {
void setCurrentEval(Fortran::lower::pft::Evaluation &eval) {
evalPtr = &eval;
}
+ Fortran::lower::pft::Evaluation &getEval() {
+ assert(evalPtr && "current evaluation not set");
+ return *evalPtr;
+ }
mlir::Location getCurrentLocation() override final { return toLocation(); }
@@ -181,6 +186,29 @@ class FirConverter : public Fortran::lower::AbstractConverter {
!currentBlock->back().hasTrait<mlir::OpTrait::IsTerminator>();
}
+ /// Unconditionally switch code insertion to a new block.
+ void startBlock(mlir::Block *newBlock) {
+ assert(newBlock && "missing block");
+ // Default termination for the current block is a fallthrough branch to
+ // the new block.
+ if (blockIsUnterminated())
+ genFIRBranch(newBlock);
+ // Some blocks may be re/started more than once, and might not be empty.
+ // If the new block already has (only) a terminator, set the insertion
+ // point to the start of the block. Otherwise set it to the end.
+ // Note that setting the insertion point causes the subsequent function
+ // call to check the existence of terminator in the newBlock.
+ builder->setInsertionPointToStart(newBlock);
+ if (blockIsUnterminated())
+ builder->setInsertionPointToEnd(newBlock);
+ }
+
+ /// Conditionally switch code insertion to a new block.
+ void maybeStartBlock(mlir::Block *newBlock) {
+ if (newBlock)
+ startBlock(newBlock);
+ }
+
/// Emit return and cleanup after the function has been translated.
void endNewFunction(Fortran::lower::pft::FunctionLikeUnit &funit) {
setCurrentPosition(Fortran::lower::pft::stmtSourceLoc(funit.endStmt));
@@ -191,6 +219,10 @@ class FirConverter : public Fortran::lower::AbstractConverter {
funit.finalBlock = nullptr;
LLVM_DEBUG(llvm::dbgs() << "*** Lowering result:\n\n"
<< *builder->getFunction() << '\n');
+ // FIXME: Simplification should happen in a normal pass, not here.
+ mlir::IRRewriter rewriter(*builder);
+ (void)mlir::simplifyRegions(rewriter,
+ {builder->getRegion()}); // remove dead code
delete builder;
builder = nullptr;
localSymbols.clear();
@@ -218,6 +250,38 @@ class FirConverter : public Fortran::lower::AbstractConverter {
if (!sym.IsFuncResult() || !funit.primaryResult)
instantiateVar(var);
}
+
+ // Create most function blocks in advance.
+ createEmptyGlobalBlocks(funit.evaluationList);
+
+ // Reinstate entry block as the current insertion point.
+ builder->setInsertionPointToEnd(&func.front());
+ }
+
+ /// Create global blocks for the current function. This eliminates the
+ /// distinction between forward and backward targets when generating
+ /// branches. A block is "global" if it can be the target of a GOTO or
+ /// other source code branch. A block that can only be targeted by a
+ /// compiler generated branch is "local". For example, a DO loop preheader
+ /// block containing loop initialization code is global. A loop header
+ /// block, which is the target of the loop back edge, is local. Blocks
+ /// belong to a region. Any block within a nested region must be replaced
+ /// with a block belonging to that region. Branches may not cross region
+ /// boundaries.
+ void createEmptyGlobalBlocks(
+ std::list<Fortran::lower::pft::Evaluation> &evaluationList) {
+ mlir::Region *region = &builder->getRegion();
+ for (Fortran::lower::pft::Evaluation &eval : evaluationList) {
+ if (eval.isNewBlock)
+ eval.block = builder->createBlock(region);
+ if (eval.isConstruct() || eval.isDirective()) {
+ if (eval.lowerAsUnstructured()) {
+ createEmptyGlobalBlocks(eval.getNestedEvaluations());
+ } else if (eval.hasNestedEvaluations()) {
+ TODO(toLocation(), "Constructs with nested evaluations");
+ }
+ }
+ }
}
/// Lower a procedure (nest).
@@ -253,6 +317,11 @@ class FirConverter : public Fortran::lower::AbstractConverter {
return {};
}
+ void genFIRBranch(mlir::Block *targetBlock) {
+ assert(targetBlock && "missing unconditional target block");
+ builder->create<cf::BranchOp>(toLocation(), targetBlock);
+ }
+
//===--------------------------------------------------------------------===//
// Termination of symbolically referenced execution units
//===--------------------------------------------------------------------===//
@@ -595,7 +664,7 @@ class FirConverter : public Fortran::lower::AbstractConverter {
}
void genFIR(const Fortran::parser::GotoStmt &) {
- TODO(toLocation(), "GotoStmt lowering");
+ genFIRBranch(getEval().controlSuccessor->block);
}
void genFIR(const Fortran::parser::AssociateStmt &) {
@@ -671,6 +740,14 @@ class FirConverter : public Fortran::lower::AbstractConverter {
void genFIR(Fortran::lower::pft::Evaluation &eval,
bool unstructuredContext = true) {
+ if (unstructuredContext) {
+ // When transitioning from unstructured to structured code,
+ // the structured code could be a target that starts a new block.
+ maybeStartBlock(eval.isConstruct() && eval.lowerAsStructured()
+ ? eval.getFirstNestedEvaluation().block
+ : eval.block);
+ }
+
setCurrentEval(eval);
setCurrentPosition(eval.position);
eval.visit([&](const auto &stmt) { genFIR(stmt); });
diff --git a/flang/test/Lower/goto-statement.f90 b/flang/test/Lower/goto-statement.f90
new file mode 100644
index 0000000000000..b5359d50e8a30
--- /dev/null
+++ b/flang/test/Lower/goto-statement.f90
@@ -0,0 +1,66 @@
+! RUN: bbc %s -emit-fir -o - | FileCheck %s
+
+! Test trivial goto statement
+subroutine sub1()
+goto 1
+1 stop
+end subroutine
+! CHECK-LABEL: sub1
+! CHECK: cf.br ^[[BB1:.*]]
+! CHECK: ^[[BB1]]:
+! CHECK: {{.*}} fir.call @_FortranAStopStatement({{.*}}, {{.*}}, {{.*}}) : (i32, i1, i1) -> none
+! CHECK: }
+
+! Test multiple goto statements
+subroutine sub2()
+goto 1
+1 goto 2
+2 goto 3
+3 stop
+end subroutine
+! CHECK-LABEL: sub2
+! CHECK: cf.br ^[[BB1:.*]]
+! CHECK: ^[[BB1]]:
+! CHECK: cf.br ^[[BB2:.*]]
+! CHECK: ^[[BB2]]:
+! CHECK: cf.br ^[[BB3:.*]]
+! CHECK: ^[[BB3]]:
+! CHECK: {{.*}} fir.call @_FortranAStopStatement({{.*}}, {{.*}}, {{.*}}) : (i32, i1, i1) -> none
+! CHECK: }
+
+! Test goto which branches to a previous label
+subroutine sub3()
+pause
+1 goto 3
+2 stop
+3 goto 2
+end subroutine
+! CHECK: sub3
+! CHECK: {{.*}} fir.call @_FortranAPauseStatement() : () -> none
+! CHECK: cf.br ^[[BB2:.*]]
+! CHECK: ^[[BB1:.*]]: //
+! CHECK: {{.*}} fir.call @_FortranAStopStatement({{.*}}, {{.*}}, {{.*}}) : (i32, i1, i1) -> none
+! CHECK: ^[[BB2]]:
+! CHECK: cf.br ^[[BB1]]
+! CHECK: }
+
+! Test removal of blocks (pauses) which are not reachable
+subroutine sub4()
+pause
+1 goto 2
+pause
+2 goto 3
+pause
+3 goto 1
+pause
+end subroutine
+! CHECK-LABEL: sub4
+! CHECK: {{.*}} fir.call @_FortranAPauseStatement() : () -> none
+! CHECK-NEXT: cf.br ^[[BB1:.*]]
+! CHECK-NEXT: ^[[BB1]]:
+! CHECK-NEXT: cf.br ^[[BB2:.*]]
+! CHECK-NEXT: ^[[BB2]]:
+! CHECK-NEXT: cf.br ^[[BB3:.*]]
+! CHECK-NEXT: ^[[BB3]]:
+! CHECK-NEXT: cf.br ^[[BB1]]
+! CHECK-NEXT: }
More information about the flang-commits
mailing list