https://github.com/akash-manna-sky updated 
https://github.com/llvm/llvm-project/pull/237754

>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

Reply via email to