Skip to content

Trying to fix undefined symbol error caused by iterator variable #141507

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 16 commits into
base: main
Choose a base branch
from

Conversation

ShashwathiNavada
Copy link
Contributor

When a mapper is declared with an iterator variable inside the map clause, it results in unintended behavior due to the iterator being implicitly created but left uninitialized.
Testcase:

typedef struct myvec{
    size_t len;
    double *data;
} myvec_t;

#pragma omp declare mapper(id:myvec_t v) map( iterator( iterator_variable=0:v.len), tofrom: v.data[iterator_variable]) 

int main() 
{ 
    int errors = 0;
   myvec_t s;
   #pragma omp target map(mapper(id), to:s)
     {
     }
  return 0;
}

The error we get while compiling this is:

/usr/lib64/gcc/x86_64-suse-linux/14/../../../../x86_64-suse-linux/bin/ld: /tmp/test-f70647.o: in function `.omp_mapper._ZTS5myvec.id':
test.cpp:(.text+0x21a): undefined reference to `iterator_variable'
/llvm-project/install/bin/clang-linker-wrapper: error: 'ld' failed
clang++: error: linker command failed with exit code 1 (use -v to see invocation)

This patch tries to fix this by initializing the iterator variable to a null constant.

Shashwathi N added 2 commits May 19, 2025 14:29
@llvmbot llvmbot added clang Clang issues not falling into any other category clang:frontend Language frontend issues, e.g. anything involving "Sema" clang:codegen IR generation bugs: mangling, exceptions, etc. clang:openmp OpenMP related changes to Clang labels May 26, 2025
@llvmbot
Copy link
Member

llvmbot commented May 26, 2025

@llvm/pr-subscribers-flang-openmp

@llvm/pr-subscribers-clang

Author: None (ShashwathiNavada)

Changes

When a mapper is declared with an iterator variable inside the map clause, it results in unintended behavior due to the iterator being implicitly created but left uninitialized.
Testcase:

typedef struct myvec{
    size_t len;
    double *data;
} myvec_t;

#pragma omp declare mapper(id:myvec_t v) map( iterator( iterator_variable=0:v.len), tofrom: v.data[iterator_variable]) 

int main() 
{ 
    int errors = 0;
   myvec_t s;
   #pragma omp target map(mapper(id), to:s)
     {
     }
  return 0;
}

The error we get while compiling this is:

/usr/lib64/gcc/x86_64-suse-linux/14/../../../../x86_64-suse-linux/bin/ld: /tmp/test-f70647.o: in function `.omp_mapper._ZTS5myvec.id':
test.cpp:(.text+0x21a): undefined reference to `iterator_variable'
/llvm-project/install/bin/clang-linker-wrapper: error: 'ld' failed
clang++: error: linker command failed with exit code 1 (use -v to see invocation)

This patch tries to fix this by initializing the iterator variable to a null constant.


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

4 Files Affected:

  • (modified) clang/include/clang/Basic/Attr.td (+7)
  • (modified) clang/lib/CodeGen/CGExpr.cpp (+5)
  • (modified) clang/lib/Sema/SemaOpenMP.cpp (+2)
  • (modified) clang/test/OpenMP/declare_mapper_codegen.cpp (+102)
diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td
index ccd13a4cca4dd..cc629443e9b2d 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -4461,6 +4461,13 @@ def OMPCaptureKind : Attr {
   }];
 }
 
+def OMPIterator : Attr {
+  // This attribute has no spellings as it is only ever created implicitly.
+  let Spellings = [];
+  let SemaHandler = 0;
+  let Documentation = [InternalOnly];
+}
+
 def OMPReferencedVar : Attr {
   // This attribute has no spellings as it is only ever created implicitly.
   let Spellings = [];
diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp
index ec01c87c13b1d..994327f690689 100644
--- a/clang/lib/CodeGen/CGExpr.cpp
+++ b/clang/lib/CodeGen/CGExpr.cpp
@@ -2969,6 +2969,11 @@ static LValue EmitGlobalVarDeclLValue(CodeGenFunction &CGF,
 
   llvm::Value *V = CGF.CGM.GetAddrOfGlobalVar(VD);
 
+  if (VD->hasAttr<OMPIteratorAttr>()) {
+  llvm::GlobalVariable *Var = llvm::dyn_cast<llvm::GlobalVariable>(V);
+  Var->setInitializer(CGF.CGM.EmitNullConstant(E->getType()));
+  }
+
   if (VD->getTLSKind() != VarDecl::TLS_None)
     V = CGF.Builder.CreateThreadLocalAddress(V);
 
diff --git a/clang/lib/Sema/SemaOpenMP.cpp b/clang/lib/Sema/SemaOpenMP.cpp
index f16f841d62edd..1756c20ce486c 100644
--- a/clang/lib/Sema/SemaOpenMP.cpp
+++ b/clang/lib/Sema/SemaOpenMP.cpp
@@ -24476,6 +24476,8 @@ ExprResult SemaOpenMP::ActOnOMPIteratorExpr(Scope *S,
         VarDecl::Create(Context, SemaRef.CurContext, StartLoc, D.DeclIdentLoc,
                         D.DeclIdent, DeclTy, TInfo, SC_None);
     VD->setImplicit();
+    VD->addAttr(OMPIteratorAttr::CreateImplicit(
+        Context, SourceRange(StartLoc)));
     if (S) {
       // Check for conflicting previous declaration.
       DeclarationNameInfo NameInfo(VD->getDeclName(), D.DeclIdentLoc);
diff --git a/clang/test/OpenMP/declare_mapper_codegen.cpp b/clang/test/OpenMP/declare_mapper_codegen.cpp
index 81453223b2a27..7dc32d0ae12ff 100644
--- a/clang/test/OpenMP/declare_mapper_codegen.cpp
+++ b/clang/test/OpenMP/declare_mapper_codegen.cpp
@@ -1055,4 +1055,106 @@ void foo(int a){
 
 #endif // CK4
 
+///==========================================================================///
+// RUN: %clang_cc1 -DCK5 -verify -fopenmp -fopenmp-version=52 -fopenmp-targets=powerpc64le-ibm-linux-gnu -x c++ -triple powerpc64le-unknown-unknown -emit-llvm -femit-all-decls -disable-llvm-passes %s -o - | FileCheck --check-prefix CK5 %s
+// RUN: %clang_cc1 -DCK5 -fopenmp -fopenmp-version=52 -fopenmp-targets=powerpc64le-ibm-linux-gnu -x c++ -std=c++11 -triple powerpc64le-unknown-unknown -emit-pch -femit-all-decls -disable-llvm-passes -o %t %s
+// RUN: %clang_cc1 -DCK5 -verify -fopenmp-version=52 -fopenmp -fopenmp-targets=i386-pc-linux-gnu -x c++ -triple i386-unknown-unknown -emit-llvm -femit-all-decls -disable-llvm-passes %s -o - | FileCheck --check-prefix CK5 %s
+// RUN: %clang_cc1 -DCK5 -fopenmp -fopenmp-version=52 -fopenmp-targets=i386-pc-linux-gnu -x c++ -std=c++11 -triple i386-unknown-unknown -emit-pch -femit-all-decls -disable-llvm-passes -o %t %s
+
+// RUN: %clang_cc1 -DCK5 -verify -fopenmp-simd -fopenmp-version=52 -fopenmp-targets=powerpc64le-ibm-linux-gnu -x c++ -triple powerpc64le-unknown-unknown -emit-llvm -femit-all-decls -disable-llvm-passes %s -o - | FileCheck --check-prefix SIMD-ONLY0 %s
+// RUN: %clang_cc1 -DCK5 -fopenmp-simd -fopenmp-version=52 -fopenmp-targets=powerpc64le-ibm-linux-gnu -x c++ -std=c++11 -triple powerpc64le-unknown-unknown -emit-pch -femit-all-decls -disable-llvm-passes -o %t %s
+// RUN: %clang_cc1 -DCK5 -verify -fopenmp-simd -fopenmp-version=52 -fopenmp-targets=i386-pc-linux-gnu -x c++ -triple i386-unknown-unknown -emit-llvm -femit-all-decls -disable-llvm-passes %s -o - | FileCheck --check-prefix SIMD-ONLY0 %s
+// RUN: %clang_cc1 -DCK5 -fopenmp-simd -fopenmp-version=52 -fopenmp-targets=i386-pc-linux-gnu -x c++ -std=c++11 -triple i386-unknown-unknown -emit-pch -femit-all-decls -disable-llvm-passes -o %t %s
+
+#ifdef CK5
+typedef struct myvec {
+    int a;
+    double *b;
+} myvec_t;
+
+#pragma omp declare mapper(id: myvec_t v) map(iterator(it=0:v.a), tofrom: v.b[it])
+// CK5: @[[ITER:[a-zA-Z0-9_]+]] = global i32 0, align 4
+
+void foo(){ 
+  myvec_t s;
+  #pragma omp target map(mapper(id), to:s)
+  {
+  }
+}
+
+// CK5: define {{.*}}void [[MPRFUNC:@[.]omp_mapper[.].*myvec[.]id]](ptr noundef [[HANDLE:%.+]], ptr noundef [[BPTR:%.+]], ptr noundef [[BEGIN:%.+]], i64 noundef [[BYTESIZE:%.+]], i64 noundef [[TYPE:%.+]], ptr{{.*}})
+// CK5-DAG: [[SIZE:%.+]] = udiv exact i64 [[BYTESIZE]], {{.*}}
+// CK5-DAG: [[PTREND:%.+]] = getelementptr %struct.myvec, ptr [[BEGIN]], i64 [[SIZE]]
+// CK5-DAG: [[ISARRAY:%.+]] = icmp sgt i64 [[SIZE]], 1
+// CK5-DAG: [[PTRSNE:%.+]] = icmp ne ptr [[BPTR]], [[BEGIN]]
+// CK5-DAG: [[PTRANDOBJ:%.+]] = and i64 [[TYPE]], 16
+// CK5-DAG: [[ISPTRANDOBJ:%.+]] = icmp ne i64 [[PTRANDOBJ]], 0
+// CK5-DAG: [[CMPA:%.+]] = and i1 [[PTRSNE]], [[ISPTRANDOBJ]]
+// CK5-DAG: [[CMP:%.+]] = or i1 [[ISARRAY]], [[CMPA]]
+// CK5-DAG: [[TYPEDEL:%.+]] = and i64 [[TYPE]], 8
+// CK5-DAG: [[ISNOTDEL:%.+]] = icmp eq i64 [[TYPEDEL]], 0
+// CK5-DAG: [[CMP1:%.+]] = and i1 [[CMP]], [[ISNOTDEL]]
+// CK5: br i1 [[CMP1]], label %[[INITEVALDEL:[^,]+]], label %[[LHEAD:[^,]+]]
+
+// CK5: [[INITEVALDEL]]
+// CK5-DAG: [[ARRSIZE:%.+]] = mul nuw i64 [[SIZE]], {{.*}}
+
+// Remove movement mappings and mark as implicit
+// CK5-DAG: [[ITYPE:%.+]] = and i64 [[TYPE]], -4
+// CK5-DAG: [[ITYPE1:%.+]] = or i64 [[ITYPE]], 512
+// CK5: call void @__tgt_push_mapper_component(ptr [[HANDLE]], ptr [[BPTR]], ptr [[BEGIN]], i64 [[ARRSIZE]], i64 [[ITYPE1]], {{.*}})
+// CK5: br label %[[LHEAD:[^,]+]]
+
+// CK5: [[LHEAD]]
+// CK5: [[ISEMPTY:%.+]] = icmp eq ptr [[BEGIN]], [[PTREND]]
+// CK5: br i1 [[ISEMPTY]], label %[[DONE:[^,]+]], label %[[LBODY:[^,]+]]
+// CK5: [[LBODY]]
+// CK5: [[PTR:%.+]] = phi ptr [ [[BEGIN]], %{{.+}} ], [ [[PTRNEXT:%.+]], %[[LCORRECT:[^,]+]] ]
+// CK5-DAG: [[ABEGIN:%.+]] = getelementptr inbounds nuw %struct.myvec, ptr [[PTR]], i32 0, i32 1
+// CK5-DAG: load i32, ptr @[[ITER]], align 4
+// CK5-DAG: [[PRESIZE:%.+]] = call i64 @__tgt_mapper_num_components(ptr [[HANDLE]])
+// CK5-DAG: [[SHIPRESIZE:%.+]] = shl i64 [[PRESIZE]], 48
+// CK5-DAG: [[MEMBERTYPE:%.+]] = add nuw i64 0, [[SHIPRESIZE]]
+// CK5-DAG: [[TYPETF:%.+]] = and i64 [[TYPE]], 3
+// CK5-DAG: [[ISALLOC:%.+]] = icmp eq i64 [[TYPETF]], 0
+// CK5-DAG: br i1 [[ISALLOC]], label %[[ALLOC:[^,]+]], label %[[ALLOCELSE:[^,]+]]
+// CK5-DAG: [[ALLOC]]
+// CK5-DAG: [[ALLOCTYPE:%.+]] = and i64 [[MEMBERTYPE]], -4
+// CK5-DAG: br label %[[TYEND:[^,]+]]
+// CK5-DAG: [[ALLOCELSE]]
+// CK5-DAG: [[ISTO:%.+]] = icmp eq i64 [[TYPETF]], 1
+// CK5-DAG: br i1 [[ISTO]], label %[[TO:[^,]+]], label %[[TOELSE:[^,]+]]
+// CK5-DAG: [[TO]]
+// CK5-DAG: [[TOTYPE:%.+]] = and i64 [[MEMBERTYPE]], -3
+// CK5-DAG: br label %[[TYEND]]
+// CK5-DAG: [[TOELSE]]
+// CK5-DAG: [[ISFROM:%.+]] = icmp eq i64 [[TYPETF]], 2
+// CK5-DAG: br i1 [[ISFROM]], label %[[FROM:[^,]+]], label %[[TYEND]]
+// CK5-DAG: [[FROM]]
+// CK5-DAG: [[FROMTYPE:%.+]] = and i64 [[MEMBERTYPE]], -2
+// CK5-DAG: br label %[[TYEND]]
+// CK5-DAG: [[TYEND]]
+// CK5-DAG: [[TYPE1:%.+]] = phi i64 [ [[ALLOCTYPE]], %[[ALLOC]] ], [ [[TOTYPE]], %[[TO]] ], [ [[FROMTYPE]], %[[FROM]] ], [ [[MEMBERTYPE]], %[[TOELSE]] ]
+// CK5: call void @__tgt_push_mapper_component(ptr [[HANDLE]], ptr [[PTR]], ptr [[ABEGIN]], i64 {{.*}}, i64 [[TYPE1]], {{.*}})
+// CK5: [[PTRNEXT]] = getelementptr %struct.myvec, ptr [[PTR]], i32 1
+// CK5: [[ISDONE:%.+]] = icmp eq ptr [[PTRNEXT]], [[PTREND]]
+// CK5: br i1 [[ISDONE]], label %[[LEXIT:[^,]+]], label %[[LBODY]]
+
+// CK5: [[LEXIT]]
+// CK5: [[ISARRAY:%.+]] = icmp sgt i64 [[SIZE]], 1
+// CK5: [[TYPEDEL:%.+]] = and i64 [[TYPE]], 8
+// CK5: [[ISNOTDEL:%.+]] = icmp ne i64 [[TYPEDEL]], {{.*}}
+// CK5: [[CMP1:%.+]] = and i1 [[ISARRAY]], [[ISNOTDEL]]
+// CK5-DAG: [[ARRSIZE:%.+]] = mul nuw i64 [[SIZE]], {{.*}}
+
+// Remove movement mappings and mark as implicit
+// CK5-DAG: [[DTYPE:%.+]] = and i64 [[TYPE]], -4
+// CK5-DAG: [[DTYPE1:%.+]] = or i64 [[DTYPE]], 512
+// CK5: call void @__tgt_push_mapper_component(ptr [[HANDLE]], ptr [[BPTR]], ptr [[BEGIN]], i64 [[ARRSIZE]], i64 [[DTYPE1]], {{.*}})
+// CK5: br label %[[DONE]]
+// CK5: [[DONE]]
+// CK5: ret void
+
+#endif // CK5
+
 #endif // HEADER

@llvmbot
Copy link
Member

llvmbot commented May 26, 2025

@llvm/pr-subscribers-clang-codegen

Author: None (ShashwathiNavada)

Changes

When a mapper is declared with an iterator variable inside the map clause, it results in unintended behavior due to the iterator being implicitly created but left uninitialized.
Testcase:

typedef struct myvec{
    size_t len;
    double *data;
} myvec_t;

#pragma omp declare mapper(id:myvec_t v) map( iterator( iterator_variable=0:v.len), tofrom: v.data[iterator_variable]) 

int main() 
{ 
    int errors = 0;
   myvec_t s;
   #pragma omp target map(mapper(id), to:s)
     {
     }
  return 0;
}

The error we get while compiling this is:

/usr/lib64/gcc/x86_64-suse-linux/14/../../../../x86_64-suse-linux/bin/ld: /tmp/test-f70647.o: in function `.omp_mapper._ZTS5myvec.id':
test.cpp:(.text+0x21a): undefined reference to `iterator_variable'
/llvm-project/install/bin/clang-linker-wrapper: error: 'ld' failed
clang++: error: linker command failed with exit code 1 (use -v to see invocation)

This patch tries to fix this by initializing the iterator variable to a null constant.


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

4 Files Affected:

  • (modified) clang/include/clang/Basic/Attr.td (+7)
  • (modified) clang/lib/CodeGen/CGExpr.cpp (+5)
  • (modified) clang/lib/Sema/SemaOpenMP.cpp (+2)
  • (modified) clang/test/OpenMP/declare_mapper_codegen.cpp (+102)
diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td
index ccd13a4cca4dd..cc629443e9b2d 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -4461,6 +4461,13 @@ def OMPCaptureKind : Attr {
   }];
 }
 
+def OMPIterator : Attr {
+  // This attribute has no spellings as it is only ever created implicitly.
+  let Spellings = [];
+  let SemaHandler = 0;
+  let Documentation = [InternalOnly];
+}
+
 def OMPReferencedVar : Attr {
   // This attribute has no spellings as it is only ever created implicitly.
   let Spellings = [];
diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp
index ec01c87c13b1d..994327f690689 100644
--- a/clang/lib/CodeGen/CGExpr.cpp
+++ b/clang/lib/CodeGen/CGExpr.cpp
@@ -2969,6 +2969,11 @@ static LValue EmitGlobalVarDeclLValue(CodeGenFunction &CGF,
 
   llvm::Value *V = CGF.CGM.GetAddrOfGlobalVar(VD);
 
+  if (VD->hasAttr<OMPIteratorAttr>()) {
+  llvm::GlobalVariable *Var = llvm::dyn_cast<llvm::GlobalVariable>(V);
+  Var->setInitializer(CGF.CGM.EmitNullConstant(E->getType()));
+  }
+
   if (VD->getTLSKind() != VarDecl::TLS_None)
     V = CGF.Builder.CreateThreadLocalAddress(V);
 
diff --git a/clang/lib/Sema/SemaOpenMP.cpp b/clang/lib/Sema/SemaOpenMP.cpp
index f16f841d62edd..1756c20ce486c 100644
--- a/clang/lib/Sema/SemaOpenMP.cpp
+++ b/clang/lib/Sema/SemaOpenMP.cpp
@@ -24476,6 +24476,8 @@ ExprResult SemaOpenMP::ActOnOMPIteratorExpr(Scope *S,
         VarDecl::Create(Context, SemaRef.CurContext, StartLoc, D.DeclIdentLoc,
                         D.DeclIdent, DeclTy, TInfo, SC_None);
     VD->setImplicit();
+    VD->addAttr(OMPIteratorAttr::CreateImplicit(
+        Context, SourceRange(StartLoc)));
     if (S) {
       // Check for conflicting previous declaration.
       DeclarationNameInfo NameInfo(VD->getDeclName(), D.DeclIdentLoc);
diff --git a/clang/test/OpenMP/declare_mapper_codegen.cpp b/clang/test/OpenMP/declare_mapper_codegen.cpp
index 81453223b2a27..7dc32d0ae12ff 100644
--- a/clang/test/OpenMP/declare_mapper_codegen.cpp
+++ b/clang/test/OpenMP/declare_mapper_codegen.cpp
@@ -1055,4 +1055,106 @@ void foo(int a){
 
 #endif // CK4
 
+///==========================================================================///
+// RUN: %clang_cc1 -DCK5 -verify -fopenmp -fopenmp-version=52 -fopenmp-targets=powerpc64le-ibm-linux-gnu -x c++ -triple powerpc64le-unknown-unknown -emit-llvm -femit-all-decls -disable-llvm-passes %s -o - | FileCheck --check-prefix CK5 %s
+// RUN: %clang_cc1 -DCK5 -fopenmp -fopenmp-version=52 -fopenmp-targets=powerpc64le-ibm-linux-gnu -x c++ -std=c++11 -triple powerpc64le-unknown-unknown -emit-pch -femit-all-decls -disable-llvm-passes -o %t %s
+// RUN: %clang_cc1 -DCK5 -verify -fopenmp-version=52 -fopenmp -fopenmp-targets=i386-pc-linux-gnu -x c++ -triple i386-unknown-unknown -emit-llvm -femit-all-decls -disable-llvm-passes %s -o - | FileCheck --check-prefix CK5 %s
+// RUN: %clang_cc1 -DCK5 -fopenmp -fopenmp-version=52 -fopenmp-targets=i386-pc-linux-gnu -x c++ -std=c++11 -triple i386-unknown-unknown -emit-pch -femit-all-decls -disable-llvm-passes -o %t %s
+
+// RUN: %clang_cc1 -DCK5 -verify -fopenmp-simd -fopenmp-version=52 -fopenmp-targets=powerpc64le-ibm-linux-gnu -x c++ -triple powerpc64le-unknown-unknown -emit-llvm -femit-all-decls -disable-llvm-passes %s -o - | FileCheck --check-prefix SIMD-ONLY0 %s
+// RUN: %clang_cc1 -DCK5 -fopenmp-simd -fopenmp-version=52 -fopenmp-targets=powerpc64le-ibm-linux-gnu -x c++ -std=c++11 -triple powerpc64le-unknown-unknown -emit-pch -femit-all-decls -disable-llvm-passes -o %t %s
+// RUN: %clang_cc1 -DCK5 -verify -fopenmp-simd -fopenmp-version=52 -fopenmp-targets=i386-pc-linux-gnu -x c++ -triple i386-unknown-unknown -emit-llvm -femit-all-decls -disable-llvm-passes %s -o - | FileCheck --check-prefix SIMD-ONLY0 %s
+// RUN: %clang_cc1 -DCK5 -fopenmp-simd -fopenmp-version=52 -fopenmp-targets=i386-pc-linux-gnu -x c++ -std=c++11 -triple i386-unknown-unknown -emit-pch -femit-all-decls -disable-llvm-passes -o %t %s
+
+#ifdef CK5
+typedef struct myvec {
+    int a;
+    double *b;
+} myvec_t;
+
+#pragma omp declare mapper(id: myvec_t v) map(iterator(it=0:v.a), tofrom: v.b[it])
+// CK5: @[[ITER:[a-zA-Z0-9_]+]] = global i32 0, align 4
+
+void foo(){ 
+  myvec_t s;
+  #pragma omp target map(mapper(id), to:s)
+  {
+  }
+}
+
+// CK5: define {{.*}}void [[MPRFUNC:@[.]omp_mapper[.].*myvec[.]id]](ptr noundef [[HANDLE:%.+]], ptr noundef [[BPTR:%.+]], ptr noundef [[BEGIN:%.+]], i64 noundef [[BYTESIZE:%.+]], i64 noundef [[TYPE:%.+]], ptr{{.*}})
+// CK5-DAG: [[SIZE:%.+]] = udiv exact i64 [[BYTESIZE]], {{.*}}
+// CK5-DAG: [[PTREND:%.+]] = getelementptr %struct.myvec, ptr [[BEGIN]], i64 [[SIZE]]
+// CK5-DAG: [[ISARRAY:%.+]] = icmp sgt i64 [[SIZE]], 1
+// CK5-DAG: [[PTRSNE:%.+]] = icmp ne ptr [[BPTR]], [[BEGIN]]
+// CK5-DAG: [[PTRANDOBJ:%.+]] = and i64 [[TYPE]], 16
+// CK5-DAG: [[ISPTRANDOBJ:%.+]] = icmp ne i64 [[PTRANDOBJ]], 0
+// CK5-DAG: [[CMPA:%.+]] = and i1 [[PTRSNE]], [[ISPTRANDOBJ]]
+// CK5-DAG: [[CMP:%.+]] = or i1 [[ISARRAY]], [[CMPA]]
+// CK5-DAG: [[TYPEDEL:%.+]] = and i64 [[TYPE]], 8
+// CK5-DAG: [[ISNOTDEL:%.+]] = icmp eq i64 [[TYPEDEL]], 0
+// CK5-DAG: [[CMP1:%.+]] = and i1 [[CMP]], [[ISNOTDEL]]
+// CK5: br i1 [[CMP1]], label %[[INITEVALDEL:[^,]+]], label %[[LHEAD:[^,]+]]
+
+// CK5: [[INITEVALDEL]]
+// CK5-DAG: [[ARRSIZE:%.+]] = mul nuw i64 [[SIZE]], {{.*}}
+
+// Remove movement mappings and mark as implicit
+// CK5-DAG: [[ITYPE:%.+]] = and i64 [[TYPE]], -4
+// CK5-DAG: [[ITYPE1:%.+]] = or i64 [[ITYPE]], 512
+// CK5: call void @__tgt_push_mapper_component(ptr [[HANDLE]], ptr [[BPTR]], ptr [[BEGIN]], i64 [[ARRSIZE]], i64 [[ITYPE1]], {{.*}})
+// CK5: br label %[[LHEAD:[^,]+]]
+
+// CK5: [[LHEAD]]
+// CK5: [[ISEMPTY:%.+]] = icmp eq ptr [[BEGIN]], [[PTREND]]
+// CK5: br i1 [[ISEMPTY]], label %[[DONE:[^,]+]], label %[[LBODY:[^,]+]]
+// CK5: [[LBODY]]
+// CK5: [[PTR:%.+]] = phi ptr [ [[BEGIN]], %{{.+}} ], [ [[PTRNEXT:%.+]], %[[LCORRECT:[^,]+]] ]
+// CK5-DAG: [[ABEGIN:%.+]] = getelementptr inbounds nuw %struct.myvec, ptr [[PTR]], i32 0, i32 1
+// CK5-DAG: load i32, ptr @[[ITER]], align 4
+// CK5-DAG: [[PRESIZE:%.+]] = call i64 @__tgt_mapper_num_components(ptr [[HANDLE]])
+// CK5-DAG: [[SHIPRESIZE:%.+]] = shl i64 [[PRESIZE]], 48
+// CK5-DAG: [[MEMBERTYPE:%.+]] = add nuw i64 0, [[SHIPRESIZE]]
+// CK5-DAG: [[TYPETF:%.+]] = and i64 [[TYPE]], 3
+// CK5-DAG: [[ISALLOC:%.+]] = icmp eq i64 [[TYPETF]], 0
+// CK5-DAG: br i1 [[ISALLOC]], label %[[ALLOC:[^,]+]], label %[[ALLOCELSE:[^,]+]]
+// CK5-DAG: [[ALLOC]]
+// CK5-DAG: [[ALLOCTYPE:%.+]] = and i64 [[MEMBERTYPE]], -4
+// CK5-DAG: br label %[[TYEND:[^,]+]]
+// CK5-DAG: [[ALLOCELSE]]
+// CK5-DAG: [[ISTO:%.+]] = icmp eq i64 [[TYPETF]], 1
+// CK5-DAG: br i1 [[ISTO]], label %[[TO:[^,]+]], label %[[TOELSE:[^,]+]]
+// CK5-DAG: [[TO]]
+// CK5-DAG: [[TOTYPE:%.+]] = and i64 [[MEMBERTYPE]], -3
+// CK5-DAG: br label %[[TYEND]]
+// CK5-DAG: [[TOELSE]]
+// CK5-DAG: [[ISFROM:%.+]] = icmp eq i64 [[TYPETF]], 2
+// CK5-DAG: br i1 [[ISFROM]], label %[[FROM:[^,]+]], label %[[TYEND]]
+// CK5-DAG: [[FROM]]
+// CK5-DAG: [[FROMTYPE:%.+]] = and i64 [[MEMBERTYPE]], -2
+// CK5-DAG: br label %[[TYEND]]
+// CK5-DAG: [[TYEND]]
+// CK5-DAG: [[TYPE1:%.+]] = phi i64 [ [[ALLOCTYPE]], %[[ALLOC]] ], [ [[TOTYPE]], %[[TO]] ], [ [[FROMTYPE]], %[[FROM]] ], [ [[MEMBERTYPE]], %[[TOELSE]] ]
+// CK5: call void @__tgt_push_mapper_component(ptr [[HANDLE]], ptr [[PTR]], ptr [[ABEGIN]], i64 {{.*}}, i64 [[TYPE1]], {{.*}})
+// CK5: [[PTRNEXT]] = getelementptr %struct.myvec, ptr [[PTR]], i32 1
+// CK5: [[ISDONE:%.+]] = icmp eq ptr [[PTRNEXT]], [[PTREND]]
+// CK5: br i1 [[ISDONE]], label %[[LEXIT:[^,]+]], label %[[LBODY]]
+
+// CK5: [[LEXIT]]
+// CK5: [[ISARRAY:%.+]] = icmp sgt i64 [[SIZE]], 1
+// CK5: [[TYPEDEL:%.+]] = and i64 [[TYPE]], 8
+// CK5: [[ISNOTDEL:%.+]] = icmp ne i64 [[TYPEDEL]], {{.*}}
+// CK5: [[CMP1:%.+]] = and i1 [[ISARRAY]], [[ISNOTDEL]]
+// CK5-DAG: [[ARRSIZE:%.+]] = mul nuw i64 [[SIZE]], {{.*}}
+
+// Remove movement mappings and mark as implicit
+// CK5-DAG: [[DTYPE:%.+]] = and i64 [[TYPE]], -4
+// CK5-DAG: [[DTYPE1:%.+]] = or i64 [[DTYPE]], 512
+// CK5: call void @__tgt_push_mapper_component(ptr [[HANDLE]], ptr [[BPTR]], ptr [[BEGIN]], i64 [[ARRSIZE]], i64 [[DTYPE1]], {{.*}})
+// CK5: br label %[[DONE]]
+// CK5: [[DONE]]
+// CK5: ret void
+
+#endif // CK5
+
 #endif // HEADER

Copy link

github-actions bot commented May 26, 2025

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

@@ -2969,6 +2969,11 @@ static LValue EmitGlobalVarDeclLValue(CodeGenFunction &CGF,

llvm::Value *V = CGF.CGM.GetAddrOfGlobalVar(VD);

if (VD->hasAttr<OMPIteratorAttr>()) {
llvm::GlobalVariable *Var = llvm::dyn_cast<llvm::GlobalVariable>(V);
Var->setInitializer(CGF.CGM.EmitNullConstant(E->getType()));
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not the place to set an initializer for a variable.

For most variable definitions, there's a single location where it makes sense to "define" the variable.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not the place to set an initializer for a variable.

For most variable definitions, there's a single location where it makes sense to "define" the variable.

Thank you for the response, I have moved the definition. Does it look good now?

Copy link
Collaborator

@efriedma-quic efriedma-quic left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To expand on what I mean...

For variables which have a strong definition, we usually don't emit them lazily: there's some statement or declaration which is the "definition" point, and we emit the definition as part of visiting that statement or declaration. This ensures consistency: we consistently emit a definition for things that need a definition.

For variables where the definition isn't necessarily needed, we also have a mechanism for lazily defining them; see DeferredDecls.

I don't want to invent a new way of emitting variable definitions specifically for OpenMP.

Shashwathi N added 2 commits July 23, 2025 03:12
…EmitGlobal function
@ShashwathiNavada
Copy link
Contributor Author

To expand on what I mean...

For variables which have a strong definition, we usually don't emit them lazily: there's some statement or declaration which is the "definition" point, and we emit the definition as part of visiting that statement or declaration. This ensures consistency: we consistently emit a definition for things that need a definition.

For variables where the definition isn't necessarily needed, we also have a mechanism for lazily defining them; see DeferredDecls.

I don't want to invent a new way of emitting variable definitions specifically for OpenMP.

@efriedma-quic Thank you! I've updated the patch to emit iterator variable using the existing global emission logic, similar to how it's done for declare target deferred variables. Let me know if this looks good.

Copy link
Collaborator

@efriedma-quic efriedma-quic left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's maybe better?

I spent a little time looking at the testcase, and I saw something weird in the AST: there's a VarDecl at the top level, but it never gets sent down the normal path for top-level decls. I suspect there's a missing call to HandleTopLevelDecl somewhere. That would be the best way to handle this, if possible.

@shafik shafik requested a review from zahiraam July 24, 2025 22:57
@zahiraam
Copy link
Contributor

The reproducer is passing with clang. See https://godbolt.org/z/adnjMndGq

@efriedma-quic
Copy link
Collaborator

The reproducer is passing with clang. See https://godbolt.org/z/adnjMndGq

The testcase is using -femit-all-decls. I assumed that was just to avoid including some irrelevant bits in the testcase. I guess there could be some interaction with -femit-all-decls specifically, though.

@ShashwathiNavada
Copy link
Contributor Author

The reproducer is passing with clang. See https://godbolt.org/z/adnjMndGq

Since parsing and semantic support for the iterator is in place, the code compiles fine without offloading. However, the issue surfaces only when offloading is actually performed.

Shashwathi N and others added 2 commits August 1, 2025 03:11

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
@ShashwathiNavada
Copy link
Contributor Author

That's maybe better?

I spent a little time looking at the testcase, and I saw something weird in the AST: there's a VarDecl at the top level, but it never gets sent down the normal path for top-level decls. I suspect there's a missing call to HandleTopLevelDecl somewhere. That would be the best way to handle this, if possible.

@efriedma-quic Thank you for the suggestion,.I have added the call. Could you please take a look and let me know if it looks good?

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
Copy link
Collaborator

@efriedma-quic efriedma-quic left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks basically right. But I'm not sure all the conditions for calling it are correct. Is getDeclareMapperVarRef relevant? Does it matter what scope we're inserting the variable into? (Can the variable be inserted in a namespace? Can it be a local variable?)

@ShashwathiNavada
Copy link
Contributor Author

This looks basically right. But I'm not sure all the conditions for calling it are correct. Is getDeclareMapperVarRef relevant? Does it matter what scope we're inserting the variable into? (Can the variable be inserted in a namespace? Can it be a local variable?)

@efriedma-quic I've added the condition to check for local variables as you suggested. However, I believe getDeclareMapperVarRef() is relevant in this context, since I'm handling iterators declared in #pragma omp declare mapper. Let me know if you think any further changes are needed.

Copy link
Collaborator

@efriedma-quic efriedma-quic left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The reason I'm suspicious of the getDeclareMapperVarRef check is that SemaOpenMP::ActOnOMPIteratorExpr creates the variable (VarDecl::Create) regardless. So what's supposed to happen for variables where getDeclareMapperVarRef is false?


Independent of your patch, the following seems to crash. Is it supposed to be valid? Is there an open bug?

typedef struct myvec {
    int a;
    double *b;
} myvec_t;
void foo() {
#pragma omp declare mapper(id: myvec_t v) map(iterator(it=0:v.a), tofrom: v.b[it])
  myvec_t s;
  #pragma omp target map(mapper(id), to:s)
  {
  }
}

@ShashwathiNavada
Copy link
Contributor Author

Independent of your patch, the following seems to crash. Is it supposed to be valid? Is there an open bug?

typedef struct myvec {
    int a;
    double *b;
} myvec_t;
void foo() {
#pragma omp declare mapper(id: myvec_t v) map(iterator(it=0:v.a), tofrom: v.b[it])
  myvec_t s;
  #pragma omp target map(mapper(id), to:s)
  {
  }
}

I think the issue here is the missing memory allocation for s in the testcase. So if we allocate memory for s.b the program works fine.

@efriedma-quic
Copy link
Collaborator

Still waiting for an answer:

The reason I'm suspicious of the getDeclareMapperVarRef check is that SemaOpenMP::ActOnOMPIteratorExpr creates the variable (VarDecl::Create) regardless. So what's supposed to happen for variables where getDeclareMapperVarRef is false?

@ShashwathiNavada
Copy link
Contributor Author

Still waiting for an answer:

The reason I'm suspicious of the getDeclareMapperVarRef check is that SemaOpenMP::ActOnOMPIteratorExpr creates the variable (VarDecl::Create) regardless. So what's supposed to happen for variables where getDeclareMapperVarRef is false?

This check was added when initially supporting iterators in declare mapper. Although ActOnOMPIteratorExpr always creates the iterator variable, this part is specifically to handle processing that variable for declare mapper purposes. So as far as I understand, even if getDeclareMapperVarRef() is false, the variable exists, but we skip special declare-mapper-related handling here. Please let me know if I’m wrong.

@efriedma-quic
Copy link
Collaborator

We need to call HandleTopLevelDecl for every translation-unit-level variable we create. ActOnOMPIteratorExpr creates a translation-unit-level variable for every top-level iterator expression. But we're only calling HandleTopLevelDecl for some of them: specifically, the ones where getDeclareMapperVarRef() is true. What happens to the other ones?

@ShashwathiNavada
Copy link
Contributor Author

We need to call HandleTopLevelDecl for every translation-unit-level variable we create. ActOnOMPIteratorExpr creates a translation-unit-level variable for every top-level iterator expression. But we're only calling HandleTopLevelDecl for some of them: specifically, the ones where getDeclareMapperVarRef() is true. What happens to the other ones?

I see your point. I noticed that ActOnOMPIteratorExpr gets called for affinity, depend, and map clauses. Out of these, only map seems to appear at global scope. That probably explains why we don’t call HandleTopLevelDecl here. How do you think we should proceed from here?

@efriedma-quic
Copy link
Collaborator

If you're convinced it can't happen, add an appropriate assertion.

@ShashwathiNavada
Copy link
Contributor Author

If you're convinced it can't happen, add an appropriate assertion.

Done! please let me know if this looks good or if there is anything else that needs to be done.

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:frontend Language frontend issues, e.g. anything involving "Sema" clang:openmp OpenMP related changes to Clang clang Clang issues not falling into any other category flang:openmp
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants