[flang-commits] [flang] 00f9c85 - [flang] Added fir.is_contiguous_box and fir.box_total_elements ops. (#131047)

via flang-commits flang-commits at lists.llvm.org
Fri Mar 14 08:25:08 PDT 2025


Author: Slava Zakharin
Date: 2025-03-14T08:25:05-07:00
New Revision: 00f9c855fb3b21fac62f70e22825a5599cfe8b8b

URL: https://github.com/llvm/llvm-project/commit/00f9c855fb3b21fac62f70e22825a5599cfe8b8b
DIFF: https://github.com/llvm/llvm-project/commit/00f9c855fb3b21fac62f70e22825a5599cfe8b8b.diff

LOG: [flang] Added fir.is_contiguous_box and fir.box_total_elements ops. (#131047)

These are helper operations to aid with expanding of fir.pack_array.

Added: 
    flang/lib/Optimizer/Transforms/SimplifyFIROperations.cpp
    flang/test/Fir/box-total-elements-fold.fir
    flang/test/Fir/is-contiguous-box-fold.fir
    flang/test/Transforms/simplify-fir-operations.fir

Modified: 
    flang/include/flang/Optimizer/Builder/Runtime/Inquiry.h
    flang/include/flang/Optimizer/Dialect/FIROps.td
    flang/include/flang/Optimizer/Transforms/Passes.h
    flang/include/flang/Optimizer/Transforms/Passes.td
    flang/include/flang/Runtime/support.h
    flang/lib/Optimizer/Builder/Runtime/Inquiry.cpp
    flang/lib/Optimizer/Dialect/FIROps.cpp
    flang/lib/Optimizer/Passes/Pipelines.cpp
    flang/lib/Optimizer/Transforms/CMakeLists.txt
    flang/test/Driver/bbc-mlir-pass-pipeline.f90
    flang/test/Driver/mlir-debug-pass-pipeline.f90
    flang/test/Driver/mlir-pass-pipeline.f90
    flang/test/Fir/basic-program.fir
    flang/test/Fir/fir-ops.fir
    flang/test/Fir/invalid.fir

Removed: 
    


################################################################################
diff  --git a/flang/include/flang/Optimizer/Builder/Runtime/Inquiry.h b/flang/include/flang/Optimizer/Builder/Runtime/Inquiry.h
index 3707273e0cbd4..9f90b64f40e48 100644
--- a/flang/include/flang/Optimizer/Builder/Runtime/Inquiry.h
+++ b/flang/include/flang/Optimizer/Builder/Runtime/Inquiry.h
@@ -50,9 +50,15 @@ mlir::Value genSize(fir::FirOpBuilder &builder, mlir::Location loc,
 mlir::Value genSizeDim(fir::FirOpBuilder &builder, mlir::Location loc,
                        mlir::Value array, mlir::Value dim);
 
-/// Generate call to `Is_contiguous` runtime routine.
+/// Generate call to `IsContiguous` runtime routine.
 mlir::Value genIsContiguous(fir::FirOpBuilder &builder, mlir::Location loc,
                             mlir::Value array);
 
+/// Generate call to `IsContiguousUpTo` runtime routine.
+/// \p dim specifies the dimension up to which contiguity
+/// needs to be checked (not exceeding the actual rank of the array).
+mlir::Value genIsContiguousUpTo(fir::FirOpBuilder &builder, mlir::Location loc,
+                                mlir::Value array, mlir::Value dim);
+
 } // namespace fir::runtime
 #endif // FORTRAN_OPTIMIZER_BUILDER_RUNTIME_INQUIRY_H

diff  --git a/flang/include/flang/Optimizer/Dialect/FIROps.td b/flang/include/flang/Optimizer/Dialect/FIROps.td
index 7147a2401baa7..ee9b959ba570f 100644
--- a/flang/include/flang/Optimizer/Dialect/FIROps.td
+++ b/flang/include/flang/Optimizer/Dialect/FIROps.td
@@ -3416,4 +3416,34 @@ def fir_UnpackArrayOp
   let hasVerifier = 1;
 }
 
+def fir_IsContiguousBoxOp : fir_Op<"is_contiguous_box", [NoMemoryEffect]> {
+  let summary = "Returns true if the boxed entity is contiguous";
+  let description = [{
+    Returns true iff the boxed entity is contiguous:
+      * in the leading dimension (if `innermost` attribute is set),
+      * in all dimensions (if `innermost` attribute is not set).
+
+    The input box cannot be absent.
+  }];
+  let arguments = (ins AnyBoxLike:$box, UnitAttr:$innermost);
+  let results = (outs I1);
+
+  let assemblyFormat = [{
+    $box (`innermost` $innermost^):(`whole`)? attr-dict `:` functional-type(operands, results)
+  }];
+  let hasCanonicalizer = 1;
+}
+
+def fir_BoxTotalElementsOp
+    : fir_SimpleOneResultOp<"box_total_elements", [NoMemoryEffect]> {
+  let summary = "Returns the boxed entity's total size in elements";
+  let description = [{
+    Returns the boxed entity's total size in elements.
+    The input box cannot be absent.
+  }];
+  let arguments = (ins AnyBoxLike:$box);
+  let results = (outs AnyIntegerLike);
+  let hasCanonicalizer = 1;
+}
+
 #endif

diff  --git a/flang/include/flang/Optimizer/Transforms/Passes.h b/flang/include/flang/Optimizer/Transforms/Passes.h
index afbbeb55632f1..406fedf220d26 100644
--- a/flang/include/flang/Optimizer/Transforms/Passes.h
+++ b/flang/include/flang/Optimizer/Transforms/Passes.h
@@ -62,6 +62,7 @@ namespace fir {
 #define GEN_PASS_DECL_COMPILERGENERATEDNAMESCONVERSION
 #define GEN_PASS_DECL_SETRUNTIMECALLATTRIBUTES
 #define GEN_PASS_DECL_GENRUNTIMECALLSFORTEST
+#define GEN_PASS_DECL_SIMPLIFYFIROPERATIONS
 
 #include "flang/Optimizer/Transforms/Passes.h.inc"
 
@@ -86,6 +87,9 @@ void populateCfgConversionRewrites(mlir::RewritePatternSet &patterns,
                                    bool forceLoopToExecuteOnce = false,
                                    bool setNSW = true);
 
+void populateSimplifyFIROperationsPatterns(mlir::RewritePatternSet &patterns,
+                                           bool preferInlineImplementation);
+
 // declarative passes
 #define GEN_PASS_REGISTRATION
 #include "flang/Optimizer/Transforms/Passes.h.inc"

diff  --git a/flang/include/flang/Optimizer/Transforms/Passes.td b/flang/include/flang/Optimizer/Transforms/Passes.td
index 64341b42bd1e4..e5c17cf7d8881 100644
--- a/flang/include/flang/Optimizer/Transforms/Passes.td
+++ b/flang/include/flang/Optimizer/Transforms/Passes.td
@@ -486,4 +486,18 @@ def GenRuntimeCallsForTest
   let dependentDialects = ["fir::FIROpsDialect", "mlir::func::FuncDialect"];
 }
 
+def SimplifyFIROperations : Pass<"simplify-fir-operations", "mlir::ModuleOp"> {
+  let summary = "Simplifies complex FIR operations";
+  let description = [{
+    Expands complex FIR operations into their equivalent using
+    FIR, SCF and other usual dialects. It may also generate calls
+    to Fortran runtime.
+  }];
+
+  let options = [Option<
+      "preferInlineImplementation", "prefer-inline-implementation", "bool",
+      /*default=*/"false",
+      "Prefer expanding without using Fortran runtime calls.">];
+}
+
 #endif // FLANG_OPTIMIZER_TRANSFORMS_PASSES

diff  --git a/flang/include/flang/Runtime/support.h b/flang/include/flang/Runtime/support.h
index 4a6d4357e8710..8a345bee7f867 100644
--- a/flang/include/flang/Runtime/support.h
+++ b/flang/include/flang/Runtime/support.h
@@ -34,6 +34,10 @@ extern "C" {
 // Predicate: is the storage described by a Descriptor contiguous in memory?
 bool RTDECL(IsContiguous)(const Descriptor &);
 
+// Predicate: is the storage described by a Descriptor contiguous in memory
+// up to the given dimension?
+bool RTDECL(IsContiguousUpTo)(const Descriptor &, int);
+
 // Predicate: is this descriptor describing an assumed-size array?
 bool RTDECL(IsAssumedSize)(const Descriptor &);
 

diff  --git a/flang/lib/Optimizer/Builder/Runtime/Inquiry.cpp b/flang/lib/Optimizer/Builder/Runtime/Inquiry.cpp
index f4d03c95ae518..718c3533564e8 100644
--- a/flang/lib/Optimizer/Builder/Runtime/Inquiry.cpp
+++ b/flang/lib/Optimizer/Builder/Runtime/Inquiry.cpp
@@ -91,7 +91,7 @@ mlir::Value fir::runtime::genSize(fir::FirOpBuilder &builder,
   return builder.create<fir::CallOp>(loc, sizeFunc, args).getResult(0);
 }
 
-/// Generate call to `Is_contiguous` runtime routine.
+/// Generate call to `IsContiguous` runtime routine.
 mlir::Value fir::runtime::genIsContiguous(fir::FirOpBuilder &builder,
                                           mlir::Location loc,
                                           mlir::Value array) {
@@ -102,6 +102,18 @@ mlir::Value fir::runtime::genIsContiguous(fir::FirOpBuilder &builder,
   return builder.create<fir::CallOp>(loc, isContiguousFunc, args).getResult(0);
 }
 
+/// Generate call to `IsContiguousUpTo` runtime routine.
+mlir::Value fir::runtime::genIsContiguousUpTo(fir::FirOpBuilder &builder,
+                                              mlir::Location loc,
+                                              mlir::Value array,
+                                              mlir::Value dim) {
+  mlir::func::FuncOp isContiguousFunc =
+      fir::runtime::getRuntimeFunc<mkRTKey(IsContiguousUpTo)>(loc, builder);
+  auto fTy = isContiguousFunc.getFunctionType();
+  auto args = fir::runtime::createArguments(builder, loc, fTy, array, dim);
+  return builder.create<fir::CallOp>(loc, isContiguousFunc, args).getResult(0);
+}
+
 void fir::runtime::genShape(fir::FirOpBuilder &builder, mlir::Location loc,
                             mlir::Value resultAddr, mlir::Value array,
                             mlir::Value kind) {

diff  --git a/flang/lib/Optimizer/Dialect/FIROps.cpp b/flang/lib/Optimizer/Dialect/FIROps.cpp
index 033d6453a619a..90202f3cee588 100644
--- a/flang/lib/Optimizer/Dialect/FIROps.cpp
+++ b/flang/lib/Optimizer/Dialect/FIROps.cpp
@@ -4671,6 +4671,83 @@ void fir::UnpackArrayOp::getEffects(
                          mlir::SideEffects::DefaultResource::get());
 }
 
+//===----------------------------------------------------------------------===//
+// IsContiguousBoxOp
+//===----------------------------------------------------------------------===//
+
+namespace {
+struct SimplifyIsContiguousBoxOp
+    : public mlir::OpRewritePattern<fir::IsContiguousBoxOp> {
+  using mlir::OpRewritePattern<fir::IsContiguousBoxOp>::OpRewritePattern;
+  mlir::LogicalResult
+  matchAndRewrite(fir::IsContiguousBoxOp op,
+                  mlir::PatternRewriter &rewriter) const override;
+};
+} // namespace
+
+mlir::LogicalResult SimplifyIsContiguousBoxOp::matchAndRewrite(
+    fir::IsContiguousBoxOp op, mlir::PatternRewriter &rewriter) const {
+  auto boxType = mlir::cast<fir::BaseBoxType>(op.getBox().getType());
+  // Nothing to do for assumed-rank arrays and !fir.box<none>.
+  if (boxType.isAssumedRank() || fir::isBoxNone(boxType))
+    return mlir::failure();
+
+  if (fir::getBoxRank(boxType) == 0) {
+    // Scalars are always contiguous.
+    mlir::Type i1Type = rewriter.getI1Type();
+    rewriter.replaceOpWithNewOp<mlir::arith::ConstantOp>(
+        op, i1Type, rewriter.getIntegerAttr(i1Type, 1));
+    return mlir::success();
+  }
+
+  // TODO: support more patterns, e.g. a result of fir.embox without
+  // the slice is contiguous. We can add fir::isSimplyContiguous(box)
+  // that walks def-use to figure it out.
+  return mlir::failure();
+}
+
+void fir::IsContiguousBoxOp::getCanonicalizationPatterns(
+    mlir::RewritePatternSet &patterns, mlir::MLIRContext *context) {
+  patterns.add<SimplifyIsContiguousBoxOp>(context);
+}
+
+//===----------------------------------------------------------------------===//
+// BoxTotalElementsOp
+//===----------------------------------------------------------------------===//
+
+namespace {
+struct SimplifyBoxTotalElementsOp
+    : public mlir::OpRewritePattern<fir::BoxTotalElementsOp> {
+  using mlir::OpRewritePattern<fir::BoxTotalElementsOp>::OpRewritePattern;
+  mlir::LogicalResult
+  matchAndRewrite(fir::BoxTotalElementsOp op,
+                  mlir::PatternRewriter &rewriter) const override;
+};
+} // namespace
+
+mlir::LogicalResult SimplifyBoxTotalElementsOp::matchAndRewrite(
+    fir::BoxTotalElementsOp op, mlir::PatternRewriter &rewriter) const {
+  auto boxType = mlir::cast<fir::BaseBoxType>(op.getBox().getType());
+  // Nothing to do for assumed-rank arrays and !fir.box<none>.
+  if (boxType.isAssumedRank() || fir::isBoxNone(boxType))
+    return mlir::failure();
+
+  if (fir::getBoxRank(boxType) == 0) {
+    // Scalar: 1 element.
+    rewriter.replaceOpWithNewOp<mlir::arith::ConstantOp>(
+        op, op.getType(), rewriter.getIntegerAttr(op.getType(), 1));
+    return mlir::success();
+  }
+
+  // TODO: support more cases, e.g. !fir.box<!fir.array<10xi32>>.
+  return mlir::failure();
+}
+
+void fir::BoxTotalElementsOp::getCanonicalizationPatterns(
+    mlir::RewritePatternSet &patterns, mlir::MLIRContext *context) {
+  patterns.add<SimplifyBoxTotalElementsOp>(context);
+}
+
 //===----------------------------------------------------------------------===//
 // FIROpsDialect
 //===----------------------------------------------------------------------===//

diff  --git a/flang/lib/Optimizer/Passes/Pipelines.cpp b/flang/lib/Optimizer/Passes/Pipelines.cpp
index 518be347fff85..3aea021e596f6 100644
--- a/flang/lib/Optimizer/Passes/Pipelines.cpp
+++ b/flang/lib/Optimizer/Passes/Pipelines.cpp
@@ -198,6 +198,11 @@ void createDefaultFIROptimizerPassPipeline(mlir::PassManager &pm,
   pm.addPass(fir::createPolymorphicOpConversion());
   pm.addPass(fir::createAssumedRankOpConversion());
 
+  // Expand FIR operations that may use SCF dialect for their
+  // implementation. This is a mandatory pass.
+  pm.addPass(fir::createSimplifyFIROperations(
+      {/*preferInlineImplementation=*/pc.OptLevel.isOptimizingForSpeed()}));
+
   if (pc.AliasAnalysis && !disableFirAliasTags && !useOldAliasTags)
     pm.addPass(fir::createAddAliasTags());
 

diff  --git a/flang/lib/Optimizer/Transforms/CMakeLists.txt b/flang/lib/Optimizer/Transforms/CMakeLists.txt
index da4fc9cb716be..6e8666759ab83 100644
--- a/flang/lib/Optimizer/Transforms/CMakeLists.txt
+++ b/flang/lib/Optimizer/Transforms/CMakeLists.txt
@@ -31,6 +31,7 @@ add_flang_library(FIRTransforms
   DebugTypeGenerator.cpp
   SetRuntimeCallAttributes.cpp
   GenRuntimeCallsForTest.cpp
+  SimplifyFIROperations.cpp
 
   DEPENDS
   CUFAttrs

diff  --git a/flang/lib/Optimizer/Transforms/SimplifyFIROperations.cpp b/flang/lib/Optimizer/Transforms/SimplifyFIROperations.cpp
new file mode 100644
index 0000000000000..e79d420c81c9c
--- /dev/null
+++ b/flang/lib/Optimizer/Transforms/SimplifyFIROperations.cpp
@@ -0,0 +1,145 @@
+//===- SimplifyFIROperations.cpp -- simplify complex FIR operations  ------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+//===----------------------------------------------------------------------===//
+/// \file
+/// This pass transforms some FIR operations into their equivalent
+/// implementations using other FIR operations. The transformation
+/// can legally use SCF dialect and generate Fortran runtime calls.
+//===----------------------------------------------------------------------===//
+
+#include "flang/Optimizer/Builder/FIRBuilder.h"
+#include "flang/Optimizer/Builder/Runtime/Inquiry.h"
+#include "flang/Optimizer/Builder/Todo.h"
+#include "flang/Optimizer/Dialect/FIROps.h"
+#include "flang/Optimizer/Transforms/Passes.h"
+#include "mlir/Pass/Pass.h"
+#include "mlir/Transforms/GreedyPatternRewriteDriver.h"
+
+namespace fir {
+#define GEN_PASS_DEF_SIMPLIFYFIROPERATIONS
+#include "flang/Optimizer/Transforms/Passes.h.inc"
+} // namespace fir
+
+#define DEBUG_TYPE "flang-simplify-fir-operations"
+
+namespace {
+/// Pass runner.
+class SimplifyFIROperationsPass
+    : public fir::impl::SimplifyFIROperationsBase<SimplifyFIROperationsPass> {
+public:
+  using fir::impl::SimplifyFIROperationsBase<
+      SimplifyFIROperationsPass>::SimplifyFIROperationsBase;
+
+  void runOnOperation() override final;
+};
+
+/// Base class for all conversions holding the pass options.
+template <typename Op>
+class ConversionBase : public mlir::OpRewritePattern<Op> {
+public:
+  using mlir::OpRewritePattern<Op>::OpRewritePattern;
+
+  template <typename... Args>
+  ConversionBase(mlir::MLIRContext *context, Args &&...args)
+      : mlir::OpRewritePattern<Op>(context),
+        options{std::forward<Args>(args)...} {}
+
+  mlir::LogicalResult matchAndRewrite(Op,
+                                      mlir::PatternRewriter &) const override;
+
+protected:
+  fir::SimplifyFIROperationsOptions options;
+};
+
+/// fir::IsContiguousBoxOp converter.
+using IsContiguousBoxCoversion = ConversionBase<fir::IsContiguousBoxOp>;
+
+/// fir::BoxTotalElementsOp converter.
+using BoxTotalElementsConversion = ConversionBase<fir::BoxTotalElementsOp>;
+} // namespace
+
+/// Generate a call to IsContiguous/IsContiguousUpTo function or an inline
+/// sequence reading extents/strides from the box and checking them.
+/// This conversion may produce fir.box_elesize and a loop (for assumed
+/// rank).
+template <>
+mlir::LogicalResult IsContiguousBoxCoversion::matchAndRewrite(
+    fir::IsContiguousBoxOp op, mlir::PatternRewriter &rewriter) const {
+  mlir::Location loc = op.getLoc();
+  fir::FirOpBuilder builder(rewriter, op.getOperation());
+  // TODO: support preferInlineImplementation.
+  bool doInline = options.preferInlineImplementation && false;
+  if (!doInline) {
+    // Generate Fortran runtime call.
+    mlir::Value result;
+    if (op.getInnermost()) {
+      mlir::Value one =
+          builder.createIntegerConstant(loc, builder.getI32Type(), 1);
+      result =
+          fir::runtime::genIsContiguousUpTo(builder, loc, op.getBox(), one);
+    } else {
+      result = fir::runtime::genIsContiguous(builder, loc, op.getBox());
+    }
+    result = builder.createConvert(loc, op.getType(), result);
+    rewriter.replaceOp(op, result);
+    return mlir::success();
+  }
+
+  // Generate inline implementation.
+  TODO(loc, "inline IsContiguousBoxOp");
+  return mlir::failure();
+}
+
+/// Generate a call to Size runtime function or an inline
+/// sequence reading extents from the box an multiplying them.
+/// This conversion may produce a loop (for assumed rank).
+template <>
+mlir::LogicalResult BoxTotalElementsConversion::matchAndRewrite(
+    fir::BoxTotalElementsOp op, mlir::PatternRewriter &rewriter) const {
+  mlir::Location loc = op.getLoc();
+  fir::FirOpBuilder builder(rewriter, op.getOperation());
+  // TODO: support preferInlineImplementation.
+  // Reading the extent from the box for 1D arrays probably
+  // results in less code than the call, so we can always
+  // inline it.
+  bool doInline = options.preferInlineImplementation && false;
+  if (!doInline) {
+    // Generate Fortran runtime call.
+    mlir::Value result = fir::runtime::genSize(builder, loc, op.getBox());
+    result = builder.createConvert(loc, op.getType(), result);
+    rewriter.replaceOp(op, result);
+    return mlir::success();
+  }
+
+  // Generate inline implementation.
+  TODO(loc, "inline BoxTotalElementsOp");
+  return mlir::failure();
+}
+
+void SimplifyFIROperationsPass::runOnOperation() {
+  mlir::ModuleOp module = getOperation();
+  mlir::MLIRContext &context = getContext();
+  mlir::RewritePatternSet patterns(&context);
+  fir::populateSimplifyFIROperationsPatterns(patterns,
+                                             preferInlineImplementation);
+  mlir::GreedyRewriteConfig config;
+  config.enableRegionSimplification = mlir::GreedySimplifyRegionLevel::Disabled;
+
+  if (mlir::failed(
+          mlir::applyPatternsGreedily(module, std::move(patterns), config))) {
+    mlir::emitError(module.getLoc(), DEBUG_TYPE " pass failed");
+    signalPassFailure();
+  }
+}
+
+void fir::populateSimplifyFIROperationsPatterns(
+    mlir::RewritePatternSet &patterns, bool preferInlineImplementation) {
+  patterns.insert<IsContiguousBoxCoversion, BoxTotalElementsConversion>(
+      patterns.getContext(), preferInlineImplementation);
+}

diff  --git a/flang/test/Driver/bbc-mlir-pass-pipeline.f90 b/flang/test/Driver/bbc-mlir-pass-pipeline.f90
index 5520d750e2ce1..276ef818622a1 100644
--- a/flang/test/Driver/bbc-mlir-pass-pipeline.f90
+++ b/flang/test/Driver/bbc-mlir-pass-pipeline.f90
@@ -47,6 +47,7 @@
 
 ! CHECK-NEXT: PolymorphicOpConversion
 ! CHECK-NEXT: AssumedRankOpConversion
+! CHECK-NEXT: SimplifyFIROperations
 
 ! CHECK-NEXT: Pipeline Collection : ['fir.global', 'func.func', 'omp.declare_reduction', 'omp.private']
 ! CHECK-NEXT: 'fir.global' Pipeline

diff  --git a/flang/test/Driver/mlir-debug-pass-pipeline.f90 b/flang/test/Driver/mlir-debug-pass-pipeline.f90
index edc6f59b0ad7c..70fa0cf5ae47c 100644
--- a/flang/test/Driver/mlir-debug-pass-pipeline.f90
+++ b/flang/test/Driver/mlir-debug-pass-pipeline.f90
@@ -77,6 +77,7 @@
 
 ! ALL-NEXT: PolymorphicOpConversion
 ! ALL-NEXT: AssumedRankOpConversion
+! ALL-NEXT: SimplifyFIROperations
 
 ! ALL-NEXT: Pipeline Collection : ['fir.global', 'func.func', 'omp.declare_reduction', 'omp.private']
 ! ALL-NEXT:   'fir.global' Pipeline

diff  --git a/flang/test/Driver/mlir-pass-pipeline.f90 b/flang/test/Driver/mlir-pass-pipeline.f90
index 740897786dd37..852764be1f136 100644
--- a/flang/test/Driver/mlir-pass-pipeline.f90
+++ b/flang/test/Driver/mlir-pass-pipeline.f90
@@ -101,6 +101,7 @@
 
 ! ALL-NEXT: PolymorphicOpConversion
 ! ALL-NEXT: AssumedRankOpConversion
+! ALL-NEXT: SimplifyFIROperations
 ! O2-NEXT:  AddAliasTags
 
 ! ALL-NEXT: Pipeline Collection : ['fir.global', 'func.func', 'omp.declare_reduction', 'omp.private']

diff  --git a/flang/test/Fir/basic-program.fir b/flang/test/Fir/basic-program.fir
index 8b299674208fa..90bff80da1915 100644
--- a/flang/test/Fir/basic-program.fir
+++ b/flang/test/Fir/basic-program.fir
@@ -99,6 +99,7 @@ func.func @_QQmain() {
 
 // PASSES-NEXT: PolymorphicOpConversion
 // PASSES-NEXT: AssumedRankOpConversion
+// PASSES-NEXT: SimplifyFIROperations
 // PASSES-NEXT: AddAliasTags
 
 // PASSES-NEXT: Pipeline Collection : ['fir.global', 'func.func', 'omp.declare_reduction', 'omp.private']

diff  --git a/flang/test/Fir/box-total-elements-fold.fir b/flang/test/Fir/box-total-elements-fold.fir
new file mode 100644
index 0000000000000..2df807dc27183
--- /dev/null
+++ b/flang/test/Fir/box-total-elements-fold.fir
@@ -0,0 +1,45 @@
+// RUN: fir-opt --canonicalize %s | FileCheck %s
+
+// No folding for !fir.box<none>.
+func.func @test_none(%arg0: !fir.box<none>) -> index {
+  %0 = fir.box_total_elements %arg0 : (!fir.box<none>) -> index
+  return %0 : index
+}
+// CHECK-LABEL:   func.func @test_none(
+// CHECK-SAME:                         %[[VAL_0:[0-9]+|[a-zA-Z$._-][a-zA-Z0-9$._-]*]]: !fir.box<none>) -> index {
+// CHECK:           %[[VAL_1:.*]] = fir.box_total_elements %[[VAL_0]] : (!fir.box<none>) -> index
+// CHECK:           return %[[VAL_1]] : index
+// CHECK:         }
+
+// No folding for assumed rank arrays.
+func.func @test_assumed_rank(%arg0: !fir.box<!fir.array<*:f32>>) -> index {
+  %0 = fir.box_total_elements %arg0 : (!fir.box<!fir.array<*:f32>>) -> index
+  return %0 : index
+}
+// CHECK-LABEL:   func.func @test_assumed_rank(
+// CHECK-SAME:                                 %[[VAL_0:[0-9]+|[a-zA-Z$._-][a-zA-Z0-9$._-]*]]: !fir.box<!fir.array<*:f32>>) -> index {
+// CHECK:           %[[VAL_1:.*]] = fir.box_total_elements %[[VAL_0]] : (!fir.box<!fir.array<*:f32>>) -> index
+// CHECK:           return %[[VAL_1]] : index
+// CHECK:         }
+
+// Scalar boxes hold one element.
+func.func @test_scalar_i32(%arg0: !fir.class<!fir.type<sometype{i:i32}>>) -> i32 {
+  %0 = fir.box_total_elements %arg0 : (!fir.class<!fir.type<sometype{i:i32}>>) -> i32
+  return %0 : i32
+}
+// CHECK-LABEL:   func.func @test_scalar_i32(
+// CHECK-SAME:                               %[[VAL_0:[0-9]+|[a-zA-Z$._-][a-zA-Z0-9$._-]*]]: !fir.class<!fir.type<sometype{i:i32}>>) -> i32 {
+// CHECK:           %[[VAL_1:.*]] = arith.constant 1 : i32
+// CHECK:           return %[[VAL_1]] : i32
+// CHECK:         }
+
+// Scalar boxes hold one element.
+func.func @test_scalar_index(%arg0: !fir.class<!fir.type<sometype{i:i32}>>) -> index {
+  %0 = fir.box_total_elements %arg0 : (!fir.class<!fir.type<sometype{i:i32}>>) -> index
+  return %0 : index
+}
+// CHECK-LABEL:   func.func @test_scalar_index(
+// CHECK-SAME:                                 %[[VAL_0:[0-9]+|[a-zA-Z$._-][a-zA-Z0-9$._-]*]]: !fir.class<!fir.type<sometype{i:i32}>>) -> index {
+// CHECK:           %[[VAL_1:.*]] = arith.constant 1 : index
+// CHECK:           return %[[VAL_1]] : index
+// CHECK:         }

diff  --git a/flang/test/Fir/fir-ops.fir b/flang/test/Fir/fir-ops.fir
index c5bcba9103db1..9cd02dce626ab 100644
--- a/flang/test/Fir/fir-ops.fir
+++ b/flang/test/Fir/fir-ops.fir
@@ -967,3 +967,40 @@ func.func @test_pack_unpack_array(%arg0: !fir.ref<!fir.box<none>>, %arg1: !fir.b
   fir.unpack_array %6 to %arg1 stack no_copy : !fir.box<!fir.array<?xi32>>
   return
 }
+
+// CHECK-LABEL:   func.func @test_is_contiguous_box(
+func.func @test_is_contiguous_box(%arg0: !fir.class<!fir.type<sometype{i:i32}>>, %arg1: !fir.box<!fir.array<?x?xf32>>, %arg2: !fir.box<i32>) -> i1 {
+// CHECK:           fir.is_contiguous_box %{{.*}} innermost : (!fir.class<!fir.type<sometype{i:i32}>>) -> i1
+  %0 = fir.is_contiguous_box %arg0 innermost : (!fir.class<!fir.type<sometype{i:i32}>>) -> i1
+// CHECK:           fir.is_contiguous_box %{{.*}} whole : (!fir.class<!fir.type<sometype{i:i32}>>) -> i1
+  %1 = fir.is_contiguous_box %arg0 whole : (!fir.class<!fir.type<sometype{i:i32}>>) -> i1
+// CHECK:           fir.is_contiguous_box %{{.*}} innermost : (!fir.box<!fir.array<?x?xf32>>) -> i1
+  %2 = fir.is_contiguous_box %arg1 innermost : (!fir.box<!fir.array<?x?xf32>>) -> i1
+// CHECK:           fir.is_contiguous_box %{{.*}} whole : (!fir.box<!fir.array<?x?xf32>>) -> i1
+  %3 = fir.is_contiguous_box %arg1 whole : (!fir.box<!fir.array<?x?xf32>>) -> i1
+// CHECK:           fir.is_contiguous_box %{{.*}} innermost : (!fir.box<i32>) -> i1
+  %4 = fir.is_contiguous_box %arg2 innermost : (!fir.box<i32>) -> i1
+// CHECK:           fir.is_contiguous_box %{{.*}} whole : (!fir.box<i32>) -> i1
+  %5 = fir.is_contiguous_box %arg2 whole : (!fir.box<i32>) -> i1
+  %6 = arith.andi %0, %1 : i1
+  %7 = arith.andi %2, %3 : i1
+  %8 = arith.andi %4, %5 : i1
+  %9 = arith.andi %6, %7 : i1
+  %10 = arith.andi %8, %9 : i1
+  return %10 : i1
+}
+
+// CHECK-LABEL:   func.func @test_box_total_elements(
+func.func @test_box_total_elements(%arg0: !fir.class<!fir.type<sometype{i:i32}>>, %arg1: !fir.box<!fir.array<?x?xf32>>, %arg2: !fir.box<i32>) -> index {
+// CHECK:           fir.box_total_elements %{{.*}} : (!fir.class<!fir.type<sometype{i:i32}>>) -> i16
+  %0 = fir.box_total_elements %arg0 : (!fir.class<!fir.type<sometype{i:i32}>>) -> i16
+// CHECK:           fir.box_total_elements %{{.*}} : (!fir.box<!fir.array<?x?xf32>>) -> i32
+  %1 = fir.box_total_elements %arg1 : (!fir.box<!fir.array<?x?xf32>>) -> i32
+// CHECK:           fir.box_total_elements %{{.*}} : (!fir.box<i32>) -> index
+  %2 = fir.box_total_elements %arg2 : (!fir.box<i32>) -> index
+  %3 = fir.convert %0 : (i16) -> index
+  %4 = fir.convert %1 : (i32) -> index
+  %5 = arith.addi %3, %4 : index
+  %6 = arith.addi %2, %5 : index
+  return %6 : index
+}

diff  --git a/flang/test/Fir/invalid.fir b/flang/test/Fir/invalid.fir
index 1876f8c7d965e..d5db644eeddb2 100644
--- a/flang/test/Fir/invalid.fir
+++ b/flang/test/Fir/invalid.fir
@@ -1146,3 +1146,19 @@ func.func @bad_unpack_array4(%arg0: !fir.box<!fir.array<?xf32>>, %arg1: !fir.box
   fir.unpack_array %arg0 to %arg1 stack : !fir.box<!fir.array<?xi32>>
   return
 }
+
+// -----
+
+func.func @bad_is_contiguous_box(%arg0: !fir.ref<!fir.box<!fir.array<?xi32>>>) -> i1 {
+  // expected-error at +1{{op operand #0 must be any box, but got '!fir.ref<!fir.box<!fir.array<?xi32>>>'}}
+  %0 = fir.is_contiguous_box %arg0 whole : (!fir.ref<!fir.box<!fir.array<?xi32>>>) -> i1
+  return %0 : i1
+}
+
+// -----
+
+func.func @bad_box_total_elements(%arg0: !fir.ref<!fir.box<!fir.array<?xi32>>>) -> i32 {
+  // expected-error at +1{{op operand #0 must be any box, but got '!fir.ref<!fir.box<!fir.array<?xi32>>>'}}
+  %0 = fir.box_total_elements %arg0 : (!fir.ref<!fir.box<!fir.array<?xi32>>>) -> i32
+  return %0 : i32
+}

diff  --git a/flang/test/Fir/is-contiguous-box-fold.fir b/flang/test/Fir/is-contiguous-box-fold.fir
new file mode 100644
index 0000000000000..1da4f632d1a4c
--- /dev/null
+++ b/flang/test/Fir/is-contiguous-box-fold.fir
@@ -0,0 +1,34 @@
+// RUN: fir-opt --canonicalize %s | FileCheck %s
+
+// No folding for !fir.box<none>.
+func.func @test_none(%arg0: !fir.box<none>) -> i1 {
+  %0 = fir.is_contiguous_box %arg0 whole : (!fir.box<none>) -> i1
+  return %0 : i1
+}
+// CHECK-LABEL:   func.func @test_none(
+// CHECK-SAME:                         %[[VAL_0:[0-9]+|[a-zA-Z$._-][a-zA-Z0-9$._-]*]]: !fir.box<none>) -> i1 {
+// CHECK:           %[[VAL_1:.*]] = fir.is_contiguous_box %[[VAL_0]] whole : (!fir.box<none>) -> i1
+// CHECK:           return %[[VAL_1]] : i1
+// CHECK:         }
+
+// No folding for assumed rank arrays.
+func.func @test_assumed_rank(%arg0: !fir.box<!fir.array<*:f32>>) -> i1 {
+  %0 = fir.is_contiguous_box %arg0 innermost : (!fir.box<!fir.array<*:f32>>) -> i1
+  return %0 : i1
+}
+// CHECK-LABEL:   func.func @test_assumed_rank(
+// CHECK-SAME:                                 %[[VAL_0:[0-9]+|[a-zA-Z$._-][a-zA-Z0-9$._-]*]]: !fir.box<!fir.array<*:f32>>) -> i1 {
+// CHECK:           %[[VAL_1:.*]] = fir.is_contiguous_box %[[VAL_0]] innermost : (!fir.box<!fir.array<*:f32>>) -> i1
+// CHECK:           return %[[VAL_1]] : i1
+// CHECK:         }
+
+// Scalars are always contiguous.
+func.func @test_scalar(%arg0: !fir.class<!fir.type<sometype{i:i32}>>) -> i1 {
+  %0 = fir.is_contiguous_box %arg0 whole : (!fir.class<!fir.type<sometype{i:i32}>>) -> i1
+  return %0 : i1
+}
+// CHECK-LABEL:   func.func @test_scalar(
+// CHECK-SAME:                           %[[VAL_0:[0-9]+|[a-zA-Z$._-][a-zA-Z0-9$._-]*]]: !fir.class<!fir.type<sometype{i:i32}>>) -> i1 {
+// CHECK:           %[[VAL_1:.*]] = arith.constant true
+// CHECK:           return %[[VAL_1]] : i1
+// CHECK:         }

diff  --git a/flang/test/Transforms/simplify-fir-operations.fir b/flang/test/Transforms/simplify-fir-operations.fir
new file mode 100644
index 0000000000000..f712efde846ad
--- /dev/null
+++ b/flang/test/Transforms/simplify-fir-operations.fir
@@ -0,0 +1,130 @@
+// RUN: fir-opt --split-input-file --simplify-fir-operations %s | FileCheck %s
+
+// -----
+
+func.func @test_none_innermost(%arg0: !fir.box<none>) -> i1 {
+  %0 = fir.is_contiguous_box %arg0 innermost : (!fir.box<none>) -> i1
+  return %0 : i1
+}
+// CHECK-LABEL:   func.func @test_none_innermost(
+// CHECK-SAME:                                   %[[VAL_0:[0-9]+|[a-zA-Z$._-][a-zA-Z0-9$._-]*]]: !fir.box<none>) -> i1 {
+// CHECK:           %[[VAL_1:.*]] = arith.constant 1 : i32
+// CHECK:           %[[VAL_2:.*]] = fir.call @_FortranAIsContiguousUpTo(%[[VAL_0]], %[[VAL_1]]) : (!fir.box<none>, i32) -> i1
+// CHECK:           return %[[VAL_2]] : i1
+// CHECK:         }
+
+// -----
+
+func.func @test_none_whole(%arg0: !fir.box<none>) -> i1 {
+  %0 = fir.is_contiguous_box %arg0 whole : (!fir.box<none>) -> i1
+  return %0 : i1
+}
+// CHECK-LABEL:   func.func @test_none_whole(
+// CHECK-SAME:                               %[[VAL_0:[0-9]+|[a-zA-Z$._-][a-zA-Z0-9$._-]*]]: !fir.box<none>) -> i1 {
+// CHECK:           %[[VAL_1:.*]] = fir.call @_FortranAIsContiguous(%[[VAL_0]]) : (!fir.box<none>) -> i1
+// CHECK:           return %[[VAL_1]] : i1
+// CHECK:         }
+
+// -----
+
+func.func @test_array_innermost(%arg0: !fir.box<!fir.array<?xf32>>) -> i1 {
+  %0 = fir.is_contiguous_box %arg0 innermost : (!fir.box<!fir.array<?xf32>>) -> i1
+  return %0 : i1
+}
+// CHECK-LABEL:   func.func @test_array_innermost(
+// CHECK-SAME:                                    %[[VAL_0:[0-9]+|[a-zA-Z$._-][a-zA-Z0-9$._-]*]]: !fir.box<!fir.array<?xf32>>) -> i1 {
+// CHECK:           %[[VAL_1:.*]] = arith.constant 1 : i32
+// CHECK:           %[[VAL_2:.*]] = fir.convert %[[VAL_0]] : (!fir.box<!fir.array<?xf32>>) -> !fir.box<none>
+// CHECK:           %[[VAL_3:.*]] = fir.call @_FortranAIsContiguousUpTo(%[[VAL_2]], %[[VAL_1]]) : (!fir.box<none>, i32) -> i1
+// CHECK:           return %[[VAL_3]] : i1
+// CHECK:         }
+
+// -----
+
+func.func @test_array_whole(%arg0: !fir.box<!fir.array<?xf32>>) -> i1 {
+  %0 = fir.is_contiguous_box %arg0 whole : (!fir.box<!fir.array<?xf32>>) -> i1
+  return %0 : i1
+}
+// CHECK-LABEL:   func.func @test_array_whole(
+// CHECK-SAME:                                %[[VAL_0:[0-9]+|[a-zA-Z$._-][a-zA-Z0-9$._-]*]]: !fir.box<!fir.array<?xf32>>) -> i1 {
+// CHECK:           %[[VAL_1:.*]] = fir.convert %[[VAL_0]] : (!fir.box<!fir.array<?xf32>>) -> !fir.box<none>
+// CHECK:           %[[VAL_2:.*]] = fir.call @_FortranAIsContiguous(%[[VAL_1]]) : (!fir.box<none>) -> i1
+// CHECK:           return %[[VAL_2]] : i1
+// CHECK:         }
+
+// -----
+
+func.func @test_assumed_rank_innermost(%arg0: !fir.box<!fir.array<*:f32>>) -> i1 {
+  %0 = fir.is_contiguous_box %arg0 innermost : (!fir.box<!fir.array<*:f32>>) -> i1
+  return %0 : i1
+}
+// CHECK-LABEL:   func.func @test_assumed_rank_innermost(
+// CHECK-SAME:                                           %[[VAL_0:[0-9]+|[a-zA-Z$._-][a-zA-Z0-9$._-]*]]: !fir.box<!fir.array<*:f32>>) -> i1 {
+// CHECK:           %[[VAL_1:.*]] = arith.constant 1 : i32
+// CHECK:           %[[VAL_2:.*]] = fir.convert %[[VAL_0]] : (!fir.box<!fir.array<*:f32>>) -> !fir.box<none>
+// CHECK:           %[[VAL_3:.*]] = fir.call @_FortranAIsContiguousUpTo(%[[VAL_2]], %[[VAL_1]]) : (!fir.box<none>, i32) -> i1
+// CHECK:           return %[[VAL_3]] : i1
+// CHECK:         }
+
+// -----
+
+func.func @test_assumed_rank_whole(%arg0: !fir.box<!fir.array<*:f32>>) -> i1 {
+  %0 = fir.is_contiguous_box %arg0 whole : (!fir.box<!fir.array<*:f32>>) -> i1
+  return %0 : i1
+}
+// CHECK-LABEL:   func.func @test_assumed_rank_whole(
+// CHECK-SAME:                                       %[[VAL_0:[0-9]+|[a-zA-Z$._-][a-zA-Z0-9$._-]*]]: !fir.box<!fir.array<*:f32>>) -> i1 {
+// CHECK:           %[[VAL_1:.*]] = fir.convert %[[VAL_0]] : (!fir.box<!fir.array<*:f32>>) -> !fir.box<none>
+// CHECK:           %[[VAL_2:.*]] = fir.call @_FortranAIsContiguous(%[[VAL_1]]) : (!fir.box<none>) -> i1
+// CHECK:           return %[[VAL_2]] : i1
+// CHECK:         }
+
+// -----
+
+func.func @test_none(%arg0: !fir.box<none>) -> i16 {
+  %0 = fir.box_total_elements %arg0 : (!fir.box<none>) -> i16
+  return %0 : i16
+}
+// CHECK-LABEL:   func.func @test_none(
+// CHECK-SAME:                         %[[VAL_0:[0-9]+|[a-zA-Z$._-][a-zA-Z0-9$._-]*]]: !fir.box<none>) -> i16 {
+// CHECK:           %[[VAL_3:.*]] = arith.constant {{.*}} : i32
+// CHECK:           %[[VAL_1:.*]] = fir.address_of(@{{.*}}) : !fir.ref<!fir.char<1,{{.*}}>>
+// CHECK:           %[[VAL_4:.*]] = fir.convert %[[VAL_1]] : (!fir.ref<!fir.char<1,{{.*}}>>) -> !fir.ref<i8>
+// CHECK:           %[[VAL_5:.*]] = fir.call @_FortranASize(%[[VAL_0]], %[[VAL_4]], %[[VAL_3]]) : (!fir.box<none>, !fir.ref<i8>, i32) -> i64
+// CHECK:           %[[VAL_6:.*]] = fir.convert %[[VAL_5]] : (i64) -> i16
+// CHECK:           return %[[VAL_6]] : i16
+// CHECK:         }
+
+// -----
+
+func.func @test_array(%arg0: !fir.box<!fir.array<?x?xf32>>) -> i32 {
+  %0 = fir.box_total_elements %arg0 : (!fir.box<!fir.array<?x?xf32>>) -> i32
+  return %0 : i32
+}
+// CHECK-LABEL:   func.func @test_array(
+// CHECK-SAME:                          %[[VAL_0:[0-9]+|[a-zA-Z$._-][a-zA-Z0-9$._-]*]]: !fir.box<!fir.array<?x?xf32>>) -> i32 {
+// CHECK:           %[[VAL_3:.*]] = arith.constant {{.*}} : i32
+// CHECK:           %[[VAL_1:.*]] = fir.address_of({{.*}}) : !fir.ref<!fir.char<1,{{.*}}>>
+// CHECK:           %[[VAL_4:.*]] = fir.convert %[[VAL_0]] : (!fir.box<!fir.array<?x?xf32>>) -> !fir.box<none>
+// CHECK:           %[[VAL_5:.*]] = fir.convert %[[VAL_1]] : (!fir.ref<!fir.char<1,{{.*}}>>) -> !fir.ref<i8>
+// CHECK:           %[[VAL_6:.*]] = fir.call @_FortranASize(%[[VAL_4]], %[[VAL_5]], %[[VAL_3]]) : (!fir.box<none>, !fir.ref<i8>, i32) -> i64
+// CHECK:           %[[VAL_7:.*]] = fir.convert %[[VAL_6]] : (i64) -> i32
+// CHECK:           return %[[VAL_7]] : i32
+// CHECK:         }
+
+// -----
+
+func.func @test_assumed_rank(%arg0: !fir.box<!fir.array<*:f32>>) -> index {
+  %0 = fir.box_total_elements %arg0 : (!fir.box<!fir.array<*:f32>>) -> index
+  return %0 : index
+}
+// CHECK-LABEL:   func.func @test_assumed_rank(
+// CHECK-SAME:                                 %[[VAL_0:[0-9]+|[a-zA-Z$._-][a-zA-Z0-9$._-]*]]: !fir.box<!fir.array<*:f32>>) -> index {
+// CHECK:           %[[VAL_3:.*]] = arith.constant {{.*}} : i32
+// CHECK:           %[[VAL_1:.*]] = fir.address_of({{.*}}) : !fir.ref<!fir.char<1,{{.*}}>>
+// CHECK:           %[[VAL_4:.*]] = fir.convert %[[VAL_0]] : (!fir.box<!fir.array<*:f32>>) -> !fir.box<none>
+// CHECK:           %[[VAL_5:.*]] = fir.convert %[[VAL_1]] : (!fir.ref<!fir.char<1,{{.*}}>>) -> !fir.ref<i8>
+// CHECK:           %[[VAL_6:.*]] = fir.call @_FortranASize(%[[VAL_4]], %[[VAL_5]], %[[VAL_3]]) : (!fir.box<none>, !fir.ref<i8>, i32) -> i64
+// CHECK:           %[[VAL_7:.*]] = fir.convert %[[VAL_6]] : (i64) -> index
+// CHECK:           return %[[VAL_7]] : index
+// CHECK:         }


        


More information about the flang-commits mailing list