[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