[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
Fri May 24 13:12:23 PDT 2024
https://github.com/jeanPerier created https://github.com/llvm/llvm-project/pull/93344
Please only review last commit. First two commits are part of #93305 and #93334, I was not brave enough to attempt doing actual stacked reviews.
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 descriptors 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.
>From e25a6254bb19c392b89b338f66b7e7ca2831e3de Mon Sep 17 00:00:00 2001
From: Jean Perier <jperier at nvidia.com>
Date: Fri, 24 May 2024 07:04:44 -0700
Subject: [PATCH 1/4] [flang] add fir.rebox_assumed_rank operation
As described in https://github.com/llvm/llvm-project/blob/main/flang/docs/AssumedRank.md,
add an operation to get copies of assumed-rank descriptors where lower bounds,
attributes, and dynamic type may have been changed.
---
.../flang/Optimizer/Dialect/FIRAttr.td | 11 +++++
.../include/flang/Optimizer/Dialect/FIROps.td | 37 +++++++++++++++
flang/lib/Optimizer/Dialect/FIROps.cpp | 46 +++++++++++++++++++
flang/test/Fir/fir-ops.fir | 12 +++++
flang/test/Fir/invalid.fir | 24 ++++++++++
5 files changed, 130 insertions(+)
diff --git a/flang/include/flang/Optimizer/Dialect/FIRAttr.td b/flang/include/flang/Optimizer/Dialect/FIRAttr.td
index 989319ff3ddaf..0c34b640a5c9c 100644
--- a/flang/include/flang/Optimizer/Dialect/FIRAttr.td
+++ b/flang/include/flang/Optimizer/Dialect/FIRAttr.td
@@ -70,4 +70,15 @@ def fir_BoxFieldAttr : I32EnumAttr<
// mlir::SideEffects::Resource for modelling operations which add debugging information
def DebuggingResource : Resource<"::fir::DebuggingResource">;
+def fir_LowerBoundModifierAttribute : I32EnumAttr<
+ "LowerBoundModifierAttribute",
+ "Describes how to modify lower bounds",
+ [
+ I32EnumAttrCase<"Preserve", 0, "preserve">,
+ I32EnumAttrCase<"SetToOnes", 1, "ones">,
+ I32EnumAttrCase<"SetToZeroes", 2, "zeroes">,
+ ]> {
+ let cppNamespace = "::fir";
+}
+
#endif // FIR_DIALECT_FIR_ATTRS
diff --git a/flang/include/flang/Optimizer/Dialect/FIROps.td b/flang/include/flang/Optimizer/Dialect/FIROps.td
index d9c1149040066..24dfde812039d 100644
--- a/flang/include/flang/Optimizer/Dialect/FIROps.td
+++ b/flang/include/flang/Optimizer/Dialect/FIROps.td
@@ -857,6 +857,43 @@ def fir_ReboxOp : fir_Op<"rebox", [NoMemoryEffect, AttrSizedOperandSegments]> {
let hasVerifier = 1;
}
+def fir_ReboxAssumedRankOp : fir_Op<"rebox_assumed_rank",
+ [DeclareOpInterfaceMethods<MemoryEffectsOpInterface>]> {
+ let summary = "create an assumed-rank box given another assumed rank-box";
+
+ let description = [{
+ Limited version of fir.rebox for assumed-rank. Only the lower bounds,
+ attribute, and element type may change.
+
+ The input may be a box or a reference to a box, in which case the operation
+ reads the incoming reference.
+ Since a fir.shift cannot be built without knowing the rank statically,
+ lower bound changes are encoded via a LowerBoundModifierAttribute.
+ Attribute and element type change are encoded in the result type.
+ Changing the element type is only allowed if the input type is a derived
+ type that extends the output element type.
+
+ Example:
+ ```
+ fir.rebox_assumed_rank %1 lbs zeroes : (!fir.box<!fir.array<*:f32>>) -> !fir.box<!fir.array<*:f32>>
+ ```
+ }];
+
+ let arguments = (ins
+ AnyRefOrBoxType:$box,
+ fir_LowerBoundModifierAttribute:$lbs_modifier
+ );
+
+ let results = (outs BoxOrClassType);
+
+ let assemblyFormat = [{
+ $box `lbs` $lbs_modifier
+ attr-dict `:` functional-type(operands, results)
+ }];
+
+ let hasVerifier = 1;
+}
+
def fir_EmboxCharOp : fir_Op<"emboxchar", [NoMemoryEffect]> {
let summary = "boxes a given CHARACTER reference and its LEN parameter";
diff --git a/flang/lib/Optimizer/Dialect/FIROps.cpp b/flang/lib/Optimizer/Dialect/FIROps.cpp
index 94113da9a46cf..998e9535582cb 100644
--- a/flang/lib/Optimizer/Dialect/FIROps.cpp
+++ b/flang/lib/Optimizer/Dialect/FIROps.cpp
@@ -2412,6 +2412,52 @@ mlir::LogicalResult fir::ReboxOp::verify() {
return mlir::success();
}
+//===----------------------------------------------------------------------===//
+// ReboxAssumedRankOp
+//===----------------------------------------------------------------------===//
+
+static bool areCompatibleAssumedRankElementType(mlir::Type inputEleTy,
+ mlir::Type outEleTy) {
+ if (inputEleTy == outEleTy)
+ return true;
+ // Output is unlimited polymorphic -> output dynamic type is the same as input
+ // type.
+ if (mlir::isa<mlir::NoneType>(outEleTy))
+ return true;
+ // Output/Input are derived types. Assuming input extends output type, output
+ // dynamic type is the output static type, unless output is polymorphic.
+ if (mlir::isa<fir::RecordType>(inputEleTy) &&
+ mlir::isa<fir::RecordType>(outEleTy))
+ return true;
+ if (areCompatibleCharacterTypes(inputEleTy, outEleTy))
+ return true;
+ return false;
+}
+
+mlir::LogicalResult fir::ReboxAssumedRankOp::verify() {
+ mlir::Type inputType = getBox().getType();
+ if (!mlir::isa<fir::BaseBoxType>(inputType) && !fir::isBoxAddress(inputType))
+ return emitOpError("input must be a box or box address");
+ mlir::Type inputEleTy =
+ mlir::cast<fir::BaseBoxType>(fir::unwrapRefType(inputType))
+ .unwrapInnerType();
+ mlir::Type outEleTy =
+ mlir::cast<fir::BaseBoxType>(getType()).unwrapInnerType();
+ if (!areCompatibleAssumedRankElementType(inputEleTy, outEleTy))
+ return emitOpError("input and output element types are incompatible");
+ return mlir::success();
+}
+
+void fir::ReboxAssumedRankOp::getEffects(
+ llvm::SmallVectorImpl<
+ mlir::SideEffects::EffectInstance<mlir::MemoryEffects::Effect>>
+ &effects) {
+ mlir::Value inputBox = getBox();
+ if (fir::isBoxAddress(inputBox.getType()))
+ effects.emplace_back(mlir::MemoryEffects::Read::get(), inputBox,
+ mlir::SideEffects::DefaultResource::get());
+}
+
//===----------------------------------------------------------------------===//
// ResultOp
//===----------------------------------------------------------------------===//
diff --git a/flang/test/Fir/fir-ops.fir b/flang/test/Fir/fir-ops.fir
index 962621c4e2e1a..a826dd49ef99d 100644
--- a/flang/test/Fir/fir-ops.fir
+++ b/flang/test/Fir/fir-ops.fir
@@ -900,3 +900,15 @@ fir.global @t1 {keep_my_attr = "data"} : i32 {
}
// CHECK-LABEL: fir.global @t1 {keep_my_attr = "data"} : i32
+
+func.func @test_rebox_assumed_rank(%arg0: !fir.box<!fir.array<*:f32>> ) {
+ %1 = fir.rebox_assumed_rank %arg0 lbs ones : (!fir.box<!fir.array<*:f32>>) -> !fir.box<!fir.array<*:f32>>
+ %2 = fir.rebox_assumed_rank %arg0 lbs zeroes : (!fir.box<!fir.array<*:f32>>) -> !fir.box<!fir.array<*:f32>>
+ %3 = fir.rebox_assumed_rank %arg0 lbs preserve : (!fir.box<!fir.array<*:f32>>) -> !fir.box<!fir.array<*:f32>>
+ return
+}
+// CHECK-LABEL: func.func @test_rebox_assumed_rank(
+// CHECK-SAME: %[[A:.*]]: !fir.box<!fir.array<*:f32>>)
+ // CHECK: fir.rebox_assumed_rank %[[A]] lbs ones : (!fir.box<!fir.array<*:f32>>) -> !fir.box<!fir.array<*:f32>>
+ // CHECK: fir.rebox_assumed_rank %[[A]] lbs zeroes : (!fir.box<!fir.array<*:f32>>) -> !fir.box<!fir.array<*:f32>>
+ // CHECK: fir.rebox_assumed_rank %[[A]] lbs preserve : (!fir.box<!fir.array<*:f32>>) -> !fir.box<!fir.array<*:f32>>
diff --git a/flang/test/Fir/invalid.fir b/flang/test/Fir/invalid.fir
index 049e108ba992d..f1e1aa433b9b0 100644
--- a/flang/test/Fir/invalid.fir
+++ b/flang/test/Fir/invalid.fir
@@ -978,3 +978,27 @@ func.func @bad_box_offset(%no_addendum : !fir.ref<!fir.box<i32>>) {
%addr1 = fir.box_offset %no_addendum derived_type : (!fir.ref<!fir.box<i32>>) -> !fir.llvm_ptr<!fir.tdesc<!fir.type<none>>>
return
}
+
+// -----
+
+func.func @bad_rebox_assumed_rank_1(%arg0: !fir.ref<!fir.array<*:f32>> ) {
+ // expected-error at +1{{'fir.rebox_assumed_rank' op input must be a box or box address}}
+ %1 = fir.rebox_assumed_rank %arg0 lbs ones : (!fir.ref<!fir.array<*:f32>>) -> !fir.box<!fir.array<*:f32>>
+ return
+}
+
+// -----
+
+func.func @bad_rebox_assumed_rank_2(%arg0: !fir.box<!fir.array<*:f32>> ) {
+ // expected-error at +1{{'fir.rebox_assumed_rank' op result #0 must be box or class, but got '!fir.ref<!fir.box<!fir.array<*:f32>>>'}}
+ %1 = fir.rebox_assumed_rank %arg0 lbs ones : (!fir.box<!fir.array<*:f32>>) -> !fir.ref<!fir.box<!fir.array<*:f32>>>
+ return
+}
+
+// -----
+
+func.func @bad_rebox_assumed_rank_3(%arg0: !fir.box<!fir.array<*:f32>> ) {
+ // expected-error at +1{{'fir.rebox_assumed_rank' op input and output element types are incompatible}}
+ %1 = fir.rebox_assumed_rank %arg0 lbs ones : (!fir.box<!fir.array<*:f32>>) -> !fir.box<!fir.array<*:i32>>
+ return
+}
>From 6e376219f742eb63b9c403faceef73bb16ee9265 Mon Sep 17 00:00:00 2001
From: Jean Perier <jperier at nvidia.com>
Date: Fri, 24 May 2024 06:54:56 -0700
Subject: [PATCH 2/4] [flang] add API to copy and update descriptors for
assumed ranks
---
flang/include/flang/Runtime/support.h | 15 +++++++
flang/runtime/support.cpp | 22 ++++++++++
flang/unittests/Runtime/CMakeLists.txt | 1 +
flang/unittests/Runtime/Support.cpp | 58 ++++++++++++++++++++++++++
4 files changed, 96 insertions(+)
create mode 100644 flang/unittests/Runtime/Support.cpp
diff --git a/flang/include/flang/Runtime/support.h b/flang/include/flang/Runtime/support.h
index e7ae2154b2a72..7887308f02245 100644
--- a/flang/include/flang/Runtime/support.h
+++ b/flang/include/flang/Runtime/support.h
@@ -10,6 +10,7 @@
#ifndef FORTRAN_RUNTIME_SUPPORT_H_
#define FORTRAN_RUNTIME_SUPPORT_H_
+#include "flang/ISO_Fortran_binding_wrapper.h"
#include "flang/Runtime/entry-names.h"
#include <cstddef>
#include <cstdint>
@@ -18,11 +19,25 @@ namespace Fortran::runtime {
class Descriptor;
+namespace typeInfo {
+class DerivedType;
+}
+
+enum LowerBoundModifier : int { Preserve = 0, SetToOnes = 1, SetToZeroes = 2 };
+
extern "C" {
// Predicate: is the storage described by a Descriptor contiguous in memory?
bool RTDECL(IsContiguous)(const Descriptor &);
+// Copy "from" descriptor into "to" descriptor and update "to" dynamic type,
+// CFI_attribute, and lower bounds according to the other arguments.
+// "newDynamicType" may be a null pointer in which case "to" dynamic type is the
+// one of "from".
+void RTDECL(CopyAndUpdateDescriptor)(Descriptor &to, const Descriptor &from,
+ const typeInfo::DerivedType *newDynamicType,
+ ISO::CFI_attribute_t newAttribute, enum LowerBoundModifier newLowerBounds);
+
} // extern "C"
} // namespace Fortran::runtime
#endif // FORTRAN_RUNTIME_SUPPORT_H_
diff --git a/flang/runtime/support.cpp b/flang/runtime/support.cpp
index 12135804f00e6..19e75429774b3 100644
--- a/flang/runtime/support.cpp
+++ b/flang/runtime/support.cpp
@@ -7,6 +7,7 @@
//===----------------------------------------------------------------------===//
#include "flang/Runtime/support.h"
+#include "type-info.h"
#include "flang/Runtime/descriptor.h"
namespace Fortran::runtime {
@@ -17,6 +18,27 @@ bool RTDEF(IsContiguous)(const Descriptor &descriptor) {
return descriptor.IsContiguous();
}
+void RTDEF(CopyAndUpdateDescriptor)(Descriptor &to, const Descriptor &from,
+ const typeInfo::DerivedType *newDynamicType,
+ ISO::CFI_attribute_t newAttribute, enum LowerBoundModifier newLowerBounds) {
+ to = from;
+ if (newDynamicType) {
+ DescriptorAddendum *toAddendum{to.Addendum()};
+ INTERNAL_CHECK(toAddendum);
+ toAddendum->set_derivedType(newDynamicType);
+ to.raw().elem_len = newDynamicType->sizeInBytes();
+ }
+ to.raw().attribute = newAttribute;
+ if (newLowerBounds != LowerBoundModifier::Preserve) {
+ const ISO::CFI_index_t newLowerBound{
+ newLowerBounds == LowerBoundModifier::SetToOnes ? 1 : 0};
+ const int rank{to.rank()};
+ for (int i = 0; i < rank; ++i) {
+ to.GetDimension(i).SetLowerBound(newLowerBound);
+ }
+ }
+}
+
RT_EXT_API_GROUP_END
} // extern "C"
} // namespace Fortran::runtime
diff --git a/flang/unittests/Runtime/CMakeLists.txt b/flang/unittests/Runtime/CMakeLists.txt
index f7caacad3a598..ed047b08ada35 100644
--- a/flang/unittests/Runtime/CMakeLists.txt
+++ b/flang/unittests/Runtime/CMakeLists.txt
@@ -25,6 +25,7 @@ add_flang_unittest(FlangRuntimeTests
Reduction.cpp
RuntimeCrashTest.cpp
Stop.cpp
+ Support.cpp
Time.cpp
TemporaryStack.cpp
Transformational.cpp
diff --git a/flang/unittests/Runtime/Support.cpp b/flang/unittests/Runtime/Support.cpp
new file mode 100644
index 0000000000000..fa2a233e1e654
--- /dev/null
+++ b/flang/unittests/Runtime/Support.cpp
@@ -0,0 +1,58 @@
+//===-- flang/unittests/Runtime/Support.cpp ----------------------*- 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/Runtime/support.h"
+#include "gtest/gtest.h"
+#include "tools.h"
+#include "flang/Runtime/descriptor.h"
+
+using namespace Fortran::runtime;
+using Fortran::common::TypeCategory;
+TEST(CopyAndUpdateDescriptor, Basic) {
+ auto x{MakeArray<TypeCategory::Integer, 4>(
+ std::vector<int>{2, 3}, std::vector<std::int32_t>{0, 1, 2, 3, 4, 5})};
+ x->GetDimension(0).SetLowerBound(11);
+ x->GetDimension(1).SetLowerBound(12);
+
+ StaticDescriptor<2, false> statDesc;
+ Descriptor &result{statDesc.descriptor()};
+
+ RTNAME(CopyAndUpdateDescriptor)
+ (result, *x, nullptr, CFI_attribute_pointer, LowerBoundModifier::Preserve);
+ ASSERT_EQ(result.rank(), 2);
+ EXPECT_EQ(result.raw().base_addr, x->raw().base_addr);
+ EXPECT_TRUE(result.IsPointer());
+ EXPECT_EQ(result.GetDimension(0).Extent(), x->GetDimension(0).Extent());
+ EXPECT_EQ(
+ result.GetDimension(0).LowerBound(), x->GetDimension(0).LowerBound());
+ EXPECT_EQ(result.GetDimension(1).Extent(), x->GetDimension(1).Extent());
+ EXPECT_EQ(
+ result.GetDimension(1).LowerBound(), x->GetDimension(1).LowerBound());
+
+ RTNAME(CopyAndUpdateDescriptor)
+ (result, *x, nullptr, CFI_attribute_allocatable,
+ LowerBoundModifier::SetToZeroes);
+ ASSERT_EQ(result.rank(), 2);
+ EXPECT_EQ(result.raw().base_addr, x->raw().base_addr);
+ EXPECT_TRUE(result.IsAllocatable());
+ EXPECT_EQ(result.GetDimension(0).Extent(), x->GetDimension(0).Extent());
+ EXPECT_EQ(result.GetDimension(0).LowerBound(), 0);
+ EXPECT_EQ(result.GetDimension(1).Extent(), x->GetDimension(1).Extent());
+ EXPECT_EQ(result.GetDimension(1).LowerBound(), 0);
+
+ RTNAME(CopyAndUpdateDescriptor)
+ (result, *x, nullptr, CFI_attribute_other, LowerBoundModifier::SetToOnes);
+ ASSERT_EQ(result.rank(), 2);
+ EXPECT_EQ(result.raw().base_addr, x->raw().base_addr);
+ EXPECT_FALSE(result.IsAllocatable());
+ EXPECT_FALSE(result.IsPointer());
+ EXPECT_EQ(result.GetDimension(0).Extent(), x->GetDimension(0).Extent());
+ EXPECT_EQ(result.GetDimension(0).LowerBound(), 1);
+ EXPECT_EQ(result.GetDimension(1).Extent(), x->GetDimension(1).Extent());
+ EXPECT_EQ(result.GetDimension(1).LowerBound(), 1);
+}
>From 75bc1e6741f4d42be322d672be2de8c69385fe9b Mon Sep 17 00:00:00 2001
From: Jean Perier <jperier at nvidia.com>
Date: Fri, 24 May 2024 12:23:56 -0700
Subject: [PATCH 3/4] use an enum class
---
flang/include/flang/Runtime/support.h | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/flang/include/flang/Runtime/support.h b/flang/include/flang/Runtime/support.h
index 7887308f02245..8bdf3b9fca83b 100644
--- a/flang/include/flang/Runtime/support.h
+++ b/flang/include/flang/Runtime/support.h
@@ -23,7 +23,11 @@ namespace typeInfo {
class DerivedType;
}
-enum LowerBoundModifier : int { Preserve = 0, SetToOnes = 1, SetToZeroes = 2 };
+enum class LowerBoundModifier : int {
+ Preserve = 0,
+ SetToOnes = 1,
+ SetToZeroes = 2
+};
extern "C" {
>From a18ab2c37458dbc36f1fed0cf850ac2773f3583d 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 4/4] [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