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 : InheritableAttr { + 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,16 @@ }]; } +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. +This is useful for variables that are always written to before use where the +default zero initialization provided by the toolchain loader is expensive. + }]; +} + def CallbackDocs : Documentation { let Category = DocCatFunction; let Content = [{ 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 handleLoaderUninitializedAttr(Sema &S, Decl *D, + const ParsedAttr &AL) { + D->addAttr(::new (S.Context) LoaderUninitializedAttr(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_LoaderUninitialized: + handleLoaderUninitializedAttr(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,29 @@ +// 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: @attr_both_uninit_wins = global i32 undef, align 4 +int attr_both_uninit_wins __attribute__((loader_uninitialized)) = 42; + +// 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)); + +// This is caused by missing sema - should be an error, actually resolves to 4 +// CHECK: @tentative_attr_sema_fail = global i32 4, align 4 +int tentative_attr_sema_fail = 4; +int tentative_attr_sema_fail __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,47 @@ +// 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::loader_uninitialized]]; + return &data; +} + +// No code emitted +extern int extern_unhelpful_but_harmless [[clang::loader_uninitialized]]; + +// 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: @nominally_zero_init = global i32 undef +int nominally_zero_init [[clang::loader_uninitialized]] = 0; + +// CHECK: @nominally_value_init = global i32 undef +int nominally_value_init [[clang::loader_uninitialized]] = 4; + +class trivial +{ + float x; +}; + +// CHECK: @ut = global %class.trivial undef +trivial ut [[clang::loader_uninitialized]]; + +struct nontrivial +{ + nontrivial() : x(3.14) {} + double x; +}; + +// CHECK: @unt = global %struct.nontrivial undef +nontrivial unt [[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.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,36 @@ +// RUN: %clang_cc1 %s -verify -fsyntax-only + +int good __attribute__((loader_uninitialized)); +const int still_cant_be_const __attribute__((loader_uninitialized)); // expected-error {{default initialization of an object of const type}} +extern int external __attribute__((loader_uninitialized)); + +int noargs __attribute__((loader_uninitialized(0))); // expected-error {{'loader_uninitialized' attribute takes no arguments}} + +void func() __attribute__((loader_uninitialized)) // expected-warning {{'loader_uninitialized' attribute only applies to global variables}} +{ + int local __attribute__((loader_uninitialized)); // expected-warning {{'loader_uninitialized' attribute only applies to global variables}} + + static int sl __attribute__((loader_uninitialized)); +} + +struct s { + __attribute__((loader_uninitialized)) int field; // expected-warning {{'loader_uninitialized' attribute only applies to global variables}} + + static __attribute__((loader_uninitialized)) int sfield; + +} __attribute__((loader_uninitialized)); // expected-warning {{'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}} + +// Would like sema to reject this, but that is not yet implemented +int multiple_definitions __attribute__((loader_uninitialized)) = 42;