Skip to content

Commit

Permalink
[clang][ABI] New C++20 module mangling scheme
Browse files Browse the repository at this point in the history
Implement a demangleable strong ownership symbol mangling.

 * The original module symbol mangling scheme turned out to be
undemangleable.

 * The hoped-for C++17 compatibility of weak ownership turns out to be
fragile

* C++20 now has better ways of controlling C++17 compatibility

The issue is captured on the ABI list at:
  itanium-cxx-abi/cxx-abi#134

GCC implements this new mangling.

The old mangling is unceremoniously dropped.  No backwards
compatibility, no deprectated old-mangling flag.  It was always
labelled experimental.  (Old and new manglings cannot be confused.)

Reviewed By: dblaikie

Differential Revision: https://reviews.llvm.org/D122256
  • Loading branch information
urnathan committed Mar 30, 2022
1 parent d688725 commit ae4dce8
Show file tree
Hide file tree
Showing 23 changed files with 379 additions and 104 deletions.
5 changes: 5 additions & 0 deletions clang/lib/AST/Decl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1547,6 +1547,11 @@ LinkageInfo LinkageComputer::getDeclLinkageAndVisibility(const NamedDecl *D) {
}

Module *Decl::getOwningModuleForLinkage(bool IgnoreLinkage) const {
if (isa<NamespaceDecl>(this))
// Namespaces never have module linkage. It is the entities within them
// that [may] do.
return nullptr;

Module *M = getOwningModule();
if (!M)
return nullptr;
Expand Down
141 changes: 76 additions & 65 deletions clang/lib/AST/ItaniumMangle.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -438,6 +438,7 @@ class CXXNameMangler {
void mangleType(QualType T);
void mangleNameOrStandardSubstitution(const NamedDecl *ND);
void mangleLambdaSig(const CXXRecordDecl *Lambda);
void mangleModuleNamePrefix(StringRef Name);

private:

Expand Down Expand Up @@ -473,22 +474,21 @@ class CXXNameMangler {

void mangleNameWithAbiTags(GlobalDecl GD,
const AbiTagList *AdditionalAbiTags);
void mangleModuleName(const Module *M);
void mangleModuleNamePrefix(StringRef Name);
void mangleModuleName(const NamedDecl *ND);
void mangleTemplateName(const TemplateDecl *TD,
const TemplateArgument *TemplateArgs,
unsigned NumTemplateArgs);
void mangleUnqualifiedName(GlobalDecl GD,
void mangleUnqualifiedName(GlobalDecl GD, const DeclContext *DC,
const AbiTagList *AdditionalAbiTags) {
mangleUnqualifiedName(GD, cast<NamedDecl>(GD.getDecl())->getDeclName(), UnknownArity,
AdditionalAbiTags);
mangleUnqualifiedName(GD, cast<NamedDecl>(GD.getDecl())->getDeclName(), DC,
UnknownArity, AdditionalAbiTags);
}
void mangleUnqualifiedName(GlobalDecl GD, DeclarationName Name,
unsigned KnownArity,
const DeclContext *DC, unsigned KnownArity,
const AbiTagList *AdditionalAbiTags);
void mangleUnscopedName(GlobalDecl GD,
void mangleUnscopedName(GlobalDecl GD, const DeclContext *DC,
const AbiTagList *AdditionalAbiTags);
void mangleUnscopedTemplateName(GlobalDecl GD,
void mangleUnscopedTemplateName(GlobalDecl GD, const DeclContext *DC,
const AbiTagList *AdditionalAbiTags);
void mangleSourceName(const IdentifierInfo *II);
void mangleRegCallName(const IdentifierInfo *II);
Expand Down Expand Up @@ -733,15 +733,17 @@ bool ItaniumMangleContextImpl::shouldMangleCXXName(const NamedDecl *D) {
if (VD->isExternC())
return false;

// Variables at global scope with non-internal linkage are not mangled.
// Variables at global scope are not mangled unless they have internal
// linkage or are specializations or are attached to a named module.
const DeclContext *DC = getEffectiveDeclContext(D);
// Check for extern variable declared locally.
if (DC->isFunctionOrMethod() && D->hasLinkage())
while (!DC->isFileContext())
DC = getEffectiveParentContext(DC);
if (DC->isTranslationUnit() && D->getFormalLinkage() != InternalLinkage &&
!CXXNameMangler::shouldHaveAbiTags(*this, VD) &&
!isa<VarTemplateSpecializationDecl>(VD))
!isa<VarTemplateSpecializationDecl>(VD) &&
!VD->getOwningModuleForLinkage())
return false;
}

Expand Down Expand Up @@ -1016,14 +1018,6 @@ void CXXNameMangler::mangleNameWithAbiTags(GlobalDecl GD,
return;
}

// Do not mangle the owning module for an external linkage declaration.
// This enables backwards-compatibility with non-modular code, and is
// a valid choice since conflicts are not permitted by C++ Modules TS
// [basic.def.odr]/6.2.
if (!ND->hasExternalFormalLinkage())
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)) {
Expand All @@ -1035,38 +1029,35 @@ void CXXNameMangler::mangleNameWithAbiTags(GlobalDecl GD,
// Check if we have a template.
const TemplateArgumentList *TemplateArgs = nullptr;
if (GlobalDecl TD = isTemplate(GD, TemplateArgs)) {
mangleUnscopedTemplateName(TD, AdditionalAbiTags);
mangleUnscopedTemplateName(TD, DC, AdditionalAbiTags);
mangleTemplateArgs(asTemplateName(TD), *TemplateArgs);
return;
}

mangleUnscopedName(GD, AdditionalAbiTags);
mangleUnscopedName(GD, DC, AdditionalAbiTags);
return;
}

mangleNestedName(GD, DC, AdditionalAbiTags);
}

void CXXNameMangler::mangleModuleName(const Module *M) {
// Implement the C++ Modules TS name mangling proposal; see
// https://gcc.gnu.org/wiki/cxx-modules?action=AttachFile
//
// <module-name> ::= W <unscoped-name>+ E
// ::= W <module-subst> <unscoped-name>* E
Out << 'W';
mangleModuleNamePrefix(M->Name);
Out << 'E';
void CXXNameMangler::mangleModuleName(const NamedDecl *ND) {
if (ND->isExternallyVisible())
if (Module *M = ND->getOwningModuleForLinkage())
mangleModuleNamePrefix(M->getPrimaryModuleInterfaceName());
}

// <module-name> ::= <module-subname>
// ::= <module-name> <module-subname>
// ::= <substitution>
// <module-subname> ::= W <source-name>
// ::= W P <source-name> # not (yet) needed
void CXXNameMangler::mangleModuleNamePrefix(StringRef Name) {
// <module-subst> ::= _ <seq-id> # 0 < seq-id < 10
// ::= W <seq-id - 10> _ # otherwise
// <substitution> ::= S <seq-id> _
auto It = ModuleSubstitutions.find(Name);
if (It != ModuleSubstitutions.end()) {
if (It->second < 10)
Out << '_' << static_cast<char>('0' + It->second);
else
Out << 'W' << (It->second - 10) << '_';
Out << 'S';
mangleSeqID(It->second);
return;
}

Expand All @@ -1078,8 +1069,9 @@ void CXXNameMangler::mangleModuleNamePrefix(StringRef Name) {
else
mangleModuleNamePrefix(Parts.first);

Out << 'W';
Out << Parts.second.size() << Parts.second;
ModuleSubstitutions.insert({Name, ModuleSubstitutions.size()});
ModuleSubstitutions.insert({Name, SeqID++});
}

void CXXNameMangler::mangleTemplateName(const TemplateDecl *TD,
Expand All @@ -1088,27 +1080,27 @@ void CXXNameMangler::mangleTemplateName(const TemplateDecl *TD,
const DeclContext *DC = Context.getEffectiveDeclContext(TD);

if (DC->isTranslationUnit() || isStdNamespace(DC)) {
mangleUnscopedTemplateName(TD, nullptr);
mangleUnscopedTemplateName(TD, DC, nullptr);
mangleTemplateArgs(asTemplateName(TD), TemplateArgs, NumTemplateArgs);
} else {
mangleNestedName(TD, TemplateArgs, NumTemplateArgs);
}
}

void CXXNameMangler::mangleUnscopedName(GlobalDecl GD,
void CXXNameMangler::mangleUnscopedName(GlobalDecl GD, const DeclContext *DC,
const AbiTagList *AdditionalAbiTags) {
const NamedDecl *ND = cast<NamedDecl>(GD.getDecl());
// <unscoped-name> ::= <unqualified-name>
// ::= St <unqualified-name> # ::std::

if (isStdNamespace(Context.getEffectiveDeclContext(ND)))
assert(!isa<LinkageSpecDecl>(DC) && "unskipped LinkageSpecDecl");
if (isStdNamespace(DC))
Out << "St";

mangleUnqualifiedName(GD, AdditionalAbiTags);
mangleUnqualifiedName(GD, DC, AdditionalAbiTags);
}

void CXXNameMangler::mangleUnscopedTemplateName(
GlobalDecl GD, const AbiTagList *AdditionalAbiTags) {
GlobalDecl GD, const DeclContext *DC, const AbiTagList *AdditionalAbiTags) {
const TemplateDecl *ND = cast<TemplateDecl>(GD.getDecl());
// <unscoped-template-name> ::= <unscoped-name>
// ::= <substitution>
Expand All @@ -1121,9 +1113,10 @@ void CXXNameMangler::mangleUnscopedTemplateName(
"template template param cannot have abi tags");
mangleTemplateParameter(TTP->getDepth(), TTP->getIndex());
} else if (isa<BuiltinTemplateDecl>(ND) || isa<ConceptDecl>(ND)) {
mangleUnscopedName(GD, AdditionalAbiTags);
mangleUnscopedName(GD, DC, AdditionalAbiTags);
} else {
mangleUnscopedName(GD.getWithDecl(ND->getTemplatedDecl()), AdditionalAbiTags);
mangleUnscopedName(GD.getWithDecl(ND->getTemplatedDecl()), DC,
AdditionalAbiTags);
}

addSubstitution(ND);
Expand Down Expand Up @@ -1399,15 +1392,19 @@ void CXXNameMangler::mangleUnresolvedName(
mangleTemplateArgs(TemplateName(), TemplateArgs, NumTemplateArgs);
}

void CXXNameMangler::mangleUnqualifiedName(GlobalDecl GD,
DeclarationName Name,
unsigned KnownArity,
const AbiTagList *AdditionalAbiTags) {
void CXXNameMangler::mangleUnqualifiedName(
GlobalDecl GD, DeclarationName Name, const DeclContext *DC,
unsigned KnownArity, const AbiTagList *AdditionalAbiTags) {
const NamedDecl *ND = cast_or_null<NamedDecl>(GD.getDecl());
unsigned Arity = KnownArity;
// <unqualified-name> ::= <operator-name>
// <unqualified-name> ::= [<module-name>] <operator-name>
// ::= <ctor-dtor-name>
// ::= <source-name>
// ::= [<module-name>] <source-name>
// ::= [<module-name>] DC <source-name>* E

if (ND && DC && DC->isFileContext())
mangleModuleName(ND);

unsigned Arity = KnownArity;
switch (Name.getNameKind()) {
case DeclarationName::Identifier: {
const IdentifierInfo *II = Name.getAsIdentifierInfo();
Expand All @@ -1418,8 +1415,6 @@ void CXXNameMangler::mangleUnqualifiedName(GlobalDecl GD,
//
// <unqualified-name> ::= DC <source-name>* E
//
// These can never be referenced across translation units, so we do
// not need a cross-vendor mangling for anything other than demanglers.
// Proposed on cxx-abi-dev on 2016-08-12
Out << "DC";
for (auto *BD : DD->bindings())
Expand Down Expand Up @@ -1716,7 +1711,7 @@ void CXXNameMangler::mangleNestedName(GlobalDecl GD,
mangleTemplateArgs(asTemplateName(TD), *TemplateArgs);
} else {
manglePrefix(DC, NoFunction);
mangleUnqualifiedName(GD, AdditionalAbiTags);
mangleUnqualifiedName(GD, DC, AdditionalAbiTags);
}

Out << 'E';
Expand Down Expand Up @@ -1746,7 +1741,7 @@ void CXXNameMangler::mangleNestedNameWithClosurePrefix(
Out << 'N';

mangleClosurePrefix(PrefixND);
mangleUnqualifiedName(GD, AdditionalAbiTags);
mangleUnqualifiedName(GD, nullptr, AdditionalAbiTags);

Out << 'E';
}
Expand Down Expand Up @@ -1824,7 +1819,7 @@ void CXXNameMangler::mangleLocalName(GlobalDecl GD,
// Mangle the name relative to the closest enclosing function.
// equality ok because RD derived from ND above
if (D == RD) {
mangleUnqualifiedName(RD, AdditionalAbiTags);
mangleUnqualifiedName(RD, DC, AdditionalAbiTags);
} else if (const BlockDecl *BD = dyn_cast<BlockDecl>(D)) {
if (const NamedDecl *PrefixND = getClosurePrefix(BD))
mangleClosurePrefix(PrefixND, true /*NoFunction*/);
Expand Down Expand Up @@ -1855,7 +1850,7 @@ void CXXNameMangler::mangleLocalName(GlobalDecl GD,
assert(!AdditionalAbiTags && "Block cannot have additional abi tags");
mangleUnqualifiedBlock(BD);
} else {
mangleUnqualifiedName(GD, AdditionalAbiTags);
mangleUnqualifiedName(GD, DC, AdditionalAbiTags);
}

if (const NamedDecl *ND = dyn_cast<NamedDecl>(RD ? RD : D)) {
Expand Down Expand Up @@ -2082,10 +2077,11 @@ void CXXNameMangler::manglePrefix(const DeclContext *DC, bool NoFunction) {
mangleTemplateArgs(asTemplateName(TD), *TemplateArgs);
} else if (const NamedDecl *PrefixND = getClosurePrefix(ND)) {
mangleClosurePrefix(PrefixND, NoFunction);
mangleUnqualifiedName(ND, nullptr);
mangleUnqualifiedName(ND, nullptr, nullptr);
} else {
manglePrefix(Context.getEffectiveDeclContext(ND), NoFunction);
mangleUnqualifiedName(ND, nullptr);
const DeclContext *DC = Context.getEffectiveDeclContext(ND);
manglePrefix(DC, NoFunction);
mangleUnqualifiedName(ND, DC, nullptr);
}

addSubstitution(ND);
Expand Down Expand Up @@ -2138,11 +2134,13 @@ void CXXNameMangler::mangleTemplatePrefix(GlobalDecl GD,
if (const auto *TTP = dyn_cast<TemplateTemplateParmDecl>(ND)) {
mangleTemplateParameter(TTP->getDepth(), TTP->getIndex());
} else {
manglePrefix(Context.getEffectiveDeclContext(ND), NoFunction);
const DeclContext *DC = Context.getEffectiveDeclContext(ND);
manglePrefix(DC, NoFunction);
if (isa<BuiltinTemplateDecl>(ND) || isa<ConceptDecl>(ND))
mangleUnqualifiedName(GD, nullptr);
mangleUnqualifiedName(GD, DC, nullptr);
else
mangleUnqualifiedName(GD.getWithDecl(ND->getTemplatedDecl()), nullptr);
mangleUnqualifiedName(GD.getWithDecl(ND->getTemplatedDecl()), DC,
nullptr);
}

addSubstitution(ND);
Expand Down Expand Up @@ -2183,8 +2181,9 @@ void CXXNameMangler::mangleClosurePrefix(const NamedDecl *ND, bool NoFunction) {
mangleTemplatePrefix(TD, NoFunction);
mangleTemplateArgs(asTemplateName(TD), *TemplateArgs);
} else {
manglePrefix(Context.getEffectiveDeclContext(ND), NoFunction);
mangleUnqualifiedName(ND, nullptr);
const auto *DC = Context.getEffectiveDeclContext(ND);
manglePrefix(DC, NoFunction);
mangleUnqualifiedName(ND, DC, nullptr);
}

Out << 'M';
Expand Down Expand Up @@ -6027,6 +6026,9 @@ bool CXXNameMangler::isSpecializedAs(QualType S, llvm::StringRef Name,
if (TemplateArgs[0].getAsType() != A)
return false;

if (SD->getSpecializedTemplate()->getOwningModuleForLinkage())
return false;

return true;
}

Expand Down Expand Up @@ -6058,6 +6060,9 @@ bool CXXNameMangler::isStdCharSpecialization(
!isSpecializedAs(TemplateArgs[2].getAsType(), "allocator", A))
return false;

if (SD->getSpecializedTemplate()->getOwningModuleForLinkage())
return false;

return true;
}

Expand All @@ -6075,6 +6080,9 @@ bool CXXNameMangler::mangleStandardSubstitution(const NamedDecl *ND) {
if (!isStdNamespace(Context.getEffectiveDeclContext(TD)))
return false;

if (TD->getOwningModuleForLinkage())
return false;

// <substitution> ::= Sa # ::std::allocator
if (TD->getIdentifier()->isStr("allocator")) {
Out << "Sa";
Expand All @@ -6094,6 +6102,9 @@ bool CXXNameMangler::mangleStandardSubstitution(const NamedDecl *ND) {
if (!isStdNamespace(Context.getEffectiveDeclContext(SD)))
return false;

if (SD->getSpecializedTemplate()->getOwningModuleForLinkage())
return false;

// <substitution> ::= Ss # ::std::basic_string<char,
// ::std::char_traits<char>,
// ::std::allocator<char> >
Expand Down
24 changes: 12 additions & 12 deletions clang/test/CXX/modules-ts/basic/basic.def.odr/p4/module.cpp
Original file line number Diff line number Diff line change
@@ -1,35 +1,35 @@
// RUN: %clang_cc1 -fmodules-ts %S/module.cppm -triple %itanium_abi_triple -emit-module-interface -o %t
// RUN: %clang_cc1 -fmodules-ts %s -triple %itanium_abi_triple -fmodule-file=%t -emit-llvm -o - | FileCheck %s --implicit-check-not=unused --implicit-check-not=global_module

// CHECK-DAG: @extern_var_exported = external {{(dso_local )?}}global
// CHECK-DAG: @inline_var_exported = linkonce_odr {{(dso_local )?}}global
// CHECK-DAG: @const_var_exported = available_externally {{(dso_local )?}}constant i32 3,
// CHECK-DAG: @_ZW6Module19extern_var_exported = external {{(dso_local )?}}global
// CHECK-DAG: @_ZW6Module19inline_var_exported = linkonce_odr {{(dso_local )?}}global
// CHECK-DAG: @_ZW6Module18const_var_exported = available_externally {{(dso_local )?}}constant i32 3,
//
// CHECK-DAG: @_ZW6ModuleE25extern_var_module_linkage = external {{(dso_local )?}}global
// CHECK-DAG: @_ZW6ModuleE25inline_var_module_linkage = linkonce_odr {{(dso_local )?}}global
// CHECK-DAG: @_ZW6ModuleE25static_var_module_linkage = available_externally {{(dso_local )?}}global i32 0,
// CHECK-DAG: @_ZW6ModuleE24const_var_module_linkage = available_externally {{(dso_local )?}}constant i32 3,
// CHECK-DAG: @_ZW6Module25extern_var_module_linkage = external {{(dso_local )?}}global
// CHECK-DAG: @_ZW6Module25inline_var_module_linkage = linkonce_odr {{(dso_local )?}}global
// CHECK-DAG: @_ZW6Module25static_var_module_linkage = available_externally {{(dso_local )?}}global i32 0,
// CHECK-DAG: @_ZW6Module24const_var_module_linkage = available_externally {{(dso_local )?}}constant i32 3,

module Module;

void use() {
// CHECK: define linkonce_odr {{.*}}@_Z20used_inline_exportedv
// CHECK: define linkonce_odr {{.*}}@_ZW6Module20used_inline_exportedv
used_inline_exported();
// CHECK: declare {{.*}}@_Z18noninline_exportedv
// CHECK: declare {{.*}}@_ZW6Module18noninline_exportedv
noninline_exported();

(void)&extern_var_exported;
(void)&inline_var_exported;
(void)&const_var_exported;

// FIXME: This symbol should not be visible here.
// CHECK: declare {{.*}}@_ZW6ModuleE26used_static_module_linkagev
// CHECK: declare {{.*}}@_ZW6Module26used_static_module_linkagev
used_static_module_linkage();

// CHECK: define linkonce_odr {{.*}}@_ZW6ModuleE26used_inline_module_linkagev
// CHECK: define linkonce_odr {{.*}}@_ZW6Module26used_inline_module_linkagev
used_inline_module_linkage();

// CHECK: declare {{.*}}@_ZW6ModuleE24noninline_module_linkagev
// CHECK: declare {{.*}}@_ZW6Module24noninline_module_linkagev
noninline_module_linkage();

(void)&extern_var_module_linkage;
Expand Down
Loading

0 comments on commit ae4dce8

Please sign in to comment.