Skip to content

Commit

Permalink
[clang][BPF] support type exist/size and enum exist/value relocations
Browse files Browse the repository at this point in the history
This patch added the following additional compile-once
run-everywhere (CO-RE) relocations:
  - existence/size of typedef, struct/union or enum type
  - enum value and enum value existence

These additional relocations will make CO-RE bpf programs more
adaptive for potential kernel internal data structure changes.

For existence/size relocations, the following two code patterns
are supported:
  1. uint32_t __builtin_preserve_type_info(*(<type> *)0, flag);
  2. <type> var;
     uint32_t __builtin_preserve_field_info(var, flag);
flag = 0 for existence relocation and flag = 1 for size relocation.

For enum value existence and enum value relocations, the following code
pattern is supported:
  uint64_t __builtin_preserve_enum_value(*(<enum_type> *)<enum_value>,
                                         flag);
flag = 0 means existence relocation and flag = 1 for enum value.
relocation. In the above <enum_type> can be an enum type or
a typedef to enum type. The <enum_value> needs to be an enumerator
value from the same enum type. The return type is uint64_t to
permit potential 64bit enumerator values.

Differential Revision: https://reviews.llvm.org/D83242
  • Loading branch information
yonghong-song committed Aug 4, 2020
1 parent 14d726a commit 6d67506
Show file tree
Hide file tree
Showing 8 changed files with 346 additions and 34 deletions.
6 changes: 6 additions & 0 deletions clang/include/clang/Basic/BuiltinsBPF.def
Expand Up @@ -23,5 +23,11 @@ TARGET_BUILTIN(__builtin_preserve_field_info, "Ui.", "t", "")
// 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
8 changes: 8 additions & 0 deletions clang/include/clang/Basic/DiagnosticSemaKinds.td
Expand Up @@ -10865,6 +10865,14 @@ 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<
"__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">;
Expand Down
66 changes: 65 additions & 1 deletion clang/lib/CodeGen/CGBuiltin.cpp
Expand Up @@ -10921,9 +10921,16 @@ Value *CodeGenFunction::EmitAArch64BuiltinExpr(unsigned BuiltinID,
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");
Expand Down Expand Up @@ -11016,6 +11023,63 @@ Value *CodeGenFunction::EmitBPFBuiltinExpr(unsigned BuiltinID,
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;
}
}
}

Expand Down
161 changes: 130 additions & 31 deletions clang/lib/Sema/SemaChecking.cpp
Expand Up @@ -2557,52 +2557,151 @@ bool Sema::CheckAArch64BuiltinFunctionCall(const TargetInfo &TI,
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;
}

Expand Down
41 changes: 41 additions & 0 deletions 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"
32 changes: 32 additions & 0 deletions 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"

0 comments on commit 6d67506

Please sign in to comment.