Skip to content

Commit f21eeaf

Browse files
committed
[BPF] Preserve debuginfo array/union/struct type/access index
For background of BPF CO-RE project, please refer to http://vger.kernel.org/bpfconf2019.html In summary, BPF CO-RE intends to compile bpf programs adjustable on struct/union layout change so the same program can run on multiple kernels with adjustment before loading based on native kernel structures. In order to do this, we need keep track of GEP(getelementptr) instruction base and result debuginfo types, so we can adjust on the host based on kernel BTF info. Capturing such information as an IR optimization is hard as various optimization may have tweaked GEP and also union is replaced by structure it is impossible to track fieldindex for union member accesses. Three intrinsic functions, preserve_{array,union,struct}_access_index, are introducted. addr = preserve_array_access_index(base, index, dimension) addr = preserve_union_access_index(base, di_index) addr = preserve_struct_access_index(base, gep_index, di_index) here, base: the base pointer for the array/union/struct access. index: the last access index for array, the same for IR/DebugInfo layout. dimension: the array dimension. gep_index: the access index based on IR layout. di_index: the access index based on user/debuginfo types. If using these intrinsics blindly, i.e., transforming all GEPs to these intrinsics and later on reducing them to GEPs, we have seen up to 7% more instructions generated. To avoid such an overhead, a clang builtin is proposed: base = __builtin_preserve_access_index(base) such that user wraps to-be-relocated GEPs in this builtin and preserve_*_access_index intrinsics only apply to those GEPs. Such a buyin will prevent performance degradation if people do not use CO-RE, even for programs which use bpf_probe_read(). For example, for the following example, $ cat test.c struct sk_buff { int i; int b1:1; int b2:2; union { struct { int o1; int o2; } o; struct { char flags; char dev_id; } dev; int netid; } u[10]; }; static int (*bpf_probe_read)(void *dst, int size, const void *unsafe_ptr) = (void *) 4; #define _(x) (__builtin_preserve_access_index(x)) int bpf_prog(struct sk_buff *ctx) { char dev_id; bpf_probe_read(&dev_id, sizeof(char), _(&ctx->u[5].dev.dev_id)); return dev_id; } $ clang -target bpf -O2 -g -emit-llvm -S -mllvm -print-before-all \ test.c >& log The generated IR looks like below: ... define dso_local i32 @bpf_prog(%struct.sk_buff*) #0 !dbg !15 { %2 = alloca %struct.sk_buff*, align 8 %3 = alloca i8, align 1 store %struct.sk_buff* %0, %struct.sk_buff** %2, align 8, !tbaa !45 call void @llvm.dbg.declare(metadata %struct.sk_buff** %2, metadata !43, metadata !DIExpression()), !dbg !49 call void @llvm.lifetime.start.p0i8(i64 1, i8* %3) #4, !dbg !50 call void @llvm.dbg.declare(metadata i8* %3, metadata !44, metadata !DIExpression()), !dbg !51 %4 = load i32 (i8*, i32, i8*)*, i32 (i8*, i32, i8*)** @bpf_probe_read, align 8, !dbg !52, !tbaa !45 %5 = load %struct.sk_buff*, %struct.sk_buff** %2, align 8, !dbg !53, !tbaa !45 %6 = call [10 x %union.anon]* @llvm.preserve.struct.access.index.p0a10s_union.anons.p0s_struct.sk_buffs( %struct.sk_buff* %5, i32 2, i32 3), !dbg !53, !llvm.preserve.access.index !19 %7 = call %union.anon* @llvm.preserve.array.access.index.p0s_union.anons.p0a10s_union.anons( [10 x %union.anon]* %6, i32 1, i32 5), !dbg !53 %8 = call %union.anon* @llvm.preserve.union.access.index.p0s_union.anons.p0s_union.anons( %union.anon* %7, i32 1), !dbg !53, !llvm.preserve.access.index !26 %9 = bitcast %union.anon* %8 to %struct.anon.0*, !dbg !53 %10 = call i8* @llvm.preserve.struct.access.index.p0i8.p0s_struct.anon.0s( %struct.anon.0* %9, i32 1, i32 1), !dbg !53, !llvm.preserve.access.index !34 %11 = call i32 %4(i8* %3, i32 1, i8* %10), !dbg !52 %12 = load i8, i8* %3, align 1, !dbg !54, !tbaa !55 %13 = sext i8 %12 to i32, !dbg !54 call void @llvm.lifetime.end.p0i8(i64 1, i8* %3) #4, !dbg !56 ret i32 %13, !dbg !57 } !19 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "sk_buff", file: !3, line: 1, size: 704, elements: !20) !26 = distinct !DICompositeType(tag: DW_TAG_union_type, scope: !19, file: !3, line: 5, size: 64, elements: !27) !34 = distinct !DICompositeType(tag: DW_TAG_structure_type, scope: !26, file: !3, line: 10, size: 16, elements: !35) Note that @llvm.preserve.{struct,union}.access.index calls have metadata llvm.preserve.access.index attached to instructions to provide struct/union debuginfo type information. For &ctx->u[5].dev.dev_id, . The "%6 = ..." represents struct member "u" with index 2 for IR layout and index 3 for DI layout. . The "%7 = ..." represents array subscript "5". . The "%8 = ..." represents union member "dev" with index 1 for DI layout. . The "%10 = ..." represents struct member "dev_id" with index 1 for both IR and DI layout. Basically, traversing the use-def chain recursively for the 3rd argument of bpf_probe_read() and examining all preserve_*_access_index calls, the debuginfo struct/union/array access index can be achieved. The intrinsics also contain enough information to regenerate codes for IR layout. For array and structure intrinsics, the proper GEP can be constructed. For union intrinsics, replacing all uses of "addr" with "base" should be enough. Signed-off-by: Yonghong Song <yhs@fb.com> llvm-svn: 365435
1 parent 92fb5f2 commit f21eeaf

9 files changed

+173
-4
lines changed

clang/docs/LanguageExtensions.rst

+29
Original file line numberDiff line numberDiff line change
@@ -1950,6 +1950,35 @@ form of ``__builtin_operator_delete`` is currently available.
19501950
These builtins are intended for use in the implementation of ``std::allocator``
19511951
and other similar allocation libraries, and are only available in C++.
19521952
1953+
``__builtin_preserve_access_index``
1954+
-----------------------------------
1955+
1956+
``__builtin_preserve_access_index`` specifies a code section where
1957+
array subscript access and structure/union member access are relocatable
1958+
under bpf compile-once run-everywhere framework. Debuginfo (typically
1959+
with ``-g``) is needed, otherwise, the compiler will exit with an error.
1960+
1961+
**Syntax**:
1962+
1963+
.. code-block:: c
1964+
1965+
const void * __builtin_preserve_access_index(const void * ptr)
1966+
1967+
**Example of Use**:
1968+
1969+
.. code-block:: c
1970+
1971+
struct t {
1972+
int i;
1973+
int j;
1974+
union {
1975+
int a;
1976+
int b;
1977+
} c[4];
1978+
};
1979+
struct t *v = ...;
1980+
const void *pb =__builtin_preserve_access_index(&v->c[3].b);
1981+
19531982
Multiprecision Arithmetic Builtins
19541983
----------------------------------
19551984

clang/include/clang/Basic/Builtins.def

+1
Original file line numberDiff line numberDiff line change
@@ -1449,6 +1449,7 @@ BUILTIN(__builtin_operator_new, "v*z", "tc")
14491449
BUILTIN(__builtin_operator_delete, "vv*", "tn")
14501450
BUILTIN(__builtin_char_memchr, "c*cC*iz", "n")
14511451
BUILTIN(__builtin_dump_struct, "ivC*v*", "tn")
1452+
BUILTIN(__builtin_preserve_access_index, "vC*vC*", "nU")
14521453

14531454
// Safestack builtins
14541455
BUILTIN(__builtin___get_unsafe_stack_start, "v*", "Fn")

clang/lib/CodeGen/CGBuilder.h

+15
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,21 @@ class CGBuilderTy : public CGBuilderBaseTy {
298298
return CreateMemSet(Dest.getPointer(), Value, Size,
299299
Dest.getAlignment().getQuantity(), IsVolatile);
300300
}
301+
302+
using CGBuilderBaseTy::CreatePreserveStructAccessIndex;
303+
Address CreatePreserveStructAccessIndex(Address Addr,
304+
unsigned Index,
305+
unsigned FieldIndex,
306+
llvm::MDNode *DbgInfo) {
307+
llvm::StructType *ElTy = cast<llvm::StructType>(Addr.getElementType());
308+
const llvm::DataLayout &DL = BB->getParent()->getParent()->getDataLayout();
309+
const llvm::StructLayout *Layout = DL.getStructLayout(ElTy);
310+
auto Offset = CharUnits::fromQuantity(Layout->getElementOffset(Index));
311+
312+
return Address(CreatePreserveStructAccessIndex(Addr.getPointer(),
313+
Index, FieldIndex, DbgInfo),
314+
Addr.getAlignment().alignmentAtOffset(Offset));
315+
}
301316
};
302317

303318
} // end namespace CodeGen

clang/lib/CodeGen/CGBuiltin.cpp

+21
Original file line numberDiff line numberDiff line change
@@ -1840,6 +1840,27 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
18401840
return RValue::get(Res);
18411841
}
18421842

1843+
case Builtin::BI__builtin_preserve_access_index: {
1844+
// Only enabled preserved access index region when debuginfo
1845+
// is available as debuginfo is needed to preserve user-level
1846+
// access pattern.
1847+
if (!getDebugInfo()) {
1848+
CGM.Error(E->getExprLoc(), "using builtin_preserve_access_index() without -g");
1849+
return RValue::get(EmitScalarExpr(E->getArg(0)));
1850+
}
1851+
1852+
// Nested builtin_preserve_access_index() not supported
1853+
if (IsInPreservedAIRegion) {
1854+
CGM.Error(E->getExprLoc(), "nested builtin_preserve_access_index() not supported");
1855+
return RValue::get(EmitScalarExpr(E->getArg(0)));
1856+
}
1857+
1858+
IsInPreservedAIRegion = true;
1859+
Value *Res = EmitScalarExpr(E->getArg(0));
1860+
IsInPreservedAIRegion = false;
1861+
return RValue::get(Res);
1862+
}
1863+
18431864
case Builtin::BI__builtin_cimag:
18441865
case Builtin::BI__builtin_cimagf:
18451866
case Builtin::BI__builtin_cimagl:

clang/lib/CodeGen/CGExpr.cpp

+45-4
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
#include "clang/AST/Attr.h"
2626
#include "clang/AST/DeclObjC.h"
2727
#include "clang/AST/NSAPI.h"
28+
#include "clang/Basic/Builtins.h"
2829
#include "clang/Basic/CodeGenOptions.h"
2930
#include "llvm/ADT/Hashing.h"
3031
#include "llvm/ADT/StringExtras.h"
@@ -3418,8 +3419,20 @@ static Address emitArraySubscriptGEP(CodeGenFunction &CGF, Address addr,
34183419
CharUnits eltAlign =
34193420
getArrayElementAlign(addr.getAlignment(), indices.back(), eltSize);
34203421

3421-
llvm::Value *eltPtr = emitArraySubscriptGEP(
3422-
CGF, addr.getPointer(), indices, inbounds, signedIndices, loc, name);
3422+
llvm::Value *eltPtr;
3423+
auto LastIndex = dyn_cast<llvm::ConstantInt>(indices.back());
3424+
if (!CGF.IsInPreservedAIRegion || !LastIndex) {
3425+
eltPtr = emitArraySubscriptGEP(
3426+
CGF, addr.getPointer(), indices, inbounds, signedIndices,
3427+
loc, name);
3428+
} else {
3429+
// Remember the original array subscript for bpf target
3430+
unsigned idx = LastIndex->getZExtValue();
3431+
eltPtr = CGF.Builder.CreatePreserveArrayAccessIndex(addr.getPointer(),
3432+
indices.size() - 1,
3433+
idx);
3434+
}
3435+
34233436
return Address(eltPtr, eltAlign);
34243437
}
34253438

@@ -3908,6 +3921,19 @@ static Address emitAddrOfFieldStorage(CodeGenFunction &CGF, Address base,
39083921
return CGF.Builder.CreateStructGEP(base, idx, field->getName());
39093922
}
39103923

3924+
static Address emitPreserveStructAccess(CodeGenFunction &CGF, Address base,
3925+
const FieldDecl *field) {
3926+
const RecordDecl *rec = field->getParent();
3927+
llvm::DIType *DbgInfo = CGF.getDebugInfo()->getOrCreateRecordType(
3928+
CGF.getContext().getRecordType(rec), rec->getLocation());
3929+
3930+
unsigned idx =
3931+
CGF.CGM.getTypes().getCGRecordLayout(rec).getLLVMFieldNo(field);
3932+
3933+
return CGF.Builder.CreatePreserveStructAccessIndex(
3934+
base, idx, field->getFieldIndex(), DbgInfo);
3935+
}
3936+
39113937
static bool hasAnyVptr(const QualType Type, const ASTContext &Context) {
39123938
const auto *RD = Type.getTypePtr()->getAsCXXRecordDecl();
39133939
if (!RD)
@@ -4015,9 +4041,24 @@ LValue CodeGenFunction::EmitLValueForField(LValue base,
40154041
// a barrier every time CXXRecord field with vptr is referenced.
40164042
addr = Address(Builder.CreateLaunderInvariantGroup(addr.getPointer()),
40174043
addr.getAlignment());
4044+
4045+
if (IsInPreservedAIRegion) {
4046+
// Remember the original union field index
4047+
llvm::DIType *DbgInfo = getDebugInfo()->getOrCreateRecordType(
4048+
getContext().getRecordType(rec), rec->getLocation());
4049+
addr = Address(
4050+
Builder.CreatePreserveUnionAccessIndex(
4051+
addr.getPointer(), field->getFieldIndex(), DbgInfo),
4052+
addr.getAlignment());
4053+
}
40184054
} else {
4019-
// For structs, we GEP to the field that the record layout suggests.
4020-
addr = emitAddrOfFieldStorage(*this, addr, field);
4055+
4056+
if (!IsInPreservedAIRegion)
4057+
// For structs, we GEP to the field that the record layout suggests.
4058+
addr = emitAddrOfFieldStorage(*this, addr, field);
4059+
else
4060+
// Remember the original struct field index
4061+
addr = emitPreserveStructAccess(*this, addr, field);
40214062

40224063
// If this is a reference field, load the reference right now.
40234064
if (FieldType->isReferenceType()) {

clang/lib/CodeGen/CodeGenFunction.h

+4
Original file line numberDiff line numberDiff line change
@@ -480,6 +480,10 @@ class CodeGenFunction : public CodeGenTypeCache {
480480
/// finally block or filter expression.
481481
bool IsOutlinedSEHHelper = false;
482482

483+
/// True if CodeGen currently emits code inside presereved access index
484+
/// region.
485+
bool IsInPreservedAIRegion = false;
486+
483487
const CodeGen::CGBlockInfo *BlockInfo = nullptr;
484488
llvm::Value *BlockPointer = nullptr;
485489

clang/lib/Sema/SemaChecking.cpp

+14
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,16 @@ static bool SemaBuiltinAddressof(Sema &S, CallExpr *TheCall) {
191191
return false;
192192
}
193193

194+
/// Check the number of arguments, and set the result type to
195+
/// the argument type.
196+
static bool SemaBuiltinPreserveAI(Sema &S, CallExpr *TheCall) {
197+
if (checkArgCount(S, TheCall, 1))
198+
return true;
199+
200+
TheCall->setType(TheCall->getArg(0)->getType());
201+
return false;
202+
}
203+
194204
static bool SemaBuiltinOverflow(Sema &S, CallExpr *TheCall) {
195205
if (checkArgCount(S, TheCall, 3))
196206
return true;
@@ -1409,6 +1419,10 @@ Sema::CheckBuiltinFunctionCall(FunctionDecl *FDecl, unsigned BuiltinID,
14091419
TheCall->setType(Context.IntTy);
14101420
break;
14111421
}
1422+
case Builtin::BI__builtin_preserve_access_index:
1423+
if (SemaBuiltinPreserveAI(*this, TheCall))
1424+
return ExprError();
1425+
break;
14121426
case Builtin::BI__builtin_call_with_static_chain:
14131427
if (SemaBuiltinCallWithStaticChain(*this, TheCall))
14141428
return ExprError();
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// RUN: %clang %s -target bpfeb -x c -emit-llvm -S -g -O2 -o - | FileCheck %s
2+
// RUN: %clang %s -target bpfel -x c -emit-llvm -S -g -O2 -o - | FileCheck %s
3+
4+
struct t {
5+
int i:1;
6+
int j:2;
7+
union {
8+
int a;
9+
int b;
10+
} c[4];
11+
};
12+
13+
#define _(x) (x)
14+
15+
const void *test(struct t *arg) {
16+
return _(&arg->c[3].b);
17+
}
18+
19+
// CHECK-NOT: llvm.preserve.struct.access.index
20+
// CHECK-NOT: llvm.preserve.array.access.index
21+
// CHECK-NOT: llvm.preserve.union.access.index
22+
// CHECK-NOT: __builtin_preserve_access_index
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// RUN: %clang %s -target bpfeb -x c -emit-llvm -S -g -O2 -o - | FileCheck --check-prefix=CHECK %s
2+
// RUN: %clang %s -target bpfel -x c -emit-llvm -S -g -O2 -o - | FileCheck --check-prefix=CHECK %s
3+
4+
struct t {
5+
int i:1;
6+
int j:2;
7+
union {
8+
int a;
9+
int b;
10+
} c[4];
11+
};
12+
13+
#define _(x) (__builtin_preserve_access_index(x))
14+
15+
const void *test(struct t *arg) {
16+
return _(&arg->c[3].b);
17+
}
18+
19+
// CHECK: llvm.preserve.struct.access.index
20+
// CHECK: llvm.preserve.array.access.index
21+
// CHECK: llvm.preserve.union.access.index
22+
// CHECK-NOT: __builtin_preserve_access_index

0 commit comments

Comments
 (0)