https://github.com/akash-manna-sky created https://github.com/llvm/llvm-project/pull/237754
Fixes #108166 `linear(uval(x))` crashed when `x` is a reference parameter. For `uval`, Sema read the initial value from the reference's initializer instead of the reference, and a parameter has none, so a null `Expr` reached `DefaultLvalueConversion`. Even with an initializer this was wrong: a defaulted `int &x = g` made the loop update `g`, and in a `parallel` region or a lambda it could name an uncaptured variable. Drop the shortcut in `ActOnOpenMPLinearClause` and `FinishOpenMPLinearClause`, so a `uval` item is read from and written back through the reference, like `val`. That is what the spec says for loop constructs, where `uval` only adds the requirement that the item be a reference, which is still checked. AI Assisted! >From dc818e959ed9256611d03c82bdbe081131598a9a Mon Sep 17 00:00:00 2001 From: Akash Manna <[email protected]> Date: Sun, 1 Nov 2026 12:15:44 +0530 Subject: [PATCH] Fixes #108166 `linear(uval(x))` crashed when `x` is a reference parameter. For `uval`, Sema read the initial value from the reference's initializer instead of the reference, and a parameter has none, so a null `Expr` reached `DefaultLvalueConversion`. Even with an initializer this was wrong: a defaulted `int &x = g` made the loop update `g`, and in a `parallel` region or a lambda it could name an uncaptured variable. Drop the shortcut in `ActOnOpenMPLinearClause` and `FinishOpenMPLinearClause`, so a `uval` item is read from and written back through the reference, like `val`. That is what the spec says for loop constructs, where `uval` only adds the requirement that the item be a reference, which is still checked. Assisted-by: LLM, mostly for the tests. --- clang/docs/ReleaseNotes.md | 2 + clang/lib/Sema/SemaOpenMP.cpp | 15 ++-- clang/test/OpenMP/for_linear_codegen.cpp | 3 +- clang/test/OpenMP/simd_codegen.cpp | 10 ++- clang/test/OpenMP/simd_linear_messages.cpp | 63 +++++++++++++++++ .../test/OpenMP/simd_linear_uval_codegen.cpp | 70 +++++++++++++++++++ 6 files changed, 150 insertions(+), 13 deletions(-) create mode 100644 clang/test/OpenMP/simd_linear_uval_codegen.cpp diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md index e6b71ba2a0b565..35ba81dfeb60d7 100644 --- a/clang/docs/ReleaseNotes.md +++ b/clang/docs/ReleaseNotes.md @@ -1388,6 +1388,8 @@ The `alpha.cplusplus.UseAfterLifetimeEnd` checker was renamed to `alpha.core.Use the clause was parsed but its condition was discarded without effect. - Fixed a crash when the loop variable or a loop bound of an OpenMP loop has a `_BitInt` type wider than any standard integer type. (#GH140074) +- Fixed a crash when a `linear` clause with the `uval` modifier names a + reference without an initializer, such as a reference parameter. (#GH108166) - Loop-transforming constructs such as `tile` and `unroll` are now allowed inside a `simd` region for OpenMP 6.0 or later, which classifies them as simdizable. diff --git a/clang/lib/Sema/SemaOpenMP.cpp b/clang/lib/Sema/SemaOpenMP.cpp index 855c2fe8f41a06..cc9ec15e2eb5e4 100644 --- a/clang/lib/Sema/SemaOpenMP.cpp +++ b/clang/lib/Sema/SemaOpenMP.cpp @@ -22934,7 +22934,6 @@ OMPClause *SemaOpenMP::ActOnOpenMPLinearClause( DeclRefExpr *PrivateRef = buildDeclRefExpr(SemaRef, Private, Type, ELoc); // Build var to save initial value. VarDecl *Init = buildVarDecl(SemaRef, ELoc, Type, ".linear.start"); - Expr *InitExpr; DeclRefExpr *Ref = nullptr; bool IsBindingDecl = isa<BindingDecl>(D); if (!VD && !IsBindingDecl && !SemaRef.CurContext->isDependentContext()) { @@ -22955,10 +22954,10 @@ OMPClause *SemaOpenMP::ActOnOpenMPLinearClause( } } } - if (LinKind == OMPC_LINEAR_uval) - InitExpr = VD ? VD->getInit() : SimpleRefExpr; - else - InitExpr = (VD || IsBindingDecl) ? SimpleRefExpr : Ref; + // Read the initial value through the list item itself for every modifier; + // a 'uval' item is a reference whose referenced value is linear, exactly as + // for 'val', and need not have an initializer (e.g. a reference parameter). + Expr *InitExpr = (VD || IsBindingDecl) ? SimpleRefExpr : Ref; SemaRef.AddInitializerToDecl( Init, SemaRef.DefaultLvalueConversion(InitExpr).get(), /*DirectInit=*/false); @@ -23036,7 +23035,6 @@ static bool FinishOpenMPLinearClause(OMPLinearClause &Clause, DeclRefExpr *IV, bool HasErrors = false; auto CurInit = Clause.inits().begin(); auto CurPrivate = Clause.privates().begin(); - OpenMPLinearClauseKind LinKind = Clause.getModifier(); for (Expr *RefExpr : Clause.varlist()) { SourceLocation ELoc; SourceRange ERange; @@ -23064,13 +23062,12 @@ static bool FinishOpenMPLinearClause(OMPLinearClause &Clause, DeclRefExpr *IV, } Expr *InitExpr = *CurInit; - // Build privatized reference to the current linear var. + // Build privatized reference to the current linear var. The final value is + // written back through the variable itself, also for 'uval' references. auto *DE = cast<DeclRefExpr>(SimpleRefExpr); Expr *CapturedRef; if (isa<BindingDecl>(DE->getDecl())) { CapturedRef = SimpleRefExpr; - } else if (LinKind == OMPC_LINEAR_uval) { - CapturedRef = cast<VarDecl>(DE->getDecl())->getInit(); } else { CapturedRef = buildDeclRefExpr(SemaRef, cast<VarDecl>(DE->getDecl()), diff --git a/clang/test/OpenMP/for_linear_codegen.cpp b/clang/test/OpenMP/for_linear_codegen.cpp index 88c218ac8af803..a4244b63da2b9a 100644 --- a/clang/test/OpenMP/for_linear_codegen.cpp +++ b/clang/test/OpenMP/for_linear_codegen.cpp @@ -1754,8 +1754,7 @@ int main() { // CHECK4-NEXT: [[TMP37:%.*]] = load ptr, ptr [[_TMP3]], align 8, !nonnull [[META2]], !align [[META3]] // CHECK4-NEXT: store ptr [[TMP37]], ptr [[_TMP21]], align 8 // CHECK4-NEXT: [[TMP38:%.*]] = load i32, ptr [[C10]], align 4 -// CHECK4-NEXT: [[C22:%.*]] = getelementptr inbounds nuw [[STRUCT_SS:%.*]], ptr [[TMP0]], i32 0, i32 2 -// CHECK4-NEXT: [[TMP39:%.*]] = load ptr, ptr [[C22]], align 8, !nonnull [[META2]], !align [[META3]] +// CHECK4-NEXT: [[TMP39:%.*]] = load ptr, ptr [[_TMP21]], align 8, !nonnull [[META2]], !align [[META3]] // CHECK4-NEXT: store i32 [[TMP38]], ptr [[TMP39]], align 4 // CHECK4-NEXT: br label [[DOTOMP_LINEAR_PU_DONE]] // CHECK4: .omp.linear.pu.done: diff --git a/clang/test/OpenMP/simd_codegen.cpp b/clang/test/OpenMP/simd_codegen.cpp index ee8d709ba47fe9..2af0b7ae3ec47e 100644 --- a/clang/test/OpenMP/simd_codegen.cpp +++ b/clang/test/OpenMP/simd_codegen.cpp @@ -714,8 +714,11 @@ void linear(float *a) { // CHECK-NEXT: store i64 [[LIN0_2]], ptr [[K_REF]] // #pragma omp simd linear(uval(k) : 3) +// CHECK: [[UVAL_REF:%.+]] = load ptr, ptr [[K_ADDR]], +// CHECK: store ptr [[UVAL_REF]], ptr [[K_ADDR_REF:%.+]], // CHECK: store i32 0, ptr [[OMP_IV:%[^,]+]] -// CHECK: [[K0LOAD:%.+]] = load i64, ptr [[VAL_ADDR]] +// CHECK: [[K_REF:%.+]] = load ptr, ptr [[K_ADDR_REF]], +// CHECK: [[K0LOAD:%.+]] = load i64, ptr [[K_REF]] // CHECK-NEXT: store i64 [[K0LOAD]], ptr [[LIN0:%[^,]+]] // CHECK: [[IV:%.+]] = load i32, ptr [[OMP_IV]]{{.*}}!llvm.access.group @@ -747,8 +750,11 @@ void linear(float *a) { // CHECK: [[SIMPLE_LOOP_END]]: // // Update linear vars after loop, as the loop was operating on a private version. +// CHECK: [[K_REF:%.+]] = load ptr, ptr [[K_ADDR_REF]], +// CHECK: store ptr [[K_REF]], ptr [[K_PRIV_REF:%.+]], // CHECK: [[LIN0_2:%.+]] = load i64, ptr [[K_PRIVATIZED]] -// CHECK-NEXT: store i64 [[LIN0_2]], ptr [[VAL_ADDR]] +// CHECK-NEXT: [[K_REF:%.+]] = load ptr, ptr [[K_PRIV_REF]], +// CHECK-NEXT: store i64 [[LIN0_2]], ptr [[K_REF]] // } diff --git a/clang/test/OpenMP/simd_linear_messages.cpp b/clang/test/OpenMP/simd_linear_messages.cpp index 3ce4de697de0c9..b605165114840e 100644 --- a/clang/test/OpenMP/simd_linear_messages.cpp +++ b/clang/test/OpenMP/simd_linear_messages.cpp @@ -202,6 +202,69 @@ void linear_modifiers(int argc) { for (int k = 0; k < argc; ++k) ++k; } +extern int &ext_ref; + +template <class T> void linear_dependent_reference(T &x, int &y) { +#ifdef OMP52 + #pragma omp simd linear(x: uval, step(y + 1)) +#else + #pragma omp simd linear(uval(x): y + 1) +#endif + for (int k = 0; k < 10; ++k) x += y + 1; +} + +template <class T> void linear_dependent_step(int &x, T y) { +#ifdef OMP52 + #pragma omp simd linear(x: uval, step(y + 1)) +#else + #pragma omp simd linear(uval(x): y + 1) +#endif + for (int k = 0; k < 10; ++k) x += y + 1; +} + +void linear_reference_params(int &x, int &y, int s) { + int ls = s + 1; + #pragma omp simd linear(uval(x)) // omp52-error {{old syntax 'linear-modifier(list)' on 'linear' clause was deprecated, use new syntax 'linear(list: [linear-modifier,] step(step-size))'}} + for (int k = 0; k < 10; ++k) ++x; + #pragma omp simd linear(ref(x)) // omp52-error {{old syntax 'linear-modifier(list)' on 'linear' clause was deprecated, use new syntax 'linear(list: [linear-modifier,] step(step-size))'}} + for (int k = 0; k < 10; ++k) ++x; + #pragma omp simd linear(uval(ext_ref)) // omp52-error {{old syntax 'linear-modifier(list)' on 'linear' clause was deprecated, use new syntax 'linear(list: [linear-modifier,] step(step-size))'}} + for (int k = 0; k < 10; ++k) ++ext_ref; + #pragma omp simd linear(uval(x): 2) // omp52-error {{old syntax 'linear-modifier(list)' on 'linear' clause was deprecated, use new syntax 'linear(list: [linear-modifier,] step(step-size))'}} omp52-error {{step simple modifier is exclusive and cannot be use with 'val', 'uval' or 'ref' modifier}} + for (int k = 0; k < 10; ++k) x += 2; + #pragma omp simd linear(uval(x): y + 1) // omp52-error {{old syntax 'linear-modifier(list)' on 'linear' clause was deprecated, use new syntax 'linear(list: [linear-modifier,] step(step-size))'}} omp52-error {{step simple modifier is exclusive and cannot be use with 'val', 'uval' or 'ref' modifier}} + for (int k = 0; k < 10; ++k) x += y + 1; + #pragma omp simd linear(uval(x): ls) // omp52-error {{old syntax 'linear-modifier(list)' on 'linear' clause was deprecated, use new syntax 'linear(list: [linear-modifier,] step(step-size))'}} omp52-error {{step simple modifier is exclusive and cannot be use with 'val', 'uval' or 'ref' modifier}} + for (int k = 0; k < 10; ++k) x += ls; + #pragma omp simd linear(ref(x): y + 1) // omp52-error {{old syntax 'linear-modifier(list)' on 'linear' clause was deprecated, use new syntax 'linear(list: [linear-modifier,] step(step-size))'}} omp52-error {{step simple modifier is exclusive and cannot be use with 'val', 'uval' or 'ref' modifier}} + for (int k = 0; k < 10; ++k) x += y + 1; + #pragma omp simd linear(val(x): y + 1) // omp52-error {{old syntax 'linear-modifier(list)' on 'linear' clause was deprecated, use new syntax 'linear(list: [linear-modifier,] step(step-size))'}} omp52-error {{step simple modifier is exclusive and cannot be use with 'val', 'uval' or 'ref' modifier}} + for (int k = 0; k < 10; ++k) x += y + 1; + #pragma omp simd linear(x: y + 1) + for (int k = 0; k < 10; ++k) x += y + 1; +#ifdef OMP52 + #pragma omp simd linear(x: uval, step(y + 1)) + for (int k = 0; k < 10; ++k) x += y + 1; + #pragma omp simd linear(x: ref, step(y + 1)) + for (int k = 0; k < 10; ++k) x += y + 1; + #pragma omp simd linear(ext_ref: uval, step(ls)) + for (int k = 0; k < 10; ++k) ext_ref += ls; +#endif + #pragma omp simd linear(uval(s): y + 1) // expected-error {{variable of non-reference type 'int' can be used only with 'val' modifier, but used with 'uval'}} omp52-error {{old syntax 'linear-modifier(list)' on 'linear' clause was deprecated, use new syntax 'linear(list: [linear-modifier,] step(step-size))'}} omp52-error {{step simple modifier is exclusive and cannot be use with 'val', 'uval' or 'ref' modifier}} + for (int k = 0; k < 10; ++k) s += y + 1; + #pragma omp simd linear(uval(x): 1.5) // expected-error {{expression must have integral or unscoped enumeration type, not 'double'}} omp52-error {{old syntax 'linear-modifier(list)' on 'linear' clause was deprecated, use new syntax 'linear(list: [linear-modifier,] step(step-size))'}} omp52-error {{step simple modifier is exclusive and cannot be use with 'val', 'uval' or 'ref' modifier}} + for (int k = 0; k < 10; ++k) ++x; + #pragma omp simd linear(uval(x): zzz) // expected-error {{use of undeclared identifier 'zzz'}} omp52-error {{old syntax 'linear-modifier(list)' on 'linear' clause was deprecated, use new syntax 'linear(list: [linear-modifier,] step(step-size))'}} + for (int k = 0; k < 10; ++k) ++x; + // expected-error@+3 3 {{use of undeclared identifier 'i'}} + // expected-error@+2 {{initialization clause of OpenMP for loop is not in canonical form ('var = init' or 'T var = init')}} + #pragma omp simd linear(uval(x): y + 1) // omp52-error {{old syntax 'linear-modifier(list)' on 'linear' clause was deprecated, use new syntax 'linear(list: [linear-modifier,] step(step-size))'}} omp52-error {{step simple modifier is exclusive and cannot be use with 'val', 'uval' or 'ref' modifier}} + for (i = 0; i < 10; i++) + x += y + 1; + linear_dependent_reference(x, y); + linear_dependent_step(x, s); +} + int f; int main(int argc, char **argv) { double darr[100]; diff --git a/clang/test/OpenMP/simd_linear_uval_codegen.cpp b/clang/test/OpenMP/simd_linear_uval_codegen.cpp new file mode 100644 index 00000000000000..a9e883eec6791a --- /dev/null +++ b/clang/test/OpenMP/simd_linear_uval_codegen.cpp @@ -0,0 +1,70 @@ +// RUN: %clang_cc1 -verify -fopenmp -x c++ -triple x86_64-unknown-linux-gnu -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -verify -fopenmp-simd -x c++ -triple x86_64-unknown-linux-gnu -emit-llvm %s -o - | FileCheck %s --check-prefix=SIMD-ONLY +// expected-no-diagnostics + +// SIMD-ONLY-NOT: {{__kmpc|__tgt}} + +// A 'uval' item is read and written back through the reference itself, not +// through its initializer (absent for a parameter, uncaptured in a region). + +// CHECK-LABEL: define {{.*}}void @{{.*}}ref_param +// CHECK-SAME: (ptr noundef nonnull align 4 dereferenceable(4) [[X:%.+]], ptr noundef nonnull align 4 dereferenceable(4) [[Y:%.+]]) +void ref_param(int &x, int &y) { +// CHECK: store ptr [[X]], ptr [[X_ADDR:%[^,]+]], +// CHECK: [[X_REF:%.+]] = load ptr, ptr [[X_ADDR]], +// CHECK-NEXT: store ptr [[X_REF]], ptr [[X_TMP:%[^,]+]], +// CHECK: store i32 0, ptr [[OMP_IV:%[^,]+]], +// CHECK-NEXT: [[X_REF1:%.+]] = load ptr, ptr [[X_TMP]], +// CHECK-NEXT: [[X_START:%.+]] = load i32, ptr [[X_REF1]], +// CHECK-NEXT: store i32 [[X_START]], ptr [[LINEAR_START:%[^,]+]], +// CHECK-NEXT: [[Y_REF:%.+]] = load ptr, ptr +// CHECK-NEXT: [[Y_VAL:%.+]] = load i32, ptr [[Y_REF]], +// CHECK-NEXT: [[STEP:%.+]] = add nsw i32 [[Y_VAL]], 1 +// CHECK-NEXT: store i32 [[STEP]], ptr [[LINEAR_STEP:%[^,]+]], +#pragma omp simd linear(uval(x) : y + 1) + for (int i = 0; i < 10; ++i) + x += y + 1; +// CHECK: [[START:%.+]] = load i32, ptr [[LINEAR_START]], +// CHECK-NEXT: [[IV:%.+]] = load i32, ptr [[OMP_IV]], +// CHECK-NEXT: [[STEP_VAL:%.+]] = load i32, ptr [[LINEAR_STEP]], +// CHECK-NEXT: [[MUL:%.+]] = mul nsw i32 [[IV]], [[STEP_VAL]] +// CHECK-NEXT: [[ADD:%.+]] = add nsw i32 [[START]], [[MUL]] +// CHECK-NEXT: store i32 [[ADD]], ptr [[X_PRIV:%[^,]+]], +// CHECK: omp.inner.for.end: +// CHECK: [[X_REF2:%.+]] = load ptr, ptr [[X_TMP]], +// CHECK-NEXT: store ptr [[X_REF2]], ptr [[X_TMP1:%[^,]+]], +// CHECK-NEXT: [[X_FINAL:%.+]] = load i32, ptr [[X_PRIV]], +// CHECK-NEXT: [[X_REF3:%.+]] = load ptr, ptr [[X_TMP1]], +// CHECK-NEXT: store i32 [[X_FINAL]], ptr [[X_REF3]], +// CHECK-NEXT: ret void +} + +// CHECK-LABEL: define {{.*}}void @{{.*}}local_ref{{.*}}() +void local_ref() { + int a = 0; + int &r = a; +// CHECK: [[R_PTR:%.+]] = load ptr, ptr %r, +// CHECK-NEXT: call void {{.*}}@__kmpc_fork_call({{.*}}, ptr @[[OUTLINED:.+]], ptr [[R_PTR]]) +#pragma omp parallel for simd linear(uval(r)) + for (int i = 0; i < 10; ++i) + r += 1; +} + +// CHECK: define internal void @[[OUTLINED]](ptr noalias noundef %{{.+}}, ptr noalias noundef %{{.+}}, ptr noundef nonnull align 4 dereferenceable(4) [[R:%.+]]) +// CHECK: store ptr [[R]], ptr [[R_ADDR:%[^,]+]], +// CHECK-NEXT: [[R_REF:%.+]] = load ptr, ptr [[R_ADDR]], +// CHECK-NEXT: store ptr [[R_REF]], ptr [[R_TMP:%[^,]+]], +// CHECK-NEXT: [[R_REF1:%.+]] = load ptr, ptr [[R_TMP]], +// CHECK-NEXT: [[R_START:%.+]] = load i32, ptr [[R_REF1]], +// CHECK-NEXT: store i32 [[R_START]], ptr [[LINEAR_START:%[^,]+]], +// CHECK: [[START:%.+]] = load i32, ptr [[LINEAR_START]], +// CHECK-NEXT: [[IV:%.+]] = load i32, ptr +// CHECK-NEXT: [[MUL:%.+]] = mul nsw i32 [[IV]], 1 +// CHECK-NEXT: [[ADD:%.+]] = add nsw i32 [[START]], [[MUL]] +// CHECK-NEXT: store i32 [[ADD]], ptr [[R_PRIV:%[^,]+]], +// CHECK: .omp.linear.pu: +// CHECK-NEXT: [[R_REF2:%.+]] = load ptr, ptr [[R_TMP]], +// CHECK-NEXT: store ptr [[R_REF2]], ptr [[R_TMP1:%[^,]+]], +// CHECK-NEXT: [[R_FINAL:%.+]] = load i32, ptr [[R_PRIV]], +// CHECK-NEXT: [[R_REF3:%.+]] = load ptr, ptr [[R_TMP1]], +// CHECK-NEXT: store i32 [[R_FINAL]], ptr [[R_REF3]], _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
