[Windows] Export Registry<T> members and accessors for plugins again (#221296)
Since #185141 reworked `llvm::Registry`, the registered-node list lives behind the explicitly specialized `llvm::detail::getRegistryLinkListInstance<R>()` accessor defined by `LLVM_DEFINE_REGISTRY`, and the `Head`/`Tail` static data members that used to be referenced across objects are gone (#163391 had special-cased those). `extract_symbols.py` handles neither. The accessor is a `??$` function template specialization, which the script drops on the assumption that it can be instantiated in the importing translation unit; here it cannot, it is defined once by `LLVM_DEFINE_REGISTRY`. And `Registry<T>` members are only exported when some object references the `Registry` template, which nothing does anymore because every use goes through the accessor. On a static clang.exe built with `LLVM_EXPORT_SYMBOLS_FOR_PLUGINS`, the export table went from 11 `Registry<clang::PluginASTAction>` symbols on 22.1.8 to zero on 23.1.0, and linking a plugin fails: ``` lld-link: error: undefined symbol: __declspec(dllimport) public: static void __cdecl llvm::Registry<class clang::PluginASTAction>::add_node(class llvm::Registry<class clang::PluginASTAction>::node *) >>> referenced by host_Registration.obj:(_GLOBAL__sub_I_Registration.cpp) ``` Keep the accessor specializations, and treat a reference to an accessor as a reference to the `Registry` template so its explicitly instantiated members are exported as they were before. Verified on the 23.1.0 static libraries: `Registry<PluginASTAction>::add_node`, the node/iterator members and the accessor come back for every registry (PluginASTAction, PragmaHandler, GCStrategy, GCMetadataPrinter, ...), 141 symbols in total in the Itanium run over the clang libraries. The `Head`/`Tail` rule from #163391 no longer matches anything and is dropped. Related: #163367 / #163391 (the previous Registry export fix), #87865. --- **AI tool use disclosure** (per the [LLVM AI Tool Use Policy](https://llvm.org/docs/AIToolPolicy.html)): this change was developed with Claude Code assisting in the analysis and drafting. The root cause was established against real 23.1.0 binaries and libraries, and the fix was verified by running extract_symbols.py over the 23.1.0 clang static libraries (the Registry members and accessors come back) and against MSVC-mangled test objects; Firefox's Windows clang-plugin and mozsearch builds link and run against a clang.exe built with it. `llvm/test/tools/extract_symbols/` adds lit coverage for both manglings. The author reviewed the change and is accountable for and able to answer questions about it. Commits carry an `Assisted-by:` trailer. (cherry picked from commit bc6c76a4d4f8b4f6d5e9b663736703f91fcbb6ed)
This commit is contained in:
1 parent
bb46d66418
commit
5177329b80
8 files changed
+105
-8
No files matched your search
@@ -0,0 +1,10 @@
|
||||
.text
|
||||
.globl _ZN4llvm6detail27getRegistryLinkListInstanceINS_8RegistryIN5clang15PluginASTActionEJEEEEERNS0_23RegistryLinkListStorageIT_EEv
|
||||
_ZN4llvm6detail27getRegistryLinkListInstanceINS_8RegistryIN5clang15PluginASTActionEJEEEEERNS0_23RegistryLinkListStorageIT_EEv:
|
||||
retq
|
||||
.globl _ZN4llvm8RegistryIN5clang15PluginASTActionEJEE8add_nodeEPNS3_4nodeE
|
||||
_ZN4llvm8RegistryIN5clang15PluginASTActionEJEE8add_nodeEPNS3_4nodeE:
|
||||
retq
|
||||
.globl _ZN4llvm8RegistryIN5clang15PluginASTActionEJEE5beginEv
|
||||
_ZN4llvm8RegistryIN5clang15PluginASTActionEJEE5beginEv:
|
||||
retq
|
||||
@@ -0,0 +1,12 @@
|
||||
# Explicit specialization defined by LLVM_DEFINE_REGISTRY, plus the explicitly instantiated Registry<T> member
|
||||
# that only a plugin references (LLVM_INSTANTIATE_REGISTRY's `template class Registry<...>`).
|
||||
.text
|
||||
.globl "??$getRegistryLinkListInstance@V?$Registry@VPluginASTAction@clang@@$$V@llvm@@@detail@llvm@@YAAEAU?$RegistryLinkListStorage@V?$Registry@VPluginASTAction@clang@@$$V@llvm@@@01@XZ"
|
||||
"??$getRegistryLinkListInstance@V?$Registry@VPluginASTAction@clang@@$$V@llvm@@@detail@llvm@@YAAEAU?$RegistryLinkListStorage@V?$Registry@VPluginASTAction@clang@@$$V@llvm@@@01@XZ":
|
||||
retq
|
||||
.globl "?add_node@?$Registry@VPluginASTAction@clang@@$$V@llvm@@SAXPEAVnode@12@@Z"
|
||||
"?add_node@?$Registry@VPluginASTAction@clang@@$$V@llvm@@SAXPEAVnode@12@@Z":
|
||||
retq
|
||||
.globl "?begin@?$Registry@VPluginASTAction@clang@@$$V@llvm@@SA?AViterator@12@XZ"
|
||||
"?begin@?$Registry@VPluginASTAction@clang@@$$V@llvm@@SA?AViterator@12@XZ":
|
||||
retq
|
||||
@@ -0,0 +1,8 @@
|
||||
.text
|
||||
.globl _Z10HasPluginsv
|
||||
_Z10HasPluginsv:
|
||||
callq _ZN4llvm6detail27getRegistryLinkListInstanceINS_8RegistryIN5clang15PluginASTActionEJEEEEERNS0_23RegistryLinkListStorageIT_EEv
|
||||
retq
|
||||
.weak _ZN4llvm8RegistryIN5clang15PluginASTActionEJEE5beginEv
|
||||
_ZN4llvm8RegistryIN5clang15PluginASTActionEJEE5beginEv:
|
||||
retq
|
||||
@@ -0,0 +1,10 @@
|
||||
# A TU that iterates the registry: references the accessor (declared-only via LLVM_DECLARE_REGISTRY) and
|
||||
# carries its own inline copy of begin(), but never touches add_node.
|
||||
.text
|
||||
.globl "?HasPlugins@@YA_NXZ"
|
||||
"?HasPlugins@@YA_NXZ":
|
||||
callq "??$getRegistryLinkListInstance@V?$Registry@VPluginASTAction@clang@@$$V@llvm@@@detail@llvm@@YAAEAU?$RegistryLinkListStorage@V?$Registry@VPluginASTAction@clang@@$$V@llvm@@@01@XZ"
|
||||
retq
|
||||
.globl "?begin@?$Registry@VPluginASTAction@clang@@$$V@llvm@@SA?AViterator@12@XZ"
|
||||
"?begin@?$Registry@VPluginASTAction@clang@@$$V@llvm@@SA?AViterator@12@XZ":
|
||||
retq
|
||||
@@ -0,0 +1,21 @@
|
||||
import os
|
||||
|
||||
import lit.util
|
||||
|
||||
config.suffixes = [".test"]
|
||||
|
||||
if not "X86" in config.root.targets:
|
||||
config.unsupported = True
|
||||
|
||||
config.substitutions.append(
|
||||
(
|
||||
"%extract_symbols",
|
||||
"'%s' %s --nm=%s --readobj=%s"
|
||||
% (
|
||||
config.python_executable,
|
||||
os.path.join(config.llvm_src_root, "utils", "extract_symbols.py"),
|
||||
lit.util.which("llvm-nm", config.llvm_tools_dir),
|
||||
lit.util.which("llvm-readobj", config.llvm_tools_dir),
|
||||
),
|
||||
)
|
||||
)
|
||||
@@ -0,0 +1,13 @@
|
||||
## Itanium counterpart of registry-microsoft.test: a reference to the
|
||||
## getRegistryLinkListInstance<R>() accessor counts as a reference to Registry,
|
||||
## so its explicitly instantiated members that are defined once are exported.
|
||||
## Symbols are printed in input order, so the checks are ordered.
|
||||
|
||||
# RUN: rm -rf %t && mkdir -p %t
|
||||
# RUN: llvm-mc -triple=x86_64-linux-gnu -filetype=obj %S/Inputs/lib-itanium.s -o %t/lib.o
|
||||
# RUN: llvm-mc -triple=x86_64-linux-gnu -filetype=obj %S/Inputs/user-itanium.s -o %t/user.o
|
||||
# RUN: %extract_symbols --mangling=itanium %t/lib.o %t/user.o \
|
||||
# RUN: | FileCheck %s --implicit-check-not=Registry
|
||||
|
||||
# CHECK: _ZN4llvm6detail27getRegistryLinkListInstanceINS_8RegistryIN5clang15PluginASTActionEJEEEEERNS0_23RegistryLinkListStorageIT_EEv
|
||||
# CHECK-NEXT: _ZN4llvm8RegistryIN5clang15PluginASTActionEJEE8add_nodeEPNS3_4nodeE
|
||||
@@ -0,0 +1,19 @@
|
||||
## Registry<T> is only ever used through the getRegistryLinkListInstance<R>()
|
||||
## accessor that LLVM_DEFINE_REGISTRY defines once, so the accessor (a ??$
|
||||
## specialization) and the explicitly instantiated Registry<T> members that only
|
||||
## a plugin references must both be exported, or a plugin cannot link against a
|
||||
## static tool built with LLVM_EXPORT_SYMBOLS_FOR_PLUGINS.
|
||||
##
|
||||
## Symbols are printed in input order, so the checks are ordered.
|
||||
|
||||
# RUN: rm -rf %t && mkdir -p %t
|
||||
# RUN: llvm-mc -triple=x86_64-pc-windows-msvc -filetype=obj %S/Inputs/lib-msvc.s -o %t/lib.obj
|
||||
# RUN: llvm-mc -triple=x86_64-pc-windows-msvc -filetype=obj %S/Inputs/user-msvc.s -o %t/user.obj
|
||||
# RUN: %extract_symbols --mangling=microsoft %t/lib.obj %t/user.obj \
|
||||
# RUN: | FileCheck %s --implicit-check-not=Registry
|
||||
|
||||
## The accessor specialization and add_node are defined once and referenced
|
||||
## only through the accessor, so both are exported. begin() is defined in both
|
||||
## objects (the user TU instantiated the inline member itself), so it is not.
|
||||
# CHECK: ??$getRegistryLinkListInstance@V?$Registry@VPluginASTAction@clang@@$$V@llvm@@@detail@llvm@@YAAEAU?$RegistryLinkListStorage@V?$Registry@VPluginASTAction@clang@@$$V@llvm@@@01@XZ
|
||||
# CHECK-NEXT: ?add_node@?$Registry@VPluginASTAction@clang@@$$V@llvm@@SAXPEAVnode@12@@Z
|
||||
@@ -106,6 +106,13 @@ def should_keep_microsoft_symbol(symbol, calling_convention_decoration):
|
||||
# instantiated locally. Pattern: ??$getAs@<template_arg>@Type@clang@@...
|
||||
if symbol.startswith("??$getAs@") and "@Type@clang@@" in symbol:
|
||||
return symbol
|
||||
# Keep the Registry<T> storage accessors: explicit specializations that
|
||||
# LLVM_DEFINE_REGISTRY defines once, and that plugins call to register.
|
||||
if (
|
||||
symbol.startswith("??$getRegistryLinkListInstance@")
|
||||
and "@detail@llvm@@" in symbol
|
||||
):
|
||||
return symbol
|
||||
return None
|
||||
# Delete lambda object constructors and operator() functions. These start
|
||||
# with ??R<lambda_ or ??0<lambda_ and can be discarded because lambdas are
|
||||
@@ -120,14 +127,6 @@ def should_keep_microsoft_symbol(symbol, calling_convention_decoration):
|
||||
# Skip X86GenMnemonicTables functions, they are not exposed from llvm/include/.
|
||||
elif re.match(r"\?is[A-Z0-9]*@X86@llvm", symbol):
|
||||
return None
|
||||
# Keep Registry<T>::Head and Registry<T>::Tail static members for plugin support.
|
||||
# Pattern matches: ?Head@?$Registry@<template_args>@llvm@@ or ?Tail@?$Registry@...
|
||||
elif (
|
||||
"?$Registry@" in symbol
|
||||
and "@llvm@@" in symbol
|
||||
and (symbol.startswith("?Head@") or symbol.startswith("?Tail@"))
|
||||
):
|
||||
return symbol
|
||||
# Skip symbols added by the compiler with -fprofile-generate.
|
||||
elif symbol.startswith("__prof"):
|
||||
return None
|
||||
@@ -519,6 +518,11 @@ if __name__ == "__main__":
|
||||
template = get_template_name(sym, args.mangling)
|
||||
if template:
|
||||
template_instantiation_refs.add(template)
|
||||
# Registry<T> is only ever used through its storage accessor.
|
||||
if template.endswith("getRegistryLinkListInstance"):
|
||||
template_instantiation_refs.add(
|
||||
"Registry" if args.mangling == "microsoft" else "8Registry"
|
||||
)
|
||||
|
||||
# Print symbols which both:
|
||||
# * Appear in exactly one input, as symbols defined in multiple
|
||||
|
||||
Reference in new issue
Block a user