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 LoaderUninitialized : Attr { + let Spellings = [Clang<"loader_uninitialized">]; + let Subjects = SubjectList<[GlobalVar]>; + let Documentation = [LoaderUninitializedDocs]; +} + def ObjCExternallyRetained : InheritableAttr { let LangOpts = [ObjCAutoRefCount]; let Spellings = [Clang<"objc_externally_retained">]; diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td --- a/clang/include/clang/Basic/AttrDocs.td +++ b/clang/include/clang/Basic/AttrDocs.td @@ -4358,6 +4358,29 @@ }]; } +def LoaderUninitializedDocs : Documentation { + let Category = DocCatVariable; + let Content = [{ +The ``loader_uninitialized`` attribute can be placed on global variables to +indicate that the variable does not need to be zero initialized by the loader. +On most targets, zero-initialization does not incur any additional cost. +For example, most general purpose operating systems deliberately ensure +that all memory is properly initialized in order to avoid leaking privileged +information from the kernel or other programs. However, some targets +do not make this guarantee, and on these targets, avoiding an unnecessary +zero-initialization can have a significant impact on load times and/or code +size. + +A declaration with this attribute is a non-tentative definition just as if it +provided an initializer. Variables with this attribute are considered to be +uninitialized in the same sense as a local variable, and the programs must +write to them before reading from them. If the variable's type is a C++ class +type with a non-trivial default constructor, or an array thereof, this attribute +only suppresses the static zero-initialization of the variable, not the dynamic +initialization provided by executing the default constructor. + }]; +} + def CallbackDocs : Documentation { let Category = DocCatFunction; let Content = [{ diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -5334,6 +5334,17 @@ "initializer for aggregate is not a compile-time constant">, InGroup; def err_local_cant_init : Error< "'__local' variable cannot have an initializer">; +def err_loader_uninitialized_cant_init + : Error<"variable with 'loader_uninitialized' attribute cannot have an " + "initializer">; +def err_loader_uninitialized_trivial_ctor + : Error<"variable with 'loader_uninitialized' attribute must have a " + "trivial default constructor">; +def err_loader_uninitialized_redeclaration + : Error<"redeclaration cannot add 'loader_uninitialized' attribute">; +def err_loader_uninitialized_extern_decl + : Error<"variable %0 cannot be declared both 'extern' and with the " + "'loader_uninitialized' attribute">; def err_block_extern_cant_init : Error< "'extern' variable cannot have an initializer">; def warn_extern_init : Warning<"'extern' variable has an initializer">, 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/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -2712,6 +2712,18 @@ --E; continue; } + } else if (isa(NewAttribute)) { + // If there is a C definition followed by a redeclaration with this + // attribute then there are two different definitions. In C++, prefer the + // standard diagnostics. + if (!S.getLangOpts().CPlusPlus) { + S.Diag(NewAttribute->getLocation(), + diag::err_loader_uninitialized_redeclaration); + S.Diag(Def->getLocation(), diag::note_previous_definition); + NewAttributes.erase(NewAttributes.begin() + I); + --E; + continue; + } } else if (isa(NewAttribute) && cast(New)->isInline() && !cast(New)->isInlineSpecified()) { @@ -11934,6 +11946,13 @@ return; } + // The LoaderUninitialized attribute acts as a definition (of undef). + if (VDecl->hasAttr()) { + Diag(VDecl->getLocation(), diag::err_loader_uninitialized_cant_init); + VDecl->setInvalidDecl(); + return; + } + // Get the decls type and save a reference for later, since // CheckInitializerTypes may change it. QualType DclT = VDecl->getType(), SavT = DclT; @@ -12347,6 +12366,22 @@ return; } + if (!Var->isInvalidDecl() && RealDecl->hasAttr()) { + if (CXXRecordDecl *RD = Var->getType()->getAsCXXRecordDecl()) { + if (!RD->hasTrivialDefaultConstructor()) { + Diag(Var->getLocation(), diag::err_loader_uninitialized_trivial_ctor); + Var->setInvalidDecl(); + return; + } + } + if (Var->getStorageClass() == SC_Extern) { + Diag(Var->getLocation(), diag::err_loader_uninitialized_extern_decl) + << Var; + Var->setInvalidDecl(); + return; + } + } + VarDecl::DefinitionKind DefKind = Var->isThisDeclarationADefinition(); if (!Var->isInvalidDecl() && DefKind != VarDecl::DeclarationOnly && Var->getType().hasNonTrivialToPrimitiveDefaultInitializeCUnion()) 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 @@ -7427,6 +7427,10 @@ handleUninitializedAttr(S, D, AL); break; + case ParsedAttr::AT_LoaderUninitialized: + handleSimpleAttribute(S, D, AL); + break; + case ParsedAttr::AT_ObjCExternallyRetained: handleObjCExternallyRetainedAttr(S, D, AL); break; diff --git a/clang/test/CodeGen/attr-loader-uninitialized.c b/clang/test/CodeGen/attr-loader-uninitialized.c new file mode 100644 --- /dev/null +++ b/clang/test/CodeGen/attr-loader-uninitialized.c @@ -0,0 +1,24 @@ +// RUN: %clang_cc1 -emit-llvm %s -o - | FileCheck %s + +// CHECK: @tentative_attr_first = weak global i32 undef, align 4 +int tentative_attr_first __attribute__((loader_uninitialized)); +int tentative_attr_first; + +// CHECK: @tentative_attr_second = weak global i32 undef, align 4 +int tentative_attr_second; +int tentative_attr_second __attribute__((loader_uninitialized)); + +// CHECK: @array = weak global [16 x float] undef, align 16 +float array[16] __attribute__((loader_uninitialized)); + +typedef struct +{ + int x; + float y; +} s; + +// CHECK: @i = weak global %struct.s undef, align 4 +s i __attribute__((loader_uninitialized)); + +// CHECK: @private_extern_ok = hidden global i32 undef, align 4 +__private_extern__ int private_extern_ok __attribute__((loader_uninitialized)); diff --git a/clang/test/CodeGenCXX/attr-loader-uninitialized.cpp b/clang/test/CodeGenCXX/attr-loader-uninitialized.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CodeGenCXX/attr-loader-uninitialized.cpp @@ -0,0 +1,29 @@ +// RUN: %clang_cc1 -emit-llvm -o - %s | FileCheck %s + +// CHECK: @defn = global i32 undef +int defn [[clang::loader_uninitialized]]; + +// CHECK: @_ZL11defn_static = internal global i32 undef +static int defn_static [[clang::loader_uninitialized]] __attribute__((used)); + +// CHECK: @_ZZ4funcvE4data = internal global i32 undef +int* func(void) +{ + static int data [[clang::loader_uninitialized]]; + return &data; +} + +class trivial +{ + float x; +}; + +// CHECK: @ut = global %class.trivial undef +trivial ut [[clang::loader_uninitialized]]; + +// CHECK: @arr = global [32 x double] undef, align 16 +double arr[32] __attribute__((loader_uninitialized)); + +// Defining as arr2[] [[clang..]] raises the error: attribute cannot be applied to types +// CHECK: @arr2 = global [4 x double] undef, align 16 +double arr2 [[clang::loader_uninitialized]] [4]; 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 @@ -65,6 +65,7 @@ // CHECK-NEXT: InitPriority (SubjectMatchRule_variable) // CHECK-NEXT: InternalLinkage (SubjectMatchRule_variable, SubjectMatchRule_function, SubjectMatchRule_record) // CHECK-NEXT: LTOVisibilityPublic (SubjectMatchRule_record) +// CHECK-NEXT: LoaderUninitialized (SubjectMatchRule_variable_is_global) // CHECK-NEXT: Lockable (SubjectMatchRule_record) // CHECK-NEXT: MIGServerRoutine (SubjectMatchRule_function, SubjectMatchRule_objc_method, SubjectMatchRule_block) // CHECK-NEXT: MSStruct (SubjectMatchRule_record) diff --git a/clang/test/Sema/attr-loader-uninitialized.c b/clang/test/Sema/attr-loader-uninitialized.c new file mode 100644 --- /dev/null +++ b/clang/test/Sema/attr-loader-uninitialized.c @@ -0,0 +1,37 @@ +// RUN: %clang_cc1 %s -verify -fsyntax-only +// See also attr-loader-uninitialized.cpp + +int good __attribute__((loader_uninitialized)); +static int local_ok __attribute__((loader_uninitialized)); +int hidden_ok __attribute__((visibility("hidden"))) __attribute__((loader_uninitialized)); + +const int can_still_be_const __attribute__((loader_uninitialized)); + +extern int external_rejected __attribute__((loader_uninitialized)); +// expected-error@-1 {{variable 'external_rejected' cannot be declared both 'extern' and with the 'loader_uninitialized' attribute}} + +int noargs __attribute__((loader_uninitialized(0))); +// expected-error@-1 {{'loader_uninitialized' attribute takes no arguments}} + +int init_rejected __attribute__((loader_uninitialized)) = 42; +// expected-error@-1 {{variable with 'loader_uninitialized' attribute cannot have an initializer}} + +int declaration_then_uninit_ok; +int declaration_then_uninit_ok __attribute__((loader_uninitialized)); + +int definition_then_uninit_rejected = 0; +int definition_then_uninit_rejected __attribute__((loader_uninitialized)); +// expected-error@-1 {{redeclaration cannot add 'loader_uninitialized' attribute}} +// expected-note@-3 {{previous definition is here}} + +int tentative_repeated_ok __attribute__((loader_uninitialized)); +int tentative_repeated_ok __attribute__((loader_uninitialized)); + +__private_extern__ int private_extern_can_be_initialised = 10; +__private_extern__ int therefore_uninit_private_extern_ok __attribute__((loader_uninitialized)); + +__private_extern__ int initialized_private_extern_rejected __attribute__((loader_uninitialized)) = 5; +// expected-error@-1 {{variable with 'loader_uninitialized' attribute cannot have an initializer}} + +extern __attribute__((visibility("hidden"))) int extern_hidden __attribute__((loader_uninitialized)); +// expected-error@-1 {{variable 'extern_hidden' cannot be declared both 'extern' and with the 'loader_uninitialized' attribute}} diff --git a/clang/test/Sema/attr-loader-uninitialized.cpp b/clang/test/Sema/attr-loader-uninitialized.cpp new file mode 100644 --- /dev/null +++ b/clang/test/Sema/attr-loader-uninitialized.cpp @@ -0,0 +1,60 @@ +// RUN: %clang_cc1 %s -verify -fsyntax-only + +int good __attribute__((loader_uninitialized)); +static int local_ok __attribute__((loader_uninitialized)); +int hidden_ok __attribute__((visibility("hidden"))) __attribute__((loader_uninitialized)); + +const int still_cant_be_const __attribute__((loader_uninitialized)); +// expected-error@-1 {{default initialization of an object of const type}} +extern int external_rejected __attribute__((loader_uninitialized)); +// expected-error@-1 {{variable 'external_rejected' cannot be declared both 'extern' and with the 'loader_uninitialized' attribute}} + +int noargs __attribute__((loader_uninitialized(0))); +// expected-error@-1 {{'loader_uninitialized' attribute takes no arguments}} + +int init_rejected __attribute__((loader_uninitialized)) = 42; +// expected-error@-1 {{variable with 'loader_uninitialized' attribute cannot have an initializer}} + +void func() __attribute__((loader_uninitialized)) +// expected-warning@-1 {{'loader_uninitialized' attribute only applies to global variables}} +{ + int local __attribute__((loader_uninitialized)); + // expected-warning@-1 {{'loader_uninitialized' attribute only applies to global variables}} + + static int sl __attribute__((loader_uninitialized)); +} + +struct s { + __attribute__((loader_uninitialized)) int field; + // expected-warning@-1 {{'loader_uninitialized' attribute only applies to global variables}} + + static __attribute__((loader_uninitialized)) int sfield; + +} __attribute__((loader_uninitialized)); +// expected-warning@-1 {{'loader_uninitialized' attribute only applies to global variables}} + +int redef_attr_first __attribute__((loader_uninitialized)); +int redef_attr_first; +// expected-error@-1 {{redefinition of 'redef_attr_first'}} +// expected-note@-3 {{previous definition is here}} + +int redef_attr_second; +int redef_attr_second __attribute__((loader_uninitialized)); +// expected-warning@-1 {{attribute declaration must precede definition}} +// expected-note@-3 {{previous definition is here}} +// expected-error@-3 {{redefinition of 'redef_attr_second'}} +// expected-note@-5 {{previous definition is here}} + +struct trivial {}; + +trivial default_ok __attribute__((loader_uninitialized)); +trivial value_rejected __attribute__((loader_uninitialized)) {}; +// expected-error@-1 {{variable with 'loader_uninitialized' attribute cannot have an initializer}} + +struct nontrivial +{ + nontrivial() {} +}; + +nontrivial needs_trivial_ctor __attribute__((loader_uninitialized)); +// expected-error@-1 {{variable with 'loader_uninitialized' attribute must have a trivial default constructor}}