[Openmp-commits] [clang] [llvm] [openmp] [CLANG][OpenMP] Restore loop variable values after transformation directives (PR #214717)

Alexey Bataev via Openmp-commits openmp-commits at lists.llvm.org
Sat Oct 17 13:57:22 PDT 2026


alexey-bataev wrote:

Check these tests:
```
// Loop variables of transformation-affected loops must have the values
// they would have without the directive (OpenMP 6.0 loop transformation
// finalization rule). Each *_ref function is the untransformed reference;
// the transformed twin must print the same values.
//
// Build with a compiler that has the PR applied, run, compare against the
// CHECK lines. The tile/stripe rows currently produce j=5 on the PR:
// the restore of an inner dimension fires even when the enclosing
// dimension has zero iterations, but without the directive the inner loop
// would not have run at all, so j must be left untouched. The interchange
// row is left without a CHECK: the PR review ruled the shared case gets
// the exit value (3); confirm that this also covers tile/stripe.
#include <stdio.h>

void tile_ref(int n) {
  int i, j;
  i = 77;
  j = 111;
  for (i = 0; i < n; i++)
    for (j = 0; j < 5; j++)
      ;
  printf("tile_ref i=%d j=%d\n", i, j);
}

void tile(int n) {
  int i, j;
  i = 77;
  j = 111;
#pragma omp tile sizes(2, 3)
  for (i = 0; i < n; i++)
    for (j = 0; j < 5; j++)
      ;
  printf("tile i=%d j=%d\n", i, j);
}

void stripe_ref(int n) {
  int i, j;
  i = 77;
  j = 111;
  for (i = 0; i < n; i++)
    for (j = 0; j < 5; j++)
      ;
  printf("stripe_ref i=%d j=%d\n", i, j);
}

void stripe(int n) {
  int i, j;
  i = 77;
  j = 111;
#pragma omp stripe sizes(2, 3)
  for (i = 0; i < n; i++)
    for (j = 0; j < 5; j++)
      ;
  printf("stripe i=%d j=%d\n", i, j);
}

void interchange_ref(int n) {
  int i, j;
  i = 77;
  j = 111;
  for (i = 0; i < n; i++)
    for (j = 0; j < 3; j++)
      ;
  printf("interchange_ref i=%d j=%d\n", i, j);
}

void interchange(int n) {
  int i, j;
  i = 77;
  j = 111;
#pragma omp interchange
  for (i = 0; i < n; i++)
    for (j = 0; j < 3; j++)
      ;
  printf("interchange i=%d j=%d\n", i, j);
}

void tile_full_ref(void) {
  int i, j;
  i = 77;
  j = 111;
  for (i = 0; i < 6; i++)
    for (j = 0; j < 5; j++)
      ;
  printf("tile_full_ref i=%d j=%d\n", i, j);
}

void tile_full(void) {
  int i, j;
  i = 77;
  j = 111;
#pragma omp tile sizes(2, 3)
  for (i = 0; i < 6; i++)
    for (j = 0; j < 5; j++)
      ;
  printf("tile_full i=%d j=%d\n", i, j);
}

int main(void) {
  tile_ref(0);
  tile(0);
  stripe_ref(0);
  stripe(0);
  interchange_ref(0);
  interchange(0);
  tile_full_ref();
  tile_full();
  return 0;
}

// Spec-correct output; the failing rows show the issue on the PR:
// CHECK: tile_ref i=0 j=111
// CHECK-NOT: tile i=0 j=5
// CHECK: tile i=0 j=111
// CHECK: stripe_ref i=0 j=111
// CHECK-NOT: stripe i=0 j=5
// CHECK: stripe i=0 j=111
// CHECK: interchange_ref i=0 j=111
// interchange row: PR review ruled the shared case gets the exit value;
// verify the ruling covers tile/stripe as well.
// CHECK: tile_full_ref i=6 j=5
// CHECK: tile_full i=6 j=5


```
```
// Restore statements after a fused loop must touch user variables only.
// The internal fused-loop index is compiler generated, is never read after
// the construct, and must not get a restoring store. The nested fuse here
// makes the outer fuse collect the inner fused loop's helpers, whose
// counter is that internal index.
// RUN: %clang_cc1 -fopenmp -fopenmp-version=60 -triple x86_64-unknown-linux-gnu -emit-llvm -o - %s | FileCheck %s

extern "C" void nested_fuse(void) {
#pragma omp fuse looprange(2, 2)
  {
#pragma omp fuse looprange(2, 2)
    {
      for (int i = 0; i < 8; ++i)
        ;
      for (int j = 0; j < 16; ++j)
        ;
      for (int k = 0; k < 32; ++k)
        ;
    }
    int l;
    for (l = 0; l < 4; ++l)
      ;
    int m;
    for (m = 0; m < 6; ++m)
      ;
  }
}

// After the outer fused loop: j and k are restored by the inner fuse, l by
// this fuse. Nothing between those stores may write an internal fuse index.
// On the PR there is a guarded block assigning .omp_fuse_index here, dead
// code visible as a failed CHECK-NOT.
// CHECK: store i32 16, ptr %j, align 4
// CHECK: store i32 32, ptr %k, align 4
// CHECK-NOT: store i32 {{.*}}, ptr %".omp_fuse_index{{[0-9]*}}"
// CHECK: store i32 4, ptr %l, align 4
```
```
// A range-based for is a canonical OpenMP loop; its control iterator must
// be restored after a fuse like any other loop variable. The plain for
// with a pointer control variable in the same sequence is restored, the
// range-for iterator currently is not.
// RUN: %clang_cc1 -fopenmp -fopenmp-version=60 -triple x86_64-unknown-linux-gnu -emit-llvm -o - %s | FileCheck %s

extern "C" void fuse_range_for(void) {
  int arr[8];
  int t;
  int *q;
#pragma omp fuse
  {
    for (t = 0; t < 4; ++t)
      ;
    for (q = arr; q != arr + 8; ++q)
      ;
    for (int &x : arr)
      ;
  }
}

// CHECK-LABEL: define {{.*}}@fuse_range_for
// CHECK: store i32 4, ptr %t, align 4
// // CHECK: store ptr {{.*}}, ptr %q, align 8
// CHECK: getelementptr inbounds i32, ptr %arr, i64 8
// CHECK: store ptr {{.*}}, ptr %q, align 8
// The range-for iterator is restored to one past arr, as under tile
// below; that store is missing on the PR and this CHECK exposes it.
// CHECK: store ptr {{.*}}, ptr %__begin

extern "C" void tile_range_for(void) {
  int arr[8];
#pragma omp tile sizes(2)
  for (int &x : arr)
    ;
}

// CHECK-LABEL: define {{.*}}@tile_range_for
// Control proving the helper expressions carry a valid restore for a
// range-for; if this CHECK also fails, the skip is not specific to the
// fuse filter.
// CHECK: store ptr {{.*}}, ptr %__begin
```
```
// Class-type loop variables are excluded from the restoration because the
// builder only handles arithmetic and pointer counters. The finalization
// rule covers them the same way: after the construct the control iterator
// must equal end().
// Build with a compiler that has the PR applied and run; compare against
// the expected output below. The PR prints 0.
#include <vector>
#include <cstdio>

int main() {
  std::vector<int> v(6);
  std::vector<int>::iterator it;

  it = v.begin();
  for (it = v.begin(); it != v.end(); ++it)
    ;
  std::printf("plain at_end=%d\n", it == v.end());

  it = v.begin();
#pragma omp tile sizes(2)
  for (it = v.begin(); it != v.end(); ++it)
    ;
  std::printf("tile at_end=%d\n", it == v.end());

  return 0;
}

// Spec-correct output; the failing row shows the issue on the PR:
// CHECK: plain at_end=1
// CHECK-NOT: tile at_end=0
// CHECK: tile at_end=1
```

https://github.com/llvm/llvm-project/pull/214717


More information about the Openmp-commits mailing list