mirror of
https://github.com/ldc-developers/ldc.git
synced 2025-05-02 16:11:08 +03:00
Emulate @weak functions on Windows and don't emit COMDATs for ELF anymore
Emulation for @weak global variables is still left out but should be
analogous.
The test adaptations are mostly a revert of 3893840f
. The testcase has
shown that @weak hasn't worked properly for ELF (linker apparently
prefers the version in the 1st object file, independent of whether it's
weak or not), because the functions are emitted in COMDATs.
clang emits COMDATs for templates and inline functions only, not for
regular functions.
This commit is contained in:
parent
24af7a32e7
commit
967947eb84
12 changed files with 104 additions and 30 deletions
|
@ -54,6 +54,7 @@
|
||||||
#include "llvm/IR/CFG.h"
|
#include "llvm/IR/CFG.h"
|
||||||
#include "llvm/Target/TargetMachine.h"
|
#include "llvm/Target/TargetMachine.h"
|
||||||
#include "llvm/Target/TargetOptions.h"
|
#include "llvm/Target/TargetOptions.h"
|
||||||
|
#include "llvm/Transforms/Utils/Cloning.h"
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
bool isAnyMainFunction(FuncDeclaration *fd) {
|
bool isAnyMainFunction(FuncDeclaration *fd) {
|
||||||
|
@ -892,6 +893,62 @@ bool eraseDummyAfterReturnBB(llvm::BasicBlock *bb) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* LLVM doesn't really support weak linkage for MSVC targets, it just prevents
|
||||||
|
* inlining. We can emulate it though, by conceptually renaming the defined
|
||||||
|
* function, only declaring the original function and embedding a linker
|
||||||
|
* directive in the object file, instructing the linker to fall back to the weak
|
||||||
|
* implementation if there's no strong definition.
|
||||||
|
* The object file still needs to be pulled in by the linker for the directive
|
||||||
|
* to be found.
|
||||||
|
*/
|
||||||
|
void emulateWeakAnyLinkageForMSVC(LLFunction *func, LINK linkage) {
|
||||||
|
const bool isWin32 = !global.params.is64bit;
|
||||||
|
|
||||||
|
std::string mangleBuffer;
|
||||||
|
llvm::StringRef finalMangle = func->getName();
|
||||||
|
if (finalMangle[0] == '\1') {
|
||||||
|
finalMangle = finalMangle.substr(1);
|
||||||
|
} else if (isWin32) {
|
||||||
|
// implicit underscore prefix for Win32
|
||||||
|
mangleBuffer = ("_" + finalMangle).str();
|
||||||
|
finalMangle = mangleBuffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string finalWeakMangle = finalMangle;
|
||||||
|
if (linkage == LINKcpp) {
|
||||||
|
assert(finalMangle.startswith("?"));
|
||||||
|
// prepend `__weak_` to first identifier
|
||||||
|
size_t offset = finalMangle.startswith("??$") ? 3 : 1;
|
||||||
|
finalWeakMangle.insert(offset, "__weak_");
|
||||||
|
} else if (linkage == LINKd) {
|
||||||
|
const size_t offset = isWin32 ? 1 : 0;
|
||||||
|
assert(finalMangle.substr(offset).startswith("_D"));
|
||||||
|
// prepend a `__weak` package
|
||||||
|
finalWeakMangle.insert(offset + 2, "6__weak");
|
||||||
|
} else {
|
||||||
|
// prepend `__weak_`
|
||||||
|
const size_t offset = isWin32 && finalMangle.startswith("_") ? 1 : 0;
|
||||||
|
finalWeakMangle.insert(offset, "__weak_");
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string linkerOption =
|
||||||
|
("/ALTERNATENAME:" + finalMangle + "=" + finalWeakMangle).str();
|
||||||
|
gIR->addLinkerOption(llvm::StringRef(linkerOption));
|
||||||
|
|
||||||
|
// work around LLVM assertion when cloning a function's debuginfos
|
||||||
|
func->setSubprogram(nullptr);
|
||||||
|
|
||||||
|
llvm::ValueToValueMapTy dummy;
|
||||||
|
auto clone = llvm::CloneFunction(func, dummy);
|
||||||
|
clone->setName("\1" + finalWeakMangle);
|
||||||
|
setLinkage({LLGlobalValue::ExternalLinkage, func->hasComdat()}, clone);
|
||||||
|
|
||||||
|
// reduce the original definition to a declaration
|
||||||
|
setLinkage({LLGlobalValue::ExternalLinkage, false}, func);
|
||||||
|
func->deleteBody();
|
||||||
|
}
|
||||||
|
|
||||||
} // anonymous namespace
|
} // anonymous namespace
|
||||||
|
|
||||||
void DtoDefineFunction(FuncDeclaration *fd, bool linkageAvailableExternally) {
|
void DtoDefineFunction(FuncDeclaration *fd, bool linkageAvailableExternally) {
|
||||||
|
@ -1274,6 +1331,11 @@ void DtoDefineFunction(FuncDeclaration *fd, bool linkageAvailableExternally) {
|
||||||
auto fn = gIR->module.getFunction(fd->mangleString);
|
auto fn = gIR->module.getFunction(fd->mangleString);
|
||||||
gIR->dcomputetarget->addKernelMetadata(fd, fn);
|
gIR->dcomputetarget->addKernelMetadata(fd, fn);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (func->getLinkage() == LLGlobalValue::WeakAnyLinkage &&
|
||||||
|
global.params.targetTriple->isWindowsMSVCEnvironment()) {
|
||||||
|
emulateWeakAnyLinkageForMSVC(func, fd->linkage);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
|
@ -310,6 +310,6 @@ llvm::GlobalVariable *genModuleInfo(Module *m) {
|
||||||
// Create a global symbol with the above initialiser.
|
// Create a global symbol with the above initialiser.
|
||||||
LLGlobalVariable *moduleInfoSym = getIrModule(m)->moduleInfoSymbol();
|
LLGlobalVariable *moduleInfoSym = getIrModule(m)->moduleInfoSymbol();
|
||||||
b.finalize(moduleInfoSym);
|
b.finalize(moduleInfoSym);
|
||||||
setLinkage({LLGlobalValue::ExternalLinkage, supportsCOMDAT()}, moduleInfoSym);
|
setLinkage({LLGlobalValue::ExternalLinkage, needsCOMDAT()}, moduleInfoSym);
|
||||||
return moduleInfoSym;
|
return moduleInfoSym;
|
||||||
}
|
}
|
||||||
|
|
|
@ -773,7 +773,7 @@ void CodeGenPGO::setFuncName(llvm::StringRef Name,
|
||||||
// If Linkage is private, and the function is in a comdat "any" group, set
|
// If Linkage is private, and the function is in a comdat "any" group, set
|
||||||
// the linkage to internal to prevent LLVM from erroring with "comdat global
|
// the linkage to internal to prevent LLVM from erroring with "comdat global
|
||||||
// value has private linkage".
|
// value has private linkage".
|
||||||
if (supportsCOMDAT() &&
|
if (needsCOMDAT() &&
|
||||||
FuncNameVar->getLinkage() == llvm::GlobalValue::PrivateLinkage) {
|
FuncNameVar->getLinkage() == llvm::GlobalValue::PrivateLinkage) {
|
||||||
FuncNameVar->setLinkage(llvm::GlobalValue::InternalLinkage);
|
FuncNameVar->setLinkage(llvm::GlobalValue::InternalLinkage);
|
||||||
}
|
}
|
||||||
|
|
|
@ -85,7 +85,7 @@ void RTTIBuilder::push_void_array(llvm::Constant *CI, Type *valtype,
|
||||||
mangleToBuffer(mangle_sym, &initname);
|
mangleToBuffer(mangle_sym, &initname);
|
||||||
initname.writestring(".rtti.voidarr.data");
|
initname.writestring(".rtti.voidarr.data");
|
||||||
|
|
||||||
const LinkageWithCOMDAT lwc(TYPEINFO_LINKAGE_TYPE, supportsCOMDAT());
|
const LinkageWithCOMDAT lwc(TYPEINFO_LINKAGE_TYPE, needsCOMDAT());
|
||||||
|
|
||||||
auto G = new LLGlobalVariable(gIR->module, CI->getType(), true, lwc.first, CI,
|
auto G = new LLGlobalVariable(gIR->module, CI->getType(), true, lwc.first, CI,
|
||||||
initname.peekChars());
|
initname.peekChars());
|
||||||
|
@ -111,7 +111,7 @@ void RTTIBuilder::push_array(llvm::Constant *CI, uint64_t dim, Type *valtype,
|
||||||
initname.writestring(tmpStr.c_str());
|
initname.writestring(tmpStr.c_str());
|
||||||
initname.writestring(".data");
|
initname.writestring(".data");
|
||||||
|
|
||||||
const LinkageWithCOMDAT lwc(TYPEINFO_LINKAGE_TYPE, supportsCOMDAT());
|
const LinkageWithCOMDAT lwc(TYPEINFO_LINKAGE_TYPE, needsCOMDAT());
|
||||||
|
|
||||||
auto G = new LLGlobalVariable(gIR->module, CI->getType(), true, lwc.first, CI,
|
auto G = new LLGlobalVariable(gIR->module, CI->getType(), true, lwc.first, CI,
|
||||||
initname.peekChars());
|
initname.peekChars());
|
||||||
|
|
|
@ -223,25 +223,25 @@ LinkageWithCOMDAT DtoLinkage(Dsymbol *sym) {
|
||||||
linkage = LLGlobalValue::WeakAnyLinkage;
|
linkage = LLGlobalValue::WeakAnyLinkage;
|
||||||
}
|
}
|
||||||
|
|
||||||
return {linkage, supportsCOMDAT()};
|
return {linkage, needsCOMDAT()};
|
||||||
}
|
}
|
||||||
|
|
||||||
bool supportsCOMDAT() {
|
bool needsCOMDAT() {
|
||||||
const auto &triple = *global.params.targetTriple;
|
/* For MSVC targets (and probably MinGW too), linkonce[_odr] and weak[_odr]
|
||||||
return !(triple.isOSBinFormatMachO() ||
|
* linkages don't work and need to be emulated via COMDATs to prevent multiple
|
||||||
#if LDC_LLVM_VER >= 500
|
* definition errors when linking.
|
||||||
triple.isOSBinFormatWasm()
|
* Simply emit all functions in COMDATs, not just templates, for aggressive
|
||||||
#else
|
* linker stripping (/OPT:REF and /OPT:ICF with MS linker/LLD), analogous to
|
||||||
triple.getArch() == llvm::Triple::wasm32 ||
|
* using /Gy with the MS compiler.
|
||||||
triple.getArch() == llvm::Triple::wasm64
|
* https://docs.microsoft.com/en-us/cpp/build/reference/opt-optimizations?view=vs-2019
|
||||||
#endif
|
*/
|
||||||
);
|
return global.params.targetTriple->isOSBinFormatCOFF();
|
||||||
}
|
}
|
||||||
|
|
||||||
void setLinkage(LinkageWithCOMDAT lwc, llvm::GlobalObject *obj) {
|
void setLinkage(LinkageWithCOMDAT lwc, llvm::GlobalObject *obj) {
|
||||||
obj->setLinkage(lwc.first);
|
obj->setLinkage(lwc.first);
|
||||||
if (lwc.second)
|
obj->setComdat(lwc.second ? gIR->module.getOrInsertComdat(obj->getName())
|
||||||
obj->setComdat(gIR->module.getOrInsertComdat(obj->getName()));
|
: nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
void setLinkageAndVisibility(Dsymbol *sym, llvm::GlobalObject *obj) {
|
void setLinkageAndVisibility(Dsymbol *sym, llvm::GlobalObject *obj) {
|
||||||
|
|
|
@ -69,7 +69,7 @@ LLValue *DtoDelegateEquals(TOK op, LLValue *lhs, LLValue *rhs);
|
||||||
typedef std::pair<llvm::GlobalValue::LinkageTypes, bool> LinkageWithCOMDAT;
|
typedef std::pair<llvm::GlobalValue::LinkageTypes, bool> LinkageWithCOMDAT;
|
||||||
LinkageWithCOMDAT DtoLinkage(Dsymbol *sym);
|
LinkageWithCOMDAT DtoLinkage(Dsymbol *sym);
|
||||||
|
|
||||||
bool supportsCOMDAT();
|
bool needsCOMDAT();
|
||||||
void setLinkage(LinkageWithCOMDAT lwc, llvm::GlobalObject *obj);
|
void setLinkage(LinkageWithCOMDAT lwc, llvm::GlobalObject *obj);
|
||||||
// Sets the linkage of the specified IR global and possibly hides it, both based
|
// Sets the linkage of the specified IR global and possibly hides it, both based
|
||||||
// on the specified D symbol.
|
// on the specified D symbol.
|
||||||
|
|
|
@ -666,7 +666,7 @@ void TypeInfoDeclaration_codegen(TypeInfoDeclaration *decl, IRState *p) {
|
||||||
LLVMDefineVisitor v(gvar);
|
LLVMDefineVisitor v(gvar);
|
||||||
decl->accept(&v);
|
decl->accept(&v);
|
||||||
|
|
||||||
setLinkage({TYPEINFO_LINKAGE_TYPE, supportsCOMDAT()}, gvar);
|
setLinkage({TYPEINFO_LINKAGE_TYPE, needsCOMDAT()}, gvar);
|
||||||
if (auto forStructType = forType->isTypeStruct())
|
if (auto forStructType = forType->isTypeStruct())
|
||||||
setVisibility(forStructType->sym, gvar);
|
setVisibility(forStructType->sym, gvar);
|
||||||
}
|
}
|
||||||
|
|
|
@ -394,7 +394,7 @@ void IrAggr::defineInterfaceVtbl(BaseClass *b, bool new_instance,
|
||||||
llvm::Function *thunk = gIR->module.getFunction(thunkIRMangle);
|
llvm::Function *thunk = gIR->module.getFunction(thunkIRMangle);
|
||||||
if (!thunk) {
|
if (!thunk) {
|
||||||
const LinkageWithCOMDAT lwc(LLGlobalValue::LinkOnceODRLinkage,
|
const LinkageWithCOMDAT lwc(LLGlobalValue::LinkOnceODRLinkage,
|
||||||
supportsCOMDAT());
|
needsCOMDAT());
|
||||||
const auto callee = irFunc->getLLVMCallee();
|
const auto callee = irFunc->getLLVMCallee();
|
||||||
thunk = LLFunction::Create(
|
thunk = LLFunction::Create(
|
||||||
isaFunction(callee->getType()->getContainedType(0)), lwc.first,
|
isaFunction(callee->getType()->getContainedType(0)), lwc.first,
|
||||||
|
|
|
@ -2,17 +2,20 @@
|
||||||
|
|
||||||
// REQUIRES: PGO_RT
|
// REQUIRES: PGO_RT
|
||||||
|
|
||||||
|
// with LLVM 4.0, @optStrategy apparently doesn't suffice to prevent eliding the hot() call
|
||||||
|
// XFAIL: llvm400
|
||||||
|
|
||||||
// RUN: %ldc -O3 -fprofile-generate=%t.profraw -run %s \
|
// RUN: %ldc -O3 -fprofile-generate=%t.profraw -run %s \
|
||||||
// RUN: && %profdata merge %t.profraw -o %t.profdata \
|
// RUN: && %profdata merge %t.profraw -o %t.profdata \
|
||||||
// RUN: && %ldc -O3 -c -output-ll -of=%t.use.ll -fprofile-use=%t.profdata %s \
|
// RUN: && %ldc -O3 -c -output-ll -of=%t.use.ll -fprofile-use=%t.profdata %s \
|
||||||
// RUN: && FileCheck %s -check-prefix=PROFUSE < %t.use.ll
|
// RUN: && FileCheck %s -check-prefix=PROFUSE < %t.use.ll
|
||||||
|
|
||||||
import ldc.attributes : weak;
|
import ldc.attributes;
|
||||||
|
|
||||||
extern (C)
|
extern (C)
|
||||||
{ // simplify name mangling for simpler string matching
|
{ // simplify name mangling for simpler string matching
|
||||||
|
|
||||||
@weak // disable reasoning about this function
|
@optStrategy("none")
|
||||||
void hot()
|
void hot()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,20 +1,23 @@
|
||||||
// Test linking+running a program with @weak function
|
// Test linking+running a program with @weak functions
|
||||||
|
|
||||||
// RUN: %ldc -O3 %S/inputs/attr_weak_input.d -c -of=%t-dir/attr_weak_input%obj
|
// RUN: %ldc -O3 %S/inputs/attr_weak_input.d -c -of=%t-dir/attr_weak_input%obj
|
||||||
// RUN: %ldc -O3 %t-dir/attr_weak_input%obj %s -of=%t%exe
|
// RUN: %ldc -O3 %s %t-dir/attr_weak_input%obj -of=%t%exe
|
||||||
// RUN: %t%exe
|
// RUN: %t%exe
|
||||||
|
|
||||||
|
|
||||||
import ldc.attributes;
|
import ldc.attributes;
|
||||||
|
|
||||||
// Should be overridden by attr_weak_input.d (but only because its object
|
// should take precedence over and not conflict with weak attr_weak_input.return_two
|
||||||
// file is specified before this one for the linker).
|
extern(C) int return_two() {
|
||||||
// The @weak attribute prevents the optimizer from making any assumptions
|
return 123;
|
||||||
// though, so the call below is not inlined.
|
}
|
||||||
|
|
||||||
|
// should be overridden by strong attr_weak_input.return_seven
|
||||||
extern(C) @weak int return_seven() {
|
extern(C) @weak int return_seven() {
|
||||||
return 1;
|
return 456;
|
||||||
}
|
}
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
|
assert( return_two() == 123 );
|
||||||
assert( return_seven() == 7 );
|
assert( return_seven() == 7 );
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,7 +20,7 @@ import ldc.attributes;
|
||||||
// CHECK-DAG: @{{.*}}myWeakGlobali{{\"?}} = weak
|
// CHECK-DAG: @{{.*}}myWeakGlobali{{\"?}} = weak
|
||||||
@(ldc.attributes.weak) int myWeakGlobal;
|
@(ldc.attributes.weak) int myWeakGlobal;
|
||||||
|
|
||||||
// CHECK-DAG: define{{.*}} weak {{.*}}void @{{.*}}weakFunc
|
// CHECK-DAG: define{{.*}} {{(weak .*void @.*_D)|(void @.*_D6__weak)}}10attributes8weakFuncFZv
|
||||||
@weak void weakFunc() {}
|
@weak void weakFunc() {}
|
||||||
|
|
||||||
//---------------------------------------------------------------------
|
//---------------------------------------------------------------------
|
||||||
|
|
|
@ -1,3 +1,9 @@
|
||||||
|
import ldc.attributes;
|
||||||
|
|
||||||
|
extern(C) @weak int return_two() {
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
extern(C) int return_seven() {
|
extern(C) int return_seven() {
|
||||||
return 7;
|
return 7;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue