[flang-commits] [flang] 131c917 - [flang][hlfir] Add hlfir.parent_comp for leaf parent component references
Jean Perier via flang-commits
flang-commits at lists.llvm.org
Tue Feb 28 05:09:05 PST 2023
Author: Jean Perier
Date: 2023-02-28T14:08:16+01:00
New Revision: 131c9174d9f176a0f6eeaea996ac20314e0c6b05
URL: https://github.com/llvm/llvm-project/commit/131c9174d9f176a0f6eeaea996ac20314e0c6b05
DIFF: https://github.com/llvm/llvm-project/commit/131c9174d9f176a0f6eeaea996ac20314e0c6b05.diff
LOG: [flang][hlfir] Add hlfir.parent_comp for leaf parent component references
In Fortran, it is possible to refer to the "parent part" of a derived
type as if it were a component:
```Fortran
type t1
integer :: i
end type
type t2
integer :: j
end type
type(t2) :: a
print *, a%t1%i ! "inner" parent component reference
print *, a%t1 ! "leaf" parent component reference
end
```
Inner parent component references can be dropped on the floor in
lowering: "a%t1%i" is equivalent to "a%i".
Leaf parent component references, however, must be taken care of. For
scalars, "a%t1" is a simple addressc ast to "t1", for arrays, however,
this creates an array section that must be represented with a descriptor
(fir.box).
hlfir.designate could have been extended to deal with this, but I think
it would make hlfir.designate too complex and hard to manipulate.
This patch adds an hlfir.parent_comp op that represents and implements
leaf parent component references.
Differential Revision: https://reviews.llvm.org/D144946
Added:
flang/test/HLFIR/parent_comp-codegen.fir
flang/test/HLFIR/parent_comp.fir
Modified:
flang/include/flang/Optimizer/HLFIR/HLFIROps.td
flang/lib/Optimizer/HLFIR/IR/HLFIROps.cpp
flang/lib/Optimizer/HLFIR/Transforms/ConvertToFIR.cpp
flang/test/HLFIR/invalid.fir
Removed:
################################################################################
diff --git a/flang/include/flang/Optimizer/HLFIR/HLFIROps.td b/flang/include/flang/Optimizer/HLFIR/HLFIROps.td
index 241412304a5f6..be07718b9c104 100644
--- a/flang/include/flang/Optimizer/HLFIR/HLFIROps.td
+++ b/flang/include/flang/Optimizer/HLFIR/HLFIROps.td
@@ -251,6 +251,51 @@ def hlfir_DesignateOp : hlfir_Op<"designate", [AttrSizedOperandSegments,
let hasVerifier = 1;
}
+def hlfir_ParentComponentOp : hlfir_Op<"parent_comp", [AttrSizedOperandSegments,
+ DeclareOpInterfaceMethods<fir_FortranVariableOpInterface>]> {
+ let summary = "Designate the parent component of a variable";
+
+ let description = [{
+ This operation represents a Fortran component reference where the
+ component name is a parent type of the variable's derived type.
+ These component references cannot be represented with an hlfir.designate
+ because the parent type names are not embedded in fir.type<> types
+ as opposed to the actual component names.
+
+ The operands are as follow:
+ - memref is a derived type variable whose parent component is being
+ designated.
+ - shape is the shape of memref and the result and must be provided if
+ memref is an array. Parent component reference lower bounds are ones,
+ so the provided shape must be a fir.shape.
+ - typeparams are the type parameters of the parent component type if any.
+ It is a subset of memref type parameters.
+ The parent component type and name is reflected in the result type.
+ }];
+
+ let arguments = (ins AnyFortranVariable:$memref,
+ Optional<AnyShapeType>:$shape,
+ Variadic<AnyIntegerType>:$typeparams);
+
+ let extraClassDeclaration = [{
+ // Implement FortranVariableInterface interface. Parent components have
+ // no attributes (pointer, allocatable or contiguous can only be added
+ // to regular components).
+ std::optional<fir::FortranVariableFlagsEnum> getFortranAttrs() const {
+ return std::nullopt;
+ }
+ }];
+
+ let results = (outs AnyFortranVariable);
+
+ let assemblyFormat = [{
+ $memref (`shape` $shape^)? (`typeparams` $typeparams^)?
+ attr-dict `:` functional-type(operands, results)
+ }];
+
+ let hasVerifier = 1;
+}
+
def hlfir_ConcatOp : hlfir_Op<"concat", []> {
let summary = "concatenate characters";
let description = [{
diff --git a/flang/lib/Optimizer/HLFIR/IR/HLFIROps.cpp b/flang/lib/Optimizer/HLFIR/IR/HLFIROps.cpp
index 6d747615b4362..bff5b3ba648e2 100644
--- a/flang/lib/Optimizer/HLFIR/IR/HLFIROps.cpp
+++ b/flang/lib/Optimizer/HLFIR/IR/HLFIROps.cpp
@@ -380,6 +380,61 @@ mlir::LogicalResult hlfir::DesignateOp::verify() {
return mlir::success();
}
+//===----------------------------------------------------------------------===//
+// ParentComponentOp
+//===----------------------------------------------------------------------===//
+
+mlir::LogicalResult hlfir::ParentComponentOp::verify() {
+ mlir::Type baseType =
+ hlfir::getFortranElementOrSequenceType(getMemref().getType());
+ auto maybeInputSeqType = baseType.dyn_cast<fir::SequenceType>();
+ unsigned inputTypeRank =
+ maybeInputSeqType ? maybeInputSeqType.getDimension() : 0;
+ unsigned shapeRank = 0;
+ if (mlir::Value shape = getShape())
+ if (auto shapeType = shape.getType().dyn_cast<fir::ShapeType>())
+ shapeRank = shapeType.getRank();
+ if (inputTypeRank != shapeRank)
+ return emitOpError(
+ "must be provided a shape if and only if the base is an array");
+ mlir::Type outputBaseType = hlfir::getFortranElementOrSequenceType(getType());
+ auto maybeOutputSeqType = outputBaseType.dyn_cast<fir::SequenceType>();
+ unsigned outputTypeRank =
+ maybeOutputSeqType ? maybeOutputSeqType.getDimension() : 0;
+ if (inputTypeRank != outputTypeRank)
+ return emitOpError("result type rank must match input type rank");
+ if (maybeOutputSeqType && maybeInputSeqType)
+ for (auto [inputDim, outputDim] :
+ llvm::zip(maybeInputSeqType.getShape(), maybeOutputSeqType.getShape()))
+ if (inputDim != fir::SequenceType::getUnknownExtent() &&
+ outputDim != fir::SequenceType::getUnknownExtent())
+ if (inputDim != outputDim)
+ return emitOpError(
+ "result type extents are inconsistent with memref type");
+ fir::RecordType baseRecType =
+ hlfir::getFortranElementType(baseType).dyn_cast<fir::RecordType>();
+ fir::RecordType outRecType =
+ hlfir::getFortranElementType(outputBaseType).dyn_cast<fir::RecordType>();
+ if (!baseRecType || !outRecType)
+ return emitOpError("result type and input type must be derived types");
+
+ // Note: result should not be a fir.class: its dynamic type is being set to
+ // the parent type and allowing fir.class would break the operation codegen:
+ // it would keep the input dynamic type.
+ if (getType().isa<fir::ClassType>())
+ return emitOpError("result type must not be polymorphic");
+
+ // The array results are known to not be dis-contiguous in most cases (the
+ // exception being if the parent type was extended by a type without any
+ // components): require a fir.box to be used for the result to carry the
+ // strides.
+ if (!getType().isa<fir::BoxType>() &&
+ (outputTypeRank != 0 || fir::isRecordWithTypeParameters(outRecType)))
+ return emitOpError("result type must be a fir.box if the result is an "
+ "array or has length parameters");
+ return mlir::success();
+}
+
//===----------------------------------------------------------------------===//
// ConcatOp
//===----------------------------------------------------------------------===//
diff --git a/flang/lib/Optimizer/HLFIR/Transforms/ConvertToFIR.cpp b/flang/lib/Optimizer/HLFIR/Transforms/ConvertToFIR.cpp
index fd4a85c8a2496..ce09ef07115dc 100644
--- a/flang/lib/Optimizer/HLFIR/Transforms/ConvertToFIR.cpp
+++ b/flang/lib/Optimizer/HLFIR/Transforms/ConvertToFIR.cpp
@@ -506,6 +506,54 @@ class DesignateOpConversion
}
};
+class ParentComponentOpConversion
+ : public mlir::OpRewritePattern<hlfir::ParentComponentOp> {
+public:
+ explicit ParentComponentOpConversion(mlir::MLIRContext *ctx)
+ : OpRewritePattern{ctx} {}
+
+ mlir::LogicalResult
+ matchAndRewrite(hlfir::ParentComponentOp parentComponent,
+ mlir::PatternRewriter &rewriter) const override {
+ mlir::Location loc = parentComponent.getLoc();
+ mlir::Type resultType = parentComponent.getType();
+ if (!parentComponent.getType().isa<fir::BoxType>()) {
+ mlir::Value baseAddr = parentComponent.getMemref();
+ // Scalar parent component ref without any length type parameters. The
+ // input may be a fir.class if it is polymorphic, since this is a scalar
+ // and the output will be monomorphic, the base address can be extracted
+ // from the fir.class.
+ if (baseAddr.getType().isa<fir::BaseBoxType>())
+ baseAddr = rewriter.create<fir::BoxAddrOp>(loc, baseAddr);
+ rewriter.replaceOpWithNewOp<fir::ConvertOp>(parentComponent, resultType,
+ baseAddr);
+ return mlir::success();
+ }
+ // Array parent component ref or PDTs.
+ hlfir::Entity base{parentComponent.getMemref()};
+ mlir::Value baseAddr = base.getBase();
+ if (!baseAddr.getType().isa<fir::BaseBoxType>()) {
+ // Embox cannot directly be used to address parent components: it expects
+ // the output type to match the input type when there are no slices. When
+ // the types have at least one component, a slice to the first element can
+ // be built, and the result set to the parent component type. Just create
+ // a fir.box with the base for now since this covers all cases.
+ mlir::Type baseBoxType =
+ fir::BoxType::get(base.getElementOrSequenceType());
+ assert(!base.hasLengthParameters() &&
+ "base must be a box if it has any type parameters");
+ baseAddr = rewriter.create<fir::EmboxOp>(
+ loc, baseBoxType, baseAddr, parentComponent.getShape(),
+ /*slice=*/mlir::Value{}, /*typeParams=*/mlir::ValueRange{});
+ }
+ rewriter.replaceOpWithNewOp<fir::ReboxOp>(parentComponent, resultType,
+ baseAddr,
+ /*shape=*/mlir::Value{},
+ /*slice=*/mlir::Value{});
+ return mlir::success();
+ }
+};
+
class NoReassocOpConversion
: public mlir::OpRewritePattern<hlfir::NoReassocOp> {
public:
@@ -546,7 +594,8 @@ class ConvertHLFIRtoFIR
mlir::RewritePatternSet patterns(context);
patterns.insert<AssignOpConversion, CopyInOpConversion, CopyOutOpConversion,
DeclareOpConversion, DesignateOpConversion,
- NoReassocOpConversion, NullOpConversion>(context);
+ NoReassocOpConversion, NullOpConversion,
+ ParentComponentOpConversion>(context);
mlir::ConversionTarget target(*context);
target.addIllegalDialect<hlfir::hlfirDialect>();
target.markUnknownOpDynamicallyLegal(
diff --git a/flang/test/HLFIR/invalid.fir b/flang/test/HLFIR/invalid.fir
index 91fb7df6a7559..0ff4bb59c8b2d 100644
--- a/flang/test/HLFIR/invalid.fir
+++ b/flang/test/HLFIR/invalid.fir
@@ -389,3 +389,51 @@ func.func @bad_assign_2(%arg0: !fir.ref<!fir.box<!fir.heap<!fir.array<?xi32>>>>,
hlfir.assign %arg1 to %arg0 realloc keep_lhs_len : !fir.box<!fir.array<?xi32>>, !fir.ref<!fir.box<!fir.heap<!fir.array<?xi32>>>>
return
}
+
+// -----
+func.func @bad_parent_comp1(%arg0: !fir.box<!fir.array<10x!fir.type<t2{i:i32,j:i32}>>>) {
+ // expected-error at +1 {{'hlfir.parent_comp' op must be provided a shape if and only if the base is an array}}
+ %2 = hlfir.parent_comp %arg0 : (!fir.box<!fir.array<10x!fir.type<t2{i:i32,j:i32}>>>) -> !fir.box<!fir.array<10x!fir.type<t1{i:i32}>>>
+ return
+}
+
+// -----
+func.func @bad_parent_comp2(%arg0: !fir.box<!fir.array<10x!fir.type<t2{i:i32,j:i32}>>>) {
+ %c10 = arith.constant 10 : index
+ %1 = fir.shape %c10 : (index) -> !fir.shape<1>
+ // expected-error at +1 {{'hlfir.parent_comp' op result type rank must match input type rank}}
+ %2 = hlfir.parent_comp %arg0 shape %1 : (!fir.box<!fir.array<10x!fir.type<t2{i:i32,j:i32}>>>, !fir.shape<1>) -> !fir.box<!fir.array<2x5x!fir.type<t1{i:i32}>>>
+ return
+}
+
+// -----
+func.func @bad_parent_comp3(%arg0: !fir.box<!fir.array<10x!fir.type<t2{i:i32,j:i32}>>>) {
+ %c10 = arith.constant 10 : index
+ %1 = fir.shape %c10 : (index) -> !fir.shape<1>
+ // expected-error at +1 {{'hlfir.parent_comp' op result type extents are inconsistent with memref type}}
+ %2 = hlfir.parent_comp %arg0 shape %1 : (!fir.box<!fir.array<10x!fir.type<t2{i:i32,j:i32}>>>, !fir.shape<1>) -> !fir.box<!fir.array<20x!fir.type<t1{i:i32}>>>
+ return
+}
+
+// -----
+func.func @bad_parent_comp4(%arg0: !fir.ref<!fir.type<t2{i:i32,j:i32}>>) {
+ // expected-error at +1 {{'hlfir.parent_comp' op result type and input type must be derived types}}
+ %1 = hlfir.parent_comp %arg0 : (!fir.ref<!fir.type<t2{i:i32,j:i32}>>) -> !fir.ref<i32>
+ return
+}
+
+// -----
+func.func @bad_parent_comp5(%arg0: !fir.class<!fir.type<t2{i:i32,j:i32}>>) {
+ // expected-error at +1 {{'hlfir.parent_comp' op result type must not be polymorphic}}
+ %2 = hlfir.parent_comp %arg0 : (!fir.class<!fir.type<t2{i:i32,j:i32}>>) -> !fir.class<!fir.type<t1{i:i32}>>
+ return
+}
+
+// -----
+func.func @bad_parent_comp6(%arg0: !fir.box<!fir.array<10x!fir.type<t2{i:i32,j:i32}>>>) {
+ %c10 = arith.constant 10 : index
+ %1 = fir.shape %c10 : (index) -> !fir.shape<1>
+ // expected-error at +1 {{'hlfir.parent_comp' op result type must be a fir.box if the result is an array or has length parameters}}
+ %2 = hlfir.parent_comp %arg0 shape %1 : (!fir.box<!fir.array<10x!fir.type<t2{i:i32,j:i32}>>>, !fir.shape<1>) -> !fir.ref<!fir.array<10x!fir.type<t1{i:i32}>>>
+ return
+}
diff --git a/flang/test/HLFIR/parent_comp-codegen.fir b/flang/test/HLFIR/parent_comp-codegen.fir
new file mode 100644
index 0000000000000..186c539b122d9
--- /dev/null
+++ b/flang/test/HLFIR/parent_comp-codegen.fir
@@ -0,0 +1,44 @@
+// Test hlfir.parent_comp code generation to FIR
+// RUN: fir-opt %s -convert-hlfir-to-fir | FileCheck %s
+
+func.func @test_scalar(%arg0: !fir.ref<!fir.type<t2{i:i32,j:i32}>>) {
+ %1 = hlfir.parent_comp %arg0 : (!fir.ref<!fir.type<t2{i:i32,j:i32}>>) -> !fir.ref<!fir.type<t1{i:i32}>>
+ return
+}
+// CHECK-LABEL: func.func @test_scalar(
+// CHECK-SAME: %[[VAL_0:.*]]: !fir.ref<!fir.type<t2{i:i32,j:i32}>>) {
+// CHECK: fir.convert %[[VAL_0]] : (!fir.ref<!fir.type<t2{i:i32,j:i32}>>) -> !fir.ref<!fir.type<t1{i:i32}>>
+
+func.func @test_scalar_polymorphic(%arg0: !fir.class<!fir.type<t2{i:i32,j:i32}>>) {
+ %1 = hlfir.parent_comp %arg0 : (!fir.class<!fir.type<t2{i:i32,j:i32}>>) -> !fir.ref<!fir.type<t1{i:i32}>>
+ return
+}
+// CHECK-LABEL: func.func @test_scalar_polymorphic(
+// CHECK-SAME: %[[VAL_0:.*]]: !fir.class<!fir.type<t2{i:i32,j:i32}>>) {
+// CHECK: %[[VAL_1:.*]] = fir.box_addr %[[VAL_0]] : (!fir.class<!fir.type<t2{i:i32,j:i32}>>) -> !fir.ref<!fir.type<t2{i:i32,j:i32}>>
+// CHECK: fir.convert %[[VAL_1]] : (!fir.ref<!fir.type<t2{i:i32,j:i32}>>) -> !fir.ref<!fir.type<t1{i:i32}>>
+
+func.func @test_array(%arg0: !fir.ref<!fir.array<10x!fir.type<t2{i:i32,j:i32}>>>) {
+ %c10 = arith.constant 10 : index
+ %1 = fir.shape %c10 : (index) -> !fir.shape<1>
+ %2 = hlfir.parent_comp %arg0 shape %1 : (!fir.ref<!fir.array<10x!fir.type<t2{i:i32,j:i32}>>>, !fir.shape<1>) -> !fir.box<!fir.array<10x!fir.type<t1{i:i32}>>>
+ return
+}
+// CHECK-LABEL: func.func @test_array(
+// CHECK-SAME: %[[VAL_0:.*]]: !fir.ref<!fir.array<10x!fir.type<t2{i:i32,j:i32}>>>) {
+// CHECK: %[[VAL_1:.*]] = arith.constant 10 : index
+// CHECK: %[[VAL_2:.*]] = fir.shape %[[VAL_1]] : (index) -> !fir.shape<1>
+// CHECK: %[[VAL_3:.*]] = fir.embox %[[VAL_0]](%[[VAL_2]]) : (!fir.ref<!fir.array<10x!fir.type<t2{i:i32,j:i32}>>>, !fir.shape<1>) -> !fir.box<!fir.array<10x!fir.type<t2{i:i32,j:i32}>>>
+// CHECK: fir.rebox %[[VAL_3]] : (!fir.box<!fir.array<10x!fir.type<t2{i:i32,j:i32}>>>) -> !fir.box<!fir.array<10x!fir.type<t1{i:i32}>>>
+
+func.func @test_array_polymorphic(%arg0: !fir.class<!fir.array<10x!fir.type<t2{i:i32,j:i32}>>>) {
+ %c10 = arith.constant 10 : index
+ %1 = fir.shape %c10 : (index) -> !fir.shape<1>
+ %2 = hlfir.parent_comp %arg0 shape %1 : (!fir.class<!fir.array<10x!fir.type<t2{i:i32,j:i32}>>>, !fir.shape<1>) -> !fir.box<!fir.array<10x!fir.type<t1{i:i32}>>>
+ return
+}
+// CHECK-LABEL: func.func @test_array_polymorphic(
+// CHECK-SAME: %[[VAL_0:.*]]: !fir.class<!fir.array<10x!fir.type<t2{i:i32,j:i32}>>>) {
+// CHECK: %[[VAL_1:.*]] = arith.constant 10 : index
+// CHECK: %[[VAL_2:.*]] = fir.shape %[[VAL_1]] : (index) -> !fir.shape<1>
+// CHECK: fir.rebox %[[VAL_0]] : (!fir.class<!fir.array<10x!fir.type<t2{i:i32,j:i32}>>>) -> !fir.box<!fir.array<10x!fir.type<t1{i:i32}>>>
diff --git a/flang/test/HLFIR/parent_comp.fir b/flang/test/HLFIR/parent_comp.fir
new file mode 100644
index 0000000000000..c84f0a750efbc
--- /dev/null
+++ b/flang/test/HLFIR/parent_comp.fir
@@ -0,0 +1,42 @@
+// Test hlfir.parent_comp operation parse, verify (no errors), and unparse.
+// RUN: fir-opt %s | fir-opt | FileCheck %s
+
+func.func @test_scalar(%arg0: !fir.ref<!fir.type<t2{i:i32,j:i32}>>) {
+ %1 = hlfir.parent_comp %arg0 : (!fir.ref<!fir.type<t2{i:i32,j:i32}>>) -> !fir.ref<!fir.type<t1{i:i32}>>
+ return
+}
+// CHECK-LABEL: func.func @test_scalar(
+// CHECK-SAME: %[[VAL_0:.*]]: !fir.ref<!fir.type<t2{i:i32,j:i32}>>) {
+// CHECK: hlfir.parent_comp %[[VAL_0]] : (!fir.ref<!fir.type<t2{i:i32,j:i32}>>) -> !fir.ref<!fir.type<t1{i:i32}>>
+
+func.func @test_scalar_polymorphic(%arg0: !fir.class<!fir.type<t2{i:i32,j:i32}>>) {
+ %1 = hlfir.parent_comp %arg0 : (!fir.class<!fir.type<t2{i:i32,j:i32}>>) -> !fir.ref<!fir.type<t1{i:i32}>>
+ return
+}
+// CHECK-LABEL: func.func @test_scalar_polymorphic(
+// CHECK-SAME: %[[VAL_0:.*]]: !fir.class<!fir.type<t2{i:i32,j:i32}>>) {
+// CHECK: hlfir.parent_comp %[[VAL_0]] : (!fir.class<!fir.type<t2{i:i32,j:i32}>>) -> !fir.ref<!fir.type<t1{i:i32}>>
+
+func.func @test_array(%arg0: !fir.ref<!fir.array<10x!fir.type<t2{i:i32,j:i32}>>>) {
+ %c10 = arith.constant 10 : index
+ %1 = fir.shape %c10 : (index) -> !fir.shape<1>
+ %2 = hlfir.parent_comp %arg0 shape %1 : (!fir.ref<!fir.array<10x!fir.type<t2{i:i32,j:i32}>>>, !fir.shape<1>) -> !fir.box<!fir.array<10x!fir.type<t1{i:i32}>>>
+ return
+}
+// CHECK-LABEL: func.func @test_array(
+// CHECK-SAME: %[[VAL_0:.*]]: !fir.ref<!fir.array<10x!fir.type<t2{i:i32,j:i32}>>>) {
+// CHECK: %[[VAL_1:.*]] = arith.constant 10 : index
+// CHECK: %[[VAL_2:.*]] = fir.shape %[[VAL_1]] : (index) -> !fir.shape<1>
+// CHECK: hlfir.parent_comp %[[VAL_0]] shape %[[VAL_2]] : (!fir.ref<!fir.array<10x!fir.type<t2{i:i32,j:i32}>>>, !fir.shape<1>) -> !fir.box<!fir.array<10x!fir.type<t1{i:i32}>>>
+
+func.func @test_array_polymorphic(%arg0: !fir.class<!fir.array<10x!fir.type<t2{i:i32,j:i32}>>>) {
+ %c10 = arith.constant 10 : index
+ %1 = fir.shape %c10 : (index) -> !fir.shape<1>
+ %2 = hlfir.parent_comp %arg0 shape %1 : (!fir.class<!fir.array<10x!fir.type<t2{i:i32,j:i32}>>>, !fir.shape<1>) -> !fir.box<!fir.array<10x!fir.type<t1{i:i32}>>>
+ return
+}
+// CHECK-LABEL: func.func @test_array_polymorphic(
+// CHECK-SAME: %[[VAL_0:.*]]: !fir.class<!fir.array<10x!fir.type<t2{i:i32,j:i32}>>>) {
+// CHECK: %[[VAL_1:.*]] = arith.constant 10 : index
+// CHECK: %[[VAL_2:.*]] = fir.shape %[[VAL_1]] : (index) -> !fir.shape<1>
+// CHECK: hlfir.parent_comp %[[VAL_0]] shape %[[VAL_2]] : (!fir.class<!fir.array<10x!fir.type<t2{i:i32,j:i32}>>>, !fir.shape<1>) -> !fir.box<!fir.array<10x!fir.type<t1{i:i32}>>>
More information about the flang-commits
mailing list