Skip to content

Commit 3fa8a2e

Browse files
committed
Fix #3916 - undefined symbols with -dllimport=all on Windows
Instantiated data symbols were previously never dllimported with `-dllimport=all`. So if the parent template instance wasn't codegen'd into the binary directly, it remained undefined. For `-dllimport=defaultLibsOnly`, the 'solution' to this problem was to define-on-declare data symbols instantiated from druntime/ Phobos templates, making sure each binary defines all such symbols it references. In both cases, switch to an approach where we dllimport all instantiated data symbols (or druntime/Phobos symbols only), and dllexport them whenever defining them (so that other object files or binaries can import them). This may lead to more 'importing locally defined symbol' linker warnings, but may also lead to less duplicates and possibly 'proper' sharing of instantiated globals across the whole process. This is superfluous and skipped with `-linkonce-templates`, as that mode defines all referenced instantiated symbols in each binary anyway, and so has already been a workaround.
1 parent 4e06179 commit 3fa8a2e

File tree

6 files changed

+47
-37
lines changed

6 files changed

+47
-37
lines changed

gen/llvmhelpers.cpp

+25-16
Original file line numberDiff line numberDiff line change
@@ -1607,7 +1607,7 @@ DValue *DtoSymbolAddress(const Loc &loc, Type *type, Declaration *decl) {
16071607
if (SymbolDeclaration *sdecl = decl->isSymbolDeclaration()) {
16081608
// this is the static initialiser (init symbol) for aggregates
16091609
AggregateDeclaration *ad = sdecl->dsym;
1610-
IF_LOG Logger::print("Sym: ad=%s\n", ad->toChars());
1610+
IF_LOG Logger::print("init symbol of %s\n", ad->toChars());
16111611
DtoResolveDsymbol(ad);
16121612
auto sd = ad->isStructDeclaration();
16131613

@@ -1733,24 +1733,33 @@ static bool isDefaultLibSymbol(Dsymbol *sym) {
17331733
(md->packages.length > 1 && md->packages.ptr[1] == Id::io)));
17341734
}
17351735

1736-
bool defineOnDeclare(Dsymbol* sym, bool isFunction) {
1737-
if (global.params.linkonceTemplates)
1738-
return sym->isInstantiated();
1739-
1740-
// With -dllimport=defaultLibsOnly, an instantiated data symbol from a
1741-
// druntime/Phobos template may be assigned to an arbitrary binary (and culled
1742-
// from others via `needsCodegen()`). Define it in each referencing CU and
1743-
// never dllimport.
1744-
return !isFunction && global.params.dllimport == DLLImport::defaultLibsOnly &&
1745-
sym->isInstantiated() && isDefaultLibSymbol(sym);
1736+
bool defineOnDeclare(Dsymbol* sym, bool) {
1737+
return global.params.linkonceTemplates && sym->isInstantiated();
17461738
}
17471739

17481740
bool dllimportDataSymbol(Dsymbol *sym) {
1749-
return sym->isExport() || global.params.dllimport == DLLImport::all ||
1750-
(global.params.dllimport == DLLImport::defaultLibsOnly &&
1751-
// exclude instantiated symbols from druntime/Phobos templates (see
1752-
// `defineOnDeclare()`)
1753-
!sym->isInstantiated() && isDefaultLibSymbol(sym));
1741+
if (!global.params.targetTriple->isOSWindows())
1742+
return false;
1743+
1744+
if (sym->isExport() || global.params.dllimport == DLLImport::all ||
1745+
(global.params.dllimport == DLLImport::defaultLibsOnly &&
1746+
isDefaultLibSymbol(sym))) {
1747+
// Okay, this symbol is a candidate. Use dllimport unless we have a
1748+
// guaranteed-codegen'd definition in a root module.
1749+
if (auto mod = sym->isModule()) {
1750+
return !mod->isRoot(); // non-root ModuleInfo symbol
1751+
} else if (sym->inNonRoot()) {
1752+
return true; // not instantiated, and defined in non-root
1753+
} else if (!global.params.linkonceTemplates &&
1754+
sym->isInstantiated()) {
1755+
return true; // instantiated but potentially culled (needsCodegen())
1756+
} else if (auto vd = sym->isVarDeclaration()) {
1757+
if (vd->storage_class & STCextern)
1758+
return true; // externally defined global variable
1759+
}
1760+
}
1761+
1762+
return false;
17541763
}
17551764

17561765
llvm::GlobalVariable *declareGlobal(const Loc &loc, llvm::Module &module,

gen/llvmhelpers.h

+1-2
Original file line numberDiff line numberDiff line change
@@ -247,8 +247,7 @@ llvm::Constant *buildStringLiteralConstant(StringExp *se, bool zeroTerm);
247247
/// primarily for -linkonce-templates.
248248
bool defineOnDeclare(Dsymbol *sym, bool isFunction);
249249

250-
/// Indicates whether the specified data symbol is a general dllimport
251-
/// candidate.
250+
/// Indicates whether the specified data symbol is to be declared as dllimport.
252251
bool dllimportDataSymbol(Dsymbol *sym);
253252

254253
/// Tries to declare an LLVM global. If a variable with the same mangled name

gen/tollvm.cpp

+6-2
Original file line numberDiff line numberDiff line change
@@ -290,8 +290,12 @@ void setVisibility(Dsymbol *sym, llvm::GlobalObject *obj) {
290290

291291
if (triple.isOSWindows()) {
292292
bool isExported = sym->isExport();
293-
// also export with -fvisibility=public without @hidden
294-
if (!isExported && global.params.dllexport && !hasHiddenUDA) {
293+
// Also export (non-linkonce_odr) symbols
294+
// * with -fvisibility=public without @hidden, or
295+
// * if declared with dllimport (so potentially imported from other object
296+
// files / DLLs).
297+
if (!isExported && ((global.params.dllexport && !hasHiddenUDA) ||
298+
obj->hasDLLImportStorageClass())) {
295299
isExported = hasExportedLinkage(obj);
296300
}
297301
// reset default visibility & DSO locality - on Windows, the DLL storage

ir/iraggr.cpp

+1-11
Original file line numberDiff line numberDiff line change
@@ -53,17 +53,7 @@ bool IrAggr::suppressTypeInfo() const {
5353
//////////////////////////////////////////////////////////////////////////////
5454

5555
bool IrAggr::useDLLImport() const {
56-
if (!global.params.targetTriple->isOSWindows())
57-
return false;
58-
59-
if (dllimportDataSymbol(aggrdecl)) {
60-
// dllimport, unless defined in a root module (=> no extra indirection for
61-
// other root modules, assuming *all* root modules will be linked together
62-
// to one or more binaries).
63-
return aggrdecl->inNonRoot();
64-
}
65-
66-
return false;
56+
return dllimportDataSymbol(aggrdecl);
6757
}
6858

6959
//////////////////////////////////////////////////////////////////////////////

ir/irvar.cpp

+2-6
Original file line numberDiff line numberDiff line change
@@ -86,12 +86,8 @@ void IrGlobal::declare() {
8686
// dllimport isn't supported for thread-local globals (MSVC++ neither)
8787
if (!V->isThreadlocal()) {
8888
// implicitly include extern(D) globals with -dllimport
89-
if (V->isExport() || (V->linkage == LINK::d && dllimportDataSymbol(V))) {
90-
const bool isDefinedInRootModule =
91-
!(V->storage_class & STCextern) && !V->inNonRoot();
92-
if (!isDefinedInRootModule)
93-
useDLLImport = true;
94-
}
89+
useDLLImport =
90+
(V->isExport() || V->linkage == LINK::d) && dllimportDataSymbol(V);
9591
}
9692
}
9793

tests/codegen/dllimport_gh3916.d

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// REQUIRES: Windows
2+
3+
// RUN: %ldc -output-ll -dllimport=all -of=%t_all.ll %s && FileCheck %s < %t_all.ll
4+
// RUN: %ldc -output-ll -dllimport=defaultLibsOnly -of=%t_dlo.ll %s && FileCheck %s < %t_dlo.ll
5+
6+
import std.random : Xorshift; // pre-instantiated template
7+
8+
void foo()
9+
{
10+
// CHECK: _D3std6random__T14XorshiftEngine{{.*}}6__initZ = external dllimport
11+
const i = __traits(initSymbol, Xorshift);
12+
}

0 commit comments

Comments
 (0)