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 @@ -220,6 +220,8 @@ unsigned ARMCDECoprocMask : 8; + unsigned BPFCOREKindTypeExist : 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 getBPFCOREKindTypeExist() const { return BPFCOREKindTypeExist; } + /// 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; + BPFCOREKindTypeExist = 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; + BPFCOREKindTypeExist = 8; } 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 @@ -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,21 @@ 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. 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 TypeExistReloc = + getContext().getTargetInfo().getBPFCOREKindTypeExist(); + if (C->getSExtValue() == TypeExistReloc) { + 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 @@ -2564,7 +2564,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,25 +2583,49 @@ return false; } - // The first argument needs to be a record field access. + // The second argument needs to be a constant int + Arg = TheCall->getArg(1); + Optional Value = Arg->getIntegerConstantExpr(Context); + if (!Value) { + Diag(Arg->getBeginLoc(), diag::err_preserve_field_info_not_const) + << 2 << Arg->getSourceRange(); + return true; + } + + // For non TYPE_EXISTENCE 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 relocation type, the argument type + // should be a typedef or a named record type. 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; + bool InvalidArg = false; + if (Arg->getType()->getAsPlaceholderType()) { + InvalidArg = true; + } else if (*Value != Context.getTargetInfo().getBPFCOREKindTypeExist()) { + if (Arg->IgnoreParens()->getObjectKind() != OK_BitField && + !dyn_cast(Arg->IgnoreParens()) && + !dyn_cast(Arg->IgnoreParens())) { + InvalidArg = true; + } + } else { + if (!dyn_cast(Arg->IgnoreParens()) && + !dyn_cast(Arg->IgnoreParens()) && + !dyn_cast(Arg->IgnoreParens())) { + InvalidArg = true; + } else if (!Arg->getType()->getAs()) { + const auto *Ty = Arg->getType()->getUnqualifiedDesugaredType(); + const RecordType *RT = Ty->getAs(); + if (!RT || RT->getDecl()->getDeclName().isEmpty()) + InvalidArg = true; + } } - // The second argument needs to be a constant int - Arg = TheCall->getArg(1); - if (!Arg->isIntegerConstantExpr(Context)) { - Diag(Arg->getBeginLoc(), diag::err_preserve_field_info_not_const) - << 2 << Arg->getSourceRange(); + if (InvalidArg) { + Diag(Arg->getBeginLoc(), diag::err_preserve_field_info_invalid) + << 1 << Arg->getSourceRange(); return true; } 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, 8) + _(*(struct s *)0, 8); +} + +// CHECK: call i32 @llvm.bpf.preserve.field.info.p0s_struct.__s1s(%struct.__s1* %{{[0-9a-z]+}}, i64 8), !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 8), !dbg !{{[0-9]+}}, !llvm.preserve.access.index ![[STRUCT_S:[0-9]+]] + +unsigned unit2() { + __func f; + return _((__func)0, 8) + _(f, 8); +} + +// CHECK: call i32 @llvm.bpf.preserve.field.info.p0f_i32f(i32 ()* null, i64 8), !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 8), !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 @@ -1,14 +1,27 @@ // 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); 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 +59,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, 8); +} + +unsigned valid13() { + return __builtin_preserve_field_info(*(struct s *)0, 8); +} + +unsigned valid14() { + __t t = {}; + return __builtin_preserve_field_info(t, 8); +} + +unsigned valid15() { + return __builtin_preserve_field_info((const __f)0, 8); +} + +unsigned invalid16(struct s *arg) { + return __builtin_preserve_field_info(arg->a + 2, 8); // expected-error {{__builtin_preserve_field_info argument 1 invalid}} +}