Index: clang/docs/LanguageExtensions.rst =================================================================== --- clang/docs/LanguageExtensions.rst +++ clang/docs/LanguageExtensions.rst @@ -3271,6 +3271,38 @@ int a = 1; __builtin_dcbf (&a); + +BPF Language Extensions +----------------------- + +Extensions provided for BPF targets. + +Dominating Type Declaration +^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Clang provides a ``bpf_dominating_decl`` attribute that can be used to +mark structure, union and enumeration definitions, and typedef +declarations as dominating declarations. A subsequent (re)definition +will be ignored, rather than emit a diagnostic. The two definitions +do not need to be compatible. + +The contents of bpf's ``vmlinux.h`` machine-generated header file can +be decorated with this attribute, and then subsequent kernel headers +that also define the same types or typedefs can be included without +diagnostic. + +.. code-block:: c + + #pragma clang attribute push(__attribute__((bpf_dominating_decl)), \ + apply_to = any(record, enum, type_alias)) + #include + #pragma clang attribute pop + #include + +This attribute breaks the expectation that an attribute does not +change the well-formedness of source code. This attribute explicitly +permits multiple definitions of the same structure, union or +enumeration, or conflicting typedef declarations. + Extensions for Static Analysis ============================== Index: clang/include/clang/Basic/Attr.td =================================================================== --- clang/include/clang/Basic/Attr.td +++ clang/include/clang/Basic/Attr.td @@ -1828,6 +1828,13 @@ let Subjects = SubjectList<[Function], ErrorDiag, "kernel functions">; } +def BPFDominating : Attr, TargetSpecificAttr { + let Spellings = [Clang<"bpf_dominating_decl">]; + let Subjects = SubjectList<[Record, TypedefName, Enum], ErrorDiag>; + let Documentation = [BPFDominatingDeclDocs]; + let LangOpts = [COnly]; +} + def BPFPreserveAccessIndex : InheritableAttr, TargetSpecificAttr { let Spellings = [Clang<"preserve_access_index">]; Index: clang/include/clang/Basic/AttrDocs.td =================================================================== --- clang/include/clang/Basic/AttrDocs.td +++ clang/include/clang/Basic/AttrDocs.td @@ -2001,6 +2001,17 @@ }]; } +def BPFDominatingDeclDocs : Documentation { + let Category = DocCatDecl; + let Content = [{ +Clang provides a ``__attribute__((bpf_dominating_decl)`` attribute for +the BPF target. The attribute can be used to mark structure, union and +enumeration definitions, and typedef declarations as dominating +declarations. A subsequent (re)definition will be ignored, rather than +emit a diagnostic. The two definitions do not need to be compatible. +}]; +} + def BPFPreserveAccessIndexDocs : Documentation { let Category = DocCatFunction; let Content = [{ Index: clang/include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- clang/include/clang/Basic/DiagnosticSemaKinds.td +++ clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -3523,6 +3523,9 @@ "it has a base of a non-trivial class type|it has a virtual base|" "it has a __weak field|it has a field of a non-trivial class type}1">; +def err_attribute_not_system_header : Error< + "attribute %0 cannot appear outside of system headers">; + // Availability attribute def warn_availability_unknown_platform : Warning< "unknown platform %0 in availability macro">, InGroup; Index: clang/lib/Sema/SemaDecl.cpp =================================================================== --- clang/lib/Sema/SemaDecl.cpp +++ clang/lib/Sema/SemaDecl.cpp @@ -2270,6 +2270,11 @@ !OldType->isDependentType() && !NewType->isDependentType() && !Context.hasSameType(OldType, NewType)) { + if (Old->hasAttr()) { + // Silently ignore, we like playing with fire. + New->setInvalidDecl(); + return true; + } int Kind = isa(Old) ? 1 : 0; Diag(New->getLocation(), diag::err_redefinition_different_typedef) << Kind << NewType << OldType; @@ -16172,6 +16177,10 @@ // Carry on and handle it like a normal definition. We'll // skip starting the definitiion later. } + } else if (SkipBody && Def->hasAttr()) { + // Don't even care about checking -- just ignore the new def + SkipBody->ShouldSkip = true; + SkipBody->Previous = Def; } else if (!IsExplicitSpecializationAfterInstantiation) { // A redeclaration in function prototype scope in C isn't // visible elsewhere, so merely issue a warning. @@ -18054,10 +18063,18 @@ assert((getLangOpts().CPlusPlus || !isa(PrevDecl)) && "Received TagDecl when not in C++!"); if (!isa(PrevDecl) && isDeclInScope(PrevDecl, CurContext, S)) { - if (isa(PrevDecl)) - Diag(IdLoc, diag::err_redefinition_of_enumerator) << Id; - else - Diag(IdLoc, diag::err_redefinition) << Id; + auto Msg = diag::err_redefinition; + if (isa(PrevDecl)) { + if (cast(PrevDecl->getDeclContext()) + ->hasAttr()) { + // We can't completely bail here, as that would disturb the [default] + // value of a following enumerator + New->setInvalidDecl(); + return New; + } + Msg = diag::err_redefinition_of_enumerator; + } + Diag(IdLoc, Msg) << Id; notePreviousDefinition(PrevDecl, IdLoc); return nullptr; } Index: clang/lib/Sema/SemaDeclAttr.cpp =================================================================== --- clang/lib/Sema/SemaDeclAttr.cpp +++ clang/lib/Sema/SemaDeclAttr.cpp @@ -6871,6 +6871,15 @@ handleSimpleAttribute(S, D, AL); } +static void handleBPFDominatingAttr(Sema &S, Decl *D, const ParsedAttr &AL) { + // Restrict dominating decl to system header files, because, danger! + if (!S.Context.getSourceManager().isInSystemHeader(D->getLocation())) { + S.Diag(AL.getLoc(), diag::err_attribute_not_system_header) << AL; + return; + } + D->addAttr(::new (S.Context) BPFDominatingAttr(S.Context, AL)); +} + static void handleBPFPreserveAIRecord(Sema &S, RecordDecl *RD) { // Add preserve_access_index attribute to all fields and inner records. for (auto D : RD->decls()) { @@ -7947,6 +7956,9 @@ case ParsedAttr::AT_AVRSignal: handleAVRSignalAttr(S, D, AL); break; + case ParsedAttr::AT_BPFDominating: + handleBPFDominatingAttr(S, D, AL); + break; case ParsedAttr::AT_BPFPreserveAccessIndex: handleBPFPreserveAccessIndexAttr(S, D, AL); break; Index: clang/test/Misc/bpf-tantdyalf.c =================================================================== --- /dev/null +++ clang/test/Misc/bpf-tantdyalf.c @@ -0,0 +1,95 @@ +// RUN: %clang_cc1 -triple=bpfel -verify -fsyntax-only %s +// RUN: %clang_cc1 -triple=i686 -verify -fsyntax-only %s + +// BPF experimental hack +// These Are Not The Duplicates You Are Looking For + +#if !INCLUDE +#define INCLUDE 1 +#include __FILE__ +#else + +#if __bpf__ + +// expected-error@+1{{outside of system headers}} +#pragma clang attribute push(__attribute__((bpf_dominating_decl)), \ + apply_to = any(record, enum, type_alias)) +struct bad_bob { // expected-note{{this declaration}} + int a; +}; +#pragma clang attribute pop + +#pragma GCC system_header + +#pragma clang attribute push(__attribute__((bpf_dominating_decl)), \ + apply_to = any(record, enum, type_alias)) +struct bob { + int a; + union { + int i; + }; +}; +void f() { + struct x { + int i; + }; +} +#pragma clang attribute pop +struct bob { + int a; +}; + +#pragma clang attribute push(__attribute__((bpf_dominating_decl)), \ + apply_to = any(record, enum, type_alias)) +struct bill { + int a; +}; +#pragma clang attribute pop +struct bill { + // + long b; + float x; +}; + +#pragma clang attribute push(__attribute__((bpf_dominating_decl)), \ + apply_to = any(record, enum, type_alias)) +typedef unsigned bob; +#pragma clang attribute pop +typedef int bob; // + +// bob should be unsigned +char ary1[(2 * ((bob)-1 > 0)) - 1]; + +#pragma clang attribute push(__attribute__((bpf_dominating_decl)), \ + apply_to = any(record, enum, type_alias)) +enum X { A }; +#pragma clang attribute pop +enum X { A }; + +#pragma clang attribute push(__attribute__((bpf_dominating_decl)), \ + apply_to = any(record, enum, type_alias)) +enum { B }; +#pragma clang attribute pop +enum { B, + C }; // OK + +int ary2[2 * (C == 1) - 1]; + +enum { A }; // also OK + +#else + +// expected-warning@+1{{unknown attribute}} +#pragma clang attribute push(__attribute__((bpf_dominating_decl)), \ + apply_to = any(record, enum, type_alias)) +// expected-note@+1{{when applied to}} +struct bob { // expected-note{{previous definition}} + int a; +}; +#pragma clang attribute pop +struct bob { // expected-error{{redefinition}} + int a; +}; + +#endif +#endif Index: clang/test/Misc/pragma-attribute-supported-attributes-list.test =================================================================== --- clang/test/Misc/pragma-attribute-supported-attributes-list.test +++ clang/test/Misc/pragma-attribute-supported-attributes-list.test @@ -21,6 +21,7 @@ // CHECK-NEXT: AssumeAligned (SubjectMatchRule_objc_method, SubjectMatchRule_function) // CHECK-NEXT: Assumption (SubjectMatchRule_function, SubjectMatchRule_objc_method) // CHECK-NEXT: Availability ((SubjectMatchRule_record, SubjectMatchRule_enum, SubjectMatchRule_enum_constant, SubjectMatchRule_field, SubjectMatchRule_function, SubjectMatchRule_namespace, SubjectMatchRule_objc_category, SubjectMatchRule_objc_implementation, SubjectMatchRule_objc_interface, SubjectMatchRule_objc_method, SubjectMatchRule_objc_property, SubjectMatchRule_objc_protocol, SubjectMatchRule_record, SubjectMatchRule_type_alias, SubjectMatchRule_variable)) +// CHECK-NEXT: BPFDominating (SubjectMatchRule_record, SubjectMatchRule_type_alias, SubjectMatchRule_enum) // CHECK-NEXT: BPFPreserveAccessIndex (SubjectMatchRule_record) // CHECK-NEXT: BTFDeclTag (SubjectMatchRule_variable, SubjectMatchRule_function, SubjectMatchRule_record, SubjectMatchRule_field, SubjectMatchRule_type_alias) // CHECK-NEXT: BuiltinAlias (SubjectMatchRule_function)