From 653e99edbb04dcbc0cc18d1d0f5ec5cf44fb582e Mon Sep 17 00:00:00 2001 From: Zahira Ammarguellat Date: Tue, 23 Sep 2025 12:37:05 -0700 Subject: [PATCH 1/3] [SYCL] Fix Lambda Mangling in Namespace-Scope Variable Initializers. --- clang/include/clang/Sema/Sema.h | 8 ++- clang/lib/AST/ASTContext.cpp | 1 + clang/lib/Sema/SemaLambda.cpp | 9 ++- clang/test/CodeGen/unnamed-types.cpp | 61 +++++++++++++++++++ clang/test/CodeGenCUDA/anon-ns.cu | 4 +- clang/test/CodeGenSYCL/unnamed-types.cpp | 74 ++++++++++++++++++++++++ 6 files changed, 153 insertions(+), 4 deletions(-) create mode 100644 clang/test/CodeGen/unnamed-types.cpp create mode 100644 clang/test/CodeGenSYCL/unnamed-types.cpp diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index e542e995ab3a3..12ccfec12da61 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -9183,7 +9183,13 @@ class Sema final : public SemaBase { }; /// Compute the mangling number context for a lambda expression or - /// block literal. Also return the extra mangling decl if any. + /// block literal that appears in the specified declaration context in + /// consideration of the current expression evaluation and template + /// instantiation contexts. If the mangling context requires external linkage, + /// then a mangling number context is returned in the first tuple + /// element. If the mangling context is non-normal (specialized for + /// lambda and block types relative to other entities), the overriding + /// declaration is returned in the second tuple element. /// /// \param DC - The DeclContext containing the lambda expression or /// block literal. diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp index 5d7e50688f354..a9a961e188a10 100644 --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -13356,6 +13356,7 @@ MangleNumberingContext & ASTContext::getManglingNumberContext(const DeclContext *DC) { assert(LangOpts.CPlusPlus); // We don't need mangling numbers for plain C. std::unique_ptr &MCtx = MangleNumberingContexts[DC]; + DC = DC->getPrimaryContext(); if (!MCtx) MCtx = createMangleNumberingContext(); return *MCtx; diff --git a/clang/lib/Sema/SemaLambda.cpp b/clang/lib/Sema/SemaLambda.cpp index 37a737764aa34..ee50c13e41444 100644 --- a/clang/lib/Sema/SemaLambda.cpp +++ b/clang/lib/Sema/SemaLambda.cpp @@ -356,7 +356,14 @@ Sema::getCurrentMangleNumberContext(const DeclContext *DC) { return std::make_tuple(&Context.getManglingNumberContext(DC), nullptr); } - return std::make_tuple(nullptr, nullptr); + if (ManglingContextDecl) { + // Lambdas defined in the initializer of a local variable are mangled + // in the enclosing function context. + if (isa(ManglingContextDecl) && + !cast(ManglingContextDecl)->hasGlobalStorage()) + ManglingContextDecl = nullptr; + } + return std::make_tuple(nullptr, ManglingContextDecl); } case NonInlineInModulePurview: diff --git a/clang/test/CodeGen/unnamed-types.cpp b/clang/test/CodeGen/unnamed-types.cpp new file mode 100644 index 0000000000000..b47552278f479 --- /dev/null +++ b/clang/test/CodeGen/unnamed-types.cpp @@ -0,0 +1,61 @@ +// RUN: %clang_cc1 -O0 -triple x86_64-unknown-unknown \ +// RUN: -emit-llvm %s -o - | FileCheck %s + +// RUN: %clang_cc1 -O0 -triple x86_64-pc-windows-msvc \ +// RUN: -emit-llvm %s -o - | FileCheck %s --check-prefix=MSVC + +namespace QL { + auto dg1 = [] { return 1; }; + inline auto dg_inline1 = [] { return 1; }; +} + +namespace QL { + auto dg2 = [] { return 2; }; + template + auto dg_template = [] { return N; }; +} + +using namespace QL; +template +void f(T t) { + t(); +} + +void g() { + f(dg1); + f(dg2); + f(dg_inline1); + f(dg_template<3>); +} + +// CHECK: @_ZN2QL3dg1E = internal global %class.anon undef, align 1 +// CHECK: @_ZN2QL3dg2E = internal global %class.anon.0 undef, align 1 +// CHECK: @_ZN2QL10dg_inline1E = linkonce_odr global %class.anon.2 undef, comdat, align 1 +// CHECK: @_ZN2QL11dg_templateILi3EEE = linkonce_odr global %class.anon.4 undef, comdat, align 1 + +// MSVC: @"?dg1@QL@@3V@1@A" = internal global %class.anon undef, align 1 +// MSVC: @"?dg2@QL@@3V@1@A" = internal global %class.anon.0 undef, align 1 +// MSVC: @"?dg_inline1@QL@@3V@01@A" = linkonce_odr dso_local global %class.anon.2 undef, comdat, align 1 +// MSVC: @"??$dg_template@$02@QL@@3V@01@A" = linkonce_odr dso_local global %class.anon.4 undef, comdat, align 1 + + +// CHECK: define internal void @"_Z1fIN2QL3$_0EEvT_" +// CHECK: call noundef i32 @"_ZNK2QL3$_0clEv" +// CHECK: define internal void @"_Z1fIN2QL3$_1EEvT_" +// CHECK: define linkonce_odr void @_Z1fIN2QL10dg_inline1MUlvE_EEvT_ +// CHECK: call noundef i32 @_ZNK2QL10dg_inline1MUlvE_clEv +// CHECK: define linkonce_odr void @_Z1fIN2QL11dg_templateILi3EEMUlvE_EEvT_ +// CHECK: call noundef i32 @_ZNK2QL11dg_templateILi3EEMUlvE_clEv +// CHECK: define internal noundef i32 @"_ZNK2QL3$_0clEv" +// CHECK: define internal noundef i32 @"_ZNK2QL3$_1clEv" +// CHECK: define linkonce_odr noundef i32 @_ZNK2QL10dg_inline1MUlvE_clEv +// CHECK: define linkonce_odr noundef i32 @_ZNK2QL11dg_templateILi3EEMUlvE_clEv + +// MSVC: define linkonce_odr dso_local void @"??$f@V@dg_inline1@QL@@@@YAXV@dg_inline1@QL@@@Z" +// MSVC: call noundef i32 @"??R@dg_inline1@QL@@QEBA?A?@@XZ" +// MSVC: define linkonce_odr dso_local void @"??$f@V@?$dg_template@$02@QL@@@@YAXV@?$dg_template@$02@QL@@@Z" +// MSVC: call noundef i32 @"??R@?$dg_template@$02@QL@@QEBA?A?@@XZ" +// MSVC: define internal noundef i32 @"??R@QL@@QEBA?A?@@XZ" +// MSVC: define internal noundef i32 @"??R@QL@@QEBA?A?@@XZ" +// MSVC: define linkonce_odr dso_local noundef i32 @"??R@dg_inline1@QL@@QEBA?A?@@XZ" +// MSVC: define linkonce_odr dso_local noundef i32 @"??R@?$dg_template@$02@QL@@QEBA?A?@@XZ" diff --git a/clang/test/CodeGenCUDA/anon-ns.cu b/clang/test/CodeGenCUDA/anon-ns.cu index d931f31d0207c..61ba92276b91a 100644 --- a/clang/test/CodeGenCUDA/anon-ns.cu +++ b/clang/test/CodeGenCUDA/anon-ns.cu @@ -26,14 +26,14 @@ // HIP-DAG: define weak_odr {{.*}}void @[[KERN:_ZN12_GLOBAL__N_16kernelEv\.intern\.b04fd23c98500190]]( // HIP-DAG: define weak_odr {{.*}}void @[[KTX:_Z2ktIN12_GLOBAL__N_11XEEvT_\.intern\.b04fd23c98500190]]( -// HIP-DAG: define weak_odr {{.*}}void @[[KTL:_Z2ktIN12_GLOBAL__N_1UlvE_EEvT_\.intern\.b04fd23c98500190]]( +// HIP-DAG: define weak_odr {{.*}}void @[[KTL:_Z2ktIN12_GLOBAL__N_16lambdaMUlvE_EEvT_.intern\.b04fd23c98500190]]( // HIP-DAG: @[[VM:_ZN12_GLOBAL__N_12vmE\.static\.b04fd23c98500190]] = addrspace(1) externally_initialized global // HIP-DAG: @[[VC:_ZN12_GLOBAL__N_12vcE\.static\.b04fd23c98500190]] = addrspace(4) externally_initialized constant // HIP-DAG: @[[VT:_Z2vtIN12_GLOBAL__N_11XEE\.static\.b04fd23c98500190]] = addrspace(1) externally_initialized global // CUDA-DAG: define weak_odr {{.*}}void @[[KERN:_ZN12_GLOBAL__N_16kernelEv__intern__b04fd23c98500190]]( // CUDA-DAG: define weak_odr {{.*}}void @[[KTX:_Z2ktIN12_GLOBAL__N_11XEEvT___intern__b04fd23c98500190]]( -// CUDA-DAG: define weak_odr {{.*}}void @[[KTL:_Z2ktIN12_GLOBAL__N_1UlvE_EEvT___intern__b04fd23c98500190]]( +// CUDA-DAG: define weak_odr {{.*}}void @[[KTL:_Z2ktIN12_GLOBAL__N_16lambdaMUlvE_EEvT___intern__b04fd23c98500190]]( // CUDA-DAG: @[[VC:_ZN12_GLOBAL__N_12vcE__static__b04fd23c98500190]] = addrspace(4) externally_initialized constant // CUDA-DAG: @[[VT:_Z2vtIN12_GLOBAL__N_11XEE__static__b04fd23c98500190]] = addrspace(1) externally_initialized global diff --git a/clang/test/CodeGenSYCL/unnamed-types.cpp b/clang/test/CodeGenSYCL/unnamed-types.cpp new file mode 100644 index 0000000000000..909c31bea55aa --- /dev/null +++ b/clang/test/CodeGenSYCL/unnamed-types.cpp @@ -0,0 +1,74 @@ +// RUN: %clang_cc1 -fsycl-is-device -O0 -triple spirv64-unknown-unknown \ +// RUN: -emit-llvm %s -o - | FileCheck %s --check-prefix=DEVICE + +// RUN: %clang_cc1 -fsycl-is-host -O0 -triple spirv64-unknown-unknown \ +// RUN: -emit-llvm %s -o - | FileCheck %s --check-prefix=HOST + +// RUN: %clang_cc1 -fsycl-is-device -emit-llvm \ +// RUN: -aux-triple x86_64-pc-windows-msvc -triple spir64-unknown--unknown \ +// RUN: %s -o - | FileCheck %s --check-prefix=MSVC + +namespace QL { + auto dg1 = [] { return 1; }; + inline auto dg_inline1 = [] { return 1; }; +} + +namespace QL { + auto dg2 = [] { return 2; }; + template + auto dg_template = [] { return N; }; +} + +using namespace QL; +template +[[clang::sycl_kernel_entry_point(T)]] void f(T t) { + t(); +} + +void g() { + f(dg1); + f(dg2); + f(dg_inline1); + f(dg_template<3>); +} + +// HOST: @_ZN2QL3dg1E = internal global %class.anon undef, align 1 +// HOST: @_ZN2QL3dg2E = internal global %class.anon.0 undef, align 1 +// HOST: @_ZN2QL10dg_inline1E = linkonce_odr global %class.anon.2 undef, comdat, align 1 +// HOST: @_ZN2QL11dg_templateILi3EEE = linkonce_odr global %class.anon.4 undef, comdat, align 1 + +// DEVICE: define spir_kernel void @_ZTSN2QL3dg1MUlvE_E +// DEVICE: call spir_func noundef i32 @_ZNK2QL3dg1MUlvE_clEv +// DEVICE: define internal spir_func noundef i32 @_ZNK2QL3dg1MUlvE_clEv +// DEVICE: define spir_kernel void @_ZTSN2QL3dg2MUlvE_E +// DEVICE: call spir_func noundef i32 @_ZNK2QL3dg2MUlvE_clEv +// DEVICE: define internal spir_func noundef i32 @_ZNK2QL3dg2MUlvE_clEv +// DEVICE: define spir_kernel void @_ZTSN2QL10dg_inline1MUlvE_E +// DEVICE: call spir_func noundef i32 @_ZNK2QL10dg_inline1MUlvE_clEv +// DEVICE: define linkonce_odr spir_func noundef i32 @_ZNK2QL10dg_inline1MUlvE_clEv +// DEVICE: define spir_kernel void @_ZTSN2QL11dg_templateILi3EEMUlvE_E +// DEVICE: call spir_func noundef i32 @_ZNK2QL11dg_templateILi3EEMUlvE_clEv +// DEVICE: define linkonce_odr spir_func noundef i32 @_ZNK2QL11dg_templateILi3EEMUlvE_clEv + +// HOST: define spir_func void @_Z1gv +// HOST: call spir_func void @_Z1fIN2QL3dg1MUlvE_EEvT_ +// HOST: call spir_func void @_Z1fIN2QL3dg2MUlvE_EEvT_ +// HOST: call spir_func void @_Z1fIN2QL10dg_inline1MUlvE_EEvT_ +// HOST: call spir_func void @_Z1fIN2QL11dg_templateILi3EEMUlvE_EEvT_ +// HOST: define internal spir_func void @_Z1fIN2QL3dg1MUlvE_EEvT +// HOST: define internal spir_func void @_Z1fIN2QL3dg2MUlvE_EEvT_ +// HOST: define linkonce_odr spir_func void @_Z1fIN2QL10dg_inline1MUlvE_EEvT_ +// HOST: define linkonce_odr spir_func void @_Z1fIN2QL11dg_templateILi3EEMUlvE_EEvT_ + +// MSVC: define dso_local spir_kernel void @_ZTSN2QL3dg1MUlvE_E +// MSVC: call spir_func noundef i32 @_ZNK2QL3dg1MUlvE_clEv +// MSVC: define internal spir_func noundef i32 @_ZNK2QL3dg1MUlvE_clEv +// MSVC: define dso_local spir_kernel void @_ZTSN2QL3dg2MUlvE_E +// MSVC: call spir_func noundef i32 @_ZNK2QL3dg2MUlvE_clEv +// MSVC: define internal spir_func noundef i32 @_ZNK2QL3dg2MUlvE_clEv +// MSVC: define dso_local spir_kernel void @_ZTSN2QL10dg_inline1MUlvE_E +// MSVC: call spir_func noundef i32 @_ZNK2QL10dg_inline1MUlvE_clEv +// MSVC: define linkonce_odr spir_func noundef i32 @_ZNK2QL10dg_inline1MUlvE_clEv +// MSVC: define dso_local spir_kernel void @_ZTSN2QL11dg_templateILi3EEMUlvE_E +// MSVC: call spir_func noundef i32 @_ZNK2QL11dg_templateILi3EEMUlvE_clEv +// MSVC: define linkonce_odr spir_func noundef i32 @_ZNK2QL11dg_templateILi3EEMUlvE_clEv From b6753257956dbbb183c12b6a1a47883369b29d2a Mon Sep 17 00:00:00 2001 From: Zahira Ammarguellat Date: Wed, 24 Sep 2025 08:17:16 -0700 Subject: [PATCH 2/3] Addressed review comments. --- clang/lib/Sema/SemaLambda.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clang/lib/Sema/SemaLambda.cpp b/clang/lib/Sema/SemaLambda.cpp index ee50c13e41444..31022d18d1532 100644 --- a/clang/lib/Sema/SemaLambda.cpp +++ b/clang/lib/Sema/SemaLambda.cpp @@ -359,8 +359,8 @@ Sema::getCurrentMangleNumberContext(const DeclContext *DC) { if (ManglingContextDecl) { // Lambdas defined in the initializer of a local variable are mangled // in the enclosing function context. - if (isa(ManglingContextDecl) && - !cast(ManglingContextDecl)->hasGlobalStorage()) + if (auto *VD = dyn_cast(ManglingContextDecl); + VD && !VD->hasGlobalStorage()) ManglingContextDecl = nullptr; } return std::make_tuple(nullptr, ManglingContextDecl); From 31ce4b6d36433bf29f5f454b4c3412a642328910 Mon Sep 17 00:00:00 2001 From: Zahira Ammarguellat Date: Wed, 24 Sep 2025 11:32:21 -0700 Subject: [PATCH 3/3] Added description comment to LIT tests. --- clang/test/CodeGen/unnamed-types.cpp | 5 +++++ clang/test/CodeGenSYCL/unnamed-types.cpp | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/clang/test/CodeGen/unnamed-types.cpp b/clang/test/CodeGen/unnamed-types.cpp index b47552278f479..f4b42b2ed9c7e 100644 --- a/clang/test/CodeGen/unnamed-types.cpp +++ b/clang/test/CodeGen/unnamed-types.cpp @@ -1,3 +1,8 @@ +// This test checks that lambdas assigned to variables (including inline and +// templated cases) in the same namespace are uniquely mangled and callable via +// template functions. It ensures that the compiler generates distinct symbols +// for each lambda and resolves them correctly in function calls. + // RUN: %clang_cc1 -O0 -triple x86_64-unknown-unknown \ // RUN: -emit-llvm %s -o - | FileCheck %s diff --git a/clang/test/CodeGenSYCL/unnamed-types.cpp b/clang/test/CodeGenSYCL/unnamed-types.cpp index 909c31bea55aa..9a409f9e3ad5d 100644 --- a/clang/test/CodeGenSYCL/unnamed-types.cpp +++ b/clang/test/CodeGenSYCL/unnamed-types.cpp @@ -1,3 +1,8 @@ +// This test checks that lambdas assigned to variables (including inline and +// templated cases) in the same namespace are uniquely mangled and callable via +// template functions. It ensures that the compiler generates distinct symbols +// for each lambda and resolves them correctly in function calls. + // RUN: %clang_cc1 -fsycl-is-device -O0 -triple spirv64-unknown-unknown \ // RUN: -emit-llvm %s -o - | FileCheck %s --check-prefix=DEVICE