1
0
mirror of https://github.com/RPCS3/llvm-mirror.git synced 2024-11-23 03:02:36 +01:00

[BPF] Fix CO-RE bugs with bitfields

bitfield handling is not robust with current implementation.
I have seen two issues as described below.

Issue 1:
  struct s {
    long long f1;
    char f2;
    char b1:1;
  } *p;
  The current approach will generate an access bit size
  56 (from b1 to the end of structure) which will be
  rejected as it is not power of 2.

Issue 2:
  struct s {
    char f1;
    char b1:3;
    char b2:5;
    char b3:6:
    char b4:2;
    char f2;
  };
  The LLVM will group 4 bitfields together with 2 bytes. But
  loading 2 bytes is not correct as it violates alignment
  requirement. Note that sometimes, LLVM breaks a large
  bitfield groups into multiple groups, but not in this case.

To resolve the above two issues, this patch takes a
different approach. The alignment for the structure is used
to construct the offset of the bitfield access. The bitfield
incurred memory access is an aligned memory access with alignment/size
equal to the alignment of the structure.
This also simplified the code.

This may not be the optimal memory access in terms of memory access
width. But this should be okay since extracting the bitfield value
will have the same amount of work regardless of what kind of
memory access width.

Differential Revision: https://reviews.llvm.org/D69837
This commit is contained in:
Yonghong Song 2019-10-30 12:44:49 -04:00
parent 73d1a7c36c
commit 1b8e92c220
3 changed files with 283 additions and 37 deletions

View File

@ -130,6 +130,8 @@ private:
BPFPreserveFieldInfoAI = 4,
};
const DataLayout *DL;
std::map<std::string, GlobalVariable *> GEPGlobals;
// A map to link preserve_*_access_index instrinsic calls.
std::map<CallInst *, std::pair<CallInst *, CallInfo>> AIChain;
@ -154,11 +156,11 @@ private:
void replaceWithGEP(std::vector<CallInst *> &CallList,
uint32_t NumOfZerosIndex, uint32_t DIIndex);
bool HasPreserveFieldInfoCall(CallInfoStack &CallStack);
void GetStorageBitRange(DICompositeType *CTy, DIDerivedType *MemberTy,
uint32_t AccessIndex, uint32_t &StartBitOffset,
uint32_t &EndBitOffset);
void GetStorageBitRange(DIDerivedType *MemberTy, uint32_t RecordAlignment,
uint32_t &StartBitOffset, uint32_t &EndBitOffset);
uint32_t GetFieldInfo(uint32_t InfoKind, DICompositeType *CTy,
uint32_t AccessIndex, uint32_t PatchImm);
uint32_t AccessIndex, uint32_t PatchImm,
uint32_t RecordAlignment);
Value *computeBaseAndAccessKey(CallInst *Call, CallInfo &CInfo,
std::string &AccessKey, MDNode *&BaseMeta);
@ -182,6 +184,7 @@ bool BPFAbstractMemberAccess::runOnModule(Module &M) {
if (M.debug_compile_units().empty())
return false;
DL = &M.getDataLayout();
return doTransformation(M);
}
@ -509,40 +512,29 @@ uint64_t BPFAbstractMemberAccess::getConstant(const Value *IndexValue) {
}
/// Get the start and the end of storage offset for \p MemberTy.
/// The storage bits are corresponding to the LLVM internal types,
/// and the storage bits for the member determines what load width
/// to use in order to extract the bitfield value.
void BPFAbstractMemberAccess::GetStorageBitRange(DICompositeType *CTy,
DIDerivedType *MemberTy,
uint32_t AccessIndex,
void BPFAbstractMemberAccess::GetStorageBitRange(DIDerivedType *MemberTy,
uint32_t RecordAlignment,
uint32_t &StartBitOffset,
uint32_t &EndBitOffset) {
auto SOff = dyn_cast<ConstantInt>(MemberTy->getStorageOffsetInBits());
assert(SOff);
StartBitOffset = SOff->getZExtValue();
uint32_t MemberBitSize = MemberTy->getSizeInBits();
uint32_t MemberBitOffset = MemberTy->getOffsetInBits();
uint32_t AlignBits = RecordAlignment * 8;
if (RecordAlignment > 8 || MemberBitSize > AlignBits)
report_fatal_error("Unsupported field expression for llvm.bpf.preserve.field.info, "
"requiring too big alignment");
EndBitOffset = CTy->getSizeInBits();
uint32_t Index = AccessIndex + 1;
for (; Index < CTy->getElements().size(); ++Index) {
auto Member = cast<DIDerivedType>(CTy->getElements()[Index]);
if (!Member->isBitField()) {
EndBitOffset = Member->getOffsetInBits();
break;
}
SOff = dyn_cast<ConstantInt>(Member->getStorageOffsetInBits());
assert(SOff);
unsigned BitOffset = SOff->getZExtValue();
if (BitOffset != StartBitOffset) {
EndBitOffset = BitOffset;
break;
}
}
StartBitOffset = MemberBitOffset & ~(AlignBits - 1);
if ((StartBitOffset + AlignBits) < (MemberBitOffset + MemberBitSize))
report_fatal_error("Unsupported field expression for llvm.bpf.preserve.field.info, "
"cross alignment boundary");
EndBitOffset = StartBitOffset + AlignBits;
}
uint32_t BPFAbstractMemberAccess::GetFieldInfo(uint32_t InfoKind,
DICompositeType *CTy,
uint32_t AccessIndex,
uint32_t PatchImm) {
uint32_t PatchImm,
uint32_t RecordAlignment) {
if (InfoKind == BPFCoreSharedInfo::FIELD_EXISTENCE)
return 1;
@ -557,9 +549,10 @@ uint32_t BPFAbstractMemberAccess::GetFieldInfo(uint32_t InfoKind,
if (!MemberTy->isBitField()) {
PatchImm += MemberTy->getOffsetInBits() >> 3;
} else {
auto SOffset = dyn_cast<ConstantInt>(MemberTy->getStorageOffsetInBits());
assert(SOffset);
PatchImm += SOffset->getZExtValue() >> 3;
unsigned SBitOffset, NextSBitOffset;
GetStorageBitRange(MemberTy, RecordAlignment, SBitOffset,
NextSBitOffset);
PatchImm += SBitOffset >> 3;
}
}
return PatchImm;
@ -576,7 +569,7 @@ uint32_t BPFAbstractMemberAccess::GetFieldInfo(uint32_t InfoKind,
return SizeInBits >> 3;
unsigned SBitOffset, NextSBitOffset;
GetStorageBitRange(CTy, MemberTy, AccessIndex, SBitOffset, NextSBitOffset);
GetStorageBitRange(MemberTy, RecordAlignment, SBitOffset, NextSBitOffset);
SizeInBits = NextSBitOffset - SBitOffset;
if (SizeInBits & (SizeInBits - 1))
report_fatal_error("Unsupported field expression for llvm.bpf.preserve.field.info");
@ -636,7 +629,7 @@ uint32_t BPFAbstractMemberAccess::GetFieldInfo(uint32_t InfoKind,
}
unsigned SBitOffset, NextSBitOffset;
GetStorageBitRange(CTy, MemberTy, AccessIndex, SBitOffset, NextSBitOffset);
GetStorageBitRange(MemberTy, RecordAlignment, SBitOffset, NextSBitOffset);
if (NextSBitOffset - SBitOffset > 64)
report_fatal_error("too big field size for llvm.bpf.preserve.field.info");
@ -667,7 +660,7 @@ uint32_t BPFAbstractMemberAccess::GetFieldInfo(uint32_t InfoKind,
}
unsigned SBitOffset, NextSBitOffset;
GetStorageBitRange(CTy, MemberTy, AccessIndex, SBitOffset, NextSBitOffset);
GetStorageBitRange(MemberTy, RecordAlignment, SBitOffset, NextSBitOffset);
if (NextSBitOffset - SBitOffset > 64)
report_fatal_error("too big field size for llvm.bpf.preserve.field.info");
@ -822,9 +815,12 @@ Value *BPFAbstractMemberAccess::computeBaseAndAccessKey(CallInst *Call,
AccessKey += ":" + std::to_string(AccessIndex);
MDNode *MDN = CInfo.Metadata;
uint32_t RecordAlignment =
DL->getABITypeAlignment(CInfo.Base->getType()->getPointerElementType());
// At this stage, it cannot be pointer type.
auto *CTy = cast<DICompositeType>(stripQualifiers(cast<DIType>(MDN)));
PatchImm = GetFieldInfo(InfoKind, CTy, AccessIndex, PatchImm);
PatchImm = GetFieldInfo(InfoKind, CTy, AccessIndex, PatchImm,
RecordAlignment);
}
// Access key is the type name + reloc type + patched imm + access string,

View File

@ -0,0 +1,126 @@
; RUN: llc -march=bpfel -filetype=asm -o - %s | FileCheck -check-prefixes=CHECK,CHECK-EL,CHECK-ALU64 %s
; RUN: llc -march=bpfeb -filetype=asm -o - %s | FileCheck -check-prefixes=CHECK,CHECK-EB,CHECK-ALU64 %s
; RUN: llc -march=bpfel -mattr=+alu32 -filetype=asm -o - %s | FileCheck -check-prefixes=CHECK,CHECK-EL,CHECK-ALU32 %s
; RUN: llc -march=bpfeb -mattr=+alu32 -filetype=asm -o - %s | FileCheck -check-prefixes=CHECK,CHECK-EB,CHECK-ALU32 %s
; Source code:
; struct s {
; unsigned long long f1;
; unsigned f2;
; unsigned f3;
; unsigned f4;
; unsigned char f5;
; unsigned bf1:5,
; bf2:1;
; };
; enum {FIELD_TYPE_OFFSET = 0, FIELD_TYPE_SIZE = 1, FIELD_TYPE_LSHIFT_U64 = 4,};
; int test(struct s *arg) {
; return __builtin_preserve_field_info(arg->bf2, FIELD_TYPE_OFFSET) +
; __builtin_preserve_field_info(arg->bf2, FIELD_TYPE_SIZE) +
; __builtin_preserve_field_info(arg->bf2, FIELD_TYPE_LSHIFT_U64);
; }
; Compilation flag:
; clang -target bpf -O2 -g -S -emit-llvm test.c
%struct.s = type { i64, i32, i32, i32, i8, i8 }
; Function Attrs: nounwind readnone
define dso_local i32 @test(%struct.s* %arg) local_unnamed_addr #0 !dbg !13 {
entry:
call void @llvm.dbg.value(metadata %struct.s* %arg, metadata !30, metadata !DIExpression()), !dbg !31
%0 = tail call i8* @llvm.preserve.struct.access.index.p0i8.p0s_struct.ss(%struct.s* %arg, i32 5, i32 6), !dbg !32, !llvm.preserve.access.index !18
%1 = tail call i32 @llvm.bpf.preserve.field.info.p0i8(i8* %0, i64 0), !dbg !33
%2 = tail call i32 @llvm.bpf.preserve.field.info.p0i8(i8* %0, i64 1), !dbg !34
%add = add i32 %2, %1, !dbg !35
%3 = tail call i32 @llvm.bpf.preserve.field.info.p0i8(i8* %0, i64 4), !dbg !36
%add1 = add i32 %add, %3, !dbg !37
ret i32 %add1, !dbg !38
}
; CHECK: r1 = 16
; CHECK: r0 = 8
; CHECK-ALU64: r0 += r1
; CHECK-ALU32: w0 += w1
; CHECK-EL: r1 = 18
; CHECK-EB: r1 = 45
; CHECK-ALU64: r0 += r1
; CHECK-ALU32: w0 += w1
; CHECK: exit
; CHECK: .long 1 # BTF_KIND_STRUCT(id = 2)
; CHECK: .byte 115 # string offset=1
; CHECK: .ascii ".text" # string offset=89
; CHECK: .ascii "0:6" # string offset=95
; CHECK: .long 16 # FieldReloc
; CHECK-NEXT: .long 89 # Field reloc section string offset=89
; CHECK-NEXT: .long 3
; CHECK-NEXT: .long .Ltmp{{[0-9]+}}
; CHECK-NEXT: .long 2
; CHECK-NEXT: .long 95
; CHECK-NEXT: .long 0
; CHECK-NEXT: .long .Ltmp{{[0-9]+}}
; CHECK-NEXT: .long 2
; CHECK-NEXT: .long 95
; CHECK-NEXT: .long 1
; CHECK-NEXT: .long .Ltmp{{[0-9]+}}
; CHECK-NEXT: .long 2
; CHECK-NEXT: .long 95
; CHECK-NEXT: .long 4
; Function Attrs: nounwind readnone
declare i8* @llvm.preserve.struct.access.index.p0i8.p0s_struct.ss(%struct.s*, i32, i32) #1
; Function Attrs: nounwind readnone
declare i32 @llvm.bpf.preserve.field.info.p0i8(i8*, i64) #1
; Function Attrs: nounwind readnone speculatable
declare void @llvm.dbg.value(metadata, metadata, metadata) #2
attributes #0 = { nounwind readnone "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 = { nounwind readnone }
attributes #2 = { nounwind readnone speculatable }
!llvm.dbg.cu = !{!0}
!llvm.module.flags = !{!9, !10, !11}
!llvm.ident = !{!12}
!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 10.0.0 (https://github.com/llvm/llvm-project.git bc6913e314806882e2b537b5b03996800078d2ad)", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, nameTableKind: None)
!1 = !DIFile(filename: "test.c", directory: "/tmp/home/yhs/work/tests/core")
!2 = !{!3}
!3 = !DICompositeType(tag: DW_TAG_enumeration_type, file: !1, line: 10, baseType: !4, size: 32, elements: !5)
!4 = !DIBasicType(name: "unsigned int", size: 32, encoding: DW_ATE_unsigned)
!5 = !{!6, !7, !8}
!6 = !DIEnumerator(name: "FIELD_TYPE_OFFSET", value: 0, isUnsigned: true)
!7 = !DIEnumerator(name: "FIELD_TYPE_SIZE", value: 1, isUnsigned: true)
!8 = !DIEnumerator(name: "FIELD_TYPE_LSHIFT_U64", value: 4, isUnsigned: true)
!9 = !{i32 2, !"Dwarf Version", i32 4}
!10 = !{i32 2, !"Debug Info Version", i32 3}
!11 = !{i32 1, !"wchar_size", i32 4}
!12 = !{!"clang version 10.0.0 (https://github.com/llvm/llvm-project.git bc6913e314806882e2b537b5b03996800078d2ad)"}
!13 = distinct !DISubprogram(name: "test", scope: !1, file: !1, line: 11, type: !14, scopeLine: 11, flags: DIFlagPrototyped, isDefinition: true, isOptimized: true, unit: !0, retainedNodes: !29)
!14 = !DISubroutineType(types: !15)
!15 = !{!16, !17}
!16 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
!17 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !18, size: 64)
!18 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "s", file: !1, line: 1, size: 192, elements: !19)
!19 = !{!20, !22, !23, !24, !25, !27, !28}
!20 = !DIDerivedType(tag: DW_TAG_member, name: "f1", scope: !18, file: !1, line: 2, baseType: !21, size: 64)
!21 = !DIBasicType(name: "long long unsigned int", size: 64, encoding: DW_ATE_unsigned)
!22 = !DIDerivedType(tag: DW_TAG_member, name: "f2", scope: !18, file: !1, line: 3, baseType: !4, size: 32, offset: 64)
!23 = !DIDerivedType(tag: DW_TAG_member, name: "f3", scope: !18, file: !1, line: 4, baseType: !4, size: 32, offset: 96)
!24 = !DIDerivedType(tag: DW_TAG_member, name: "f4", scope: !18, file: !1, line: 5, baseType: !4, size: 32, offset: 128)
!25 = !DIDerivedType(tag: DW_TAG_member, name: "f5", scope: !18, file: !1, line: 6, baseType: !26, size: 8, offset: 160)
!26 = !DIBasicType(name: "unsigned char", size: 8, encoding: DW_ATE_unsigned_char)
!27 = !DIDerivedType(tag: DW_TAG_member, name: "bf1", scope: !18, file: !1, line: 7, baseType: !4, size: 5, offset: 168, flags: DIFlagBitField, extraData: i64 168)
!28 = !DIDerivedType(tag: DW_TAG_member, name: "bf2", scope: !18, file: !1, line: 8, baseType: !4, size: 1, offset: 173, flags: DIFlagBitField, extraData: i64 168)
!29 = !{!30}
!30 = !DILocalVariable(name: "arg", arg: 1, scope: !13, file: !1, line: 11, type: !17)
!31 = !DILocation(line: 0, scope: !13)
!32 = !DILocation(line: 12, column: 45, scope: !13)
!33 = !DILocation(line: 12, column: 10, scope: !13)
!34 = !DILocation(line: 13, column: 10, scope: !13)
!35 = !DILocation(line: 12, column: 69, scope: !13)
!36 = !DILocation(line: 14, column: 10, scope: !13)
!37 = !DILocation(line: 13, column: 67, scope: !13)
!38 = !DILocation(line: 12, column: 3, scope: !13)

View File

@ -0,0 +1,124 @@
; RUN: llc -march=bpfel -filetype=asm -o - %s | FileCheck -check-prefixes=CHECK,CHECK-EL,CHECK-ALU64 %s
; RUN: llc -march=bpfeb -filetype=asm -o - %s | FileCheck -check-prefixes=CHECK,CHECK-EB,CHECK-ALU64 %s
; RUN: llc -march=bpfel -mattr=+alu32 -filetype=asm -o - %s | FileCheck -check-prefixes=CHECK,CHECK-EL,CHECK-ALU32 %s
; RUN: llc -march=bpfeb -mattr=+alu32 -filetype=asm -o - %s | FileCheck -check-prefixes=CHECK,CHECK-EB,CHECK-ALU32 %s
; Source code:
; struct s {
; char f1;
; char bf1:6,
; bf2:2,
; bf3:5,
; bf4:3;
; };
; enum {FIELD_TYPE_OFFSET = 0, FIELD_TYPE_SIZE = 1, FIELD_TYPE_LSHIFT_U64 = 4,};
; int test(struct s *arg) {
; return __builtin_preserve_field_info(arg->bf4, FIELD_TYPE_OFFSET) +
; __builtin_preserve_field_info(arg->bf4, FIELD_TYPE_SIZE) +
; __builtin_preserve_field_info(arg->bf4, FIELD_TYPE_LSHIFT_U64);
; }
; For this case, the IR type has the same starting storage offset for fields
; bf1, bf1, bf3 and bf4 and the ABI alignment is 1 byte. So for bf4 access,
; the starting offset has to be at the beginning of field bf3.
; Compilation flag:
; clang -target bpf -O2 -g -S -emit-llvm test.c
%struct.s = type <{ i8, i16 }>
; Function Attrs: nounwind readnone
define dso_local i32 @test(%struct.s* %arg) local_unnamed_addr #0 !dbg !13 {
entry:
call void @llvm.dbg.value(metadata %struct.s* %arg, metadata !27, metadata !DIExpression()), !dbg !28
%0 = tail call i16* @llvm.preserve.struct.access.index.p0i16.p0s_struct.ss(%struct.s* %arg, i32 1, i32 4), !dbg !29, !llvm.preserve.access.index !18
%1 = tail call i32 @llvm.bpf.preserve.field.info.p0i16(i16* %0, i64 0), !dbg !30
%2 = tail call i32 @llvm.bpf.preserve.field.info.p0i16(i16* %0, i64 1), !dbg !31
%add = add i32 %2, %1, !dbg !32
%3 = tail call i32 @llvm.bpf.preserve.field.info.p0i16(i16* %0, i64 4), !dbg !33
%add1 = add i32 %add, %3, !dbg !34
ret i32 %add1, !dbg !35
}
; CHECK: r1 = 2
; CHECK: r0 = 1
; CHECK-ALU64: r0 += r1
; CHECK-ALU32: w0 += w1
; CHECK-EL: r1 = 56
; CHECK-EB: r1 = 61
; CHECK-ALU64: r0 += r1
; CHECK-ALU32: w0 += w1
; CHECK: exit
; CHECK: .long 1 # BTF_KIND_STRUCT(id = 2)
; CHECK: .byte 115 # string offset=1
; CHECK: .ascii ".text" # string offset=40
; CHECK: .ascii "0:4" # string offset=46
; CHECK: .long 16 # FieldReloc
; CHECK-NEXT: .long 40 # Field reloc section string offset=40
; CHECK-NEXT: .long 3
; CHECK-NEXT: .long .Ltmp{{[0-9]+}}
; CHECK-NEXT: .long 2
; CHECK-NEXT: .long 46
; CHECK-NEXT: .long 0
; CHECK-NEXT: .long .Ltmp{{[0-9]+}}
; CHECK-NEXT: .long 2
; CHECK-NEXT: .long 46
; CHECK-NEXT: .long 1
; CHECK-NEXT: .long .Ltmp{{[0-9]+}}
; CHECK-NEXT: .long 2
; CHECK-NEXT: .long 46
; CHECK-NEXT: .long 4
; Function Attrs: nounwind readnone
declare i16* @llvm.preserve.struct.access.index.p0i16.p0s_struct.ss(%struct.s*, i32, i32) #1
; Function Attrs: nounwind readnone
declare i32 @llvm.bpf.preserve.field.info.p0i16(i16*, i64) #1
; Function Attrs: nounwind readnone speculatable
declare void @llvm.dbg.value(metadata, metadata, metadata) #2
attributes #0 = { nounwind readnone "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 = { nounwind readnone }
attributes #2 = { nounwind readnone speculatable }
!llvm.dbg.cu = !{!0}
!llvm.module.flags = !{!9, !10, !11}
!llvm.ident = !{!12}
!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 10.0.0 (https://github.com/llvm/llvm-project.git 630ca91834ecc06349cb3b4bd2982c1b85b5ad96)", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, nameTableKind: None)
!1 = !DIFile(filename: "test.c", directory: "/tmp/home/yhs/work/tests/core")
!2 = !{!3}
!3 = !DICompositeType(tag: DW_TAG_enumeration_type, file: !1, line: 8, baseType: !4, size: 32, elements: !5)
!4 = !DIBasicType(name: "unsigned int", size: 32, encoding: DW_ATE_unsigned)
!5 = !{!6, !7, !8}
!6 = !DIEnumerator(name: "FIELD_TYPE_OFFSET", value: 0, isUnsigned: true)
!7 = !DIEnumerator(name: "FIELD_TYPE_SIZE", value: 1, isUnsigned: true)
!8 = !DIEnumerator(name: "FIELD_TYPE_LSHIFT_U64", value: 4, isUnsigned: true)
!9 = !{i32 2, !"Dwarf Version", i32 4}
!10 = !{i32 2, !"Debug Info Version", i32 3}
!11 = !{i32 1, !"wchar_size", i32 4}
!12 = !{!"clang version 10.0.0 (https://github.com/llvm/llvm-project.git 630ca91834ecc06349cb3b4bd2982c1b85b5ad96)"}
!13 = distinct !DISubprogram(name: "test", scope: !1, file: !1, line: 9, type: !14, scopeLine: 9, flags: DIFlagPrototyped, isDefinition: true, isOptimized: true, unit: !0, retainedNodes: !26)
!14 = !DISubroutineType(types: !15)
!15 = !{!16, !17}
!16 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
!17 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !18, size: 64)
!18 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "s", file: !1, line: 1, size: 24, elements: !19)
!19 = !{!20, !22, !23, !24, !25}
!20 = !DIDerivedType(tag: DW_TAG_member, name: "f1", scope: !18, file: !1, line: 2, baseType: !21, size: 8)
!21 = !DIBasicType(name: "char", size: 8, encoding: DW_ATE_signed_char)
!22 = !DIDerivedType(tag: DW_TAG_member, name: "bf1", scope: !18, file: !1, line: 3, baseType: !21, size: 6, offset: 8, flags: DIFlagBitField, extraData: i64 8)
!23 = !DIDerivedType(tag: DW_TAG_member, name: "bf2", scope: !18, file: !1, line: 4, baseType: !21, size: 2, offset: 14, flags: DIFlagBitField, extraData: i64 8)
!24 = !DIDerivedType(tag: DW_TAG_member, name: "bf3", scope: !18, file: !1, line: 5, baseType: !21, size: 5, offset: 16, flags: DIFlagBitField, extraData: i64 8)
!25 = !DIDerivedType(tag: DW_TAG_member, name: "bf4", scope: !18, file: !1, line: 6, baseType: !21, size: 3, offset: 21, flags: DIFlagBitField, extraData: i64 8)
!26 = !{!27}
!27 = !DILocalVariable(name: "arg", arg: 1, scope: !13, file: !1, line: 9, type: !17)
!28 = !DILocation(line: 0, scope: !13)
!29 = !DILocation(line: 10, column: 45, scope: !13)
!30 = !DILocation(line: 10, column: 10, scope: !13)
!31 = !DILocation(line: 11, column: 10, scope: !13)
!32 = !DILocation(line: 10, column: 69, scope: !13)
!33 = !DILocation(line: 12, column: 10, scope: !13)
!34 = !DILocation(line: 11, column: 67, scope: !13)
!35 = !DILocation(line: 10, column: 3, scope: !13)