https://github.com/yonghong-song updated https://github.com/llvm/llvm-project/pull/232498
>From 2ed53e96b875ede2960b7c5344b48ceafd553a44 Mon Sep 17 00:00:00 2001 From: Yonghong Song <[email protected]> Date: Mon, 19 Oct 2026 17:40:45 -0700 Subject: [PATCH] [BPF] Support preserve_access_index attribute for enum Currently, an enum value can only be relocated with the form __builtin_preserve_enum_value(*(enum E *)C, BPF_ENUMVAL_VALUE) or the libbpf macro bpf_core_enum_value(enum E, C). It is easy to forget to use it, and since most enum values are stable, a missing relocation is often not noticed until the program runs on a kernel with a different value. This patch allows the preserve_access_index attribute on enum declarations, in addition to struct and union declarations. If -g is specified, each use of an enumerator of such an enum whose value is computed at runtime becomes an ENUM_VALUE relocation, without any builtin. For example: enum cgroup_subsys_id { cpuset_cgrp_id = 0, cpu_cgrp_id = 1, memory_cgrp_id = 4, CGROUP_SUBSYS_COUNT = 10, } __attribute__((preserve_access_index)); struct css_set { void *subsys[CGROUP_SUBSYS_COUNT]; } __attribute__((preserve_access_index)); int is_valid(int id) { return id >= 0 && id < CGROUP_SUBSYS_COUNT; } void *get_mem_css(struct css_set *cset) { return cset->subsys[memory_cgrp_id]; } The generated code is: is_valid: w0 = 0 if w1 s< 0 goto +5 r2 = 10 ll CO-RE <enumval_value> enum cgroup_subsys_id::CGROUP_SUBSYS_COUNT = 10 w0 = 1 if w1 s< w2 goto +1 w0 = 0 exit get_mem_css: r2 = 0 CO-RE <byte_off> struct css_set::subsys (0:0) r1 += r2 r2 = 4 ll CO-RE <enumval_value> enum cgroup_subsys_id::memory_cgrp_id = 4 r2 <<= 32 r2 s>>= 29 r1 += r2 r0 = *(u64 *)(r1 + 0) exit Without the attribute on the enum, memory_cgrp_id is folded into the struct relocation, so the array index 4 is fixed: get_mem_css: r0 = *(u64 *)(r1 + 32) CO-RE <byte_off> struct css_set::subsys[4] (0:0:4) exit The attribute takes effect for - a named enum, e.g., enum cgroup_subsys_id above, - an anonymous enum with a typedef name, e.g., typedef enum { A = 1 } __attribute__((preserve_access_index)) T; - a named enum defined inside a struct or union with the attribute, which inherits the attribute as nested records do. An anonymous enum without a typedef name is not supported since libbpf matches relocations by type name. The attribute has no effect on it. If the attribute is written directly on such an enum, an error is issued by default, consistent with other misuses of the attribute: enum { A1 } __attribute__((preserve_access_index)); error: 'preserve_access_index' attribute has no effect on an anonymous enum without a typedef name [-Wignored-attributes] It can be downgraded to a warning with -Wno-error=ignored-attributes. There is no diagnostic if such an enum gets the attribute implicitly from an enclosing struct or union, or from #pragma clang attribute, so existing annotated records and headers with many anonymous enums like vmlinux.h are not affected. An enumerator is relocated where its value is used at runtime, e.g., - in expressions, such as return values, function arguments, array indices and comparisons, - in conditions of if/switch/loops/?:/&&/|| statements and expressions, - in builtin calls which can be constant evaluated, - in initializers of local variables, including local aggregates, - through a local const variable or its members initialized with such an enumerator, e.g., k in const int k = memory_cgrp_id; if (k == 4) ... An enumerator is an integer constant expression, so clang codegen may fold an expression containing it before the enumerator itself is emitted. Such folding is disabled in the above cases. An enumerator keeps its compile-time value - where a compile-time constant is required, e.g., case labels, array sizes like subsys[CGROUP_SUBSYS_COUNT] above, bitfield widths, static assertions and inline asm "i" operands, - in initializers of global and static variables, including uses of a global or static const variable initialized with an enumerator, - in unevaluated operands, e.g., sizeof(memory_cgrp_id), - if -g is not specified. The enumerators are lowered to the same llvm.bpf.preserve.enum.value intrinsic as __builtin_preserve_enum_value(), so no backend or libbpf change is needed. __builtin_preserve_enum_value() works as before. Its intrinsic lowering is moved into a helper which is shared with the new case. Existing code is mostly not affected since bpftool-generated vmlinux.h only applies the attribute to records and never defines a named enum inside a record. To relocate enum values from vmlinux.h, bpftool can apply the attribute to enums as well. Older clang rejects the attribute on enums with a hard error, which cannot be downgraded with any -W option, either directly: error: 'preserve_access_index' attribute only applies to structs, unions, and classes or through #pragma clang attribute: error: attribute 'preserve_access_index' cannot be applied to 'enum' Checking __clang_major__ is not reliable, since a development build of the same major version prior to this patch still rejects the attribute on enums. So a new macro __BPF_FEATURE_ENUM_PRESERVE_ACCESS_INDEX is defined, and vmlinux.h can apply the attribute to enums only if it is supported: #ifndef BPF_NO_PRESERVE_ACCESS_INDEX #ifdef __BPF_FEATURE_ENUM_PRESERVE_ACCESS_INDEX #pragma clang attribute push (__attribute__((preserve_access_index)), apply_to = any(record, enum)) #else #pragma clang attribute push (__attribute__((preserve_access_index)), apply_to = record) #endif #endif --- clang/include/clang/Basic/Attr.td | 2 +- clang/include/clang/Basic/AttrDocs.td | 14 ++ .../clang/Basic/DiagnosticSemaKinds.td | 3 + clang/include/clang/Sema/SemaBPF.h | 6 + clang/lib/Basic/Targets/BPF.cpp | 1 + clang/lib/CodeGen/CGBuiltin.cpp | 2 +- clang/lib/CodeGen/CGDecl.cpp | 3 +- clang/lib/CodeGen/CGExprAgg.cpp | 3 +- clang/lib/CodeGen/CGExprScalar.cpp | 19 +- clang/lib/CodeGen/CodeGenFunction.cpp | 54 +++++ clang/lib/CodeGen/CodeGenFunction.h | 14 ++ clang/lib/CodeGen/TargetBuiltins/ARM.cpp | 74 +++--- clang/lib/Sema/Sema.cpp | 1 + clang/lib/Sema/SemaBPF.cpp | 23 +- .../bpf-attr-preserve-access-index-enum.c | 210 ++++++++++++++++++ ...a-attribute-supported-attributes-list.test | 2 +- .../test/Preprocessor/bpf-predefined-macros.c | 9 + .../Sema/bpf-attr-preserve-access-index.c | 37 ++- 18 files changed, 434 insertions(+), 43 deletions(-) create mode 100644 clang/test/CodeGen/bpf-attr-preserve-access-index-enum.c diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index f8bd097733529a..b7eccfa414dc6a 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -2576,7 +2576,7 @@ def AMDGPUAvailableVisible : StmtAttr, TargetSpecificAttr<TargetAMDGPU> { def BPFPreserveAccessIndex : InheritableAttr, TargetSpecificAttr<TargetBPF> { let Spellings = [Clang<"preserve_access_index">]; - let Subjects = SubjectList<[Record], ErrorDiag>; + let Subjects = SubjectList<[Record, Enum], ErrorDiag>; let Documentation = [BPFPreserveAccessIndexDocs]; let LangOpts = [COnly]; } diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td index 02b7466c679520..9eed3567bcb153 100644 --- a/clang/include/clang/Basic/AttrDocs.td +++ b/clang/include/clang/Basic/AttrDocs.td @@ -3073,6 +3073,20 @@ attribute for the BPF target. This attribute may be attached to a struct or union declaration, where if -g is specified, it enables preserving struct or union member access debuginfo indices of this struct or union, similar to clang `__builtin_preserve_access_index()`. + +The attribute may also be attached to an enum declaration. If -g is +specified, each use of an enumerator of this enum whose value is computed +at runtime is replaced by an `ENUM_VALUE` CO-RE relocation, similar to +`__builtin_preserve_enum_value(*(enum_type *)enumerator, 1)`, so that the +bpf loader may adjust the value based on the target kernel. Such uses are +not constant folded. Uses that require a compile-time constant, e.g., +case labels, array sizes, bitfield widths, static assertions and global +or static variable initializers, keep the compile-time value. The enum +must be named or have a typedef name; otherwise, the attribute has no +effect and an error is issued, which may be downgraded to a warning with +`-Wno-error=ignored-attributes`. The macro +`__BPF_FEATURE_ENUM_PRESERVE_ACCESS_INDEX` is defined if the attribute is +supported on enum declarations. }]; } diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index f0e06e314e3c29..1fd63115325933 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -13556,6 +13556,9 @@ def err_preserve_enum_value_invalid : Error< "__builtin_preserve_enum_value argument %0 invalid">; def err_preserve_enum_value_not_const: Error< "__builtin_preserve_enum_value argument %0 not a constant">; +def warn_preserve_access_index_anon_enum : Warning< + "%0 attribute has no effect on an anonymous enum without a typedef name">, + InGroup<IgnoredAttributes>, DefaultError; def err_bit_cast_non_trivially_copyable : Error< "'__builtin_bit_cast' %select{source|destination}0 type must be trivially copyable">; diff --git a/clang/include/clang/Sema/SemaBPF.h b/clang/include/clang/Sema/SemaBPF.h index d7577aea701227..dd2644ba5619c0 100644 --- a/clang/include/clang/Sema/SemaBPF.h +++ b/clang/include/clang/Sema/SemaBPF.h @@ -15,6 +15,7 @@ #include "clang/AST/ASTFwd.h" #include "clang/Sema/SemaBase.h" +#include "llvm/ADT/MapVector.h" namespace clang { class ParsedAttr; @@ -27,6 +28,11 @@ class SemaBPF : public SemaBase { void handlePreserveAIRecord(RecordDecl *RD); void handlePreserveAccessIndexAttr(Decl *D, const ParsedAttr &AL); + void DiagnoseAnonymousPreserveAIEnums(); + +private: + llvm::MapVector<EnumDecl *, const BPFPreserveAccessIndexAttr *> + AnonPreserveAIEnums; }; } // namespace clang diff --git a/clang/lib/Basic/Targets/BPF.cpp b/clang/lib/Basic/Targets/BPF.cpp index 2c37d56186ce52..ea46d94be9b073 100644 --- a/clang/lib/Basic/Targets/BPF.cpp +++ b/clang/lib/Basic/Targets/BPF.cpp @@ -48,6 +48,7 @@ void BPFTargetInfo::getTargetDefines(const LangOptions &Opts, Builder.defineMacro("__BPF_FEATURE_ATOMIC_MEM_ORDERING"); Builder.defineMacro("__BPF_FEATURE_STACK_ARGUMENT"); Builder.defineMacro("__BPF_FEATURE_UNWIND"); + Builder.defineMacro("__BPF_FEATURE_ENUM_PRESERVE_ACCESS_INDEX"); if (CPU.empty()) CPU = "v3"; diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp index e154b9e752fd5c..e7f130f9b7e39e 100644 --- a/clang/lib/CodeGen/CGBuiltin.cpp +++ b/clang/lib/CodeGen/CGBuiltin.cpp @@ -2725,7 +2725,7 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID, // TODO: Extend this handling to all builtin calls that we can constant-fold. Expr::EvalResult Result; if (E->isPRValue() && E->EvaluateAsRValue(Result, CGM.getContext()) && - !Result.hasSideEffects()) { + !Result.hasSideEffects() && !containsBPFPreservedEnumerator(E)) { if (Result.Val.isInt()) return RValue::get(llvm::ConstantInt::get(getLLVMContext(), Result.Val.getInt())); diff --git a/clang/lib/CodeGen/CGDecl.cpp b/clang/lib/CodeGen/CGDecl.cpp index 8862f573014126..98c26b1ae80ac7 100644 --- a/clang/lib/CodeGen/CGDecl.cpp +++ b/clang/lib/CodeGen/CGDecl.cpp @@ -1538,7 +1538,8 @@ CodeGenFunction::EmitAutoVarAlloca(const VarDecl &D) { if (D.getInit() && (Ty->isArrayType() || Ty->isRecordType()) && (D.isConstexpr() || ((Ty.isPODType(getContext()) || - getContext().getBaseElementType(Ty)->isObjCObjectPointerType())))) { + getContext().getBaseElementType(Ty)->isObjCObjectPointerType()))) && + !containsBPFPreservedEnumerator(D.getInit())) { emission.ConstantAggregateInitializer = ConstantEmitter(*this).tryEmitAbstractForInitializer(D); if (emission.ConstantAggregateInitializer) { diff --git a/clang/lib/CodeGen/CGExprAgg.cpp b/clang/lib/CodeGen/CGExprAgg.cpp index 73c6301c5d585b..963229dee95066 100644 --- a/clang/lib/CodeGen/CGExprAgg.cpp +++ b/clang/lib/CodeGen/CGExprAgg.cpp @@ -581,7 +581,8 @@ void AggExprEmitter::EmitArrayInit(Address DestPtr, llvm::ArrayType *AType, // more efficient than per-element initialization, the size of the elements // with explicit initializers should be large enough. if (NumInitElements * elementSize.getQuantity() > 16 && - elementType.isTriviallyCopyableType(CGF.getContext())) { + elementType.isTriviallyCopyableType(CGF.getContext()) && + !CGF.containsBPFPreservedEnumerator(ExprToVisit)) { CodeGen::CodeGenModule &CGM = CGF.CGM; ConstantEmitter Emitter(CGF); QualType GVArrayQTy = CGM.getContext().getAddrSpaceQualType( diff --git a/clang/lib/CodeGen/CGExprScalar.cpp b/clang/lib/CodeGen/CGExprScalar.cpp index 0e2e29f0745d27..53b35afb97cfb0 100644 --- a/clang/lib/CodeGen/CGExprScalar.cpp +++ b/clang/lib/CodeGen/CGExprScalar.cpp @@ -611,8 +611,11 @@ class ScalarExprEmitter // l-values. Value *VisitDeclRefExpr(DeclRefExpr *E) { - if (CodeGenFunction::ConstantEmission Constant = CGF.tryEmitAsConstant(E)) - return CGF.emitScalarConstant(Constant, E); + if (Value *V = CGF.EmitBPFPreservedEnumerator(E)) + return V; + if (!CGF.containsBPFPreservedEnumerator(E)) + if (CodeGenFunction::ConstantEmission Constant = CGF.tryEmitAsConstant(E)) + return CGF.emitScalarConstant(Constant, E); return EmitLoadOfLValue(E); } @@ -2147,7 +2150,10 @@ Value *ScalarExprEmitter::VisitConvertVectorExpr(ConvertVectorExpr *E) { } Value *ScalarExprEmitter::VisitMemberExpr(MemberExpr *E) { - if (CodeGenFunction::ConstantEmission Constant = CGF.tryEmitAsConstant(E)) { + if (CGF.containsBPFPreservedEnumerator(E)) { + // Do not constant fold. + } else if (CodeGenFunction::ConstantEmission Constant = + CGF.tryEmitAsConstant(E)) { CGF.EmitIgnoredExpr(E->getBase()); return CGF.emitScalarConstant(Constant, E); } else { @@ -5998,11 +6004,16 @@ VisitAbstractConditionalOperator(const AbstractConditionalOperator *E) { // If this is a really simple expression (like x ? 4 : 5), emit this as a // select instead of as control flow. We can only do this if it is cheap and // safe to evaluate the LHS and RHS unconditionally. + // A BPF enumerator that is relocated is a load of its relocation global in + // the backend. A select of two such loads may become a load from the + // selected global, which cannot be relocated, so use control flow. if (!llvm::EnableSingleByteCoverage && CodeGenUtils::isCheapEnoughToEvaluateUnconditionally(lhsExpr, CGF.getContext()) && CodeGenUtils::isCheapEnoughToEvaluateUnconditionally(rhsExpr, - CGF.getContext())) { + CGF.getContext()) && + !CGF.containsBPFPreservedEnumerator(lhsExpr) && + !CGF.containsBPFPreservedEnumerator(rhsExpr)) { llvm::Value *CondV = CGF.EvaluateExprAsBool(condExpr); llvm::Value *StepV = Builder.CreateZExtOrBitCast(CondV, CGF.Int64Ty); diff --git a/clang/lib/CodeGen/CodeGenFunction.cpp b/clang/lib/CodeGen/CodeGenFunction.cpp index b6081479fd7bce..378d2227c99965 100644 --- a/clang/lib/CodeGen/CodeGenFunction.cpp +++ b/clang/lib/CodeGen/CodeGenFunction.cpp @@ -1782,6 +1782,57 @@ bool CodeGenFunction::mightAddDeclToScope(const Stmt *S) { return false; } +QualType CodeGenFunction::getBPFPreservedEnumType(const DeclRefExpr *E) { + const auto *Enumerator = dyn_cast<EnumConstantDecl>(E->getDecl()); + if (!Enumerator || !getTarget().getTriple().isBPF() || !getDebugInfo()) + return QualType(); + + // In C, the type of an enumerator may not be the enum type, so get the + // enum from the declaration context. + const auto *ED = dyn_cast<EnumDecl>(Enumerator->getDeclContext()); + if (!ED || !ED->hasAttr<BPFPreserveAccessIndexAttr>()) + return QualType(); + + // The relocation is matched by type name, so an anonymous enum is + // only supported through its typedef name. + if (ED->getDeclName()) + return getContext().getCanonicalTagType(ED); + if (const TypedefNameDecl *TD = ED->getTypedefNameForAnonDecl()) + return getContext().getTypeDeclType(TD); + return QualType(); +} + +bool CodeGenFunction::containsBPFPreservedEnumerator(const Expr *E) { + if (!getTarget().getTriple().isBPF() || !getDebugInfo()) + return false; + + llvm::SmallPtrSet<const VarDecl *, 4> Visited; + auto Contains = [this, &Visited](auto &&Self, const Stmt *S) -> bool { + if (!S) + return false; + // A ConstantExpr is always emitted as a constant, and the enumerators + // in an unevaluated operand do not contribute their values. + if (isa<ConstantExpr, UnaryExprOrTypeTraitExpr>(S)) + return false; + if (const auto *DRE = dyn_cast<DeclRefExpr>(S)) { + if (!getBPFPreservedEnumType(DRE).isNull()) + return true; + // A local const variable may be constant folded through its + // initializer. + const auto *VD = dyn_cast<VarDecl>(DRE->getDecl()); + if (VD && VD->hasLocalStorage() && VD->getType().isConstQualified() && + Visited.insert(VD).second) + return Self(Self, VD->getInit()); + return false; + } + for (const Stmt *SubStmt : S->children()) + if (Self(Self, SubStmt)) + return true; + return false; + }; + return Contains(Contains, E); +} + /// ConstantFoldsToSimpleInteger - If the specified expression does not fold /// to a constant, or if it does but contains a label, return false. If it /// constant folds return true and set the boolean result in Result. @@ -1815,6 +1866,9 @@ bool CodeGenFunction::ConstantFoldsToSimpleInteger(const Expr *Cond, if (!Cond->EvaluateAsInt(Result, getContext())) return false; // Not foldable, not integer or not fully evaluatable. + if (containsBPFPreservedEnumerator(Cond)) + return false; + llvm::APSInt Int = Result.Val.getInt(); if (!AllowLabels && CodeGenFunction::ContainsLabel(Cond)) return false; // Contains a label. diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h index e17ed28c5d4f3a..a31b179a3b2789 100644 --- a/clang/lib/CodeGen/CodeGenFunction.h +++ b/clang/lib/CodeGen/CodeGenFunction.h @@ -5059,6 +5059,10 @@ class CodeGenFunction : public CodeGenTypeCache { llvm::Value *EmitAArch64BuiltinExpr(unsigned BuiltinID, const CallExpr *E, llvm::Triple::ArchType Arch); llvm::Value *EmitBPFBuiltinExpr(unsigned BuiltinID, const CallExpr *E); + llvm::Value *EmitBPFPreserveEnumValue(const EnumConstantDecl *Enumerator, + QualType EnumTy, SourceLocation Loc, + uint64_t Flag); + llvm::Value *EmitBPFPreservedEnumerator(const DeclRefExpr *E); llvm::Value *BuildVector(ArrayRef<llvm::Value *> Ops); llvm::Value *EmitX86BuiltinExpr(unsigned BuiltinID, const CallExpr *E); @@ -5390,6 +5394,16 @@ class CodeGenFunction : public CodeGenTypeCache { /// current scope, by being a (possibly-labelled) DeclStmt. static bool mightAddDeclToScope(const Stmt *S); + /// For the BPF target, if E refers to an enumerator of an enum with the + /// preserve_access_index attribute, return the enum type to be used for + /// its CO-RE relocation. Otherwise, return a null type. + QualType getBPFPreservedEnumType(const DeclRefExpr *E); + + /// For the BPF target, return true if the expression contains an + /// enumerator to be relocated, directly or through the initializer of a + /// local const variable. Such an expression should not be constant folded. + bool containsBPFPreservedEnumerator(const Expr *E); + /// ConstantFoldsToSimpleInteger - If the specified expression does not fold /// to a constant, or if it does but contains a label, return false. If it /// constant folds return true and set the boolean result in Result. diff --git a/clang/lib/CodeGen/TargetBuiltins/ARM.cpp b/clang/lib/CodeGen/TargetBuiltins/ARM.cpp index 5d4b87b75abe7b..458a14b9fc87e1 100644 --- a/clang/lib/CodeGen/TargetBuiltins/ARM.cpp +++ b/clang/lib/CodeGen/TargetBuiltins/ARM.cpp @@ -7287,6 +7287,51 @@ Value *CodeGenFunction::EmitAArch64BuiltinExpr(unsigned BuiltinID, } } +// A sequence number, injected into IR builtin functions, to +// prevent CSE given the only difference of the function +// may just be the debuginfo metadata. +static uint32_t BPFBuiltinSeqNum; + +Value * +CodeGenFunction::EmitBPFPreserveEnumValue(const EnumConstantDecl *Enumerator, + QualType EnumTy, SourceLocation Loc, + uint64_t Flag) { + llvm::DIType *DbgInfo = + getDebugInfo()->getOrCreateStandaloneType(EnumTy, Loc); + + auto InitVal = Enumerator->getInitVal(); + std::string InitValStr; + if (InitVal.isNegative() || InitVal > uint64_t(INT64_MAX)) + InitValStr = std::to_string(InitVal.getSExtValue()); + else + InitValStr = std::to_string(InitVal.getZExtValue()); + std::string EnumStr = Enumerator->getNameAsString() + ":" + InitValStr; + Value *EnumStrVal = Builder.CreateGlobalString(EnumStr); + + Value *FlagValue = ConstantInt::get(Int64Ty, Flag); + Value *SeqNumVal = ConstantInt::get(Int32Ty, BPFBuiltinSeqNum++); + + llvm::Function *IntrinsicFn = Intrinsic::getOrInsertDeclaration( + &CGM.getModule(), Intrinsic::bpf_preserve_enum_value, {}); + CallInst *Fn = + Builder.CreateCall(IntrinsicFn, {SeqNumVal, EnumStrVal, FlagValue}); + Fn->setMetadata(LLVMContext::MD_preserve_access_index, DbgInfo); + return Fn; +} + +Value *CodeGenFunction::EmitBPFPreservedEnumerator(const DeclRefExpr *E) { + QualType EnumTy = getBPFPreservedEnumType(E); + if (EnumTy.isNull()) + return nullptr; + + // Flag 1 is ENUM_VALUE. + Value *V = EmitBPFPreserveEnumValue(cast<EnumConstantDecl>(E->getDecl()), + EnumTy, E->getExprLoc(), 1); + QualType Ty = E->getType(); + return Builder.CreateIntCast(V, ConvertType(Ty), + Ty->isSignedIntegerOrEnumerationType()); +} + Value *CodeGenFunction::EmitBPFBuiltinExpr(unsigned BuiltinID, const CallExpr *E) { assert((BuiltinID == BPF::BI__builtin_preserve_field_info || @@ -7295,11 +7340,6 @@ Value *CodeGenFunction::EmitBPFBuiltinExpr(unsigned BuiltinID, BuiltinID == BPF::BI__builtin_preserve_enum_value) && "unexpected BPF builtin"); - // A sequence number, injected into IR builtin functions, to - // prevent CSE given the only difference of the function - // may just be the debuginfo metadata. - static uint32_t BuiltinSeqNum; - switch (BuiltinID) { default: llvm_unreachable("Unexpected BPF builtin"); @@ -7348,7 +7388,7 @@ Value *CodeGenFunction::EmitBPFBuiltinExpr(unsigned BuiltinID, ConstantInt *Flag = cast<ConstantInt>(EmitScalarExpr(E->getArg(1))); Value *FlagValue = ConstantInt::get(Int64Ty, Flag->getSExtValue()); - Value *SeqNumVal = ConstantInt::get(Int32Ty, BuiltinSeqNum++); + Value *SeqNumVal = ConstantInt::get(Int32Ty, BPFBuiltinSeqNum++); llvm::Function *FnDecl; if (BuiltinID == BPF::BI__builtin_btf_type_id) @@ -7368,8 +7408,6 @@ Value *CodeGenFunction::EmitBPFBuiltinExpr(unsigned BuiltinID, } const Expr *Arg0 = E->getArg(0); - llvm::DIType *DbgInfo = getDebugInfo()->getOrCreateStandaloneType( - Arg0->getType(), Arg0->getExprLoc()); // Find enumerator const auto *UO = cast<UnaryOperator>(Arg0->IgnoreParens()); @@ -7377,25 +7415,9 @@ Value *CodeGenFunction::EmitBPFBuiltinExpr(unsigned BuiltinID, const auto *DR = cast<DeclRefExpr>(CE->getSubExpr()); const auto *Enumerator = cast<EnumConstantDecl>(DR->getDecl()); - auto InitVal = Enumerator->getInitVal(); - std::string InitValStr; - if (InitVal.isNegative() || InitVal > uint64_t(INT64_MAX)) - InitValStr = std::to_string(InitVal.getSExtValue()); - else - InitValStr = std::to_string(InitVal.getZExtValue()); - std::string EnumStr = Enumerator->getNameAsString() + ":" + InitValStr; - Value *EnumStrVal = Builder.CreateGlobalString(EnumStr); - ConstantInt *Flag = cast<ConstantInt>(EmitScalarExpr(E->getArg(1))); - Value *FlagValue = ConstantInt::get(Int64Ty, Flag->getSExtValue()); - Value *SeqNumVal = ConstantInt::get(Int32Ty, BuiltinSeqNum++); - - llvm::Function *IntrinsicFn = Intrinsic::getOrInsertDeclaration( - &CGM.getModule(), Intrinsic::bpf_preserve_enum_value, {}); - CallInst *Fn = - Builder.CreateCall(IntrinsicFn, {SeqNumVal, EnumStrVal, FlagValue}); - Fn->setMetadata(LLVMContext::MD_preserve_access_index, DbgInfo); - return Fn; + return EmitBPFPreserveEnumValue(Enumerator, Arg0->getType(), + Arg0->getExprLoc(), Flag->getSExtValue()); } } } diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp index b121f17b96a288..b00a6ade47acd4 100644 --- a/clang/lib/Sema/Sema.cpp +++ b/clang/lib/Sema/Sema.cpp @@ -1335,6 +1335,7 @@ void Sema::ActOnEndOfTranslationUnit() { DiagnoseUnterminatedPragmaAlignPack(); DiagnoseUnterminatedPragmaAttribute(); + BPF().DiagnoseAnonymousPreserveAIEnums(); OpenMP().DiagnoseUnterminatedOpenMPDeclareTarget(); DiagnosePrecisionLossInComplexDivision(); DiagnoseUnusedAPINotesSelectors(); diff --git a/clang/lib/Sema/SemaBPF.cpp b/clang/lib/Sema/SemaBPF.cpp index a9761764bbdc67..8f7183ab73aa67 100644 --- a/clang/lib/Sema/SemaBPF.cpp +++ b/clang/lib/Sema/SemaBPF.cpp @@ -179,10 +179,25 @@ void SemaBPF::handlePreserveAIRecord(RecordDecl *RD) { } void SemaBPF::handlePreserveAccessIndexAttr(Decl *D, const ParsedAttr &AL) { - auto *Rec = cast<RecordDecl>(D); - handlePreserveAIRecord(Rec); - Rec->addAttr(::new (getASTContext()) - BPFPreserveAccessIndexAttr(getASTContext(), AL)); + if (auto *Rec = dyn_cast<RecordDecl>(D)) + handlePreserveAIRecord(Rec); + auto *A = + ::new (getASTContext()) BPFPreserveAccessIndexAttr(getASTContext(), AL); + // The relocation of an enumerator is matched by type name. Whether an + // anonymous enum gets a typedef name is checked at the end of the + // translation unit. Skip #pragma clang attribute which may cover many + // anonymous enums in a header. + if (auto *ED = dyn_cast<EnumDecl>(D); + ED && !ED->getDeclName() && !AL.isPragmaClangAttribute()) + AnonPreserveAIEnums.insert({ED, A}); + D->addAttr(A); +} + +void SemaBPF::DiagnoseAnonymousPreserveAIEnums() { + for (auto [ED, A] : AnonPreserveAIEnums) + if (!ED->getTypedefNameForAnonDecl()) + Diag(A->getLocation(), diag::warn_preserve_access_index_anon_enum) << A; + AnonPreserveAIEnums.clear(); } } // namespace clang diff --git a/clang/test/CodeGen/bpf-attr-preserve-access-index-enum.c b/clang/test/CodeGen/bpf-attr-preserve-access-index-enum.c new file mode 100644 index 00000000000000..24f1e47ba3d1b4 --- /dev/null +++ b/clang/test/CodeGen/bpf-attr-preserve-access-index-enum.c @@ -0,0 +1,210 @@ +// REQUIRES: bpf-registered-target +// RUN: %clang_cc1 -triple bpf -emit-llvm -debug-info-kind=limited -disable-llvm-passes \ +// RUN: -Wno-error=ignored-attributes -verify %s -o - | FileCheck %s + +#define __reloc__ __attribute__((preserve_access_index)) + +enum AA { VAL1 = -100, VAL2 = 0xffff8000 }; +typedef enum { VAL10 = 0xffffFFFF80000000 } __BB; +enum e1 { E1_A = 1, E1_C = 10 } __reloc__; +typedef enum { E2_A = 3 } __reloc__ e2; +enum { E3_A = 4 } __reloc__; // expected-warning {{attribute has no effect on an anonymous enum without a typedef name}} +struct s { + enum e4 { E4_A = 5 } a; +} __reloc__; +enum s64 { S64_BIG = 0x100000000, S64_NEG = -5 } __reloc__; +enum u64 { U64_BIG = 0xffffffff80000000 } __reloc__; + +// Contexts requiring a constant keep the compile-time value. +int g1 = E1_C; +int g2[E1_C]; +_Static_assert(E1_A == 1, ""); + +// CHECK: @g1 = global i32 10 +// CHECK: @[[VAL1:[0-9]+]] = private unnamed_addr constant [10 x i8] c"VAL1:-100\00" +// CHECK: @[[VAL2:[0-9]+]] = private unnamed_addr constant [16 x i8] c"VAL2:4294934528\00" +// CHECK: @[[VAL10:[0-9]+]] = private unnamed_addr constant [18 x i8] c"VAL10:-2147483648\00" +// CHECK: @[[E1_A:[0-9]+]] = private unnamed_addr constant [7 x i8] c"E1_A:1\00" +// CHECK: @[[E1_C:[0-9]+]] = private unnamed_addr constant [8 x i8] c"E1_C:10\00" +// CHECK: @[[E2_A:[0-9]+]] = private unnamed_addr constant [7 x i8] c"E2_A:3\00" +// CHECK: @[[E4_A:[0-9]+]] = private unnamed_addr constant [7 x i8] c"E4_A:5\00" +// CHECK: @[[S64_BIG:[0-9]+]] = private unnamed_addr constant [19 x i8] c"S64_BIG:4294967296\00" +// CHECK: @[[S64_NEG:[0-9]+]] = private unnamed_addr constant [11 x i8] c"S64_NEG:-5\00" +// CHECK: @[[U64_BIG:[0-9]+]] = private unnamed_addr constant [20 x i8] c"U64_BIG:-2147483648\00" +// CHECK: @g2 = global [10 x i32] zeroinitializer + +// __builtin_preserve_enum_value() is not affected by the attribute. +int test1(void) { + return __builtin_preserve_enum_value(*(enum AA *)VAL1, 0) + + __builtin_preserve_enum_value(*(enum AA *)VAL2, 1) + + __builtin_preserve_enum_value(*(__BB *)VAL10, 1) + + __builtin_preserve_enum_value(*(enum e1 *)E1_A, 0); +} + +// CHECK: define dso_local i32 @test1 +// CHECK: call i64 @llvm.bpf.preserve.enum.value(i32 0, ptr @[[VAL1]], i64 0), !dbg !{{[0-9]+}}, !llvm.preserve.access.index ![[AA:[0-9]+]] +// CHECK: call i64 @llvm.bpf.preserve.enum.value(i32 1, ptr @[[VAL2]], i64 1), !dbg !{{[0-9]+}}, !llvm.preserve.access.index ![[AA]] +// CHECK: call i64 @llvm.bpf.preserve.enum.value(i32 2, ptr @[[VAL10]], i64 1), !dbg !{{[0-9]+}}, !llvm.preserve.access.index ![[BB:[0-9]+]] +// CHECK: call i64 @llvm.bpf.preserve.enum.value(i32 3, ptr @[[E1_A]], i64 0), !dbg !{{[0-9]+}}, !llvm.preserve.access.index ![[E1:[0-9]+]] +// CHECK-NOT: @llvm.bpf.preserve.enum.value +// CHECK: ret i32 + +// An enumerator of an attributed enum is an ENUM_VALUE relocation, with or +// without __builtin_preserve_access_index(). +int test2(int x) { + return (x == E1_C) + __builtin_preserve_access_index(E2_A) + (E4_A); +} + +// CHECK: define dso_local i32 @test2 +// CHECK: [[V:%[0-9a-z]+]] = call i64 @llvm.bpf.preserve.enum.value(i32 {{[0-9]+}}, ptr @[[E1_C]], i64 1), !dbg !{{[0-9]+}}, !llvm.preserve.access.index ![[E1]] +// CHECK: [[T:%[0-9a-z]+]] = trunc i64 [[V]] to i32 +// CHECK: icmp eq i32 %{{[0-9a-z]+}}, [[T]] +// CHECK: call i64 @llvm.bpf.preserve.enum.value(i32 {{[0-9]+}}, ptr @[[E2_A]], i64 1), !dbg !{{[0-9]+}}, !llvm.preserve.access.index ![[E2:[0-9]+]] +// CHECK: call i64 @llvm.bpf.preserve.enum.value(i32 {{[0-9]+}}, ptr @[[E4_A]], i64 1), !dbg !{{[0-9]+}}, !llvm.preserve.access.index ![[E4:[0-9]+]] + +// Conditions and builtin calls using the enumerators are not folded. +int test3(int x) { + if (E1_C == 10) + x++; + while (x < E1_C) + x += E1_C > 5 ? 2 : 3; + switch (E1_A) { + case E1_A: // Case labels keep the compile-time value. + x++; + break; + default: + break; + } + return x + __builtin_bswap32(E1_C); +} + +// CHECK: define dso_local i32 @test3 +// CHECK: call i64 @llvm.bpf.preserve.enum.value(i32 {{[0-9]+}}, ptr @{{[0-9]+}}, i64 1), !dbg !{{[0-9]+}}, !llvm.preserve.access.index ![[E1]] +// CHECK: icmp eq i32 %{{[0-9a-z]+}}, 10 +// CHECK: call i64 @llvm.bpf.preserve.enum.value(i32 {{[0-9]+}}, ptr @{{[0-9]+}}, i64 1), !dbg !{{[0-9]+}}, !llvm.preserve.access.index ![[E1]] +// CHECK: icmp slt i32 +// CHECK: call i64 @llvm.bpf.preserve.enum.value(i32 {{[0-9]+}}, ptr @{{[0-9]+}}, i64 1), !dbg !{{[0-9]+}}, !llvm.preserve.access.index ![[E1]] +// CHECK: icmp sgt i32 %{{[0-9a-z]+}}, 5 +// CHECK: select i1 +// CHECK: [[V:%[0-9a-z]+]] = call i64 @llvm.bpf.preserve.enum.value(i32 {{[0-9]+}}, ptr @{{[0-9]+}}, i64 1), !dbg !{{[0-9]+}}, !llvm.preserve.access.index ![[E1]] +// CHECK: [[T:%[0-9a-z]+]] = trunc i64 [[V]] to i32 +// CHECK: switch i32 [[T]], label %{{.*}} [ +// CHECK-NEXT: i32 1, label +// CHECK: call i64 @llvm.bpf.preserve.enum.value(i32 {{[0-9]+}}, ptr @{{[0-9]+}}, i64 1), !dbg !{{[0-9]+}}, !llvm.preserve.access.index ![[E1]] +// CHECK: call i32 @llvm.bswap.i32 + +// Local aggregates initialized with the enumerators are not constant +// initialized. +struct key { int a; int b; }; +void use(void *); +void test4(void) { + struct key k = { E1_A, 2 }; + long arr[4] = { 1, 2, 3, E1_C }; + use(&k); + use(arr); +} + +// CHECK: define dso_local void @test4 +// CHECK-NOT: memcpy +// CHECK: call i64 @llvm.bpf.preserve.enum.value(i32 {{[0-9]+}}, ptr @{{[0-9]+}}, i64 1), !dbg !{{[0-9]+}}, !llvm.preserve.access.index ![[E1]] +// CHECK-NOT: memcpy +// CHECK: call i64 @llvm.bpf.preserve.enum.value(i32 {{[0-9]+}}, ptr @{{[0-9]+}}, i64 1), !dbg !{{[0-9]+}}, !llvm.preserve.access.index ![[E1]] +// CHECK-NOT: memcpy +// CHECK: ret void + +// Enumerators of an enum without the attribute, of an anonymous enum without +// typedef name, and in unevaluated operands keep their compile-time value. +int test5(void) { + static int s = E1_C; + return s + VAL1 + E3_A + sizeof(E1_C); +} + +// CHECK: define dso_local i32 @test5 +// CHECK-NOT: @llvm.bpf.preserve.enum.value +// CHECK: add nsw i64 %{{[0-9a-z]+}}, -100 +// CHECK-NEXT: add nsw i64 %{{[0-9a-z]+}}, 4 +// CHECK-NEXT: add i64 %{{[0-9a-z]+}}, 4 +// CHECK-NOT: @llvm.bpf.preserve.enum.value +// CHECK: ret i32 + +// Enumerators with 64-bit values are relocated without truncation. +long test6(void) { + return S64_BIG + S64_NEG; +} + +// CHECK: define dso_local i64 @test6 +// CHECK: [[V1:%[0-9a-z]+]] = call i64 @llvm.bpf.preserve.enum.value(i32 {{[0-9]+}}, ptr @[[S64_BIG]], i64 1), !dbg !{{[0-9]+}}, !llvm.preserve.access.index ![[S64:[0-9]+]] +// CHECK: [[V2:%[0-9a-z]+]] = call i64 @llvm.bpf.preserve.enum.value(i32 {{[0-9]+}}, ptr @[[S64_NEG]], i64 1), !dbg !{{[0-9]+}}, !llvm.preserve.access.index ![[S64]] +// CHECK: [[ADD:%[0-9a-z]+]] = add nsw i64 [[V1]], [[V2]] +// CHECK: ret i64 [[ADD]] + +unsigned long test7(void) { + return U64_BIG; +} + +// CHECK: define dso_local i64 @test7 +// CHECK: [[V:%[0-9a-z]+]] = call i64 @llvm.bpf.preserve.enum.value(i32 {{[0-9]+}}, ptr @[[U64_BIG]], i64 1), !dbg !{{[0-9]+}}, !llvm.preserve.access.index ![[U64:[0-9]+]] +// CHECK: ret i64 [[V]] + +// Local const variables initialized with the enumerators are not constant +// folded. Global and static local ones keep the compile-time value. +const int g3 = E1_C; +int test8(int x) { + const int k = E1_C; + const int k2 = k + 1; + const struct key kk = { E1_A, 2 }; + static const int s = E1_C; + if (k2 == 11) + x++; + return x + k + kk.a + s + g3; +} + +// CHECK: define dso_local i32 @test8 +// CHECK: [[V:%[0-9a-z]+]] = call i64 @llvm.bpf.preserve.enum.value(i32 {{[0-9]+}}, ptr @{{[0-9]+}}, i64 1), !dbg !{{[0-9]+}}, !llvm.preserve.access.index ![[E1]] +// CHECK: [[T:%[0-9a-z]+]] = trunc i64 [[V]] to i32 +// CHECK: store i32 [[T]], ptr %k, +// CHECK: [[K:%[0-9a-z]+]] = load i32, ptr %k, +// CHECK: add nsw i32 [[K]], 1 +// CHECK: call i64 @llvm.bpf.preserve.enum.value(i32 {{[0-9]+}}, ptr @{{[0-9]+}}, i64 1), !dbg !{{[0-9]+}}, !llvm.preserve.access.index ![[E1]] +// CHECK: [[K2:%[0-9a-z]+]] = load i32, ptr %k2, +// CHECK: icmp eq i32 [[K2]], 11 +// CHECK: load i32, ptr %k, +// CHECK: load i32, ptr %a{{[0-9]+}}, +// CHECK: add nsw i32 %{{[0-9a-z]+}}, 10 +// CHECK-NEXT: add nsw i32 %{{[0-9a-z]+}}, 10 +// CHECK: ret i32 + +// A self-referencing initializer is handled. +int test9(void) { + const int k = k + E1_C; + return k; +} + +// CHECK: define dso_local i32 @test9 +// CHECK: call i64 @llvm.bpf.preserve.enum.value(i32 {{[0-9]+}}, ptr @{{[0-9]+}}, i64 1), !dbg !{{[0-9]+}}, !llvm.preserve.access.index ![[E1]] +// CHECK: ret i32 + +// A conditional operator with relocated enumerators uses control flow, not a +// select, since the backend cannot relocate a select of two relocations. +int test10(int x) { + return x ? E1_A : E1_C; +} + +// CHECK: define dso_local i32 @test10 +// CHECK: br i1 %{{[0-9a-z]+}}, label %cond.true, label %cond.false +// CHECK: cond.true: +// CHECK-NEXT: call i64 @llvm.bpf.preserve.enum.value(i32 {{[0-9]+}}, ptr @{{[0-9]+}}, i64 1), !dbg !{{[0-9]+}}, !llvm.preserve.access.index ![[E1]] +// CHECK: cond.false: +// CHECK-NEXT: call i64 @llvm.bpf.preserve.enum.value(i32 {{[0-9]+}}, ptr @{{[0-9]+}}, i64 1), !dbg !{{[0-9]+}}, !llvm.preserve.access.index ![[E1]] +// CHECK: cond.end: +// CHECK-NEXT: phi i32 +// CHECK-NOT: select +// CHECK: ret i32 + +// CHECK-DAG: ![[AA]] = !DICompositeType(tag: DW_TAG_enumeration_type, name: "AA" +// CHECK-DAG: ![[BB]] = !DIDerivedType(tag: DW_TAG_typedef, name: "__BB" +// CHECK-DAG: ![[E1]] = !DICompositeType(tag: DW_TAG_enumeration_type, name: "e1" +// CHECK-DAG: ![[E2]] = !DIDerivedType(tag: DW_TAG_typedef, name: "e2" +// CHECK-DAG: ![[E4]] = !DICompositeType(tag: DW_TAG_enumeration_type, name: "e4" +// CHECK-DAG: ![[S64]] = !DICompositeType(tag: DW_TAG_enumeration_type, name: "s64"{{.*}}, size: 64, +// CHECK-DAG: ![[U64]] = !DICompositeType(tag: DW_TAG_enumeration_type, name: "u64"{{.*}}, size: 64, diff --git a/clang/test/Misc/pragma-attribute-supported-attributes-list.test b/clang/test/Misc/pragma-attribute-supported-attributes-list.test index 8f8c1aa3d3f9cd..a228186ea9fdb2 100644 --- a/clang/test/Misc/pragma-attribute-supported-attributes-list.test +++ b/clang/test/Misc/pragma-attribute-supported-attributes-list.test @@ -23,7 +23,7 @@ // 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: AvailableOnlyInDefaultEvalMethod (SubjectMatchRule_type_alias) // CHECK-NEXT: BPFFastCall (SubjectMatchRule_hasType_functionType) -// CHECK-NEXT: BPFPreserveAccessIndex (SubjectMatchRule_record) +// CHECK-NEXT: BPFPreserveAccessIndex (SubjectMatchRule_record, SubjectMatchRule_enum) // CHECK-NEXT: BPFPreserveStaticOffset (SubjectMatchRule_record) // CHECK-NEXT: BTFDeclTag (SubjectMatchRule_variable, SubjectMatchRule_function, SubjectMatchRule_record, SubjectMatchRule_field, SubjectMatchRule_type_alias) // CHECK-NEXT: BuiltinAlias (SubjectMatchRule_function) diff --git a/clang/test/Preprocessor/bpf-predefined-macros.c b/clang/test/Preprocessor/bpf-predefined-macros.c index f89e04ae6327af..977f1d3cffd44c 100644 --- a/clang/test/Preprocessor/bpf-predefined-macros.c +++ b/clang/test/Preprocessor/bpf-predefined-macros.c @@ -92,6 +92,9 @@ int ab; #if __BPF_CPU_VERSION__ < 5 && defined(__BPF_FEATURE_HMUL) #error HMUL requires CPU v5 or later #endif +#ifdef __BPF_FEATURE_ENUM_PRESERVE_ACCESS_INDEX +int ac; +#endif // CHECK: int b; // CHECK: int c; @@ -169,6 +172,12 @@ int ab; // CPU_V5: int ab; +// CPU_V1: int ac; +// CPU_V2: int ac; +// CPU_V3: int ac; +// CPU_V4: int ac; +// CPU_V5: int ac; + // CPU_GENERIC: int g; // CPU_PROBE: int f; diff --git a/clang/test/Sema/bpf-attr-preserve-access-index.c b/clang/test/Sema/bpf-attr-preserve-access-index.c index 34b8c917febf54..459637b06584ec 100644 --- a/clang/test/Sema/bpf-attr-preserve-access-index.c +++ b/clang/test/Sema/bpf-attr-preserve-access-index.c @@ -36,13 +36,42 @@ struct p { int a; }; -int a __reloc__; // expected-error {{'preserve_access_index' attribute only applies to structs, unions, and classes}} -struct s *p __reloc__; // expected-error {{'preserve_access_index' attribute only applies to structs, unions, and classes}} +int a __reloc__; // expected-error {{'preserve_access_index' attribute only applies to structs, unions, classes, and enums}} +struct s *p __reloc__; // expected-error {{'preserve_access_index' attribute only applies to structs, unions, classes, and enums}} -void invalid1(const int __reloc__ *arg) {} // expected-error {{'preserve_access_index' attribute only applies to structs, unions, and classes}} -void invalid2(void) { const int __reloc__ *arg; } // expected-error {{'preserve_access_index' attribute only applies to structs, unions, and classes}} +void invalid1(const int __reloc__ *arg) {} // expected-error {{'preserve_access_index' attribute only applies to structs, unions, classes, and enums}} +void invalid2(void) { const int __reloc__ *arg; } // expected-error {{'preserve_access_index' attribute only applies to structs, unions, classes, and enums}} int valid3(struct t4 *arg) { return arg->a + arg->b[3] + arg->c + arg->d; } int valid4(void *arg) { struct local_t { int a; int b; } __reloc__; return ((struct local_t *)arg)->b; } + +enum e1 { E1_A, E1_B } __reloc__; +enum __reloc__ e2 { E2_A }; +typedef enum { E3_A } __reloc__ e3; +enum e4 { E4_A __reloc__ }; // expected-error {{'preserve_access_index' attribute only applies to structs, unions, classes, and enums}} +int valid5(void) { return E1_B + E2_A + E3_A; } + +// An anonymous enum without typedef name cannot be relocated. +enum { A1 } __reloc__; // expected-error {{'preserve_access_index' attribute has no effect on an anonymous enum without a typedef name}} +enum { A2 } __reloc__ v2; // expected-error {{'preserve_access_index' attribute has no effect on an anonymous enum}} +typedef enum { A3 } __reloc__ *t3p; // expected-error {{'preserve_access_index' attribute has no effect on an anonymous enum}} +struct s4 { enum { A4 } __reloc__ f; }; // expected-error {{'preserve_access_index' attribute has no effect on an anonymous enum}} +enum __reloc__ { A9 } __reloc__; // expected-error {{'preserve_access_index' attribute has no effect on an anonymous enum}} +#pragma clang attribute push (__attribute__((preserve_access_index)), apply_to = enum) +enum { A10 } __reloc__; // expected-error {{'preserve_access_index' attribute has no effect on an anonymous enum}} +#pragma clang attribute pop +int invalid3(void) { + enum { A5 } __reloc__; // expected-error {{'preserve_access_index' attribute has no effect on an anonymous enum}} + return A1 + A2 + A3 + A4 + A5; +} + +// No diagnostic with a typedef name, an attribute implicitly added from the +// enclosing record, or #pragma clang attribute. +typedef enum __reloc__ { A6 } t6; +struct s7 { enum { A7 } f; } __reloc__; +#pragma clang attribute push (__attribute__((preserve_access_index)), apply_to = enum) +enum { A8 }; +#pragma clang attribute pop +int valid6(void) { return A6 + A7 + A8; } _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
