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 @@ -10838,8 +10838,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 @@ -220,6 +220,8 @@ unsigned ARMCDECoprocMask : 8; + unsigned BPFCOREKindExistence : 8; + unsigned MaxOpenCLWorkGroupSize; // TargetInfo Constructor. Default initializes all fields. @@ -860,6 +862,9 @@ /// as Custom Datapath. uint32_t getARMCDECoprocMask() const { return ARMCDECoprocMask; } + /// For BPF targets return the CORE relocation kind FIELD_EXISTENCE. + uint32_t getBPFCOREKindExistence() const { return BPFCOREKindExistence; } + /// Returns whether the passed in string is a valid clobber in an /// inline asm statement. /// diff --git a/clang/lib/Basic/TargetInfo.cpp b/clang/lib/Basic/TargetInfo.cpp --- a/clang/lib/Basic/TargetInfo.cpp +++ b/clang/lib/Basic/TargetInfo.cpp @@ -116,6 +116,7 @@ IsRenderScriptTarget = false; HasAArch64SVETypes = false; ARMCDECoprocMask = 0; + BPFCOREKindExistence = 0; // Default to no types using fpret. RealTypeUsesObjCFPRet = 0; 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 @@ -42,6 +42,7 @@ MaxAtomicPromoteWidth = 64; MaxAtomicInlineWidth = 64; TLSSupported = false; + BPFCOREKindExistence = 2; } void getTargetDefines(const LangOptions &Opts, 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 @@ -10948,11 +10948,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); } @@ -10960,8 +10963,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))); @@ -10971,7 +10978,31 @@ 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 FIELD_EXISTENCE if the argument is + // not a member access or array subscript expression. This is + // used to capture the existence of a typedef or a struct/union type, + // for which we do not have IR intrinsics generated. + uint32_t ExistenceReloc = + getContext().getTargetInfo().getBPFCOREKindExistence(); + if (C->getSExtValue() == ExistenceReloc) { + bool AttachMeta = false; + if (Arg->IgnoreParens()->getObjectKind() != OK_BitField && + !dyn_cast(Arg->IgnoreParens()) && + !dyn_cast(Arg->IgnoreParens())) { + AttachMeta = true; + } + + if (AttachMeta) { + 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 @@ -2563,7 +2563,7 @@ 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; @@ -2583,20 +2583,6 @@ 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); llvm::APSInt Value; @@ -2606,6 +2592,41 @@ return true; } + // 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 BPF CORE FIELD_EXISTENCE relocation type, we + // further permits the argument type to be a typedef or + // a named record type. + Arg = TheCall->getArg(0); + bool InvalidArg = false; + if (Arg->getType()->getAsPlaceholderType()) { + InvalidArg = true; + } else if (Arg->IgnoreParens()->getObjectKind() != OK_BitField && + !dyn_cast(Arg->IgnoreParens()) && + !dyn_cast(Arg->IgnoreParens())) { + if (Value != Context.getTargetInfo().getBPFCOREKindExistence()) { + InvalidArg = true; + } else if (!dyn_cast(Arg->IgnoreParens()) && + !dyn_cast(Arg->IgnoreParens()) && + !dyn_cast(Arg->IgnoreParens())) { + InvalidArg = true; + } else if (!Arg->getType()->getAs()) { + auto *Ty = Arg->getType()->getUnqualifiedDesugaredType(); + const RecordType* RT = Ty->getAs(); + if (!RT || RT->getDecl()->getDeclName().isEmpty()) + InvalidArg = true; + } + } + + if (InvalidArg) { + 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-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,33 @@ +// 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 struct { + char a; +} __s1; + +typedef int (*__func)(void); + +unsigned unit1() { + const __s1 v = {}; + return _(v, 2) + _(*(struct s *)0, 2); +} + +// CHECK: call i32 @llvm.bpf.preserve.field.info.p0s_struct.__s1s(%struct.__s1* %{{[0-9a-z]+}}, i64 2), !dbg !{{[0-9]+}}, !llvm.preserve.access.index ![[CONST_S1:[0-9]+]] +// CHECK: call i32 @llvm.bpf.preserve.field.info.p0s_struct.ss(%struct.s* null, i64 2), !dbg !{{[0-9]+}}, !llvm.preserve.access.index ![[STRUCT_S:[0-9]+]] + +unsigned unit2() { + __func f; + return _((__func)0, 2) + _(f, 2); +} + +// CHECK: call i32 @llvm.bpf.preserve.field.info.p0f_i32f(i32 ()* null, i64 2), !dbg !{{[0-9]+}}, !llvm.preserve.access.index ![[TYPEDEF:[0-9]+]] +// CHECK: call i32 @llvm.bpf.preserve.field.info.p0p0f_i32f(i32 ()** %{{[0-9a-z]+}}, i64 2), !dbg !{{[0-9]+}}, !llvm.preserve.access.index ![[TYPEDEF:[0-9]+]] + +// CHECK: ![[STRUCT_S]] = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "s" +// CHECK: ![[TYPEDEF]] = !DIDerivedType(tag: DW_TAG_typedef, name: "__func" +// CHECK: ![[CONST_S1]] = !DIDerivedType(tag: DW_TAG_const_type 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 @@ -2,13 +2,15 @@ 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); 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 +48,25 @@ 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, 2); +} + +unsigned valid13() { + return __builtin_preserve_field_info(*(struct s *)0, 2); +} + +unsigned valid14() { + __t t = {}; + return __builtin_preserve_field_info(t, 2); +} + +unsigned valid15() { + return __builtin_preserve_field_info((const __f)0, 2); +} + +unsigned invalid16(struct s *arg) { + return __builtin_preserve_field_info(arg->a + 2, 2); // expected-error {{__builtin_preserve_field_info argument 1 invalid}} +}