diff --git a/llvm/docs/ReleaseNotes.rst b/llvm/docs/ReleaseNotes.rst --- a/llvm/docs/ReleaseNotes.rst +++ b/llvm/docs/ReleaseNotes.rst @@ -108,6 +108,14 @@ * ... +Changes to the Windows Target +----------------------------- + +* For MinGW, generate embedded ``-exclude-symbols:`` directives for symbols + with hidden visibility, omitting them from automatic export of all symbols. + This roughly makes hidden visibility work like it does for other object + file formats. + Changes to the X86 Backend -------------------------- diff --git a/llvm/lib/IR/Mangler.cpp b/llvm/lib/IR/Mangler.cpp --- a/llvm/lib/IR/Mangler.cpp +++ b/llvm/lib/IR/Mangler.cpp @@ -210,18 +210,46 @@ void llvm::emitLinkerFlagsForGlobalCOFF(raw_ostream &OS, const GlobalValue *GV, const Triple &TT, Mangler &Mangler) { - if (!GV->hasDLLExportStorageClass() || GV->isDeclaration()) - return; + if (GV->hasDLLExportStorageClass() && !GV->isDeclaration()) { - if (TT.isWindowsMSVCEnvironment()) - OS << " /EXPORT:"; - else - OS << " -export:"; + if (TT.isWindowsMSVCEnvironment()) + OS << " /EXPORT:"; + else + OS << " -export:"; + + bool NeedQuotes = GV->hasName() && !canBeUnquotedInDirective(GV->getName()); + if (NeedQuotes) + OS << "\""; + if (TT.isWindowsGNUEnvironment() || TT.isWindowsCygwinEnvironment()) { + std::string Flag; + raw_string_ostream FlagOS(Flag); + Mangler.getNameWithPrefix(FlagOS, GV, false); + FlagOS.flush(); + if (Flag[0] == GV->getParent()->getDataLayout().getGlobalPrefix()) + OS << Flag.substr(1); + else + OS << Flag; + } else { + Mangler.getNameWithPrefix(OS, GV, false); + } + if (NeedQuotes) + OS << "\""; + + if (!GV->getValueType()->isFunctionTy()) { + if (TT.isWindowsMSVCEnvironment()) + OS << ",DATA"; + else + OS << ",data"; + } + } + if (GV->hasHiddenVisibility() && !GV->isDeclaration() && TT.isOSCygMing()) { + + OS << " -exclude-symbols:"; + + bool NeedQuotes = GV->hasName() && !canBeUnquotedInDirective(GV->getName()); + if (NeedQuotes) + OS << "\""; - bool NeedQuotes = GV->hasName() && !canBeUnquotedInDirective(GV->getName()); - if (NeedQuotes) - OS << "\""; - if (TT.isWindowsGNUEnvironment() || TT.isWindowsCygwinEnvironment()) { std::string Flag; raw_string_ostream FlagOS(Flag); Mangler.getNameWithPrefix(FlagOS, GV, false); @@ -230,17 +258,9 @@ OS << Flag.substr(1); else OS << Flag; - } else { - Mangler.getNameWithPrefix(OS, GV, false); - } - if (NeedQuotes) - OS << "\""; - if (!GV->getValueType()->isFunctionTy()) { - if (TT.isWindowsMSVCEnvironment()) - OS << ",DATA"; - else - OS << ",data"; + if (NeedQuotes) + OS << "\""; } } diff --git a/llvm/test/CodeGen/X86/mingw-hidden.ll b/llvm/test/CodeGen/X86/mingw-hidden.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/X86/mingw-hidden.ll @@ -0,0 +1,81 @@ +; RUN: llc -mtriple i386-pc-win32 < %s \ +; RUN: | FileCheck --check-prefixes=CHECK,CHECK-MSVC %s +; RUN: llc -mtriple i386-pc-mingw32 < %s \ +; RUN: | FileCheck --check-prefixes=CHECK,CHECK-MINGW %s +; RUN: llc -mtriple i386-pc-mingw32 < %s \ +; RUN: | FileCheck --check-prefix=NOTEXPORTED %s + +; CHECK: .text + +; CHECK: .globl _notHidden +define void @notHidden() { + ret void +} + +; CHECK: .globl _f1 +define hidden void @f1() { + ret void +} + +; CHECK: .globl _f2 +define hidden void @f2() unnamed_addr { + ret void +} + +declare hidden void @notDefined() + +; CHECK: .globl _stdfun@0 +define hidden x86_stdcallcc void @stdfun() nounwind { + ret void +} + +; CHECK: .globl _lnk1 +$lnk1 = comdat any + +define linkonce_odr hidden void @lnk1() comdat { + ret void +} + +; CHECK: .globl _lnk2 +$lnk2 = comdat any + +define linkonce_odr hidden void @lnk2() alwaysinline comdat { + ret void +} + +; CHECK: .data +; CHECK: .globl _Var1 +@Var1 = hidden global i32 1, align 4 + +; CHECK: .rdata,"dr" +; CHECK: .globl _Var2 +@Var2 = hidden unnamed_addr constant i32 1 + +; CHECK: .comm _Var3 +@Var3 = common hidden global i32 0, align 4 + +; CHECK: .globl "_complex-name" +@"complex-name" = hidden global i32 1, align 4 + +; CHECK: .globl _complex.name +@"complex.name" = hidden global i32 1, align 4 + + +; Verify items that should not be marked hidden do not appear in the directives. +; We use a separate check prefix to avoid confusion between -NOT and -SAME. +; NOTEXPORTED: .section .drectve +; NOTEXPORTED-NOT: :notHidden +; NOTEXPORTED-NOT: :notDefined + +; CHECK-MSVC-NOT: .section .drectve +; CHECK-MINGW: .section .drectve +; CHECK-MINGW: .ascii " -exclude-symbols:f1" +; CHECK-MINGW: .ascii " -exclude-symbols:f2" +; CHECK-MINGW: .ascii " -exclude-symbols:stdfun@0" +; CHECK-MINGW: .ascii " -exclude-symbols:lnk1" +; CHECK-MINGW: .ascii " -exclude-symbols:lnk2" +; CHECK-MINGW: .ascii " -exclude-symbols:Var1" +; CHECK-MINGW: .ascii " -exclude-symbols:Var2" +; CHECK-MINGW: .ascii " -exclude-symbols:Var3" +; CHECK-MINGW: .ascii " -exclude-symbols:\"complex-name\"" +; CHECK-MINGW: .ascii " -exclude-symbols:\"complex.name\""