Skip to content

[MS] Put dllexported inline global initializers in a comdat #107154

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Sep 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 31 additions & 12 deletions clang/lib/CodeGen/CGDeclCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -586,31 +586,50 @@ CodeGenModule::EmitCXXGlobalVarDeclInitFunc(const VarDecl *D,
PrioritizedCXXGlobalInits.size());
PrioritizedCXXGlobalInits.push_back(std::make_pair(Key, Fn));
} else if (isTemplateInstantiation(D->getTemplateSpecializationKind()) ||
getContext().GetGVALinkageForVariable(D) == GVA_DiscardableODR ||
!isUniqueGVALinkage(getContext().GetGVALinkageForVariable(D)) ||
D->hasAttr<SelectAnyAttr>()) {
// For vague linkage globals, put the initializer into its own global_ctors
// entry with the global as a comdat key. This ensures at most one
// initializer per DSO runs during DSO dynamic initialization.
//
// For ELF platforms, this is an important code size and startup time
// optimization. For dynamic, non-hidden symbols, the weak guard variable
// remains to ensure that other DSOs do not re-initialize the global.
//
// For PE-COFF platforms, there is no guard variable, and COMDAT
// associativity is the only way to ensure vauge linkage globals are
// initialized exactly once.
//
// MachO is the only remaining platform with no comdats that doesn't
// benefit from this optimization. The rest are mainly modeled on ELF
// behavior.
//
// C++ requires that inline global variables are initialized in source
// order, but this requirement does not exist for templated entities.
// llvm.global_ctors does not guarantee initialization order, so in
// general, Clang does not fully conform to the ordering requirement.
// However, in practice, LLVM emits global_ctors in the provided order, and
// users typically don't rely on ordering between inline globals in
// different headers which are then transitively included in varying order.
// Clang's current behavior is a practical tradeoff, since dropping the
// comdat would lead to unacceptable impact on code size and startup time.
//
// FIXME: Find a solution to guarantee source-order initialization of
// inline variables.
//
// C++ [basic.start.init]p2:
// Definitions of explicitly specialized class template static data
// members have ordered initialization. Other class template static data
// members (i.e., implicitly or explicitly instantiated specializations)
// have unordered initialization.
//
// As a consequence, we can put them into their own llvm.global_ctors entry.
//
// If the global is externally visible, put the initializer into a COMDAT
// group with the global being initialized. On most platforms, this is a
// minor startup time optimization. In the MS C++ ABI, there are no guard
// variables, so this COMDAT key is required for correctness.
//
// SelectAny globals will be comdat-folded. Put the initializer into a
// COMDAT group associated with the global, so the initializers get folded
// too.
I = DelayedCXXInitPosition.find(D);
// CXXGlobalInits.size() is the lex order number for the next deferred
// VarDecl. Use it when the current VarDecl is non-deferred. Although this
// lex order number is shared between current VarDecl and some following
// VarDecls, their order of insertion into `llvm.global_ctors` is the same
// as the lexing order and the following stable sort would preserve such
// order.
I = DelayedCXXInitPosition.find(D);
unsigned LexOrder =
I == DelayedCXXInitPosition.end() ? CXXGlobalInits.size() : I->second;
AddGlobalCtor(Fn, 65535, LexOrder, COMDATKey);
Expand Down
5 changes: 2 additions & 3 deletions clang/test/CodeGenCXX/microsoft-abi-template-static-init.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,6 @@ struct X {
static T ioo;
static T init();
};
// template specialized static data don't need in llvm.used,
// the static init routine get call from _GLOBAL__sub_I_ routines.
template <> int X<int>::ioo = X<int>::init();
template struct X<int>;
class a {
Expand Down Expand Up @@ -87,5 +85,6 @@ struct S1
int foo();
inline int zoo = foo();
inline static int boo = foo();
inline __declspec(dllexport) A exported_inline{};

// CHECK: @llvm.used = appending global [8 x ptr] [ptr @"?x@selectany_init@@3HA", ptr @"?x1@selectany_init@@3HA", ptr @"?x@?$A@H@explicit_template_instantiation@@2HA", ptr @"?ioo@?$X_@H@@2HA", ptr @"?aoo@S1@@2UA@@A", ptr @"?zoo@@3HA", ptr @"?s@?$ExportedTemplate@H@@2US@@A", ptr @"?x@?$A@H@implicit_template_instantiation@@2HA"], section "llvm.metadata"
// CHECK: @llvm.used = appending global [10 x ptr] [ptr @"?x@selectany_init@@3HA", ptr @"?x1@selectany_init@@3HA", ptr @"?x@?$A@H@explicit_template_instantiation@@2HA", ptr @"?ioo@?$X_@H@@2HA", ptr @"?ioo@?$X@H@@2HA", ptr @"?aoo@S1@@2UA@@A", ptr @"?zoo@@3HA", ptr @"?exported_inline@@3UA@@A", ptr @"?s@?$ExportedTemplate@H@@2US@@A", ptr @"?x@?$A@H@implicit_template_instantiation@@2HA"], section "llvm.metadata"
Loading