[flang-commits] [flang] [flang] add FIR to FIR pass to lower assumed-rank operations (PR #93344)
via flang-commits
flang-commits at lists.llvm.org
Mon May 27 02:04:16 PDT 2024
https://github.com/jeanPerier updated https://github.com/llvm/llvm-project/pull/93344
>From 80e2a89578204f8d95bc42f06f55759290f26db4 Mon Sep 17 00:00:00 2001
From: Jean Perier <jperier at nvidia.com>
Date: Fri, 24 May 2024 07:00:14 -0700
Subject: [PATCH] [flang] add FIR to FIR pass to lower assumed-rank operations
Add pass to lower assumed-rank operations. The current patch adds
codegen for fir.rebox_assumed_rank. It will be the pass lowering
fir.select_rank.
fir.rebox_assumed_rank is lowered to a call to CopyAndUpdateDescriptor
runtime API.
Note that the lowering ends-up allocating two new descriptor at the LLVM
level (one alloca created by the pass for the CopyAndUpdateDescriptor
result descriptor argument, the second one is created by the fir.load
of the result descriptor in codegen).
LLVM is currently unable to properly optimize and merge those allocas.
The "nocapture" attribute added to CopyAndUpdateDescriptor arguments
gives part of the information to LLVM, but the fir.load codegen of
descriptors must be updated to use llvm.memcpy instead of llvm.load+store
to allow LLVM to optimize it. This will be done in later patch.
---
.../flang/Optimizer/Builder/FIRBuilder.h | 6 +-
.../Optimizer/Builder/Runtime/RTBuilder.h | 6 +
.../flang/Optimizer/Builder/Runtime/Support.h | 31 +++++
.../include/flang/Optimizer/Dialect/FIRType.h | 1 +
.../flang/Optimizer/Transforms/Passes.h | 1 +
.../flang/Optimizer/Transforms/Passes.td | 12 ++
flang/include/flang/Tools/CLOptions.inc | 1 +
flang/lib/Optimizer/Builder/CMakeLists.txt | 1 +
.../lib/Optimizer/Builder/Runtime/Support.cpp | 46 ++++++
flang/lib/Optimizer/Dialect/FIRType.cpp | 11 ++
.../Transforms/AssumedRankOpConversion.cpp | 131 ++++++++++++++++++
flang/lib/Optimizer/Transforms/CMakeLists.txt | 1 +
flang/test/Driver/bbc-mlir-pass-pipeline.f90 | 1 +
.../test/Driver/mlir-debug-pass-pipeline.f90 | 1 +
flang/test/Driver/mlir-pass-pipeline.f90 | 1 +
flang/test/Fir/basic-program.fir | 1 +
flang/test/Fir/rebox_assumed_rank_codegen.fir | 111 +++++++++++++++
17 files changed, 361 insertions(+), 2 deletions(-)
create mode 100644 flang/include/flang/Optimizer/Builder/Runtime/Support.h
create mode 100644 flang/lib/Optimizer/Builder/Runtime/Support.cpp
create mode 100644 flang/lib/Optimizer/Transforms/AssumedRankOpConversion.cpp
create mode 100644 flang/test/Fir/rebox_assumed_rank_codegen.fir
diff --git a/flang/include/flang/Optimizer/Builder/FIRBuilder.h b/flang/include/flang/Optimizer/Builder/FIRBuilder.h
index 287730ef2ac85..f9ef8b7566299 100644
--- a/flang/include/flang/Optimizer/Builder/FIRBuilder.h
+++ b/flang/include/flang/Optimizer/Builder/FIRBuilder.h
@@ -50,8 +50,10 @@ class FirOpBuilder : public mlir::OpBuilder, public mlir::OpBuilder::Listener {
mlir::SymbolTable *symbolTable = nullptr)
: OpBuilder{op, /*listener=*/this}, kindMap{std::move(kindMap)},
symbolTable{symbolTable} {}
- explicit FirOpBuilder(mlir::OpBuilder &builder, fir::KindMapping kindMap)
- : OpBuilder(builder), OpBuilder::Listener(), kindMap{std::move(kindMap)} {
+ explicit FirOpBuilder(mlir::OpBuilder &builder, fir::KindMapping kindMap,
+ mlir::SymbolTable *symbolTable = nullptr)
+ : OpBuilder(builder), OpBuilder::Listener(), kindMap{std::move(kindMap)},
+ symbolTable{symbolTable} {
setListener(this);
}
explicit FirOpBuilder(mlir::OpBuilder &builder, mlir::ModuleOp mod)
diff --git a/flang/include/flang/Optimizer/Builder/Runtime/RTBuilder.h b/flang/include/flang/Optimizer/Builder/Runtime/RTBuilder.h
index c9884ef7df8bb..575746374fcc4 100644
--- a/flang/include/flang/Optimizer/Builder/Runtime/RTBuilder.h
+++ b/flang/include/flang/Optimizer/Builder/Runtime/RTBuilder.h
@@ -130,6 +130,12 @@ constexpr TypeBuilderFunc getModel<signed char>() {
};
}
template <>
+constexpr TypeBuilderFunc getModel<unsigned char>() {
+ return [](mlir::MLIRContext *context) -> mlir::Type {
+ return mlir::IntegerType::get(context, 8 * sizeof(unsigned char));
+ };
+}
+template <>
constexpr TypeBuilderFunc getModel<void *>() {
return [](mlir::MLIRContext *context) -> mlir::Type {
return fir::LLVMPointerType::get(context,
diff --git a/flang/include/flang/Optimizer/Builder/Runtime/Support.h b/flang/include/flang/Optimizer/Builder/Runtime/Support.h
new file mode 100644
index 0000000000000..fe263ca2975ee
--- /dev/null
+++ b/flang/include/flang/Optimizer/Builder/Runtime/Support.h
@@ -0,0 +1,31 @@
+//===-- Support.h - generate support runtime API calls ----------*- C++ -*-===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef FORTRAN_OPTIMIZER_BUILDER_RUNTIME_SUPPORT_H
+#define FORTRAN_OPTIMIZER_BUILDER_RUNTIME_SUPPORT_H
+
+namespace mlir {
+class Value;
+class Location;
+} // namespace mlir
+
+namespace fir {
+class FirOpBuilder;
+}
+
+namespace fir::runtime {
+
+/// Generate call to `CopyAndUpdateDescriptor` runtime routine.
+void genCopyAndUpdateDescriptor(fir::FirOpBuilder &builder, mlir::Location loc,
+ mlir::Value to, mlir::Value from,
+ mlir::Value newDynamicType,
+ mlir::Value newAttribute,
+ mlir::Value newLowerBounds);
+
+} // namespace fir::runtime
+#endif // FORTRAN_OPTIMIZER_BUILDER_RUNTIME_SUPPORT_H
diff --git a/flang/include/flang/Optimizer/Dialect/FIRType.h b/flang/include/flang/Optimizer/Dialect/FIRType.h
index b4344435db9f5..0aeb29a93d71e 100644
--- a/flang/include/flang/Optimizer/Dialect/FIRType.h
+++ b/flang/include/flang/Optimizer/Dialect/FIRType.h
@@ -53,6 +53,7 @@ class BaseBoxType : public mlir::Type {
/// Return the same type, except for the shape, that is taken the shape
/// of shapeMold.
BaseBoxType getBoxTypeWithNewShape(mlir::Type shapeMold) const;
+ BaseBoxType getBoxTypeWithNewShape(int rank) const;
/// Methods for support type inquiry through isa, cast, and dyn_cast.
static bool classof(mlir::Type type);
diff --git a/flang/include/flang/Optimizer/Transforms/Passes.h b/flang/include/flang/Optimizer/Transforms/Passes.h
index e40e2faed5335..ebdd60630c330 100644
--- a/flang/include/flang/Optimizer/Transforms/Passes.h
+++ b/flang/include/flang/Optimizer/Transforms/Passes.h
@@ -36,6 +36,7 @@ namespace fir {
#define GEN_PASS_DECL_AFFINEDIALECTDEMOTION
#define GEN_PASS_DECL_ANNOTATECONSTANTOPERANDS
#define GEN_PASS_DECL_ARRAYVALUECOPY
+#define GEN_PASS_DECL_ASSUMEDRANKOPCONVERSION
#define GEN_PASS_DECL_CHARACTERCONVERSION
#define GEN_PASS_DECL_CFGCONVERSION
#define GEN_PASS_DECL_EXTERNALNAMECONVERSION
diff --git a/flang/include/flang/Optimizer/Transforms/Passes.td b/flang/include/flang/Optimizer/Transforms/Passes.td
index 28420a8b3f70c..f494da555f5ae 100644
--- a/flang/include/flang/Optimizer/Transforms/Passes.td
+++ b/flang/include/flang/Optimizer/Transforms/Passes.td
@@ -402,4 +402,16 @@ def FunctionAttr : Pass<"function-attr", "mlir::func::FuncOp"> {
let constructor = "::fir::createFunctionAttrPass()";
}
+def AssumedRankOpConversion : Pass<"fir-assumed-rank-op", "mlir::ModuleOp"> {
+ let summary =
+ "Simplify operations on assumed-rank types";
+ let description = [{
+ This pass breaks up the lowering of operations on assumed-rank types by
+ introducing an intermediate FIR level that simplifies code generation.
+ }];
+ let dependentDialects = [
+ "fir::FIROpsDialect", "mlir::func::FuncDialect"
+ ];
+}
+
#endif // FLANG_OPTIMIZER_TRANSFORMS_PASSES
diff --git a/flang/include/flang/Tools/CLOptions.inc b/flang/include/flang/Tools/CLOptions.inc
index 56cc9da7de0dd..a215488ebd0f3 100644
--- a/flang/include/flang/Tools/CLOptions.inc
+++ b/flang/include/flang/Tools/CLOptions.inc
@@ -292,6 +292,7 @@ inline void createDefaultFIROptimizerPassPipeline(
// Polymorphic types
pm.addPass(fir::createPolymorphicOpConversion());
+ pm.addPass(fir::createAssumedRankOpConversion());
if (pc.AliasAnalysis && !disableFirAliasTags && !useOldAliasTags)
pm.addPass(fir::createAddAliasTags());
diff --git a/flang/lib/Optimizer/Builder/CMakeLists.txt b/flang/lib/Optimizer/Builder/CMakeLists.txt
index 6d0aeb429d35d..8ffd0aa4cf42b 100644
--- a/flang/lib/Optimizer/Builder/CMakeLists.txt
+++ b/flang/lib/Optimizer/Builder/CMakeLists.txt
@@ -29,6 +29,7 @@ add_flang_library(FIRBuilder
Runtime/Ragged.cpp
Runtime/Reduction.cpp
Runtime/Stop.cpp
+ Runtime/Support.cpp
Runtime/TemporaryStack.cpp
Runtime/Transformational.cpp
TemporaryStorage.cpp
diff --git a/flang/lib/Optimizer/Builder/Runtime/Support.cpp b/flang/lib/Optimizer/Builder/Runtime/Support.cpp
new file mode 100644
index 0000000000000..12e47233e3d99
--- /dev/null
+++ b/flang/lib/Optimizer/Builder/Runtime/Support.cpp
@@ -0,0 +1,46 @@
+//===-- Support.cpp - generate support runtime API calls --------*- C++ -*-===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "flang/Optimizer/Builder/Runtime/Support.h"
+#include "flang/Optimizer/Builder/FIRBuilder.h"
+#include "flang/Optimizer/Builder/Runtime/RTBuilder.h"
+#include "flang/Runtime/support.h"
+#include "mlir/Dialect/LLVMIR/LLVMDialect.h"
+
+using namespace Fortran::runtime;
+
+template <>
+constexpr fir::runtime::TypeBuilderFunc
+fir::runtime::getModel<Fortran::runtime::LowerBoundModifier>() {
+ return [](mlir::MLIRContext *context) -> mlir::Type {
+ return mlir::IntegerType::get(
+ context, sizeof(Fortran::runtime::LowerBoundModifier) * 8);
+ };
+}
+
+void fir::runtime::genCopyAndUpdateDescriptor(fir::FirOpBuilder &builder,
+ mlir::Location loc,
+ mlir::Value to, mlir::Value from,
+ mlir::Value newDynamicType,
+ mlir::Value newAttribute,
+ mlir::Value newLowerBounds) {
+ mlir::func::FuncOp func =
+ fir::runtime::getRuntimeFunc<mkRTKey(CopyAndUpdateDescriptor)>(loc,
+ builder);
+ auto fTy = func.getFunctionType();
+ auto args =
+ fir::runtime::createArguments(builder, loc, fTy, to, from, newDynamicType,
+ newAttribute, newLowerBounds);
+ llvm::StringRef noCapture = mlir::LLVM::LLVMDialect::getNoCaptureAttrName();
+ if (!func.getArgAttr(0, noCapture)) {
+ mlir::UnitAttr unitAttr = mlir::UnitAttr::get(func.getContext());
+ func.setArgAttr(0, noCapture, unitAttr);
+ func.setArgAttr(1, noCapture, unitAttr);
+ }
+ builder.create<fir::CallOp>(loc, func, args);
+}
diff --git a/flang/lib/Optimizer/Dialect/FIRType.cpp b/flang/lib/Optimizer/Dialect/FIRType.cpp
index daa3ac905dad5..b6adb31213cd1 100644
--- a/flang/lib/Optimizer/Dialect/FIRType.cpp
+++ b/flang/lib/Optimizer/Dialect/FIRType.cpp
@@ -1324,6 +1324,17 @@ fir::BaseBoxType::getBoxTypeWithNewShape(mlir::Type shapeMold) const {
return mlir::cast<fir::BaseBoxType>(changeTypeShape(*this, newShape));
}
+fir::BaseBoxType fir::BaseBoxType::getBoxTypeWithNewShape(int rank) const {
+ std::optional<fir::SequenceType::ShapeRef> newShape;
+ fir::SequenceType::Shape shapeVector;
+ if (rank > 0) {
+ shapeVector =
+ fir::SequenceType::Shape(rank, fir::SequenceType::getUnknownExtent());
+ newShape = shapeVector;
+ }
+ return mlir::cast<fir::BaseBoxType>(changeTypeShape(*this, newShape));
+}
+
bool fir::BaseBoxType::isAssumedRank() const {
if (auto seqTy =
mlir::dyn_cast<fir::SequenceType>(fir::unwrapRefType(getEleTy())))
diff --git a/flang/lib/Optimizer/Transforms/AssumedRankOpConversion.cpp b/flang/lib/Optimizer/Transforms/AssumedRankOpConversion.cpp
new file mode 100644
index 0000000000000..5cc70c4d61257
--- /dev/null
+++ b/flang/lib/Optimizer/Transforms/AssumedRankOpConversion.cpp
@@ -0,0 +1,131 @@
+//===-- AssumedRankOpConversion.cpp ---------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "flang/Common/Fortran.h"
+#include "flang/Lower/BuiltinModules.h"
+#include "flang/Optimizer/Builder/FIRBuilder.h"
+#include "flang/Optimizer/Builder/Runtime/Support.h"
+#include "flang/Optimizer/Builder/Todo.h"
+#include "flang/Optimizer/Dialect/FIRDialect.h"
+#include "flang/Optimizer/Dialect/FIROps.h"
+#include "flang/Optimizer/Support/TypeCode.h"
+#include "flang/Optimizer/Support/Utils.h"
+#include "flang/Optimizer/Transforms/Passes.h"
+#include "flang/Runtime/support.h"
+#include "mlir/Dialect/Func/IR/FuncOps.h"
+#include "mlir/Pass/Pass.h"
+#include "mlir/Transforms/DialectConversion.h"
+#include "mlir/Transforms/GreedyPatternRewriteDriver.h"
+
+namespace fir {
+#define GEN_PASS_DEF_ASSUMEDRANKOPCONVERSION
+#include "flang/Optimizer/Transforms/Passes.h.inc"
+} // namespace fir
+
+using namespace fir;
+using namespace mlir;
+
+namespace {
+
+static int getCFIAttribute(mlir::Type boxType) {
+ if (fir::isAllocatableType(boxType))
+ return CFI_attribute_allocatable;
+ if (fir::isPointerType(boxType))
+ return CFI_attribute_pointer;
+ return CFI_attribute_other;
+}
+
+static Fortran::runtime::LowerBoundModifier
+getLowerBoundModifier(fir::LowerBoundModifierAttribute modifier) {
+ switch (modifier) {
+ case fir::LowerBoundModifierAttribute::Preserve:
+ return Fortran::runtime::LowerBoundModifier::Preserve;
+ case fir::LowerBoundModifierAttribute::SetToOnes:
+ return Fortran::runtime::LowerBoundModifier::SetToOnes;
+ case fir::LowerBoundModifierAttribute::SetToZeroes:
+ return Fortran::runtime::LowerBoundModifier::SetToZeroes;
+ }
+ llvm_unreachable("bad modifier code");
+}
+
+class ReboxAssumedRankConv
+ : public mlir::OpRewritePattern<fir::ReboxAssumedRankOp> {
+public:
+ using OpRewritePattern::OpRewritePattern;
+
+ ReboxAssumedRankConv(mlir::MLIRContext *context,
+ mlir::SymbolTable *symbolTable, fir::KindMapping kindMap)
+ : mlir::OpRewritePattern<fir::ReboxAssumedRankOp>(context),
+ symbolTable{symbolTable}, kindMap{kindMap} {};
+
+ mlir::LogicalResult
+ matchAndRewrite(fir::ReboxAssumedRankOp rebox,
+ mlir::PatternRewriter &rewriter) const override {
+ fir::FirOpBuilder builder{rewriter, kindMap, symbolTable};
+ mlir::Location loc = rebox.getLoc();
+ auto newBoxType = mlir::cast<fir::BaseBoxType>(rebox.getType());
+ mlir::Type newMaxRankBoxType =
+ newBoxType.getBoxTypeWithNewShape(Fortran::common::maxRank);
+ // CopyAndUpdateDescriptor FIR interface requires loading
+ // !fir.ref<fir.box> input which is expensive with assumed-rank. It could
+ // be best to add an entry point that takes a non "const" from to cover
+ // this case, but it would be good to indicate to LLVM that from does not
+ // get modified.
+ if (fir::isBoxAddress(rebox.getBox().getType()))
+ TODO(loc, "fir.rebox_assumed_rank codegen with fir.ref<fir.box<>> input");
+ mlir::Value tempDesc = builder.createTemporary(loc, newMaxRankBoxType);
+ mlir::Value newDtype;
+ mlir::Type newEleType = newBoxType.unwrapInnerType();
+ auto oldBoxType = mlir::cast<fir::BaseBoxType>(
+ fir::unwrapRefType(rebox.getBox().getType()));
+ auto newDerivedType = mlir::dyn_cast<fir::RecordType>(newEleType);
+ if (newDerivedType && (newEleType != oldBoxType.unwrapInnerType()) &&
+ !fir::isPolymorphicType(newBoxType)) {
+ newDtype = builder.create<fir::TypeDescOp>(
+ loc, mlir::TypeAttr::get(newDerivedType));
+ } else {
+ newDtype = builder.createNullConstant(loc);
+ }
+ mlir::Value newAttribute = builder.createIntegerConstant(
+ loc, builder.getIntegerType(8), getCFIAttribute(newBoxType));
+ int lbsModifierCode =
+ static_cast<int>(getLowerBoundModifier(rebox.getLbsModifier()));
+ mlir::Value lowerBoundModifier = builder.createIntegerConstant(
+ loc, builder.getIntegerType(32), lbsModifierCode);
+ fir::runtime::genCopyAndUpdateDescriptor(builder, loc, tempDesc,
+ rebox.getBox(), newDtype,
+ newAttribute, lowerBoundModifier);
+
+ mlir::Value descValue = builder.create<fir::LoadOp>(loc, tempDesc);
+ mlir::Value castDesc = builder.createConvert(loc, newBoxType, descValue);
+ rewriter.replaceOp(rebox, castDesc);
+ return mlir::success();
+ }
+
+private:
+ mlir::SymbolTable *symbolTable = nullptr;
+ fir::KindMapping kindMap;
+};
+
+/// Convert FIR structured control flow ops to CFG ops.
+class AssumedRankOpConversion
+ : public fir::impl::AssumedRankOpConversionBase<AssumedRankOpConversion> {
+public:
+ void runOnOperation() override {
+ auto *context = &getContext();
+ mlir::ModuleOp mod = getOperation();
+ mlir::SymbolTable symbolTable(mod);
+ fir::KindMapping kindMap = fir::getKindMapping(mod);
+ mlir::RewritePatternSet patterns(context);
+ patterns.insert<ReboxAssumedRankConv>(context, &symbolTable, kindMap);
+ mlir::GreedyRewriteConfig config;
+ config.enableRegionSimplification = false;
+ (void)applyPatternsAndFoldGreedily(mod, std::move(patterns), config);
+ }
+};
+} // namespace
diff --git a/flang/lib/Optimizer/Transforms/CMakeLists.txt b/flang/lib/Optimizer/Transforms/CMakeLists.txt
index 308b5ed06623f..5ef930fdb2c2f 100644
--- a/flang/lib/Optimizer/Transforms/CMakeLists.txt
+++ b/flang/lib/Optimizer/Transforms/CMakeLists.txt
@@ -4,6 +4,7 @@ add_flang_library(FIRTransforms
AffinePromotion.cpp
AffineDemotion.cpp
AnnotateConstant.cpp
+ AssumedRankOpConversion.cpp
CharacterConversion.cpp
ControlFlowConverter.cpp
ArrayValueCopy.cpp
diff --git a/flang/test/Driver/bbc-mlir-pass-pipeline.f90 b/flang/test/Driver/bbc-mlir-pass-pipeline.f90
index 2cc25b3c473fe..c94b98c7c5805 100644
--- a/flang/test/Driver/bbc-mlir-pass-pipeline.f90
+++ b/flang/test/Driver/bbc-mlir-pass-pipeline.f90
@@ -46,6 +46,7 @@
! CHECK-NEXT: (S) 0 num-dce'd - Number of operations DCE'd
! CHECK-NEXT: PolymorphicOpConversion
+! CHECK-NEXT: AssumedRankOpConversion
! 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 e555ce735853b..49b1f8c5c3134 100644
--- a/flang/test/Driver/mlir-debug-pass-pipeline.f90
+++ b/flang/test/Driver/mlir-debug-pass-pipeline.f90
@@ -73,6 +73,7 @@
! ALL-NEXT: (S) 0 num-dce'd - Number of operations DCE'd
! ALL-NEXT: PolymorphicOpConversion
+! ALL-NEXT: AssumedRankOpConversion
! 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 b3712db4ac611..8e1a3d43edd1c 100644
--- a/flang/test/Driver/mlir-pass-pipeline.f90
+++ b/flang/test/Driver/mlir-pass-pipeline.f90
@@ -80,6 +80,7 @@
! ALL-NEXT: (S) 0 num-dce'd - Number of operations DCE'd
! ALL-NEXT: PolymorphicOpConversion
+! ALL-NEXT: AssumedRankOpConversion
! 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 db252c4adfd41..dd184d99cb809 100644
--- a/flang/test/Fir/basic-program.fir
+++ b/flang/test/Fir/basic-program.fir
@@ -80,6 +80,7 @@ func.func @_QQmain() {
// PASSES-NEXT: (S) 0 num-dce'd - Number of operations DCE'd
// PASSES-NEXT: PolymorphicOpConversion
+// PASSES-NEXT: AssumedRankOpConversion
// PASSES-NEXT: AddAliasTags
// PASSES-NEXT: Pipeline Collection : ['fir.global', 'func.func', 'omp.declare_reduction', 'omp.private']
diff --git a/flang/test/Fir/rebox_assumed_rank_codegen.fir b/flang/test/Fir/rebox_assumed_rank_codegen.fir
new file mode 100644
index 0000000000000..6f9cd6edda31b
--- /dev/null
+++ b/flang/test/Fir/rebox_assumed_rank_codegen.fir
@@ -0,0 +1,111 @@
+// Test fir.rebox_assumed_rank lowering to runtime calls in fir-assumed-rank-op pass.
+// RUN: fir-opt -o - --fir-assumed-rank-op %s | FileCheck %s
+
+func.func @test_simple(%arg0: !fir.box<!fir.array<*:f32>> ) {
+ %1 = fir.rebox_assumed_rank %arg0 lbs ones : (!fir.box<!fir.array<*:f32>>) -> !fir.box<!fir.array<*:f32>>
+ fir.call @somefunc(%1) : (!fir.box<!fir.array<*:f32>>) -> ()
+ return
+}
+func.func @test_simple_zeroes(%arg0: !fir.box<!fir.array<*:f32>> ) {
+ %1 = fir.rebox_assumed_rank %arg0 lbs zeroes : (!fir.box<!fir.array<*:f32>>) -> !fir.box<!fir.array<*:f32>>
+ fir.call @somefunc(%1) : (!fir.box<!fir.array<*:f32>>) -> ()
+ return
+}
+func.func @test_simple_preserve(%arg0: !fir.box<!fir.array<*:f32>> ) {
+ %1 = fir.rebox_assumed_rank %arg0 lbs preserve : (!fir.box<!fir.array<*:f32>>) -> !fir.box<!fir.array<*:f32>>
+ fir.call @somefunc(%1) : (!fir.box<!fir.array<*:f32>>) -> ()
+ return
+}
+func.func @test_allocatable(%arg0: !fir.box<!fir.array<*:f32>> ) {
+ %1 = fir.rebox_assumed_rank %arg0 lbs preserve : (!fir.box<!fir.array<*:f32>>) -> !fir.box<!fir.heap<!fir.array<*:f32>>>
+ fir.call @somefuncalloc(%1) : (!fir.box<!fir.heap<!fir.array<*:f32>>>) -> ()
+ return
+}
+func.func @test_pointer(%arg0: !fir.box<!fir.array<*:f32>> ) {
+ %1 = fir.rebox_assumed_rank %arg0 lbs preserve : (!fir.box<!fir.array<*:f32>>) -> !fir.box<!fir.ptr<!fir.array<*:f32>>>
+ fir.call @somefuncpointer(%1) : (!fir.box<!fir.ptr<!fir.array<*:f32>>>) -> ()
+ return
+}
+!t1= !fir.type<t1{i:i32}>
+!t2= !fir.type<t2{t1:!t1, x:f32}>
+func.func @test_new_dtype(%arg0: !fir.box<!fir.array<*:!t2>> ) {
+ %1 = fir.rebox_assumed_rank %arg0 lbs ones : (!fir.box<!fir.array<*:!t2>>) -> !fir.box<!fir.array<*:!t1>>
+ fir.call @somefunct1(%1) : (!fir.box<!fir.array<*:!t1>>) -> ()
+ return
+}
+
+func.func private @somefunc(!fir.box<!fir.array<*:f32>>)
+func.func private @somefuncalloc(!fir.box<!fir.heap<!fir.array<*:f32>>>)
+func.func private @somefuncpointer(!fir.box<!fir.ptr<!fir.array<*:f32>>>)
+func.func private @somefunct1(!fir.box<!fir.array<*:!t1>>)
+
+// CHECK-LABEL: func.func @test_simple(
+// CHECK-SAME: %[[VAL_0:.*]]: !fir.box<!fir.array<*:f32>>) {
+// CHECK: %[[VAL_1:.*]] = arith.constant 1 : i32
+// CHECK: %[[VAL_2:.*]] = arith.constant 0 : i8
+// CHECK: %[[VAL_3:.*]] = fir.alloca !fir.box<!fir.array<?x?x?x?x?x?x?x?x?x?x?x?x?x?x?xf32>>
+// CHECK: %[[VAL_4:.*]] = fir.zero_bits !fir.ref<none>
+// CHECK: %[[VAL_5:.*]] = fir.convert %[[VAL_3]] : (!fir.ref<!fir.box<!fir.array<?x?x?x?x?x?x?x?x?x?x?x?x?x?x?xf32>>>) -> !fir.ref<!fir.box<none>>
+// CHECK: %[[VAL_6:.*]] = fir.convert %[[VAL_0]] : (!fir.box<!fir.array<*:f32>>) -> !fir.box<none>
+// CHECK: %[[VAL_7:.*]] = fir.call @_FortranACopyAndUpdateDescriptor(%[[VAL_5]], %[[VAL_6]], %[[VAL_4]], %[[VAL_2]], %[[VAL_1]]) : (!fir.ref<!fir.box<none>>, !fir.box<none>, !fir.ref<none>, i8, i32) -> none
+// CHECK: %[[VAL_8:.*]] = fir.load %[[VAL_3]] : !fir.ref<!fir.box<!fir.array<?x?x?x?x?x?x?x?x?x?x?x?x?x?x?xf32>>>
+// CHECK: %[[VAL_9:.*]] = fir.convert %[[VAL_8]] : (!fir.box<!fir.array<?x?x?x?x?x?x?x?x?x?x?x?x?x?x?xf32>>) -> !fir.box<!fir.array<*:f32>>
+// CHECK: fir.call @somefunc(%[[VAL_9]]) : (!fir.box<!fir.array<*:f32>>) -> ()
+// CHECK: return
+// CHECK: }
+
+// CHECK-LABEL: func.func @test_simple_zeroes(
+// CHECK: %[[VAL_1:.*]] = arith.constant 2 : i32
+// CHECK: fir.call @_FortranACopyAndUpdateDescriptor(%{{.*}}, %{{.*}}, %{{.*}}, %{{.*}}, %[[VAL_1]])
+
+// CHECK-LABEL: func.func @test_simple_preserve(
+// CHECK: %[[VAL_1:.*]] = arith.constant 0 : i32
+// CHECK: fir.call @_FortranACopyAndUpdateDescriptor(%{{.*}}, %{{.*}}, %{{.*}}, %{{.*}}, %[[VAL_1]])
+
+// CHECK-LABEL: func.func @test_allocatable(
+// CHECK-SAME: %[[VAL_0:.*]]: !fir.box<!fir.array<*:f32>>) {
+// CHECK: %[[VAL_1:.*]] = arith.constant 0 : i32
+// CHECK: %[[VAL_2:.*]] = arith.constant 2 : i8
+// CHECK: %[[VAL_3:.*]] = fir.alloca !fir.box<!fir.heap<!fir.array<?x?x?x?x?x?x?x?x?x?x?x?x?x?x?xf32>>>
+// CHECK: %[[VAL_4:.*]] = fir.zero_bits !fir.ref<none>
+// CHECK: %[[VAL_5:.*]] = fir.convert %[[VAL_3]] : (!fir.ref<!fir.box<!fir.heap<!fir.array<?x?x?x?x?x?x?x?x?x?x?x?x?x?x?xf32>>>>) -> !fir.ref<!fir.box<none>>
+// CHECK: %[[VAL_6:.*]] = fir.convert %[[VAL_0]] : (!fir.box<!fir.array<*:f32>>) -> !fir.box<none>
+// CHECK: %[[VAL_7:.*]] = fir.call @_FortranACopyAndUpdateDescriptor(%[[VAL_5]], %[[VAL_6]], %[[VAL_4]], %[[VAL_2]], %[[VAL_1]]) : (!fir.ref<!fir.box<none>>, !fir.box<none>, !fir.ref<none>, i8, i32) -> none
+// CHECK: %[[VAL_8:.*]] = fir.load %[[VAL_3]] : !fir.ref<!fir.box<!fir.heap<!fir.array<?x?x?x?x?x?x?x?x?x?x?x?x?x?x?xf32>>>>
+// CHECK: %[[VAL_9:.*]] = fir.convert %[[VAL_8]] : (!fir.box<!fir.heap<!fir.array<?x?x?x?x?x?x?x?x?x?x?x?x?x?x?xf32>>>) -> !fir.box<!fir.heap<!fir.array<*:f32>>>
+// CHECK: fir.call @somefuncalloc(%[[VAL_9]]) : (!fir.box<!fir.heap<!fir.array<*:f32>>>) -> ()
+// CHECK: return
+// CHECK: }
+
+// CHECK-LABEL: func.func @test_pointer(
+// CHECK-SAME: %[[VAL_0:.*]]: !fir.box<!fir.array<*:f32>>) {
+// CHECK: %[[VAL_1:.*]] = arith.constant 0 : i32
+// CHECK: %[[VAL_2:.*]] = arith.constant 1 : i8
+// CHECK: %[[VAL_3:.*]] = fir.alloca !fir.box<!fir.ptr<!fir.array<?x?x?x?x?x?x?x?x?x?x?x?x?x?x?xf32>>>
+// CHECK: %[[VAL_4:.*]] = fir.zero_bits !fir.ref<none>
+// CHECK: %[[VAL_5:.*]] = fir.convert %[[VAL_3]] : (!fir.ref<!fir.box<!fir.ptr<!fir.array<?x?x?x?x?x?x?x?x?x?x?x?x?x?x?xf32>>>>) -> !fir.ref<!fir.box<none>>
+// CHECK: %[[VAL_6:.*]] = fir.convert %[[VAL_0]] : (!fir.box<!fir.array<*:f32>>) -> !fir.box<none>
+// CHECK: %[[VAL_7:.*]] = fir.call @_FortranACopyAndUpdateDescriptor(%[[VAL_5]], %[[VAL_6]], %[[VAL_4]], %[[VAL_2]], %[[VAL_1]]) : (!fir.ref<!fir.box<none>>, !fir.box<none>, !fir.ref<none>, i8, i32) -> none
+// CHECK: %[[VAL_8:.*]] = fir.load %[[VAL_3]] : !fir.ref<!fir.box<!fir.ptr<!fir.array<?x?x?x?x?x?x?x?x?x?x?x?x?x?x?xf32>>>>
+// CHECK: %[[VAL_9:.*]] = fir.convert %[[VAL_8]] : (!fir.box<!fir.ptr<!fir.array<?x?x?x?x?x?x?x?x?x?x?x?x?x?x?xf32>>>) -> !fir.box<!fir.ptr<!fir.array<*:f32>>>
+// CHECK: fir.call @somefuncpointer(%[[VAL_9]]) : (!fir.box<!fir.ptr<!fir.array<*:f32>>>) -> ()
+// CHECK: return
+// CHECK: }
+
+// CHECK-LABEL: func.func @test_new_dtype(
+// CHECK-SAME: %[[VAL_0:.*]]: !fir.box<!fir.array<*:!fir.type<t2{t1:!fir.type<t1{i:i32}>,x:f32}>>>) {
+// CHECK: %[[VAL_1:.*]] = arith.constant 1 : i32
+// CHECK: %[[VAL_2:.*]] = arith.constant 0 : i8
+// CHECK: %[[VAL_3:.*]] = fir.alloca !fir.box<!fir.array<?x?x?x?x?x?x?x?x?x?x?x?x?x?x?x!fir.type<t1{i:i32}>>>
+// CHECK: %[[VAL_4:.*]] = fir.type_desc !fir.type<t1{i:i32}>
+// CHECK: %[[VAL_5:.*]] = fir.convert %[[VAL_3]] : (!fir.ref<!fir.box<!fir.array<?x?x?x?x?x?x?x?x?x?x?x?x?x?x?x!fir.type<t1{i:i32}>>>>) -> !fir.ref<!fir.box<none>>
+// CHECK: %[[VAL_6:.*]] = fir.convert %[[VAL_0]] : (!fir.box<!fir.array<*:!fir.type<t2{t1:!fir.type<t1{i:i32}>,x:f32}>>>) -> !fir.box<none>
+// CHECK: %[[VAL_7:.*]] = fir.convert %[[VAL_4]] : (!fir.tdesc<!fir.type<t1{i:i32}>>) -> !fir.ref<none>
+// CHECK: %[[VAL_8:.*]] = fir.call @_FortranACopyAndUpdateDescriptor(%[[VAL_5]], %[[VAL_6]], %[[VAL_7]], %[[VAL_2]], %[[VAL_1]]) : (!fir.ref<!fir.box<none>>, !fir.box<none>, !fir.ref<none>, i8, i32) -> none
+// CHECK: %[[VAL_9:.*]] = fir.load %[[VAL_3]] : !fir.ref<!fir.box<!fir.array<?x?x?x?x?x?x?x?x?x?x?x?x?x?x?x!fir.type<t1{i:i32}>>>>
+// CHECK: %[[VAL_10:.*]] = fir.convert %[[VAL_9]] : (!fir.box<!fir.array<?x?x?x?x?x?x?x?x?x?x?x?x?x?x?x!fir.type<t1{i:i32}>>>) -> !fir.box<!fir.array<*:!fir.type<t1{i:i32}>>>
+// CHECK: fir.call @somefunct1(%[[VAL_10]]) : (!fir.box<!fir.array<*:!fir.type<t1{i:i32}>>>) -> ()
+// CHECK: return
+// CHECK: }
+
+// CHECK: func.func private @_FortranACopyAndUpdateDescriptor(!fir.ref<!fir.box<none>> {llvm.nocapture}, !fir.box<none> {llvm.nocapture}, !fir.ref<none>, i8, i32) -> none attributes {fir.runtime}
More information about the flang-commits
mailing list