Skip to content

Commit

Permalink
[BPF] Handling type conversions correctly for CO-RE
Browse files Browse the repository at this point in the history
With newly added debuginfo type
metadata for preserve_array_access_index() intrinsic,
this patch did the following two things:
 (1). checking validity before adding a new access index
      to the access chain.
 (2). calculating access byte offset in IR phase
      BPFAbstractMemberAccess instead of when BTF is emitted.

For (1), the metadata provided by all preserve_*_access_index()
intrinsics are used to check whether the to-be-added type
is a proper struct/union member or array element.

For (2), with all available metadata, calculating access byte
offset becomes easier in BPFAbstractMemberAccess IR phase.
This enables us to remove the unnecessary complexity in
BTFDebug.cpp.

New tests are added for
  . user explicit casting to array/structure/union
  . global variable (or its dereference) as the source of base
  . multi demensional arrays
  . array access given a base pointer
  . cases where we won't generate relocation if we cannot find
    type name.

Differential Revision: https://reviews.llvm.org/D65618

llvm-svn: 367735
  • Loading branch information
yonghong-song committed Aug 2, 2019
1 parent 748dac7 commit 37d24a6
Show file tree
Hide file tree
Showing 22 changed files with 1,812 additions and 184 deletions.
332 changes: 266 additions & 66 deletions llvm/lib/Target/BPF/BPFAbstractMemberAccess.cpp

Large diffs are not rendered by default.

110 changes: 10 additions & 100 deletions llvm/lib/Target/BPF/BTFDebug.cpp
Expand Up @@ -30,18 +30,6 @@ static const char *BTFKindStr[] = {
#include "BTF.def"
};

static const DIType * stripQualifiers(const DIType *Ty) {
while (const auto *DTy = dyn_cast<DIDerivedType>(Ty)) {
unsigned Tag = DTy->getTag();
if (Tag != dwarf::DW_TAG_typedef && Tag != dwarf::DW_TAG_const_type &&
Tag != dwarf::DW_TAG_volatile_type && Tag != dwarf::DW_TAG_restrict_type)
break;
Ty = DTy->getBaseType();
}

return Ty;
}

/// Emit a BTF common type.
void BTFTypeBase::emitType(MCStreamer &OS) {
OS.AddComment(std::string(BTFKindStr[Kind]) + "(id = " + std::to_string(Id) +
Expand Down Expand Up @@ -196,9 +184,7 @@ void BTFTypeEnum::emitType(MCStreamer &OS) {
}
}

BTFTypeArray::BTFTypeArray(const DIType *Ty, uint32_t ElemTypeId,
uint32_t ElemSize, uint32_t NumElems)
: ElemTyNoQual(Ty), ElemSize(ElemSize) {
BTFTypeArray::BTFTypeArray(uint32_t ElemTypeId, uint32_t NumElems) {
Kind = BTF::BTF_KIND_ARRAY;
BTFType.NameOff = 0;
BTFType.Info = Kind << 24;
Expand All @@ -219,9 +205,6 @@ void BTFTypeArray::completeType(BTFDebug &BDebug) {
// created during initial type traversal. Just
// retrieve that type id.
ArrayInfo.IndexType = BDebug.getArrayIndexTypeId();

ElemTypeNoQual = ElemTyNoQual ? BDebug.getTypeId(ElemTyNoQual)
: ArrayInfo.ElemType;
}

void BTFTypeArray::emitType(MCStreamer &OS) {
Expand All @@ -231,12 +214,6 @@ void BTFTypeArray::emitType(MCStreamer &OS) {
OS.EmitIntValue(ArrayInfo.Nelems, 4);
}

void BTFTypeArray::getLocInfo(uint32_t Loc, uint32_t &LocOffset,
uint32_t &ElementTypeId) {
ElementTypeId = ElemTypeNoQual;
LocOffset = Loc * ElemSize;
}

/// Represent either a struct or a union.
BTFTypeStruct::BTFTypeStruct(const DICompositeType *STy, bool IsStruct,
bool HasBitField, uint32_t Vlen)
Expand Down Expand Up @@ -268,7 +245,6 @@ void BTFTypeStruct::completeType(BTFDebug &BDebug) {
}
const auto *BaseTy = DDTy->getBaseType();
BTFMember.Type = BDebug.getTypeId(BaseTy);
MemberTypeNoQual.push_back(BDebug.getTypeId(stripQualifiers(BaseTy)));
Members.push_back(BTFMember);
}
}
Expand All @@ -285,15 +261,6 @@ void BTFTypeStruct::emitType(MCStreamer &OS) {

std::string BTFTypeStruct::getName() { return STy->getName(); }

void BTFTypeStruct::getMemberInfo(uint32_t Loc, uint32_t &MemberOffset,
uint32_t &MemberType) {
MemberType = MemberTypeNoQual[Loc];
MemberOffset =
HasBitField ? Members[Loc].Offset & 0xffffff : Members[Loc].Offset;
}

uint32_t BTFTypeStruct::getStructSize() { return STy->getSizeInBits() >> 3; }

/// The Func kind represents both subprogram and pointee of function
/// pointers. If the FuncName is empty, it represents a pointee of function
/// pointer. Otherwise, it represents a subprogram. The func arg names
Expand Down Expand Up @@ -511,12 +478,10 @@ void BTFDebug::visitArrayType(const DICompositeType *CTy, uint32_t &TypeId) {
visitTypeEntry(ElemType, ElemTypeId, false, false);

// Strip qualifiers from element type to get accurate element size.
ElemType = stripQualifiers(ElemType);
ElemSize = ElemType->getSizeInBits() >> 3;

if (!CTy->getSizeInBits()) {
auto TypeEntry = llvm::make_unique<BTFTypeArray>(ElemType, ElemTypeId, 0, 0);
ArrayTypes.push_back(TypeEntry.get());
auto TypeEntry = llvm::make_unique<BTFTypeArray>(ElemTypeId, 0);
ElemTypeId = addType(std::move(TypeEntry), CTy);
} else {
// Visit array dimensions.
Expand All @@ -527,12 +492,9 @@ void BTFDebug::visitArrayType(const DICompositeType *CTy, uint32_t &TypeId) {
const DISubrange *SR = cast<DISubrange>(Element);
auto *CI = SR->getCount().dyn_cast<ConstantInt *>();
int64_t Count = CI->getSExtValue();
const DIType *ArrayElemTy = (I == 0) ? ElemType : nullptr;

auto TypeEntry =
llvm::make_unique<BTFTypeArray>(ArrayElemTy, ElemTypeId,
ElemSize, Count);
ArrayTypes.push_back(TypeEntry.get());
llvm::make_unique<BTFTypeArray>(ElemTypeId, Count);
if (I == 0)
ElemTypeId = addType(std::move(TypeEntry), CTy);
else
Expand Down Expand Up @@ -1002,74 +964,22 @@ unsigned BTFDebug::populateStructType(const DIType *Ty) {
return Id;
}

// Find struct/array debuginfo types given a type id.
void BTFDebug::setTypeFromId(uint32_t TypeId, BTFTypeStruct **PrevStructType,
BTFTypeArray **PrevArrayType) {
for (const auto &StructType : StructTypes) {
if (StructType->getId() == TypeId) {
*PrevStructType = StructType;
return;
}
}
for (const auto &ArrayType : ArrayTypes) {
if (ArrayType->getId() == TypeId) {
*PrevArrayType = ArrayType;
return;
}
}
}

/// Generate a struct member offset relocation.
void BTFDebug::generateOffsetReloc(const MachineInstr *MI,
const MCSymbol *ORSym, DIType *RootTy,
StringRef AccessPattern) {
BTFTypeStruct *PrevStructType = nullptr;
BTFTypeArray *PrevArrayType = nullptr;
unsigned RootId = populateStructType(RootTy);
setTypeFromId(RootId, &PrevStructType, &PrevArrayType);
unsigned RootTySize = PrevStructType->getStructSize();
StringRef IndexPattern = AccessPattern.substr(AccessPattern.find_first_of(':') + 1);
size_t FirstDollar = AccessPattern.find_first_of('$');
size_t FirstColon = AccessPattern.find_first_of(':');
StringRef IndexPattern = AccessPattern.substr(FirstDollar + 1);
StringRef OffsetStr = AccessPattern.substr(FirstColon + 1,
FirstDollar - FirstColon);

BTFOffsetReloc OffsetReloc;
OffsetReloc.Label = ORSym;
OffsetReloc.OffsetNameOff = addString(IndexPattern.drop_back());
OffsetReloc.OffsetNameOff = addString(IndexPattern);
OffsetReloc.TypeID = RootId;

uint32_t Start = 0, End = 0, Offset = 0;
bool FirstAccess = true;
for (auto C : IndexPattern) {
if (C != ':') {
End++;
} else {
std::string SubStr = IndexPattern.substr(Start, End - Start);
int Loc = std::stoi(SubStr);

if (FirstAccess) {
Offset = Loc * RootTySize;
FirstAccess = false;
} else if (PrevStructType) {
uint32_t MemberOffset, MemberTypeId;
PrevStructType->getMemberInfo(Loc, MemberOffset, MemberTypeId);

Offset += MemberOffset >> 3;
PrevStructType = nullptr;
setTypeFromId(MemberTypeId, &PrevStructType, &PrevArrayType);
} else if (PrevArrayType) {
uint32_t LocOffset, ElementTypeId;
PrevArrayType->getLocInfo(Loc, LocOffset, ElementTypeId);

Offset += LocOffset;
PrevArrayType = nullptr;
setTypeFromId(ElementTypeId, &PrevStructType, &PrevArrayType);
} else {
llvm_unreachable("Internal Error: BTF offset relocation type traversal error");
}

Start = End + 1;
End = Start;
}
}
AccessOffsets[AccessPattern.str()] = Offset;
AccessOffsets[AccessPattern.str()] = std::stoi(OffsetStr);
OffsetRelocTable[SecNameOff].push_back(OffsetReloc);
}

Expand Down
15 changes: 1 addition & 14 deletions llvm/lib/Target/BPF/BTFDebug.h
Expand Up @@ -104,26 +104,20 @@ class BTFTypeEnum : public BTFTypeBase {

/// Handle array type.
class BTFTypeArray : public BTFTypeBase {
const DIType *ElemTyNoQual;
uint32_t ElemSize;
struct BTF::BTFArray ArrayInfo;
uint32_t ElemTypeNoQual;

public:
BTFTypeArray(const DIType *Ty, uint32_t ElemTypeId,
uint32_t ElemSize, uint32_t NumElems);
BTFTypeArray(uint32_t ElemTypeId, uint32_t NumElems);
uint32_t getSize() { return BTFTypeBase::getSize() + BTF::BTFArraySize; }
void completeType(BTFDebug &BDebug);
void emitType(MCStreamer &OS);
void getLocInfo(uint32_t Loc, uint32_t &LocOffset, uint32_t &ElementTypeId);
};

/// Handle struct/union type.
class BTFTypeStruct : public BTFTypeBase {
const DICompositeType *STy;
bool HasBitField;
std::vector<struct BTF::BTFMember> Members;
std::vector<uint32_t> MemberTypeNoQual;

public:
BTFTypeStruct(const DICompositeType *STy, bool IsStruct, bool HasBitField,
Expand All @@ -134,8 +128,6 @@ class BTFTypeStruct : public BTFTypeBase {
void completeType(BTFDebug &BDebug);
void emitType(MCStreamer &OS);
std::string getName();
void getMemberInfo(uint32_t Loc, uint32_t &Offset, uint32_t &MemberType);
uint32_t getStructSize();
};

/// Handle function pointer.
Expand Down Expand Up @@ -262,7 +254,6 @@ class BTFDebug : public DebugHandlerBase {
StringMap<std::vector<std::string>> FileContent;
std::map<std::string, std::unique_ptr<BTFKindDataSec>> DataSecEntries;
std::vector<BTFTypeStruct *> StructTypes;
std::vector<BTFTypeArray *> ArrayTypes;
std::map<std::string, int64_t> AccessOffsets;
std::map<StringRef, std::pair<bool, std::vector<BTFTypeDerived *>>>
FixupDerivedTypes;
Expand Down Expand Up @@ -312,10 +303,6 @@ class BTFDebug : public DebugHandlerBase {
void generateOffsetReloc(const MachineInstr *MI, const MCSymbol *ORSym,
DIType *RootTy, StringRef AccessPattern);

/// Set the to-be-traversed Struct/Array Type based on TypeId.
void setTypeFromId(uint32_t TypeId, BTFTypeStruct **PrevStructType,
BTFTypeArray **PrevArrayType);

/// Populating unprocessed struct type.
unsigned populateStructType(const DIType *Ty);

Expand Down
124 changes: 124 additions & 0 deletions llvm/test/CodeGen/BPF/CORE/offset-reloc-cast-array-1.ll
@@ -0,0 +1,124 @@
; RUN: llc -march=bpfel -filetype=asm -o - %s | FileCheck -check-prefixes=CHECK %s
; RUN: llc -march=bpfeb -filetype=asm -o - %s | FileCheck -check-prefixes=CHECK %s
; Source code:
; struct v1 {int a; int b;};
; typedef struct v1 __v1;
; typedef __v1 arr[4];
; struct v3 { char c; int d[100]; };
; #define _(x) (__builtin_preserve_access_index(x))
; #define cast_to_arr(x) ((arr *)(x))
; int get_value(const int *arg);
; int test(struct v3 *arg) {
; return get_value(_(&cast_to_arr(&arg->d[0])[0][2].b));
; }
; Compilation flag:
; clang -target bpf -O2 -g -S -emit-llvm test.c

%struct.v3 = type { i8, [100 x i32] }
%struct.v1 = type { i32, i32 }

; Function Attrs: nounwind
define dso_local i32 @test(%struct.v3* %arg) local_unnamed_addr #0 !dbg !22 {
entry:
call void @llvm.dbg.value(metadata %struct.v3* %arg, metadata !32, metadata !DIExpression()), !dbg !33
%0 = tail call [100 x i32]* @llvm.preserve.struct.access.index.p0a100i32.p0s_struct.v3s(%struct.v3* %arg, i32 1, i32 1), !dbg !34, !llvm.preserve.access.index !26
%1 = tail call i32* @llvm.preserve.array.access.index.p0i32.p0a100i32([100 x i32]* %0, i32 1, i32 0), !dbg !34, !llvm.preserve.access.index !15
%2 = bitcast i32* %1 to [4 x %struct.v1]*, !dbg !34
%3 = tail call [4 x %struct.v1]* @llvm.preserve.array.access.index.p0a4s_struct.v1s.p0a4s_struct.v1s([4 x %struct.v1]* %2, i32 0, i32 0), !dbg !34, !llvm.preserve.access.index !4
%4 = tail call %struct.v1* @llvm.preserve.array.access.index.p0s_struct.v1s.p0a4s_struct.v1s([4 x %struct.v1]* %3, i32 1, i32 2), !dbg !34, !llvm.preserve.access.index !5
%5 = tail call i32* @llvm.preserve.struct.access.index.p0i32.p0s_struct.v1s(%struct.v1* %4, i32 1, i32 1), !dbg !34, !llvm.preserve.access.index !8
%call = tail call i32 @get_value(i32* %5) #4, !dbg !35
ret i32 %call, !dbg !36
}

; CHECK: r2 = 4
; CHECK: r1 += r2
; CHECK: r2 = 20
; CHECK: r1 += r2
; CHECK: call get_value

; CHECK: .long 1 # BTF_KIND_STRUCT(id = [[TID1:[0-9]+]])
; CHECK: .long 100 # BTF_KIND_STRUCT(id = [[TID2:[0-9]+]])

; CHECK: .ascii "v3" # string offset=1
; CHECK: .ascii ".text" # string offset=46
; CHECK: .ascii "0:1:0" # string offset=52
; CHECK: .ascii "2:1" # string offset=107

; CHECK: .long 12 # OffsetReloc
; CHECK-NEXT: .long 46 # Offset reloc section string offset=46
; CHECK-NEXT: .long 2
; CHECK-NEXT: .long .Ltmp{{[0-9]+}}
; CHECK-NEXT: .long [[TID1]]
; CHECK-NEXT: .long 52
; CHECK-NEXT: .long .Ltmp{{[0-9]+}}
; CHECK-NEXT: .long [[TID2]]
; CHECK-NEXT: .long 107

declare dso_local i32 @get_value(i32*) local_unnamed_addr #1

; Function Attrs: nounwind readnone
declare [100 x i32]* @llvm.preserve.struct.access.index.p0a100i32.p0s_struct.v3s(%struct.v3*, i32, i32) #2

; Function Attrs: nounwind readnone
declare i32* @llvm.preserve.array.access.index.p0i32.p0a100i32([100 x i32]*, i32, i32) #2

; Function Attrs: nounwind readnone
declare [4 x %struct.v1]* @llvm.preserve.array.access.index.p0a4s_struct.v1s.p0a4s_struct.v1s([4 x %struct.v1]*, i32, i32) #2

; Function Attrs: nounwind readnone
declare %struct.v1* @llvm.preserve.array.access.index.p0s_struct.v1s.p0a4s_struct.v1s([4 x %struct.v1]*, i32, i32) #2

; Function Attrs: nounwind readnone
declare i32* @llvm.preserve.struct.access.index.p0i32.p0s_struct.v1s(%struct.v1*, i32, i32) #2

; Function Attrs: nounwind readnone speculatable willreturn
declare void @llvm.dbg.value(metadata, metadata, metadata) #3

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 #1 = { "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "no-infs-fp-math"="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 #2 = { nounwind readnone }
attributes #3 = { nounwind readnone speculatable willreturn }
attributes #4 = { nounwind }

!llvm.dbg.cu = !{!0}
!llvm.module.flags = !{!18, !19, !20}
!llvm.ident = !{!21}

!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 10.0.0 (trunk 367256) (llvm/trunk 367266)", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, retainedTypes: !3, nameTableKind: None)
!1 = !DIFile(filename: "test.c", directory: "/tmp/home/yhs/work/tests/llvm/cast")
!2 = !{}
!3 = !{!4, !15, !5}
!4 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !5, size: 64)
!5 = !DIDerivedType(tag: DW_TAG_typedef, name: "arr", file: !1, line: 3, baseType: !6)
!6 = !DICompositeType(tag: DW_TAG_array_type, baseType: !7, size: 256, elements: !13)
!7 = !DIDerivedType(tag: DW_TAG_typedef, name: "__v1", file: !1, line: 2, baseType: !8)
!8 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "v1", file: !1, line: 1, size: 64, elements: !9)
!9 = !{!10, !12}
!10 = !DIDerivedType(tag: DW_TAG_member, name: "a", scope: !8, file: !1, line: 1, baseType: !11, size: 32)
!11 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
!12 = !DIDerivedType(tag: DW_TAG_member, name: "b", scope: !8, file: !1, line: 1, baseType: !11, size: 32, offset: 32)
!13 = !{!14}
!14 = !DISubrange(count: 4)
!15 = !DICompositeType(tag: DW_TAG_array_type, baseType: !11, size: 3200, elements: !16)
!16 = !{!17}
!17 = !DISubrange(count: 100)
!18 = !{i32 2, !"Dwarf Version", i32 4}
!19 = !{i32 2, !"Debug Info Version", i32 3}
!20 = !{i32 1, !"wchar_size", i32 4}
!21 = !{!"clang version 10.0.0 (trunk 367256) (llvm/trunk 367266)"}
!22 = distinct !DISubprogram(name: "test", scope: !1, file: !1, line: 8, type: !23, scopeLine: 8, flags: DIFlagPrototyped, isDefinition: true, isOptimized: true, unit: !0, retainedNodes: !31)
!23 = !DISubroutineType(types: !24)
!24 = !{!11, !25}
!25 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !26, size: 64)
!26 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "v3", file: !1, line: 4, size: 3232, elements: !27)
!27 = !{!28, !30}
!28 = !DIDerivedType(tag: DW_TAG_member, name: "c", scope: !26, file: !1, line: 4, baseType: !29, size: 8)
!29 = !DIBasicType(name: "char", size: 8, encoding: DW_ATE_signed_char)
!30 = !DIDerivedType(tag: DW_TAG_member, name: "d", scope: !26, file: !1, line: 4, baseType: !15, size: 3200, offset: 32)
!31 = !{!32}
!32 = !DILocalVariable(name: "arg", arg: 1, scope: !22, file: !1, line: 8, type: !25)
!33 = !DILocation(line: 0, scope: !22)
!34 = !DILocation(line: 9, column: 20, scope: !22)
!35 = !DILocation(line: 9, column: 10, scope: !22)
!36 = !DILocation(line: 9, column: 3, scope: !22)

0 comments on commit 37d24a6

Please sign in to comment.