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 <[email protected]> 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 +} + _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
