Index: clang/docs/LanguageExtensions.rst =================================================================== --- clang/docs/LanguageExtensions.rst +++ clang/docs/LanguageExtensions.rst @@ -3193,6 +3193,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,14 @@ 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 = [BPFPreserveAccessIndexDocs];// FIXME: document + let SimpleHandler = 1; + let LangOpts = [COnly]; +} + def BPFPreserveAccessIndex : InheritableAttr, TargetSpecificAttr { let Spellings = [Clang<"preserve_access_index">]; Index: clang/lib/Sema/SemaDecl.cpp =================================================================== --- clang/lib/Sema/SemaDecl.cpp +++ clang/lib/Sema/SemaDecl.cpp @@ -2204,6 +2204,18 @@ return New; } +namespace { +/// Return true iff Decl has the bpf_dominating_decl attribute, and is thus the +/// prevailing definition. +bool IsBPFDominatingDecl(const NamedDecl *Decl) { + for (auto attr : Decl->attrs()) { + if (isa(attr)) + return true; + } + return false; +} +} // namespace + /// Typedef declarations don't have linkage, but they still denote the same /// entity if their types are the same. /// FIXME: This is notionally doing the same thing as ASTReaderDecl's @@ -2270,6 +2282,11 @@ !OldType->isDependentType() && !NewType->isDependentType() && !Context.hasSameType(OldType, NewType)) { + if (IsBPFDominatingDecl(Old)) { + // 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; @@ -16161,6 +16178,10 @@ // Carry on and handle it like a normal definition. We'll // skip starting the definitiion later. } + } else if (SkipBody && IsBPFDominatingDecl(Def)) { + // 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. @@ -18042,10 +18063,17 @@ 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 (IsBPFDominatingDecl(cast(PrevDecl->getDeclContext()))) { + // 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/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) // CHECK-NEXT: BuiltinAlias (SubjectMatchRule_function) Index: clang/test/Misc/tantdyalf.c =================================================================== --- /dev/null +++ clang/test/Misc/tantdyalf.c @@ -0,0 +1,80 @@ +// 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 __bpf__ + +// expected-no-diagnostics +#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