// 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
"__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">;
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");
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<ConstantInt>(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<UnaryOperator>(Arg0->IgnoreParens());
+ const auto *CE = cast<CStyleCastExpr>(UO->getSubExpr());
+ 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.CreateGlobalStringPtr(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 = 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;
+ }
}
}
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<MemberExpr>(Arg->IgnoreParens()) ||
+ dyn_cast<ArraySubscriptExpr>(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(*(<type> *)0, flag);
+ // 2. <type> var;
+ // __builtin_preserve_type_info(var, flag);
+ if (!dyn_cast<DeclRefExpr>(Arg->IgnoreParens()) &&
+ !dyn_cast<UnaryOperator>(Arg->IgnoreParens()))
+ return false;
+
+ // Typedef type.
+ if (ArgType->getAs<TypedefType>())
+ return true;
+
+ // Record type or Enum type.
+ const Type *Ty = ArgType->getUnqualifiedDesugaredType();
+ if (const auto *RT = Ty->getAs<RecordType>()) {
+ if (!RT->getDecl()->getDeclName().isEmpty())
+ return true;
+ } else if (const auto *ET = Ty->getAs<EnumType>()) {
+ 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(*(<enum_type> *)<enum_value>,
+ // flag);
+ const auto *UO = dyn_cast<UnaryOperator>(Arg->IgnoreParens());
+ if (!UO)
+ return false;
+
+ const auto *CE = dyn_cast<CStyleCastExpr>(UO->getSubExpr());
+ if (!CE || CE->getCastKind() != CK_IntegralToPointer)
+ return false;
+
+ // The integer must be from an EnumConstantDecl.
+ const auto *DR = dyn_cast<DeclRefExpr>(CE->getSubExpr());
+ if (!DR)
+ return false;
+
+ const EnumConstantDecl *Enumerator =
+ dyn_cast<EnumConstantDecl>(DR->getDecl());
+ if (!Enumerator)
+ return false;
+
+ // The type must be EnumType.
+ const Type *Ty = ArgType->getUnqualifiedDesugaredType();
+ const auto *ET = Ty->getAs<EnumType>();
+ 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<llvm::APSInt> 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<MemberExpr>(Arg->IgnoreParens()) &&
- !dyn_cast<ArraySubscriptExpr>(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;
}
--- /dev/null
+// 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"
--- /dev/null
+// 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"
// 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}}
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}}
+}
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]>;
}