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(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(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(loc, builder); + auto fTy = isContiguousFunc.getFunctionType(); + auto args = fir::runtime::createArguments(builder, loc, fTy, array, dim); + return builder.create(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 { + using mlir::OpRewritePattern::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(op.getBox().getType()); + // Nothing to do for assumed-rank arrays and !fir.box. + 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( + 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(context); +} + +//===----------------------------------------------------------------------===// +// BoxTotalElementsOp +//===----------------------------------------------------------------------===// + +namespace { +struct SimplifyBoxTotalElementsOp + : public mlir::OpRewritePattern { + using mlir::OpRewritePattern::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(op.getBox().getType()); + // Nothing to do for assumed-rank arrays and !fir.box. + if (boxType.isAssumedRank() || fir::isBoxNone(boxType)) + return mlir::failure(); + + if (fir::getBoxRank(boxType) == 0) { + // Scalar: 1 element. + rewriter.replaceOpWithNewOp( + op, op.getType(), rewriter.getIntegerAttr(op.getType(), 1)); + return mlir::success(); + } + + // TODO: support more cases, e.g. !fir.box>. + return mlir::failure(); +} + +void fir::BoxTotalElementsOp::getCanonicalizationPatterns( + mlir::RewritePatternSet &patterns, mlir::MLIRContext *context) { + patterns.add(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 { +public: + using fir::impl::SimplifyFIROperationsBase< + SimplifyFIROperationsPass>::SimplifyFIROperationsBase; + + void runOnOperation() override final; +}; + +/// Base class for all conversions holding the pass options. +template +class ConversionBase : public mlir::OpRewritePattern { +public: + using mlir::OpRewritePattern::OpRewritePattern; + + template + ConversionBase(mlir::MLIRContext *context, Args &&...args) + : mlir::OpRewritePattern(context), + options{std::forward(args)...} {} + + mlir::LogicalResult matchAndRewrite(Op, + mlir::PatternRewriter &) const override; + +protected: + fir::SimplifyFIROperationsOptions options; +}; + +/// fir::IsContiguousBoxOp converter. +using IsContiguousBoxCoversion = ConversionBase; + +/// fir::BoxTotalElementsOp converter. +using BoxTotalElementsConversion = ConversionBase; +} // 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( + 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. +func.func @test_none(%arg0: !fir.box) -> index { + %0 = fir.box_total_elements %arg0 : (!fir.box) -> 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) -> index { +// CHECK: %[[VAL_1:.*]] = fir.box_total_elements %[[VAL_0]] : (!fir.box) -> index +// CHECK: return %[[VAL_1]] : index +// CHECK: } + +// No folding for assumed rank arrays. +func.func @test_assumed_rank(%arg0: !fir.box>) -> index { + %0 = fir.box_total_elements %arg0 : (!fir.box>) -> 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>) -> index { +// CHECK: %[[VAL_1:.*]] = fir.box_total_elements %[[VAL_0]] : (!fir.box>) -> index +// CHECK: return %[[VAL_1]] : index +// CHECK: } + +// Scalar boxes hold one element. +func.func @test_scalar_i32(%arg0: !fir.class>) -> i32 { + %0 = fir.box_total_elements %arg0 : (!fir.class>) -> 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>) -> 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>) -> index { + %0 = fir.box_total_elements %arg0 : (!fir.class>) -> 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>) -> 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>, %arg1: !fir.b fir.unpack_array %6 to %arg1 stack no_copy : !fir.box> return } + +// CHECK-LABEL: func.func @test_is_contiguous_box( +func.func @test_is_contiguous_box(%arg0: !fir.class>, %arg1: !fir.box>, %arg2: !fir.box) -> i1 { +// CHECK: fir.is_contiguous_box %{{.*}} innermost : (!fir.class>) -> i1 + %0 = fir.is_contiguous_box %arg0 innermost : (!fir.class>) -> i1 +// CHECK: fir.is_contiguous_box %{{.*}} whole : (!fir.class>) -> i1 + %1 = fir.is_contiguous_box %arg0 whole : (!fir.class>) -> i1 +// CHECK: fir.is_contiguous_box %{{.*}} innermost : (!fir.box>) -> i1 + %2 = fir.is_contiguous_box %arg1 innermost : (!fir.box>) -> i1 +// CHECK: fir.is_contiguous_box %{{.*}} whole : (!fir.box>) -> i1 + %3 = fir.is_contiguous_box %arg1 whole : (!fir.box>) -> i1 +// CHECK: fir.is_contiguous_box %{{.*}} innermost : (!fir.box) -> i1 + %4 = fir.is_contiguous_box %arg2 innermost : (!fir.box) -> i1 +// CHECK: fir.is_contiguous_box %{{.*}} whole : (!fir.box) -> i1 + %5 = fir.is_contiguous_box %arg2 whole : (!fir.box) -> 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>, %arg1: !fir.box>, %arg2: !fir.box) -> index { +// CHECK: fir.box_total_elements %{{.*}} : (!fir.class>) -> i16 + %0 = fir.box_total_elements %arg0 : (!fir.class>) -> i16 +// CHECK: fir.box_total_elements %{{.*}} : (!fir.box>) -> i32 + %1 = fir.box_total_elements %arg1 : (!fir.box>) -> i32 +// CHECK: fir.box_total_elements %{{.*}} : (!fir.box) -> index + %2 = fir.box_total_elements %arg2 : (!fir.box) -> 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>, %arg1: !fir.box fir.unpack_array %arg0 to %arg1 stack : !fir.box> return } + +// ----- + +func.func @bad_is_contiguous_box(%arg0: !fir.ref>>) -> i1 { + // expected-error@+1{{op operand #0 must be any box, but got '!fir.ref>>'}} + %0 = fir.is_contiguous_box %arg0 whole : (!fir.ref>>) -> i1 + return %0 : i1 +} + +// ----- + +func.func @bad_box_total_elements(%arg0: !fir.ref>>) -> i32 { + // expected-error@+1{{op operand #0 must be any box, but got '!fir.ref>>'}} + %0 = fir.box_total_elements %arg0 : (!fir.ref>>) -> 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. +func.func @test_none(%arg0: !fir.box) -> i1 { + %0 = fir.is_contiguous_box %arg0 whole : (!fir.box) -> 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) -> i1 { +// CHECK: %[[VAL_1:.*]] = fir.is_contiguous_box %[[VAL_0]] whole : (!fir.box) -> i1 +// CHECK: return %[[VAL_1]] : i1 +// CHECK: } + +// No folding for assumed rank arrays. +func.func @test_assumed_rank(%arg0: !fir.box>) -> i1 { + %0 = fir.is_contiguous_box %arg0 innermost : (!fir.box>) -> 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>) -> i1 { +// CHECK: %[[VAL_1:.*]] = fir.is_contiguous_box %[[VAL_0]] innermost : (!fir.box>) -> i1 +// CHECK: return %[[VAL_1]] : i1 +// CHECK: } + +// Scalars are always contiguous. +func.func @test_scalar(%arg0: !fir.class>) -> i1 { + %0 = fir.is_contiguous_box %arg0 whole : (!fir.class>) -> 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>) -> 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) -> i1 { + %0 = fir.is_contiguous_box %arg0 innermost : (!fir.box) -> 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) -> i1 { +// CHECK: %[[VAL_1:.*]] = arith.constant 1 : i32 +// CHECK: %[[VAL_2:.*]] = fir.call @_FortranAIsContiguousUpTo(%[[VAL_0]], %[[VAL_1]]) : (!fir.box, i32) -> i1 +// CHECK: return %[[VAL_2]] : i1 +// CHECK: } + +// ----- + +func.func @test_none_whole(%arg0: !fir.box) -> i1 { + %0 = fir.is_contiguous_box %arg0 whole : (!fir.box) -> 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) -> i1 { +// CHECK: %[[VAL_1:.*]] = fir.call @_FortranAIsContiguous(%[[VAL_0]]) : (!fir.box) -> i1 +// CHECK: return %[[VAL_1]] : i1 +// CHECK: } + +// ----- + +func.func @test_array_innermost(%arg0: !fir.box>) -> i1 { + %0 = fir.is_contiguous_box %arg0 innermost : (!fir.box>) -> 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>) -> i1 { +// CHECK: %[[VAL_1:.*]] = arith.constant 1 : i32 +// CHECK: %[[VAL_2:.*]] = fir.convert %[[VAL_0]] : (!fir.box>) -> !fir.box +// CHECK: %[[VAL_3:.*]] = fir.call @_FortranAIsContiguousUpTo(%[[VAL_2]], %[[VAL_1]]) : (!fir.box, i32) -> i1 +// CHECK: return %[[VAL_3]] : i1 +// CHECK: } + +// ----- + +func.func @test_array_whole(%arg0: !fir.box>) -> i1 { + %0 = fir.is_contiguous_box %arg0 whole : (!fir.box>) -> 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>) -> i1 { +// CHECK: %[[VAL_1:.*]] = fir.convert %[[VAL_0]] : (!fir.box>) -> !fir.box +// CHECK: %[[VAL_2:.*]] = fir.call @_FortranAIsContiguous(%[[VAL_1]]) : (!fir.box) -> i1 +// CHECK: return %[[VAL_2]] : i1 +// CHECK: } + +// ----- + +func.func @test_assumed_rank_innermost(%arg0: !fir.box>) -> i1 { + %0 = fir.is_contiguous_box %arg0 innermost : (!fir.box>) -> 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>) -> i1 { +// CHECK: %[[VAL_1:.*]] = arith.constant 1 : i32 +// CHECK: %[[VAL_2:.*]] = fir.convert %[[VAL_0]] : (!fir.box>) -> !fir.box +// CHECK: %[[VAL_3:.*]] = fir.call @_FortranAIsContiguousUpTo(%[[VAL_2]], %[[VAL_1]]) : (!fir.box, i32) -> i1 +// CHECK: return %[[VAL_3]] : i1 +// CHECK: } + +// ----- + +func.func @test_assumed_rank_whole(%arg0: !fir.box>) -> i1 { + %0 = fir.is_contiguous_box %arg0 whole : (!fir.box>) -> 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>) -> i1 { +// CHECK: %[[VAL_1:.*]] = fir.convert %[[VAL_0]] : (!fir.box>) -> !fir.box +// CHECK: %[[VAL_2:.*]] = fir.call @_FortranAIsContiguous(%[[VAL_1]]) : (!fir.box) -> i1 +// CHECK: return %[[VAL_2]] : i1 +// CHECK: } + +// ----- + +func.func @test_none(%arg0: !fir.box) -> i16 { + %0 = fir.box_total_elements %arg0 : (!fir.box) -> 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) -> i16 { +// CHECK: %[[VAL_3:.*]] = arith.constant {{.*}} : i32 +// CHECK: %[[VAL_1:.*]] = fir.address_of(@{{.*}}) : !fir.ref> +// CHECK: %[[VAL_4:.*]] = fir.convert %[[VAL_1]] : (!fir.ref>) -> !fir.ref +// CHECK: %[[VAL_5:.*]] = fir.call @_FortranASize(%[[VAL_0]], %[[VAL_4]], %[[VAL_3]]) : (!fir.box, !fir.ref, i32) -> i64 +// CHECK: %[[VAL_6:.*]] = fir.convert %[[VAL_5]] : (i64) -> i16 +// CHECK: return %[[VAL_6]] : i16 +// CHECK: } + +// ----- + +func.func @test_array(%arg0: !fir.box>) -> i32 { + %0 = fir.box_total_elements %arg0 : (!fir.box>) -> 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>) -> i32 { +// CHECK: %[[VAL_3:.*]] = arith.constant {{.*}} : i32 +// CHECK: %[[VAL_1:.*]] = fir.address_of({{.*}}) : !fir.ref> +// CHECK: %[[VAL_4:.*]] = fir.convert %[[VAL_0]] : (!fir.box>) -> !fir.box +// CHECK: %[[VAL_5:.*]] = fir.convert %[[VAL_1]] : (!fir.ref>) -> !fir.ref +// CHECK: %[[VAL_6:.*]] = fir.call @_FortranASize(%[[VAL_4]], %[[VAL_5]], %[[VAL_3]]) : (!fir.box, !fir.ref, 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>) -> index { + %0 = fir.box_total_elements %arg0 : (!fir.box>) -> 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>) -> index { +// CHECK: %[[VAL_3:.*]] = arith.constant {{.*}} : i32 +// CHECK: %[[VAL_1:.*]] = fir.address_of({{.*}}) : !fir.ref> +// CHECK: %[[VAL_4:.*]] = fir.convert %[[VAL_0]] : (!fir.box>) -> !fir.box +// CHECK: %[[VAL_5:.*]] = fir.convert %[[VAL_1]] : (!fir.ref>) -> !fir.ref +// CHECK: %[[VAL_6:.*]] = fir.call @_FortranASize(%[[VAL_4]], %[[VAL_5]], %[[VAL_3]]) : (!fir.box, !fir.ref, i32) -> i64 +// CHECK: %[[VAL_7:.*]] = fir.convert %[[VAL_6]] : (i64) -> index +// CHECK: return %[[VAL_7]] : index +// CHECK: }