Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion clang/include/clang/Sema/Sema.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
1 change: 1 addition & 0 deletions clang/lib/AST/ASTContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<MangleNumberingContext> &MCtx = MangleNumberingContexts[DC];
DC = DC->getPrimaryContext();
if (!MCtx)
MCtx = createMangleNumberingContext();
return *MCtx;
Expand Down
9 changes: 8 additions & 1 deletion clang/lib/Sema/SemaLambda.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 (auto *VD = dyn_cast<VarDecl>(ManglingContextDecl);
VD && !VD->hasGlobalStorage())
ManglingContextDecl = nullptr;
}
return std::make_tuple(nullptr, ManglingContextDecl);
}

case NonInlineInModulePurview:
Expand Down
66 changes: 66 additions & 0 deletions clang/test/CodeGen/unnamed-types.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
// 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

// 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<int N>
auto dg_template = [] { return N; };
}

using namespace QL;
template<typename T>
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
Copy link
Contributor

Choose a reason for hiding this comment

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

Am I understanding correctly that the variable names are now included in the mangling while it was not previously?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The issues occur when using the -fsycl flag or internal OpenMP flags. Several problems can arise: depending on the scenario, either the lambda name mangling does not follow the Itanium ABI, or there are name collisions. For examples, see this https://godbolt.org/z/Yvvrjnnjv

// 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<lambda_0>@1@A" = internal global %class.anon undef, align 1
// MSVC: @"?dg2@QL@@3V<lambda_1>@1@A" = internal global %class.anon.0 undef, align 1
// MSVC: @"?dg_inline1@QL@@3V<lambda_1>@01@A" = linkonce_odr dso_local global %class.anon.2 undef, comdat, align 1
// MSVC: @"??$dg_template@$02@QL@@3V<lambda_1>@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<lambda_1>@dg_inline1@QL@@@@YAXV<lambda_1>@dg_inline1@QL@@@Z"
// MSVC: call noundef i32 @"??R<lambda_1>@dg_inline1@QL@@QEBA?A?<auto>@@XZ"
// MSVC: define linkonce_odr dso_local void @"??$f@V<lambda_1>@?$dg_template@$02@QL@@@@YAXV<lambda_1>@?$dg_template@$02@QL@@@Z"
// MSVC: call noundef i32 @"??R<lambda_1>@?$dg_template@$02@QL@@QEBA?A?<auto>@@XZ"
// MSVC: define internal noundef i32 @"??R<lambda_0>@QL@@QEBA?A?<auto>@@XZ"
// MSVC: define internal noundef i32 @"??R<lambda_1>@QL@@QEBA?A?<auto>@@XZ"
// MSVC: define linkonce_odr dso_local noundef i32 @"??R<lambda_1>@dg_inline1@QL@@QEBA?A?<auto>@@XZ"
// MSVC: define linkonce_odr dso_local noundef i32 @"??R<lambda_1>@?$dg_template@$02@QL@@QEBA?A?<auto>@@XZ"
4 changes: 2 additions & 2 deletions clang/test/CodeGenCUDA/anon-ns.cu
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
79 changes: 79 additions & 0 deletions clang/test/CodeGenSYCL/unnamed-types.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
// 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

// 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<int N>
auto dg_template = [] { return N; };
}

using namespace QL;
template<typename T>
[[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