diff --git a/clang/include/clang/Basic/BuiltinsBPF.def b/clang/include/clang/Basic/BuiltinsBPF.def --- a/clang/include/clang/Basic/BuiltinsBPF.def +++ b/clang/include/clang/Basic/BuiltinsBPF.def @@ -23,5 +23,11 @@ // Get BTF type id. TARGET_BUILTIN(__builtin_btf_type_id, "Ui.", "t", "") +// Get type information. +TARGET_BUILTIN(__builtin_preserve_type_info, "Ui.", "t", "") + +// Preserve enum value. +TARGET_BUILTIN(__builtin_preserve_enum_value, "Li.", "t", "") + #undef BUILTIN #undef TARGET_BUILTIN 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 @@ -10862,6 +10862,14 @@ "__builtin_preserve_field_info argument %0 not a constant">; def err_btf_type_id_not_const: Error< "__builtin_btf_type_id argument %0 not a constant">; +def err_preserve_type_info_invalid : Error< + "__builtin_preserve_type_info argument %0 invalid">; +def err_preserve_type_info_not_const: Error< + "__builtin_preserve_type_info argument %0 not a constant">; +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 err_bit_cast_non_trivially_copyable : Error< "__builtin_bit_cast %select{source|destination}0 type must be trivially copyable">; 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 @@ -10952,9 +10952,16 @@ Value *CodeGenFunction::EmitBPFBuiltinExpr(unsigned BuiltinID, const CallExpr *E) { assert((BuiltinID == BPF::BI__builtin_preserve_field_info || - BuiltinID == BPF::BI__builtin_btf_type_id) && + BuiltinID == BPF::BI__builtin_btf_type_id || + BuiltinID == BPF::BI__builtin_preserve_type_info || + 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 funciton + // may just be the debuginfo metadata. + static uint32_t BuiltinSeqNum; + switch (BuiltinID) { default: llvm_unreachable("Unexpected BPF builtin"); @@ -11047,6 +11054,63 @@ Fn->setMetadata(LLVMContext::MD_preserve_access_index, DbgInfo); return Fn; } + case BPF::BI__builtin_preserve_type_info: { + if (!getDebugInfo()) { + CGM.Error(E->getExprLoc(), "using builtin function without -g"); + return nullptr; + } + + const Expr *Arg0 = E->getArg(0); + llvm::DIType *DbgInfo = getDebugInfo()->getOrCreateStandaloneType( + Arg0->getType(), Arg0->getExprLoc()); + + ConstantInt *Flag = cast(EmitScalarExpr(E->getArg(1))); + Value *FlagValue = ConstantInt::get(Int64Ty, Flag->getSExtValue()); + Value *SeqNumVal = ConstantInt::get(Int32Ty, BuiltinSeqNum++); + + llvm::Function *FnPreserveTypeInfo = llvm::Intrinsic::getDeclaration( + &CGM.getModule(), llvm::Intrinsic::bpf_preserve_type_info, {}); + CallInst *Fn = + Builder.CreateCall(FnPreserveTypeInfo, {SeqNumVal, FlagValue}); + Fn->setMetadata(LLVMContext::MD_preserve_access_index, DbgInfo); + return Fn; + } + case BPF::BI__builtin_preserve_enum_value: { + if (!getDebugInfo()) { + CGM.Error(E->getExprLoc(), "using builtin function without -g"); + return nullptr; + } + + const Expr *Arg0 = E->getArg(0); + llvm::DIType *DbgInfo = getDebugInfo()->getOrCreateStandaloneType( + Arg0->getType(), Arg0->getExprLoc()); + + // Find enumerator + const auto *UO = cast(Arg0->IgnoreParens()); + const auto *CE = cast(UO->getSubExpr()); + const auto *DR = cast(CE->getSubExpr()); + const auto *Enumerator = cast(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.CreateGlobalStringPtr(EnumStr); + + ConstantInt *Flag = cast(EmitScalarExpr(E->getArg(1))); + Value *FlagValue = ConstantInt::get(Int64Ty, Flag->getSExtValue()); + Value *SeqNumVal = ConstantInt::get(Int32Ty, BuiltinSeqNum++); + + llvm::Function *IntrinsicFn = llvm::Intrinsic::getDeclaration( + &CGM.getModule(), llvm::Intrinsic::bpf_preserve_enum_value, {}); + CallInst *Fn = + Builder.CreateCall(IntrinsicFn, {SeqNumVal, EnumStrVal, FlagValue}); + Fn->setMetadata(LLVMContext::MD_preserve_access_index, DbgInfo); + return Fn; + } } } 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,52 +2557,151 @@ return SemaBuiltinConstantArgRange(TheCall, i, l, u + l); } +static bool isValidBPFPreserveFieldInfoArg(Expr *Arg) { + if (Arg->getType()->getAsPlaceholderType()) + 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. + return (Arg->IgnoreParens()->getObjectKind() == OK_BitField || + dyn_cast(Arg->IgnoreParens()) || + dyn_cast(Arg->IgnoreParens())); +} + +static bool isValidBPFPreserveTypeInfoArg(Expr *Arg) { + QualType ArgType = Arg->getType(); + if (ArgType->getAsPlaceholderType()) + return false; + + // for TYPE_EXISTENCE/TYPE_SIZEOF reloc type + // format: + // 1. __builtin_preserve_type_info(*( *)0, flag); + // 2. var; + // __builtin_preserve_type_info(var, flag); + if (!dyn_cast(Arg->IgnoreParens()) && + !dyn_cast(Arg->IgnoreParens())) + return false; + + // Typedef type. + 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; +} + +static bool isValidBPFPreserveEnumValueArg(Expr *Arg) { + QualType ArgType = Arg->getType(); + if (ArgType->getAsPlaceholderType()) + return false; + + // for ENUM_VALUE_EXISTENCE/ENUM_VALUE reloc type + // format: + // __builtin_preserve_enum_value(*( *), + // flag); + 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 EnumConstantDecl. + const auto *DR = dyn_cast(CE->getSubExpr()); + if (!DR) + return false; + + const EnumConstantDecl *Enumerator = + dyn_cast(DR->getDecl()); + if (!Enumerator) + 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 (EDI == Enumerator) + 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"); + BuiltinID == BPF::BI__builtin_btf_type_id || + BuiltinID == BPF::BI__builtin_preserve_type_info || + BuiltinID == BPF::BI__builtin_preserve_enum_value) && + "unexpected BPF builtin"); if (checkArgCount(*this, TheCall, 2)) return true; - Expr *Arg; - if (BuiltinID == BPF::BI__builtin_btf_type_id) { - // The second argument needs to be a constant int - Arg = TheCall->getArg(1); - if (!Arg->isIntegerConstantExpr(Context)) { - Diag(Arg->getBeginLoc(), diag::err_btf_type_id_not_const) - << 2 << Arg->getSourceRange(); - return true; - } - - TheCall->setType(Context.UnsignedIntTy); - return false; + // The second argument needs to be a constant int + Expr *Arg = TheCall->getArg(1); + Optional Value = Arg->getIntegerConstantExpr(Context); + diag::kind kind; + if (!Value) { + if (BuiltinID == BPF::BI__builtin_preserve_field_info) + kind = diag::err_preserve_field_info_not_const; + else if (BuiltinID == BPF::BI__builtin_btf_type_id) + kind = diag::err_btf_type_id_not_const; + else if (BuiltinID == BPF::BI__builtin_preserve_type_info) + kind = diag::err_preserve_type_info_not_const; + else + kind = diag::err_preserve_enum_value_not_const; + Diag(Arg->getBeginLoc(), kind) << 2 << Arg->getSourceRange(); + 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. + // The first argument 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; + bool ReturnUnsignedInt = true; + if (BuiltinID == BPF::BI__builtin_preserve_field_info) { + if (!isValidBPFPreserveFieldInfoArg(Arg)) { + InvalidArg = true; + kind = diag::err_preserve_field_info_not_field; + } + } else if (BuiltinID == BPF::BI__builtin_preserve_type_info) { + if (!isValidBPFPreserveTypeInfoArg(Arg)) { + InvalidArg = true; + kind = diag::err_preserve_type_info_invalid; + } + } else if (BuiltinID == BPF::BI__builtin_preserve_enum_value) { + if (!isValidBPFPreserveEnumValueArg(Arg)) { + InvalidArg = true; + kind = diag::err_preserve_enum_value_invalid; + } + ReturnUnsignedInt = false; } - // 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(), kind) << 1 << Arg->getSourceRange(); return true; } - TheCall->setType(Context.UnsignedIntTy); + if (ReturnUnsignedInt) + TheCall->setType(Context.UnsignedIntTy); + else + TheCall->setType(Context.UnsignedLongTy); 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,41 @@ +// REQUIRES: bpf-registered-target +// RUN: %clang -target bpf -emit-llvm -S -g %s -o - | FileCheck %s + +#define _(x, y) (__builtin_preserve_type_info((x), (y))) + +struct s { + char a; +}; +typedef int __int; +enum AA { + VAL1 = 1, + VAL2 = 2, +}; + +unsigned unit1() { + struct s v = {}; + return _(v, 0) + _(*(struct s *)0, 0); +} + +// CHECK: call i32 @llvm.bpf.preserve.type.info(i32 0, i64 0), !dbg !{{[0-9]+}}, !llvm.preserve.access.index ![[STRUCT_S:[0-9]+]] +// CHECK: call i32 @llvm.bpf.preserve.type.info(i32 1, i64 0), !dbg !{{[0-9]+}}, !llvm.preserve.access.index ![[STRUCT_S]] + +unsigned unit2() { + __int n; + return _(n, 1) + _(*(__int *)0, 1); +} + +// CHECK: call i32 @llvm.bpf.preserve.type.info(i32 2, i64 1), !dbg !{{[0-9]+}}, !llvm.preserve.access.index ![[TYPEDEF_INT:[0-9]+]] +// CHECK: call i32 @llvm.bpf.preserve.type.info(i32 3, i64 1), !dbg !{{[0-9]+}}, !llvm.preserve.access.index ![[TYPEDEF_INT]] + +unsigned unit3() { + enum AA t; + return _(t, 0) + _(*(enum AA *)0, 1); +} + +// CHECK: call i32 @llvm.bpf.preserve.type.info(i32 4, i64 0), !dbg !{{[0-9]+}}, !llvm.preserve.access.index ![[ENUM_AA:[0-9]+]] +// CHECK: call i32 @llvm.bpf.preserve.type.info(i32 5, i64 1), !dbg !{{[0-9]+}}, !llvm.preserve.access.index ![[ENUM_AA]] + +// CHECK: ![[ENUM_AA]] = !DICompositeType(tag: DW_TAG_enumeration_type, name: "AA" +// CHECK: ![[TYPEDEF_INT]] = !DIDerivedType(tag: DW_TAG_typedef, name: "__int" +// CHECK: ![[STRUCT_S]] = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "s" diff --git a/clang/test/CodeGen/builtins-bpf-preserve-field-info-4.c b/clang/test/CodeGen/builtins-bpf-preserve-field-info-4.c new file mode 100644 --- /dev/null +++ b/clang/test/CodeGen/builtins-bpf-preserve-field-info-4.c @@ -0,0 +1,32 @@ +// REQUIRES: bpf-registered-target +// RUN: %clang -target bpf -emit-llvm -S -g %s -o - | FileCheck %s + +#define _(x, y) (__builtin_preserve_enum_value((x), (y))) + +enum AA { + VAL1 = 2, + VAL2 = 0xffffffff80000000UL, +}; +typedef enum { VAL10 = -2, VAL11 = 0xffff8000, } __BB; + +unsigned unit1() { + return _(*(enum AA *)VAL1, 0) + _(*(__BB *)VAL10, 1); +} + +unsigned unit2() { + return _(*(enum AA *)VAL2, 0) + _(*(__BB *)VAL11, 1); +} + +// CHECK: @0 = private unnamed_addr constant [7 x i8] c"VAL1:2\00", align 1 +// CHECK: @1 = private unnamed_addr constant [9 x i8] c"VAL10:-2\00", align 1 +// CHECK: @2 = private unnamed_addr constant [17 x i8] c"VAL2:-2147483648\00", align 1 +// CHECK: @3 = private unnamed_addr constant [17 x i8] c"VAL11:4294934528\00", align 1 + +// CHECK: call i64 @llvm.bpf.preserve.enum.value(i32 0, i8* getelementptr inbounds ([7 x i8], [7 x i8]* @0, i32 0, i32 0), i64 0), !dbg !{{[0-9]+}}, !llvm.preserve.access.index ![[ENUM_AA:[0-9]+]] +// CHECK: call i64 @llvm.bpf.preserve.enum.value(i32 1, i8* getelementptr inbounds ([9 x i8], [9 x i8]* @1, i32 0, i32 0), i64 1), !dbg !{{[0-9]+}}, !llvm.preserve.access.index ![[TYPEDEF_ENUM:[0-9]+]] + +// CHECK: call i64 @llvm.bpf.preserve.enum.value(i32 2, i8* getelementptr inbounds ([17 x i8], [17 x i8]* @2, i32 0, i32 0), i64 0), !dbg !{{[0-9]+}}, !llvm.preserve.access.index ![[ENUM_AA]] +// CHECK: call i64 @llvm.bpf.preserve.enum.value(i32 3, i8* getelementptr inbounds ([17 x i8], [17 x i8]* @3, i32 0, i32 0), i64 1), !dbg !{{[0-9]+}}, !llvm.preserve.access.index ![[TYPEDEF_ENUM]] + +// CHECK: ![[ENUM_AA]] = !DICompositeType(tag: DW_TAG_enumeration_type, name: "AA" +// CHECK: ![[TYPEDEF_ENUM]] = !DIDerivedType(tag: DW_TAG_typedef, name: "__BB" 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,7 +1,28 @@ // 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 = 10, + VAL2 = 0xffffffff80000000UL, +}; +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}} @@ -46,3 +67,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_type_info(t, 0) + + __builtin_preserve_type_info(*(struct s *)0, 1); +} + +unsigned valid13() { + __t t; + return __builtin_preserve_type_info(t, 1) + + __builtin_preserve_type_info(*(__t *)0, 0); +} + +unsigned valid14() { + enum AA t; + return __builtin_preserve_type_info(t, 0) + + __builtin_preserve_type_info(*(enum AA *)0, 1); +} + +unsigned valid15() { + return __builtin_preserve_enum_value(*(enum AA *)VAL1, 1) + + __builtin_preserve_enum_value(*(enum AA *)VAL2, 1); +} + +unsigned invalid16() { + return __builtin_preserve_enum_value(*(enum AA *)0, 1); // expected-error {{__builtin_preserve_enum_value argument 1 invalid}} +} + +unsigned invalid17() { + return __builtin_preserve_enum_value(*(enum AA *)VAL10, 1); // expected-error {{__builtin_preserve_enum_value argument 1 invalid}} +} + +unsigned invalid18(struct s *arg) { + return __builtin_preserve_type_info(arg->a + 2, 0); // expected-error {{__builtin_preserve_type_info argument 1 invalid}} +} diff --git a/llvm/include/llvm/IR/IntrinsicsBPF.td b/llvm/include/llvm/IR/IntrinsicsBPF.td --- a/llvm/include/llvm/IR/IntrinsicsBPF.td +++ b/llvm/include/llvm/IR/IntrinsicsBPF.td @@ -26,4 +26,10 @@ def int_bpf_btf_type_id : GCCBuiltin<"__builtin_bpf_btf_type_id">, Intrinsic<[llvm_i32_ty], [llvm_any_ty, llvm_any_ty, llvm_i64_ty], [IntrNoMem]>; + def int_bpf_preserve_type_info : GCCBuiltin<"__builtin_bpf_preserve_type_info">, + Intrinsic<[llvm_i32_ty], [llvm_i32_ty, llvm_i64_ty], + [IntrNoMem]>; + def int_bpf_preserve_enum_value : GCCBuiltin<"__builtin_bpf_preserve_enum_value">, + Intrinsic<[llvm_i64_ty], [llvm_i32_ty, llvm_ptr_ty, llvm_i64_ty], + [IntrNoMem]>; }