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 @@ -10850,8 +10850,8 @@ "import %select{module|name}0 cannot be applied to a function with a definition">, InGroup; -def err_preserve_field_info_not_field : Error< - "__builtin_preserve_field_info argument %0 not a field access">; +def err_preserve_field_info_invalid : Error< + "__builtin_preserve_field_info argument %0 invalid">; def err_preserve_field_info_not_const: Error< "__builtin_preserve_field_info argument %0 not a constant">; def err_btf_type_id_not_const: Error< diff --git a/clang/include/clang/Basic/TargetInfo.h b/clang/include/clang/Basic/TargetInfo.h --- a/clang/include/clang/Basic/TargetInfo.h +++ b/clang/include/clang/Basic/TargetInfo.h @@ -856,6 +856,15 @@ /// available on this target. bool hasAArch64SVETypes() const { return HasAArch64SVETypes; } + /// Returns whether or not the BPF target should add + /// debuginfo as meta data for a given relocation kind. + /// Also set IsEnumValue depending on whether the reloc kind is + /// for ENUM_VALUE. + virtual bool BPFTagIntrinsic(int64_t RelocKind, bool &IsEnumValue) const { + IsEnumValue = false; + return false; + } + /// For ARM targets returns a mask defining which coprocessors are configured /// as Custom Datapath. uint32_t getARMCDECoprocMask() const { return ARMCDECoprocMask; } diff --git a/clang/lib/Basic/Targets/BPF.h b/clang/lib/Basic/Targets/BPF.h --- a/clang/lib/Basic/Targets/BPF.h +++ b/clang/lib/Basic/Targets/BPF.h @@ -44,6 +44,19 @@ TLSSupported = false; } + bool BPFTagIntrinsic(int64_t RelocKind, bool &IsEnumValue) const override { + // Reloc Kind 8: TYPE_EXISTENCE, 9: TYPE_SIZEOF, 10: ENUM_VALUE + if (RelocKind == 8 || RelocKind == 9) { + IsEnumValue = false; + return true; + } else if (RelocKind == 10) { + IsEnumValue = true; + return true; + } else { + return false; + } + } + void getTargetDefines(const LangOptions &Opts, MacroBuilder &Builder) const override; diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp --- a/clang/lib/CodeGen/CGBuiltin.cpp +++ b/clang/lib/CodeGen/CGBuiltin.cpp @@ -10960,11 +10960,14 @@ llvm_unreachable("Unexpected BPF builtin"); case BPF::BI__builtin_preserve_field_info: { const Expr *Arg = E->getArg(0); + bool IsLValue = Arg->isLValue(); bool IsBitField = Arg->IgnoreParens()->getObjectKind() == OK_BitField; if (!getDebugInfo()) { CGM.Error(E->getExprLoc(), "using __builtin_preserve_field_info() without -g"); + if (!IsLValue) + return EmitScalarExpr(Arg); return IsBitField ? EmitLValue(Arg).getBitFieldPointer() : EmitLValue(Arg).getPointer(*this); } @@ -10972,8 +10975,12 @@ // Enable underlying preserve_*_access_index() generation. bool OldIsInPreservedAIRegion = IsInPreservedAIRegion; IsInPreservedAIRegion = true; - Value *FieldAddr = IsBitField ? EmitLValue(Arg).getBitFieldPointer() - : EmitLValue(Arg).getPointer(*this); + Value *FieldAddr; + if (!IsLValue) + FieldAddr = EmitScalarExpr(Arg); + else + FieldAddr = IsBitField ? EmitLValue(Arg).getBitFieldPointer() + : EmitLValue(Arg).getPointer(*this); IsInPreservedAIRegion = OldIsInPreservedAIRegion; ConstantInt *C = cast(EmitScalarExpr(E->getArg(1))); @@ -10983,7 +10990,23 @@ llvm::Function *FnGetFieldInfo = llvm::Intrinsic::getDeclaration( &CGM.getModule(), llvm::Intrinsic::bpf_preserve_field_info, {FieldAddr->getType()}); - return Builder.CreateCall(FnGetFieldInfo, {FieldAddr, InfoKind}); + CallInst *Fn = Builder.CreateCall(FnGetFieldInfo, {FieldAddr, InfoKind}); + + // Add debuginfo metadata to bpf_preserve_field_info intrinsic + // for CORE relocation type TYPE_EXISTENCE/TYPE_SIZEOF/ENUM_VALUE. + // This is used to capture the existence/size of a typedef/record/enum + // type or a enum value, for which we do not have IR intrinsics + // generated. + bool IsEnumValue; + bool TagDbgInfo = getContext().getTargetInfo().BPFTagIntrinsic( + C->getSExtValue(), IsEnumValue); + if (TagDbgInfo) { + llvm::DIType *DbgInfo = getDebugInfo()->getOrCreateStandaloneType( + E->getArg(0)->getType(), E->getArg(0)->getExprLoc()); + Fn->setMetadata(LLVMContext::MD_preserve_access_index, DbgInfo); + } + + return Fn; } case BPF::BI__builtin_btf_type_id: { Value *FieldVal = nullptr; diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp --- a/clang/lib/Sema/SemaChecking.cpp +++ b/clang/lib/Sema/SemaChecking.cpp @@ -2557,11 +2557,111 @@ return SemaBuiltinConstantArgRange(TheCall, i, l, u + l); } +// For non TYPE_EXISTENCE/TYPE_SIZEOF/ENUM_VALUE relocation type, +// the first argument needs to be a record field access. +// If it is an array element access, we delay decision +// to BPF backend to check whether the access is a +// field access or not. +// +// For TYPE_EXISTENCE/TYPE_SIZEOF relocation type, +// the argument type should be a typedef, a named record type, +// or a named enum type. +// +// For ENUM_VALUE relocation type, the argument type should be +// an enum type or a typedef resolving into an enum type. +// The enum value must be one of supported values in the +// enum type. +static bool isValidBPFPreserveFieldInfoArg(Sema &S, Expr *Arg, + int64_t RelocKind) { + QualType ArgType = Arg->getType(); + if (ArgType->getAsPlaceholderType()) + return false; + + // struct/union field based relocations + bool IsEnumValue; + if (!S.Context.getTargetInfo().BPFTagIntrinsic(RelocKind, IsEnumValue)) { + if (Arg->IgnoreParens()->getObjectKind() == OK_BitField || + dyn_cast(Arg->IgnoreParens()) || + dyn_cast(Arg->IgnoreParens())) { + return true; + } + return false; + } + + // for TYPE_EXISTENCE/TYPE_SIZEOF reloc type + // format: + // 1. __builtin_preserve_field_info(*( *)0, reloc_kind); + // 2. var; + // __builtin_preserve_field_info(var, reloc_kind); + if (!IsEnumValue) { + if (!dyn_cast(Arg->IgnoreParens()) && + !dyn_cast(Arg->IgnoreParens())) + return false; + + if (ArgType->getAs()) + return true; + + // Record type or Enum type. + const Type *Ty = ArgType->getUnqualifiedDesugaredType(); + if (const auto *RT = Ty->getAs()) { + if (!RT->getDecl()->getDeclName().isEmpty()) + return true; + } else if (const auto *ET = Ty->getAs()) { + if (!ET->getDecl()->getDeclName().isEmpty()) + return true; + } + return false; + } + + // for ENUM_VALUE reloc type + // format: + // __builtin_preserve_field_info(*( *), + // ENUM_VALUE); + const auto *UO = dyn_cast(Arg->IgnoreParens()); + if (!UO) + return false; + + const auto *CE = dyn_cast(UO->getSubExpr()); + if (!CE || CE->getCastKind() != CK_IntegralToPointer) + return false; + + // The integer must be from an IntegerLiteral or an EnumConstantDecl. + llvm::APInt EnumValue; + const EnumConstantDecl *Enumerator = nullptr; + if (const auto *I = dyn_cast(CE->getSubExpr())) { + EnumValue = I->getValue(); + } else if (const auto *DR = dyn_cast(CE->getSubExpr())) { + Enumerator = dyn_cast(DR->getDecl()); + if (!Enumerator) + return false; + } else { + return false; + } + + // The type must be EnumType. + const Type *Ty = ArgType->getUnqualifiedDesugaredType(); + const auto *ET = Ty->getAs(); + if (!ET) + return false; + + // The enum value must be supported. + for (auto *EDI : ET->getDecl()->enumerators()) { + if (Enumerator) { + if (EDI == Enumerator) + return true; + } else if (EDI->getInitVal() == EnumValue.getLimitedValue()) { + return true; + } + } + + return false; +} + bool Sema::CheckBPFBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall) { assert((BuiltinID == BPF::BI__builtin_preserve_field_info || BuiltinID == BPF::BI__builtin_btf_type_id) && - "unexpected ARM builtin"); + "unexpected BPF builtin"); if (checkArgCount(*this, TheCall, 2)) return true; @@ -2580,28 +2680,23 @@ return false; } - // The first argument needs to be a record field access. - // If it is an array element access, we delay decision - // to BPF backend to check whether the access is a - // field access or not. - Arg = TheCall->getArg(0); - if (Arg->getType()->getAsPlaceholderType() || - (Arg->IgnoreParens()->getObjectKind() != OK_BitField && - !dyn_cast(Arg->IgnoreParens()) && - !dyn_cast(Arg->IgnoreParens()))) { - Diag(Arg->getBeginLoc(), diag::err_preserve_field_info_not_field) - << 1 << Arg->getSourceRange(); - return true; - } - // The second argument needs to be a constant int Arg = TheCall->getArg(1); - if (!Arg->isIntegerConstantExpr(Context)) { + Optional Value = Arg->getIntegerConstantExpr(Context); + if (!Value) { Diag(Arg->getBeginLoc(), diag::err_preserve_field_info_not_const) << 2 << Arg->getSourceRange(); return true; } + int64_t RelocKind = Value->getExtValue(); + Arg = TheCall->getArg(0); + if (!isValidBPFPreserveFieldInfoArg(*this, Arg, RelocKind)) { + Diag(Arg->getBeginLoc(), diag::err_preserve_field_info_invalid) + << 1 << Arg->getSourceRange(); + return true; + } + TheCall->setType(Context.UnsignedIntTy); return false; } diff --git a/clang/test/CodeGen/builtins-bpf-preserve-field-info-1.c b/clang/test/CodeGen/builtins-bpf-preserve-field-info-1.c --- a/clang/test/CodeGen/builtins-bpf-preserve-field-info-1.c +++ b/clang/test/CodeGen/builtins-bpf-preserve-field-info-1.c @@ -14,22 +14,22 @@ }; unsigned unit1(struct s1 *arg) { - return _(arg->a, 10) + _(arg->b, 10); + return _(arg->a, 4) + _(arg->b, 4); } // CHECK: define dso_local i32 @unit1 // CHECK: call i8* @llvm.preserve.struct.access.index.p0i8.p0s_struct.s1s(%struct.s1* %{{[0-9a-z]+}}, i32 0, i32 0), !dbg !{{[0-9]+}}, !llvm.preserve.access.index ![[STRUCT_S1:[0-9]+]] -// CHECK: call i32 @llvm.bpf.preserve.field.info.p0i8(i8* %{{[0-9a-z]+}}, i64 10), !dbg !{{[0-9]+}} +// CHECK: call i32 @llvm.bpf.preserve.field.info.p0i8(i8* %{{[0-9a-z]+}}, i64 4), !dbg !{{[0-9]+}} // CHECK: call i8* @llvm.preserve.struct.access.index.p0i8.p0s_struct.s1s(%struct.s1* %{{[0-9a-z]+}}, i32 1, i32 1), !dbg !{{[0-9]+}}, !llvm.preserve.access.index ![[STRUCT_S1:[0-9]+]] -// CHECK: call i32 @llvm.bpf.preserve.field.info.p0i8(i8* %{{[0-9a-z]+}}, i64 10), !dbg !{{[0-9]+}} +// CHECK: call i32 @llvm.bpf.preserve.field.info.p0i8(i8* %{{[0-9a-z]+}}, i64 4), !dbg !{{[0-9]+}} unsigned unit2(union u1 *arg) { - return _(arg->a, 10) + _(arg->b, 10); + return _(arg->a, 4) + _(arg->b, 4); } // CHECK: define dso_local i32 @unit2 // CHECK: call %union.u1* @llvm.preserve.union.access.index.p0s_union.u1s.p0s_union.u1s(%union.u1* %{{[0-9a-z]+}}, i32 0), !dbg !{{[0-9]+}}, !llvm.preserve.access.index ![[UNION_U1:[0-9]+]] -// CHECK: call i32 @llvm.bpf.preserve.field.info.p0i8(i8* %{{[0-9a-z]+}}, i64 10), !dbg !{{[0-9]+}} +// CHECK: call i32 @llvm.bpf.preserve.field.info.p0i8(i8* %{{[0-9a-z]+}}, i64 4), !dbg !{{[0-9]+}} // CHECK: call i8* @llvm.preserve.struct.access.index.p0i8.p0s_union.u1s(%union.u1* %{{[0-9a-z]+}}, i32 0, i32 1), !dbg !{{[0-9]+}}, !llvm.preserve.access.index ![[UNION_U1:[0-9]+]] -// CHECK: call i32 @llvm.bpf.preserve.field.info.p0i8(i8* %{{[0-9a-z]+}}, i64 10), !dbg !{{[0-9]+}} +// CHECK: call i32 @llvm.bpf.preserve.field.info.p0i8(i8* %{{[0-9a-z]+}}, i64 4), !dbg !{{[0-9]+}} // CHECK: ![[STRUCT_S1]] = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "s1" // CHECK: ![[UNION_U1]] = distinct !DICompositeType(tag: DW_TAG_union_type, name: "u1" diff --git a/clang/test/CodeGen/builtins-bpf-preserve-field-info-2.c b/clang/test/CodeGen/builtins-bpf-preserve-field-info-2.c --- a/clang/test/CodeGen/builtins-bpf-preserve-field-info-2.c +++ b/clang/test/CodeGen/builtins-bpf-preserve-field-info-2.c @@ -12,15 +12,15 @@ }; unsigned unit1(struct s2 *arg) { - return _(arg->s.a, 10) + _(arg->s.b, 10); + return _(arg->s.a, 4) + _(arg->s.b, 4); } // CHECK: define dso_local i32 @unit1 // CHECK: call %struct.s1* @llvm.preserve.struct.access.index.p0s_struct.s1s.p0s_struct.s2s(%struct.s2* %{{[0-9a-z]+}}, i32 0, i32 0), !dbg !{{[0-9]+}}, !llvm.preserve.access.index ![[STRUCT_S2:[0-9]+]] // CHECK: call i8* @llvm.preserve.struct.access.index.p0i8.p0s_struct.s1s(%struct.s1* %{{[0-9a-z]+}}, i32 0, i32 0), !dbg !{{[0-9]+}}, !llvm.preserve.access.index ![[STRUCT_S1:[0-9]+]] -// CHECK: call i32 @llvm.bpf.preserve.field.info.p0i8(i8* %{{[0-9a-z]+}}, i64 10), !dbg !{{[0-9]+}} +// CHECK: call i32 @llvm.bpf.preserve.field.info.p0i8(i8* %{{[0-9a-z]+}}, i64 4), !dbg !{{[0-9]+}} // CHECK: call %struct.s1* @llvm.preserve.struct.access.index.p0s_struct.s1s.p0s_struct.s2s(%struct.s2* %{{[0-9a-z]+}}, i32 0, i32 0), !dbg !{{[0-9]+}}, !llvm.preserve.access.index ![[STRUCT_S2:[0-9]+]] // CHECK: call i8* @llvm.preserve.struct.access.index.p0i8.p0s_struct.s1s(%struct.s1* %{{[0-9a-z]+}}, i32 1, i32 1), !dbg !{{[0-9]+}}, !llvm.preserve.access.index ![[STRUCT_S1:[0-9]+]] -// CHECK: call i32 @llvm.bpf.preserve.field.info.p0i8(i8* %{{[0-9a-z]+}}, i64 10), !dbg !{{[0-9]+}} +// CHECK: call i32 @llvm.bpf.preserve.field.info.p0i8(i8* %{{[0-9a-z]+}}, i64 4), !dbg !{{[0-9]+}} // CHECK: ![[STRUCT_S2]] = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "s2" // CHECK: ![[STRUCT_S1]] = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "s1" diff --git a/clang/test/CodeGen/builtins-bpf-preserve-field-info-3.c b/clang/test/CodeGen/builtins-bpf-preserve-field-info-3.c new file mode 100644 --- /dev/null +++ b/clang/test/CodeGen/builtins-bpf-preserve-field-info-3.c @@ -0,0 +1,55 @@ +// REQUIRES: bpf-registered-target +// RUN: %clang -target bpf -emit-llvm -S -g %s -o - | FileCheck %s + +#define _(x, y) (__builtin_preserve_field_info((x), (y))) + +struct s { + char a; +}; +typedef int __int; +enum AA { + VAL1 = 1, + VAL2 = 2, +}; +typedef enum AA __AA; + +unsigned unit1() { + struct s v = {}; + return _(v, 8) + _(*(struct s *)0, 8); +} + +// CHECK: call i32 @llvm.bpf.preserve.field.info.p0s_struct.ss(%struct.s* %{{[0-9a-z]+}}, i64 8), !dbg !{{[0-9]+}}, !llvm.preserve.access.index ![[STRUCT_S:[0-9]+]] +// CHECK: call i32 @llvm.bpf.preserve.field.info.p0s_struct.ss(%struct.s* null, i64 8), !dbg !{{[0-9]+}}, !llvm.preserve.access.index ![[STRUCT_S]] + +unsigned unit2() { + __int n; + return _(n, 8) + _(*(__int *)0, 8); +} + +// CHECK: call i32 @llvm.bpf.preserve.field.info.p0i32(i32* %{{[0-9a-z]+}}, i64 8), !dbg !{{[0-9]+}}, !llvm.preserve.access.index ![[TYPEDEF_INT:[0-9]+]] +// CHECK: call i32 @llvm.bpf.preserve.field.info.p0i32(i32* null, i64 8), !dbg !{{[0-9]+}}, !llvm.preserve.access.index ![[TYPEDEF_INT]] + +unsigned unit3() { + enum AA t; + return _(t, 8) + _(*(enum AA *)0, 8); +} + +// CHECK: call i32 @llvm.bpf.preserve.field.info.p0i32(i32* %{{[0-9a-z]+}}, i64 8), !dbg !{{[0-9]+}}, !llvm.preserve.access.index ![[ENUM_AA:[0-9]+]] +// CHECK: call i32 @llvm.bpf.preserve.field.info.p0i32(i32* null, i64 8), !dbg !{{[0-9]+}}, !llvm.preserve.access.index ![[ENUM_AA]] + +unsigned unit4() { + return _(*(enum AA *)VAL2, 10); +} + +// CHECK: call i32 @llvm.bpf.preserve.field.info.p0i32(i32* inttoptr (i64 2 to i32*), i64 10), !dbg !{{[0-9]+}}, !llvm.preserve.access.index ![[ENUM_AA]] + +unsigned unit5() { + return _(*(__AA *)VAL2, 10); +} + +// CHECK: call i32 @llvm.bpf.preserve.field.info.p0i32(i32* inttoptr (i64 2 to i32*), i64 10), !dbg !{{[0-9]+}}, !llvm.preserve.access.index ![[TYPEDEF_ENUM:[0-9]+]] + +// CHECK: ![[ENUM_AA]] = !DICompositeType(tag: DW_TAG_enumeration_type, name: "AA" +// CHECK: ![[STRUCT_S]] = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "s" +// CHECK: ![[TYPEDEF_INT]] = !DIDerivedType(tag: DW_TAG_typedef, name: "__int" +// CHECK: ![[TYPEDEF_ENUM]] = !DIDerivedType(tag: DW_TAG_typedef, name: "__AA" diff --git a/clang/test/Sema/builtins-bpf.c b/clang/test/Sema/builtins-bpf.c --- a/clang/test/Sema/builtins-bpf.c +++ b/clang/test/Sema/builtins-bpf.c @@ -1,14 +1,36 @@ // RUN: %clang_cc1 -x c -triple bpf-pc-linux-gnu -dwarf-version=4 -fsyntax-only -verify %s -struct s { int a; int b[4]; int c:1; }; -union u { int a; int b[4]; int c:1; }; +struct s { + int a; + int b[4]; + int c:1; +}; +union u { + int a; + int b[4]; + int c:1; +}; +typedef struct { + int a; + int b; +} __t; +typedef int (*__f)(void); +enum AA { + VAL1 = 1, + VAL2 = 2, + VAL3 = 10, +}; +typedef enum { + VAL10 = 10, + VAL11 = 11, +} __BB; unsigned invalid1(const int *arg) { - return __builtin_preserve_field_info(arg, 1); // expected-error {{__builtin_preserve_field_info argument 1 not a field access}} + return __builtin_preserve_field_info(arg, 1); // expected-error {{__builtin_preserve_field_info argument 1 invalid}} } unsigned invalid2(const int *arg) { - return __builtin_preserve_field_info(*arg, 1); // expected-error {{__builtin_preserve_field_info argument 1 not a field access}} + return __builtin_preserve_field_info(*arg, 1); // expected-error {{__builtin_preserve_field_info argument 1 invalid}} } void *invalid3(struct s *arg) { @@ -46,3 +68,38 @@ unsigned invalid11(struct s *arg, int info_kind) { return __builtin_preserve_field_info(arg->a, info_kind); // expected-error {{__builtin_preserve_field_info argument 2 not a constant}} } + +unsigned valid12() { + const struct s t; + return __builtin_preserve_field_info(t, 8) + + __builtin_preserve_field_info(*(struct s *)0, 8); +} + +unsigned valid13() { + __t t; + return __builtin_preserve_field_info(t, 8) + + __builtin_preserve_field_info(*(__t *)0, 8); +} + +unsigned valid14() { + enum AA t; + return __builtin_preserve_field_info(t, 8) + + __builtin_preserve_field_info(*(enum AA *)0, 8); +} + +unsigned invalid15() { + return __builtin_preserve_field_info(*(__BB *)2, 10); // expected-error {{__builtin_preserve_field_info argument 1 invalid}} +} + +unsigned invalid16() { + return __builtin_preserve_field_info(*(__BB *)VAL3, 10); // expected-error {{__builtin_preserve_field_info argument 1 invalid}} +} + +unsigned invalid17(struct s *arg) { + return __builtin_preserve_field_info(arg->a + 2, 8); // expected-error {{__builtin_preserve_field_info argument 1 invalid}} +} + +unsigned invalid18() { + enum AA t = 1; + return __builtin_preserve_field_info(t, 10); // expected-error {{__builtin_preserve_field_info argument 1 invalid}} +}