BPF: simplify IR generation for __builtin_btf_type_id()

This patch simplified IR generation for __builtin_btf_type_id().
For __builtin_btf_type_id(obj, flag), previously IR builtin
looks like
   if (obj is a lvalue)
     llvm.bpf.btf.type.id(obj.ptr, 1, flag)  !type
   else
     llvm.bpf.btf.type.id(obj, 0, flag)  !type
The purpose of the 2nd argument is to differentiate
   __builtin_btf_type_id(obj, flag) where obj is a lvalue
vs.
   __builtin_btf_type_id(obj.ptr, flag)

Note that obj or obj.ptr is never used by the backend
and the `obj` argument is only used to derive the type.
This code sequence is subject to potential llvm CSE when
  - obj is the same .e.g., nullptr
  - flag is the same
  - metadata type is different, e.g., typedef of struct "s"
    and strust "s".
In the above, we don't want CSE since their metadata is different.

This patch change IR builtin to
   llvm.bpf.btf.type.id(seq_num, flag)  !type
and seq_num is always increasing. This will prevent potential
llvm CSE.

Also report an error if the type name is empty for
remote relocation since remote relocation needs non-empty
type name to do relocation against vmlinux.

Differential Revision: https://reviews.llvm.org/D85174
This commit is contained in:
Yonghong Song 2020-08-03 16:12:19 -07:00
parent 4b25f67299
commit 00602ee7ef
5 changed files with 80 additions and 123 deletions

View File

@ -10961,68 +10961,7 @@ Value *CodeGenFunction::EmitBPFBuiltinExpr(unsigned BuiltinID,
{FieldAddr->getType()});
return Builder.CreateCall(FnGetFieldInfo, {FieldAddr, InfoKind});
}
case BPF::BI__builtin_btf_type_id: {
Value *FieldVal = nullptr;
// The LValue cannot be converted Value in order to be used as the function
// parameter. If it is a structure, it is the "alloca" result of the LValue
// (a pointer) is used in the parameter. If it is a simple type,
// the value will be loaded from its corresponding "alloca" and used as
// the parameter. In our case, let us just get a pointer of the LValue
// since we do not really use the parameter. The purpose of parameter
// is to prevent the generated IR llvm.bpf.btf.type.id intrinsic call,
// which carries metadata, from being changed.
bool IsLValue = E->getArg(0)->isLValue();
if (IsLValue)
FieldVal = EmitLValue(E->getArg(0)).getPointer(*this);
else
FieldVal = EmitScalarExpr(E->getArg(0));
if (!getDebugInfo()) {
CGM.Error(E->getExprLoc(), "using __builtin_btf_type_id() without -g");
return nullptr;
}
// Generate debuginfo type for the first argument.
llvm::DIType *DbgInfo =
getDebugInfo()->getOrCreateStandaloneType(E->getArg(0)->getType(),
E->getArg(0)->getExprLoc());
ConstantInt *Flag = cast<ConstantInt>(EmitScalarExpr(E->getArg(1)));
Value *FlagValue = ConstantInt::get(Int64Ty, Flag->getSExtValue());
// Built the IR for the btf_type_id intrinsic.
//
// In the above, we converted LValue argument to a pointer to LValue.
// For example, the following
// int v;
// C1: __builtin_btf_type_id(v, flag);
// will be converted to
// L1: llvm.bpf.btf.type.id(&v, flag)
// This makes it hard to differentiate from
// C2: __builtin_btf_type_id(&v, flag);
// to
// L2: llvm.bpf.btf.type.id(&v, flag)
//
// If both C1 and C2 are present in the code, the llvm may later
// on do CSE on L1 and L2, which will result in incorrect tagged types.
//
// The C1->L1 transformation only happens if the argument of
// __builtin_btf_type_id() is a LValue. So Let us put whether
// the argument is an LValue or not into generated IR. This should
// prevent potential CSE from causing debuginfo type loss.
//
// The generated IR intrinsics will hence look like
// L1: llvm.bpf.btf.type.id(&v, 1, flag) !di_type_for_{v};
// L2: llvm.bpf.btf.type.id(&v, 0, flag) !di_type_for_{&v};
Constant *CV = ConstantInt::get(IntTy, IsLValue);
llvm::Function *FnBtfTypeId = llvm::Intrinsic::getDeclaration(
&CGM.getModule(), llvm::Intrinsic::bpf_btf_type_id,
{FieldVal->getType(), CV->getType()});
CallInst *Fn = Builder.CreateCall(FnBtfTypeId, {FieldVal, CV, FlagValue});
Fn->setMetadata(LLVMContext::MD_preserve_access_index, DbgInfo);
return Fn;
}
case BPF::BI__builtin_btf_type_id:
case BPF::BI__builtin_preserve_type_info: {
if (!getDebugInfo()) {
CGM.Error(E->getExprLoc(), "using builtin function without -g");
@ -11037,10 +10976,14 @@ Value *CodeGenFunction::EmitBPFBuiltinExpr(unsigned BuiltinID,
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});
llvm::Function *FnDecl;
if (BuiltinID == BPF::BI__builtin_btf_type_id)
FnDecl = llvm::Intrinsic::getDeclaration(
&CGM.getModule(), llvm::Intrinsic::bpf_btf_type_id, {});
else
FnDecl = llvm::Intrinsic::getDeclaration(
&CGM.getModule(), llvm::Intrinsic::bpf_preserve_type_info, {});
CallInst *Fn = Builder.CreateCall(FnDecl, {SeqNumVal, FlagValue});
Fn->setMetadata(LLVMContext::MD_preserve_access_index, DbgInfo);
return Fn;
}

View File

@ -4,10 +4,22 @@
unsigned test1(int a) { return __builtin_btf_type_id(a, 0); }
unsigned test2(int a) { return __builtin_btf_type_id(&a, 0); }
struct t1 { int a; };
typedef struct t1 __t1;
unsigned test3() {
return __builtin_btf_type_id(*(struct t1 *)0, 1) +
__builtin_btf_type_id(*(__t1 *)0, 1);
}
// CHECK: define dso_local i32 @test1
// CHECK: call i32 @llvm.bpf.btf.type.id.p0i32.i32(i32* %{{[0-9a-z.]+}}, i32 1, i64 0), !dbg !{{[0-9]+}}, !llvm.preserve.access.index ![[INT:[0-9]+]]
// CHECK: call i32 @llvm.bpf.btf.type.id(i32 0, i64 0), !dbg !{{[0-9]+}}, !llvm.preserve.access.index ![[INT:[0-9]+]]
// CHECK: define dso_local i32 @test2
// CHECK: call i32 @llvm.bpf.btf.type.id.p0i32.i32(i32* %{{[0-9a-z.]+}}, i32 0, i64 0), !dbg !{{[0-9]+}}, !llvm.preserve.access.index ![[INT_POINTER:[0-9]+]]
// CHECK: call i32 @llvm.bpf.btf.type.id(i32 1, i64 0), !dbg !{{[0-9]+}}, !llvm.preserve.access.index ![[INT_POINTER:[0-9]+]]
// CHECK: define dso_local i32 @test3
// CHECK: call i32 @llvm.bpf.btf.type.id(i32 2, i64 1), !dbg !{{[0-9]+}}, !llvm.preserve.access.index ![[STRUCT_T1:[0-9]+]]
// CHECK: call i32 @llvm.bpf.btf.type.id(i32 3, i64 1), !dbg !{{[0-9]+}}, !llvm.preserve.access.index ![[TYPEDEF_T1:[0-9]+]]
//
// CHECK: ![[INT]] = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed
// CHECK: ![[INT_POINTER]] = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: ![[INT]], size: 64
// CHECK: ![[TYPEDEF_T1]] = !DIDerivedType(tag: DW_TAG_typedef, name: "__t1"
// CHECK: ![[STRUCT_T1]] = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "t1"

View File

@ -24,7 +24,7 @@ let TargetPrefix = "bpf" in { // All intrinsics start with "llvm.bpf."
Intrinsic<[llvm_i32_ty], [llvm_anyptr_ty, llvm_i64_ty],
[IntrNoMem, ImmArg<ArgIndex<1>>]>;
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],
Intrinsic<[llvm_i32_ty], [llvm_i32_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],

View File

@ -95,18 +95,24 @@ bool BPFPreserveDIType::doTransformation(Module &M) {
std::string BaseName = "llvm.btf_type_id.";
int Count = 0;
for (auto Call : PreserveDITypeCalls) {
const ConstantInt *Flag = dyn_cast<ConstantInt>(Call->getArgOperand(2));
const ConstantInt *Flag = dyn_cast<ConstantInt>(Call->getArgOperand(1));
assert(Flag);
uint64_t FlagValue = Flag->getValue().getZExtValue();
if (FlagValue >= BPFCoreSharedInfo::MAX_BTF_TYPE_ID_FLAG)
report_fatal_error("Incorrect flag for llvm.bpf.btf.type.id intrinsic");
MDNode *MD = Call->getMetadata(LLVMContext::MD_preserve_access_index);
uint32_t Reloc;
if (FlagValue == BPFCoreSharedInfo::BTF_TYPE_ID_LOCAL_RELOC)
if (FlagValue == BPFCoreSharedInfo::BTF_TYPE_ID_LOCAL_RELOC) {
Reloc = BPFCoreSharedInfo::BTF_TYPE_ID_LOCAL;
else
} else {
Reloc = BPFCoreSharedInfo::BTF_TYPE_ID_REMOTE;
DIType *Ty = cast<DIType>(MD);
if (Ty->getName().empty())
report_fatal_error("Empty type name for BTF_TYPE_ID_REMOTE reloc");
}
BasicBlock *BB = Call->getParent();
IntegerType *VarType = Type::getInt32Ty(BB->getContext());
@ -116,7 +122,6 @@ bool BPFPreserveDIType::doTransformation(Module &M) {
new GlobalVariable(M, VarType, false, GlobalVariable::ExternalLinkage,
NULL, GVName);
GV->addAttribute(BPFCoreSharedInfo::TypeIdAttr);
MDNode *MD = Call->getMetadata(LLVMContext::MD_preserve_access_index);
GV->setMetadata(LLVMContext::MD_preserve_access_index, MD);
// Load the global variable which represents the type info.

View File

@ -13,7 +13,7 @@
; bpf_log(__builtin_btf_type_id(tmp__abc, 0), &tmp__abc, sizeof(tmp__abc));
; }
; void prog2() {
; bpf_log(__builtin_btf_type_id(&tmp__abc, 1), &tmp__abc, sizeof(tmp__abc));
; bpf_log(__builtin_btf_type_id(&tmp__abc, 0), &tmp__abc, sizeof(tmp__abc));
; }
; void prog3() {
; bpf_log(__builtin_btf_type_id(tmp__abc.f1[3], 1), &tmp__abc, sizeof(tmp__abc));
@ -21,25 +21,23 @@
; Compilation flag:
; clang -target bpf -O2 -g -S -emit-llvm test.c
%struct.anon = type { [100 x i8], i32 }
@tmp__abc = dso_local global { <{ i8, i8, [98 x i8] }>, i32 } { <{ i8, i8, [98 x i8] }> <{ i8 1, i8 3, [98 x i8] zeroinitializer }>, i32 0 }, align 4, !dbg !0
; Function Attrs: nounwind
define dso_local void @prog1() local_unnamed_addr #0 !dbg !28 {
entry:
%0 = tail call i32 @llvm.bpf.btf.type.id.p0s_struct.anons.i32(%struct.anon* bitcast ({ <{ i8, i8, [98 x i8] }>, i32 }* @tmp__abc to %struct.anon*), i32 1, i64 0), !dbg !31, !llvm.preserve.access.index !7
%0 = tail call i32 @llvm.bpf.btf.type.id(i32 0, i64 0), !dbg !31, !llvm.preserve.access.index !7
%call = tail call i32 inttoptr (i64 999 to i32 (i32, i8*, i32)*)(i32 %0, i8* getelementptr inbounds ({ <{ i8, i8, [98 x i8] }>, i32 }, { <{ i8, i8, [98 x i8] }>, i32 }* @tmp__abc, i64 0, i32 0, i32 0), i32 104) #2, !dbg !32
ret void, !dbg !33
}
; Function Attrs: nounwind readnone
declare i32 @llvm.bpf.btf.type.id.p0s_struct.anons.i32(%struct.anon*, i32, i64) #1
declare i32 @llvm.bpf.btf.type.id(i32, i64) #1
; Function Attrs: nounwind
define dso_local void @prog2() local_unnamed_addr #0 !dbg !34 {
entry:
%0 = tail call i32 @llvm.bpf.btf.type.id.p0s_struct.anons.i32(%struct.anon* bitcast ({ <{ i8, i8, [98 x i8] }>, i32 }* @tmp__abc to %struct.anon*), i32 0, i64 1), !dbg !35, !llvm.preserve.access.index !6
%0 = tail call i32 @llvm.bpf.btf.type.id(i32 1, i64 0), !dbg !35, !llvm.preserve.access.index !6
%call = tail call i32 inttoptr (i64 999 to i32 (i32, i8*, i32)*)(i32 %0, i8* getelementptr inbounds ({ <{ i8, i8, [98 x i8] }>, i32 }, { <{ i8, i8, [98 x i8] }>, i32 }* @tmp__abc, i64 0, i32 0, i32 0), i32 104) #2, !dbg !36
ret void, !dbg !37
}
@ -47,56 +45,55 @@ entry:
; Function Attrs: nounwind
define dso_local void @prog3() local_unnamed_addr #0 !dbg !38 {
entry:
%0 = tail call i32 @llvm.bpf.btf.type.id.p0i8.i32(i8* getelementptr inbounds ({ <{ i8, i8, [98 x i8] }>, i32 }, { <{ i8, i8, [98 x i8] }>, i32 }* @tmp__abc, i64 0, i32 0, i32 2, i64 1), i32 1, i64 1), !dbg !39, !llvm.preserve.access.index !11
%0 = tail call i32 @llvm.bpf.btf.type.id(i32 2, i64 1), !dbg !39, !llvm.preserve.access.index !11
%call = tail call i32 inttoptr (i64 999 to i32 (i32, i8*, i32)*)(i32 %0, i8* getelementptr inbounds ({ <{ i8, i8, [98 x i8] }>, i32 }, { <{ i8, i8, [98 x i8] }>, i32 }* @tmp__abc, i64 0, i32 0, i32 0), i32 104) #2, !dbg !40
ret void, !dbg !41
}
; CHECK-LABEL: prog1
; CHECK: r1 = 3
; CHECK-LABEL: prog2
; CHECK: r1 = 10
; CHECK-LABEL: prog3
; CHECK: r1 = 4
;
; CHECK: .long 0 # BTF_KIND_STRUCT(id = 3)
; CHECK-NEXT: .long 67108866 # 0x4000002
; CHECK-NEXT: .long 104
; CHECK-NEXT: .long 13
; CHECK-NEXT: .long 5
; CHECK-NEXT: .long 0 # 0x0
; CHECK-NEXT: .long 16
; CHECK-NEXT: .long 7
; CHECK-NEXT: .long 800 # 0x320
; CHECK-NEXT: .long 19 # BTF_KIND_INT(id = 4)
; CHECK-NEXT: .long 16777216 # 0x1000000
; CHECK-NEXT: .long 1
; CHECK-NEXT: .long 16777224 # 0x1000008
; CHECK: .long 0 # BTF_KIND_PTR(id = 10)
; CHECK-NEXT: .long 33554432 # 0x2000000
; CHECK-NEXT: .long 3
; CHECK-LABEL: prog1
; CHECK: r1 = 3
; CHECK-LABEL: prog2
; CHECK: r1 = 10
; CHECK-LABEL: prog3
; CHECK: r1 = 4
; CHECK: .long 16 # FieldReloc
; CHECK-NEXT: .long {{[0-9]+}} # Field reloc section string offset={{[0-9]+}}
; CHECK-NEXT: .long 3
; CHECK-NEXT: .long .Ltmp{{[0-9]+}}
; CHECK-NEXT: .long 3
; CHECK-NEXT: .long {{[0-9]+}}
; CHECK-NEXT: .long 6
; CHECK-NEXT: .long .Ltmp{{[0-9]+}}
; CHECK-NEXT: .long 10
; CHECK-NEXT: .long {{[0-9]+}}
; CHECK-NEXT: .long 7
; CHECK-NEXT: .long .Ltmp{{[0-9]+}}
; CHECK-NEXT: .long 4
; CHECK-NEXT: .long {{[0-9]+}}
; CHECK-NEXT: .long 7
; CHECK: .long 0 # BTF_KIND_STRUCT(id = 3)
; CHECK-NEXT: .long 67108866 # 0x4000002
; CHECK-NEXT: .long 104
; CHECK-NEXT: .long 13
; CHECK-NEXT: .long 5
; CHECK-NEXT: .long 0 # 0x0
; CHECK-NEXT: .long 16
; CHECK-NEXT: .long 7
; CHECK-NEXT: .long 800 # 0x320
; CHECK: .long 19 # BTF_KIND_INT(id = 4)
; CHECK: .long 0 # BTF_KIND_PTR(id = 10)
; CHECK-NEXT: .long 33554432 # 0x2000000
; CHECK-NEXT: .long 3
; CHECK: .ascii ".text" # string offset=7
; CHECK: .ascii "f1" # string offset=13
; CHECK: .ascii "f2" # string offset=16
; CHECK: .ascii "char" # string offset=19
; CHECK: .byte 48 # string offset=48
; Function Attrs: nounwind readnone
declare i32 @llvm.bpf.btf.type.id.p0i8.i32(i8*, i32, i64) #1
; CHECK: .long 16 # FieldReloc
; CHECK-NEXT: .long 7 # Field reloc section string offset=7
; CHECK-NEXT: .long 3
; CHECK-NEXT: .long .Ltmp{{[0-9]+}}
; CHECK-NEXT: .long 3
; CHECK-NEXT: .long 48
; CHECK-NEXT: .long 6
; CHECK-NEXT: .long .Ltmp{{[0-9]+}}
; CHECK-NEXT: .long 10
; CHECK-NEXT: .long 48
; CHECK-NEXT: .long 6
; CHECK-NEXT: .long .Ltmp{{[0-9]+}}
; CHECK-NEXT: .long 4
; CHECK-NEXT: .long 48
; CHECK-NEXT: .long 7
attributes #0 = { nounwind "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" }
attributes #0 = { nounwind "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" }
attributes #1 = { nounwind readnone }
attributes #2 = { nounwind }
@ -106,7 +103,7 @@ attributes #2 = { nounwind }
!0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression())
!1 = distinct !DIGlobalVariable(name: "tmp__abc", scope: !2, file: !3, line: 5, type: !7, isLocal: false, isDefinition: true)
!2 = distinct !DICompileUnit(language: DW_LANG_C99, file: !3, producer: "clang version 11.0.0 (https://github.com/llvm/llvm-project.git 95253d8f16b8085b4b85cb3a6106ccbfe8a6d9b2)", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !4, retainedTypes: !5, globals: !16, splitDebugInlining: false, nameTableKind: None)
!2 = distinct !DICompileUnit(language: DW_LANG_C99, file: !3, producer: "clang version 12.0.0 (https://github.com/llvm/llvm-project.git f39aae11dca3f8f8c2c755a871726ed2fa82fd57)", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !4, retainedTypes: !5, globals: !16, splitDebugInlining: false, nameTableKind: None)
!3 = !DIFile(filename: "test.c", directory: "/tmp/home/yhs/work/tests/core")
!4 = !{}
!5 = !{!6, !11}
@ -131,7 +128,7 @@ attributes #2 = { nounwind }
!24 = !{i32 7, !"Dwarf Version", i32 4}
!25 = !{i32 2, !"Debug Info Version", i32 3}
!26 = !{i32 1, !"wchar_size", i32 4}
!27 = !{!"clang version 11.0.0 (https://github.com/llvm/llvm-project.git 95253d8f16b8085b4b85cb3a6106ccbfe8a6d9b2)"}
!27 = !{!"clang version 12.0.0 (https://github.com/llvm/llvm-project.git f39aae11dca3f8f8c2c755a871726ed2fa82fd57)"}
!28 = distinct !DISubprogram(name: "prog1", scope: !3, file: !3, line: 6, type: !29, scopeLine: 6, flags: DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !2, retainedNodes: !4)
!29 = !DISubroutineType(types: !30)
!30 = !{null}