Windows: Make implicit dllimport more selective

* Newly require `-link-defaultlib-shared` for implicit dllimport.
  E.g., this enables to compile druntime DLL with `-fvisibility=public`
  for pure exports and no (local) imports (such as builtin TypeInfos).
* `-link-defaultlib-shared` alone now only implicitly imports symbols
  from druntime/Phobos.
  This simplifies building complex DLLs linked against a bunch of
  static libs (dub only supports static lib dependencies!); the static
  libs don't need to be compiled with `-fvisibility=public` anymore
  (if the DLL itself isn't either), `-link-defaultlib-shared` is
  sufficient.
  This is mainly useful for existing DLLs with explicit exports, to make
  them link against *shared* druntime/Phobos and so end up with a single
  druntime/Phobos for the whole process.
This commit is contained in:
Martin Kinkelin 2021-06-14 15:27:34 +02:00
parent 2f0ece3274
commit 9865e459d1
15 changed files with 73 additions and 23 deletions

View file

@ -109,6 +109,14 @@ enum FeatureState : byte
enabled = 1 /// Specified as `-preview=` enabled = 1 /// Specified as `-preview=`
} }
version (IN_LLVM)
enum DLLImport : byte
{
none,
defaultLibsOnly, // only symbols from druntime/Phobos
all
}
// Put command line switches in here // Put command line switches in here
extern (C++) struct Param extern (C++) struct Param
{ {
@ -304,8 +312,8 @@ version (IN_LLVM)
bool linkonceTemplates; // -linkonce-templates bool linkonceTemplates; // -linkonce-templates
// Windows-specific: // Windows-specific:
bool dllexport; // dllexport ~all defined symbols? bool dllexport; // dllexport ~all defined symbols?
bool dllimport; // dllimport data symbols not defined in any root module? DLLImport dllimport; // dllimport data symbols not defined in any root module?
} // IN_LLVM } // IN_LLVM
} }

View file

@ -98,6 +98,15 @@ enum class FeatureState : signed char
enabled = 1 /// Specified as `-preview=` enabled = 1 /// Specified as `-preview=`
}; };
#if IN_LLVM
enum class DLLImport : char
{
none,
defaultLibsOnly, // only symbols from druntime/Phobos
all
};
#endif
// Put command line switches in here // Put command line switches in here
struct Param struct Param
{ {
@ -279,8 +288,8 @@ struct Param
bool linkonceTemplates; // -linkonce-templates bool linkonceTemplates; // -linkonce-templates
// Windows-specific: // Windows-specific:
bool dllexport; // dllexport ~all defined symbols? bool dllexport; // dllexport ~all defined symbols?
bool dllimport; // dllimport data symbols not defined in any root module? DLLImport dllimport; // dllimport data symbols not defined in any root module?
#endif #endif
}; };

View file

@ -44,6 +44,9 @@ struct Id
static Identifier *dcompute; static Identifier *dcompute;
static Identifier *dcPointer; static Identifier *dcPointer;
static Identifier *object; static Identifier *object;
static Identifier *core;
static Identifier *etc;
static Identifier *std;
static Identifier *ensure; static Identifier *ensure;
static Identifier *require; static Identifier *require;
static Identifier *xopEquals; static Identifier *xopEquals;

View file

@ -1090,10 +1090,10 @@ int cppmain() {
v == opts::SymbolVisibility::public_ || v == opts::SymbolVisibility::public_ ||
// default with -shared // default with -shared
(v == opts::SymbolVisibility::default_ && global.params.dll); (v == opts::SymbolVisibility::default_ && global.params.dll);
global.params.dllimport = global.params.dllimport = !linkAgainstSharedDefaultLibs() ? DLLImport::none
v == opts::SymbolVisibility::public_ || : v == opts::SymbolVisibility::public_
// enforced when linking against shared default libs ? DLLImport::all
linkAgainstSharedDefaultLibs(); : DLLImport::defaultLibsOnly;
} }
// allocate the target abi // allocate the target abi

View file

@ -1670,6 +1670,22 @@ std::string llvmTypeToString(llvm::Type *type) {
return result; return result;
} }
bool isDefaultLibSymbol(Dsymbol *sym) {
auto mod = sym->getModule();
if (!mod)
return false;
auto md = mod->md;
if (!md)
return false;
if (md->packages.length == 0)
return md->id == Id::object;
auto p = md->packages.ptr[0];
return p == Id::core || p == Id::std || p == Id::etc || p == Id::ldc;
}
llvm::GlobalVariable *declareGlobal(const Loc &loc, llvm::Module &module, llvm::GlobalVariable *declareGlobal(const Loc &loc, llvm::Module &module,
llvm::Type *type, llvm::Type *type,
llvm::StringRef mangledName, llvm::StringRef mangledName,

View file

@ -245,6 +245,9 @@ LLConstant *toConstantArray(LLType *ct, LLArrayType *at, T *str, size_t len,
llvm::Constant *buildStringLiteralConstant(StringExp *se, bool zeroTerm); llvm::Constant *buildStringLiteralConstant(StringExp *se, bool zeroTerm);
/// Is the specified symbol part of druntime/Phobos?
bool isDefaultLibSymbol(Dsymbol *sym);
/// Tries to declare an LLVM global. If a variable with the same mangled name /// Tries to declare an LLVM global. If a variable with the same mangled name
/// already exists, checks if the types match and returns it instead. /// already exists, checks if the types match and returns it instead.
/// ///

View file

@ -190,8 +190,9 @@ LLFunction *build_module_reference_and_ctor(const char *moduleMangle,
LLConstant *mref = gIR->module.getNamedGlobal(mrefIRMangle); LLConstant *mref = gIR->module.getNamedGlobal(mrefIRMangle);
LLType *modulerefPtrTy = getPtrToType(modulerefTy); LLType *modulerefPtrTy = getPtrToType(modulerefTy);
if (!mref) { if (!mref) {
mref = declareGlobal(Loc(), gIR->module, modulerefPtrTy, mrefIRMangle, mref =
false, false, global.params.dllimport); declareGlobal(Loc(), gIR->module, modulerefPtrTy, mrefIRMangle, false,
false, global.params.dllimport != DLLImport::none);
} }
mref = DtoBitCast(mref, getPtrToType(modulerefPtrTy)); mref = DtoBitCast(mref, getPtrToType(modulerefPtrTy));

View file

@ -423,7 +423,7 @@ bool ldc_optimize_module(llvm::Module *M) {
addOptimizationPasses(mpm, fpm, optLevel(), sizeLevel()); addOptimizationPasses(mpm, fpm, optLevel(), sizeLevel());
if (global.params.dllimport) { if (global.params.dllimport != DLLImport::none) {
mpm.add(createDLLImportRelocationPass()); mpm.add(createDLLImportRelocationPass());
} }

View file

@ -439,8 +439,7 @@ void buildTypeInfo(TypeInfoDeclaration *decl) {
// immutable on the D side, and e.g. synchronized() can be used on the // immutable on the D side, and e.g. synchronized() can be used on the
// implicit monitor. // implicit monitor.
const bool isConstant = false; const bool isConstant = false;
// TODO: no dllimport when compiling druntime itself bool useDLLImport = isBuiltin && global.params.dllimport != DLLImport::none;
const bool useDLLImport = isBuiltin && global.params.dllimport;
gvar = declareGlobal(decl->loc, gIR->module, type, irMangle, isConstant, gvar = declareGlobal(decl->loc, gIR->module, type, irMangle, isConstant,
false, useDLLImport); false, useDLLImport);
} }

View file

@ -56,7 +56,9 @@ bool IrAggr::useDLLImport() const {
if (!global.params.targetTriple->isOSWindows()) if (!global.params.targetTriple->isOSWindows())
return false; return false;
if (global.params.dllimport || aggrdecl->isExport()) { if (aggrdecl->isExport() || global.params.dllimport == DLLImport::all ||
(global.params.dllimport == DLLImport::defaultLibsOnly &&
isDefaultLibSymbol(aggrdecl))) {
// dllimport, unless defined in a root module (=> no extra indirection for // dllimport, unless defined in a root module (=> no extra indirection for
// other root modules, assuming *all* root modules will be linked together // other root modules, assuming *all* root modules will be linked together
// to one or more binaries). // to one or more binaries).

View file

@ -27,7 +27,11 @@ llvm::GlobalVariable *IrModule::moduleInfoSymbol() {
const auto irMangle = getIRMangledModuleInfoSymbolName(M); const auto irMangle = getIRMangledModuleInfoSymbolName(M);
const bool useDLLImport = global.params.dllimport && !M->isRoot(); bool useDLLImport = false;
if (global.params.dllimport == DLLImport::all)
useDLLImport = !M->isRoot();
else if (global.params.dllimport == DLLImport::defaultLibsOnly)
useDLLImport = !M->isRoot() && isDefaultLibSymbol(M);
moduleInfoVar = declareGlobal(Loc(), gIR->module, moduleInfoVar = declareGlobal(Loc(), gIR->module,
llvm::StructType::create(gIR->context()), llvm::StructType::create(gIR->context()),

View file

@ -85,9 +85,13 @@ void IrGlobal::declare() {
if (global.params.targetTriple->isOSWindows()) { if (global.params.targetTriple->isOSWindows()) {
// dllimport isn't supported for thread-local globals (MSVC++ neither) // dllimport isn't supported for thread-local globals (MSVC++ neither)
if (!V->isThreadlocal()) { if (!V->isThreadlocal()) {
// with -fvisibility=public / -link-defaultlib-shared, also include all // with -fvisibility=public / -link-defaultlib-shared, also include
// extern(D) globals // extern(D) globals
if (V->isExport() || (global.params.dllimport && V->linkage == LINK::d)) { if (V->isExport() ||
(V->linkage == LINK::d &&
(global.params.dllimport == DLLImport::all ||
(global.params.dllimport == DLLImport::defaultLibsOnly &&
isDefaultLibSymbol(V))))) {
const bool isDefinedInRootModule = const bool isDefinedInRootModule =
!(V->storage_class & STCextern) && !V->inNonRoot(); !(V->storage_class & STCextern) && !V->inNonRoot();
if (!isDefinedInRootModule) if (!isDefinedInRootModule)

View file

@ -708,7 +708,7 @@ macro(build_runtime_variant d_flags c_flags ld_flags lib_suffix path_suffix emit
AND NOT ${all_d_files_at_once}) AND NOT ${all_d_files_at_once})
set(phobos2_o "") set(phobos2_o "")
set(phobos2_bc "") set(phobos2_bc "")
compile_phobos2("${phobos2_d_flags};-relocation-model=pic;-fvisibility=public" compile_phobos2("${phobos2_d_flags};-relocation-model=pic;-fvisibility=public;-link-defaultlib-shared"
"${lib_suffix}${SHARED_LIB_SUFFIX}" "${path_suffix}" "${lib_suffix}${SHARED_LIB_SUFFIX}" "${path_suffix}"
"${emit_bc}" "${all_d_files_at_once}" "OFF" phobos2_o phobos2_bc) "${emit_bc}" "${all_d_files_at_once}" "OFF" phobos2_o phobos2_bc)
@ -761,7 +761,7 @@ macro(build_runtime_variant d_flags c_flags ld_flags lib_suffix path_suffix emit
if(phobos2_common STREQUAL "") if(phobos2_common STREQUAL "")
set(phobos2_o "") set(phobos2_o "")
set(phobos2_bc "") set(phobos2_bc "")
compile_phobos2("${phobos2_d_flags};-relocation-model=pic;-fvisibility=public" compile_phobos2("${phobos2_d_flags};-relocation-model=pic;-fvisibility=public;-link-defaultlib-shared"
"${lib_suffix}${SHARED_LIB_SUFFIX}" "${path_suffix}" "${lib_suffix}${SHARED_LIB_SUFFIX}" "${path_suffix}"
"OFF" "${all_d_files_at_once}" "${all_d_files_at_once}" phobos2_o phobos2_bc) "OFF" "${all_d_files_at_once}" "${all_d_files_at_once}" phobos2_o phobos2_bc)
endif() endif()

@ -1 +1 @@
Subproject commit 65a26b5756fd1b4f693b013e1838ac11ac2e19d4 Subproject commit c424142bbab8def67d362ac18d534c2be165d39b

View file

@ -4,11 +4,12 @@
// REQUIRES: Windows // REQUIRES: Windows
// generate DLL and import lib // generate DLL and import lib (public visibility by default)
// RUN: %ldc %S/inputs/fvisibility_dll_lib.d -betterC -shared -of=%t_lib.dll // RUN: %ldc %S/inputs/fvisibility_dll_lib.d -betterC -shared -of=%t_lib.dll
// compile, link and run the app; -link-defaultlib-shared for importing data symbols as dllimport // compile, link and run the app;
// RUN: %ldc %s -I%S/inputs -betterC -link-defaultlib-shared %t_lib.lib -of=%t.exe // `-link-defaultlib-shared -fvisibility=public` for dllimporting data symbols
// RUN: %ldc %s -I%S/inputs -betterC -link-defaultlib-shared -fvisibility=public %t_lib.lib -of=%t.exe
// RUN: %t.exe // RUN: %t.exe
import fvisibility_dll_lib; import fvisibility_dll_lib;