llvm-project/clang/test/OpenMP/loop_collapse_2.cpp
Julian Brown 3188e9b4e0
[clang][OpenMP] Diagnose badly-formed collapsed imperfect loop nests (#60678) (#101305)
This patch fixes a couple of cases where Clang aborts with loop nests
that are being collapsed (via the relevant OpenMP clause) into a new,
combined loop.

The problematic cases happen when a variable declared within the loop
nest is used in the (init, condition, iter) statement of a more
deeply-nested loop. I don't think these cases (generally?) fall under
the non-rectangular loop nest rules as defined in OpenMP 5.0+, but I
could be wrong (and anyway, emitting an error is better than crashing).

In terms of implementation: the crash happens because (to a first
approximation) all the loop bounds calculations are pulled out to the
start of the new, combined loop, but variables declared in the loop nest
"haven't been seen yet". I believe there is special handling for
iteration variables declared in "for" init statements, but not for
variables declared elsewhere in the "imperfect" parts of a loop nest.

So, this patch tries to diagnose the troublesome cases before they can
cause a crash. This is slightly awkward because at the point where we
want to do the diagnosis (SemaOpenMP.cpp), we don't have scope
information readily available. Instead we "manually" scan through the
AST of the loop nest looking for var decls (ForVarDeclFinder), then we
ensure we're not using any of those in loop control subexprs
(ForSubExprChecker). All that is only done when we have a "collapse"
clause.

Range-for loops can also cause crashes at present without this patch, so
are handled too.
2024-08-19 09:37:32 -04:00

81 lines
2.1 KiB
C++

// RUN: %clang_cc1 -fopenmp -fopenmp-version=50 -verify %s
// We just want to try out a range for statement... this seems a bit OTT.
template<typename T>
class fakevector {
T *contents;
long size;
public:
fakevector(long sz) : size(sz) {
contents = new T[sz];
}
~fakevector() {
delete[] contents;
}
T& operator[](long x) { return contents[x]; }
typedef T *iterator;
fakevector<T>::iterator begin() {
return &contents[0];
}
fakevector<T>::iterator end() {
return &contents[size];
}
};
void func( double *A, int N, int M, int NB ) {
#pragma omp parallel
{
int nblks = (N-1)/NB;
int lnb = ((N-1)/NB)*NB;
#pragma omp for collapse(2)
for (int jblk = 0 ; jblk < nblks ; jblk++ ) {
int jb = (jblk == nblks - 1 ? lnb : NB);
for (int jk = 0; jk < N; jk+=jb) { // expected-error{{cannot use variable 'jb' in collapsed imperfectly-nested loop increment statement}}
}
}
#pragma omp for collapse(2)
for (int a = 0; a < N; a++) {
for (int b = 0; b < M; b++) {
int cx = a+b < NB ? a : b;
for (int c = 0; c < cx; c++) {
}
}
}
fakevector<float> myvec{N};
#pragma omp for collapse(2)
for (auto &a : myvec) {
fakevector<float> myvec3{M};
for (auto &b : myvec3) { // expected-error{{cannot use variable 'myvec3' in collapsed imperfectly-nested loop init statement}}
}
}
fakevector<float> myvec2{M};
#pragma omp for collapse(3)
for (auto &a : myvec) {
for (auto &b : myvec2) {
int cx = a < b ? N : M;
for (int c = 0; c < cx; c++) { // expected-error {{cannot use variable 'cx' in collapsed imperfectly-nested loop condition statement}}
}
}
}
#pragma omp for collapse(3)
for (auto &a : myvec) {
int cx = a < 5 ? M : N;
for (auto &b : myvec2) {
for (int c = 0; c < cx; c++) { // expected-error{{cannot use variable 'cx' in collapsed imperfectly-nested loop condition statement}}
}
}
}
}
}
int main(void) {
double arr[256];
func (arr, 16, 16, 16);
return 0;
}