[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