[patch] Add support for comdats to the gold plugin

Duncan P. N. Exon Smith dexonsmith at apple.com
Fri Aug 22 14:31:53 PDT 2014


> On 2014-Aug-22, at 09:57, Rafael EspĂ­ndola <rafael.espindola at gmail.com> wrote:
> 
> The attached patch adds comdat support to the gold plugin.
> 
> There are two parts to it. One is the plugin telling gold the comdat.
> That part is really easy, just setting comdat_key.
> 
> What gets things a bit more complicated is that gold only seems
> symbols. In particular, if A is an alias to B, it only see the symbols
> A and B. It can then ask us to keep symbol A but drop symbol B. What
> we have to do instead is to create an internal version of B and make A
> an alias to that.
> 

Why not just give B internal (or private?) linkage?

> There is an example with the problem in llvm.org/pr20617. The patch
> includes an extended testcase.
> 
> At some point some of this logic should be moved to lib/Linker so that
> we don't map a Constant to an internal version just to have lib/Linker
> map that again to the destination module. Moving the logic should also
> allow us to fix pr20617 in llvm-link.
> 
> The reason for implementing this in tools/gold for now is simplicity.
> With it in place it should be possible to update clang to use comdats
> for constructors and destructors on ELF without breaking the LTO
> bootstrap. Once that is done I intend to come back and improve the
> interface lib/Linker exposes.
> 
> Duncan, do you known if there is anything else that needs to be done
> when creating an  local function replacement or is just he call to
> splice sufficient?

I think the splice should work.

I have a couple of other comments below.

> diff --git a/tools/gold/gold-plugin.cpp b/tools/gold/gold-plugin.cpp
> index 25a21b7..8fc91ce 100644
> --- a/tools/gold/gold-plugin.cpp
> +++ b/tools/gold/gold-plugin.cpp
> @@ -13,6 +13,7 @@
>  //===----------------------------------------------------------------------===//
>  
>  #include "llvm/Config/config.h" // plugin-api.h requires HAVE_STDINT_H
> +#include "llvm/ADT/DenseSet.h"
>  #include "llvm/ADT/StringSet.h"
>  #include "llvm/Bitcode/ReaderWriter.h"
>  #include "llvm/CodeGen/Analysis.h"
> @@ -34,6 +35,7 @@
>  #include "llvm/Transforms/IPO/PassManagerBuilder.h"
>  #include "llvm/Transforms/Utils/GlobalStatus.h"
>  #include "llvm/Transforms/Utils/ModuleUtils.h"
> +#include "llvm/Transforms/Utils/ValueMapper.h"
>  #include <list>
>  #include <plugin-api.h>
>  #include <system_error>
> @@ -255,6 +257,12 @@ ld_plugin_status onload(ld_plugin_tv *tv) {
>    return LDPS_OK;
>  }
>  
> +static const GlobalObject *getBaseObject(const GlobalValue &GV) {
> +  if (auto *GA = dyn_cast<GlobalAlias>(&GV))
> +    return GA->getBaseObject();
> +  return cast<GlobalObject>(&GV);
> +}
> +
>  /// Called by gold to see whether this file is one that our plugin can handle.
>  /// We'll try to open it and register all the symbols with add_symbol if
>  /// possible.
> @@ -362,8 +370,16 @@ static ld_plugin_status claim_file_hook(const ld_plugin_input_file *file,
>  
>      sym.size = 0;
>      sym.comdat_key = nullptr;
> -    if (GV && (GV->hasWeakLinkage() || GV->hasLinkOnceLinkage()))
> -      sym.comdat_key = sym.name;
> +    if (GV) {
> +      const GlobalObject *Base = getBaseObject(*GV);
> +      if (!Base)
> +        message(LDPL_FATAL, "Unable to determine comdat of alias!");
> +      const Comdat *C = Base->getComdat();
> +      if (C)
> +        sym.comdat_key = strdup(C->getName().str().c_str());
> +      else if (Base->hasWeakLinkage() || Base->hasLinkOnceLinkage())
> +        sym.comdat_key = strdup(sym.name);
> +    }
>  
>      sym.resolution = LDPR_UNKNOWN;
>    }
> @@ -378,9 +394,13 @@ static ld_plugin_status claim_file_hook(const ld_plugin_input_file *file,
>    return LDPS_OK;
>  }
>  
> -static void keepGlobalValue(GlobalValue &GV) {
> +static void keepGlobalValue(GlobalValue &GV,
> +                            std::vector<GlobalAlias *> &KeptAliases) {
>    assert(!GV.hasLocalLinkage());
>  
> +  if (auto *GA = dyn_cast<GlobalAlias>(&GV))
> +    KeptAliases.push_back(GA);
> +
>    switch (GV.getLinkage()) {
>    default:
>      break;
> @@ -415,12 +435,15 @@ static void internalize(GlobalValue &GV) {
>  static void drop(GlobalValue &GV) {
>    if (auto *F = dyn_cast<Function>(&GV)) {
>      F->deleteBody();
> +    F->setComdat(nullptr); // Should deleteBody do this?
>      return;
>    }
>  
>    if (auto *Var = dyn_cast<GlobalVariable>(&GV)) {
>      Var->setInitializer(nullptr);
> -    Var->setLinkage(GlobalValue::ExternalLinkage);
> +    Var->setLinkage(
> +        GlobalValue::ExternalLinkage); // Should setInitializer do this?
> +    Var->setComdat(nullptr); // and this?
>      return;
>    }
>  
> @@ -460,6 +483,55 @@ static const char *getResolutionName(ld_plugin_symbol_resolution R) {
>    }
>  }
>  
> +static GlobalObject *makeInternalReplacement(GlobalObject *GO) {
> +  Module *M = GO->getParent();
> +  GlobalObject *Ret;
> +  if (auto *F = dyn_cast<Function>(GO)) {
> +    auto *NewF = Function::Create(
> +        F->getFunctionType(), GlobalValue::InternalLinkage, F->getName(), M);
> +    NewF->getBasicBlockList().splice(NewF->end(), F->getBasicBlockList());
> +    Ret = NewF;
> +    F->deleteBody();
> +  } else {
> +    auto *Var = cast<GlobalVariable>(GO);
> +    Ret = new GlobalVariable(
> +        *M, Var->getType()->getElementType(), Var->isConstant(),
> +        GlobalValue::InternalLinkage, Var->getInitializer(), Var->getName(),
> +        nullptr, Var->getThreadLocalMode(), Var->getType()->getAddressSpace(),
> +        Var->isExternallyInitialized());
> +    Var->setInitializer(nullptr);
> +  }
> +  Ret->copyAttributesFrom(GO);
> +  Ret->setComdat(GO->getComdat());
> +
> +  return Ret;
> +}
> +
> +namespace {
> +class LocalValueMaterializer : public ValueMaterializer {
> +  DenseSet<GlobalValue *> &Droped;

s/Droped/Dropped/

> +
> +public:
> +  LocalValueMaterializer(DenseSet<GlobalValue *> &Droped) : Droped(Droped) {}
> +  Value *materializeValueFor(Value *V) override;
> +};
> +}
> +
> +Value *LocalValueMaterializer::materializeValueFor(Value *V) {
> +  auto *GV = dyn_cast<GlobalValue>(V);
> +  if (!GV)
> +    return nullptr;
> +  if (!Droped.count(GV))
> +    return nullptr;
> +  assert(!isa<GlobalAlias>(GV) && "Found alias point to weak alias.");
> +  return makeInternalReplacement(cast<GlobalObject>(GV));
> +}
> +
> +static Constant *mapConstanToLocalCopy(Constant *C, ValueToValueMapTy &VM,
> +                                       LocalValueMaterializer *Materializer) {

s/mapConstanToLocalCopy/mapConstantToLocalCopy/

> +  return MapValue(C, VM, RF_IgnoreMissingEntries, nullptr, Materializer);
> +}
> +
>  static std::unique_ptr<Module>
>  getModuleForFile(LLVMContext &Context, claimed_file &F, raw_fd_ostream *ApiFile,
>                   StringSet<> &Internalize, StringSet<> &Maybe) {
> @@ -492,7 +564,8 @@ getModuleForFile(LLVMContext &Context, claimed_file &F, raw_fd_ostream *ApiFile,
>    SmallPtrSet<GlobalValue *, 8> Used;
>    collectUsedGlobalVariables(*M, Used, /*CompilerUsed*/ false);
>  
> -  std::vector<GlobalValue *> Drop;
> +  DenseSet<GlobalValue *> Drop;
> +  std::vector<GlobalAlias *> KeptAliases;
>    for (ld_plugin_symbol &Sym : F.syms) {
>      ld_plugin_symbol_resolution Resolution =
>          (ld_plugin_symbol_resolution)Sym.resolution;
> @@ -516,23 +589,23 @@ getModuleForFile(LLVMContext &Context, claimed_file &F, raw_fd_ostream *ApiFile,
>        break;
>  
>      case LDPR_PREVAILING_DEF_IRONLY: {
> +      keepGlobalValue(*GV, KeptAliases);
>        if (!Used.count(GV)) {
>          // Since we use the regular lib/Linker, we cannot just internalize GV
>          // now or it will not be copied to the merged module. Instead we force
>          // it to be copied and then internalize it.
> -        keepGlobalValue(*GV);
>          Internalize.insert(Sym.name);
>        }
>        break;
>      }
>  
>      case LDPR_PREVAILING_DEF:
> -      keepGlobalValue(*GV);
> +      keepGlobalValue(*GV, KeptAliases);
>        break;
>  
>      case LDPR_PREEMPTED_REG:
>      case LDPR_PREEMPTED_IR:
> -      Drop.push_back(GV);
> +      Drop.insert(GV);
>        break;
>  
>      case LDPR_PREVAILING_DEF_IRONLY_EXP: {
> @@ -542,25 +615,37 @@ getModuleForFile(LLVMContext &Context, claimed_file &F, raw_fd_ostream *ApiFile,
>        // copy will be LDPR_PREEMPTED_IR.
>        if (GV->hasLinkOnceODRLinkage())
>          Maybe.insert(Sym.name);
> -      keepGlobalValue(*GV);
> +      keepGlobalValue(*GV, KeptAliases);
>        break;
>      }
>      }
>  
>      free(Sym.name);
> +    free(Sym.comdat_key);
>      Sym.name = nullptr;
>      Sym.comdat_key = nullptr;
>    }
>  
> -  if (!Drop.empty()) {
> +  if (!Drop.empty())
>      // This is horrible. Given how lazy loading is implemented, dropping
>      // the body while there is a materializer present doesn't work, the
>      // linker will just read the body back.
>      M->materializeAllPermanently();
> -    for (auto *GV : Drop)
> -      drop(*GV);
> +
> +  ValueToValueMapTy VM;
> +  LocalValueMaterializer Materializer(Drop);
> +  for (GlobalAlias *GA : KeptAliases) {
> +    // Gold told us to keep GA. It is possible that a GV usied in the aliasee
> +    // expression is being dropped. If that is the case, that GV must be copied.
> +    Constant *Aliasee = GA->getAliasee();
> +    Constant *Replacement = mapConstanToLocalCopy(Aliasee, VM, &Materializer);
> +    if (Aliasee != Replacement)
> +      GA->setAliasee(Replacement);

This seems funny to me.  I'm probably missing something, but why don't
you just remove the referenced values from `Drop` and change their
linkage to `Internal`?

>    }
>  
> +  for (auto *GV : Drop)
> +    drop(*GV);
> +
>    return M;
>  }
>  





More information about the llvm-commits mailing list