diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -3430,6 +3430,12 @@ let Documentation = [UninitializedDocs]; } +def NoZeroInitializer : InheritableAttr { + let Spellings = [Clang<"no_zero_initializer">]; + let Subjects = SubjectList<[GlobalVar]>; + let Documentation = [Undocumented]; +} + def ObjCExternallyRetained : InheritableAttr { let LangOpts = [ObjCAutoRefCount]; let Spellings = [Clang<"objc_externally_retained">]; diff --git a/clang/lib/AST/DeclBase.cpp b/clang/lib/AST/DeclBase.cpp --- a/clang/lib/AST/DeclBase.cpp +++ b/clang/lib/AST/DeclBase.cpp @@ -454,7 +454,8 @@ } bool Decl::hasDefiningAttr() const { - return hasAttr() || hasAttr(); + return hasAttr() || hasAttr() || + hasAttr(); } const Attr *Decl::getDefiningAttr() const { @@ -462,6 +463,8 @@ return AA; if (auto *IFA = getAttr()) return IFA; + if (auto *NZA = getAttr()) + return NZA; return nullptr; } diff --git a/clang/lib/CodeGen/CGDecl.cpp b/clang/lib/CodeGen/CGDecl.cpp --- a/clang/lib/CodeGen/CGDecl.cpp +++ b/clang/lib/CodeGen/CGDecl.cpp @@ -249,7 +249,7 @@ // variables cannot have an initializer. llvm::Constant *Init = nullptr; if (Ty.getAddressSpace() == LangAS::opencl_local || - D.hasAttr()) + D.hasAttr() || D.hasAttr()) Init = llvm::UndefValue::get(LTy); else Init = EmitNullConstant(Ty); diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp --- a/clang/lib/CodeGen/CodeGenModule.cpp +++ b/clang/lib/CodeGen/CodeGenModule.cpp @@ -3942,6 +3942,8 @@ if (getLangOpts().CUDA && (IsCUDASharedVar || IsCUDAShadowVar || IsHIPPinnedShadowVar)) Init = llvm::UndefValue::get(getTypes().ConvertType(ASTTy)); + else if (D->hasAttr()) + Init = llvm::UndefValue::get(getTypes().ConvertType(ASTTy)); else if (!InitExpr) { // This is a tentative definition; tentative definitions are // implicitly initialized with { 0 }. diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -6505,6 +6505,11 @@ D->addAttr(::new (S.Context) UninitializedAttr(S.Context, AL)); } +static void handleNoZeroInitializerAttr(Sema &S, Decl *D, + const ParsedAttr &AL) { + D->addAttr(::new (S.Context) NoZeroInitializerAttr(S.Context, AL)); +} + static bool tryMakeVariablePseudoStrong(Sema &S, VarDecl *VD, bool DiagnoseFailure) { QualType Ty = VD->getType(); @@ -7427,6 +7432,10 @@ handleUninitializedAttr(S, D, AL); break; + case ParsedAttr::AT_NoZeroInitializer: + handleNoZeroInitializerAttr(S, D, AL); + break; + case ParsedAttr::AT_ObjCExternallyRetained: handleObjCExternallyRetainedAttr(S, D, AL); break; diff --git a/clang/test/CodeGenCXX/attr-no-zero-initializer.cpp b/clang/test/CodeGenCXX/attr-no-zero-initializer.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CodeGenCXX/attr-no-zero-initializer.cpp @@ -0,0 +1,40 @@ +// RUN: %clang_cc1 -triple x86_64-unknown-unknown -std=c++11 -emit-llvm -o - %s | FileCheck %s + +// CHECK: @_ZZ4funcvE4data = internal global i32 undef +int* func(void) +{ + static int data [[clang::no_zero_initializer]]; + return &data; +} + +// No code emitted +extern int extern_unhelpful_but_harmless [[clang::no_zero_initializer]]; + +// CHECK: @tentative = global i32 undef +int tentative [[clang::no_zero_initializer]]; + +// CHECK: @_ZL16tentative_static = internal global i32 undef +static int tentative_static [[clang::no_zero_initializer]] __attribute__((used)); + +// CHECK: @nominally_zero_init = global i32 undef +int nominally_zero_init [[clang::no_zero_initializer]] = 0; + +// CHECK: @nominally_value_init = global i32 undef +int nominally_value_init [[clang::no_zero_initializer]] = 4; + +class trivial +{ + float x; +}; + +// CHECK: @ut = global %class.trivial undef +trivial ut [[clang::no_zero_initializer]]; + +struct nontrivial +{ + nontrivial() : x(3.14) {} + double x; +}; + +// CHECK: @unt = global %struct.nontrivial undef +nontrivial unt [[clang::no_zero_initializer]]; diff --git a/clang/test/Misc/pragma-attribute-supported-attributes-list.test b/clang/test/Misc/pragma-attribute-supported-attributes-list.test --- a/clang/test/Misc/pragma-attribute-supported-attributes-list.test +++ b/clang/test/Misc/pragma-attribute-supported-attributes-list.test @@ -94,6 +94,7 @@ // CHECK-NEXT: NoStackProtector (SubjectMatchRule_function) // CHECK-NEXT: NoThreadSafetyAnalysis (SubjectMatchRule_function) // CHECK-NEXT: NoThrow (SubjectMatchRule_hasType_functionType) +// CHECK-NEXT: NoZeroInitializer (SubjectMatchRule_variable_is_global) // CHECK-NEXT: NotTailCalled (SubjectMatchRule_function) // CHECK-NEXT: OSConsumed (SubjectMatchRule_variable_is_parameter) // CHECK-NEXT: OSReturnsNotRetained (SubjectMatchRule_function, SubjectMatchRule_objc_method, SubjectMatchRule_objc_property, SubjectMatchRule_variable_is_parameter) diff --git a/clang/test/Sema/attr-no-zero-initializer.cpp b/clang/test/Sema/attr-no-zero-initializer.cpp new file mode 100644 --- /dev/null +++ b/clang/test/Sema/attr-no-zero-initializer.cpp @@ -0,0 +1,19 @@ +// RUN: %clang_cc1 %s -verify -fsyntax-only + +int good __attribute__((no_zero_initializer)); +const int still_cant_be_const __attribute__((no_zero_initializer)); // expected-error {{default initialization of an object of const type}} +extern int external __attribute__((no_zero_initializer)); + +void func() __attribute__((no_zero_initializer)) // expected-warning {{'no_zero_initializer' attribute only applies to global variables}} +{ + int local __attribute__((no_zero_initializer)); // expected-warning {{'no_zero_initializer' attribute only applies to global variables}} + + static int sl __attribute__((no_zero_initializer)); +} + +struct s { + __attribute__((no_zero_initializer)) int field; // expected-warning {{'no_zero_initializer' attribute only applies to global variables}} + + static __attribute__((no_zero_initializer)) int sfield; + +} __attribute__((no_zero_initializer)); // expected-warning {{'no_zero_initializer' attribute only applies to global variables}}