diff --git a/gen/functions.cpp b/gen/functions.cpp index a7210dd7c1a..10398a10ff4 100644 --- a/gen/functions.cpp +++ b/gen/functions.cpp @@ -54,6 +54,7 @@ #include "llvm/IR/CFG.h" #include "llvm/Target/TargetMachine.h" #include "llvm/Target/TargetOptions.h" +#include "llvm/Transforms/Utils/Cloning.h" #include bool isAnyMainFunction(FuncDeclaration *fd) { @@ -892,6 +893,62 @@ bool eraseDummyAfterReturnBB(llvm::BasicBlock *bb) { 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 void DtoDefineFunction(FuncDeclaration *fd, bool linkageAvailableExternally) { @@ -1274,6 +1331,11 @@ void DtoDefineFunction(FuncDeclaration *fd, bool linkageAvailableExternally) { auto fn = gIR->module.getFunction(fd->mangleString); gIR->dcomputetarget->addKernelMetadata(fd, fn); } + + if (func->getLinkage() == LLGlobalValue::WeakAnyLinkage && + global.params.targetTriple->isWindowsMSVCEnvironment()) { + emulateWeakAnyLinkageForMSVC(func, fd->linkage); + } } //////////////////////////////////////////////////////////////////////////////// diff --git a/gen/moduleinfo.cpp b/gen/moduleinfo.cpp index af00b2d37b8..62b23cf0243 100644 --- a/gen/moduleinfo.cpp +++ b/gen/moduleinfo.cpp @@ -310,6 +310,6 @@ llvm::GlobalVariable *genModuleInfo(Module *m) { // Create a global symbol with the above initialiser. LLGlobalVariable *moduleInfoSym = getIrModule(m)->moduleInfoSymbol(); b.finalize(moduleInfoSym); - setLinkage({LLGlobalValue::ExternalLinkage, supportsCOMDAT()}, moduleInfoSym); + setLinkage({LLGlobalValue::ExternalLinkage, needsCOMDAT()}, moduleInfoSym); return moduleInfoSym; } diff --git a/gen/pgo_ASTbased.cpp b/gen/pgo_ASTbased.cpp index c80165b071f..821bb074e36 100644 --- a/gen/pgo_ASTbased.cpp +++ b/gen/pgo_ASTbased.cpp @@ -773,7 +773,7 @@ void CodeGenPGO::setFuncName(llvm::StringRef Name, // 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 // value has private linkage". - if (supportsCOMDAT() && + if (needsCOMDAT() && FuncNameVar->getLinkage() == llvm::GlobalValue::PrivateLinkage) { FuncNameVar->setLinkage(llvm::GlobalValue::InternalLinkage); } diff --git a/gen/rttibuilder.cpp b/gen/rttibuilder.cpp index e07febb125a..037d75efbf6 100644 --- a/gen/rttibuilder.cpp +++ b/gen/rttibuilder.cpp @@ -85,7 +85,7 @@ void RTTIBuilder::push_void_array(llvm::Constant *CI, Type *valtype, mangleToBuffer(mangle_sym, &initname); 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, 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(".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, initname.peekChars()); diff --git a/gen/tollvm.cpp b/gen/tollvm.cpp index ffcf6dacafd..2d86c556961 100644 --- a/gen/tollvm.cpp +++ b/gen/tollvm.cpp @@ -223,25 +223,25 @@ LinkageWithCOMDAT DtoLinkage(Dsymbol *sym) { linkage = LLGlobalValue::WeakAnyLinkage; } - return {linkage, supportsCOMDAT()}; + return {linkage, needsCOMDAT()}; } -bool supportsCOMDAT() { - const auto &triple = *global.params.targetTriple; - return !(triple.isOSBinFormatMachO() || -#if LDC_LLVM_VER >= 500 - triple.isOSBinFormatWasm() -#else - triple.getArch() == llvm::Triple::wasm32 || - triple.getArch() == llvm::Triple::wasm64 -#endif - ); +bool needsCOMDAT() { + /* For MSVC targets (and probably MinGW too), linkonce[_odr] and weak[_odr] + * linkages don't work and need to be emulated via COMDATs to prevent multiple + * definition errors when linking. + * Simply emit all functions in COMDATs, not just templates, for aggressive + * linker stripping (/OPT:REF and /OPT:ICF with MS linker/LLD), analogous to + * using /Gy with the MS compiler. + * https://docs.microsoft.com/en-us/cpp/build/reference/opt-optimizations?view=vs-2019 + */ + return global.params.targetTriple->isOSBinFormatCOFF(); } void setLinkage(LinkageWithCOMDAT lwc, llvm::GlobalObject *obj) { obj->setLinkage(lwc.first); - if (lwc.second) - obj->setComdat(gIR->module.getOrInsertComdat(obj->getName())); + obj->setComdat(lwc.second ? gIR->module.getOrInsertComdat(obj->getName()) + : nullptr); } void setLinkageAndVisibility(Dsymbol *sym, llvm::GlobalObject *obj) { diff --git a/gen/tollvm.h b/gen/tollvm.h index 3e797146823..65a863feaaa 100644 --- a/gen/tollvm.h +++ b/gen/tollvm.h @@ -69,7 +69,7 @@ LLValue *DtoDelegateEquals(TOK op, LLValue *lhs, LLValue *rhs); typedef std::pair LinkageWithCOMDAT; LinkageWithCOMDAT DtoLinkage(Dsymbol *sym); -bool supportsCOMDAT(); +bool needsCOMDAT(); void setLinkage(LinkageWithCOMDAT lwc, llvm::GlobalObject *obj); // Sets the linkage of the specified IR global and possibly hides it, both based // on the specified D symbol. diff --git a/gen/typinf.cpp b/gen/typinf.cpp index 9c0a78afba0..dddd330b31b 100644 --- a/gen/typinf.cpp +++ b/gen/typinf.cpp @@ -666,7 +666,7 @@ void TypeInfoDeclaration_codegen(TypeInfoDeclaration *decl, IRState *p) { LLVMDefineVisitor v(gvar); decl->accept(&v); - setLinkage({TYPEINFO_LINKAGE_TYPE, supportsCOMDAT()}, gvar); + setLinkage({TYPEINFO_LINKAGE_TYPE, needsCOMDAT()}, gvar); if (auto forStructType = forType->isTypeStruct()) setVisibility(forStructType->sym, gvar); } diff --git a/ir/irclass.cpp b/ir/irclass.cpp index 5c66f903540..704fa6d8d64 100644 --- a/ir/irclass.cpp +++ b/ir/irclass.cpp @@ -394,7 +394,7 @@ void IrAggr::defineInterfaceVtbl(BaseClass *b, bool new_instance, llvm::Function *thunk = gIR->module.getFunction(thunkIRMangle); if (!thunk) { const LinkageWithCOMDAT lwc(LLGlobalValue::LinkOnceODRLinkage, - supportsCOMDAT()); + needsCOMDAT()); const auto callee = irFunc->getLLVMCallee(); thunk = LLFunction::Create( isaFunction(callee->getType()->getContainedType(0)), lwc.first, diff --git a/runtime/druntime b/runtime/druntime index 18e2d32dd1e..f317ad7898a 160000 --- a/runtime/druntime +++ b/runtime/druntime @@ -1 +1 @@ -Subproject commit 18e2d32dd1e59c48810e02ed993d2c1e77e19798 +Subproject commit f317ad7898aa928b98fcf906d5880b0d3e4d913c diff --git a/tests/PGO/irbased_indirect_calls.d b/tests/PGO/irbased_indirect_calls.d index e61a70f00bd..9649a430d2e 100644 --- a/tests/PGO/irbased_indirect_calls.d +++ b/tests/PGO/irbased_indirect_calls.d @@ -2,17 +2,20 @@ // 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: && %profdata merge %t.profraw -o %t.profdata \ // RUN: && %ldc -O3 -c -output-ll -of=%t.use.ll -fprofile-use=%t.profdata %s \ // RUN: && FileCheck %s -check-prefix=PROFUSE < %t.use.ll -import ldc.attributes : weak; +import ldc.attributes; extern (C) { // simplify name mangling for simpler string matching - @weak // disable reasoning about this function + @optStrategy("none") void hot() { } diff --git a/tests/codegen/attr_weak.d b/tests/codegen/attr_weak.d index af34c0aa9de..c2abc9418c4 100644 --- a/tests/codegen/attr_weak.d +++ b/tests/codegen/attr_weak.d @@ -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 %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 import ldc.attributes; -// Should be overridden by attr_weak_input.d (but only because its object -// file is specified before this one for the linker). -// The @weak attribute prevents the optimizer from making any assumptions -// though, so the call below is not inlined. +// should take precedence over and not conflict with weak attr_weak_input.return_two +extern(C) int return_two() { + return 123; +} + +// should be overridden by strong attr_weak_input.return_seven extern(C) @weak int return_seven() { - return 1; + return 456; } void main() { + assert( return_two() == 123 ); assert( return_seven() == 7 ); } diff --git a/tests/codegen/attributes.d b/tests/codegen/attributes.d index 0e1f2157b50..321d0debe87 100644 --- a/tests/codegen/attributes.d +++ b/tests/codegen/attributes.d @@ -20,7 +20,7 @@ import ldc.attributes; // CHECK-DAG: @{{.*}}myWeakGlobali{{\"?}} = weak @(ldc.attributes.weak) int myWeakGlobal; -// CHECK-DAG: define{{.*}} weak {{.*}}void @{{.*}}weakFunc +// CHECK-DAG: define{{.*}} {{(weak .*void @.*_D)|(void @.*_D6__weak)}}10attributes8weakFuncFZv @weak void weakFunc() {} //--------------------------------------------------------------------- diff --git a/tests/codegen/inputs/attr_weak_input.d b/tests/codegen/inputs/attr_weak_input.d index 5509aececf5..9b530f192f6 100644 --- a/tests/codegen/inputs/attr_weak_input.d +++ b/tests/codegen/inputs/attr_weak_input.d @@ -1,3 +1,9 @@ +import ldc.attributes; + +extern(C) @weak int return_two() { + return 2; +} + extern(C) int return_seven() { return 7; }