Map export visibility to LLVM DLL storage classes

Compatible with DMD, but restricted to Windows and functions only.

`export` functions with bodies get the dllexport attribute and will be
exported if the containing object is pulled in when linking.

Body-less `export` functions get the dllimport attribute and will be
accessed via an import table indirection, set up at runtime by the OS.

This is a temporary solution, the proper fix is a pending DMD PR, after
which LDC will need to be adapted.
This commit is contained in:
Martin 2016-10-26 15:25:38 +02:00
parent 8a2a6c1e7f
commit 67d5fe5624
6 changed files with 60 additions and 1 deletions

View file

@ -356,6 +356,7 @@ public:
setLinkage(lwc, newGvar); setLinkage(lwc, newGvar);
newGvar->setAlignment(gvar->getAlignment()); newGvar->setAlignment(gvar->getAlignment());
newGvar->setDLLStorageClass(gvar->getDLLStorageClass());
applyVarDeclUDAs(decl, newGvar); applyVarDeclUDAs(decl, newGvar);
newGvar->takeName(gvar); newGvar->takeName(gvar);
@ -368,6 +369,8 @@ public:
irGlobal->value = newGvar; irGlobal->value = newGvar;
} }
assert(!gvar->hasDLLImportStorageClass());
// Now, set the initializer. // Now, set the initializer.
assert(!irGlobal->constInit); assert(!irGlobal->constInit);
irGlobal->constInit = initVal; irGlobal->constInit = initVal;

View file

@ -538,6 +538,12 @@ void DtoDeclareFunction(FuncDeclaration *fdecl) {
func->setCallingConv(gABI->callingConv(func->getFunctionType(), link, fdecl)); func->setCallingConv(gABI->callingConv(func->getFunctionType(), link, fdecl));
if (global.params.isWindows && fdecl->isExport()) {
func->setDLLStorageClass(fdecl->isImportedSymbol()
? LLGlobalValue::DLLImportStorageClass
: LLGlobalValue::DLLExportStorageClass);
}
IF_LOG Logger::cout() << "func = " << *func << std::endl; IF_LOG Logger::cout() << "func = " << *func << std::endl;
// add func to IRFunc // add func to IRFunc
@ -906,11 +912,12 @@ void DtoDefineFunction(FuncDeclaration *fd, bool linkageAvailableExternally) {
const auto f = static_cast<TypeFunction *>(fd->type->toBasetype()); const auto f = static_cast<TypeFunction *>(fd->type->toBasetype());
IrFuncTy &irFty = irFunc->irFty; IrFuncTy &irFty = irFunc->irFty;
llvm::Function *func = irFunc->func;; llvm::Function *func = irFunc->func;
const auto lwc = lowerFuncLinkage(fd); const auto lwc = lowerFuncLinkage(fd);
if (linkageAvailableExternally) { if (linkageAvailableExternally) {
func->setLinkage(llvm::GlobalValue::AvailableExternallyLinkage); func->setLinkage(llvm::GlobalValue::AvailableExternallyLinkage);
func->setDLLStorageClass(llvm::GlobalValue::DefaultStorageClass);
// Assert that we are not overriding a linkage type that disallows inlining // Assert that we are not overriding a linkage type that disallows inlining
assert(lwc.first != llvm::GlobalValue::WeakAnyLinkage && assert(lwc.first != llvm::GlobalValue::WeakAnyLinkage &&
lwc.first != llvm::GlobalValue::ExternalWeakLinkage && lwc.first != llvm::GlobalValue::ExternalWeakLinkage &&
@ -919,6 +926,8 @@ void DtoDefineFunction(FuncDeclaration *fd, bool linkageAvailableExternally) {
setLinkage(lwc, func); setLinkage(lwc, func);
} }
assert(!func->hasDLLImportStorageClass());
// On x86_64, always set 'uwtable' for System V ABI compatibility. // On x86_64, always set 'uwtable' for System V ABI compatibility.
// TODO: Find a better place for this. // TODO: Find a better place for this.
// TODO: Is this required for Win64 as well? // TODO: Is this required for Win64 as well?

View file

@ -851,6 +851,14 @@ void DtoResolveVariable(VarDeclaration *vd) {
// as well). // as well).
gvar->setAlignment(DtoAlignment(vd)); gvar->setAlignment(DtoAlignment(vd));
/* TODO: set DLL storage class when `export` is fixed (an attribute)
if (global.params.isWindows && vd->isExport()) {
auto c = vd->isImportedSymbol() ? LLGlobalValue::DLLImportStorageClass
: LLGlobalValue::DLLExportStorageClass;
gvar->setDLLStorageClass(c);
}
*/
applyVarDeclUDAs(vd, gvar); applyVarDeclUDAs(vd, gvar);
IF_LOG Logger::cout() << *gvar << '\n'; IF_LOG Logger::cout() << *gvar << '\n';

19
tests/codegen/export.d Normal file
View file

@ -0,0 +1,19 @@
// RUN: %ldc -output-ll -of=%t.ll %s
// RUN: FileCheck %s < %t.ll
// REQUIRES: Windows
export
{
// CHECK-DAG: define dllexport {{.*}}_D6export11exportedFooFZv
void exportedFoo() {}
// CHECK-DAG: declare dllimport {{.*}}_D6export11importedFooFZv
void importedFoo();
}
void bar()
{
exportedFoo();
importedFoo();
}

View file

@ -0,0 +1,19 @@
// Make sure exported functions can be cross-module inlined without exporting the local function copy.
// REQUIRES: atleast_llvm307
// RUN: %ldc -O -release -enable-cross-module-inlining -output-ll -of=%t.ll -I%S/inputs %s
// RUN: FileCheck %s < %t.ll
import export2;
// CHECK-NOT: _D7export23fooFZi
// CHECK: define {{.*}}_D26export_crossModuleInlining3barFZi
int bar()
{
// CHECK-NEXT: ret i32 666
return foo();
}
// CHECK-NOT: _D7export23fooFZi

View file

@ -0,0 +1 @@
export int foo() { return 666; }