[clang] [OpenMP] Restore loop variable values after loop-transformation const… (PR #208533)
Zahira Ammarguellat via cfe-commits
cfe-commits at lists.llvm.org
Thu Jul 9 11:46:40 PDT 2026
https://github.com/zahiraam created https://github.com/llvm/llvm-project/pull/208533
…ructs
>From 444217556666673b9efe64e2edb40ff08d777f16 Mon Sep 17 00:00:00 2001
From: Zahira Ammarguellat <zahira.ammarguellat at intel.com>
Date: Thu, 9 Jul 2026 11:10:26 -0700
Subject: [PATCH] [OpenMP] Restore loop variable values after
loop-transformation constructs
---
clang/lib/Sema/SemaOpenMP.cpp | 44 ++++++-
clang/test/OpenMP/loop_transform_final_iv.c | 129 ++++++++++++++++++++
2 files changed, 172 insertions(+), 1 deletion(-)
create mode 100644 clang/test/OpenMP/loop_transform_final_iv.c
diff --git a/clang/lib/Sema/SemaOpenMP.cpp b/clang/lib/Sema/SemaOpenMP.cpp
index 0f45a21c5e461..b2605e41bb513 100644
--- a/clang/lib/Sema/SemaOpenMP.cpp
+++ b/clang/lib/Sema/SemaOpenMP.cpp
@@ -14960,6 +14960,31 @@ static Expr *makeFloorIVRef(Sema &SemaRef, ArrayRef<VarDecl *> FloorIndVars,
OrigCntVar->getExprLoc());
}
+/// Add loop variable finalization statements to restore original loop variable
+/// values after a loop-transformation construct, as required by OpenMP 6.0 spec
+/// (pg 371, lines 19-21).
+static Stmt *addLoopVarFinalization(
+ ASTContext &Context, Stmt *TransformedStmt,
+ ArrayRef<OMPLoopBasedDirective::HelperExprs> LoopHelpers) {
+ SmallVector<Stmt *, 8> FinalizationStmts;
+ FinalizationStmts.push_back(TransformedStmt);
+
+ for (const auto &LoopHelper : LoopHelpers) {
+ if (!LoopHelper.Finals.empty()) {
+ if (Expr *Final = LoopHelper.Finals[0]) {
+ FinalizationStmts.push_back(Final);
+ }
+ }
+ }
+
+ if (FinalizationStmts.size() > 1) {
+ return CompoundStmt::Create(Context, FinalizationStmts, FPOptionsOverride(),
+ TransformedStmt->getBeginLoc(),
+ TransformedStmt->getEndLoc());
+ }
+ return TransformedStmt;
+}
+
StmtResult SemaOpenMP::ActOnOpenMPTileDirective(ArrayRef<OMPClause *> Clauses,
Stmt *AStmt,
SourceLocation StartLoc,
@@ -15230,6 +15255,8 @@ StmtResult SemaOpenMP::ActOnOpenMPTileDirective(ArrayRef<OMPClause *> Clauses,
LoopHelper.Init->getBeginLoc(), LoopHelper.Inc->getEndLoc());
}
+ Inner = addLoopVarFinalization(Context, Inner, LoopHelpers);
+
return OMPTileDirective::Create(Context, StartLoc, EndLoc, Clauses, NumLoops,
AStmt, Inner,
buildPreInits(Context, PreInits));
@@ -15489,6 +15516,8 @@ StmtResult SemaOpenMP::ActOnOpenMPStripeDirective(ArrayRef<OMPClause *> Clauses,
LoopHelper.Init->getBeginLoc(), LoopHelper.Inc->getEndLoc());
}
+ Inner = addLoopVarFinalization(Context, Inner, LoopHelpers);
+
return OMPStripeDirective::Create(Context, StartLoc, EndLoc, Clauses,
NumLoops, AStmt, Inner,
buildPreInits(Context, PreInits));
@@ -15957,8 +15986,11 @@ StmtResult SemaOpenMP::ActOnOpenMPReverseDirective(Stmt *AStmt,
ForStmt(Context, Init.get(), Cond.get(), nullptr, Incr.get(),
ReversedBody, LoopHelper.Init->getBeginLoc(),
LoopHelper.Init->getBeginLoc(), LoopHelper.Inc->getEndLoc());
+
+ Stmt *Inner = addLoopVarFinalization(Context, ReversedFor, LoopHelpers);
+
return OMPReverseDirective::Create(Context, StartLoc, EndLoc, AStmt, NumLoops,
- ReversedFor,
+ Inner,
buildPreInits(Context, PreInits));
}
@@ -16381,6 +16413,8 @@ StmtResult SemaOpenMP::ActOnOpenMPInterchangeDirective(
SourceHelper.Inc->getEndLoc());
}
+ Inner = addLoopVarFinalization(Context, Inner, LoopHelpers);
+
return OMPInterchangeDirective::Create(Context, StartLoc, EndLoc, Clauses,
NumLoops, AStmt, Inner,
buildPreInits(Context, PreInits));
@@ -16859,6 +16893,14 @@ StmtResult SemaOpenMP::ActOnOpenMPFuseDirective(ArrayRef<OMPClause *> Clauses,
FusionStmt = CompoundStmt::Create(Context, FinalLoops, FPOptionsOverride(),
SourceLocation(), SourceLocation());
}
+
+ // Collect HelperExprs from all loops for finalization
+ SmallVector<OMPLoopBasedDirective::HelperExprs, 2> LoopHelpers;
+ for (const auto &Loop : SeqAnalysis.Loops) {
+ LoopHelpers.push_back(Loop.HelperExprs);
+ }
+ FusionStmt = addLoopVarFinalization(Context, FusionStmt, LoopHelpers);
+
return OMPFuseDirective::Create(Context, StartLoc, EndLoc, Clauses,
NumGeneratedTopLevelLoops, AStmt, FusionStmt,
buildPreInits(Context, PreInits));
diff --git a/clang/test/OpenMP/loop_transform_final_iv.c b/clang/test/OpenMP/loop_transform_final_iv.c
new file mode 100644
index 0000000000000..969badbe9636b
--- /dev/null
+++ b/clang/test/OpenMP/loop_transform_final_iv.c
@@ -0,0 +1,129 @@
+// RUN: %clang_cc1 -verify -fopenmp -fopenmp-version=60 -emit-llvm %s -o - | FileCheck %s
+// expected-no-diagnostics
+
+// Test that loop variables are correctly finalized after loop-transformation
+// constructs as required by OpenMP 6.0 spec (pg 371, lines 19-21).
+
+void test_tile(void) {
+ // CHECK-LABEL: define {{.*}} @test_tile
+ int i;
+ #pragma omp tile sizes(2)
+ for (i = 1; i <= 10; i++) {
+ }
+ // CHECK: store i32 11, ptr %i
+ // After loop: i should be 11 (lb + tripcount * stride = 1 + 10 * 1)
+}
+
+void test_stripe(void) {
+ // CHECK-LABEL: define {{.*}} @test_stripe
+ int i;
+ #pragma omp stripe sizes(3)
+ for (i = 0; i < 10; i++) {
+ }
+ // CHECK: store i32 10, ptr %i
+ // After loop: i should be 10 (lb + tripcount * stride = 0 + 10 * 1)
+}
+
+void test_tile_nested(void) {
+ // CHECK-LABEL: define {{.*}} @test_tile_nested
+ int i, j;
+ #pragma omp tile sizes(2, 3)
+ for (i = 0; i < 10; i++)
+ for (j = 0; j < 5; j++) {
+ }
+ // CHECK: store i32 10, ptr %i
+ // CHECK: store i32 5, ptr %j
+ // After loop: i should be 10, j should be 5
+}
+
+void test_tile_stride(void) {
+ // CHECK-LABEL: define {{.*}} @test_tile_stride
+ int i;
+ #pragma omp tile sizes(4)
+ for (i = 5; i <= 15; i += 2) {
+ }
+ // CHECK: store i32 17, ptr %i
+ // After loop: i should be 17 (lb + tripcount * stride = 5 + 6 * 2)
+}
+
+void test_tile_variable_bound(int n) {
+ // CHECK-LABEL: define {{.*}} @test_tile_variable_bound
+ int i;
+ #pragma omp tile sizes(3)
+ for (i = 0; i < n; i++) {
+ }
+ // CHECK: for.end14:
+ // CHECK-NEXT: %{{.*}} = load i32, ptr %.capture_expr.
+ // CHECK-NEXT: %{{.*}} = sub nsw i32 %{{.*}}, 0
+ // CHECK-NEXT: %{{.*}} = sdiv i32 %{{.*}}, 1
+ // CHECK-NEXT: %{{.*}} = mul nsw i32 %{{.*}}, 1
+ // CHECK-NEXT: %{{.*}} = add nsw i32 0, %{{.*}}
+ // CHECK-NEXT: store i32 %{{.*}}, ptr %i
+ // After loop: i should equal n (the runtime upper bound)
+}
+
+void test_reverse(void) {
+ // CHECK-LABEL: define {{.*}} @test_reverse
+ int i;
+ #pragma omp reverse
+ for (i = 0; i < 10; i++) {
+ }
+ // CHECK: store i32 10, ptr %i
+ // After loop: i should be 10 (lb + tripcount * stride = 0 + 10 * 1)
+}
+
+void test_interchange(void) {
+ // CHECK-LABEL: define {{.*}} @test_interchange
+ int i, j;
+ #pragma omp interchange permutation(2, 1)
+ for (i = 0; i < 10; i++)
+ for (j = 0; j < 5; j++) {
+ }
+ // CHECK: store i32 10, ptr %i
+ // CHECK: store i32 5, ptr %j
+ // After loop: i should be 10, j should be 5
+}
+
+void test_fuse(void) {
+ // CHECK-LABEL: define {{.*}} @test_fuse
+ int i, j;
+ #pragma omp fuse
+ {
+ for (i = 0; i < 10; i++) {
+ }
+ for (j = 0; j < 5; j++) {
+ }
+ }
+ // CHECK: store i32 10, ptr %i
+ // CHECK: store i32 5, ptr %j
+ // After loop: i should be 10, j should be 5
+}
+
+void test_for_workshare(void) {
+ // CHECK-LABEL: define {{.*}} @test_for_workshare
+ int i;
+ #pragma omp for
+ for (i = 5; i <= 10; i += 2) {
+ }
+ // CHECK: omp.loop.exit:
+ // CHECK-NEXT: call void @__kmpc_for_static_fini
+ // CHECK-NEXT: call void @__kmpc_barrier
+ // CHECK-NEXT: ret void
+ // omp for worksharing doesn't finalize the loop variable after the loop
+}
+
+void test_for_lastprivate(void) {
+ // CHECK-LABEL: define {{.*}} @test_for_lastprivate
+ int i;
+ #pragma omp parallel
+ #pragma omp for lastprivate(i)
+ for (i = 5; i <= 10; i += 2) {
+ }
+ // CHECK: .omp.lastprivate.then:
+ // CHECK-NEXT: store i32 11, ptr %i{{.*}}, align 4
+ // CHECK-NEXT: %{{.*}} = load i32, ptr %i{{.*}}, align 4
+ // CHECK-NEXT: store i32 %{{.*}}, ptr %{{.*}}, align 4
+ // omp for with lastprivate DOES finalize the loop variable
+ // i = last_iteration_value + stride = 9 + 2 = 11
+}
+
More information about the cfe-commits
mailing list