[flang-commits] [flang] 7a6a165 - [flang] Lower where statement

Valentin Clement via flang-commits flang-commits at lists.llvm.org
Thu Mar 10 09:44:41 PST 2022


Author: Valentin Clement
Date: 2022-03-10T18:44:23+01:00
New Revision: 7a6a1655d83b3ff79d120e399d8b9cc7ad2b143c

URL: https://github.com/llvm/llvm-project/commit/7a6a1655d83b3ff79d120e399d8b9cc7ad2b143c
DIFF: https://github.com/llvm/llvm-project/commit/7a6a1655d83b3ff79d120e399d8b9cc7ad2b143c.diff

LOG: [flang] Lower where statement

This patch lowers where statement to FIR.
The where statement is lowered to a conbination of
loops and if conditions.

This patch is part of the upstreaming effort from fir-dev branch.

Reviewed By: PeteSteinfeld

Differential Revision: https://reviews.llvm.org/D121385

Co-authored-by: Jean Perier <jperier at nvidia.com>
Co-authored-by: Eric Schweitz <eschweitz at nvidia.com>

Added: 
    flang/test/Lower/where.f90

Modified: 
    flang/include/flang/Lower/ConvertExpr.h
    flang/lib/Lower/Bridge.cpp
    flang/lib/Lower/ConvertExpr.cpp

Removed: 
    


################################################################################
diff  --git a/flang/include/flang/Lower/ConvertExpr.h b/flang/include/flang/Lower/ConvertExpr.h
index c1791723fed43..966007696e150 100644
--- a/flang/include/flang/Lower/ConvertExpr.h
+++ b/flang/include/flang/Lower/ConvertExpr.h
@@ -140,6 +140,40 @@ void createSomeArrayAssignment(AbstractConverter &converter,
                                const fir::ExtendedValue &rhs, SymMap &symMap,
                                StatementContext &stmtCtx);
 
+/// Common entry point for both explicit iteration spaces and implicit iteration
+/// spaces with masks.
+///
+/// For an implicit iteration space with masking, lowers an array assignment
+/// expression with masking expression(s).
+///
+/// 1. Evaluate the lhs to determine the rank and how to form the ArrayLoad
+/// (e.g., if there is a slicing op).
+/// 2. Scan the rhs, creating the ArrayLoads and evaluate the scalar subparts to
+/// be added to the map.
+/// 3. Create the loop nest.
+/// 4. Create the masking condition. Step 5 is conditionally executed only when
+/// the mask condition evaluates to true.
+/// 5. Evaluate the elemental expression, threading the results.
+/// 6. Copy the resulting array back with ArrayMergeStore to the lhs as
+/// determined per step 1.
+///
+/// For an explicit iteration space, lower a scalar or array assignment
+/// expression with a user-defined iteration space and possibly with masking
+/// expression(s).
+///
+/// If the expression is scalar, then the assignment is an array assignment but
+/// the array accesses are explicitly defined by the user and not implied for
+/// each element in the array. Mask expressions are optional.
+///
+/// If the expression has rank, then the assignment has a combined user-defined
+/// iteration space as well as a inner (subordinate) implied iteration
+/// space. The implied iteration space may include WHERE conditions, `masks`.
+void createAnyMaskedArrayAssignment(AbstractConverter &converter,
+                                    const SomeExpr &lhs, const SomeExpr &rhs,
+                                    ExplicitIterSpace &explicitIterSpace,
+                                    ImplicitIterSpace &implicitIterSpace,
+                                    SymMap &symMap, StatementContext &stmtCtx);
+
 /// Lower an assignment to an allocatable array, allocating the array if
 /// it is not allocated yet or reallocation it if it does not conform
 /// with the right hand side.
@@ -157,6 +191,19 @@ fir::ExtendedValue createSomeArrayTempValue(AbstractConverter &converter,
                                             SymMap &symMap,
                                             StatementContext &stmtCtx);
 
+/// Somewhat similar to createSomeArrayTempValue, but the temporary buffer is
+/// allocated lazily (inside the loops instead of before the loops) to
+/// accomodate buffers with shapes that cannot be precomputed. In fact, the
+/// buffer need not even be hyperrectangular. The buffer may be created as an
+/// instance of a ragged array, which may be useful if an array's extents are
+/// functions of other loop indices. The ragged array structure is built with \p
+/// raggedHeader being the root header variable. The header is a tuple of
+/// `{rank, data-is-headers, [data]*, [extents]*}`, which is built recursively.
+/// The base header, \p raggedHeader, must be initialized to zeros.
+void createLazyArrayTempValue(AbstractConverter &converter,
+                              const SomeExpr &expr, mlir::Value raggedHeader,
+                              SymMap &symMap, StatementContext &stmtCtx);
+
 // Attribute for an alloca that is a trivial adaptor for converting a value to
 // pass-by-ref semantics for a VALUE parameter. The optimizer may be able to
 // eliminate these.

diff  --git a/flang/lib/Lower/Bridge.cpp b/flang/lib/Lower/Bridge.cpp
index b062406f9eba4..6bfe8ccb34c36 100644
--- a/flang/lib/Lower/Bridge.cpp
+++ b/flang/lib/Lower/Bridge.cpp
@@ -1165,6 +1165,12 @@ class FirConverter : public Fortran::lower::AbstractConverter {
     TODO(toLocation(), "CaseConstruct lowering");
   }
 
+  template <typename A>
+  void genNestedStatement(const Fortran::parser::Statement<A> &stmt) {
+    setCurrentPosition(stmt.source);
+    genFIR(stmt.statement);
+  }
+
   void genFIR(const Fortran::parser::ConcurrentHeader &header) {
     TODO(toLocation(), "ConcurrentHeader lowering");
   }
@@ -1461,6 +1467,15 @@ class FirConverter : public Fortran::lower::AbstractConverter {
     TODO(toLocation(), "LockStmt lowering");
   }
 
+  /// Return true if the current context is a conditionalized and implied
+  /// iteration space.
+  bool implicitIterationSpace() { return !implicitIterSpace.empty(); }
+
+  /// Return true if context is currently an explicit iteration space. A scalar
+  /// assignment expression may be contextually within a user-defined iteration
+  /// space, transforming it into an array expression.
+  bool explicitIterationSpace() { return explicitIterSpace.isActive(); }
+
   /// Generate an array assignment.
   /// This is an assignment expression with rank > 0. The assignment may or may
   /// not be in a WHERE and/or FORALL context.
@@ -1475,46 +1490,106 @@ class FirConverter : public Fortran::lower::AbstractConverter {
       return;
     }
 
-    // No masks and the iteration space is implied by the array, so create a
-    // simple array assignment.
-    Fortran::lower::createSomeArrayAssignment(*this, assign.lhs, assign.rhs,
-                                              localSymbols, stmtCtx);
+    if (!implicitIterationSpace() && !explicitIterationSpace()) {
+      // No masks and the iteration space is implied by the array, so create a
+      // simple array assignment.
+      Fortran::lower::createSomeArrayAssignment(*this, assign.lhs, assign.rhs,
+                                                localSymbols, stmtCtx);
+      return;
+    }
+
+    // If there is an explicit iteration space, generate an array assignment
+    // with a user-specified iteration space and possibly with masks. These
+    // assignments may *appear* to be scalar expressions, but the scalar
+    // expression is evaluated at all points in the user-defined space much like
+    // an ordinary array assignment. More specifically, the semantics inside the
+    // FORALL much more closely resembles that of WHERE than a scalar
+    // assignment.
+    // Otherwise, generate a masked array assignment. The iteration space is
+    // implied by the lhs array expression.
+    Fortran::lower::createAnyMaskedArrayAssignment(
+        *this, assign.lhs, assign.rhs, explicitIterSpace, implicitIterSpace,
+        localSymbols,
+        explicitIterationSpace() ? explicitIterSpace.stmtContext()
+                                 : implicitIterSpace.stmtContext());
   }
 
   void genFIR(const Fortran::parser::WhereConstruct &c) {
-    TODO(toLocation(), "WhereConstruct lowering");
+    implicitIterSpace.growStack();
+    genNestedStatement(
+        std::get<
+            Fortran::parser::Statement<Fortran::parser::WhereConstructStmt>>(
+            c.t));
+    for (const auto &body :
+         std::get<std::list<Fortran::parser::WhereBodyConstruct>>(c.t))
+      genFIR(body);
+    for (const auto &e :
+         std::get<std::list<Fortran::parser::WhereConstruct::MaskedElsewhere>>(
+             c.t))
+      genFIR(e);
+    if (const auto &e =
+            std::get<std::optional<Fortran::parser::WhereConstruct::Elsewhere>>(
+                c.t);
+        e.has_value())
+      genFIR(*e);
+    genNestedStatement(
+        std::get<Fortran::parser::Statement<Fortran::parser::EndWhereStmt>>(
+            c.t));
   }
-
   void genFIR(const Fortran::parser::WhereBodyConstruct &body) {
-    TODO(toLocation(), "WhereBodyConstruct lowering");
+    std::visit(
+        Fortran::common::visitors{
+            [&](const Fortran::parser::Statement<
+                Fortran::parser::AssignmentStmt> &stmt) {
+              genNestedStatement(stmt);
+            },
+            [&](const Fortran::parser::Statement<Fortran::parser::WhereStmt>
+                    &stmt) { genNestedStatement(stmt); },
+            [&](const Fortran::common::Indirection<
+                Fortran::parser::WhereConstruct> &c) { genFIR(c.value()); },
+        },
+        body.u);
   }
-
   void genFIR(const Fortran::parser::WhereConstructStmt &stmt) {
-    TODO(toLocation(), "WhereConstructStmt lowering");
+    implicitIterSpace.append(Fortran::semantics::GetExpr(
+        std::get<Fortran::parser::LogicalExpr>(stmt.t)));
   }
-
   void genFIR(const Fortran::parser::WhereConstruct::MaskedElsewhere &ew) {
-    TODO(toLocation(), "MaskedElsewhere lowering");
+    genNestedStatement(
+        std::get<
+            Fortran::parser::Statement<Fortran::parser::MaskedElsewhereStmt>>(
+            ew.t));
+    for (const auto &body :
+         std::get<std::list<Fortran::parser::WhereBodyConstruct>>(ew.t))
+      genFIR(body);
   }
-
   void genFIR(const Fortran::parser::MaskedElsewhereStmt &stmt) {
-    TODO(toLocation(), "MaskedElsewhereStmt lowering");
+    implicitIterSpace.append(Fortran::semantics::GetExpr(
+        std::get<Fortran::parser::LogicalExpr>(stmt.t)));
   }
-
   void genFIR(const Fortran::parser::WhereConstruct::Elsewhere &ew) {
-    TODO(toLocation(), "Elsewhere lowering");
+    genNestedStatement(
+        std::get<Fortran::parser::Statement<Fortran::parser::ElsewhereStmt>>(
+            ew.t));
+    for (const auto &body :
+         std::get<std::list<Fortran::parser::WhereBodyConstruct>>(ew.t))
+      genFIR(body);
   }
-
   void genFIR(const Fortran::parser::ElsewhereStmt &stmt) {
-    TODO(toLocation(), "ElsewhereStmt lowering");
+    implicitIterSpace.append(nullptr);
   }
-
   void genFIR(const Fortran::parser::EndWhereStmt &) {
-    TODO(toLocation(), "EndWhereStmt lowering");
+    implicitIterSpace.shrinkStack();
   }
 
   void genFIR(const Fortran::parser::WhereStmt &stmt) {
-    TODO(toLocation(), "WhereStmt lowering");
+    Fortran::lower::StatementContext stmtCtx;
+    const auto &assign = std::get<Fortran::parser::AssignmentStmt>(stmt.t);
+    implicitIterSpace.growStack();
+    implicitIterSpace.append(Fortran::semantics::GetExpr(
+        std::get<Fortran::parser::LogicalExpr>(stmt.t)));
+    genAssignment(*assign.typedAssignment->v);
+    implicitIterSpace.shrinkStack();
   }
 
   void genFIR(const Fortran::parser::PointerAssignmentStmt &stmt) {

diff  --git a/flang/lib/Lower/ConvertExpr.cpp b/flang/lib/Lower/ConvertExpr.cpp
index c98938628ef35..7fdd4ca83a585 100644
--- a/flang/lib/Lower/ConvertExpr.cpp
+++ b/flang/lib/Lower/ConvertExpr.cpp
@@ -30,7 +30,9 @@
 #include "flang/Optimizer/Builder/Factory.h"
 #include "flang/Optimizer/Builder/LowLevelIntrinsics.h"
 #include "flang/Optimizer/Builder/MutableBox.h"
+#include "flang/Optimizer/Builder/Runtime/Character.h"
 #include "flang/Optimizer/Builder/Runtime/RTBuilder.h"
+#include "flang/Optimizer/Builder/Runtime/Ragged.h"
 #include "flang/Optimizer/Dialect/FIROpsSupport.h"
 #include "flang/Semantics/expression.h"
 #include "flang/Semantics/symbol.h"
@@ -2425,6 +2427,36 @@ class ArrayExprLowering {
     }
   }
 
+  //===--------------------------------------------------------------------===//
+  // WHERE array assignment, FORALL assignment, and FORALL+WHERE array
+  // assignment
+  //===--------------------------------------------------------------------===//
+
+  /// Entry point for array assignment when the iteration space is explicitly
+  /// defined (Fortran's FORALL) with or without masks, and/or the implied
+  /// iteration space involves masks (Fortran's WHERE). Both contexts (explicit
+  /// space and implicit space with masks) may be present.
+  static void lowerAnyMaskedArrayAssignment(
+      Fortran::lower::AbstractConverter &converter,
+      Fortran::lower::SymMap &symMap, Fortran::lower::StatementContext &stmtCtx,
+      const Fortran::lower::SomeExpr &lhs, const Fortran::lower::SomeExpr &rhs,
+      Fortran::lower::ExplicitIterSpace &explicitSpace,
+      Fortran::lower::ImplicitIterSpace &implicitSpace) {
+    if (explicitSpace.isActive() && lhs.Rank() == 0) {
+      // Scalar assignment expression in a FORALL context.
+      ArrayExprLowering ael(converter, stmtCtx, symMap,
+                            ConstituentSemantics::RefTransparent,
+                            &explicitSpace, &implicitSpace);
+      ael.lowerScalarAssignment(lhs, rhs);
+      return;
+    }
+    // Array assignment expression in a FORALL and/or WHERE context.
+    ArrayExprLowering ael(converter, stmtCtx, symMap,
+                          ConstituentSemantics::CopyInCopyOut, &explicitSpace,
+                          &implicitSpace);
+    ael.lowerArrayAssignment(lhs, rhs);
+  }
+
   //===--------------------------------------------------------------------===//
   // Array assignment to allocatable array
   //===--------------------------------------------------------------------===//
@@ -2568,6 +2600,291 @@ class ArrayExprLowering {
     return fir::ArrayBoxValue(tempRes, dest.getExtents());
   }
 
+  static void lowerLazyArrayExpression(
+      Fortran::lower::AbstractConverter &converter,
+      Fortran::lower::SymMap &symMap, Fortran::lower::StatementContext &stmtCtx,
+      const Fortran::lower::SomeExpr &expr, mlir::Value raggedHeader) {
+    ArrayExprLowering ael(converter, stmtCtx, symMap);
+    ael.lowerLazyArrayExpression(expr, raggedHeader);
+  }
+
+  /// Lower the expression \p expr into a buffer that is created on demand. The
+  /// variable containing the pointer to the buffer is \p var and the variable
+  /// containing the shape of the buffer is \p shapeBuffer.
+  void lowerLazyArrayExpression(const Fortran::lower::SomeExpr &expr,
+                                mlir::Value header) {
+    mlir::Location loc = getLoc();
+    mlir::TupleType hdrTy = fir::factory::getRaggedArrayHeaderType(builder);
+    mlir::IntegerType i32Ty = builder.getIntegerType(32);
+
+    // Once the loop extents have been computed, which may require being inside
+    // some explicit loops, lazily allocate the expression on the heap. The
+    // following continuation creates the buffer as needed.
+    ccPrelude = [=](llvm::ArrayRef<mlir::Value> shape) {
+      mlir::IntegerType i64Ty = builder.getIntegerType(64);
+      mlir::Value byteSize = builder.createIntegerConstant(loc, i64Ty, 1);
+      fir::runtime::genRaggedArrayAllocate(
+          loc, builder, header, /*asHeaders=*/false, byteSize, shape);
+    };
+
+    // Create a dummy array_load before the loop. We're storing to a lazy
+    // temporary, so there will be no conflict and no copy-in. TODO: skip this
+    // as there isn't any necessity for it.
+    ccLoadDest = [=](llvm::ArrayRef<mlir::Value> shape) -> fir::ArrayLoadOp {
+      mlir::Value one = builder.createIntegerConstant(loc, i32Ty, 1);
+      auto var = builder.create<fir::CoordinateOp>(
+          loc, builder.getRefType(hdrTy.getType(1)), header, one);
+      auto load = builder.create<fir::LoadOp>(loc, var);
+      mlir::Type eleTy =
+          fir::unwrapSequenceType(fir::unwrapRefType(load.getType()));
+      auto seqTy = fir::SequenceType::get(eleTy, shape.size());
+      mlir::Value castTo =
+          builder.createConvert(loc, fir::HeapType::get(seqTy), load);
+      mlir::Value shapeOp = builder.genShape(loc, shape);
+      return builder.create<fir::ArrayLoadOp>(
+          loc, seqTy, castTo, shapeOp, /*slice=*/mlir::Value{}, llvm::None);
+    };
+    // Custom lowering of the element store to deal with the extra indirection
+    // to the lazy allocated buffer.
+    ccStoreToDest = [=](IterSpace iters) {
+      mlir::Value one = builder.createIntegerConstant(loc, i32Ty, 1);
+      auto var = builder.create<fir::CoordinateOp>(
+          loc, builder.getRefType(hdrTy.getType(1)), header, one);
+      auto load = builder.create<fir::LoadOp>(loc, var);
+      mlir::Type eleTy =
+          fir::unwrapSequenceType(fir::unwrapRefType(load.getType()));
+      auto seqTy = fir::SequenceType::get(eleTy, iters.iterVec().size());
+      auto toTy = fir::HeapType::get(seqTy);
+      mlir::Value castTo = builder.createConvert(loc, toTy, load);
+      mlir::Value shape = builder.genShape(loc, genIterationShape());
+      llvm::SmallVector<mlir::Value> indices = fir::factory::originateIndices(
+          loc, builder, castTo.getType(), shape, iters.iterVec());
+      auto eleAddr = builder.create<fir::ArrayCoorOp>(
+          loc, builder.getRefType(eleTy), castTo, shape,
+          /*slice=*/mlir::Value{}, indices, destination.getTypeparams());
+      mlir::Value eleVal =
+          builder.createConvert(loc, eleTy, iters.getElement());
+      builder.create<fir::StoreOp>(loc, eleVal, eleAddr);
+      return iters.innerArgument();
+    };
+
+    // Lower the array expression now. Clean-up any temps that may have
+    // been generated when lowering `expr` right after the lowered value
+    // was stored to the ragged array temporary. The local temps will not
+    // be needed afterwards.
+    stmtCtx.pushScope();
+    [[maybe_unused]] ExtValue loopRes = lowerArrayExpression(expr);
+    stmtCtx.finalize(/*popScope=*/true);
+    assert(fir::getBase(loopRes));
+  }
+
+  template <typename A, typename B>
+  ExtValue lowerScalarAssignment(const A &lhs, const B &rhs) {
+    // 1) Lower the rhs expression with array_fetch op(s).
+    IterationSpace iters;
+    iters.setElement(genarr(rhs)(iters));
+    fir::ExtendedValue elementalExv = iters.elementExv();
+    // 2) Lower the lhs expression to an array_update.
+    semant = ConstituentSemantics::ProjectedCopyInCopyOut;
+    auto lexv = genarr(lhs)(iters);
+    // 3) Finalize the inner context.
+    explicitSpace->finalizeContext();
+    // 4) Thread the array value updated forward. Note: the lhs might be
+    // ill-formed (performing scalar assignment in an array context),
+    // in which case there is no array to thread.
+    auto createResult = [&](auto op) {
+      mlir::Value oldInnerArg = op.getSequence();
+      std::size_t offset = explicitSpace->argPosition(oldInnerArg);
+      explicitSpace->setInnerArg(offset, fir::getBase(lexv));
+      builder.create<fir::ResultOp>(getLoc(), fir::getBase(lexv));
+    };
+    if (auto updateOp = mlir::dyn_cast<fir::ArrayUpdateOp>(
+            fir::getBase(lexv).getDefiningOp()))
+      createResult(updateOp);
+    else if (auto amend = mlir::dyn_cast<fir::ArrayAmendOp>(
+                 fir::getBase(lexv).getDefiningOp()))
+      createResult(amend);
+    else if (auto modifyOp = mlir::dyn_cast<fir::ArrayModifyOp>(
+                 fir::getBase(lexv).getDefiningOp()))
+      createResult(modifyOp);
+    return lexv;
+  }
+
+  bool explicitSpaceIsActive() const {
+    return explicitSpace && explicitSpace->isActive();
+  }
+
+  bool implicitSpaceHasMasks() const {
+    return implicitSpace && !implicitSpace->empty();
+  }
+
+  CC genMaskAccess(mlir::Value tmp, mlir::Value shape) {
+    mlir::Location loc = getLoc();
+    return [=, builder = &converter.getFirOpBuilder()](IterSpace iters) {
+      mlir::Type arrTy = fir::dyn_cast_ptrOrBoxEleTy(tmp.getType());
+      auto eleTy = arrTy.cast<fir::SequenceType>().getEleTy();
+      mlir::Type eleRefTy = builder->getRefType(eleTy);
+      mlir::IntegerType i1Ty = builder->getI1Type();
+      // Adjust indices for any shift of the origin of the array.
+      llvm::SmallVector<mlir::Value> indices = fir::factory::originateIndices(
+          loc, *builder, tmp.getType(), shape, iters.iterVec());
+      auto addr = builder->create<fir::ArrayCoorOp>(
+          loc, eleRefTy, tmp, shape, /*slice=*/mlir::Value{}, indices,
+          /*typeParams=*/llvm::None);
+      auto load = builder->create<fir::LoadOp>(loc, addr);
+      return builder->createConvert(loc, i1Ty, load);
+    };
+  }
+
+  /// Construct the incremental instantiations of the ragged array structure.
+  /// Rebind the lazy buffer variable, etc. as we go.
+  template <bool withAllocation = false>
+  mlir::Value prepareRaggedArrays(Fortran::lower::FrontEndExpr expr) {
+    assert(explicitSpaceIsActive());
+    mlir::Location loc = getLoc();
+    mlir::TupleType raggedTy = fir::factory::getRaggedArrayHeaderType(builder);
+    llvm::SmallVector<llvm::SmallVector<fir::DoLoopOp>> loopStack =
+        explicitSpace->getLoopStack();
+    const std::size_t depth = loopStack.size();
+    mlir::IntegerType i64Ty = builder.getIntegerType(64);
+    [[maybe_unused]] mlir::Value byteSize =
+        builder.createIntegerConstant(loc, i64Ty, 1);
+    mlir::Value header = implicitSpace->lookupMaskHeader(expr);
+    for (std::remove_const_t<decltype(depth)> i = 0; i < depth; ++i) {
+      auto insPt = builder.saveInsertionPoint();
+      if (i < depth - 1)
+        builder.setInsertionPoint(loopStack[i + 1][0]);
+
+      // Compute and gather the extents.
+      llvm::SmallVector<mlir::Value> extents;
+      for (auto doLoop : loopStack[i])
+        extents.push_back(builder.genExtentFromTriplet(
+            loc, doLoop.getLowerBound(), doLoop.getUpperBound(),
+            doLoop.getStep(), i64Ty));
+      if constexpr (withAllocation) {
+        fir::runtime::genRaggedArrayAllocate(
+            loc, builder, header, /*asHeader=*/true, byteSize, extents);
+      }
+
+      // Compute the dynamic position into the header.
+      llvm::SmallVector<mlir::Value> offsets;
+      for (auto doLoop : loopStack[i]) {
+        auto m = builder.create<mlir::arith::SubIOp>(
+            loc, doLoop.getInductionVar(), doLoop.getLowerBound());
+        auto n = builder.create<mlir::arith::DivSIOp>(loc, m, doLoop.getStep());
+        mlir::Value one = builder.createIntegerConstant(loc, n.getType(), 1);
+        offsets.push_back(builder.create<mlir::arith::AddIOp>(loc, n, one));
+      }
+      mlir::IntegerType i32Ty = builder.getIntegerType(32);
+      mlir::Value uno = builder.createIntegerConstant(loc, i32Ty, 1);
+      mlir::Type coorTy = builder.getRefType(raggedTy.getType(1));
+      auto hdOff = builder.create<fir::CoordinateOp>(loc, coorTy, header, uno);
+      auto toTy = fir::SequenceType::get(raggedTy, offsets.size());
+      mlir::Type toRefTy = builder.getRefType(toTy);
+      auto ldHdr = builder.create<fir::LoadOp>(loc, hdOff);
+      mlir::Value hdArr = builder.createConvert(loc, toRefTy, ldHdr);
+      auto shapeOp = builder.genShape(loc, extents);
+      header = builder.create<fir::ArrayCoorOp>(
+          loc, builder.getRefType(raggedTy), hdArr, shapeOp,
+          /*slice=*/mlir::Value{}, offsets,
+          /*typeparams=*/mlir::ValueRange{});
+      auto hdrVar = builder.create<fir::CoordinateOp>(loc, coorTy, header, uno);
+      auto inVar = builder.create<fir::LoadOp>(loc, hdrVar);
+      mlir::Value two = builder.createIntegerConstant(loc, i32Ty, 2);
+      mlir::Type coorTy2 = builder.getRefType(raggedTy.getType(2));
+      auto hdrSh = builder.create<fir::CoordinateOp>(loc, coorTy2, header, two);
+      auto shapePtr = builder.create<fir::LoadOp>(loc, hdrSh);
+      // Replace the binding.
+      implicitSpace->rebind(expr, genMaskAccess(inVar, shapePtr));
+      if (i < depth - 1)
+        builder.restoreInsertionPoint(insPt);
+    }
+    return header;
+  }
+
+  /// Lower mask expressions with implied iteration spaces from the variants of
+  /// WHERE syntax. Since it is legal for mask expressions to have side-effects
+  /// and modify values that will be used for the lhs, rhs, or both of
+  /// subsequent assignments, the mask must be evaluated before the assignment
+  /// is processed.
+  /// Mask expressions are array expressions too.
+  void genMasks() {
+    // Lower the mask expressions, if any.
+    if (implicitSpaceHasMasks()) {
+      mlir::Location loc = getLoc();
+      // Mask expressions are array expressions too.
+      for (const auto *e : implicitSpace->getExprs())
+        if (e && !implicitSpace->isLowered(e)) {
+          if (mlir::Value var = implicitSpace->lookupMaskVariable(e)) {
+            // Allocate the mask buffer lazily.
+            assert(explicitSpaceIsActive());
+            mlir::Value header =
+                prepareRaggedArrays</*withAllocations=*/true>(e);
+            Fortran::lower::createLazyArrayTempValue(converter, *e, header,
+                                                     symMap, stmtCtx);
+            // Close the explicit loops.
+            builder.create<fir::ResultOp>(loc, explicitSpace->getInnerArgs());
+            builder.setInsertionPointAfter(explicitSpace->getOuterLoop());
+            // Open a new copy of the explicit loop nest.
+            explicitSpace->genLoopNest();
+            continue;
+          }
+          fir::ExtendedValue tmp = Fortran::lower::createSomeArrayTempValue(
+              converter, *e, symMap, stmtCtx);
+          mlir::Value shape = builder.createShape(loc, tmp);
+          implicitSpace->bind(e, genMaskAccess(fir::getBase(tmp), shape));
+        }
+
+      // Set buffer from the header.
+      for (const auto *e : implicitSpace->getExprs()) {
+        if (!e)
+          continue;
+        if (implicitSpace->lookupMaskVariable(e)) {
+          // Index into the ragged buffer to retrieve cached results.
+          const int rank = e->Rank();
+          assert(destShape.empty() ||
+                 static_cast<std::size_t>(rank) == destShape.size());
+          mlir::Value header = prepareRaggedArrays(e);
+          mlir::TupleType raggedTy =
+              fir::factory::getRaggedArrayHeaderType(builder);
+          mlir::IntegerType i32Ty = builder.getIntegerType(32);
+          mlir::Value one = builder.createIntegerConstant(loc, i32Ty, 1);
+          auto coor1 = builder.create<fir::CoordinateOp>(
+              loc, builder.getRefType(raggedTy.getType(1)), header, one);
+          auto db = builder.create<fir::LoadOp>(loc, coor1);
+          mlir::Type eleTy =
+              fir::unwrapSequenceType(fir::unwrapRefType(db.getType()));
+          mlir::Type buffTy =
+              builder.getRefType(fir::SequenceType::get(eleTy, rank));
+          // Address of ragged buffer data.
+          mlir::Value buff = builder.createConvert(loc, buffTy, db);
+
+          mlir::Value two = builder.createIntegerConstant(loc, i32Ty, 2);
+          auto coor2 = builder.create<fir::CoordinateOp>(
+              loc, builder.getRefType(raggedTy.getType(2)), header, two);
+          auto shBuff = builder.create<fir::LoadOp>(loc, coor2);
+          mlir::IntegerType i64Ty = builder.getIntegerType(64);
+          mlir::IndexType idxTy = builder.getIndexType();
+          llvm::SmallVector<mlir::Value> extents;
+          for (std::remove_const_t<decltype(rank)> i = 0; i < rank; ++i) {
+            mlir::Value off = builder.createIntegerConstant(loc, i32Ty, i);
+            auto coor = builder.create<fir::CoordinateOp>(
+                loc, builder.getRefType(i64Ty), shBuff, off);
+            auto ldExt = builder.create<fir::LoadOp>(loc, coor);
+            extents.push_back(builder.createConvert(loc, idxTy, ldExt));
+          }
+          if (destShape.empty())
+            destShape = extents;
+          // Construct shape of buffer.
+          mlir::Value shapeOp = builder.genShape(loc, extents);
+
+          // Replace binding with the local result.
+          implicitSpace->rebind(e, genMaskAccess(buff, shapeOp));
+        }
+      }
+    }
+  }
+
   // FIXME: should take multiple inner arguments.
   std::pair<IterationSpace, mlir::OpBuilder::InsertPoint>
   genImplicitLoops(mlir::ValueRange shape, mlir::Value innerArg) {
@@ -2688,7 +3005,7 @@ class ArrayExprLowering {
           builder.create<fir::ResultOp>(loc, innerArg);
           builder.setInsertionPointToStart(&ifOp.getThenRegion().front());
         };
-        for (std::remove_const_t<decltype(size)> i = 0; i < size; ++i)
+        for (std::size_t i = 0; i < size; ++i)
           if (const auto *e = maskExprs[i])
             genFalseBlock(e, genCond(e, iters));
 
@@ -3048,7 +3365,11 @@ class ArrayExprLowering {
   template <int KIND>
   CC genarr(const Fortran::evaluate::Negate<Fortran::evaluate::Type<
                 Fortran::common::TypeCategory::Real, KIND>> &x) {
-    TODO(getLoc(), "");
+    mlir::Location loc = getLoc();
+    auto f = genarr(x.left());
+    return [=](IterSpace iters) -> ExtValue {
+      return builder.create<mlir::arith::NegFOp>(loc, fir::getBase(f(iters)));
+    };
   }
   template <int KIND>
   CC genarr(const Fortran::evaluate::Negate<Fortran::evaluate::Type<
@@ -3629,29 +3950,56 @@ class ArrayExprLowering {
     TODO(getLoc(), "genarr LogicalOperation");
   }
 
+  //===--------------------------------------------------------------------===//
+  // Relational operators (<, <=, ==, etc.)
+  //===--------------------------------------------------------------------===//
+
+  template <typename OP, typename PRED, typename A>
+  CC createCompareOp(PRED pred, const A &x) {
+    mlir::Location loc = getLoc();
+    auto lf = genarr(x.left());
+    auto rf = genarr(x.right());
+    return [=](IterSpace iters) -> ExtValue {
+      mlir::Value lhs = fir::getBase(lf(iters));
+      mlir::Value rhs = fir::getBase(rf(iters));
+      return builder.create<OP>(loc, pred, lhs, rhs);
+    };
+  }
+  template <typename A>
+  CC createCompareCharOp(mlir::arith::CmpIPredicate pred, const A &x) {
+    mlir::Location loc = getLoc();
+    auto lf = genarr(x.left());
+    auto rf = genarr(x.right());
+    return [=](IterSpace iters) -> ExtValue {
+      auto lhs = lf(iters);
+      auto rhs = rf(iters);
+      return fir::runtime::genCharCompare(builder, loc, pred, lhs, rhs);
+    };
+  }
   template <int KIND>
   CC genarr(const Fortran::evaluate::Relational<Fortran::evaluate::Type<
                 Fortran::common::TypeCategory::Integer, KIND>> &x) {
-    TODO(getLoc(), "genarr Relational Integer");
+    return createCompareOp<mlir::arith::CmpIOp>(translateRelational(x.opr), x);
   }
   template <int KIND>
   CC genarr(const Fortran::evaluate::Relational<Fortran::evaluate::Type<
                 Fortran::common::TypeCategory::Character, KIND>> &x) {
-    TODO(getLoc(), "genarr Relational Character");
+    return createCompareCharOp(translateRelational(x.opr), x);
   }
   template <int KIND>
   CC genarr(const Fortran::evaluate::Relational<Fortran::evaluate::Type<
                 Fortran::common::TypeCategory::Real, KIND>> &x) {
-    TODO(getLoc(), "genarr Relational Real");
+    return createCompareOp<mlir::arith::CmpFOp>(translateFloatRelational(x.opr),
+                                                x);
   }
   template <int KIND>
   CC genarr(const Fortran::evaluate::Relational<Fortran::evaluate::Type<
                 Fortran::common::TypeCategory::Complex, KIND>> &x) {
-    TODO(getLoc(), "genarr Relational Complex");
+    return createCompareOp<fir::CmpcOp>(translateFloatRelational(x.opr), x);
   }
   CC genarr(
       const Fortran::evaluate::Relational<Fortran::evaluate::SomeType> &r) {
-    TODO(getLoc(), "genarr Relational SomeType");
+    return std::visit([&](const auto &x) { return genarr(x); }, r.u);
   }
 
   template <typename A>
@@ -4322,14 +4670,6 @@ class ArrayExprLowering {
                         "failed to compute the array expression shape");
   }
 
-  bool explicitSpaceIsActive() const {
-    return explicitSpace && explicitSpace->isActive();
-  }
-
-  bool implicitSpaceHasMasks() const {
-    return implicitSpace && !implicitSpace->empty();
-  }
-
   explicit ArrayExprLowering(Fortran::lower::AbstractConverter &converter,
                              Fortran::lower::StatementContext &stmtCtx,
                              Fortran::lower::SymMap &symMap)
@@ -4355,7 +4695,7 @@ class ArrayExprLowering {
         implicitSpace(impSpace->empty() ? nullptr : impSpace), semant{sem} {
     // Generate any mask expressions, as necessary. This is the compute step
     // that creates the effective masks. See 10.2.3.2 in particular.
-    // genMasks();
+    genMasks();
   }
 
   mlir::Location getLoc() { return converter.getCurrentLocation(); }
@@ -4552,6 +4892,21 @@ void Fortran::lower::createSomeArrayAssignment(
   ArrayExprLowering::lowerArrayAssignment(converter, symMap, stmtCtx, lhs, rhs);
 }
 
+void Fortran::lower::createAnyMaskedArrayAssignment(
+    Fortran::lower::AbstractConverter &converter,
+    const Fortran::lower::SomeExpr &lhs, const Fortran::lower::SomeExpr &rhs,
+    Fortran::lower::ExplicitIterSpace &explicitSpace,
+    Fortran::lower::ImplicitIterSpace &implicitSpace,
+    Fortran::lower::SymMap &symMap, Fortran::lower::StatementContext &stmtCtx) {
+  LLVM_DEBUG(lhs.AsFortran(llvm::dbgs() << "onto array: ") << '\n';
+             rhs.AsFortran(llvm::dbgs() << "assign expression: ")
+             << " given the explicit iteration space:\n"
+             << explicitSpace << "\n and implied mask conditions:\n"
+             << implicitSpace << '\n';);
+  ArrayExprLowering::lowerAnyMaskedArrayAssignment(
+      converter, symMap, stmtCtx, lhs, rhs, explicitSpace, implicitSpace);
+}
+
 void Fortran::lower::createAllocatableArrayAssignment(
     Fortran::lower::AbstractConverter &converter,
     const Fortran::lower::SomeExpr &lhs, const Fortran::lower::SomeExpr &rhs,
@@ -4576,6 +4931,15 @@ fir::ExtendedValue Fortran::lower::createSomeArrayTempValue(
                                                     expr);
 }
 
+void Fortran::lower::createLazyArrayTempValue(
+    Fortran::lower::AbstractConverter &converter,
+    const Fortran::lower::SomeExpr &expr, mlir::Value raggedHeader,
+    Fortran::lower::SymMap &symMap, Fortran::lower::StatementContext &stmtCtx) {
+  LLVM_DEBUG(expr.AsFortran(llvm::dbgs() << "array value: ") << '\n');
+  ArrayExprLowering::lowerLazyArrayExpression(converter, symMap, stmtCtx, expr,
+                                              raggedHeader);
+}
+
 mlir::Value Fortran::lower::genMaxWithZero(fir::FirOpBuilder &builder,
                                            mlir::Location loc,
                                            mlir::Value value) {

diff  --git a/flang/test/Lower/where.f90 b/flang/test/Lower/where.f90
new file mode 100644
index 0000000000000..ae378ea5a1135
--- /dev/null
+++ b/flang/test/Lower/where.f90
@@ -0,0 +1,239 @@
+  ! RUN: bbc -emit-fir %s -o - | FileCheck %s
+
+  ! CHECK-LABEL: func @_QQmain() {
+  ! CHECK:         %[[VAL_0:.*]] = fir.address_of(@_QFEa) : !fir.ref<!fir.array<10xf32>>
+  ! CHECK:         %[[VAL_1:.*]] = arith.constant 10 : index
+  ! CHECK:         %[[VAL_2:.*]] = fir.address_of(@_QFEb) : !fir.ref<!fir.array<10xf32>>
+  ! CHECK:         %[[VAL_3:.*]] = arith.constant 10 : index
+  ! CHECK:         %[[VAL_5:.*]] = arith.constant 10 : index
+  ! CHECK:         %[[VAL_6:.*]] = fir.shape %[[VAL_1]] : (index) -> !fir.shape<1>
+  ! CHECK:         %[[VAL_7:.*]] = fir.array_load %[[VAL_0]](%[[VAL_6]]) : (!fir.ref<!fir.array<10xf32>>, !fir.shape<1>) -> !fir.array<10xf32>
+  ! CHECK:         %[[VAL_8:.*]] = arith.constant 4.000000e+00 : f32
+  ! CHECK:         %[[VAL_9:.*]] = fir.allocmem !fir.array<10x!fir.logical<4>>
+  ! CHECK:         %[[VAL_10:.*]] = fir.shape %[[VAL_5]] : (index) -> !fir.shape<1>
+  ! CHECK:         %[[VAL_11:.*]] = fir.array_load %[[VAL_9]](%[[VAL_10]]) : (!fir.heap<!fir.array<10x!fir.logical<4>>>, !fir.shape<1>) -> !fir.array<10x!fir.logical<4>>
+  ! CHECK:         %[[VAL_12:.*]] = arith.constant 1 : index
+  ! CHECK:         %[[VAL_13:.*]] = arith.constant 0 : index
+  ! CHECK:         %[[VAL_14:.*]] = arith.subi %[[VAL_5]], %[[VAL_12]] : index
+  ! CHECK:         %[[VAL_15:.*]] = fir.do_loop %[[VAL_16:.*]] = %[[VAL_13]] to %[[VAL_14]] step %[[VAL_12]] unordered iter_args(%[[VAL_17:.*]] = %[[VAL_11]]) -> (!fir.array<10x!fir.logical<4>>) {
+  ! CHECK:           %[[VAL_18:.*]] = fir.array_fetch %[[VAL_7]], %[[VAL_16]] : (!fir.array<10xf32>, index) -> f32
+  ! CHECK:           %[[VAL_19:.*]] = arith.cmpf ogt, %[[VAL_18]], %[[VAL_8]] : f32
+  ! CHECK:           %[[VAL_20:.*]] = fir.convert %[[VAL_19]] : (i1) -> !fir.logical<4>
+  ! CHECK:           %[[VAL_21:.*]] = fir.array_update %[[VAL_17]], %[[VAL_20]], %[[VAL_16]] : (!fir.array<10x!fir.logical<4>>, !fir.logical<4>, index) -> !fir.array<10x!fir.logical<4>>
+  ! CHECK:           fir.result %[[VAL_21]] : !fir.array<10x!fir.logical<4>>
+  ! CHECK:         }
+  ! CHECK:         fir.array_merge_store %[[VAL_11]], %[[VAL_22:.*]] to %[[VAL_9]] : !fir.array<10x!fir.logical<4>>, !fir.array<10x!fir.logical<4>>, !fir.heap<!fir.array<10x!fir.logical<4>>>
+  ! CHECK:         %[[VAL_23:.*]] = fir.shape %[[VAL_5]] : (index) -> !fir.shape<1>
+  ! CHECK:         %[[VAL_24:.*]] = fir.shape %[[VAL_3]] : (index) -> !fir.shape<1>
+  ! CHECK:         %[[VAL_25:.*]] = fir.array_load %[[VAL_2]](%[[VAL_24]]) : (!fir.ref<!fir.array<10xf32>>, !fir.shape<1>) -> !fir.array<10xf32>
+  ! CHECK:         %[[VAL_26:.*]] = fir.shape %[[VAL_1]] : (index) -> !fir.shape<1>
+  ! CHECK:         %[[VAL_27:.*]] = fir.array_load %[[VAL_0]](%[[VAL_26]]) : (!fir.ref<!fir.array<10xf32>>, !fir.shape<1>) -> !fir.array<10xf32>
+  ! CHECK:         %[[VAL_28:.*]] = arith.constant 1 : index
+  ! CHECK:         %[[VAL_29:.*]] = arith.constant 0 : index
+  ! CHECK:         %[[VAL_30:.*]] = arith.subi %[[VAL_3]], %[[VAL_28]] : index
+  ! CHECK:         %[[VAL_31:.*]] = fir.do_loop %[[VAL_32:.*]] = %[[VAL_29]] to %[[VAL_30]] step %[[VAL_28]] unordered iter_args(%[[VAL_33:.*]] = %[[VAL_25]]) -> (!fir.array<10xf32>) {
+  ! CHECK:           %[[VAL_34:.*]] = arith.constant 1 : index
+  ! CHECK:           %[[VAL_35:.*]] = arith.addi %[[VAL_32]], %[[VAL_34]] : index
+  ! CHECK:           %[[VAL_36:.*]] = fir.array_coor %[[VAL_9]](%[[VAL_23]]) %[[VAL_35]] : (!fir.heap<!fir.array<10x!fir.logical<4>>>, !fir.shape<1>, index) -> !fir.ref<!fir.logical<4>>
+  ! CHECK:           %[[VAL_37:.*]] = fir.load %[[VAL_36]] : !fir.ref<!fir.logical<4>>
+  ! CHECK:           %[[VAL_38:.*]] = fir.convert %[[VAL_37]] : (!fir.logical<4>) -> i1
+  ! CHECK:           %[[VAL_39:.*]] = fir.if %[[VAL_38]] -> (!fir.array<10xf32>) {
+  ! CHECK:             %[[VAL_40:.*]] = fir.array_fetch %[[VAL_27]], %[[VAL_32]] : (!fir.array<10xf32>, index) -> f32
+  ! CHECK:             %[[VAL_41:.*]] = arith.negf %[[VAL_40]] : f32
+  ! CHECK:             %[[VAL_42:.*]] = fir.array_update %[[VAL_33]], %[[VAL_41]], %[[VAL_32]] : (!fir.array<10xf32>, f32, index) -> !fir.array<10xf32>
+  ! CHECK:             fir.result %[[VAL_42]] : !fir.array<10xf32>
+  ! CHECK:           } else {
+  ! CHECK:             fir.result %[[VAL_33]] : !fir.array<10xf32>
+  ! CHECK:           }
+  ! CHECK:           fir.result %[[VAL_43:.*]] : !fir.array<10xf32>
+  ! CHECK:         }
+  ! CHECK:         fir.array_merge_store %[[VAL_25]], %[[VAL_44:.*]] to %[[VAL_2]] : !fir.array<10xf32>, !fir.array<10xf32>, !fir.ref<!fir.array<10xf32>>
+  ! CHECK:         fir.freemem %[[VAL_9]]
+  ! CHECK:         %[[VAL_46:.*]] = arith.constant 10 : index
+  ! CHECK:         %[[VAL_47:.*]] = fir.shape %[[VAL_1]] : (index) -> !fir.shape<1>
+  ! CHECK:         %[[VAL_48:.*]] = fir.array_load %[[VAL_0]](%[[VAL_47]]) : (!fir.ref<!fir.array<10xf32>>, !fir.shape<1>) -> !fir.array<10xf32>
+  ! CHECK:         %[[VAL_49:.*]] = arith.constant 1.000000e+02 : f32
+  ! CHECK:         %[[VAL_50:.*]] = fir.allocmem !fir.array<10x!fir.logical<4>>
+  ! CHECK:         %[[VAL_51:.*]] = fir.shape %[[VAL_46]] : (index) -> !fir.shape<1>
+  ! CHECK:         %[[VAL_52:.*]] = fir.array_load %[[VAL_50]](%[[VAL_51]]) : (!fir.heap<!fir.array<10x!fir.logical<4>>>, !fir.shape<1>) -> !fir.array<10x!fir.logical<4>>
+  ! CHECK:         %[[VAL_53:.*]] = arith.constant 1 : index
+  ! CHECK:         %[[VAL_54:.*]] = arith.constant 0 : index
+  ! CHECK:         %[[VAL_55:.*]] = arith.subi %[[VAL_46]], %[[VAL_53]] : index
+  ! CHECK:         %[[VAL_56:.*]] = fir.do_loop %[[VAL_57:.*]] = %[[VAL_54]] to %[[VAL_55]] step %[[VAL_53]] unordered iter_args(%[[VAL_58:.*]] = %[[VAL_52]]) -> (!fir.array<10x!fir.logical<4>>) {
+  ! CHECK:           %[[VAL_59:.*]] = fir.array_fetch %[[VAL_48]], %[[VAL_57]] : (!fir.array<10xf32>, index) -> f32
+  ! CHECK:           %[[VAL_60:.*]] = arith.cmpf ogt, %[[VAL_59]], %[[VAL_49]] : f32
+  ! CHECK:           %[[VAL_61:.*]] = fir.convert %[[VAL_60]] : (i1) -> !fir.logical<4>
+  ! CHECK:           %[[VAL_62:.*]] = fir.array_update %[[VAL_58]], %[[VAL_61]], %[[VAL_57]] : (!fir.array<10x!fir.logical<4>>, !fir.logical<4>, index) -> !fir.array<10x!fir.logical<4>>
+  ! CHECK:           fir.result %[[VAL_62]] : !fir.array<10x!fir.logical<4>>
+  ! CHECK:         }
+  ! CHECK:         fir.array_merge_store %[[VAL_52]], %[[VAL_63:.*]] to %[[VAL_50]] : !fir.array<10x!fir.logical<4>>, !fir.array<10x!fir.logical<4>>, !fir.heap<!fir.array<10x!fir.logical<4>>>
+  ! CHECK:         %[[VAL_64:.*]] = fir.shape %[[VAL_46]] : (index) -> !fir.shape<1>
+  ! CHECK:         %[[VAL_65:.*]] = fir.shape %[[VAL_3]] : (index) -> !fir.shape<1>
+  ! CHECK:         %[[VAL_66:.*]] = fir.array_load %[[VAL_2]](%[[VAL_65]]) : (!fir.ref<!fir.array<10xf32>>, !fir.shape<1>) -> !fir.array<10xf32>
+  ! CHECK:         %[[VAL_67:.*]] = arith.constant 2.000000e+00 : f32
+  ! CHECK:         %[[VAL_68:.*]] = fir.shape %[[VAL_1]] : (index) -> !fir.shape<1>
+  ! CHECK:         %[[VAL_69:.*]] = fir.array_load %[[VAL_0]](%[[VAL_68]]) : (!fir.ref<!fir.array<10xf32>>, !fir.shape<1>) -> !fir.array<10xf32>
+  ! CHECK:         %[[VAL_70:.*]] = arith.constant 1 : index
+  ! CHECK:         %[[VAL_71:.*]] = arith.constant 0 : index
+  ! CHECK:         %[[VAL_72:.*]] = arith.subi %[[VAL_3]], %[[VAL_70]] : index
+  ! CHECK:         %[[VAL_73:.*]] = fir.do_loop %[[VAL_74:.*]] = %[[VAL_71]] to %[[VAL_72]] step %[[VAL_70]] unordered iter_args(%[[VAL_75:.*]] = %[[VAL_66]]) -> (!fir.array<10xf32>) {
+  ! CHECK:           %[[VAL_76:.*]] = arith.constant 1 : index
+  ! CHECK:           %[[VAL_77:.*]] = arith.addi %[[VAL_74]], %[[VAL_76]] : index
+  ! CHECK:           %[[VAL_78:.*]] = fir.array_coor %[[VAL_50]](%[[VAL_64]]) %[[VAL_77]] : (!fir.heap<!fir.array<10x!fir.logical<4>>>, !fir.shape<1>, index) -> !fir.ref<!fir.logical<4>>
+  ! CHECK:           %[[VAL_79:.*]] = fir.load %[[VAL_78]] : !fir.ref<!fir.logical<4>>
+  ! CHECK:           %[[VAL_80:.*]] = fir.convert %[[VAL_79]] : (!fir.logical<4>) -> i1
+  ! CHECK:           %[[VAL_81:.*]] = fir.if %[[VAL_80]] -> (!fir.array<10xf32>) {
+  ! CHECK:             %[[VAL_82:.*]] = fir.array_fetch %[[VAL_69]], %[[VAL_74]] : (!fir.array<10xf32>, index) -> f32
+  ! CHECK:             %[[VAL_83:.*]] = arith.mulf %[[VAL_67]], %[[VAL_82]] : f32
+  ! CHECK:             %[[VAL_84:.*]] = fir.array_update %[[VAL_75]], %[[VAL_83]], %[[VAL_74]] : (!fir.array<10xf32>, f32, index) -> !fir.array<10xf32>
+  ! CHECK:             fir.result %[[VAL_84]] : !fir.array<10xf32>
+  ! CHECK:           } else {
+  ! CHECK:             fir.result %[[VAL_75]] : !fir.array<10xf32>
+  ! CHECK:           }
+  ! CHECK:           fir.result %[[VAL_85:.*]] : !fir.array<10xf32>
+  ! CHECK:         }
+  ! CHECK:         fir.array_merge_store %[[VAL_66]], %[[VAL_86:.*]] to %[[VAL_2]] : !fir.array<10xf32>, !fir.array<10xf32>, !fir.ref<!fir.array<10xf32>>
+  ! CHECK:         %[[VAL_88:.*]] = arith.constant 10 : index
+  ! CHECK:         %[[VAL_89:.*]] = fir.shape %[[VAL_1]] : (index) -> !fir.shape<1>
+  ! CHECK:         %[[VAL_90:.*]] = fir.array_load %[[VAL_0]](%[[VAL_89]]) : (!fir.ref<!fir.array<10xf32>>, !fir.shape<1>) -> !fir.array<10xf32>
+  ! CHECK:         %[[VAL_91:.*]] = arith.constant 5.000000e+01 : f32
+  ! CHECK:         %[[VAL_92:.*]] = fir.allocmem !fir.array<10x!fir.logical<4>>
+  ! CHECK:         %[[VAL_93:.*]] = fir.shape %[[VAL_88]] : (index) -> !fir.shape<1>
+  ! CHECK:         %[[VAL_94:.*]] = fir.array_load %[[VAL_92]](%[[VAL_93]]) : (!fir.heap<!fir.array<10x!fir.logical<4>>>, !fir.shape<1>) -> !fir.array<10x!fir.logical<4>>
+  ! CHECK:         %[[VAL_95:.*]] = arith.constant 1 : index
+  ! CHECK:         %[[VAL_96:.*]] = arith.constant 0 : index
+  ! CHECK:         %[[VAL_97:.*]] = arith.subi %[[VAL_88]], %[[VAL_95]] : index
+  ! CHECK:         %[[VAL_98:.*]] = fir.do_loop %[[VAL_99:.*]] = %[[VAL_96]] to %[[VAL_97]] step %[[VAL_95]] unordered iter_args(%[[VAL_100:.*]] = %[[VAL_94]]) -> (!fir.array<10x!fir.logical<4>>) {
+  ! CHECK:           %[[VAL_101:.*]] = fir.array_fetch %[[VAL_90]], %[[VAL_99]] : (!fir.array<10xf32>, index) -> f32
+  ! CHECK:           %[[VAL_102:.*]] = arith.cmpf ogt, %[[VAL_101]], %[[VAL_91]] : f32
+  ! CHECK:           %[[VAL_103:.*]] = fir.convert %[[VAL_102]] : (i1) -> !fir.logical<4>
+  ! CHECK:           %[[VAL_104:.*]] = fir.array_update %[[VAL_100]], %[[VAL_103]], %[[VAL_99]] : (!fir.array<10x!fir.logical<4>>, !fir.logical<4>, index) -> !fir.array<10x!fir.logical<4>>
+  ! CHECK:           fir.result %[[VAL_104]] : !fir.array<10x!fir.logical<4>>
+  ! CHECK:         }
+  ! CHECK:         fir.array_merge_store %[[VAL_94]], %[[VAL_105:.*]] to %[[VAL_92]] : !fir.array<10x!fir.logical<4>>, !fir.array<10x!fir.logical<4>>, !fir.heap<!fir.array<10x!fir.logical<4>>>
+  ! CHECK:         %[[VAL_106:.*]] = fir.shape %[[VAL_88]] : (index) -> !fir.shape<1>
+  ! CHECK:         %[[VAL_107:.*]] = fir.shape %[[VAL_3]] : (index) -> !fir.shape<1>
+  ! CHECK:         %[[VAL_108:.*]] = fir.array_load %[[VAL_2]](%[[VAL_107]]) : (!fir.ref<!fir.array<10xf32>>, !fir.shape<1>) -> !fir.array<10xf32>
+  ! CHECK:         %[[VAL_109:.*]] = arith.constant 3.000000e+00 : f32
+  ! CHECK:         %[[VAL_110:.*]] = fir.shape %[[VAL_1]] : (index) -> !fir.shape<1>
+  ! CHECK:         %[[VAL_111:.*]] = fir.array_load %[[VAL_0]](%[[VAL_110]]) : (!fir.ref<!fir.array<10xf32>>, !fir.shape<1>) -> !fir.array<10xf32>
+  ! CHECK:         %[[VAL_112:.*]] = arith.constant 1 : index
+  ! CHECK:         %[[VAL_113:.*]] = arith.constant 0 : index
+  ! CHECK:         %[[VAL_114:.*]] = arith.subi %[[VAL_3]], %[[VAL_112]] : index
+  ! CHECK:         %[[VAL_115:.*]] = fir.do_loop %[[VAL_116:.*]] = %[[VAL_113]] to %[[VAL_114]] step %[[VAL_112]] unordered iter_args(%[[VAL_117:.*]] = %[[VAL_108]]) -> (!fir.array<10xf32>) {
+  ! CHECK:           %[[VAL_118:.*]] = arith.constant 1 : index
+  ! CHECK:           %[[VAL_119:.*]] = arith.addi %[[VAL_116]], %[[VAL_118]] : index
+  ! CHECK:           %[[VAL_120:.*]] = fir.array_coor %[[VAL_50]](%[[VAL_64]]) %[[VAL_119]] : (!fir.heap<!fir.array<10x!fir.logical<4>>>, !fir.shape<1>, index) -> !fir.ref<!fir.logical<4>>
+  ! CHECK:           %[[VAL_121:.*]] = fir.load %[[VAL_120]] : !fir.ref<!fir.logical<4>>
+  ! CHECK:           %[[VAL_122:.*]] = fir.convert %[[VAL_121]] : (!fir.logical<4>) -> i1
+  ! CHECK:           %[[VAL_123:.*]] = fir.if %[[VAL_122]] -> (!fir.array<10xf32>) {
+  ! CHECK:             fir.result %[[VAL_117]] : !fir.array<10xf32>
+  ! CHECK:           } else {
+  ! CHECK:             %[[VAL_124:.*]] = arith.constant 1 : index
+  ! CHECK:             %[[VAL_125:.*]] = arith.addi %[[VAL_116]], %[[VAL_124]] : index
+  ! CHECK:             %[[VAL_126:.*]] = fir.array_coor %[[VAL_92]](%[[VAL_106]]) %[[VAL_125]] : (!fir.heap<!fir.array<10x!fir.logical<4>>>, !fir.shape<1>, index) -> !fir.ref<!fir.logical<4>>
+  ! CHECK:             %[[VAL_127:.*]] = fir.load %[[VAL_126]] : !fir.ref<!fir.logical<4>>
+  ! CHECK:             %[[VAL_128:.*]] = fir.convert %[[VAL_127]] : (!fir.logical<4>) -> i1
+  ! CHECK:             %[[VAL_129:.*]] = fir.if %[[VAL_128]] -> (!fir.array<10xf32>) {
+  ! CHECK:               %[[VAL_130:.*]] = fir.array_fetch %[[VAL_111]], %[[VAL_116]] : (!fir.array<10xf32>, index) -> f32
+  ! CHECK:               %[[VAL_131:.*]] = arith.addf %[[VAL_109]], %[[VAL_130]] : f32
+  ! CHECK:               %[[VAL_132:.*]] = fir.array_update %[[VAL_117]], %[[VAL_131]], %[[VAL_116]] : (!fir.array<10xf32>, f32, index) -> !fir.array<10xf32>
+  ! CHECK:               fir.result %[[VAL_132]] : !fir.array<10xf32>
+  ! CHECK:             } else {
+  ! CHECK:               fir.result %[[VAL_117]] : !fir.array<10xf32>
+  ! CHECK:             }
+  ! CHECK:             fir.result %[[VAL_133:.*]] : !fir.array<10xf32>
+  ! CHECK:           }
+  ! CHECK:           fir.result %[[VAL_134:.*]] : !fir.array<10xf32>
+  ! CHECK:         }
+  ! CHECK:         fir.array_merge_store %[[VAL_108]], %[[VAL_135:.*]] to %[[VAL_2]] : !fir.array<10xf32>, !fir.array<10xf32>, !fir.ref<!fir.array<10xf32>>
+  ! CHECK:         %[[VAL_136:.*]] = fir.shape %[[VAL_1]] : (index) -> !fir.shape<1>
+  ! CHECK:         %[[VAL_137:.*]] = fir.array_load %[[VAL_0]](%[[VAL_136]]) : (!fir.ref<!fir.array<10xf32>>, !fir.shape<1>) -> !fir.array<10xf32>
+  ! CHECK:         %[[VAL_138:.*]] = fir.shape %[[VAL_1]] : (index) -> !fir.shape<1>
+  ! CHECK:         %[[VAL_139:.*]] = fir.array_load %[[VAL_0]](%[[VAL_138]]) : (!fir.ref<!fir.array<10xf32>>, !fir.shape<1>) -> !fir.array<10xf32>
+  ! CHECK:         %[[VAL_140:.*]] = arith.constant 1.000000e+00 : f32
+  ! CHECK:         %[[VAL_141:.*]] = arith.constant 1 : index
+  ! CHECK:         %[[VAL_142:.*]] = arith.constant 0 : index
+  ! CHECK:         %[[VAL_143:.*]] = arith.subi %[[VAL_1]], %[[VAL_141]] : index
+  ! CHECK:         %[[VAL_144:.*]] = fir.do_loop %[[VAL_145:.*]] = %[[VAL_142]] to %[[VAL_143]] step %[[VAL_141]] unordered iter_args(%[[VAL_146:.*]] = %[[VAL_137]]) -> (!fir.array<10xf32>) {
+  ! CHECK:           %[[VAL_147:.*]] = arith.constant 1 : index
+  ! CHECK:           %[[VAL_148:.*]] = arith.addi %[[VAL_145]], %[[VAL_147]] : index
+  ! CHECK:           %[[VAL_149:.*]] = fir.array_coor %[[VAL_50]](%[[VAL_64]]) %[[VAL_148]] : (!fir.heap<!fir.array<10x!fir.logical<4>>>, !fir.shape<1>, index) -> !fir.ref<!fir.logical<4>>
+  ! CHECK:           %[[VAL_150:.*]] = fir.load %[[VAL_149]] : !fir.ref<!fir.logical<4>>
+  ! CHECK:           %[[VAL_151:.*]] = fir.convert %[[VAL_150]] : (!fir.logical<4>) -> i1
+  ! CHECK:           %[[VAL_152:.*]] = fir.if %[[VAL_151]] -> (!fir.array<10xf32>) {
+  ! CHECK:             fir.result %[[VAL_146]] : !fir.array<10xf32>
+  ! CHECK:           } else {
+  ! CHECK:             %[[VAL_153:.*]] = arith.constant 1 : index
+  ! CHECK:             %[[VAL_154:.*]] = arith.addi %[[VAL_145]], %[[VAL_153]] : index
+  ! CHECK:             %[[VAL_155:.*]] = fir.array_coor %[[VAL_92]](%[[VAL_106]]) %[[VAL_154]] : (!fir.heap<!fir.array<10x!fir.logical<4>>>, !fir.shape<1>, index) -> !fir.ref<!fir.logical<4>>
+  ! CHECK:             %[[VAL_156:.*]] = fir.load %[[VAL_155]] : !fir.ref<!fir.logical<4>>
+  ! CHECK:             %[[VAL_157:.*]] = fir.convert %[[VAL_156]] : (!fir.logical<4>) -> i1
+  ! CHECK:             %[[VAL_158:.*]] = fir.if %[[VAL_157]] -> (!fir.array<10xf32>) {
+  ! CHECK:               %[[VAL_159:.*]] = fir.array_fetch %[[VAL_139]], %[[VAL_145]] : (!fir.array<10xf32>, index) -> f32
+  ! CHECK:               %[[VAL_160:.*]] = arith.subf %[[VAL_159]], %[[VAL_140]] : f32
+  ! CHECK:               %[[VAL_161:.*]] = fir.array_update %[[VAL_146]], %[[VAL_160]], %[[VAL_145]] : (!fir.array<10xf32>, f32, index) -> !fir.array<10xf32>
+  ! CHECK:               fir.result %[[VAL_161]] : !fir.array<10xf32>
+  ! CHECK:             } else {
+  ! CHECK:               fir.result %[[VAL_146]] : !fir.array<10xf32>
+  ! CHECK:             }
+  ! CHECK:             fir.result %[[VAL_162:.*]] : !fir.array<10xf32>
+  ! CHECK:           }
+  ! CHECK:           fir.result %[[VAL_163:.*]] : !fir.array<10xf32>
+  ! CHECK:         }
+  ! CHECK:         fir.array_merge_store %[[VAL_137]], %[[VAL_164:.*]] to %[[VAL_0]] : !fir.array<10xf32>, !fir.array<10xf32>, !fir.ref<!fir.array<10xf32>>
+  ! CHECK:         %[[VAL_165:.*]] = fir.shape %[[VAL_1]] : (index) -> !fir.shape<1>
+  ! CHECK:         %[[VAL_166:.*]] = fir.array_load %[[VAL_0]](%[[VAL_165]]) : (!fir.ref<!fir.array<10xf32>>, !fir.shape<1>) -> !fir.array<10xf32>
+  ! CHECK:         %[[VAL_167:.*]] = fir.shape %[[VAL_1]] : (index) -> !fir.shape<1>
+  ! CHECK:         %[[VAL_168:.*]] = fir.array_load %[[VAL_0]](%[[VAL_167]]) : (!fir.ref<!fir.array<10xf32>>, !fir.shape<1>) -> !fir.array<10xf32>
+  ! CHECK:         %[[VAL_169:.*]] = arith.constant 2.000000e+00 : f32
+  ! CHECK:         %[[VAL_170:.*]] = arith.constant 1 : index
+  ! CHECK:         %[[VAL_171:.*]] = arith.constant 0 : index
+  ! CHECK:         %[[VAL_172:.*]] = arith.subi %[[VAL_1]], %[[VAL_170]] : index
+  ! CHECK:         %[[VAL_173:.*]] = fir.do_loop %[[VAL_174:.*]] = %[[VAL_171]] to %[[VAL_172]] step %[[VAL_170]] unordered iter_args(%[[VAL_175:.*]] = %[[VAL_166]]) -> (!fir.array<10xf32>) {
+  ! CHECK:           %[[VAL_176:.*]] = arith.constant 1 : index
+  ! CHECK:           %[[VAL_177:.*]] = arith.addi %[[VAL_174]], %[[VAL_176]] : index
+  ! CHECK:           %[[VAL_178:.*]] = fir.array_coor %[[VAL_50]](%[[VAL_64]]) %[[VAL_177]] : (!fir.heap<!fir.array<10x!fir.logical<4>>>, !fir.shape<1>, index) -> !fir.ref<!fir.logical<4>>
+  ! CHECK:           %[[VAL_179:.*]] = fir.load %[[VAL_178]] : !fir.ref<!fir.logical<4>>
+  ! CHECK:           %[[VAL_180:.*]] = fir.convert %[[VAL_179]] : (!fir.logical<4>) -> i1
+  ! CHECK:           %[[VAL_181:.*]] = fir.if %[[VAL_180]] -> (!fir.array<10xf32>) {
+  ! CHECK:             fir.result %[[VAL_175]] : !fir.array<10xf32>
+  ! CHECK:           } else {
+  ! CHECK:             %[[VAL_182:.*]] = arith.constant 1 : index
+  ! CHECK:             %[[VAL_183:.*]] = arith.addi %[[VAL_174]], %[[VAL_182]] : index
+  ! CHECK:             %[[VAL_184:.*]] = fir.array_coor %[[VAL_92]](%[[VAL_106]]) %[[VAL_183]] : (!fir.heap<!fir.array<10x!fir.logical<4>>>, !fir.shape<1>, index) -> !fir.ref<!fir.logical<4>>
+  ! CHECK:             %[[VAL_185:.*]] = fir.load %[[VAL_184]] : !fir.ref<!fir.logical<4>>
+  ! CHECK:             %[[VAL_186:.*]] = fir.convert %[[VAL_185]] : (!fir.logical<4>) -> i1
+  ! CHECK:             %[[VAL_187:.*]] = fir.if %[[VAL_186]] -> (!fir.array<10xf32>) {
+  ! CHECK:               fir.result %[[VAL_175]] : !fir.array<10xf32>
+  ! CHECK:             } else {
+  ! CHECK:               %[[VAL_188:.*]] = fir.array_fetch %[[VAL_168]], %[[VAL_174]] : (!fir.array<10xf32>, index) -> f32
+  ! CHECK:               %[[VAL_189:.*]] = arith.divf %[[VAL_188]], %[[VAL_169]] : f32
+  ! CHECK:               %[[VAL_190:.*]] = fir.array_update %[[VAL_175]], %[[VAL_189]], %[[VAL_174]] : (!fir.array<10xf32>, f32, index) -> !fir.array<10xf32>
+  ! CHECK:               fir.result %[[VAL_190]] : !fir.array<10xf32>
+  ! CHECK:             }
+  ! CHECK:             fir.result %[[VAL_191:.*]] : !fir.array<10xf32>
+  ! CHECK:           }
+  ! CHECK:           fir.result %[[VAL_192:.*]] : !fir.array<10xf32>
+  ! CHECK:         }
+  ! CHECK:         fir.array_merge_store %[[VAL_166]], %[[VAL_193:.*]] to %[[VAL_0]] : !fir.array<10xf32>, !fir.array<10xf32>, !fir.ref<!fir.array<10xf32>>
+  ! CHECK:         fir.freemem %[[VAL_92]]
+  ! CHECK:         fir.freemem %[[VAL_50]]
+  ! CHECK:         return
+  ! CHECK:       }
+
+  real :: a(10), b(10)
+
+  ! Statement
+  where (a > 4.0) b = -a
+
+  ! Construct
+  where (a > 100.0)
+     b = 2.0 * a
+  elsewhere (a > 50.0)
+     b = 3.0 + a
+     a = a - 1.0
+  elsewhere
+     a = a / 2.0
+  end where
+end


        


More information about the flang-commits mailing list