Skip to content

Commit c86899d

Browse files
authored
[clang] Add support for __declspec(no_init_all) (#116847)
In MSVC, when `/d1initall` is enabled, `__declspec(no_init_all)` can be applied to a type to suppress auto-initialization for all instances of that type or to a function to suppress auto-initialization for all locals within that function. This change does the same for Clang, except that it applies to the `-ftrivial-auto-var-init` flag instead. NOTE: I did not add a Clang-specific spelling for this but would be happy to make a followup PR if folks are interested in that.
1 parent 905e831 commit c86899d

File tree

4 files changed

+90
-10
lines changed

4 files changed

+90
-10
lines changed

clang/include/clang/Basic/Attr.td

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4888,3 +4888,10 @@ def ClspvLibclcBuiltin: InheritableAttr {
48884888
let Documentation = [ClspvLibclcBuiltinDoc];
48894889
let SimpleHandler = 1;
48904890
}
4891+
4892+
def NoTrivialAutoVarInit: InheritableAttr {
4893+
let Spellings = [Declspec<"no_init_all">];
4894+
let Subjects = SubjectList<[Function, Tag]>;
4895+
let Documentation = [NoTrivialAutoVarInitDocs];
4896+
let SimpleHandler = 1;
4897+
}

clang/include/clang/Basic/AttrDocs.td

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8760,6 +8760,18 @@ Attribute used by `clspv`_ (OpenCL-C to Vulkan SPIR-V compiler) to identify func
87608760
}];
87618761
}
87628762

8763+
def NoTrivialAutoVarInitDocs : Documentation {
8764+
let Category = DocCatDecl;
8765+
let Content = [{
8766+
The ``__declspec(no_init_all)`` attribute disables the automatic initialization that the
8767+
`-ftrivial-auto-var-init`_ flag would have applied to locals in a marked function, or instances of
8768+
a marked type. Note that this attribute has no effect for locals that are automatically initialized
8769+
without the `-ftrivial-auto-var-init`_ flag.
8770+
8771+
.. _`-ftrivial-auto-var-init`: ClangCommandLineReference.html#cmdoption-clang-ftrivial-auto-var-init
8772+
}];
8773+
}
8774+
87638775
def DocCatNonBlockingNonAllocating : DocumentationCategory<"Performance Constraint Attributes"> {
87648776
let Content = [{
87658777
The ``nonblocking``, ``blocking``, ``nonallocating`` and ``allocating`` attributes can be attached

clang/lib/CodeGen/CGDecl.cpp

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1899,13 +1899,16 @@ void CodeGenFunction::EmitAutoVarInit(const AutoVarEmission &emission) {
18991899
const Address Loc =
19001900
locIsByrefHeader ? emission.getObjectAddress(*this) : emission.Addr;
19011901

1902+
auto hasNoTrivialAutoVarInitAttr = [&](const Decl *D) {
1903+
return D && D->hasAttr<NoTrivialAutoVarInitAttr>();
1904+
};
19021905
// Note: constexpr already initializes everything correctly.
19031906
LangOptions::TrivialAutoVarInitKind trivialAutoVarInit =
1904-
(D.isConstexpr()
1907+
((D.isConstexpr() || D.getAttr<UninitializedAttr>() ||
1908+
hasNoTrivialAutoVarInitAttr(type->getAsTagDecl()) ||
1909+
hasNoTrivialAutoVarInitAttr(CurFuncDecl))
19051910
? LangOptions::TrivialAutoVarInitKind::Uninitialized
1906-
: (D.getAttr<UninitializedAttr>()
1907-
? LangOptions::TrivialAutoVarInitKind::Uninitialized
1908-
: getContext().getLangOpts().getTrivialAutoVarInit()));
1911+
: getContext().getLangOpts().getTrivialAutoVarInit());
19091912

19101913
auto initializeWhatIsTechnicallyUninitialized = [&](Address Loc) {
19111914
if (trivialAutoVarInit ==
@@ -1944,13 +1947,13 @@ void CodeGenFunction::EmitAutoVarInit(const AutoVarEmission &emission) {
19441947
replaceUndef(CGM, isPattern, constant));
19451948
}
19461949

1947-
if (constant && D.getType()->isBitIntType() &&
1948-
CGM.getTypes().typeRequiresSplitIntoByteArray(D.getType())) {
1950+
if (constant && type->isBitIntType() &&
1951+
CGM.getTypes().typeRequiresSplitIntoByteArray(type)) {
19491952
// Constants for long _BitInt types are split into individual bytes.
19501953
// Try to fold these back into an integer constant so it can be stored
19511954
// properly.
1952-
llvm::Type *LoadType = CGM.getTypes().convertTypeForLoadStore(
1953-
D.getType(), constant->getType());
1955+
llvm::Type *LoadType =
1956+
CGM.getTypes().convertTypeForLoadStore(type, constant->getType());
19541957
constant = llvm::ConstantFoldLoadFromConst(
19551958
constant, LoadType, llvm::APInt::getZero(32), CGM.getDataLayout());
19561959
}
@@ -1967,8 +1970,7 @@ void CodeGenFunction::EmitAutoVarInit(const AutoVarEmission &emission) {
19671970
// It may be that the Init expression uses other uninitialized memory,
19681971
// but auto-var-init here would not help, as auto-init would get
19691972
// overwritten by Init.
1970-
if (!D.getType()->isScalarType() || capturedByInit ||
1971-
isAccessedBy(D, Init)) {
1973+
if (!type->isScalarType() || capturedByInit || isAccessedBy(D, Init)) {
19721974
initializeWhatIsTechnicallyUninitialized(Loc);
19731975
}
19741976
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
// RUN: %clang_cc1 -std=c++14 -triple x86_64-unknown-unknown -fblocks -fdeclspec -ftrivial-auto-var-init=zero %s -emit-llvm -o - | FileCheck %s
2+
3+
struct S { char c; };
4+
class C { char c; };
5+
enum class E { ZERO };
6+
union U { char c; int i; };
7+
8+
struct __declspec(no_init_all) NoInitS { char c; };
9+
class __declspec(no_init_all) NoInitC { char c; };
10+
enum class __declspec(no_init_all) NoInitE { ZERO };
11+
union __declspec(no_init_all) NoInitU { char c; int i; };
12+
13+
extern "C" {
14+
void test_no_attr() {
15+
// CHECK-LABEL: @test_no_attr()
16+
// CHECK-NEXT: entry:
17+
// CHECK-NEXT: %s = alloca %struct.S, align 1
18+
// CHECK-NEXT: %c = alloca %class.C, align 1
19+
// CHECK-NEXT: %e = alloca i32, align 4
20+
// CHECK-NEXT: %u = alloca %union.U, align 4
21+
// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 1 %s, i8 0, i64 1, i1 false)
22+
// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 1 %c, i8 0, i64 1, i1 false)
23+
// CHECK-NEXT: store i32 0, ptr %e, align 4
24+
// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 %u, i8 0, i64 4, i1 false)
25+
// CHECK-NEXT ret void
26+
S s;
27+
C c;
28+
E e;
29+
U u;
30+
}
31+
32+
void __declspec(no_init_all) test_attr_on_function() {
33+
// CHECK-LABEL: @test_attr_on_function()
34+
// CHECK-NEXT: entry:
35+
// CHECK-NEXT: %s = alloca %struct.S, align 1
36+
// CHECK-NEXT: %c = alloca %class.C, align 1
37+
// CHECK-NEXT: %e = alloca i32, align 4
38+
// CHECK-NEXT: %u = alloca %union.U, align 4
39+
// CHECK-NEXT: ret void
40+
S s;
41+
C c;
42+
E e;
43+
U u;
44+
}
45+
46+
void test_attr_on_decl() {
47+
// CHECK-LABEL: @test_attr_on_decl()
48+
// CHECK-NEXT: entry:
49+
// CHECK-NEXT: %s = alloca %struct.NoInitS, align 1
50+
// CHECK-NEXT: %c = alloca %class.NoInitC, align 1
51+
// CHECK-NEXT: %e = alloca i32, align 4
52+
// CHECK-NEXT: %u = alloca %union.NoInitU, align 4
53+
// CHECK-NEXT: ret void
54+
NoInitS s;
55+
NoInitC c;
56+
NoInitE e;
57+
NoInitU u;
58+
}
59+
}

0 commit comments

Comments
 (0)