[flang] lower vector subscripted polymorphic designators (#84778)

A mold argument need to be added to the hlfir.element_addr and set in
lowering so that when the hlfir.element_addr need to be turned into an
hlfir.elemental operation because the designator must be turned into a
value, the mold can be set on the hlfir.elemental to later allocate the
temporary according the the dynamic type.

This situation happens whenever the vector subscripted polymorphic
designator does not appear as an assignment left-hand side, or as an
IO-input item.


I initially thought retrieving the mold would be tricky if the dynamic
type of the designator was set by a part-ref of the right of the vector
subscripts ("array(vector)%polymorphic_comp"), but this turned out to be
impossible because:
1. A derived type component can be polymorphic only if it has the
POINTER or ALLOCATABLE attribute (F2023 C708).
2. Vector-subscripted part are ranked and F2023 C919 prohibits any
part-ref on the right of the rank part to have the POINTER or
ALLOCATABLE attribute.

=> If a vector subscripted designator is polymorphic, the vector
subscripted part is the rightmost part, and the mold is the base of the
vector subscripted part. This makes the retrieval of the mold easy in
lowering. The mold argument is always set to be the base of the vector
subscripted part when lowering the vector subscripted part, and it is
removed at the end of the designator lowering if the designator is not
polymorphic. This way there is no need to find back the mold from the
inside of the hlfir.element_addr body.
This commit is contained in:
jeanPerier 2024-03-12 10:29:19 +01:00 committed by GitHub
parent b274b23665
commit 939f038296
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 141 additions and 51 deletions

View File

@ -1358,7 +1358,9 @@ def hlfir_YieldOp : hlfir_Op<"yield", [Terminator, ParentOneOf<["RegionAssignOp"
let assemblyFormat = "$entity attr-dict `:` type($entity) custom<YieldOpCleanup>($cleanup)";
}
def hlfir_ElementalAddrOp : hlfir_Op<"elemental_addr", [Terminator, HasParent<"RegionAssignOp">, RecursiveMemoryEffects, RecursivelySpeculatable, hlfir_ElementalOpInterface]> {
def hlfir_ElementalAddrOp : hlfir_Op<"elemental_addr", [Terminator, HasParent<"RegionAssignOp">,
RecursiveMemoryEffects, RecursivelySpeculatable, hlfir_ElementalOpInterface,
AttrSizedOperandSegments]> {
let summary = "Yield the address of a vector subscripted variable inside an hlfir.region_assign";
let description = [{
Special terminator node for the left-hand side region of an hlfir.region_assign
@ -1398,6 +1400,7 @@ def hlfir_ElementalAddrOp : hlfir_Op<"elemental_addr", [Terminator, HasParent<"R
let arguments = (ins
fir_ShapeType:$shape,
Optional<AnyPolymorphicObject>:$mold,
Variadic<AnyIntegerType>:$typeparams,
OptionalAttr<UnitAttr>:$unordered
);
@ -1406,11 +1409,15 @@ def hlfir_ElementalAddrOp : hlfir_Op<"elemental_addr", [Terminator, HasParent<"R
MaxSizedRegion<1>:$cleanup);
let builders = [
OpBuilder<(ins "mlir::Value":$shape, CArg<"bool", "false">:$isUnordered)>
OpBuilder<(ins "mlir::Value":$shape,
CArg<"mlir::Value", "{}">:$mold,
CArg<"mlir::ValueRange", "{}">:$typeparams,
CArg<"bool", "false">:$isUnordered)>
];
let assemblyFormat = [{
$shape (`typeparams` $typeparams^)? (`unordered` $unordered^)?
$shape (`mold` $mold^)? (`typeparams` $typeparams^)?
(`unordered` $unordered^)?
attr-dict `:` type(operands) $body
custom<YieldOpCleanup>($cleanup)}];

View File

@ -761,9 +761,17 @@ private:
// of the whole designator (not the ones of the vector subscripted part).
// These are not yet known and will be added when finalizing the designator
// lowering.
auto elementalAddrOp =
builder.create<hlfir::ElementalAddrOp>(loc, shape,
/*isUnordered=*/true);
// The resulting designator may be polymorphic, in which case the resulting
// type is the base of the vector subscripted part because
// allocatable/pointer components cannot be referenced after a vector
// subscripted part. Set the mold to the current base. It will be erased if
// the resulting designator is not polymorphic.
assert(partInfo.base.has_value() &&
"vector subscripted part must have a base");
mlir::Value mold = *partInfo.base;
auto elementalAddrOp = builder.create<hlfir::ElementalAddrOp>(
loc, shape, mold, mlir::ValueRange{},
/*isUnordered=*/true);
setVectorSubscriptElementAddrOp(elementalAddrOp);
builder.setInsertionPointToEnd(&elementalAddrOp.getBody().front());
mlir::Region::BlockArgListType indices = elementalAddrOp.getIndices();
@ -804,15 +812,8 @@ private:
hlfir::EntityWithAttributes elementAddr) {
fir::FirOpBuilder &builder = getBuilder();
builder.setInsertionPointToEnd(&elementalAddrOp.getBody().front());
// For polymorphic entities, it will be needed to add a mold on the
// hlfir.elemental so that we are able to create temporary storage
// for it using the dynamic type. It seems that a reference to the mold
// entity can be created by evaluating the hlfir.elemental_addr
// for a single index. The evaluation should be legal as long as
// the hlfir.elemental_addr has no side effects, otherwise,
// it is not clear how to get the mold reference.
if (elementAddr.isPolymorphic())
TODO(loc, "vector subscripted polymorphic entity in HLFIR");
if (!elementAddr.isPolymorphic())
elementalAddrOp.getMoldMutable().clear();
builder.create<hlfir::YieldOp>(loc, elementAddr);
builder.setInsertionPointAfter(elementalAddrOp);
}
@ -929,6 +930,8 @@ HlfirDesignatorBuilder::convertVectorSubscriptedExprToElementalAddr(
hlfir::genLengthParameters(loc, builder, elementAddrEntity, lengths);
if (!lengths.empty())
elementalAddrOp.getTypeparamsMutable().assign(lengths);
if (!elementAddrEntity.isPolymorphic())
elementalAddrOp.getMoldMutable().clear();
// Create the hlfir.yield terminator inside the hlfir.elemental_body.
builder.setInsertionPointToEnd(&elementalAddrOp.getBody().front());
builder.create<hlfir::YieldOp>(loc, elementAddrEntity);

View File

@ -1036,9 +1036,9 @@ hlfir::cloneToElementalOp(mlir::Location loc, fir::FirOpBuilder &builder,
return hlfir::loadTrivialScalar(l, b, newAddr);
};
mlir::Type elementType = scalarAddress.getFortranElementType();
return hlfir::genElementalOp(loc, builder, elementType,
elementalAddrOp.getShape(), typeParams,
genKernel, !elementalAddrOp.isOrdered());
return hlfir::genElementalOp(
loc, builder, elementType, elementalAddrOp.getShape(), typeParams,
genKernel, !elementalAddrOp.isOrdered(), elementalAddrOp.getMold());
}
bool hlfir::elementalOpMustProduceTemp(hlfir::ElementalOp elemental) {

View File

@ -1406,31 +1406,43 @@ void hlfir::AsExprOp::getEffects(
// ElementalOp
//===----------------------------------------------------------------------===//
/// Common builder for ElementalOp and ElementalAddrOp to add the arguments and
/// create the elemental body. Result and clean-up body must be handled in
/// specific builders.
template <typename Op>
static void buildElemental(mlir::OpBuilder &builder,
mlir::OperationState &odsState, mlir::Value shape,
mlir::Value mold, mlir::ValueRange typeparams,
bool isUnordered) {
odsState.addOperands(shape);
if (mold)
odsState.addOperands(mold);
odsState.addOperands(typeparams);
odsState.addAttribute(
Op::getOperandSegmentSizesAttrName(odsState.name),
builder.getDenseI32ArrayAttr({/*shape=*/1, (mold ? 1 : 0),
static_cast<int32_t>(typeparams.size())}));
if (isUnordered)
odsState.addAttribute(Op::getUnorderedAttrName(odsState.name),
isUnordered ? builder.getUnitAttr() : nullptr);
mlir::Region *bodyRegion = odsState.addRegion();
bodyRegion->push_back(new mlir::Block{});
if (auto shapeType = shape.getType().dyn_cast<fir::ShapeType>()) {
unsigned dim = shapeType.getRank();
mlir::Type indexType = builder.getIndexType();
for (unsigned d = 0; d < dim; ++d)
bodyRegion->front().addArgument(indexType, odsState.location);
}
}
void hlfir::ElementalOp::build(mlir::OpBuilder &builder,
mlir::OperationState &odsState,
mlir::Type resultType, mlir::Value shape,
mlir::Value mold, mlir::ValueRange typeparams,
bool isUnordered) {
odsState.addOperands(shape);
if (mold)
odsState.addOperands(mold);
odsState.addOperands(typeparams);
odsState.addTypes(resultType);
odsState.addAttribute(
getOperandSegmentSizesAttrName(odsState.name),
builder.getDenseI32ArrayAttr({/*shape=*/1, (mold ? 1 : 0),
static_cast<int32_t>(typeparams.size())}));
if (isUnordered)
odsState.addAttribute(getUnorderedAttrName(odsState.name),
isUnordered ? builder.getUnitAttr() : nullptr);
mlir::Region *bodyRegion = odsState.addRegion();
bodyRegion->push_back(new mlir::Block{});
if (auto exprType = resultType.dyn_cast<hlfir::ExprType>()) {
unsigned dim = exprType.getRank();
mlir::Type indexType = builder.getIndexType();
for (unsigned d = 0; d < dim; ++d)
bodyRegion->front().addArgument(indexType, odsState.location);
}
buildElemental<hlfir::ElementalOp>(builder, odsState, shape, mold, typeparams,
isUnordered);
}
mlir::Value hlfir::ElementalOp::getElementEntity() {
@ -1681,19 +1693,11 @@ static void printYieldOpCleanup(mlir::OpAsmPrinter &p, YieldOp yieldOp,
void hlfir::ElementalAddrOp::build(mlir::OpBuilder &builder,
mlir::OperationState &odsState,
mlir::Value shape, bool isUnordered) {
odsState.addOperands(shape);
if (isUnordered)
odsState.addAttribute(getUnorderedAttrName(odsState.name),
isUnordered ? builder.getUnitAttr() : nullptr);
mlir::Region *bodyRegion = odsState.addRegion();
bodyRegion->push_back(new mlir::Block{});
if (auto shapeType = shape.getType().dyn_cast<fir::ShapeType>()) {
unsigned dim = shapeType.getRank();
mlir::Type indexType = builder.getIndexType();
for (unsigned d = 0; d < dim; ++d)
bodyRegion->front().addArgument(indexType, odsState.location);
}
mlir::Value shape, mlir::Value mold,
mlir::ValueRange typeparams,
bool isUnordered) {
buildElemental<hlfir::ElementalAddrOp>(builder, odsState, shape, mold,
typeparams, isUnordered);
// Push cleanUp region.
odsState.addRegion();
}

View File

@ -114,3 +114,45 @@ func.func @unordered() {
// CHECK: }
// CHECK: return
// CHECK: }
// "X(VECTOR) = Y" with polymorphic X and Y and user defined assignment.
func.func @test_mold(%x: !fir.class<!fir.array<?x!fir.type<t>>>, %y: !fir.class<!fir.array<?x!fir.type<t>>>, %vector: !fir.box<!fir.array<?xi64>>) {
hlfir.region_assign {
hlfir.yield %y : !fir.class<!fir.array<?x!fir.type<t>>>
} to {
%c0 = arith.constant 0 : index
%0:3 = fir.box_dims %vector, %c0 : (!fir.box<!fir.array<?xi64>>, index) -> (index, index, index)
%1 = fir.shape %0#1 : (index) -> !fir.shape<1>
hlfir.elemental_addr %1 mold %x unordered : !fir.shape<1>, !fir.class<!fir.array<?x!fir.type<t>>> {
^bb0(%arg3: index):
%2 = hlfir.designate %vector (%arg3) : (!fir.box<!fir.array<?xi64>>, index) -> !fir.ref<i64>
%3 = fir.load %2 : !fir.ref<i64>
%4 = hlfir.designate %x (%3) : (!fir.class<!fir.array<?x!fir.type<t>>>, i64) -> !fir.class<!fir.type<t>>
hlfir.yield %4 : !fir.class<!fir.type<t>>
}
} user_defined_assign (%arg3: !fir.class<!fir.type<t>>) to (%arg4: !fir.class<!fir.type<t>>) {
fir.call @user_def_assign(%arg4, %arg3) : (!fir.class<!fir.type<t>>, !fir.class<!fir.type<t>>) -> ()
}
return
}
func.func private @user_def_assign(!fir.class<!fir.type<t>>, !fir.class<!fir.type<t>>)
// CHECK-LABEL: func.func @test_mold(
// CHECK-SAME: %[[VAL_0:[^:]*]]: !fir.class<!fir.array<?x!fir.type<t>>>,
// CHECK-SAME: %[[VAL_1:.*]]: !fir.class<!fir.array<?x!fir.type<t>>>,
// CHECK-SAME: %[[VAL_2:.*]]: !fir.box<!fir.array<?xi64>>) {
// CHECK: hlfir.region_assign {
// CHECK: hlfir.yield %[[VAL_1]] : !fir.class<!fir.array<?x!fir.type<t>>>
// CHECK: } to {
// CHECK: %[[VAL_3:.*]] = arith.constant 0 : index
// CHECK: %[[VAL_4:.*]]:3 = fir.box_dims %[[VAL_2]], %[[VAL_3]] : (!fir.box<!fir.array<?xi64>>, index) -> (index, index, index)
// CHECK: %[[VAL_5:.*]] = fir.shape %[[VAL_4]]#1 : (index) -> !fir.shape<1>
// CHECK: hlfir.elemental_addr %[[VAL_5]] mold %[[VAL_0]] unordered : !fir.shape<1>, !fir.class<!fir.array<?x!fir.type<t>>> {
// CHECK: ^bb0(%[[VAL_6:.*]]: index):
// CHECK: %[[VAL_7:.*]] = hlfir.designate %[[VAL_2]] (%[[VAL_6]]) : (!fir.box<!fir.array<?xi64>>, index) -> !fir.ref<i64>
// CHECK: %[[VAL_8:.*]] = fir.load %[[VAL_7]] : !fir.ref<i64>
// CHECK: %[[VAL_9:.*]] = hlfir.designate %[[VAL_0]] (%[[VAL_8]]) : (!fir.class<!fir.array<?x!fir.type<t>>>, i64) -> !fir.class<!fir.type<t>>
// CHECK: hlfir.yield %[[VAL_9]] : !fir.class<!fir.type<t>>
// CHECK: }
// CHECK: } user_defined_assign (%[[VAL_10:.*]]: !fir.class<!fir.type<t>>) to (%[[VAL_11:.*]]: !fir.class<!fir.type<t>>) {
// CHECK: fir.call @user_def_assign(%[[VAL_11]], %[[VAL_10]]) : (!fir.class<!fir.type<t>>, !fir.class<!fir.type<t>>) -> ()
// CHECK: }

View File

@ -1,6 +1,6 @@
! Test lowering of vector subscript designators outside of the
! assignment left-and side and input IO context.
! RUN: bbc -emit-hlfir -o - -I nw %s 2>&1 | FileCheck %s
! RUN: bbc -emit-hlfir -o - -I nw %s --polymorphic-type 2>&1 | FileCheck %s
subroutine foo(x, y)
integer :: x(100)
@ -182,3 +182,37 @@ end subroutine
! CHECK: %[[VAL_27:.*]] = hlfir.designate %[[VAL_4]]#0 (%[[VAL_26]]) substr %[[VAL_15]], %[[VAL_16]] typeparams %[[VAL_22]] : (!fir.box<!fir.array<?x!fir.char<1,?>>>, i64, index, index, index) -> !fir.boxchar<1>
! CHECK: hlfir.yield_element %[[VAL_27]] : !fir.boxchar<1>
! CHECK: }
subroutine test_passing_subscripted_poly(x, vector)
interface
subroutine do_something(x)
class(*) :: x(:)
end subroutine
end interface
class(*) :: x(:, :)
integer(8) :: vector(:)
call do_something(x(314, vector))
end subroutine
! CHECK-LABEL: func.func @_QPtest_passing_subscripted_poly(
! CHECK-SAME: %[[VAL_0:.*]]: !fir.class<!fir.array<?x?xnone>>
! CHECK-SAME: %[[VAL_1:.*]]: !fir.box<!fir.array<?xi64>>
! CHECK: %[[VAL_2:.*]]:2 = hlfir.declare %[[VAL_1]] {uniq_name = "_QFtest_passing_subscripted_polyEvector"} : (!fir.box<!fir.array<?xi64>>) -> (!fir.box<!fir.array<?xi64>>, !fir.box<!fir.array<?xi64>>)
! CHECK: %[[VAL_3:.*]]:2 = hlfir.declare %[[VAL_0]] {uniq_name = "_QFtest_passing_subscripted_polyEx"} : (!fir.class<!fir.array<?x?xnone>>) -> (!fir.class<!fir.array<?x?xnone>>, !fir.class<!fir.array<?x?xnone>>)
! CHECK: %[[VAL_4:.*]] = arith.constant 314 : index
! CHECK: %[[VAL_5:.*]] = arith.constant 0 : index
! CHECK: %[[VAL_6:.*]]:3 = fir.box_dims %[[VAL_2]]#0, %[[VAL_5]] : (!fir.box<!fir.array<?xi64>>, index) -> (index, index, index)
! CHECK: %[[VAL_7:.*]] = fir.shape %[[VAL_6]]#1 : (index) -> !fir.shape<1>
! CHECK: %[[VAL_8:.*]] = hlfir.elemental %[[VAL_7]] mold %[[VAL_3]]#0 unordered : (!fir.shape<1>, !fir.class<!fir.array<?x?xnone>>) -> !hlfir.expr<?xnone?> {
! CHECK: ^bb0(%[[VAL_9:.*]]: index):
! CHECK: %[[VAL_10:.*]] = hlfir.designate %[[VAL_2]]#0 (%[[VAL_9]]) : (!fir.box<!fir.array<?xi64>>, index) -> !fir.ref<i64>
! CHECK: %[[VAL_11:.*]] = fir.load %[[VAL_10]] : !fir.ref<i64>
! CHECK: %[[VAL_12:.*]] = hlfir.designate %[[VAL_3]]#0 (%[[VAL_4]], %[[VAL_11]]) : (!fir.class<!fir.array<?x?xnone>>, index, i64) -> !fir.class<none>
! CHECK: hlfir.yield_element %[[VAL_12]] : !fir.class<none>
! CHECK: }
! CHECK: %[[VAL_13:.*]]:3 = hlfir.associate %[[VAL_8]](%[[VAL_7]]) {adapt.valuebyref} : (!hlfir.expr<?xnone?>, !fir.shape<1>) -> (!fir.class<!fir.heap<!fir.array<?xnone>>>, !fir.class<!fir.heap<!fir.array<?xnone>>>, i1)
! CHECK: %[[VAL_14:.*]] = fir.rebox %[[VAL_13]]#0 : (!fir.class<!fir.heap<!fir.array<?xnone>>>) -> !fir.class<!fir.array<?xnone>>
! CHECK: fir.call @_QPdo_something(%[[VAL_14]]) fastmath<contract> : (!fir.class<!fir.array<?xnone>>) -> ()
! CHECK: hlfir.end_associate %[[VAL_13]]#0, %[[VAL_13]]#2 : !fir.class<!fir.heap<!fir.array<?xnone>>>, i1
! CHECK: hlfir.destroy %[[VAL_8]] : !hlfir.expr<?xnone?>
! CHECK: return
! CHECK: }