Skip to content

[flang] lower vector subscripted polymorphic designators #84778

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Mar 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 10 additions & 3 deletions flang/include/flang/Optimizer/HLFIR/HLFIROps.td
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
);
Expand All @@ -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)}];

Expand Down
27 changes: 15 additions & 12 deletions flang/lib/Lower/ConvertExprToHLFIR.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -761,9 +761,17 @@ class HlfirDesignatorBuilder {
// 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();
Expand Down Expand Up @@ -804,15 +812,8 @@ class HlfirDesignatorBuilder {
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);
}
Expand Down Expand Up @@ -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);
Expand Down
6 changes: 3 additions & 3 deletions flang/lib/Optimizer/Builder/HLFIRTools.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1033,9 +1033,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) {
Expand Down
50 changes: 27 additions & 23 deletions flang/lib/Optimizer/HLFIR/IR/HLFIROps.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1406,33 +1406,45 @@ void hlfir::AsExprOp::getEffects(
// ElementalOp
//===----------------------------------------------------------------------===//

void hlfir::ElementalOp::build(mlir::OpBuilder &builder,
mlir::OperationState &odsState,
mlir::Type resultType, mlir::Value shape,
mlir::Value mold, mlir::ValueRange typeparams,
bool isUnordered) {
/// 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.addTypes(resultType);
odsState.addAttribute(
getOperandSegmentSizesAttrName(odsState.name),
Op::getOperandSegmentSizesAttrName(odsState.name),
builder.getDenseI32ArrayAttr({/*shape=*/1, (mold ? 1 : 0),
static_cast<int32_t>(typeparams.size())}));
if (isUnordered)
odsState.addAttribute(getUnorderedAttrName(odsState.name),
odsState.addAttribute(Op::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();
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.addTypes(resultType);
buildElemental<hlfir::ElementalOp>(builder, odsState, shape, mold, typeparams,
isUnordered);
}

mlir::Value hlfir::ElementalOp::getElementEntity() {
return mlir::cast<hlfir::YieldElementOp>(getBody()->back()).getElementValue();
}
Expand Down Expand Up @@ -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();
}
Expand Down
42 changes: 42 additions & 0 deletions flang/test/HLFIR/element-addr.fir
Original file line number Diff line number Diff line change
Expand Up @@ -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: }
36 changes: 35 additions & 1 deletion flang/test/Lower/HLFIR/vector-subscript-as-value.f90
Original file line number Diff line number Diff line change
@@ -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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not for this commit, but I think we should keep the defaults in bbc in line with flang-new. As I understand it, polymorphism is enabled by default now?


subroutine foo(x, y)
integer :: x(100)
Expand Down Expand Up @@ -182,3 +182,37 @@ subroutine substring(c, vector, i, j)
! 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: }