Skip to content

Commit 5449408

Browse files
authored
[flang][CUDA] Add error & warning for device argument first dimension… (#136058)
… discontiguity For dummy assumed-shape/-rank device arrays, test the associated actual argument for stride-1 contiguity, and report an error when the actual argument is known to not be stride-1 contiguous and nonempty, or a warning when when the actual argument is not known to be empty or stride-1 contiguous.
1 parent 32145a5 commit 5449408

File tree

4 files changed

+118
-45
lines changed

4 files changed

+118
-45
lines changed

flang/include/flang/Evaluate/check-expression.h

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -113,21 +113,29 @@ extern template void CheckSpecificationExpr(
113113
// read-only data.
114114
template <typename A>
115115
std::optional<bool> IsContiguous(const A &, FoldingContext &,
116-
bool namedConstantSectionsAreContiguous = true);
116+
bool namedConstantSectionsAreContiguous = true,
117+
bool firstDimensionStride1 = false);
117118
extern template std::optional<bool> IsContiguous(const Expr<SomeType> &,
118-
FoldingContext &, bool namedConstantSectionsAreContiguous);
119+
FoldingContext &, bool namedConstantSectionsAreContiguous,
120+
bool firstDimensionStride1);
119121
extern template std::optional<bool> IsContiguous(const ArrayRef &,
120-
FoldingContext &, bool namedConstantSectionsAreContiguous);
122+
FoldingContext &, bool namedConstantSectionsAreContiguous,
123+
bool firstDimensionStride1);
121124
extern template std::optional<bool> IsContiguous(const Substring &,
122-
FoldingContext &, bool namedConstantSectionsAreContiguous);
125+
FoldingContext &, bool namedConstantSectionsAreContiguous,
126+
bool firstDimensionStride1);
123127
extern template std::optional<bool> IsContiguous(const Component &,
124-
FoldingContext &, bool namedConstantSectionsAreContiguous);
128+
FoldingContext &, bool namedConstantSectionsAreContiguous,
129+
bool firstDimensionStride1);
125130
extern template std::optional<bool> IsContiguous(const ComplexPart &,
126-
FoldingContext &, bool namedConstantSectionsAreContiguous);
131+
FoldingContext &, bool namedConstantSectionsAreContiguous,
132+
bool firstDimensionStride1);
127133
extern template std::optional<bool> IsContiguous(const CoarrayRef &,
128-
FoldingContext &, bool namedConstantSectionsAreContiguous);
129-
extern template std::optional<bool> IsContiguous(
130-
const Symbol &, FoldingContext &, bool namedConstantSectionsAreContiguous);
134+
FoldingContext &, bool namedConstantSectionsAreContiguous,
135+
bool firstDimensionStride1);
136+
extern template std::optional<bool> IsContiguous(const Symbol &,
137+
FoldingContext &, bool namedConstantSectionsAreContiguous,
138+
bool firstDimensionStride1);
131139
static inline std::optional<bool> IsContiguous(const SymbolRef &s,
132140
FoldingContext &c, bool namedConstantSectionsAreContiguous = true) {
133141
return IsContiguous(s.get(), c, namedConstantSectionsAreContiguous);

flang/lib/Evaluate/check-expression.cpp

Lines changed: 53 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -881,10 +881,12 @@ class IsContiguousHelper
881881
public:
882882
using Result = std::optional<bool>; // tri-state
883883
using Base = AnyTraverse<IsContiguousHelper, Result>;
884-
explicit IsContiguousHelper(
885-
FoldingContext &c, bool namedConstantSectionsAreContiguous)
886-
: Base{*this}, context_{c}, namedConstantSectionsAreContiguous_{
887-
namedConstantSectionsAreContiguous} {}
884+
explicit IsContiguousHelper(FoldingContext &c,
885+
bool namedConstantSectionsAreContiguous,
886+
bool firstDimensionStride1 = false)
887+
: Base{*this}, context_{c},
888+
namedConstantSectionsAreContiguous_{namedConstantSectionsAreContiguous},
889+
firstDimensionStride1_{firstDimensionStride1} {}
888890
using Base::operator();
889891

890892
template <typename T> Result operator()(const Constant<T> &) const {
@@ -956,13 +958,14 @@ class IsContiguousHelper
956958
if (!*baseIsContiguous) {
957959
return false;
958960
}
959-
// TODO could be true if base contiguous and this is only component, or
960-
// if base has only one element?
961+
// TODO: should be true if base is contiguous and this is only
962+
// component, or when the base has at most one element
961963
}
962964
return std::nullopt;
963965
}
964966
}
965967
Result operator()(const ComplexPart &x) const {
968+
// TODO: should be true when base is empty array, too
966969
return x.complex().Rank() == 0;
967970
}
968971
Result operator()(const Substring &x) const {
@@ -1061,6 +1064,9 @@ class IsContiguousHelper
10611064
auto dims{subscript.size()};
10621065
std::vector<bool> knownPartialSlice(dims, false);
10631066
for (auto j{dims}; j-- > 0;) {
1067+
if (j == 0 && firstDimensionStride1_ && !result.value_or(true)) {
1068+
result.reset(); // ignore problems on later dimensions
1069+
}
10641070
std::optional<ConstantSubscript> dimLbound;
10651071
std::optional<ConstantSubscript> dimUbound;
10661072
std::optional<ConstantSubscript> dimExtent;
@@ -1083,18 +1089,20 @@ class IsContiguousHelper
10831089
dimExtent = 0;
10841090
}
10851091
}
1086-
10871092
if (const auto *triplet{std::get_if<Triplet>(&subscript[j].u)}) {
10881093
++rank;
1094+
const Expr<SubscriptInteger> *lowerBound{triplet->GetLower()};
1095+
const Expr<SubscriptInteger> *upperBound{triplet->GetUpper()};
1096+
std::optional<ConstantSubscript> lowerVal{lowerBound
1097+
? ToInt64(Fold(context_, Expr<SubscriptInteger>{*lowerBound}))
1098+
: dimLbound};
1099+
std::optional<ConstantSubscript> upperVal{upperBound
1100+
? ToInt64(Fold(context_, Expr<SubscriptInteger>{*upperBound}))
1101+
: dimUbound};
10891102
if (auto stride{ToInt64(triplet->stride())}) {
1090-
const Expr<SubscriptInteger> *lowerBound{triplet->GetLower()};
1091-
const Expr<SubscriptInteger> *upperBound{triplet->GetUpper()};
1092-
std::optional<ConstantSubscript> lowerVal{lowerBound
1093-
? ToInt64(Fold(context_, Expr<SubscriptInteger>{*lowerBound}))
1094-
: dimLbound};
1095-
std::optional<ConstantSubscript> upperVal{upperBound
1096-
? ToInt64(Fold(context_, Expr<SubscriptInteger>{*upperBound}))
1097-
: dimUbound};
1103+
if (j == 0 && *stride == 1 && firstDimensionStride1_) {
1104+
result = *stride == 1; // contiguous or empty if so
1105+
}
10981106
if (lowerVal && upperVal) {
10991107
if (*lowerVal < *upperVal) {
11001108
if (*stride < 0) {
@@ -1110,23 +1118,31 @@ class IsContiguousHelper
11101118
*lowerVal + *stride >= *upperVal) {
11111119
result = false; // discontiguous if not empty
11121120
}
1113-
} else {
1114-
mayBeEmpty = true;
1121+
} else { // bounds known and equal
1122+
if (j == 0 && firstDimensionStride1_) {
1123+
result = true; // stride doesn't matter
1124+
}
1125+
}
1126+
} else { // bounds not both known
1127+
mayBeEmpty = true;
1128+
}
1129+
} else { // stride not known
1130+
if (lowerVal && upperVal && *lowerVal == *upperVal) {
1131+
// stride doesn't matter when bounds are equal
1132+
if (j == 0 && firstDimensionStride1_) {
1133+
result = true;
11151134
}
11161135
} else {
11171136
mayBeEmpty = true;
11181137
}
1119-
} else {
1120-
mayBeEmpty = true;
11211138
}
1122-
} else if (subscript[j].Rank() > 0) {
1139+
} else if (subscript[j].Rank() > 0) { // vector subscript
11231140
++rank;
11241141
if (!result) {
1125-
result = false; // vector subscript
1142+
result = false;
11261143
}
11271144
mayBeEmpty = true;
1128-
} else {
1129-
// Scalar subscript.
1145+
} else { // scalar subscript
11301146
if (dimExtent && *dimExtent > 1) {
11311147
knownPartialSlice[j] = true;
11321148
}
@@ -1138,7 +1154,7 @@ class IsContiguousHelper
11381154
if (result) {
11391155
return result;
11401156
}
1141-
// Not provably discontiguous at this point.
1157+
// Not provably contiguous or discontiguous at this point.
11421158
// Return "true" if simply contiguous, otherwise nullopt.
11431159
for (auto j{subscript.size()}; j-- > 0;) {
11441160
if (const auto *triplet{std::get_if<Triplet>(&subscript[j].u)}) {
@@ -1170,33 +1186,36 @@ class IsContiguousHelper
11701186

11711187
FoldingContext &context_;
11721188
bool namedConstantSectionsAreContiguous_{false};
1189+
bool firstDimensionStride1_{false};
11731190
};
11741191

11751192
template <typename A>
11761193
std::optional<bool> IsContiguous(const A &x, FoldingContext &context,
1177-
bool namedConstantSectionsAreContiguous) {
1194+
bool namedConstantSectionsAreContiguous, bool firstDimensionStride1) {
11781195
if (!IsVariable(x) &&
11791196
(namedConstantSectionsAreContiguous || !ExtractDataRef(x, true, true))) {
11801197
return true;
11811198
} else {
1182-
return IsContiguousHelper{context, namedConstantSectionsAreContiguous}(x);
1199+
return IsContiguousHelper{
1200+
context, namedConstantSectionsAreContiguous, firstDimensionStride1}(x);
11831201
}
11841202
}
11851203

11861204
template std::optional<bool> IsContiguous(const Expr<SomeType> &,
1187-
FoldingContext &, bool namedConstantSectionsAreContiguous);
1205+
FoldingContext &, bool namedConstantSectionsAreContiguous,
1206+
bool firstDimensionStride1);
11881207
template std::optional<bool> IsContiguous(const ArrayRef &, FoldingContext &,
1189-
bool namedConstantSectionsAreContiguous);
1208+
bool namedConstantSectionsAreContiguous, bool firstDimensionStride1);
11901209
template std::optional<bool> IsContiguous(const Substring &, FoldingContext &,
1191-
bool namedConstantSectionsAreContiguous);
1210+
bool namedConstantSectionsAreContiguous, bool firstDimensionStride1);
11921211
template std::optional<bool> IsContiguous(const Component &, FoldingContext &,
1193-
bool namedConstantSectionsAreContiguous);
1212+
bool namedConstantSectionsAreContiguous, bool firstDimensionStride1);
11941213
template std::optional<bool> IsContiguous(const ComplexPart &, FoldingContext &,
1195-
bool namedConstantSectionsAreContiguous);
1214+
bool namedConstantSectionsAreContiguous, bool firstDimensionStride1);
11961215
template std::optional<bool> IsContiguous(const CoarrayRef &, FoldingContext &,
1197-
bool namedConstantSectionsAreContiguous);
1198-
template std::optional<bool> IsContiguous(
1199-
const Symbol &, FoldingContext &, bool namedConstantSectionsAreContiguous);
1216+
bool namedConstantSectionsAreContiguous, bool firstDimensionStride1);
1217+
template std::optional<bool> IsContiguous(const Symbol &, FoldingContext &,
1218+
bool namedConstantSectionsAreContiguous, bool firstDimensionStride1);
12001219

12011220
// IsErrorExpr()
12021221
struct IsErrorExprHelper : public AnyTraverse<IsErrorExprHelper, bool> {

flang/lib/Semantics/check-call.cpp

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1015,10 +1015,26 @@ static void CheckExplicitDataArg(const characteristics::DummyDataObject &dummy,
10151015
actualDataAttr = common::CUDADataAttr::Device;
10161016
}
10171017
}
1018+
if (dummyDataAttr == common::CUDADataAttr::Device &&
1019+
(dummyIsAssumedShape || dummyIsAssumedRank)) {
1020+
if (auto contig{evaluate::IsContiguous(actual, foldingContext,
1021+
/*namedConstantSectionsAreContiguous=*/true,
1022+
/*firstDimensionStride1=*/true)}) {
1023+
if (!*contig) {
1024+
messages.Say(
1025+
"actual argument associated with assumed shape/rank device %s is known to be discontiguous on its first dimension"_err_en_US,
1026+
dummyName);
1027+
}
1028+
} else {
1029+
messages.Say(
1030+
"actual argument associated with assumed shape/rank device %s is not known to be contiguous on its first dimension"_warn_en_US,
1031+
dummyName);
1032+
}
1033+
}
10181034
std::optional<std::string> warning;
1019-
bool isHostDeviceProc = procedure.cudaSubprogramAttrs &&
1035+
bool isHostDeviceProc{procedure.cudaSubprogramAttrs &&
10201036
*procedure.cudaSubprogramAttrs ==
1021-
common::CUDASubprogramAttrs::HostDevice;
1037+
common::CUDASubprogramAttrs::HostDevice};
10221038
if (!common::AreCompatibleCUDADataAttrs(dummyDataAttr, actualDataAttr,
10231039
dummy.ignoreTKR, &warning, /*allowUnifiedMatchingRule=*/true,
10241040
isHostDeviceProc, &context.languageFeatures())) {

flang/test/Semantics/cuf19.cuf

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
! RUN: %python %S/test_errors.py %s %flang_fc1
2+
interface
3+
subroutine foo(a)
4+
real, device, dimension(:,:) :: a
5+
end
6+
end interface
7+
8+
real, device, allocatable :: a(:,:)
9+
complex, device, allocatable :: z(:,:)
10+
integer :: i = 2, j = 3
11+
allocate(a(10,10))
12+
allocate(z(10,10))
13+
call foo(a) ! ok
14+
call foo(a(:,:)) ! ok
15+
call foo(a(1:10,1:10)) ! ok
16+
!ERROR: actual argument associated with assumed shape/rank device dummy argument 'a=' is known to be discontiguous on its first dimension
17+
call foo(a(1:10:2,1:10))
18+
call foo(a(1:0:2,1:10)) ! empty dimension is ok
19+
call foo(a(1:10:2,1:0)) ! any empty dimension is ok
20+
call foo(a(1:10,1:10:2)) ! discontiguous second dimension is ok
21+
!WARNING: actual argument associated with assumed shape/rank device dummy argument 'a=' is not known to be contiguous on its first dimension
22+
call foo(a(1:10:i,1:10))
23+
!WARNING: actual argument associated with assumed shape/rank device dummy argument 'a=' is not known to be contiguous on its first dimension
24+
call foo(a(1:i:2,1:10))
25+
call foo(a(i:j:1,1:10)) ! stride 1, okay, despite unknown bounds
26+
!WARNING: actual argument associated with assumed shape/rank device dummy argument 'a=' is not known to be contiguous on its first dimension
27+
call foo(a(i:j:-1,1:10))
28+
!ERROR: actual argument associated with assumed shape/rank device dummy argument 'a=' is known to be discontiguous on its first dimension
29+
call foo(z%re)
30+
end

0 commit comments

Comments
 (0)