Skip to content

Conversation

chandraghale
Copy link
Contributor

@chandraghale chandraghale commented Aug 29, 2025

Conditional modifier on lastprivate clause is producing incorrect result when compiled with clang( C compiler). IR is not emitting while compilation with C compiler.
However it is working correctly with clang++
The OpenMP hook that emits the conditional modifier (checkAndEmitLastprivateConditional) is skipped in C because assignment is a prvalue and takes the scalar path.
Original Codegen Support : eddb8

C  = → prvalue → EmitAnyExpr(TEK_Scalar) → ScalarExprEmitter::VisitBinAssign (hook not reached)
C++ = → lvalue → EmitBinaryOperatorLValue
Failing Test Case :
#include <stdio.h>
#define N 10

int A[N];
void condlastprivate() {
int x, y, z, k;
x = y = z = k = 0;
#pragma omp parallel for lastprivate(conditional: x,y, z) lastprivate(k)
for( k=0; k<N; k++){
if ((k >2 ) && (k <6))
{ x = A[k]; z = A[k]+111; }
else
{ y = A[k]+222; }
}
printf("Expecting: x=5, y=231, z=116 k=10 Got: x=%d y=%d z=%d k=%d \n", x,y,z,k);
}
int main() {
for( int i=0; i<N; i++)
{ A[i] = i; }
condlastprivate();
return 0;
}
#>./clang  -fopenmp  cond_c.c 
#> ./a.out 
Expecting: x=5, y=231, z=116 k=10 **Got: x=-1376379760 y=231 z=631465600** k=10 

@llvmbot llvmbot added clang Clang issues not falling into any other category clang:codegen IR generation bugs: mangling, exceptions, etc. clang:openmp OpenMP related changes to Clang labels Aug 29, 2025
@llvmbot
Copy link
Member

llvmbot commented Aug 29, 2025

@llvm/pr-subscribers-clang

Author: CHANDRA GHALE (chandraghale)

Changes

Conditional modifier on lastprivate clause is producing incorrect result when compiled with clang( C compiler). IR is not emitting while compilation with C compiler.
However it is working correctly with clang++
The OpenMP hook that emits the conditional modifier (checkAndEmitLastprivateConditional) is skipped in C because assignment is a prvalue and takes the scalar path.

C  = → prvalue → EmitAnyExpr(TEK_Scalar) → ScalarExprEmitter::VisitBinAssign (hook not reached)
C++ = → lvalue → EmitBinaryOperatorLValue
Failing Test Case :
#include &lt;stdio.h&gt;
#define N 10

int A[N];
void condlastprivate() {
int x, y, z, k;
x = y = z = k = 0;
#pragma omp parallel for lastprivate(conditional: x,y, z) lastprivate(k)
for( k=0; k&lt;N; k++){
if ((k &gt;2 ) &amp;&amp; (k &lt;6))
{ x = A[k]; z = A[k]+111; }
else
{ y = A[k]+222; }
}
printf("Expecting: x=5, y=231, z=116 k=10 Got: x=%d y=%d z=%d k=%d \n", x,y,z,k);
}
int main() {
for( int i=0; i&lt;N; i++)
{ A[i] = i; }
condlastprivate();
return 0;
}
#&gt;./clang  -fopenmp  cond_c.c 
#&gt; ./a.out 
Expecting: x=5, y=231, z=116 k=10 **Got: x=-1376379760 y=231 z=631465600** k=10 

Full diff: https://github.com/llvm/llvm-project/pull/156004.diff

2 Files Affected:

  • (modified) clang/lib/CodeGen/CGExprScalar.cpp (+5)
  • (added) clang/test/OpenMP/for_lst_private_codegen_c.c (+53)
diff --git a/clang/lib/CodeGen/CGExprScalar.cpp b/clang/lib/CodeGen/CGExprScalar.cpp
index 193710bef2d16..d37712f27ed5e 100644
--- a/clang/lib/CodeGen/CGExprScalar.cpp
+++ b/clang/lib/CodeGen/CGExprScalar.cpp
@@ -5183,6 +5183,11 @@ Value *ScalarExprEmitter::VisitBinAssign(const BinaryOperator *E) {
       CGF.EmitStoreThroughLValue(RValue::get(RHS), LHS);
     }
   }
+  // OpenMP: Handle lastprivate(condition:) in scalar assignment
+  if (CGF.getLangOpts().OpenMP) {
+      CGF.CGM.getOpenMPRuntime().checkAndEmitLastprivateConditional(CGF,
+                                                                    E->getLHS());
+  }
 
   // If the result is clearly ignored, return now.
   if (Ignore)
diff --git a/clang/test/OpenMP/for_lst_private_codegen_c.c b/clang/test/OpenMP/for_lst_private_codegen_c.c
new file mode 100644
index 0000000000000..c56c5b6fb9351
--- /dev/null
+++ b/clang/test/OpenMP/for_lst_private_codegen_c.c
@@ -0,0 +1,53 @@
+// RUN: %clang_cc1 -verify -x c -triple x86_64-unknown-linux-gnu -fopenmp -fopenmp-version=52  -emit-llvm -o - %s | FileCheck %s
+// expected-no-diagnostics
+
+#define N 100
+int A[N];
+
+void condlastprivate() {
+  int x, y, z, k;
+  x = y = z = k = 0;
+
+  #pragma omp parallel for lastprivate(conditional: x,y,z) lastprivate(k)
+  for (k = 0; k < N; k++) {
+    if ((k > 2) && (k < 6)) {
+      x = A[k];
+      z = A[k] + 111;
+    } else {
+      y = A[k] + 222;
+    }
+  }
+}
+
+int main() {
+  for (int i = 0; i < N; i++)
+    A[i] = i;
+  condlastprivate();
+  return 0;
+}
+
+// CHECK: @.pl_cond.x_[[ID:[0-9]+]].iv = common global i32 0, align 4
+// CHECK: @pl_cond.x_[[ID]] = common global i32 0, align 4
+// CHECK: @.gomp_critical_user_pl_cond.x_[[ID]].var = common global [8 x i32] zeroinitializer, align 8
+
+// CHECK: @.pl_cond.z_[[ID]].iv = common global i32 0, align 4
+// CHECK: @pl_cond.z_[[ID]] = common global i32 0, align 4
+// CHECK: @.gomp_critical_user_pl_cond.z_[[ID]].var = common global [8 x i32] zeroinitializer, align 8
+
+// CHECK: @.pl_cond.y_[[ID]].iv = common global i32 0, align 4
+// CHECK: @pl_cond.y_[[ID]] = common global i32 0, align 4
+// CHECK: @.gomp_critical_user_pl_cond.y_[[ID]].var = common global [8 x i32] zeroinitializer, align 8
+
+// CHECK-LABEL: define internal void @condlastprivate.omp_outlined(
+// CHECK: call void @__kmpc_critical(ptr @2, {{.*}}, ptr @.gomp_critical_user_pl_cond.x_[[ID]].var)
+// CHECK: store i32 %{{[0-9]+}}, ptr @pl_cond.x_[[ID]], align 4
+// CHECK: call void @__kmpc_end_critical(ptr @2, {{.*}}, ptr @.gomp_critical_user_pl_cond.x_[[ID]].var)
+
+// CHECK: call void @__kmpc_critical(ptr @2, {{.*}}, ptr @.gomp_critical_user_pl_cond.z_[[ID]].var)
+// CHECK: store i32 %{{[0-9]+}}, ptr @pl_cond.z_[[ID]], align 4
+// CHECK: call void @__kmpc_end_critical(ptr @2, {{.*}}, ptr @.gomp_critical_user_pl_cond.z_[[ID]].var)
+
+// CHECK: call void @__kmpc_critical(ptr @2, {{.*}}, ptr @.gomp_critical_user_pl_cond.y_[[ID]].var)
+// CHECK: store i32 %{{[0-9]+}}, ptr @pl_cond.y_[[ID]], align 4
+// CHECK: call void @__kmpc_end_critical(ptr @2, {{.*}}, ptr @.gomp_critical_user_pl_cond.y_[[ID]].var)
+

@llvmbot
Copy link
Member

llvmbot commented Aug 29, 2025

@llvm/pr-subscribers-clang-codegen

Author: CHANDRA GHALE (chandraghale)

Changes

Conditional modifier on lastprivate clause is producing incorrect result when compiled with clang( C compiler). IR is not emitting while compilation with C compiler.
However it is working correctly with clang++
The OpenMP hook that emits the conditional modifier (checkAndEmitLastprivateConditional) is skipped in C because assignment is a prvalue and takes the scalar path.

C  = → prvalue → EmitAnyExpr(TEK_Scalar) → ScalarExprEmitter::VisitBinAssign (hook not reached)
C++ = → lvalue → EmitBinaryOperatorLValue
Failing Test Case :
#include &lt;stdio.h&gt;
#define N 10

int A[N];
void condlastprivate() {
int x, y, z, k;
x = y = z = k = 0;
#pragma omp parallel for lastprivate(conditional: x,y, z) lastprivate(k)
for( k=0; k&lt;N; k++){
if ((k &gt;2 ) &amp;&amp; (k &lt;6))
{ x = A[k]; z = A[k]+111; }
else
{ y = A[k]+222; }
}
printf("Expecting: x=5, y=231, z=116 k=10 Got: x=%d y=%d z=%d k=%d \n", x,y,z,k);
}
int main() {
for( int i=0; i&lt;N; i++)
{ A[i] = i; }
condlastprivate();
return 0;
}
#&gt;./clang  -fopenmp  cond_c.c 
#&gt; ./a.out 
Expecting: x=5, y=231, z=116 k=10 **Got: x=-1376379760 y=231 z=631465600** k=10 

Full diff: https://github.com/llvm/llvm-project/pull/156004.diff

2 Files Affected:

  • (modified) clang/lib/CodeGen/CGExprScalar.cpp (+5)
  • (added) clang/test/OpenMP/for_lst_private_codegen_c.c (+53)
diff --git a/clang/lib/CodeGen/CGExprScalar.cpp b/clang/lib/CodeGen/CGExprScalar.cpp
index 193710bef2d16..d37712f27ed5e 100644
--- a/clang/lib/CodeGen/CGExprScalar.cpp
+++ b/clang/lib/CodeGen/CGExprScalar.cpp
@@ -5183,6 +5183,11 @@ Value *ScalarExprEmitter::VisitBinAssign(const BinaryOperator *E) {
       CGF.EmitStoreThroughLValue(RValue::get(RHS), LHS);
     }
   }
+  // OpenMP: Handle lastprivate(condition:) in scalar assignment
+  if (CGF.getLangOpts().OpenMP) {
+      CGF.CGM.getOpenMPRuntime().checkAndEmitLastprivateConditional(CGF,
+                                                                    E->getLHS());
+  }
 
   // If the result is clearly ignored, return now.
   if (Ignore)
diff --git a/clang/test/OpenMP/for_lst_private_codegen_c.c b/clang/test/OpenMP/for_lst_private_codegen_c.c
new file mode 100644
index 0000000000000..c56c5b6fb9351
--- /dev/null
+++ b/clang/test/OpenMP/for_lst_private_codegen_c.c
@@ -0,0 +1,53 @@
+// RUN: %clang_cc1 -verify -x c -triple x86_64-unknown-linux-gnu -fopenmp -fopenmp-version=52  -emit-llvm -o - %s | FileCheck %s
+// expected-no-diagnostics
+
+#define N 100
+int A[N];
+
+void condlastprivate() {
+  int x, y, z, k;
+  x = y = z = k = 0;
+
+  #pragma omp parallel for lastprivate(conditional: x,y,z) lastprivate(k)
+  for (k = 0; k < N; k++) {
+    if ((k > 2) && (k < 6)) {
+      x = A[k];
+      z = A[k] + 111;
+    } else {
+      y = A[k] + 222;
+    }
+  }
+}
+
+int main() {
+  for (int i = 0; i < N; i++)
+    A[i] = i;
+  condlastprivate();
+  return 0;
+}
+
+// CHECK: @.pl_cond.x_[[ID:[0-9]+]].iv = common global i32 0, align 4
+// CHECK: @pl_cond.x_[[ID]] = common global i32 0, align 4
+// CHECK: @.gomp_critical_user_pl_cond.x_[[ID]].var = common global [8 x i32] zeroinitializer, align 8
+
+// CHECK: @.pl_cond.z_[[ID]].iv = common global i32 0, align 4
+// CHECK: @pl_cond.z_[[ID]] = common global i32 0, align 4
+// CHECK: @.gomp_critical_user_pl_cond.z_[[ID]].var = common global [8 x i32] zeroinitializer, align 8
+
+// CHECK: @.pl_cond.y_[[ID]].iv = common global i32 0, align 4
+// CHECK: @pl_cond.y_[[ID]] = common global i32 0, align 4
+// CHECK: @.gomp_critical_user_pl_cond.y_[[ID]].var = common global [8 x i32] zeroinitializer, align 8
+
+// CHECK-LABEL: define internal void @condlastprivate.omp_outlined(
+// CHECK: call void @__kmpc_critical(ptr @2, {{.*}}, ptr @.gomp_critical_user_pl_cond.x_[[ID]].var)
+// CHECK: store i32 %{{[0-9]+}}, ptr @pl_cond.x_[[ID]], align 4
+// CHECK: call void @__kmpc_end_critical(ptr @2, {{.*}}, ptr @.gomp_critical_user_pl_cond.x_[[ID]].var)
+
+// CHECK: call void @__kmpc_critical(ptr @2, {{.*}}, ptr @.gomp_critical_user_pl_cond.z_[[ID]].var)
+// CHECK: store i32 %{{[0-9]+}}, ptr @pl_cond.z_[[ID]], align 4
+// CHECK: call void @__kmpc_end_critical(ptr @2, {{.*}}, ptr @.gomp_critical_user_pl_cond.z_[[ID]].var)
+
+// CHECK: call void @__kmpc_critical(ptr @2, {{.*}}, ptr @.gomp_critical_user_pl_cond.y_[[ID]].var)
+// CHECK: store i32 %{{[0-9]+}}, ptr @pl_cond.y_[[ID]], align 4
+// CHECK: call void @__kmpc_end_critical(ptr @2, {{.*}}, ptr @.gomp_critical_user_pl_cond.y_[[ID]].var)
+

Copy link

github-actions bot commented Aug 29, 2025

✅ With the latest revision this PR passed the C/C++ code formatter.

Chandra Ghale and others added 2 commits August 29, 2025 06:03
@chandraghale chandraghale requested review from alexey-bataev and removed request for alexey-bataev September 8, 2025 15:55
@chandraghale chandraghale merged commit 2f755c5 into llvm:main Sep 11, 2025
9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
clang:codegen IR generation bugs: mangling, exceptions, etc. clang:openmp OpenMP related changes to Clang clang Clang issues not falling into any other category
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants