diff --git a/clang/include/clang/Basic/CodeGenOptions.h b/clang/include/clang/Basic/CodeGenOptions.h --- a/clang/include/clang/Basic/CodeGenOptions.h +++ b/clang/include/clang/Basic/CodeGenOptions.h @@ -75,23 +75,6 @@ LocalExecTLSModel }; - /// Clang versions with different platform ABI conformance. - enum class ClangABI { - /// Attempt to be ABI-compatible with code generated by Clang 3.8.x - /// (SVN r257626). This causes <1 x long long> to be passed in an - /// integer register instead of an SSE register on x64_64. - Ver3_8, - - /// Attempt to be ABI-compatible with code generated by Clang 4.0.x - /// (SVN r291814). This causes move operations to be ignored when - /// determining whether a class type can be passed or returned directly. - Ver4, - - /// Conform to the underlying platform's C and C++ ABIs as closely - /// as we can. - Latest - }; - enum StructReturnConventionKind { SRCK_Default, // No special option was passed. SRCK_OnStack, // Small structs on the stack (-fpcc-struct-return). diff --git a/clang/include/clang/Basic/LangOptions.h b/clang/include/clang/Basic/LangOptions.h --- a/clang/include/clang/Basic/LangOptions.h +++ b/clang/include/clang/Basic/LangOptions.h @@ -164,13 +164,18 @@ Ver9, /// Attempt to be ABI-compatible with code generated by Clang 11.0.x - /// (git 2e10b7a39b93). This causes clang to pass unions with a 256-bit + /// (git 2e10b7a39b93). This causes clang to pass unions with a 256-bit /// vector member on the stack instead of using registers, to not properly /// mangle substitutions for template names in some cases, and to mangle /// declaration template arguments without a cast to the parameter type /// even when that can lead to mangling collisions. Ver11, + /// Attempt to be ABI-compatible with code generated by Clang 12.0.x + /// (git 8e464dd76bef). This causes clang to mangle lambdas within + /// global-scope inline variables incorrectly. + Ver12, + /// Conform to the underlying platform's C and C++ ABIs as closely /// as we can. Latest diff --git a/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp --- a/clang/lib/AST/ItaniumMangle.cpp +++ b/clang/lib/AST/ItaniumMangle.cpp @@ -538,11 +538,16 @@ void mangleNestedName(const TemplateDecl *TD, const TemplateArgument *TemplateArgs, unsigned NumTemplateArgs); + void mangleNestedNameWithClosurePrefix(GlobalDecl GD, + const NamedDecl *PrefixND, + const AbiTagList *AdditionalAbiTags); void manglePrefix(NestedNameSpecifier *qualifier); void manglePrefix(const DeclContext *DC, bool NoFunction=false); void manglePrefix(QualType type); void mangleTemplatePrefix(GlobalDecl GD, bool NoFunction=false); void mangleTemplatePrefix(TemplateName Template); + const NamedDecl *getClosurePrefix(const Decl *ND); + void mangleClosurePrefix(const NamedDecl *ND, bool NoFunction = false); bool mangleUnresolvedTypeOrSimpleId(QualType DestroyedType, StringRef Prefix = ""); void mangleOperatorName(DeclarationName Name, unsigned Arity); @@ -982,6 +987,13 @@ if (Module *M = ND->getOwningModuleForLinkage()) mangleModuleName(M); + // Closures can require a nested-name mangling even if they're semantically + // in the global namespace. + if (const NamedDecl *PrefixND = getClosurePrefix(ND)) { + mangleNestedNameWithClosurePrefix(GD, PrefixND, AdditionalAbiTags); + return; + } + if (DC->isTranslationUnit() || isStdNamespace(DC)) { // Check if we have a template. const TemplateArgumentList *TemplateArgs = nullptr; @@ -1656,8 +1668,7 @@ if (GlobalDecl TD = isTemplate(GD, TemplateArgs)) { mangleTemplatePrefix(TD, NoFunction); mangleTemplateArgs(asTemplateName(TD), *TemplateArgs); - } - else { + } else { manglePrefix(DC, NoFunction); mangleUnqualifiedName(GD, AdditionalAbiTags); } @@ -1677,6 +1688,23 @@ Out << 'E'; } +void CXXNameMangler::mangleNestedNameWithClosurePrefix( + GlobalDecl GD, const NamedDecl *PrefixND, + const AbiTagList *AdditionalAbiTags) { + // A represents a variable or field, not a regular + // DeclContext, so needs special handling. In this case we're mangling a + // limited form of : + // + // ::= N E + + Out << 'N'; + + mangleClosurePrefix(PrefixND); + mangleUnqualifiedName(GD, AdditionalAbiTags); + + Out << 'E'; +} + static GlobalDecl getParentOfLocalEntity(const DeclContext *DC) { GlobalDecl GD; // The Itanium spec says: @@ -1752,7 +1780,10 @@ if (D == RD) { mangleUnqualifiedName(RD, AdditionalAbiTags); } else if (const BlockDecl *BD = dyn_cast(D)) { - manglePrefix(getEffectiveDeclContext(BD), true /*NoFunction*/); + if (const NamedDecl *PrefixND = getClosurePrefix(BD)) + mangleClosurePrefix(PrefixND, true /*NoFunction*/); + else + manglePrefix(getEffectiveDeclContext(BD), true /*NoFunction*/); assert(!AdditionalAbiTags && "Block cannot have additional abi tags"); mangleUnqualifiedBlock(BD); } else { @@ -1802,13 +1833,20 @@ mangleLocalName(Block, /* AdditionalAbiTags */ nullptr); return; } - manglePrefix(getEffectiveDeclContext(Block)); + if (const NamedDecl *PrefixND = getClosurePrefix(Block)) + mangleClosurePrefix(PrefixND); + else + manglePrefix(DC); mangleUnqualifiedBlock(Block); } void CXXNameMangler::mangleUnqualifiedBlock(const BlockDecl *Block) { + // When trying to be ABI-compatibility with clang 12 and before, mangle a + // now, with no substitutions and no . if (Decl *Context = Block->getBlockManglingContextDecl()) { - if ((isa(Context) || isa(Context)) && + if (getASTContext().getLangOpts().getClangABICompat() <= + LangOptions::ClangABI::Ver12 && + (isa(Context) || isa(Context)) && Context->getDeclContext()->isRecord()) { const auto *ND = cast(Context); if (ND->getIdentifier()) { @@ -1881,20 +1919,13 @@ } void CXXNameMangler::mangleLambda(const CXXRecordDecl *Lambda) { - // If the context of a closure type is an initializer for a class member - // (static or nonstatic), it is encoded in a qualified name with a final - // of the form: - // - // := M - // - // Technically, the data-member-prefix is part of the . However, - // since a closure type will always be mangled with a prefix, it's easier - // to emit that last part of the prefix here. + // When trying to be ABI-compatibility with clang 12 and before, mangle a + // now, with no substitutions. if (Decl *Context = Lambda->getLambdaContextDecl()) { - if ((isa(Context) || isa(Context)) && + if (getASTContext().getLangOpts().getClangABICompat() <= + LangOptions::ClangABI::Ver12 && + (isa(Context) || isa(Context)) && !isa(Context)) { - // FIXME: 'inline auto [a, b] = []{ return ... };' does not get a - // reasonable mangling here. if (const IdentifierInfo *Name = cast(Context)->getIdentifier()) { mangleSourceName(Name); @@ -1977,6 +2008,7 @@ void CXXNameMangler::manglePrefix(const DeclContext *DC, bool NoFunction) { // ::= // ::= + // ::= // ::= // ::= # empty // ::= @@ -1995,11 +2027,14 @@ if (mangleSubstitution(ND)) return; - // Check if we have a template. + // Check if we have a template-prefix or a closure-prefix. const TemplateArgumentList *TemplateArgs = nullptr; if (GlobalDecl TD = isTemplate(ND, TemplateArgs)) { mangleTemplatePrefix(TD); mangleTemplateArgs(asTemplateName(TD), *TemplateArgs); + } else if (const NamedDecl *PrefixND = getClosurePrefix(ND)) { + mangleClosurePrefix(PrefixND, NoFunction); + mangleUnqualifiedName(ND, nullptr); } else { manglePrefix(getEffectiveDeclContext(ND), NoFunction); mangleUnqualifiedName(ND, nullptr); @@ -2065,6 +2100,50 @@ addSubstitution(ND); } +const NamedDecl *CXXNameMangler::getClosurePrefix(const Decl *ND) { + if (getASTContext().getLangOpts().getClangABICompat() <= + LangOptions::ClangABI::Ver12) + return nullptr; + + const NamedDecl *Context = nullptr; + if (auto *Block = dyn_cast(ND)) { + Context = dyn_cast_or_null(Block->getBlockManglingContextDecl()); + } else if (auto *RD = dyn_cast(ND)) { + if (RD->isLambda()) + Context = dyn_cast_or_null(RD->getLambdaContextDecl()); + } + if (!Context) + return nullptr; + + // Only lambdas within the initializer of a non-local variable or non-static + // data member get a . + if ((isa(Context) && cast(Context)->hasGlobalStorage()) || + isa(Context)) + return Context; + + return nullptr; +} + +void CXXNameMangler::mangleClosurePrefix(const NamedDecl *ND, bool NoFunction) { + // ::= [ ] M + // ::= M + if (mangleSubstitution(ND)) + return; + + const TemplateArgumentList *TemplateArgs = nullptr; + if (GlobalDecl TD = isTemplate(ND, TemplateArgs)) { + mangleTemplatePrefix(TD, NoFunction); + mangleTemplateArgs(asTemplateName(TD), *TemplateArgs); + } else { + manglePrefix(getEffectiveDeclContext(ND), NoFunction); + mangleUnqualifiedName(ND, nullptr); + } + + Out << 'M'; + + addSubstitution(ND); +} + /// Mangles a template name under the production . Required for /// template template arguments. /// ::= diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp --- a/clang/lib/Frontend/CompilerInvocation.cpp +++ b/clang/lib/Frontend/CompilerInvocation.cpp @@ -3509,6 +3509,8 @@ GenerateArg(Args, OPT_fclang_abi_compat_EQ, "9.0", SA); else if (Opts.getClangABICompat() == LangOptions::ClangABI::Ver11) GenerateArg(Args, OPT_fclang_abi_compat_EQ, "11.0", SA); + else if (Opts.getClangABICompat() == LangOptions::ClangABI::Ver12) + GenerateArg(Args, OPT_fclang_abi_compat_EQ, "12.0", SA); if (Opts.getSignReturnAddressScope() == LangOptions::SignReturnAddressScopeKind::All) @@ -3970,6 +3972,8 @@ Opts.setClangABICompat(LangOptions::ClangABI::Ver9); else if (Major <= 11) Opts.setClangABICompat(LangOptions::ClangABI::Ver11); + else if (Major <= 12) + Opts.setClangABICompat(LangOptions::ClangABI::Ver12); } else if (Ver != "latest") { Diags.Report(diag::err_drv_invalid_value) << A->getAsString(Args) << A->getValue(); diff --git a/clang/test/CodeGenCXX/clang-abi-compat.cpp b/clang/test/CodeGenCXX/clang-abi-compat.cpp --- a/clang/test/CodeGenCXX/clang-abi-compat.cpp +++ b/clang/test/CodeGenCXX/clang-abi-compat.cpp @@ -13,11 +13,13 @@ // RUN: %clang_cc1 -std=c++17 -triple x86_64-linux-gnu -fenable-matrix -fclang-abi-compat=11 %s -emit-llvm -o - \ // RUN: | FileCheck --check-prefixes=CHECK,V39,V5,PRE12,PRE12-CXX17 %s // RUN: %clang_cc1 -std=c++20 -triple x86_64-linux-gnu -fenable-matrix -fclang-abi-compat=11 %s -emit-llvm -o - \ -// RUN: | FileCheck --check-prefixes=CHECK,V39,V5,PRE12,PRE12-CXX17,PRE12-CXX20 %s +// RUN: | FileCheck --check-prefixes=CHECK,V39,V5,PRE12,PRE12-CXX17,PRE12-CXX20,PRE13-CXX20 %s +// RUN: %clang_cc1 -std=c++20 -triple x86_64-linux-gnu -fenable-matrix -fclang-abi-compat=12 %s -emit-llvm -o - \ +// RUN: | FileCheck --check-prefixes=CHECK,V39,V5,V12,V12-CXX17,V12-CXX20,PRE13-CXX20 %s // RUN: %clang_cc1 -std=c++98 -triple x86_64-linux-gnu -fenable-matrix -fclang-abi-compat=latest %s -emit-llvm -o - -Wno-c++11-extensions \ // RUN: | FileCheck --check-prefixes=CHECK,V39,V5,V12 %s // RUN: %clang_cc1 -std=c++20 -triple x86_64-linux-gnu -fenable-matrix -fclang-abi-compat=latest %s -emit-llvm -o - \ -// RUN: | FileCheck --check-prefixes=CHECK,V39,V5,V12,V12-CXX17,V12-CXX20 %s +// RUN: | FileCheck --check-prefixes=CHECK,V39,V5,V12,V12-CXX17,V12-CXX20,V13-CXX20 %s typedef __attribute__((vector_size(8))) long long v1xi64; void clang39(v1xi64) {} @@ -136,3 +138,12 @@ void test9(void) __attribute__((enable_if(1, ""))) {} } + +#if __cplusplus >= 202002L +// PRE13-CXX20: @_Z15observe_lambdasI17inline_var_lambdaMUlvE_17inline_var_lambdaMUlvE0_PiS2_S0_S1_EiT_T0_T1_T2_ +// V13-CXX20: @_Z15observe_lambdasIN17inline_var_lambdaMUlvE_ENS0_UlvE0_EPiS3_S1_S2_EiT_T0_T1_T2_ +template +int observe_lambdas(T, U, V, W) { return 0; } +inline auto inline_var_lambda = observe_lambdas([]{}, []{}, (int*)0, (int*)0); +int use_inline_var_lambda() { return inline_var_lambda; } +#endif diff --git a/clang/test/CodeGenCXX/lambda-expressions-nested-linkage.cpp b/clang/test/CodeGenCXX/lambda-expressions-nested-linkage.cpp --- a/clang/test/CodeGenCXX/lambda-expressions-nested-linkage.cpp +++ b/clang/test/CodeGenCXX/lambda-expressions-nested-linkage.cpp @@ -22,6 +22,13 @@ L l; } +// It's important that this is not in a namespace; we're testing the mangling +// of lambdas in top-level inline variables here. +inline auto lambda_in_inline_variable = [] {}; +template struct Wrap {}; +// CHECK-LABEL: define {{.*}} @_Z30test_lambda_in_inline_variable4WrapIN25lambda_in_inline_variableMUlvE_EE +void test_lambda_in_inline_variable(Wrap) {} + namespace lambdas_in_NSDMIs_template_class { template struct L { diff --git a/clang/test/CodeGenCXX/mangle-lambda-explicit-template-params.cpp b/clang/test/CodeGenCXX/mangle-lambda-explicit-template-params.cpp --- a/clang/test/CodeGenCXX/mangle-lambda-explicit-template-params.cpp +++ b/clang/test/CodeGenCXX/mangle-lambda-explicit-template-params.cpp @@ -38,7 +38,7 @@ inline auto pack = [](T (&...)[N]) {}; int arr1[] = {1}; int arr2[] = {1, 2}; -// CHECK: @_ZNK4packMUlTpTyTpTnT_DpRAT0__S_E_clIJiiEJLi1ELi2EEEEDaS2_( +// CHECK: @_ZNK4packMUlTpTyTpTnT_DpRAT0__S0_E_clIJiiEJLi1ELi2EEEEDaS3_( void use_pack() { pack(arr1, arr2); } inline void collision() {