Skip to content

Commit

Permalink
Implement the "linker_private_weak" linkage type. This will be used for
Browse files Browse the repository at this point in the history
Objective-C metadata types which should be marked as "weak", but which the
linker will remove upon final linkage. However, this linkage isn't specific to
Objective-C.

For example, the "objc_msgSend_fixup_alloc" symbol is defined like this:

      .globl l_objc_msgSend_fixup_alloc
      .weak_definition l_objc_msgSend_fixup_alloc
      .section __DATA, __objc_msgrefs, coalesced
      .align 3
l_objc_msgSend_fixup_alloc:
       .quad   _objc_msgSend_fixup
       .quad   L_OBJC_METH_VAR_NAME_1

This is different from the "linker_private" linkage type, because it can't have
the metadata defined with ".weak_definition".

Currently only supported on Darwin platforms.


git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@107433 91177308-0d34-0410-b5e6-96231b3b80d8
  • Loading branch information
isanbard committed Jul 1, 2010
1 parent 0f66d4b commit 5e721d7
Show file tree
Hide file tree
Showing 21 changed files with 90 additions and 44 deletions.
6 changes: 4 additions & 2 deletions bindings/ada/llvm/llvm.ads
Original file line number Diff line number Diff line change
Expand Up @@ -316,7 +316,8 @@ package llvm is
LLVMExternalWeakLinkage,
LLVMGhostLinkage,
LLVMCommonLinkage,
LLVMLinkerPrivateLinkage);
LLVMLinkerPrivateLinkage,
LLVMLinkerPrivateWeakLinkage);

for LLVMLinkage use
(LLVMExternalLinkage => 0,
Expand All @@ -333,7 +334,8 @@ package llvm is
LLVMExternalWeakLinkage => 11,
LLVMGhostLinkage => 12,
LLVMCommonLinkage => 13,
LLVMLinkerPrivateLinkage => 14);
LLVMLinkerPrivateLinkage => 14,
LLVMLinkerPrivateWeakLinkage => 15);

pragma Convention (C, LLVMLinkage);

Expand Down
33 changes: 19 additions & 14 deletions docs/LangRef.html
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
<ol>
<li><a href="#linkage_private">'<tt>private</tt>' Linkage</a></li>
<li><a href="#linkage_linker_private">'<tt>linker_private</tt>' Linkage</a></li>
<li><a href="#linkage_linker_private_weak">'<tt>linker_private_weak</tt>' Linkage</a></li>
<li><a href="#linkage_internal">'<tt>internal</tt>' Linkage</a></li>
<li><a href="#linkage_available_externally">'<tt>available_externally</tt>' Linkage</a></li>
<li><a href="#linkage_linkonce">'<tt>linkonce</tt>' Linkage</a></li>
Expand Down Expand Up @@ -546,20 +547,24 @@

<dl>
<dt><tt><b><a name="linkage_private">private</a></b></tt></dt>
<dd>Global values with private linkage are only directly accessible by objects
in the current module. In particular, linking code into a module with an
private global value may cause the private to be renamed as necessary to
avoid collisions. Because the symbol is private to the module, all
references can be updated. This doesn't show up in any symbol table in the
object file.</dd>
<dd>Global values with "<tt>private</tt>" linkage are only directly accessible
by objects in the current module. In particular, linking code into a
module with an private global value may cause the private to be renamed as
necessary to avoid collisions. Because the symbol is private to the
module, all references can be updated. This doesn't show up in any symbol
table in the object file.</dd>

<dt><tt><b><a name="linkage_linker_private">linker_private</a></b></tt></dt>
<dd>Similar to private, but the symbol is passed through the assembler and
removed by the linker after evaluation. Note that (unlike private
symbols) linker_private symbols are subject to coalescing by the linker:
weak symbols get merged and redefinitions are rejected. However, unlike
normal strong symbols, they are removed by the linker from the final
linked image (executable or dynamic library).</dd>
<dd>Similar to <tt>private</tt>, but the symbol is passed through the
assembler and evaluated by the linker. Unlike normal strong symbols, they
are removed by the linker from the final linked image (executable or
dynamic library).</dd>

<dt><tt><b><a name="linkage_linker_private_weak">linker_private_weak</a></b></tt></dt>
<dd>Similar to "<tt>linker_private</tt>", but the symbol is weak. Note that
<tt>linker_private_weak</tt> symbols are subject to coalescing by the
linker. The symbols are removed by the linker from the final linked image
(executable or dynamic library).</dd>

<dt><tt><b><a name="linkage_internal">internal</a></b></tt></dt>
<dd>Similar to private, but the value shows as a local symbol
Expand Down Expand Up @@ -623,8 +628,8 @@
<dt><tt><b><a name="linkage_weak_odr">weak_odr</a></b></tt></dt>
<dd>Some languages allow differing globals to be merged, such as two functions
with different semantics. Other languages, such as <tt>C++</tt>, ensure
that only equivalent globals are ever merged (the "one definition rule" -
"ODR"). Such languages can use the <tt>linkonce_odr</tt>
that only equivalent globals are ever merged (the "one definition rule"
&mdash; "ODR"). Such languages can use the <tt>linkonce_odr</tt>
and <tt>weak_odr</tt> linkage types to indicate that the global will only
be merged with equivalent globals. These linkage types are otherwise the
same as their non-<tt>odr</tt> versions.</dd>
Expand Down
3 changes: 2 additions & 1 deletion include/llvm-c/Core.h
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,8 @@ typedef enum {
LLVMExternalWeakLinkage,/**< ExternalWeak linkage description */
LLVMGhostLinkage, /**< Obsolete */
LLVMCommonLinkage, /**< Tentative definitions */
LLVMLinkerPrivateLinkage /**< Like Private, but linker removes. */
LLVMLinkerPrivateLinkage, /**< Like Private, but linker removes. */
LLVMLinkerPrivateWeakLinkage /**< Like LinkerPrivate, but is weak. */
} LLVMLinkage;

typedef enum {
Expand Down
17 changes: 13 additions & 4 deletions include/llvm/GlobalValue.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ class GlobalValue : public Constant {
InternalLinkage, ///< Rename collisions when linking (static functions).
PrivateLinkage, ///< Like Internal, but omit from symbol table.
LinkerPrivateLinkage, ///< Like Private, but linker removes.
LinkerPrivateWeakLinkage, ///< Like LinkerPrivate, but weak.
DLLImportLinkage, ///< Function to be imported from DLL
DLLExportLinkage, ///< Function to be accessible from DLL.
ExternalWeakLinkage,///< ExternalWeak linkage description.
Expand Down Expand Up @@ -132,11 +133,14 @@ class GlobalValue : public Constant {
return Linkage == PrivateLinkage;
}
static bool isLinkerPrivateLinkage(LinkageTypes Linkage) {
return Linkage==LinkerPrivateLinkage;
return Linkage == LinkerPrivateLinkage;
}
static bool isLinkerPrivateWeakLinkage(LinkageTypes Linkage) {
return Linkage == LinkerPrivateWeakLinkage;
}
static bool isLocalLinkage(LinkageTypes Linkage) {
return isInternalLinkage(Linkage) || isPrivateLinkage(Linkage) ||
isLinkerPrivateLinkage(Linkage);
isLinkerPrivateLinkage(Linkage) || isLinkerPrivateWeakLinkage(Linkage);
}
static bool isDLLImportLinkage(LinkageTypes Linkage) {
return Linkage == DLLImportLinkage;
Expand All @@ -158,7 +162,8 @@ class GlobalValue : public Constant {
return (Linkage == WeakAnyLinkage ||
Linkage == LinkOnceAnyLinkage ||
Linkage == CommonLinkage ||
Linkage == ExternalWeakLinkage);
Linkage == ExternalWeakLinkage ||
Linkage == LinkerPrivateWeakLinkage);
}

/// isWeakForLinker - Whether the definition of this global may be replaced at
Expand All @@ -170,7 +175,8 @@ class GlobalValue : public Constant {
Linkage == LinkOnceAnyLinkage ||
Linkage == LinkOnceODRLinkage ||
Linkage == CommonLinkage ||
Linkage == ExternalWeakLinkage);
Linkage == ExternalWeakLinkage ||
Linkage == LinkerPrivateWeakLinkage);
}

bool hasExternalLinkage() const { return isExternalLinkage(Linkage); }
Expand All @@ -187,6 +193,9 @@ class GlobalValue : public Constant {
bool hasInternalLinkage() const { return isInternalLinkage(Linkage); }
bool hasPrivateLinkage() const { return isPrivateLinkage(Linkage); }
bool hasLinkerPrivateLinkage() const { return isLinkerPrivateLinkage(Linkage); }
bool hasLinkerPrivateWeakLinkage() const {
return isLinkerPrivateWeakLinkage(Linkage);
}
bool hasLocalLinkage() const { return isLocalLinkage(Linkage); }
bool hasDLLImportLinkage() const { return isDLLImportLinkage(Linkage); }
bool hasDLLExportLinkage() const { return isDLLExportLinkage(Linkage); }
Expand Down
1 change: 1 addition & 0 deletions lib/AsmParser/LLLexer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -492,6 +492,7 @@ lltok::Kind LLLexer::LexIdentifier() {

KEYWORD(private);
KEYWORD(linker_private);
KEYWORD(linker_private_weak);
KEYWORD(internal);
KEYWORD(available_externally);
KEYWORD(linkonce);
Expand Down
36 changes: 22 additions & 14 deletions lib/AsmParser/LLParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -196,19 +196,20 @@ bool LLParser::ParseTopLevelEntities() {
// optional leading prefixes, the production is:
// GlobalVar ::= OptionalLinkage OptionalVisibility OptionalThreadLocal
// OptionalAddrSpace ('constant'|'global') ...
case lltok::kw_private : // OptionalLinkage
case lltok::kw_linker_private: // OptionalLinkage
case lltok::kw_internal: // OptionalLinkage
case lltok::kw_weak: // OptionalLinkage
case lltok::kw_weak_odr: // OptionalLinkage
case lltok::kw_linkonce: // OptionalLinkage
case lltok::kw_linkonce_odr: // OptionalLinkage
case lltok::kw_appending: // OptionalLinkage
case lltok::kw_dllexport: // OptionalLinkage
case lltok::kw_common: // OptionalLinkage
case lltok::kw_dllimport: // OptionalLinkage
case lltok::kw_extern_weak: // OptionalLinkage
case lltok::kw_external: { // OptionalLinkage
case lltok::kw_private: // OptionalLinkage
case lltok::kw_linker_private: // OptionalLinkage
case lltok::kw_linker_private_weak: // OptionalLinkage
case lltok::kw_internal: // OptionalLinkage
case lltok::kw_weak: // OptionalLinkage
case lltok::kw_weak_odr: // OptionalLinkage
case lltok::kw_linkonce: // OptionalLinkage
case lltok::kw_linkonce_odr: // OptionalLinkage
case lltok::kw_appending: // OptionalLinkage
case lltok::kw_dllexport: // OptionalLinkage
case lltok::kw_common: // OptionalLinkage
case lltok::kw_dllimport: // OptionalLinkage
case lltok::kw_extern_weak: // OptionalLinkage
case lltok::kw_external: { // OptionalLinkage
unsigned Linkage, Visibility;
if (ParseOptionalLinkage(Linkage) ||
ParseOptionalVisibility(Visibility) ||
Expand Down Expand Up @@ -629,7 +630,8 @@ bool LLParser::ParseAlias(const std::string &Name, LocTy NameLoc,
Linkage != GlobalValue::WeakODRLinkage &&
Linkage != GlobalValue::InternalLinkage &&
Linkage != GlobalValue::PrivateLinkage &&
Linkage != GlobalValue::LinkerPrivateLinkage)
Linkage != GlobalValue::LinkerPrivateLinkage &&
Linkage != GlobalValue::LinkerPrivateWeakLinkage)
return Error(LinkageLoc, "invalid linkage type for alias");

Constant *Aliasee;
Expand Down Expand Up @@ -1013,11 +1015,13 @@ bool LLParser::ParseOptionalAttrs(unsigned &Attrs, unsigned AttrKind) {
/// ::= /*empty*/
/// ::= 'private'
/// ::= 'linker_private'
/// ::= 'linker_private_weak'
/// ::= 'internal'
/// ::= 'weak'
/// ::= 'weak_odr'
/// ::= 'linkonce'
/// ::= 'linkonce_odr'
/// ::= 'available_externally'
/// ::= 'appending'
/// ::= 'dllexport'
/// ::= 'common'
Expand All @@ -1030,6 +1034,9 @@ bool LLParser::ParseOptionalLinkage(unsigned &Res, bool &HasLinkage) {
default: Res=GlobalValue::ExternalLinkage; return false;
case lltok::kw_private: Res = GlobalValue::PrivateLinkage; break;
case lltok::kw_linker_private: Res = GlobalValue::LinkerPrivateLinkage; break;
case lltok::kw_linker_private_weak:
Res = GlobalValue::LinkerPrivateWeakLinkage;
break;
case lltok::kw_internal: Res = GlobalValue::InternalLinkage; break;
case lltok::kw_weak: Res = GlobalValue::WeakAnyLinkage; break;
case lltok::kw_weak_odr: Res = GlobalValue::WeakODRLinkage; break;
Expand Down Expand Up @@ -2704,6 +2711,7 @@ bool LLParser::ParseFunctionHeader(Function *&Fn, bool isDefine) {
break;
case GlobalValue::PrivateLinkage:
case GlobalValue::LinkerPrivateLinkage:
case GlobalValue::LinkerPrivateWeakLinkage:
case GlobalValue::InternalLinkage:
case GlobalValue::AvailableExternallyLinkage:
case GlobalValue::LinkOnceAnyLinkage:
Expand Down
6 changes: 3 additions & 3 deletions lib/AsmParser/LLToken.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,9 @@ namespace lltok {
kw_declare, kw_define,
kw_global, kw_constant,

kw_private, kw_linker_private, kw_internal, kw_linkonce, kw_linkonce_odr,
kw_weak, kw_weak_odr, kw_appending, kw_dllimport, kw_dllexport, kw_common,
kw_available_externally,
kw_private, kw_linker_private, kw_linker_private_weak, kw_internal,
kw_linkonce, kw_linkonce_odr, kw_weak, kw_weak_odr, kw_appending,
kw_dllimport, kw_dllexport, kw_common, kw_available_externally,
kw_default, kw_hidden, kw_protected,
kw_extern_weak,
kw_external, kw_thread_local,
Expand Down
1 change: 1 addition & 0 deletions lib/Bitcode/Reader/BitcodeReader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ static GlobalValue::LinkageTypes GetDecodedLinkage(unsigned Val) {
case 11: return GlobalValue::LinkOnceODRLinkage;
case 12: return GlobalValue::AvailableExternallyLinkage;
case 13: return GlobalValue::LinkerPrivateLinkage;
case 14: return GlobalValue::LinkerPrivateWeakLinkage;
}
}

Expand Down
1 change: 1 addition & 0 deletions lib/Bitcode/Writer/BitcodeWriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -313,6 +313,7 @@ static unsigned getEncodedLinkage(const GlobalValue *GV) {
case GlobalValue::LinkOnceODRLinkage: return 11;
case GlobalValue::AvailableExternallyLinkage: return 12;
case GlobalValue::LinkerPrivateLinkage: return 13;
case GlobalValue::LinkerPrivateWeakLinkage: return 14;
}
}

Expand Down
1 change: 1 addition & 0 deletions lib/CodeGen/AsmPrinter/AsmPrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,7 @@ void AsmPrinter::EmitLinkage(unsigned Linkage, MCSymbol *GVSym) const {
case GlobalValue::WeakAnyLinkage:
case GlobalValue::WeakODRLinkage:
case GlobalValue::LinkerPrivateLinkage:
case GlobalValue::LinkerPrivateWeakLinkage:
if (MAI->getWeakDefDirective() != 0) {
// .globl _foo
OutStreamer.EmitSymbolAttribute(GVSym, MCSA_Global);
Expand Down
2 changes: 2 additions & 0 deletions lib/Target/CppBackend/CPPBackend.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,8 @@ void CppWriter::printLinkageType(GlobalValue::LinkageTypes LT) {
Out << "GlobalValue::PrivateLinkage"; break;
case GlobalValue::LinkerPrivateLinkage:
Out << "GlobalValue::LinkerPrivateLinkage"; break;
case GlobalValue::LinkerPrivateWeakLinkage:
Out << "GlobalValue::LinkerPrivateWeakLinkage"; break;
case GlobalValue::AvailableExternallyLinkage:
Out << "GlobalValue::AvailableExternallyLinkage "; break;
case GlobalValue::LinkOnceAnyLinkage:
Expand Down
2 changes: 1 addition & 1 deletion lib/Target/Mangler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ void Mangler::getNameWithPrefix(SmallVectorImpl<char> &OutName,
ManglerPrefixTy PrefixTy = Mangler::Default;
if (GV->hasPrivateLinkage() || isImplicitlyPrivate)
PrefixTy = Mangler::Private;
else if (GV->hasLinkerPrivateLinkage())
else if (GV->hasLinkerPrivateLinkage() || GV->hasLinkerPrivateWeakLinkage())
PrefixTy = Mangler::LinkerPrivate;

// If this global has a name, handle it simply.
Expand Down
1 change: 0 additions & 1 deletion lib/Target/XCore/AsmPrinter/XCoreAsmPrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,6 @@ void XCoreAsmPrinter::EmitGlobalVariable(const GlobalVariable *GV) {
// FALL THROUGH
case GlobalValue::InternalLinkage:
case GlobalValue::PrivateLinkage:
case GlobalValue::LinkerPrivateLinkage:
break;
case GlobalValue::DLLImportLinkage:
llvm_unreachable("DLLImport linkage is not supported by this target!");
Expand Down
1 change: 1 addition & 0 deletions lib/Transforms/IPO/MergeFunctions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -535,6 +535,7 @@ static LinkageCategory categorize(const Function *F) {
case GlobalValue::WeakAnyLinkage:
case GlobalValue::WeakODRLinkage:
case GlobalValue::ExternalWeakLinkage:
case GlobalValue::LinkerPrivateWeakLinkage:
return ExternalWeak;

case GlobalValue::ExternalLinkage:
Expand Down
3 changes: 3 additions & 0 deletions lib/VMCore/AsmWriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1419,6 +1419,9 @@ static void PrintLinkage(GlobalValue::LinkageTypes LT,
case GlobalValue::ExternalLinkage: break;
case GlobalValue::PrivateLinkage: Out << "private "; break;
case GlobalValue::LinkerPrivateLinkage: Out << "linker_private "; break;
case GlobalValue::LinkerPrivateWeakLinkage:
Out << "linker_private_weak ";
break;
case GlobalValue::InternalLinkage: Out << "internal "; break;
case GlobalValue::LinkOnceAnyLinkage: Out << "linkonce "; break;
case GlobalValue::LinkOnceODRLinkage: Out << "linkonce_odr "; break;
Expand Down
5 changes: 5 additions & 0 deletions lib/VMCore/Core.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1058,6 +1058,8 @@ LLVMLinkage LLVMGetLinkage(LLVMValueRef Global) {
return LLVMPrivateLinkage;
case GlobalValue::LinkerPrivateLinkage:
return LLVMLinkerPrivateLinkage;
case GlobalValue::LinkerPrivateWeakLinkage:
return LLVMLinkerPrivateWeakLinkage;
case GlobalValue::DLLImportLinkage:
return LLVMDLLImportLinkage;
case GlobalValue::DLLExportLinkage:
Expand Down Expand Up @@ -1108,6 +1110,9 @@ void LLVMSetLinkage(LLVMValueRef Global, LLVMLinkage Linkage) {
case LLVMLinkerPrivateLinkage:
GV->setLinkage(GlobalValue::LinkerPrivateLinkage);
break;
case LLVMLinkerPrivateWeakLinkage:
GV->setLinkage(GlobalValue::LinkerPrivateWeakLinkage);
break;
case LLVMDLLImportLinkage:
GV->setLinkage(GlobalValue::DLLImportLinkage);
break;
Expand Down
2 changes: 1 addition & 1 deletion test/CodeGen/ARM/2009-08-23-linkerprivate.ll
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

; ModuleID = '/Volumes/MacOS9/tests/WebKit/JavaScriptCore/profiler/ProfilerServer.mm'

@"\01l_objc_msgSend_fixup_alloc" = linker_private hidden global i32 0, section "__DATA, __objc_msgrefs, coalesced", align 16 ; <i32*> [#uses=0]
@"\01l_objc_msgSend_fixup_alloc" = linker_private_weak hidden global i32 0, section "__DATA, __objc_msgrefs, coalesced", align 16

; CHECK: .globl l_objc_msgSend_fixup_alloc
; CHECK: .weak_definition l_objc_msgSend_fixup_alloc
2 changes: 1 addition & 1 deletion test/CodeGen/PowerPC/2009-08-23-linkerprivate.ll
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

; ModuleID = '/Volumes/MacOS9/tests/WebKit/JavaScriptCore/profiler/ProfilerServer.mm'

@"\01l_objc_msgSend_fixup_alloc" = linker_private hidden global i32 0, section "__DATA, __objc_msgrefs, coalesced", align 16 ; <i32*> [#uses=0]
@"\01l_objc_msgSend_fixup_alloc" = linker_private_weak hidden global i32 0, section "__DATA, __objc_msgrefs, coalesced", align 16

; CHECK: .globl l_objc_msgSend_fixup_alloc
; CHECK: .weak_definition l_objc_msgSend_fixup_alloc
2 changes: 1 addition & 1 deletion test/CodeGen/X86/2009-08-23-linkerprivate.ll
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

; ModuleID = '/Volumes/MacOS9/tests/WebKit/JavaScriptCore/profiler/ProfilerServer.mm'

@"\01l_objc_msgSend_fixup_alloc" = linker_private hidden global i32 0, section "__DATA, __objc_msgrefs, coalesced", align 16 ; <i32*> [#uses=0]
@"\01l_objc_msgSend_fixup_alloc" = linker_private_weak hidden global i32 0, section "__DATA, __objc_msgrefs, coalesced", align 16

; CHECK: .globl l_objc_msgSend_fixup_alloc
; CHECK: .weak_definition l_objc_msgSend_fixup_alloc
6 changes: 6 additions & 0 deletions test/Feature/linker_private_linkages.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
; RUN: llvm-as < %s | llvm-dis > %t1.ll
; RUN: llvm-as %t1.ll -o - | llvm-dis > %t2.ll
; RUN: diff %t1.ll %t2.ll

@foo = linker_private hidden global i32 0
@bar = linker_private_weak hidden global i32 0
3 changes: 2 additions & 1 deletion tools/llvm-nm/llvm-nm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,8 @@ static char TypeCharForSymbol(GlobalValue &GV) {
static void DumpSymbolNameForGlobalValue(GlobalValue &GV) {
// Private linkage and available_externally linkage don't exist in symtab.
if (GV.hasPrivateLinkage() || GV.hasLinkerPrivateLinkage() ||
GV.hasAvailableExternallyLinkage()) return;
GV.hasLinkerPrivateWeakLinkage() || GV.hasAvailableExternallyLinkage())
return;

const std::string SymbolAddrStr = " "; // Not used yet...
char TypeChar = TypeCharForSymbol(GV);
Expand Down

0 comments on commit 5e721d7

Please sign in to comment.