yonghong-song created this revision. yonghong-song added reviewers: eli.friedman, ast. Herald added subscribers: cfe-commits, arphaman, kosarev. Herald added a project: clang.
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. An intrinsic function, preserve_di_access_index, is introducted. naddr = preserve_di_access_index(addr, base, type_name, access_index) here, addr: the previous getelementptr result, used as the result value do program semantics are kept intact. base: the base of previous getelementptr, used for later code generation with new relocatable access offset. type_name: the struct/union type name if available, can be used to match corresponding types in debuginfo. access_index: the access index based on user/debuginfo types. naddr: the result, having the same type as "addr". 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, void *unsafe_ptr) = (void *) 4; 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 = getelementptr inbounds %struct.sk_buff, %struct.sk_buff* %5, i32 0, i32 2, !dbg !54 %7 = call [10 x %union.anon]* @llvm.preserve.di.access.index.p0a10s_union.anons.p0a10s_union.anons.p0s_struct.sk_buffs( [10 x %union.anon]* %6, %struct.sk_buff* %5, i8* getelementptr inbounds ([8 x i8], [8 x i8]* @0, i32 0, i32 0), i32 3), !dbg !54 %8 = getelementptr inbounds [10 x %union.anon], [10 x %union.anon]* %7, i64 0, i64 5, !dbg !53 %9 = call %union.anon* @llvm.preserve.di.access.index.p0s_union.anons.p0s_union.anons.p0a10s_union.anons( %union.anon* %8, [10 x %union.anon]* %7, i8* getelementptr inbounds ([1 x i8], [1 x i8]* @1, i32 0, i32 0), i32 5), !dbg !53 %10 = call %union.anon* @llvm.preserve.di.access.index.p0s_union.anons.p0s_union.anons.p0s_union.anons( %union.anon* %9, %union.anon* %9, i8* getelementptr inbounds ([1 x i8], [1 x i8]* @2, i32 0, i32 0), i32 1), !dbg !55 %11 = bitcast %union.anon* %10 to %struct.anon.0*, !dbg !55 %12 = getelementptr inbounds %struct.anon.0, %struct.anon.0* %11, i32 0, i32 1, !dbg !56 %13 = call i8* @llvm.preserve.di.access.index.p0i8.p0i8.p0s_struct.anon.0s(i8* %12, %struct.anon.0* %11, i8* getelementptr inbounds ([1 x i8], [1 x i8]* @3, i32 0, i32 0), i32 1), !dbg !56 %14 = call i32 %4(i8* %3, i32 1, i8* %13), !dbg !52 %15 = load i8, i8* %3, align 1, !dbg !57, !tbaa !58 %16 = sext i8 %15 to i32, !dbg !57 call void @llvm.lifetime.end.p0i8(i64 1, i8* %3) #4, !dbg !59 ret i32 %16, !dbg !60 } For &ctx->u[5].dev.dev_id, . The first getelementptr (%6 = ...) has index 2 based on IR layout, and subsequent preserve_di_access_index (%7 = ...) has index 3 which reflects the debuginfo type layout. . The second getelementptr (%8 = ...) has index 5 which is the same as preserve_di_access_index (%9 = ...) for array subscript. . The instruction "%10 ..." is a call to preserve_di_access_index, which encodes the union member access index "1". Such information is lost in the original IR. . The third getelementptr (%12 = ...) has index 1 anonymous struct member "dev_id". The subsequent preserve_di_access_index also has the index "1". Basically, traversing the use-def chain recursively for the 3rd argument of bpf_probe_read() and examining all preserve_di_access_index calls, the original struct/union/array access index can be achieved. The original type name is also kept by preserve_di_access_index call. For example, for "%7 = ...", the second argument is a constantexpr getelementptr instrunction with base @0 = private unnamed_addr constant [8 x i8] c"sk_buff\00", align 1 which indicates the type name sk_buff. The type name can be used to search in DebugInfo types to find the precise definition. Finally, the intrinsic naddr = preserve_di_access_index(addr, base, type_name, access_index) can be simply transformed to naddr = addr or deleted by replacing all uses of "naddr" with "addr". Repository: rC Clang https://reviews.llvm.org/D61809 Files: lib/Basic/Targets/BPF.h lib/CodeGen/CGExpr.cpp Index: lib/CodeGen/CGExpr.cpp =================================================================== --- lib/CodeGen/CGExpr.cpp +++ lib/CodeGen/CGExpr.cpp @@ -3352,6 +3352,15 @@ llvm::Value *eltPtr = emitArraySubscriptGEP( CGF, addr.getPointer(), indices, inbounds, signedIndices, loc, name); + + // Remember the original array subscript for bpf target + auto Arch = CGF.getTarget().getTriple().getArch(); + if (Arch == llvm::Triple::bpfeb || Arch == llvm::Triple::bpfel) + return Address(CGF.Builder.CreatePreserveDIAccessIndex( + eltPtr, addr.getPointer(), StringRef(), + cast<llvm::ConstantInt>(indices.back())->getZExtValue()), + eltAlign); + return Address(eltPtr, eltAlign); } @@ -3932,9 +3941,31 @@ // a barrier every time CXXRecord field with vptr is referenced. addr = Address(Builder.CreateLaunderInvariantGroup(addr.getPointer()), addr.getAlignment()); + + // Remember the original union field index for bpf target + auto Arch = CGM.getTarget().getTriple().getArch(); + if (Arch == llvm::Triple::bpfeb || Arch == llvm::Triple::bpfel) + addr = Address( + Builder.CreatePreserveDIAccessIndex(addr.getPointer(), + addr.getPointer(), + rec->getName(), + field->getFieldIndex()), + addr.getAlignment()); } else { // For structs, we GEP to the field that the record layout suggests. - addr = emitAddrOfFieldStorage(*this, addr, field); + Address naddr = emitAddrOfFieldStorage(*this, addr, field); + + // Remember the original struct field index for bpf target + auto Arch = CGM.getTarget().getTriple().getArch(); + if (Arch != llvm::Triple::bpfeb && Arch != llvm::Triple::bpfel) + addr = std::move(naddr); + else + addr = Address( + Builder.CreatePreserveDIAccessIndex(naddr.getPointer(), + addr.getPointer(), + rec->getName(), + field->getFieldIndex()), + naddr.getAlignment()); // If this is a reference field, load the reference right now. if (FieldType->isReferenceType()) { Index: lib/Basic/Targets/BPF.h =================================================================== --- lib/Basic/Targets/BPF.h +++ lib/Basic/Targets/BPF.h @@ -46,7 +46,8 @@ MacroBuilder &Builder) const override; bool hasFeature(StringRef Feature) const override { - return Feature == "bpf" || Feature == "alu32" || Feature == "dwarfris"; + return Feature == "bpf" || Feature == "alu32" || Feature == "dwarfris" || + Feature == "offsetreloc"; } void setFeatureEnabled(llvm::StringMap<bool> &Features, StringRef Name,
Index: lib/CodeGen/CGExpr.cpp =================================================================== --- lib/CodeGen/CGExpr.cpp +++ lib/CodeGen/CGExpr.cpp @@ -3352,6 +3352,15 @@ llvm::Value *eltPtr = emitArraySubscriptGEP( CGF, addr.getPointer(), indices, inbounds, signedIndices, loc, name); + + // Remember the original array subscript for bpf target + auto Arch = CGF.getTarget().getTriple().getArch(); + if (Arch == llvm::Triple::bpfeb || Arch == llvm::Triple::bpfel) + return Address(CGF.Builder.CreatePreserveDIAccessIndex( + eltPtr, addr.getPointer(), StringRef(), + cast<llvm::ConstantInt>(indices.back())->getZExtValue()), + eltAlign); + return Address(eltPtr, eltAlign); } @@ -3932,9 +3941,31 @@ // a barrier every time CXXRecord field with vptr is referenced. addr = Address(Builder.CreateLaunderInvariantGroup(addr.getPointer()), addr.getAlignment()); + + // Remember the original union field index for bpf target + auto Arch = CGM.getTarget().getTriple().getArch(); + if (Arch == llvm::Triple::bpfeb || Arch == llvm::Triple::bpfel) + addr = Address( + Builder.CreatePreserveDIAccessIndex(addr.getPointer(), + addr.getPointer(), + rec->getName(), + field->getFieldIndex()), + addr.getAlignment()); } else { // For structs, we GEP to the field that the record layout suggests. - addr = emitAddrOfFieldStorage(*this, addr, field); + Address naddr = emitAddrOfFieldStorage(*this, addr, field); + + // Remember the original struct field index for bpf target + auto Arch = CGM.getTarget().getTriple().getArch(); + if (Arch != llvm::Triple::bpfeb && Arch != llvm::Triple::bpfel) + addr = std::move(naddr); + else + addr = Address( + Builder.CreatePreserveDIAccessIndex(naddr.getPointer(), + addr.getPointer(), + rec->getName(), + field->getFieldIndex()), + naddr.getAlignment()); // If this is a reference field, load the reference right now. if (FieldType->isReferenceType()) { Index: lib/Basic/Targets/BPF.h =================================================================== --- lib/Basic/Targets/BPF.h +++ lib/Basic/Targets/BPF.h @@ -46,7 +46,8 @@ MacroBuilder &Builder) const override; bool hasFeature(StringRef Feature) const override { - return Feature == "bpf" || Feature == "alu32" || Feature == "dwarfris"; + return Feature == "bpf" || Feature == "alu32" || Feature == "dwarfris" || + Feature == "offsetreloc"; } void setFeatureEnabled(llvm::StringMap<bool> &Features, StringRef Name,
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits