Link the default GC strategies everywhere getGCStrategy is used.
authorCampbell Suter <znix@znix.xyz>
Thu, 22 Dec 2022 14:29:58 +0000 (21:29 +0700)
committerDenis Antrushin <dantrushin@gmail.com>
Tue, 3 Jan 2023 08:07:03 +0000 (15:07 +0700)
GC strategies are registered using a system of global constructors: any
object can include a GCRegistry::Add stataic variable to register a
strategy, and that will generate a static constructor which registers
this strategy into a global list.

This allows shared libraries to easily register their own strategies,
but poses a problem related to linking: the default GC strategies
(defined and registered in their own file) must obviously be included in
LLVM binaries.

The previous solution was to define an empty functon in BuiltinGCs.cpp,
and call it from LinkAllCodegenComponents.h - this is the solution used
for many other codegen components. This header is then included into the
llc and lli main source files, ensuring everything gets linked into
those binaries.

This isn't great for GCStrategy, which we'd like [1] to use in other
binaries, notably opt for the RS4GC [2] pass. Instead of doing something
specific to opt (for example, adding a call in LinkAllIR), this patch
links to the registry function from getGCStrategy, in the assumption
that anything that might look up a GC strategy probably also expects
the default strategies to exist.

[1] https://reviews.llvm.org/D140458
[2] RewriteStatepointsForGC

Differential Revision: https://reviews.llvm.org/D140504

llvm/include/llvm/CodeGen/LinkAllCodegenComponents.h
llvm/lib/IR/GCStrategy.cpp

index d615a5d..565d1c3 100644 (file)
@@ -14,7 +14,6 @@
 #ifndef LLVM_CODEGEN_LINKALLCODEGENCOMPONENTS_H
 #define LLVM_CODEGEN_LINKALLCODEGENCOMPONENTS_H
 
-#include "llvm/IR/BuiltinGCs.h"
 #include "llvm/CodeGen/Passes.h"
 #include "llvm/CodeGen/SchedulerRegistry.h"
 #include "llvm/Target/TargetMachine.h"
@@ -38,8 +37,6 @@ namespace {
       (void) llvm::createGreedyRegisterAllocator();
       (void) llvm::createDefaultPBQPRegisterAllocator();
 
-      llvm::linkAllBuiltinGCs();
-
       (void) llvm::createBURRListDAGScheduler(nullptr,
                                               llvm::CodeGenOpt::Default);
       (void) llvm::createSourceListDAGScheduler(nullptr,
index 5833dc2..c3e35bd 100644 (file)
@@ -13,6 +13,7 @@
 
 #include "llvm/IR/GCStrategy.h"
 #include "llvm/ADT/Twine.h"
+#include "llvm/IR/BuiltinGCs.h"
 
 using namespace llvm;
 
@@ -25,6 +26,16 @@ std::unique_ptr<GCStrategy> llvm::getGCStrategy(const StringRef Name) {
     if (S.getName() == Name)
       return S.instantiate();
 
+  // We need to link all the builtin GCs when LLVM is used as a static library.
+  // The linker will quite happily remove the static constructors that register
+  // the builtin GCs if we don't use a function from that object. This function
+  // does nothing but we need to make sure it is (or at least could be, even
+  // with all optimisations enabled) called *somewhere*, and this is a good
+  // place to do that: if the GC strategies are being used then this function
+  // obviously can't be removed by the linker, and here it won't affect
+  // performance, since there's about to be a fatal error anyway.
+  llvm::linkAllBuiltinGCs();
+
   if (GCRegistry::begin() == GCRegistry::end()) {
     // In normal operation, the registry should not be empty.  There should
     // be the builtin GCs if nothing else.  The most likely scenario here is