Skip to content

Commit b348545

Browse files
committed
Lower lbound/ubound with non constant DIM argument
Lower UBOUND with non compile time constant DIM argument using LBOUND and SIZE (other cases are folded by the front-end to descriptor inquiries). This required implementing LBOUND. To get LBOUND correctly, the "asBox" intrinsic argument option needed to be ramped-up a bit. It was not fulfilling its contract entirely because it was not placing the fir.box in a fir::BoxValue ExtendedValue. Ensure the correct category is created from the argument while ensuring its non default lower bounds/non deferred parameters information are not lost when emboxing the value. Fix all the places that expected an "UnboxedValue" with the asBox option, and use asBox in SIZE lowering to avoid evaluating array section in temps to compute their size.
1 parent 1066e81 commit b348545

File tree

7 files changed

+306
-61
lines changed

7 files changed

+306
-61
lines changed

flang/include/flang/Optimizer/Builder/FIRBuilder.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -411,6 +411,18 @@ llvm::SmallVector<mlir::Value> getExtents(fir::FirOpBuilder &builder,
411411
fir::ExtendedValue readBoxValue(fir::FirOpBuilder &builder, mlir::Location loc,
412412
const fir::BoxValue &box);
413413

414+
/// Get non default (not all ones) lower bounds of \p exv. Returns empty
415+
/// vector if the lower bounds are all ones.
416+
llvm::SmallVector<mlir::Value>
417+
getNonDefaultLowerBounds(fir::FirOpBuilder &builder, mlir::Location loc,
418+
const fir::ExtendedValue &exv);
419+
420+
/// Return length parameters associated to \p exv that are not deferred (that
421+
/// are available without having to read any fir.box values).
422+
/// Empty if \p exv has no length parameters or if they are all deferred.
423+
llvm::SmallVector<mlir::Value>
424+
getNonDeferredLengthParams(const fir::ExtendedValue &exv);
425+
414426
//===--------------------------------------------------------------------===//
415427
// String literal helper helpers
416428
//===--------------------------------------------------------------------===//

flang/lib/Lower/ConvertExpr.cpp

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1657,6 +1657,18 @@ class ScalarExprLowering {
16571657
return gen(expr);
16581658
}
16591659

1660+
/// Helper to lower intrinsic arguments to a fir::BoxValue.
1661+
/// It preserves all the non default lower bounds/non deferred length
1662+
/// parameter information.
1663+
ExtValue lowerIntrinsicArgumentAsBox(const Fortran::lower::SomeExpr &expr) {
1664+
mlir::Location loc = getLoc();
1665+
ExtValue exv = genBoxArg(expr);
1666+
mlir::Value box = builder.createBox(loc, exv);
1667+
return fir::BoxValue(
1668+
box, fir::factory::getNonDefaultLowerBounds(builder, loc, exv),
1669+
fir::factory::getNonDeferredLengthParams(exv));
1670+
}
1671+
16601672
/// Generate a call to an intrinsic function.
16611673
ExtValue
16621674
genIntrinsicRef(const Fortran::evaluate::ProcedureRef &procRef,
@@ -1693,7 +1705,7 @@ class ScalarExprLowering {
16931705
operands.emplace_back(gen(*expr));
16941706
continue;
16951707
case Fortran::lower::LowerIntrinsicArgAs::Box:
1696-
operands.emplace_back(builder.createBox(getLoc(), genBoxArg(*expr)));
1708+
operands.emplace_back(lowerIntrinsicArgumentAsBox(*expr));
16971709
continue;
16981710
case Fortran::lower::LowerIntrinsicArgAs::Inquired:
16991711
operands.emplace_back(lowerIntrinsicArgumentAsInquired(*expr));

flang/lib/Lower/IntrinsicCall.cpp

Lines changed: 69 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -477,6 +477,7 @@ struct IntrinsicLibrary {
477477
mlir::Value genIor(mlir::Type, llvm::ArrayRef<mlir::Value>);
478478
mlir::Value genIshft(mlir::Type, llvm::ArrayRef<mlir::Value>);
479479
mlir::Value genIshftc(mlir::Type, llvm::ArrayRef<mlir::Value>);
480+
fir::ExtendedValue genLbound(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
480481
fir::ExtendedValue genLen(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
481482
fir::ExtendedValue genLenTrim(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
482483
fir::ExtendedValue genMatmul(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
@@ -518,6 +519,7 @@ struct IntrinsicLibrary {
518519
fir::ExtendedValue genTranspose(mlir::Type,
519520
llvm::ArrayRef<fir::ExtendedValue>);
520521
fir::ExtendedValue genTrim(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
522+
fir::ExtendedValue genUbound(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
521523
fir::ExtendedValue genUnpack(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
522524
fir::ExtendedValue genVerify(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
523525
/// Implement all conversion functions like DBLE, the first argument is
@@ -711,6 +713,10 @@ static constexpr IntrinsicHandler handlers[]{
711713
{"ior", &I::genIor},
712714
{"ishft", &I::genIshft},
713715
{"ishftc", &I::genIshftc},
716+
{"lbound",
717+
&I::genLbound,
718+
{{{"array", asBox}, {"dim", asValue}, {"kind", asValue}}},
719+
/*isElemental=*/false},
714720
{"len", &I::genLen},
715721
{"len_trim", &I::genLenTrim},
716722
{"lge", &I::genCharacterCompare<mlir::arith::CmpIPredicate::sge>},
@@ -812,7 +818,7 @@ static constexpr IntrinsicHandler handlers[]{
812818
{"sign", &I::genSign},
813819
{"size",
814820
&I::genSize,
815-
{{{"array", asAddr}, {"dim", asValue}, {"kind", asValue}}},
821+
{{{"array", asBox}, {"dim", asValue}, {"kind", asValue}}},
816822
/*isElemental=*/false},
817823
{"spacing", &I::genSpacing},
818824
{"spread",
@@ -836,6 +842,10 @@ static constexpr IntrinsicHandler handlers[]{
836842
{{{"matrix", asAddr}}},
837843
/*isElemental=*/false},
838844
{"trim", &I::genTrim, {{{"string", asAddr}}}, /*isElemental=*/false},
845+
{"ubound",
846+
&I::genUbound,
847+
{{{"array", asBox}, {"dim", asValue}, {"kind", asValue}}},
848+
/*isElemental=*/false},
839849
{"unpack",
840850
&I::genUnpack,
841851
{{{"vector", asAddr}, {"mask", asAddr}, {"field", asAddr}}},
@@ -1916,7 +1926,7 @@ IntrinsicLibrary::genAssociated(mlir::Type resultType,
19161926
fir::factory::getMutableIRBox(builder, loc, *pointer);
19171927
auto pointerBox = builder.create<fir::LoadOp>(loc, pointerBoxRef);
19181928
return Fortran::lower::genAssociated(builder, loc, pointerBox,
1919-
*args[1].getUnboxed());
1929+
fir::getBase(args[1]));
19201930
}
19211931

19221932
// AINT
@@ -2796,23 +2806,23 @@ IntrinsicLibrary::genProduct(mlir::Type resultType,
27962806
// RANDOM_INIT
27972807
void IntrinsicLibrary::genRandomInit(llvm::ArrayRef<fir::ExtendedValue> args) {
27982808
assert(args.size() == 2);
2799-
Fortran::lower::genRandomInit(builder, loc, *args[0].getUnboxed(),
2800-
*args[1].getUnboxed());
2809+
Fortran::lower::genRandomInit(builder, loc, fir::getBase(args[0]),
2810+
fir::getBase(args[1]));
28012811
}
28022812

28032813
// RANDOM_NUMBER
28042814
void IntrinsicLibrary::genRandomNumber(
28052815
llvm::ArrayRef<fir::ExtendedValue> args) {
28062816
assert(args.size() == 1);
2807-
Fortran::lower::genRandomNumber(builder, loc, *args[0].getUnboxed());
2817+
Fortran::lower::genRandomNumber(builder, loc, fir::getBase(args[0]));
28082818
}
28092819

28102820
// RANDOM_SEED
28112821
void IntrinsicLibrary::genRandomSeed(llvm::ArrayRef<fir::ExtendedValue> args) {
28122822
assert(args.size() == 3);
28132823
for (int i = 0; i < 3; ++i)
28142824
if (isPresent(args[i])) {
2815-
Fortran::lower::genRandomSeed(builder, loc, i, *args[i].getUnboxed());
2825+
Fortran::lower::genRandomSeed(builder, loc, i, fir::getBase(args[i]));
28162826
return;
28172827
}
28182828
Fortran::lower::genRandomSeed(builder, loc, -1, mlir::Value{});
@@ -3014,12 +3024,10 @@ mlir::Value IntrinsicLibrary::genSign(mlir::Type resultType,
30143024
fir::ExtendedValue
30153025
IntrinsicLibrary::genSize(mlir::Type resultType,
30163026
llvm::ArrayRef<fir::ExtendedValue> args) {
3017-
// TODO: handle assumed-rank arrays, especially a dummy whose actual argument
3018-
// is an assumed-size array
30193027
assert(args.size() == 3);
3020-
3021-
// Calls to SIZE that don't have the DIM argument are handled elsewhere
3022-
assert(!isAbsent(args[1]));
3028+
if (const auto *boxValue = args[0].getBoxOf<fir::BoxValue>())
3029+
if (boxValue->hasAssumedRank())
3030+
TODO(loc, "SIZE intrinsic with assumed rank argument");
30233031

30243032
// Handle the ARRAY argument
30253033
mlir::Value array = builder.createBox(loc, args[0]);
@@ -3049,6 +3057,54 @@ IntrinsicLibrary::genSize(mlir::Type resultType,
30493057
return builder.createConvert(loc, resultType, result);
30503058
}
30513059

3060+
// LBOUND
3061+
fir::ExtendedValue
3062+
IntrinsicLibrary::genLbound(mlir::Type resultType,
3063+
llvm::ArrayRef<fir::ExtendedValue> args) {
3064+
assert(args.size() == 3);
3065+
if (const auto *boxValue = args[0].getBoxOf<fir::BoxValue>())
3066+
if (boxValue->hasAssumedRank())
3067+
TODO(loc, "LBOUND intrinsic with assumed rank argument");
3068+
3069+
// Calls to LBOUND that don't have the DIM argument, or for which
3070+
// the DIM is a compile time constant, are folded to descriptor inquiries by
3071+
// semantics.
3072+
assert(!isAbsent(args[1]));
3073+
const fir::ExtendedValue &array = args[0];
3074+
llvm::SmallVector<mlir::Value> lbounds =
3075+
fir::factory::getNonDefaultLowerBounds(builder, loc, array);
3076+
if (lbounds.empty())
3077+
return builder.createIntegerConstant(loc, resultType, 1);
3078+
mlir::Type lbArrayType = fir::SequenceType::get(
3079+
{static_cast<fir::SequenceType::Extent>(array.rank())}, resultType);
3080+
auto lbArray = builder.createTemporary(loc, lbArrayType);
3081+
auto lbAddrType = builder.getRefType(resultType);
3082+
auto indexType = builder.getIndexType();
3083+
for (auto lb : llvm::enumerate(lbounds)) {
3084+
auto index = builder.createIntegerConstant(loc, indexType, lb.index());
3085+
auto lbAddr =
3086+
builder.create<fir::CoordinateOp>(loc, lbAddrType, lbArray, index);
3087+
mlir::Value lbValue = builder.createConvert(loc, resultType, lb.value());
3088+
builder.create<fir::StoreOp>(loc, lbValue, lbAddr);
3089+
}
3090+
mlir::Value resAddr = builder.create<fir::CoordinateOp>(
3091+
loc, lbAddrType, lbArray, fir::getBase(args[1]));
3092+
return builder.create<fir::LoadOp>(loc, resAddr);
3093+
}
3094+
3095+
// UBOUND
3096+
fir::ExtendedValue
3097+
IntrinsicLibrary::genUbound(mlir::Type resultType,
3098+
llvm::ArrayRef<fir::ExtendedValue> args) {
3099+
assert(args.size() == 3);
3100+
mlir::Value extent = fir::getBase(genSize(resultType, args));
3101+
mlir::Value lbound = fir::getBase(genLbound(resultType, args));
3102+
3103+
mlir::Value one = builder.createIntegerConstant(loc, resultType, 1);
3104+
mlir::Value ubound = builder.create<mlir::arith::SubIOp>(loc, lbound, one);
3105+
return builder.create<mlir::arith::AddIOp>(loc, ubound, extent);
3106+
}
3107+
30523108
// SPACING
30533109
mlir::Value IntrinsicLibrary::genSpacing(mlir::Type resultType,
30543110
llvm::ArrayRef<mlir::Value> args) {
@@ -3102,8 +3158,8 @@ IntrinsicLibrary::genSum(mlir::Type resultType,
31023158
// SYSTEM_CLOCK
31033159
void IntrinsicLibrary::genSystemClock(llvm::ArrayRef<fir::ExtendedValue> args) {
31043160
assert(args.size() == 3);
3105-
Fortran::lower::genSystemClock(builder, loc, *args[0].getUnboxed(),
3106-
*args[1].getUnboxed(), *args[2].getUnboxed());
3161+
Fortran::lower::genSystemClock(builder, loc, fir::getBase(args[0]),
3162+
fir::getBase(args[1]), fir::getBase(args[2]));
31073163
}
31083164

31093165
// TRANSFER

flang/lib/Optimizer/Builder/FIRBuilder.cpp

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -646,6 +646,46 @@ fir::ExtendedValue fir::factory::readBoxValue(fir::FirOpBuilder &builder,
646646
box.getLBounds());
647647
}
648648

649+
llvm::SmallVector<mlir::Value>
650+
fir::factory::getNonDefaultLowerBounds(fir::FirOpBuilder &builder,
651+
mlir::Location loc,
652+
const fir::ExtendedValue &exv) {
653+
return exv.match(
654+
[&](const fir::ArrayBoxValue &array) -> llvm::SmallVector<mlir::Value> {
655+
return {array.getLBounds().begin(), array.getLBounds().end()};
656+
},
657+
[&](const fir::CharArrayBoxValue &array)
658+
-> llvm::SmallVector<mlir::Value> {
659+
return {array.getLBounds().begin(), array.getLBounds().end()};
660+
},
661+
[&](const fir::BoxValue &box) -> llvm::SmallVector<mlir::Value> {
662+
return {box.getLBounds().begin(), box.getLBounds().end()};
663+
},
664+
[&](const fir::MutableBoxValue &box) -> llvm::SmallVector<mlir::Value> {
665+
auto load = fir::factory::genMutableBoxRead(builder, loc, box);
666+
return fir::factory::getNonDefaultLowerBounds(builder, loc, load);
667+
},
668+
[&](const auto &) -> llvm::SmallVector<mlir::Value> { return {}; });
669+
}
670+
671+
llvm::SmallVector<mlir::Value>
672+
fir::factory::getNonDeferredLengthParams(const fir::ExtendedValue &exv) {
673+
return exv.match(
674+
[&](const fir::CharArrayBoxValue &character)
675+
-> llvm::SmallVector<mlir::Value> { return {character.getLen()}; },
676+
[&](const fir::CharBoxValue &character)
677+
-> llvm::SmallVector<mlir::Value> { return {character.getLen()}; },
678+
[&](const fir::MutableBoxValue &box) -> llvm::SmallVector<mlir::Value> {
679+
return {box.nonDeferredLenParams().begin(),
680+
box.nonDeferredLenParams().end()};
681+
},
682+
[&](const fir::BoxValue &box) -> llvm::SmallVector<mlir::Value> {
683+
return {box.getExplicitParameters().begin(),
684+
box.getExplicitParameters().end()};
685+
},
686+
[&](const auto &) -> llvm::SmallVector<mlir::Value> { return {}; });
687+
}
688+
649689
std::string fir::factory::uniqueCGIdent(llvm::StringRef prefix,
650690
llvm::StringRef name) {
651691
// For "long" identifiers use a hash value
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
! RUN: bbc -emit-fir %s -o - | FileCheck %s
2+
3+
4+
! CHECK-LABEL: func @_QPlbound_test(
5+
! CHECK-SAME: %[[VAL_0:.*]]: !fir.box<!fir.array<?x?xf32>>,
6+
! CHECK-SAME: %[[VAL_1:.*]]: !fir.ref<i64>,
7+
! CHECK-SAME: %[[VAL_2:.*]]: !fir.ref<i64>) {
8+
subroutine lbound_test(a, dim, res)
9+
real, dimension(:, :) :: a
10+
integer(8):: dim, res
11+
! CHECK: %[[VAL_3:.*]] = arith.constant 1 : i64
12+
! CHECK: fir.store %[[VAL_3]] to %[[VAL_2]] : !fir.ref<i64>
13+
res = lbound(a, dim, 8)
14+
end subroutine
15+
16+
! CHECK-LABEL: func @_QPlbound_test_2(
17+
! CHECK-SAME: %[[VAL_0:.*]]: !fir.box<!fir.array<?x?xf32>>,
18+
! CHECK-SAME: %[[VAL_1:.*]]: !fir.ref<i64>,
19+
! CHECK-SAME: %[[VAL_2:.*]]: !fir.ref<i64>) {
20+
subroutine lbound_test_2(a, dim, res)
21+
real, dimension(:, 2:) :: a
22+
integer(8):: dim, res
23+
! CHECK: %[[VAL_3:.*]] = fir.alloca !fir.array<2xi64>
24+
! CHECK: %[[VAL_4:.*]] = arith.constant 1 : i64
25+
! CHECK: %[[VAL_5:.*]] = fir.convert %[[VAL_4]] : (i64) -> index
26+
! CHECK: %[[VAL_6:.*]] = arith.constant 2 : i64
27+
! CHECK: %[[VAL_7:.*]] = fir.convert %[[VAL_6]] : (i64) -> index
28+
! CHECK: %[[VAL_8:.*]] = fir.load %[[VAL_1]] : !fir.ref<i64>
29+
! CHECK: %[[VAL_9:.*]] = arith.constant 0 : index
30+
! CHECK: %[[VAL_10:.*]] = fir.coordinate_of %[[VAL_3]], %[[VAL_9]] : (!fir.ref<!fir.array<2xi64>>, index) -> !fir.ref<i64>
31+
! CHECK: %[[VAL_11:.*]] = fir.convert %[[VAL_5]] : (index) -> i64
32+
! CHECK: fir.store %[[VAL_11]] to %[[VAL_10]] : !fir.ref<i64>
33+
! CHECK: %[[VAL_12:.*]] = arith.constant 1 : index
34+
! CHECK: %[[VAL_13:.*]] = fir.coordinate_of %[[VAL_3]], %[[VAL_12]] : (!fir.ref<!fir.array<2xi64>>, index) -> !fir.ref<i64>
35+
! CHECK: %[[VAL_14:.*]] = fir.convert %[[VAL_7]] : (index) -> i64
36+
! CHECK: fir.store %[[VAL_14]] to %[[VAL_13]] : !fir.ref<i64>
37+
! CHECK: %[[VAL_15:.*]] = fir.coordinate_of %[[VAL_3]], %[[VAL_8]] : (!fir.ref<!fir.array<2xi64>>, i64) -> !fir.ref<i64>
38+
! CHECK: %[[VAL_16:.*]] = fir.load %[[VAL_15]] : !fir.ref<i64>
39+
! CHECK: fir.store %[[VAL_16]] to %[[VAL_2]] : !fir.ref<i64>
40+
res = lbound(a, dim, 8)
41+
end subroutine
42+
43+
! CHECK-LABEL: func @_QPlbound_test_3(
44+
! CHECK-SAME: %[[VAL_0:.*]]: !fir.ref<!fir.array<9x?xf32>>,
45+
! CHECK-SAME: %[[VAL_1:.*]]: !fir.ref<i64>,
46+
! CHECK-SAME: %[[VAL_2:.*]]: !fir.ref<i64>) {
47+
subroutine lbound_test_3(a, dim, res)
48+
real, dimension(2:10, 3:*) :: a
49+
integer(8):: dim, res
50+
! CHECK: %[[VAL_3:.*]] = fir.alloca !fir.array<2xi64>
51+
! CHECK: %[[VAL_4:.*]] = arith.constant 2 : index
52+
! CHECK: %[[VAL_5:.*]] = arith.constant 3 : index
53+
! CHECK: %[[VAL_6:.*]] = fir.load %[[VAL_1]] : !fir.ref<i64>
54+
! CHECK: %[[VAL_7:.*]] = arith.constant 0 : index
55+
! CHECK: %[[VAL_8:.*]] = fir.coordinate_of %[[VAL_3]], %[[VAL_7]] : (!fir.ref<!fir.array<2xi64>>, index) -> !fir.ref<i64>
56+
! CHECK: %[[VAL_9:.*]] = fir.convert %[[VAL_4]] : (index) -> i64
57+
! CHECK: fir.store %[[VAL_9]] to %[[VAL_8]] : !fir.ref<i64>
58+
! CHECK: %[[VAL_10:.*]] = arith.constant 1 : index
59+
! CHECK: %[[VAL_11:.*]] = fir.coordinate_of %[[VAL_3]], %[[VAL_10]] : (!fir.ref<!fir.array<2xi64>>, index) -> !fir.ref<i64>
60+
! CHECK: %[[VAL_12:.*]] = fir.convert %[[VAL_5]] : (index) -> i64
61+
! CHECK: fir.store %[[VAL_12]] to %[[VAL_11]] : !fir.ref<i64>
62+
! CHECK: %[[VAL_13:.*]] = fir.coordinate_of %[[VAL_3]], %[[VAL_6]] : (!fir.ref<!fir.array<2xi64>>, i64) -> !fir.ref<i64>
63+
! CHECK: %[[VAL_14:.*]] = fir.load %[[VAL_13]] : !fir.ref<i64>
64+
! CHECK: fir.store %[[VAL_14]] to %[[VAL_2]] : !fir.ref<i64>
65+
res = lbound(a, dim, 8)
66+
end subroutine

0 commit comments

Comments
 (0)