--- /dev/null
+; RUN: llvm-as %s -o %t.o
+; RUN: llvm-as %p/Inputs/comdat.ll -o %t2.o
+; RUN: ld -shared -o %t3.o -plugin %llvmshlibdir/LLVMgold.so %t.o %t2.o \
+; RUN: -plugin-opt=emit-llvm
+; RUN: llvm-dis %t3.o -o - | FileCheck %s
+
+$c1 = comdat any
+
+@v1 = weak_odr global i32 42, comdat $c1
+define weak_odr i32 @f1() comdat $c1 {
+bb10:
+ br label %bb11
+bb11:
+ ret i32 42
+}
+
+@r11 = global i32* @v1
+@r12 = global i32 ()* @f1
+
+@a11 = alias i32* @v1
+@a12 = alias bitcast (i32* @v1 to i16*)
+
+@a13 = alias i32 ()* @f1
+@a14 = alias bitcast (i32 ()* @f1 to i16*)
+@a15 = alias i16* @a14
+
+; CHECK: $c1 = comdat any
+; CHECK: $c2 = comdat any
+
+; CHECK: @v1 = weak_odr global i32 42, comdat $c1
+
+; CHECK: @r11 = global i32* @v1{{$}}
+; CHECK: @r12 = global i32 ()* @f1{{$}}
+
+; CHECK: @r21 = global i32* @v1{{$}}
+; CHECK: @r22 = global i32 ()* @f1{{$}}
+
+; CHECK: @v11 = internal global i32 41, comdat $c2
+
+; CHECK: @a11 = alias i32* @v1{{$}}
+; CHECK: @a12 = alias bitcast (i32* @v1 to i16*)
+
+; CHECK: @a13 = alias i32 ()* @f1{{$}}
+; CHECK: @a14 = alias bitcast (i32 ()* @f1 to i16*)
+
+; CHECK: @a21 = alias i32* @v11{{$}}
+; CHECK: @a22 = alias bitcast (i32* @v11 to i16*)
+
+; CHECK: @a23 = alias i32 ()* @f12{{$}}
+; CHECK: @a24 = alias bitcast (i32 ()* @f12 to i16*)
+
+; CHECK: define weak_odr i32 @f1() comdat $c1 {
+; CHECK-NEXT: bb10:
+; CHECK-NEXT: br label %bb11{{$}}
+; CHECK: bb11:
+; CHECK-NEXT: ret i32 42
+; CHECK-NEXT: }
+
+; CHECK: define internal i32 @f12() comdat $c2 {
+; CHECK-NEXT: bb20:
+; CHECK-NEXT: br label %bb21
+; CHECK: bb21:
+; CHECK-NEXT: ret i32 41
+; CHECK-NEXT: }
//===----------------------------------------------------------------------===//
#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"
#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>
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.
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;
}
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;
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;
}
}
}
+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 *> &Dropped;
+
+public:
+ LocalValueMaterializer(DenseSet<GlobalValue *> &Dropped) : Dropped(Dropped) {}
+ Value *materializeValueFor(Value *V) override;
+};
+}
+
+Value *LocalValueMaterializer::materializeValueFor(Value *V) {
+ auto *GV = dyn_cast<GlobalValue>(V);
+ if (!GV)
+ return nullptr;
+ if (!Dropped.count(GV))
+ return nullptr;
+ assert(!isa<GlobalAlias>(GV) && "Found alias point to weak alias.");
+ return makeInternalReplacement(cast<GlobalObject>(GV));
+}
+
+static Constant *mapConstantToLocalCopy(Constant *C, ValueToValueMapTy &VM,
+ LocalValueMaterializer *Materializer) {
+ 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) {
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;
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: {
// 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 = mapConstantToLocalCopy(Aliasee, VM, &Materializer);
+ if (Aliasee != Replacement)
+ GA->setAliasee(Replacement);
}
+ for (auto *GV : Drop)
+ drop(*GV);
+
return M;
}