From cd93532dfc455255cb2fa553090d14aaa52b106b Mon Sep 17 00:00:00 2001 From: Bolshakov Date: Wed, 3 May 2023 17:31:05 -0700 Subject: [PATCH] [MS ABI] Fix C++ mangling references to declarations. Several issues have been discovered and (hopefully) fixed here: - Reference NTTPs should be mangled in the same manner as pointer ones. - Pointer fields of class type NTTPs should be treated in the same manner as reference ones. - Pointer-to-member fields of class type NTTPs should be treated differently compared to pointer-to-member NTTPs. Tests on pointer-to-member-function NTTP class fields added. - Correct mangling of pointers to anonymous union members. - A bug in mangling references to subobjects fixed. - Mangling array subscripts and base class members in references to subobjects. Reference NTTP mangling was done back in 2013 in e8fdc06e0dab2e7b98339425dbe369e27e2092a3, and Microsoft might change mangling algorithm since then. But class type NTTPs are introduced only in C++20, and the test was written in b637148ecb62b900872b34eedd78b923bb43c378. It is strange if the MS ABI had been realy changed, because Microsoft claims that they maintain ABI stability since VS 2015. I've tested both on v142 and v143 MSVC toolsets, and they show the same behavior on the test cases which are changed in this PR. But pointer-to-member-function NTTP class field mangling has been actually changed, because it was erroneous in v142, leading to name collisions. Moreover, pointer-to-member mangling with conversions across class hierarchy has been enabled. Differential Revision: https://reviews.llvm.org/D146386 --- clang/lib/AST/MicrosoftMangle.cpp | 135 +++++++++++++----- clang/test/CodeGenCXX/mangle-class-nttp.cpp | 99 ++++++++++--- clang/test/CodeGenCXX/mangle-ms-templates.cpp | 4 +- 3 files changed, 181 insertions(+), 57 deletions(-) diff --git a/clang/lib/AST/MicrosoftMangle.cpp b/clang/lib/AST/MicrosoftMangle.cpp index e0fd8abe5e3b..d70f1a19acbb 100644 --- a/clang/lib/AST/MicrosoftMangle.cpp +++ b/clang/lib/AST/MicrosoftMangle.cpp @@ -29,12 +29,14 @@ #include "clang/Basic/FileManager.h" #include "clang/Basic/SourceManager.h" #include "clang/Basic/TargetInfo.h" +#include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringExtras.h" #include "llvm/Support/CRC.h" #include "llvm/Support/MD5.h" #include "llvm/Support/MathExtras.h" #include "llvm/Support/StringSaver.h" #include "llvm/Support/xxhash.h" +#include #include using namespace clang; @@ -368,9 +370,13 @@ public: void mangleVariableEncoding(const VarDecl *VD); void mangleMemberDataPointer(const CXXRecordDecl *RD, const ValueDecl *VD, StringRef Prefix = "$"); + void mangleMemberDataPointerInClassNTTP(const CXXRecordDecl *, + const ValueDecl *); void mangleMemberFunctionPointer(const CXXRecordDecl *RD, const CXXMethodDecl *MD, StringRef Prefix = "$"); + void mangleMemberFunctionPointerInClassNTTP(const CXXRecordDecl *RD, + const CXXMethodDecl *MD); void mangleVirtualMemPtrThunk(const CXXMethodDecl *MD, const MethodVFTableLocation &ML); void mangleNumber(int64_t Number); @@ -711,6 +717,28 @@ void MicrosoftCXXNameMangler::mangleMemberDataPointer(const CXXRecordDecl *RD, mangleNumber(VBTableOffset); } +void MicrosoftCXXNameMangler::mangleMemberDataPointerInClassNTTP( + const CXXRecordDecl *RD, const ValueDecl *VD) { + MSInheritanceModel IM = RD->getMSInheritanceModel(); + // ::= + // ::= N + // ::= 8 @ @ + + if (IM != MSInheritanceModel::Single && IM != MSInheritanceModel::Multiple) + return mangleMemberDataPointer(RD, VD, ""); + + if (!VD) { + Out << 'N'; + return; + } + + Out << '8'; + mangleNestedName(VD); + Out << '@'; + mangleUnqualifiedName(VD); + Out << '@'; +} + void MicrosoftCXXNameMangler::mangleMemberFunctionPointer(const CXXRecordDecl *RD, const CXXMethodDecl *MD, @@ -775,6 +803,34 @@ MicrosoftCXXNameMangler::mangleMemberFunctionPointer(const CXXRecordDecl *RD, mangleNumber(VBTableOffset); } +void MicrosoftCXXNameMangler::mangleMemberFunctionPointerInClassNTTP( + const CXXRecordDecl *RD, const CXXMethodDecl *MD) { + // ::= + // ::= N + // ::= E? + // ::= E? + + if (!MD) { + if (RD->getMSInheritanceModel() != MSInheritanceModel::Single) + return mangleMemberFunctionPointer(RD, MD, ""); + + Out << 'N'; + return; + } + + Out << "E?"; + if (MD->isVirtual()) { + MicrosoftVTableContext *VTContext = + cast(getASTContext().getVTableContext()); + MethodVFTableLocation ML = + VTContext->getMethodVFTableLocation(GlobalDecl(MD)); + mangleVirtualMemPtrThunk(MD, ML); + } else { + mangleName(MD); + mangleFunctionEncoding(MD, /*ShouldMangle=*/true); + } +} + void MicrosoftCXXNameMangler::mangleVirtualMemPtrThunk( const CXXMethodDecl *MD, const MethodVFTableLocation &ML) { // Get the vftable offset. @@ -1188,6 +1244,11 @@ void MicrosoftCXXNameMangler::mangleUnqualifiedName(GlobalDecl GD, // ::= [] void MicrosoftCXXNameMangler::mangleNestedName(GlobalDecl GD) { const NamedDecl *ND = cast(GD.getDecl()); + + if (const auto *ID = dyn_cast(ND)) + for (unsigned I = 1, IE = ID->getChainingSize(); I < IE; ++I) + mangleSourceName(""); + const DeclContext *DC = getEffectiveDeclContext(ND); while (!DC->isTranslationUnit()) { if (isa(ND) || isa(ND)) { @@ -1570,7 +1631,6 @@ void MicrosoftCXXNameMangler::mangleTemplateArg(const TemplateDecl *TD, // ::= 8 @ // ::= A # float // ::= B # double - // ::= E # reference to D // # pointer to member, by component value // ::= F // ::= G @@ -1615,7 +1675,7 @@ void MicrosoftCXXNameMangler::mangleTemplateArg(const TemplateDecl *TD, mangleTemplateArgValue(TPO->getType().getUnqualifiedType(), TPO->getValue()); } else { - mangle(ND, TA.getParamTypeForDecl()->isReferenceType() ? "$E?" : "$1?"); + mangle(ND, "$1?"); } break; } @@ -1744,46 +1804,62 @@ void MicrosoftCXXNameMangler::mangleTemplateArgValue(QualType T, // FIXME: This can only happen as an extension. Invent a mangling. break; } else if (auto *VD = Base.dyn_cast()) { - Out << (T->isReferenceType() ? "E" : "1"); + Out << "E"; mangle(VD); } else { break; } } else { - unsigned NumAts = 0; - if (T->isPointerType()) { + if (T->isPointerType()) Out << "5"; - ++NumAts; - } - QualType T = Base.getType(); + SmallVector EntryTypes; + SmallVector, 2> EntryManglers; + QualType ET = Base.getType(); for (APValue::LValuePathEntry E : V.getLValuePath()) { - // We don't know how to mangle array subscripting yet. - if (T->isArrayType()) - goto mangling_unknown; + if (auto *AT = ET->getAsArrayTypeUnsafe()) { + EntryTypes.push_back('C'); + EntryManglers.push_back([this, I = E.getAsArrayIndex()] { + Out << '0'; + mangleNumber(I); + Out << '@'; + }); + ET = AT->getElementType(); + continue; + } const Decl *D = E.getAsBaseOrMember().getPointer(); - auto *FD = dyn_cast(D); - // We don't know how to mangle derived-to-base conversions yet. - if (!FD) - goto mangling_unknown; + if (auto *FD = dyn_cast(D)) { + ET = FD->getType(); + if (const auto *RD = ET->getAsRecordDecl()) + if (RD->isAnonymousStructOrUnion()) + continue; + } else { + ET = getASTContext().getRecordType(cast(D)); + // Bug in MSVC: fully qualified name of base class should be used for + // mangling to prevent collisions e.g. on base classes with same names + // in different namespaces. + } - Out << "6"; - ++NumAts; - T = FD->getType(); + EntryTypes.push_back('6'); + EntryManglers.push_back([this, D] { + mangleUnqualifiedName(cast(D)); + Out << '@'; + }); } + for (auto I = EntryTypes.rbegin(), E = EntryTypes.rend(); I != E; ++I) + Out << *I; + auto *VD = Base.dyn_cast(); if (!VD) break; Out << "E"; mangle(VD); - for (APValue::LValuePathEntry E : V.getLValuePath()) { - const Decl *D = E.getAsBaseOrMember().getPointer(); - mangleUnqualifiedName(cast(D)); - } - for (unsigned I = 0; I != NumAts; ++I) + for (const std::function &Mangler : EntryManglers) + Mangler(); + if (T->isPointerType()) Out << '@'; } @@ -1794,20 +1870,14 @@ void MicrosoftCXXNameMangler::mangleTemplateArgValue(QualType T, if (WithScalarType) mangleType(T, SourceRange(), QMM_Escape); - // FIXME: The below manglings don't include a conversion, so bail if there - // would be one. MSVC mangles the (possibly converted) value of the - // pointer-to-member object as if it were a struct, leading to collisions - // in some cases. - if (!V.getMemberPointerPath().empty()) - break; - const CXXRecordDecl *RD = T->castAs()->getMostRecentCXXRecordDecl(); const ValueDecl *D = V.getMemberPointerDecl(); if (T->isMemberDataPointerType()) - mangleMemberDataPointer(RD, D, ""); + mangleMemberDataPointerInClassNTTP(RD, D); else - mangleMemberFunctionPointer(RD, cast_or_null(D), ""); + mangleMemberFunctionPointerInClassNTTP(RD, + cast_or_null(D)); return; } @@ -1895,7 +1965,6 @@ void MicrosoftCXXNameMangler::mangleTemplateArgValue(QualType T, break; } -mangling_unknown: DiagnosticsEngine &Diags = Context.getDiags(); unsigned DiagID = Diags.getCustomDiagID( DiagnosticsEngine::Error, "cannot mangle this template argument yet"); diff --git a/clang/test/CodeGenCXX/mangle-class-nttp.cpp b/clang/test/CodeGenCXX/mangle-class-nttp.cpp index 70e5be8456cc..12c81f2ba051 100644 --- a/clang/test/CodeGenCXX/mangle-class-nttp.cpp +++ b/clang/test/CodeGenCXX/mangle-class-nttp.cpp @@ -15,7 +15,7 @@ template void f() {} int n = 0; // CHECK: define weak_odr void @_Z1fIXtl1BadL_Z1nEEEEvv( -// MSABI: define {{.*}} @"??$f@$2UB@@PEBH1?n@@3HAH0A@@@@YAXXZ" +// MSABI: define {{.*}} @"??$f@$2UB@@PEBHE?n@@3HAH0A@@@@YAXXZ" template void f(); // CHECK: define weak_odr void @_Z1fIXtl1BLPKi0ELi1EEEEvv( // MSABI: define {{.*}} @"??$f@$2UB@@PEBH0A@H00@@@YAXXZ" @@ -36,15 +36,19 @@ template void f(0))}>(); // Pointers to subobjects. struct Nested { union { int k; int arr[2]; }; } nested[2]; -struct Derived : A, Nested { int z; } extern derived; +struct Derived : A, Nested { int z; A a_field; } extern derived; // CHECK: define weak_odr void @_Z1fIXtl1BadsoKiL_Z7derivedE16EEEEvv // MSABI: define {{.*}} void @"??$f@$2UB@@PEBH56E?derived@@3UDerived@@Az@@@H0A@@@@YAXXZ" template void f(); -// FIXME: We don't know the MS ABI mangling for array subscripting and -// past-the-end pointers yet. -#ifndef _WIN32 +// CHECK: define weak_odr void @_Z1fIXtl1BadsoKiL_Z7derivedE20EEEEvv +// MSABI: define {{.*}} void @"??$f@$2UB@@PEBH566E?derived@@3UDerived@@Aa_field@@a@@@H0A@@@@YAXXZ" +template void f(); // CHECK: define weak_odr void @_Z1fIXtl1BadsoKiL_Z6nestedE_EEEEvv +// MSABI: define {{.*}} void @"??$f@$2UB@@PEBH56CE?nested@@3PAUNested@@A0A@@k@@@H0A@@@@YAXXZ" template void f(); +// Mangling of pointers to nested array elements and past-the-end pointers +// is still incorrect in MSVC. +#ifndef _WIN32 // CHECK: define weak_odr void @_Z1fIXtl1BadsoKiL_Z6nestedE16_0pEEEEvv template void f(); // CHECK: define weak_odr void @_Z1fIXtl1BadsoKiL_Z7derivedE8pEEEEvv @@ -59,14 +63,16 @@ template
void f() {} // CHECK: define weak_odr void @_Z1fIXtl2BRsoKiL_Z7derivedE16EEEEvv // MSABI: define {{.*}} void @"??$f@$2UBR@@AEBH6E?derived@@3UDerived@@Az@@@@@YAXXZ" template void f(); -// FIXME: We don't know the MS ABI mangling for array subscripting yet. -#ifndef _WIN32 // CHECK: define weak_odr void @_Z1fIXtl2BRsoKiL_Z6nestedE_EEEEvv +// MSABI: define {{.*}} void @"??$f@$2UBR@@AEBH6CE?nested@@3PAUNested@@A0A@@k@@@@@YAXXZ" template void f(); // CHECK: define weak_odr void @_Z1fIXtl2BRsoKiL_Z6nestedE12_0EEEEvv +// MSABI: define {{.*}} void @"??$f@$2UBR@@AEBHC6CE?nested@@3PAUNested@@A00@arr@@00@@@@YAXXZ" template void f(); // CHECK: define weak_odr void @_Z1fIXtl2BRsoKiL_Z7derivedE4EEEEvv +// MSABI: define {{.*}} void @"??$f@$2UBR@@AEBH66E?derived@@3UDerived@@AA@@b@@@@@YAXXZ" template void f(); +#ifndef _WIN32 // CHECK: define weak_odr void @_Z1fIXtl2BRdecvPKiplcvPcadL_Z7derivedELl16EEEEvv template void f(); #endif @@ -77,42 +83,93 @@ template void f() {} // CHECK: define weak_odr void @_Z1fIXtl1CadsoKiL_Z7derivedE16EEEEvv // MSABI: define {{.*}} void @"??$f@$2UC@@PEBH56E?derived@@3UDerived@@Az@@@@@@YAXXZ" template void f(); -#ifndef _WIN32 // CHECK: define weak_odr void @_Z1fIXtl1CadsoKiL_Z7derivedE4EEEEvv +// MSABI: define {{.*}} void @"??$f@$2UC@@PEBH566E?derived@@3UDerived@@AA@@b@@@@@@YAXXZ" template void f(); -#endif // Pointers to members. struct D { const int Derived::*p; int k; }; template void f() {} // CHECK: define weak_odr void @_Z1fIXtl1DLM7DerivedKi0ELi1EEEEvv -// MSABI: define {{.*}} @"??$f@$2UD@@PERDerived@@H0?0H00@@@YAXXZ" +// MSABI: define {{.*}} @"??$f@$2UD@@PERDerived@@HNH00@@@YAXXZ" template void f(); // CHECK: define weak_odr void @_Z1fIXtl1DEEEvv -// MSABI: define {{.*}} @"??$f@$2UD@@PERDerived@@H0?0H0A@@@@YAXXZ" +// MSABI: define {{.*}} @"??$f@$2UD@@PERDerived@@HNH0A@@@@YAXXZ" template void f(); // CHECK: define weak_odr void @_Z1fIXtl1DadL_ZN7Derived1zEEEEEvv -// MSABI: define {{.*}} @"??$f@$2UD@@PERDerived@@H0BA@H0A@@@@YAXXZ" +// MSABI: define {{.*}} @"??$f@$2UD@@PERDerived@@H82@z@@H0A@@@@YAXXZ" template void f(); -#ifndef _WIN32 // CHECK: define weak_odr void @_Z1fIXtl1DmcM7DerivedKiadL_ZN1A1aEEEEEEvv -// MSABI-FIXME: define {{.*}} @"??$f@$2UD@@PERDerived@@H0A@H0A@@@@YAXXZ" +// MSABI: define {{.*}} @"??$f@$2UD@@PERDerived@@H8A@@a@@H0A@@@@YAXXZ" template void f(); // CHECK: define weak_odr void @_Z1fIXtl1DmcM7DerivedKiadL_ZN1A1bEEEEEEvv -// MSABI-FIXME: define {{.*}} @"??$f@$2UD@@PERDerived@@H03H0A@@@@YAXXZ" +// MSABI: define {{.*}} @"??$f@$2UD@@PERDerived@@H8A@@b@@H0A@@@@YAXXZ" template void f(); // FIXME: Is the Ut_1 mangling here correct? // CHECK: define weak_odr void @_Z1fIXtl1DmcM7DerivedKiadL_ZN6NestedUt_1kEE8ELi2EEEEvv -// FIXME: This mangles the same as &A::a (bug in the MS ABI). -// MSABI-FIXME: define {{.*}} @"??$f@$2UD@@PERDerived@@H0A@H01@@@YAXXZ" +// MSABI: define {{.*}} @"??$f@$2UD@@PERDerived@@H8@Nested@@k@@H01@@@YAXXZ" template void f(); struct MoreDerived : A, Derived { int z; }; // CHECK: define weak_odr void @_Z1fIXtl1DmcM7DerivedKiadL_ZN11MoreDerived1zEEn8EEEEvv -// MSABI-FIXME: define {{.*}} @"??$f@$2UD@@PERDerived@@H0BI@H0A@@@@YAXXZ" +// MSABI: define {{.*}} @"??$f@$2UD@@PERDerived@@H8MoreDerived@@z@@H0A@@@@YAXXZ" template void f(); -#endif -// FIXME: Pointers to member functions. +struct DerivedVirtually : virtual A, Nested { int z; }; +struct D2 { const int DerivedVirtually::*p; int k; }; +template void f() {} +// CHECK: define weak_odr void @_Z1fIXtl2D2LM16DerivedVirtuallyKi0ELi1EEEEvv +// MSABI: define {{.*}} @"??$f@$2UD2@@PERDerivedVirtually@@HFA@?0H00@@@YAXXZ" +template void f(); +// CHECK: define weak_odr void @_Z1fIXtl2D2EEEvv +// MSABI: define {{.*}} @"??$f@$2UD2@@PERDerivedVirtually@@HFA@?0H0A@@@@YAXXZ" +template void f(); +// CHECK: define weak_odr void @_Z1fIXtl2D2adL_ZN16DerivedVirtually1zEEEEEvv +// MSABI: define {{.*}} @"??$f@$2UD2@@PERDerivedVirtually@@HFBA@A@H0A@@@@YAXXZ" +template void f(); + +// Forward-decl without MS inheritance keyword means unspecified inheritance +// which is different from e. g. single inheritance. +struct UnspecInherStruct; +struct D3 { const int UnspecInherStruct::*p; }; +template void f() {} +struct UnspecInherStruct { int i; }; +// CHECK: define weak_odr void @_Z1fIXtl2D3adL_ZN17UnspecInherStruct1iEEEEEvv +// MSABI: define {{.*}} @"??$f@$2UD3@@PERUnspecInherStruct@@HGA@A@A@@@@YAXXZ" +template void f(); + +// Pointers to member functions. +// Test struct templates instead of function templates so as to cover +// the separate code which handles nullptr in their pointer-to-member arguments. +struct Derived2 : A, Nested { void f(); virtual void g(); }; +struct D4 { void (Derived2::*p)(); }; +template struct S1 { static void fn() {} }; +// CHECK: define weak_odr void @_ZN2S1IXtl2D4adL_ZN8Derived21fEvEEEE2fnEv +// MSABI: define {{.*}} @"?fn@?$S1@$2UD4@@P8Derived2@@EAAXXZE?f@2@QEAAXXZ@@@SAXXZ" +template void S1::fn(); +// CHECK: define weak_odr void @_ZN2S1IXtl2D4adL_ZN8Derived21gEvEEEE2fnEv +// MSABI: define {{.*}} @"?fn@?$S1@$2UD4@@P8Derived2@@EAAXXZE??_92@$BA@AA@@@SAXXZ" +template void S1::fn(); +// CHECK: define weak_odr void @_ZN2S1IXtl2D4EEE2fnEv +// MSABI: define {{.*}} @"?fn@?$S1@$2UD4@@P8Derived2@@EAAXXZHA@@@@SAXXZ" +template void S1::fn(); + +struct NoInheritance { void f(); }; +struct D5 { void (NoInheritance::*p)(); }; +template struct S2 { static void fn() {} }; +// CHECK: define weak_odr void @_ZN2S2IXtl2D5adL_ZN13NoInheritance1fEvEEEE2fnEv +// MSABI: define {{.*}} @"?fn@?$S2@$2UD5@@P8NoInheritance@@EAAXXZE?f@2@QEAAXXZ@@@SAXXZ" +template void S2::fn(); +// CHECK: define weak_odr void @_ZN2S2IXtl2D5EEE2fnEv +// MSABI: define {{.*}} @"?fn@?$S2@$2UD5@@P8NoInheritance@@EAAXXZN@@@SAXXZ" +template void S2::fn(); + +struct NoInheritanceButUnspecified; +struct D6 { void (NoInheritanceButUnspecified::*p)(); }; +template struct S3 { static void fn() {} }; +// CHECK: define weak_odr void @_ZN2S3IXtl2D6EEE2fnEv +// MSABI: define {{.*}} @"?fn@?$S3@$2UD6@@P8NoInheritanceButUnspecified@@EAAXXZJA@A@?0@@@SAXXZ" +template void S3::fn(); + union E { int n; @@ -204,8 +261,6 @@ template void f(); template void f(); // Empty and nearly-empty unions. -// Some of the MSVC manglings here are our invention, because MSVC rejects, but -// seem likely to be right. union H1 {}; union H2 { int : 1, : 2, : 3; }; union H3 { int : 1, a, : 2, b, : 3; }; diff --git a/clang/test/CodeGenCXX/mangle-ms-templates.cpp b/clang/test/CodeGenCXX/mangle-ms-templates.cpp index 7402d367ae3e..cefe1e0cb5a5 100644 --- a/clang/test/CodeGenCXX/mangle-ms-templates.cpp +++ b/clang/test/CodeGenCXX/mangle-ms-templates.cpp @@ -272,7 +272,7 @@ struct type1 { }; extern const record inst; void recref(type1) {} -// CHECK: "?recref@@YAXU?$type1@$E?inst@@3Urecord@@B@@@Z" +// CHECK: "?recref@@YAXU?$type1@$1?inst@@3Urecord@@B@@@Z" struct _GUID {}; struct __declspec(uuid("{12345678-1234-1234-1234-1234567890aB}")) uuid; @@ -286,7 +286,7 @@ struct UUIDType2 {}; void fun(UUIDType1 a) {} // CHECK: "?fun@@YAXU?$UUIDType1@Uuuid@@$1?_GUID_12345678_1234_1234_1234_1234567890ab@@3U__s_GUID@@B@@@Z" void fun(UUIDType2 b) {} -// CHECK: "?fun@@YAXU?$UUIDType2@Uuuid@@$E?_GUID_12345678_1234_1234_1234_1234567890ab@@3U__s_GUID@@B@@@Z" +// CHECK: "?fun@@YAXU?$UUIDType2@Uuuid@@$1?_GUID_12345678_1234_1234_1234_1234567890ab@@3U__s_GUID@@B@@@Z" template struct TypeWithFriendDefinition { friend void FunctionDefinedWithInjectedName(TypeWithFriendDefinition) {}