diff --git a/llvm/lib/IR/Constants.cpp b/llvm/lib/IR/Constants.cpp --- a/llvm/lib/IR/Constants.cpp +++ b/llvm/lib/IR/Constants.cpp @@ -1914,6 +1914,12 @@ getContext().pImpl->DSOLocalEquivalents.erase(getGlobalValue()); NewEquiv = this; setOperand(0, Func); + + if (Func->getType() != getType()) { + // It is ok to mutate the type here because this constant should always + // reflect the type of the function it's holding. + mutateType(Func->getType()); + } return nullptr; } diff --git a/llvm/lib/Transforms/Utils/ValueMapper.cpp b/llvm/lib/Transforms/Utils/ValueMapper.cpp --- a/llvm/lib/Transforms/Utils/ValueMapper.cpp +++ b/llvm/lib/Transforms/Utils/ValueMapper.cpp @@ -432,6 +432,20 @@ if (BlockAddress *BA = dyn_cast(C)) return mapBlockAddress(*BA); + if (const auto *E = dyn_cast(C)) { + auto *Val = mapValue(E->getGlobalValue()); + GlobalValue *GV = dyn_cast(Val); + if (GV) + return getVM()[E] = DSOLocalEquivalent::get(GV); + + auto *Func = cast(Val->stripPointerCastsAndAliases()); + Type *NewTy = E->getType(); + if (TypeMapper) + NewTy = TypeMapper->remapType(NewTy); + return getVM()[E] = llvm::ConstantExpr::getBitCast( + DSOLocalEquivalent::get(Func), NewTy); + } + auto mapValueOrNull = [this](Value *V) { auto Mapped = mapValue(V); assert((Mapped || (Flags & RF_NullMapMissingGlobalValues)) && diff --git a/llvm/test/Linker/DSOLocalEquivalent.ll b/llvm/test/Linker/DSOLocalEquivalent.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Linker/DSOLocalEquivalent.ll @@ -0,0 +1,34 @@ +; RUN: llvm-link %s %S/Inputs/DSOLocalEquivalent.ll -S | FileCheck %s +; RUN: llvm-link %S/Inputs/DSOLocalEquivalent.ll %s -S | FileCheck %s + +declare void @extern_func() +declare void @defined_extern_func() +declare hidden void @hidden_func() + +; CHECK: define void @call_extern_func() { +; CHECK-NEXT: call void dso_local_equivalent @extern_func() +; CHECK-NEXT: ret void +; CHECK-NEXT: } +define void @call_extern_func() { + + call void dso_local_equivalent @extern_func() + ret void +} + +; CHECK: define void @call_defined_extern_func() { +; CHECK-NEXT: call void dso_local_equivalent @defined_extern_func() +; CHECK-NEXT: ret void +; CHECK-NEXT: } +define void @call_defined_extern_func() { + call void dso_local_equivalent @defined_extern_func() + ret void +} + +; CHECK: define void @call_hidden_func() { +; CHECK-NEXT: call void dso_local_equivalent @hidden_func() +; CHECK-NEXT: ret void +; CHECK-NEXT: } +define void @call_hidden_func() { + call void dso_local_equivalent @hidden_func() + ret void +} diff --git a/llvm/test/Linker/Inputs/DSOLocalEquivalent.ll b/llvm/test/Linker/Inputs/DSOLocalEquivalent.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Linker/Inputs/DSOLocalEquivalent.ll @@ -0,0 +1,7 @@ +define void @defined_extern_func() { + ret void +} + +define hidden void @hidden_func() { + ret void +} diff --git a/llvm/test/ThinLTO/X86/DSOLocalEquivalent.ll b/llvm/test/ThinLTO/X86/DSOLocalEquivalent.ll new file mode 100644 --- /dev/null +++ b/llvm/test/ThinLTO/X86/DSOLocalEquivalent.ll @@ -0,0 +1,17 @@ +; RUN: llvm-as %s -o %t1.bc +; RUN: llvm-lto2 run %t1.bc -o %t2.o -r=%t1.bc,caller,plx -r=%t1.bc,extern_func,plx -save-temps +; RUN: llvm-dis %t2.o.0.5.precodegen.bc -o - | FileCheck %s + +target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-fuchsia" + +declare void @extern_func() + +; CHECK: define {{.*}} void @caller() {{.*}}{ +; CHECK-NEXT: tail call void dso_local_equivalent @extern_func() +; CHECK-NEXT: ret void +; CHECK-NEXT: } +define void @caller() { + call void dso_local_equivalent @extern_func() + ret void +}