[clang] [clang][CodeGen] Use guarded init for weak static locals without constant initialization (PR #226645)
Divyansh Yadav via cfe-commits
cfe-commits at lists.llvm.org
Sun Sep 27 11:54:00 PDT 2026
https://github.com/schizophrenicmaniac updated https://github.com/llvm/llvm-project/pull/226645
>From 045a77f1533f3a4659c21565015753e3d8631ebc Mon Sep 17 00:00:00 2001
From: Divyansh <anshmcs at gmail.com>
Date: Sat, 26 Sep 2026 11:56:14 +0530
Subject: [PATCH 1/2] [clang][CodeGen] Use guarded init for weak static locals
without constant init (#226631)
---
clang/lib/CodeGen/CGDecl.cpp | 22 ++++--
.../static-local-inline-non-constant-init.cpp | 77 +++++++++++++++++++
2 files changed, 94 insertions(+), 5 deletions(-)
create mode 100644 clang/test/CodeGenCXX/static-local-inline-non-constant-init.cpp
diff --git a/clang/lib/CodeGen/CGDecl.cpp b/clang/lib/CodeGen/CGDecl.cpp
index e1ed66ae71243e..bbf94e8c4ecce1 100644
--- a/clang/lib/CodeGen/CGDecl.cpp
+++ b/clang/lib/CodeGen/CGDecl.cpp
@@ -224,10 +224,6 @@ void CodeGenFunction::EmitVarDecl(const VarDecl &D) {
llvm::GlobalValue::LinkageTypes Linkage =
CGM.getLLVMLinkageVarDefinition(&D);
- // FIXME: We need to force the emission/use of a guard variable for
- // some variables even if we can constant-evaluate them because
- // we can't guarantee every translation unit will constant-evaluate them.
-
return EmitStaticVarDecl(D, Linkage);
}
@@ -360,8 +356,24 @@ llvm::Constant *CodeGenModule::getOrCreateStaticVarDecl(
llvm::GlobalVariable *
CodeGenFunction::AddInitializerToStaticVarDecl(const VarDecl &D,
llvm::GlobalVariable *GV) {
+ // A static local with vague linkage (e.g. one in an inline function) is
+ // shared by every translation unit that emits it, but only a constant
+ // initializer is guaranteed to be emitted as a constant in all of them. If
+ // we were to fold an initializer that another translation unit can't fold
+ // (say, because it reads a variable whose initializer is only visible here),
+ // that translation unit would perform a guarded initialization which could
+ // store to our copy of the variable (possibly placed in read-only memory) or
+ // race with our unguarded reads of it. Use a guarded initialization here as
+ // well, so that every translation unit agrees on how the variable is
+ // initialized.
+ bool NeedsGuardedInit =
+ getLangOpts().CPlusPlus && !getLangOpts().CUDAIsDevice &&
+ GV->isWeakForLinker() && !D.hasConstantInitialization() &&
+ !D.hasFlexibleArrayInit(getContext());
+
ConstantEmitter emitter(*this);
- llvm::Constant *Init = emitter.tryEmitForInitializer(D);
+ llvm::Constant *Init =
+ NeedsGuardedInit ? nullptr : emitter.tryEmitForInitializer(D);
// If constant emission failed, then this should be a C++ static
// initializer.
diff --git a/clang/test/CodeGenCXX/static-local-inline-non-constant-init.cpp b/clang/test/CodeGenCXX/static-local-inline-non-constant-init.cpp
new file mode 100644
index 00000000000000..ec78d2d90e3600
--- /dev/null
+++ b/clang/test/CodeGenCXX/static-local-inline-non-constant-init.cpp
@@ -0,0 +1,77 @@
+// RUN: %clang_cc1 -triple x86_64-linux-gnu -std=c++17 -emit-llvm -o - %s | FileCheck %s --check-prefix=ITANIUM
+// RUN: %clang_cc1 -triple x86_64-windows-msvc -std=c++17 -emit-llvm -o - %s | FileCheck %s --check-prefix=MSVC
+
+// A static local in an inline function is shared by every translation unit
+// that uses the function. If its initializer isn't a constant initializer,
+// another translation unit might not be able to fold it (here, one that can't
+// see the initializer of kSize) and will initialize the variable dynamically,
+// behind a guard variable. So it must not be emitted as a (possibly read-only)
+// constant without a guard in this translation unit either.
+// See https://github.com/llvm/llvm-project/issues/226631.
+
+extern const int kSize;
+
+inline const int &instance() {
+ static const int meta = kSize;
+ return meta;
+}
+
+// Constant initializers are still emitted as constants, without a guard.
+inline const int &constant() {
+ static const int c = 42;
+ return c;
+}
+
+// A static local that isn't shared with other translation units can still be
+// folded, even if its initializer isn't a constant initializer.
+static const int &internal() {
+ static const int i = kSize;
+ return i;
+}
+
+extern const int kSize = 3;
+
+const int *a() { return &instance(); }
+const int *b() { return &constant(); }
+const int *c() { return &internal(); }
+
+// ITANIUM-DAG: @_ZZ8instancevE4meta = linkonce_odr global i32 0, comdat, align 4
+// ITANIUM-DAG: @_ZGVZ8instancevE4meta = linkonce_odr global i64 0, comdat, align 8
+// ITANIUM-DAG: @_ZZ8constantvE1c = linkonce_odr constant i32 42, comdat, align 4
+// ITANIUM-DAG: @_ZZL8internalvE1i = internal constant i32 3, align 4
+// ITANIUM-NOT: @_ZGVZ8constantvE1c
+// ITANIUM-NOT: @_ZGVZL8internalvE1i
+
+// ITANIUM-LABEL: define linkonce_odr {{.*}} ptr @_Z8instancev()
+// ITANIUM: load atomic i8, ptr @_ZGVZ8instancevE4meta acquire
+// ITANIUM: call i32 @__cxa_guard_acquire(ptr @_ZGVZ8instancevE4meta)
+// ITANIUM: store i32 3, ptr @_ZZ8instancevE4meta
+// ITANIUM: call void @__cxa_guard_release(ptr @_ZGVZ8instancevE4meta)
+// ITANIUM: ret ptr @_ZZ8instancevE4meta
+
+// ITANIUM-LABEL: define linkonce_odr {{.*}} ptr @_Z8constantv()
+// ITANIUM-NEXT: entry:
+// ITANIUM-NEXT: ret ptr @_ZZ8constantvE1c
+
+// ITANIUM-LABEL: define internal {{.*}} ptr @_ZL8internalv()
+// ITANIUM-NEXT: entry:
+// ITANIUM-NEXT: ret ptr @_ZZL8internalvE1i
+
+// MSVC-DAG: @"?meta@?1??instance@@YAAEBHXZ at 4HB" = linkonce_odr dso_local global i32 0, comdat, align 4
+// MSVC-DAG: @"?$TSS0@?1??instance@@YAAEBHXZ at 4HA" = linkonce_odr global i32 0, comdat, align 4
+// MSVC-DAG: @"?c@?1??constant@@YAAEBHXZ at 4HB" = linkonce_odr dso_local constant i32 42, comdat, align 4
+// MSVC-DAG: @"?i@?1??internal@@YAAEBHXZ at 4HB" = internal constant i32 3, align 4
+
+// MSVC-LABEL: define linkonce_odr {{.*}} ptr @"?instance@@YAAEBHXZ"()
+// MSVC: call void @_Init_thread_header(ptr @"?$TSS0@?1??instance@@YAAEBHXZ at 4HA")
+// MSVC: store i32 3, ptr @"?meta@?1??instance@@YAAEBHXZ at 4HB"
+// MSVC: call void @_Init_thread_footer(ptr @"?$TSS0@?1??instance@@YAAEBHXZ at 4HA")
+// MSVC: ret ptr @"?meta@?1??instance@@YAAEBHXZ at 4HB"
+
+// MSVC-LABEL: define linkonce_odr {{.*}} ptr @"?constant@@YAAEBHXZ"()
+// MSVC-NEXT: entry:
+// MSVC-NEXT: ret ptr @"?c@?1??constant@@YAAEBHXZ at 4HB"
+
+// MSVC-LABEL: define internal {{.*}} ptr @"?internal@@YAAEBHXZ"()
+// MSVC-NEXT: entry:
+// MSVC-NEXT: ret ptr @"?i@?1??internal@@YAAEBHXZ at 4HB"
>From c9a8d315376307fa60bf58aa1e5d013def5cc49c Mon Sep 17 00:00:00 2001
From: Divyansh <anshmcs at gmail.com>
Date: Mon, 28 Sep 2026 00:02:13 +0530
Subject: [PATCH 2/2] [clang][CodeGen] Also use dynamic init for inline
variables and templated static data members without constant init
---
clang/lib/CodeGen/CGDecl.cpp | 20 +--
clang/lib/CodeGen/CodeGenModule.cpp | 29 +++-
clang/lib/CodeGen/CodeGenModule.h | 4 +
.../static-local-inline-non-constant-init.cpp | 77 ----------
.../vague-linkage-non-constant-init.cpp | 132 ++++++++++++++++++
5 files changed, 166 insertions(+), 96 deletions(-)
delete mode 100644 clang/test/CodeGenCXX/static-local-inline-non-constant-init.cpp
create mode 100644 clang/test/CodeGenCXX/vague-linkage-non-constant-init.cpp
diff --git a/clang/lib/CodeGen/CGDecl.cpp b/clang/lib/CodeGen/CGDecl.cpp
index bbf94e8c4ecce1..7a03893cb715e1 100644
--- a/clang/lib/CodeGen/CGDecl.cpp
+++ b/clang/lib/CodeGen/CGDecl.cpp
@@ -356,24 +356,10 @@ llvm::Constant *CodeGenModule::getOrCreateStaticVarDecl(
llvm::GlobalVariable *
CodeGenFunction::AddInitializerToStaticVarDecl(const VarDecl &D,
llvm::GlobalVariable *GV) {
- // A static local with vague linkage (e.g. one in an inline function) is
- // shared by every translation unit that emits it, but only a constant
- // initializer is guaranteed to be emitted as a constant in all of them. If
- // we were to fold an initializer that another translation unit can't fold
- // (say, because it reads a variable whose initializer is only visible here),
- // that translation unit would perform a guarded initialization which could
- // store to our copy of the variable (possibly placed in read-only memory) or
- // race with our unguarded reads of it. Use a guarded initialization here as
- // well, so that every translation unit agrees on how the variable is
- // initialized.
- bool NeedsGuardedInit =
- getLangOpts().CPlusPlus && !getLangOpts().CUDAIsDevice &&
- GV->isWeakForLinker() && !D.hasConstantInitialization() &&
- !D.hasFlexibleArrayInit(getContext());
-
ConstantEmitter emitter(*this);
- llvm::Constant *Init =
- NeedsGuardedInit ? nullptr : emitter.tryEmitForInitializer(D);
+ llvm::Constant *Init = CGM.mustDynamicallyInitialize(D)
+ ? nullptr
+ : emitter.tryEmitForInitializer(D);
// If constant emission failed, then this should be a C++ static
// initializer.
diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp
index 7274a8588670f4..b4c907afc07140 100644
--- a/clang/lib/CodeGen/CodeGenModule.cpp
+++ b/clang/lib/CodeGen/CodeGenModule.cpp
@@ -6649,8 +6649,11 @@ void CodeGenModule::EmitGlobalVarDefinition(const VarDecl *D,
Init = EmitNullConstant(D->getType());
} else {
initializedGlobalDecl = GlobalDecl(D);
- emitter.emplace(*this);
- llvm::Constant *Initializer = emitter->tryEmitForInitializer(*InitDecl);
+ llvm::Constant *Initializer = nullptr;
+ if (!mustDynamicallyInitialize(*D)) {
+ emitter.emplace(*this);
+ Initializer = emitter->tryEmitForInitializer(*InitDecl);
+ }
if (!Initializer) {
QualType T = InitExpr->getType();
if (D->getType()->isReferenceType())
@@ -7013,6 +7016,28 @@ CodeGenModule::getLLVMLinkageVarDefinition(const VarDecl *VD) {
return getLLVMLinkageForDeclarator(VD, Linkage);
}
+bool CodeGenModule::mustDynamicallyInitialize(const VarDecl &D) {
+ // A variable with vague linkage (an inline variable, a static data member of
+ // a class template, a static local of an inline function, ...) is shared by
+ // every translation unit that emits it, but only a constant initializer is
+ // guaranteed to be constant-folded in all of them. If we were to fold an
+ // initializer that another translation unit can't fold (say, because it
+ // reads a variable whose initializer is only visible here), that translation
+ // unit would initialize the variable dynamically, and could store to our
+ // copy of it (possibly placed in read-only memory) or race with our
+ // unguarded reads of it. Initialize it dynamically here as well, so that
+ // every translation unit agrees on how the variable is initialized.
+ if (!getLangOpts().CPlusPlus || getLangOpts().CUDAIsDevice)
+ return false;
+
+ const VarDecl *InitDecl;
+ if (!D.getAnyInitializer(InitDecl) || InitDecl->hasConstantInitialization() ||
+ InitDecl->hasFlexibleArrayInit(getContext()))
+ return false;
+
+ return llvm::GlobalValue::isWeakForLinker(getLLVMLinkageVarDefinition(&D));
+}
+
/// Replace the uses of a function that was declared with a non-proto type.
/// We want to silently drop extra arguments from call sites
static void replaceUsesOfNonProtoConstant(llvm::Constant *old,
diff --git a/clang/lib/CodeGen/CodeGenModule.h b/clang/lib/CodeGen/CodeGenModule.h
index 9e3f073c20f4a0..b6eb86b903f1ce 100644
--- a/clang/lib/CodeGen/CodeGenModule.h
+++ b/clang/lib/CodeGen/CodeGenModule.h
@@ -1583,6 +1583,10 @@ class CodeGenModule : public CodeGenTypeCache {
llvm::GlobalValue::LinkageTypes
getLLVMLinkageVarDefinition(const VarDecl *VD);
+ /// Returns true if the initializer of \p D must not be constant-folded,
+ /// because other translation units might initialize it dynamically.
+ bool mustDynamicallyInitialize(const VarDecl &D);
+
/// Emit all the global annotations.
void EmitGlobalAnnotations();
diff --git a/clang/test/CodeGenCXX/static-local-inline-non-constant-init.cpp b/clang/test/CodeGenCXX/static-local-inline-non-constant-init.cpp
deleted file mode 100644
index ec78d2d90e3600..00000000000000
--- a/clang/test/CodeGenCXX/static-local-inline-non-constant-init.cpp
+++ /dev/null
@@ -1,77 +0,0 @@
-// RUN: %clang_cc1 -triple x86_64-linux-gnu -std=c++17 -emit-llvm -o - %s | FileCheck %s --check-prefix=ITANIUM
-// RUN: %clang_cc1 -triple x86_64-windows-msvc -std=c++17 -emit-llvm -o - %s | FileCheck %s --check-prefix=MSVC
-
-// A static local in an inline function is shared by every translation unit
-// that uses the function. If its initializer isn't a constant initializer,
-// another translation unit might not be able to fold it (here, one that can't
-// see the initializer of kSize) and will initialize the variable dynamically,
-// behind a guard variable. So it must not be emitted as a (possibly read-only)
-// constant without a guard in this translation unit either.
-// See https://github.com/llvm/llvm-project/issues/226631.
-
-extern const int kSize;
-
-inline const int &instance() {
- static const int meta = kSize;
- return meta;
-}
-
-// Constant initializers are still emitted as constants, without a guard.
-inline const int &constant() {
- static const int c = 42;
- return c;
-}
-
-// A static local that isn't shared with other translation units can still be
-// folded, even if its initializer isn't a constant initializer.
-static const int &internal() {
- static const int i = kSize;
- return i;
-}
-
-extern const int kSize = 3;
-
-const int *a() { return &instance(); }
-const int *b() { return &constant(); }
-const int *c() { return &internal(); }
-
-// ITANIUM-DAG: @_ZZ8instancevE4meta = linkonce_odr global i32 0, comdat, align 4
-// ITANIUM-DAG: @_ZGVZ8instancevE4meta = linkonce_odr global i64 0, comdat, align 8
-// ITANIUM-DAG: @_ZZ8constantvE1c = linkonce_odr constant i32 42, comdat, align 4
-// ITANIUM-DAG: @_ZZL8internalvE1i = internal constant i32 3, align 4
-// ITANIUM-NOT: @_ZGVZ8constantvE1c
-// ITANIUM-NOT: @_ZGVZL8internalvE1i
-
-// ITANIUM-LABEL: define linkonce_odr {{.*}} ptr @_Z8instancev()
-// ITANIUM: load atomic i8, ptr @_ZGVZ8instancevE4meta acquire
-// ITANIUM: call i32 @__cxa_guard_acquire(ptr @_ZGVZ8instancevE4meta)
-// ITANIUM: store i32 3, ptr @_ZZ8instancevE4meta
-// ITANIUM: call void @__cxa_guard_release(ptr @_ZGVZ8instancevE4meta)
-// ITANIUM: ret ptr @_ZZ8instancevE4meta
-
-// ITANIUM-LABEL: define linkonce_odr {{.*}} ptr @_Z8constantv()
-// ITANIUM-NEXT: entry:
-// ITANIUM-NEXT: ret ptr @_ZZ8constantvE1c
-
-// ITANIUM-LABEL: define internal {{.*}} ptr @_ZL8internalv()
-// ITANIUM-NEXT: entry:
-// ITANIUM-NEXT: ret ptr @_ZZL8internalvE1i
-
-// MSVC-DAG: @"?meta@?1??instance@@YAAEBHXZ at 4HB" = linkonce_odr dso_local global i32 0, comdat, align 4
-// MSVC-DAG: @"?$TSS0@?1??instance@@YAAEBHXZ at 4HA" = linkonce_odr global i32 0, comdat, align 4
-// MSVC-DAG: @"?c@?1??constant@@YAAEBHXZ at 4HB" = linkonce_odr dso_local constant i32 42, comdat, align 4
-// MSVC-DAG: @"?i@?1??internal@@YAAEBHXZ at 4HB" = internal constant i32 3, align 4
-
-// MSVC-LABEL: define linkonce_odr {{.*}} ptr @"?instance@@YAAEBHXZ"()
-// MSVC: call void @_Init_thread_header(ptr @"?$TSS0@?1??instance@@YAAEBHXZ at 4HA")
-// MSVC: store i32 3, ptr @"?meta@?1??instance@@YAAEBHXZ at 4HB"
-// MSVC: call void @_Init_thread_footer(ptr @"?$TSS0@?1??instance@@YAAEBHXZ at 4HA")
-// MSVC: ret ptr @"?meta@?1??instance@@YAAEBHXZ at 4HB"
-
-// MSVC-LABEL: define linkonce_odr {{.*}} ptr @"?constant@@YAAEBHXZ"()
-// MSVC-NEXT: entry:
-// MSVC-NEXT: ret ptr @"?c@?1??constant@@YAAEBHXZ at 4HB"
-
-// MSVC-LABEL: define internal {{.*}} ptr @"?internal@@YAAEBHXZ"()
-// MSVC-NEXT: entry:
-// MSVC-NEXT: ret ptr @"?i@?1??internal@@YAAEBHXZ at 4HB"
diff --git a/clang/test/CodeGenCXX/vague-linkage-non-constant-init.cpp b/clang/test/CodeGenCXX/vague-linkage-non-constant-init.cpp
new file mode 100644
index 00000000000000..bee88541b5c94d
--- /dev/null
+++ b/clang/test/CodeGenCXX/vague-linkage-non-constant-init.cpp
@@ -0,0 +1,132 @@
+// RUN: %clang_cc1 -triple x86_64-linux-gnu -std=c++17 -emit-llvm -o - %s | FileCheck %s --check-prefix=ITANIUM
+// RUN: %clang_cc1 -triple x86_64-windows-msvc -std=c++17 -emit-llvm -o - %s | FileCheck %s --check-prefix=MSVC
+
+// A variable with vague linkage (a static local of an inline function, an
+// inline variable, a static data member of a class template) is shared by
+// every translation unit that uses it. If its initializer isn't a constant
+// initializer, another translation unit might not be able to fold it (here, one
+// that can't see the initializer of kSize) and will initialize the variable
+// dynamically. So it must not be emitted as a (possibly read-only) constant
+// without a guard in this translation unit either.
+// See https://github.com/llvm/llvm-project/issues/226631.
+
+extern const int kSize;
+
+inline const int &instance() {
+ static const int meta = kSize;
+ return meta;
+}
+
+// Constant initializers are still emitted as constants, without a guard.
+inline const int &constant() {
+ static const int c = 42;
+ return c;
+}
+
+// A static local that isn't shared with other translation units can still be
+// folded, even if its initializer isn't a constant initializer.
+static const int &internal() {
+ static const int i = kSize;
+ return i;
+}
+
+inline const int iv = kSize;
+inline int mv = kSize;
+inline const int ic = 42;
+
+template <class T> struct S { static const int x; };
+template <class T> const int S<T>::x = kSize;
+
+static const int sv = kSize;
+
+const int *d() { return &iv; }
+int *e() { return &mv; }
+const int *f() { return ⁣ }
+const int *g() { return &S<int>::x; }
+const int *h() { return &sv; }
+
+extern const int kSize = 3;
+
+const int *a() { return &instance(); }
+const int *b() { return &constant(); }
+const int *c() { return &internal(); }
+
+// ITANIUM-DAG: @_ZZ8instancevE4meta = linkonce_odr global i32 0, comdat, align 4
+// ITANIUM-DAG: @_ZGVZ8instancevE4meta = linkonce_odr global i64 0, comdat, align 8
+// ITANIUM-DAG: @_ZZ8constantvE1c = linkonce_odr constant i32 42, comdat, align 4
+// ITANIUM-DAG: @_ZZL8internalvE1i = internal constant i32 3, align 4
+// ITANIUM-DAG: @iv = linkonce_odr global i32 0, comdat, align 4
+// ITANIUM-DAG: @_ZGV2iv = linkonce_odr global i64 0, comdat($iv), align 8
+// ITANIUM-DAG: @mv = linkonce_odr global i32 0, comdat, align 4
+// ITANIUM-DAG: @_ZGV2mv = linkonce_odr global i64 0, comdat($mv), align 8
+// ITANIUM-DAG: @ic = linkonce_odr constant i32 42, comdat, align 4
+// ITANIUM-DAG: @_ZN1SIiE1xE = linkonce_odr global i32 0, comdat, align 4
+// ITANIUM-DAG: @_ZGVN1SIiE1xE = linkonce_odr global i64 0, comdat($_ZN1SIiE1xE), align 8
+// ITANIUM-DAG: @_ZL2sv = internal constant i32 3, align 4
+// ITANIUM-NOT: @_ZGVZ8constantvE1c
+// ITANIUM-NOT: @_ZGVZL8internalvE1i
+// ITANIUM-NOT: @_ZGV2ic
+
+// ITANIUM-LABEL: define linkonce_odr {{.*}} ptr @_Z8instancev()
+// ITANIUM: load atomic i8, ptr @_ZGVZ8instancevE4meta acquire
+// ITANIUM: call i32 @__cxa_guard_acquire(ptr @_ZGVZ8instancevE4meta)
+// ITANIUM: store i32 3, ptr @_ZZ8instancevE4meta
+// ITANIUM: call void @__cxa_guard_release(ptr @_ZGVZ8instancevE4meta)
+// ITANIUM: ret ptr @_ZZ8instancevE4meta
+
+// ITANIUM-LABEL: define linkonce_odr {{.*}} ptr @_Z8constantv()
+// ITANIUM-NEXT: entry:
+// ITANIUM-NEXT: ret ptr @_ZZ8constantvE1c
+
+// ITANIUM-LABEL: define internal {{.*}} ptr @_ZL8internalv()
+// ITANIUM-NEXT: entry:
+// ITANIUM-NEXT: ret ptr @_ZZL8internalvE1i
+
+// ITANIUM-LABEL: define internal void @__cxx_global_var_init() {{.*}} comdat($iv)
+// ITANIUM: call i32 @__cxa_guard_acquire(ptr @_ZGV2iv)
+// ITANIUM: store i32 3, ptr @iv
+// ITANIUM: call void @__cxa_guard_release(ptr @_ZGV2iv)
+
+// ITANIUM-LABEL: define internal void @__cxx_global_var_init.1() {{.*}} comdat($mv)
+// ITANIUM: call i32 @__cxa_guard_acquire(ptr @_ZGV2mv)
+// ITANIUM: store i32 3, ptr @mv
+// ITANIUM: call void @__cxa_guard_release(ptr @_ZGV2mv)
+
+// ITANIUM-LABEL: define internal void @__cxx_global_var_init.2() {{.*}} comdat($_ZN1SIiE1xE)
+// ITANIUM: load i8, ptr @_ZGVN1SIiE1xE
+// ITANIUM: store i8 1, ptr @_ZGVN1SIiE1xE
+// ITANIUM: store i32 3, ptr @_ZN1SIiE1xE
+
+// MSVC-DAG: @"?meta@?1??instance@@YAAEBHXZ at 4HB" = linkonce_odr dso_local global i32 0, comdat, align 4
+// MSVC-DAG: @"?$TSS0@?1??instance@@YAAEBHXZ at 4HA" = linkonce_odr global i32 0, comdat, align 4
+// MSVC-DAG: @"?c@?1??constant@@YAAEBHXZ at 4HB" = linkonce_odr dso_local constant i32 42, comdat, align 4
+// MSVC-DAG: @"?i@?1??internal@@YAAEBHXZ at 4HB" = internal constant i32 3, align 4
+// MSVC-DAG: @"?iv@@3HB" = linkonce_odr dso_local global i32 0, comdat, align 4
+// MSVC-DAG: @"?mv@@3HA" = linkonce_odr dso_local global i32 0, comdat, align 4
+// MSVC-DAG: @"?ic@@3HB" = linkonce_odr dso_local constant i32 42, comdat, align 4
+// MSVC-DAG: @"?x@?$S at H@@2HB" = linkonce_odr dso_local global i32 0, comdat, align 4
+// MSVC-DAG: @sv = internal constant i32 3, align 4
+// MSVC-DAG: @llvm.global_ctors = appending global [3 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 65535, ptr @"??__Eiv@@YAXXZ", ptr @"?iv@@3HB" }, { i32, ptr, ptr } { i32 65535, ptr @"??__Emv@@YAXXZ", ptr @"?mv@@3HA" }, { i32, ptr, ptr } { i32 65535, ptr @"??__E?x@?$S at H@@2HB@@YAXXZ", ptr @"?x@?$S at H@@2HB" }]
+
+// MSVC-LABEL: define linkonce_odr {{.*}} ptr @"?instance@@YAAEBHXZ"()
+// MSVC: call void @_Init_thread_header(ptr @"?$TSS0@?1??instance@@YAAEBHXZ at 4HA")
+// MSVC: store i32 3, ptr @"?meta@?1??instance@@YAAEBHXZ at 4HB"
+// MSVC: call void @_Init_thread_footer(ptr @"?$TSS0@?1??instance@@YAAEBHXZ at 4HA")
+// MSVC: ret ptr @"?meta@?1??instance@@YAAEBHXZ at 4HB"
+
+// MSVC-LABEL: define linkonce_odr {{.*}} ptr @"?constant@@YAAEBHXZ"()
+// MSVC-NEXT: entry:
+// MSVC-NEXT: ret ptr @"?c@?1??constant@@YAAEBHXZ at 4HB"
+
+// MSVC-LABEL: define internal {{.*}} ptr @"?internal@@YAAEBHXZ"()
+// MSVC-NEXT: entry:
+// MSVC-NEXT: ret ptr @"?i@?1??internal@@YAAEBHXZ at 4HB"
+
+// MSVC-LABEL: define linkonce_odr dso_local void @"??__Eiv@@YAXXZ"()
+// MSVC: store i32 3, ptr @"?iv@@3HB"
+
+// MSVC-LABEL: define linkonce_odr dso_local void @"??__Emv@@YAXXZ"()
+// MSVC: store i32 3, ptr @"?mv@@3HA"
+
+// MSVC-LABEL: define linkonce_odr dso_local void @"??__E?x@?$S at H@@2HB@@YAXXZ"()
+// MSVC: store i32 3, ptr @"?x@?$S at H@@2HB"
More information about the cfe-commits
mailing list