[clang] 13dd65b - Do not create LLVM IR `constant`s for objects with dynamic initialisation
Momchil Velikov via cfe-commits
cfe-commits at lists.llvm.org
Mon May 24 14:05:34 PDT 2021
Author: Momchil Velikov
Date: 2021-05-24T22:04:15+01:00
New Revision: 13dd65b3a1a3ac049b5f3a9712059f7c61649bea
URL: https://github.com/llvm/llvm-project/commit/13dd65b3a1a3ac049b5f3a9712059f7c61649bea
DIFF: https://github.com/llvm/llvm-project/commit/13dd65b3a1a3ac049b5f3a9712059f7c61649bea.diff
LOG: Do not create LLVM IR `constant`s for objects with dynamic initialisation
When a const-qualified object has a section attribute, that
section is set to read-only and clang outputs a LLVM IR constant
for that object. This is incorrect for dynamically initialised
objects.
For example:
int init() { return 15; }
__attribute__((section("SA")))
const int a = init();
a is allocated to a read-only section and is left
unintialised (zero-initialised).
This patch adds checks if an initialiser is a constant expression
and allocates objects to sections as follows:
* const-qualified objects
- no initialiser or constant initialiser: .rodata
- dynamic initializer: .bss
* non const-qualified objects
- no initialiser or dynamic initialiser: .bss
- constant initialiser: .data
(".rodata", ".data", and ".bss" names used just for explanatory
purpose)
Differential Revision: https://reviews.llvm.org/D102693
Added:
clang/test/CodeGenCXX/clang-sections-1.cpp
clang/test/CodeGenCXX/const-dynamic-init.cpp
Modified:
clang/lib/Sema/SemaDecl.cpp
Removed:
################################################################################
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 601f4f2502f0..e08e8d8346c0 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -13047,43 +13047,6 @@ void Sema::CheckCompleteVariableDeclaration(VarDecl *var) {
}
}
- // Apply section attributes and pragmas to global variables.
- bool GlobalStorage = var->hasGlobalStorage();
- if (GlobalStorage && var->isThisDeclarationADefinition() &&
- !inTemplateInstantiation()) {
- PragmaStack<StringLiteral *> *Stack = nullptr;
- int SectionFlags = ASTContext::PSF_Read;
- if (var->getType().isConstQualified())
- Stack = &ConstSegStack;
- else if (!var->getInit()) {
- Stack = &BSSSegStack;
- SectionFlags |= ASTContext::PSF_Write;
- } else {
- Stack = &DataSegStack;
- SectionFlags |= ASTContext::PSF_Write;
- }
- if (const SectionAttr *SA = var->getAttr<SectionAttr>()) {
- if (SA->getSyntax() == AttributeCommonInfo::AS_Declspec)
- SectionFlags |= ASTContext::PSF_Implicit;
- UnifySection(SA->getName(), SectionFlags, var);
- } else if (Stack->CurrentValue) {
- SectionFlags |= ASTContext::PSF_Implicit;
- auto SectionName = Stack->CurrentValue->getString();
- var->addAttr(SectionAttr::CreateImplicit(
- Context, SectionName, Stack->CurrentPragmaLocation,
- AttributeCommonInfo::AS_Pragma, SectionAttr::Declspec_allocate));
- if (UnifySection(SectionName, SectionFlags, var))
- var->dropAttr<SectionAttr>();
- }
-
- // Apply the init_seg attribute if this has an initializer. If the
- // initializer turns out to not be dynamic, we'll end up ignoring this
- // attribute.
- if (CurInitSeg && var->getInit())
- var->addAttr(InitSegAttr::CreateImplicit(Context, CurInitSeg->getString(),
- CurInitSegLoc,
- AttributeCommonInfo::AS_Pragma));
- }
if (!var->getType()->isStructureType() && var->hasInit() &&
isa<InitListExpr>(var->getInit())) {
@@ -13133,14 +13096,6 @@ void Sema::CheckCompleteVariableDeclaration(VarDecl *var) {
}
}
- // All the following checks are C++ only.
- if (!getLangOpts().CPlusPlus) {
- // If this variable must be emitted, add it as an initializer for the
- // current module.
- if (Context.DeclMustBeEmitted(var) && !ModuleScopes.empty())
- Context.addModuleInitializer(ModuleScopes.back().Module, var);
- return;
- }
QualType type = var->getType();
@@ -13148,11 +13103,14 @@ void Sema::CheckCompleteVariableDeclaration(VarDecl *var) {
getCurFunction()->addByrefBlockVar(var);
Expr *Init = var->getInit();
+ bool GlobalStorage = var->hasGlobalStorage();
bool IsGlobal = GlobalStorage && !var->isStaticLocal();
QualType baseType = Context.getBaseElementType(type);
+ bool HasConstInit = true;
// Check whether the initializer is sufficiently constant.
- if (!type->isDependentType() && Init && !Init->isValueDependent() &&
+ if (getLangOpts().CPlusPlus && !type->isDependentType() && Init &&
+ !Init->isValueDependent() &&
(GlobalStorage || var->isConstexpr() ||
var->mightBeUsableInConstantExpressions(Context))) {
// If this variable might have a constant initializer or might be usable in
@@ -13160,7 +13118,6 @@ void Sema::CheckCompleteVariableDeclaration(VarDecl *var) {
// do this lazily, because the result might depend on things that change
// later, such as which constexpr functions happen to be defined.
SmallVector<PartialDiagnosticAt, 8> Notes;
- bool HasConstInit;
if (!getLangOpts().CPlusPlus11) {
// Prior to C++11, in contexts where a constant initializer is required,
// the set of valid constant initializers is described by syntactic rules
@@ -13225,6 +13182,57 @@ void Sema::CheckCompleteVariableDeclaration(VarDecl *var) {
}
}
+ // Apply section attributes and pragmas to global variables.
+ if (GlobalStorage && var->isThisDeclarationADefinition() &&
+ !inTemplateInstantiation()) {
+ PragmaStack<StringLiteral *> *Stack = nullptr;
+ int SectionFlags = ASTContext::PSF_Read;
+ if (var->getType().isConstQualified()) {
+ if (HasConstInit)
+ Stack = &ConstSegStack;
+ else {
+ Stack = &BSSSegStack;
+ SectionFlags |= ASTContext::PSF_Write;
+ }
+ } else if (var->hasInit() && HasConstInit) {
+ Stack = &DataSegStack;
+ SectionFlags |= ASTContext::PSF_Write;
+ } else {
+ Stack = &BSSSegStack;
+ SectionFlags |= ASTContext::PSF_Write;
+ }
+ if (const SectionAttr *SA = var->getAttr<SectionAttr>()) {
+ if (SA->getSyntax() == AttributeCommonInfo::AS_Declspec)
+ SectionFlags |= ASTContext::PSF_Implicit;
+ UnifySection(SA->getName(), SectionFlags, var);
+ } else if (Stack->CurrentValue) {
+ SectionFlags |= ASTContext::PSF_Implicit;
+ auto SectionName = Stack->CurrentValue->getString();
+ var->addAttr(SectionAttr::CreateImplicit(
+ Context, SectionName, Stack->CurrentPragmaLocation,
+ AttributeCommonInfo::AS_Pragma, SectionAttr::Declspec_allocate));
+ if (UnifySection(SectionName, SectionFlags, var))
+ var->dropAttr<SectionAttr>();
+ }
+
+ // Apply the init_seg attribute if this has an initializer. If the
+ // initializer turns out to not be dynamic, we'll end up ignoring this
+ // attribute.
+ if (CurInitSeg && var->getInit())
+ var->addAttr(InitSegAttr::CreateImplicit(Context, CurInitSeg->getString(),
+ CurInitSegLoc,
+ AttributeCommonInfo::AS_Pragma));
+ }
+
+ // All the following checks are C++ only.
+ if (!getLangOpts().CPlusPlus) {
+ // If this variable must be emitted, add it as an initializer for the
+ // current module.
+ if (Context.DeclMustBeEmitted(var) && !ModuleScopes.empty())
+ Context.addModuleInitializer(ModuleScopes.back().Module, var);
+ return;
+ }
+
// Require the destructor.
if (!type->isDependentType())
if (const RecordType *recordType = baseType->getAs<RecordType>())
diff --git a/clang/test/CodeGenCXX/clang-sections-1.cpp b/clang/test/CodeGenCXX/clang-sections-1.cpp
new file mode 100644
index 000000000000..5dbfb0afac22
--- /dev/null
+++ b/clang/test/CodeGenCXX/clang-sections-1.cpp
@@ -0,0 +1,50 @@
+// RUN: %clang_cc1 -triple x86_64-linux -emit-llvm -o - %s | FileCheck %s --check-prefix=LLVM
+// RUN: %clang_cc1 -triple x86_64-linux -S -o - %s | FileCheck %s --check-prefix=ASM
+// Actually, any ELF target would do
+// REQUIRES: x86_64-linux
+
+#pragma clang section bss = "B$$" data = "d at t@" rodata = "r0d at t@"
+
+const int a = 1;
+const int *f() { return &a; }
+
+int init();
+const int b = init();
+
+int c = 2;
+
+int d = init();
+
+int e;
+
+// LLVM: @_ZL1a = internal constant i32 1, align 4 #[[#A:]]
+// LLVM: @_ZL1b = internal global i32 0, align 4 #[[#A]]
+// LLVM: @c = {{.*}}global i32 2, align 4 #[[#A]]
+// LLVM: @d = {{.*}}global i32 0, align 4 #[[#A]]
+// LLVM: @e = {{.*}}global i32 0, align 4 #[[#A]]
+
+// LLVM: attributes #[[#A]] = { "bss-section"="B$$" "data-section"="d at t@" "rodata-section"="r0d at t@" }
+
+// ASM: .section "r0d at t@","a", at progbits
+// ASM-NOT: .section
+// ASM-LABEL: _ZL1a:
+// ASM-NEXT: .long 1
+
+// ASM: .section "B$$","aw", at nobits
+// ASM-NOT: .section
+// ASM-LABEL: _ZL1b:
+// ASM-NEXT: .long 0
+
+// ASM: .section "d at t@","aw", at progbits
+// ASM-NOT: .section
+// ASM-LABEL: c:
+// ASM: .long 2
+
+// ASM: .section "B$$","aw", at nobits
+// ASM-NOT: .section
+// ASM-LABEL: d:
+// ASM: .long 0
+
+// ASM-NOT: .section
+// ASM-LABEL: e:
+// ASM .long 0
diff --git a/clang/test/CodeGenCXX/const-dynamic-init.cpp b/clang/test/CodeGenCXX/const-dynamic-init.cpp
new file mode 100644
index 000000000000..fcec343a43d2
--- /dev/null
+++ b/clang/test/CodeGenCXX/const-dynamic-init.cpp
@@ -0,0 +1,25 @@
+// RUN: %clang_cc1 -triple %itanium_abi_triple -emit-llvm -o - %s | FileCheck %s
+
+__attribute__((section("A")))
+const int a = 1;
+const int *f() { return &a; }
+// CHECK: @_ZL1a = internal constant i32 1, section "A"
+
+int init();
+__attribute__((section("B")))
+const int b = init();
+// Even if it's const-qualified, it must not be LLVM IR `constant` since it's
+// dynamically initialised.
+// CHECK: @_ZL1b = internal global i32 0, section "B"
+
+__attribute__((section("C")))
+int c = 2;
+// CHECK: @c = {{.*}}global i32 2, section "C"
+
+__attribute__((section("D")))
+int d = init();
+// CHECK: @d = {{.*}}global i32 0, section "D"
+
+__attribute__((section("E")))
+int e;
+// CHECK: @e = {{.*}}global i32 0, section "E", align 4
More information about the cfe-commits
mailing list