static char ID;
BPFAbstractMemberAccess() : ModulePass(ID) {}
+ struct CallInfo {
+ uint32_t Kind;
+ uint32_t AccessIndex;
+ MDNode *Metadata;
+ Value *Base;
+ };
+
private:
enum : uint32_t {
BPFPreserveArrayAI = 1,
std::map<std::string, GlobalVariable *> GEPGlobals;
// A map to link preserve_*_access_index instrinsic calls.
- std::map<CallInst *, std::pair<CallInst *, uint32_t>> AIChain;
+ std::map<CallInst *, std::pair<CallInst *, CallInfo>> AIChain;
// A map to hold all the base preserve_*_access_index instrinsic calls.
// The base call is not an input of any other preserve_*_access_index
// intrinsics.
- std::map<CallInst *, uint32_t> BaseAICalls;
+ std::map<CallInst *, CallInfo> BaseAICalls;
bool doTransformation(Module &M);
- void traceAICall(CallInst *Call, uint32_t Kind, const MDNode *ParentMeta,
- uint32_t ParentAI);
- void traceBitCast(BitCastInst *BitCast, CallInst *Parent, uint32_t Kind,
- const MDNode *ParentMeta, uint32_t ParentAI);
- void traceGEP(GetElementPtrInst *GEP, CallInst *Parent, uint32_t Kind,
- const MDNode *ParentMeta, uint32_t ParentAI);
+ void traceAICall(CallInst *Call, CallInfo &ParentInfo);
+ void traceBitCast(BitCastInst *BitCast, CallInst *Parent,
+ CallInfo &ParentInfo);
+ void traceGEP(GetElementPtrInst *GEP, CallInst *Parent,
+ CallInfo &ParentInfo);
void collectAICallChains(Module &M, Function &F);
- bool IsPreserveDIAccessIndexCall(const CallInst *Call, uint32_t &Kind,
- const MDNode *&TypeMeta, uint32_t &AccessIndex);
+ bool IsPreserveDIAccessIndexCall(const CallInst *Call, CallInfo &Cinfo);
bool IsValidAIChain(const MDNode *ParentMeta, uint32_t ParentAI,
const MDNode *ChildMeta);
bool removePreserveAccessIndexIntrinsic(Module &M);
void replaceWithGEP(std::vector<CallInst *> &CallList,
uint32_t NumOfZerosIndex, uint32_t DIIndex);
- Value *computeBaseAndAccessKey(CallInst *Call, std::string &AccessKey,
- uint32_t Kind, MDNode *&BaseMeta);
- bool getAccessIndex(const Value *IndexValue, uint64_t &AccessIndex);
- bool transformGEPChain(Module &M, CallInst *Call, uint32_t Kind);
+ Value *computeBaseAndAccessKey(CallInst *Call, CallInfo &CInfo,
+ std::string &AccessKey, MDNode *&BaseMeta);
+ uint64_t getConstant(const Value *IndexValue);
+ bool transformGEPChain(Module &M, CallInst *Call, CallInfo &CInfo);
};
} // End anonymous namespace
/// Check whether a call is a preserve_*_access_index intrinsic call or not.
bool BPFAbstractMemberAccess::IsPreserveDIAccessIndexCall(const CallInst *Call,
- uint32_t &Kind,
- const MDNode *&TypeMeta,
- uint32_t &AccessIndex) {
+ CallInfo &CInfo) {
if (!Call)
return false;
if (!GV)
return false;
if (GV->getName().startswith("llvm.preserve.array.access.index")) {
- Kind = BPFPreserveArrayAI;
- TypeMeta = Call->getMetadata(LLVMContext::MD_preserve_access_index);
- if (!TypeMeta)
+ CInfo.Kind = BPFPreserveArrayAI;
+ CInfo.Metadata = Call->getMetadata(LLVMContext::MD_preserve_access_index);
+ if (!CInfo.Metadata)
report_fatal_error("Missing metadata for llvm.preserve.array.access.index intrinsic");
- AccessIndex = cast<ConstantInt>(Call->getArgOperand(2))
- ->getZExtValue();
+ CInfo.AccessIndex = getConstant(Call->getArgOperand(2));
+ CInfo.Base = Call->getArgOperand(0);
return true;
}
if (GV->getName().startswith("llvm.preserve.union.access.index")) {
- Kind = BPFPreserveUnionAI;
- TypeMeta = Call->getMetadata(LLVMContext::MD_preserve_access_index);
- if (!TypeMeta)
+ CInfo.Kind = BPFPreserveUnionAI;
+ CInfo.Metadata = Call->getMetadata(LLVMContext::MD_preserve_access_index);
+ if (!CInfo.Metadata)
report_fatal_error("Missing metadata for llvm.preserve.union.access.index intrinsic");
- AccessIndex = cast<ConstantInt>(Call->getArgOperand(1))
- ->getZExtValue();
+ CInfo.AccessIndex = getConstant(Call->getArgOperand(1));
+ CInfo.Base = Call->getArgOperand(0);
return true;
}
if (GV->getName().startswith("llvm.preserve.struct.access.index")) {
- Kind = BPFPreserveStructAI;
- TypeMeta = Call->getMetadata(LLVMContext::MD_preserve_access_index);
- if (!TypeMeta)
+ CInfo.Kind = BPFPreserveStructAI;
+ CInfo.Metadata = Call->getMetadata(LLVMContext::MD_preserve_access_index);
+ if (!CInfo.Metadata)
report_fatal_error("Missing metadata for llvm.preserve.struct.access.index intrinsic");
- AccessIndex = cast<ConstantInt>(Call->getArgOperand(2))
- ->getZExtValue();
+ CInfo.AccessIndex = getConstant(Call->getArgOperand(2));
+ CInfo.Base = Call->getArgOperand(0);
return true;
}
for (auto Call : CallList) {
uint32_t Dimension = 1;
if (DimensionIndex > 0)
- Dimension = cast<ConstantInt>(Call->getArgOperand(DimensionIndex))
- ->getZExtValue();
+ Dimension = getConstant(Call->getArgOperand(DimensionIndex));
Constant *Zero =
ConstantInt::get(Type::getInt32Ty(Call->getParent()->getContext()), 0);
for (auto &BB : F)
for (auto &I : BB) {
auto *Call = dyn_cast<CallInst>(&I);
- uint32_t Kind;
- const MDNode *TypeMeta;
- uint32_t AccessIndex;
- if (!IsPreserveDIAccessIndexCall(Call, Kind, TypeMeta, AccessIndex))
+ CallInfo CInfo;
+ if (!IsPreserveDIAccessIndexCall(Call, CInfo))
continue;
Found = true;
- if (Kind == BPFPreserveArrayAI)
+ if (CInfo.Kind == BPFPreserveArrayAI)
PreserveArrayIndexCalls.push_back(Call);
- else if (Kind == BPFPreserveUnionAI)
+ else if (CInfo.Kind == BPFPreserveUnionAI)
PreserveUnionIndexCalls.push_back(Call);
else
PreserveStructIndexCalls.push_back(Call);
return dyn_cast<DICompositeType>(stripQualifiers(Ty)) == CTy;
}
-void BPFAbstractMemberAccess::traceAICall(CallInst *Call, uint32_t Kind,
- const MDNode *ParentMeta,
- uint32_t ParentAI) {
+void BPFAbstractMemberAccess::traceAICall(CallInst *Call,
+ CallInfo &ParentInfo) {
for (User *U : Call->users()) {
Instruction *Inst = dyn_cast<Instruction>(U);
if (!Inst)
continue;
if (auto *BI = dyn_cast<BitCastInst>(Inst)) {
- traceBitCast(BI, Call, Kind, ParentMeta, ParentAI);
+ traceBitCast(BI, Call, ParentInfo);
} else if (auto *CI = dyn_cast<CallInst>(Inst)) {
- uint32_t CIKind;
- const MDNode *ChildMeta;
- uint32_t ChildAI;
- if (IsPreserveDIAccessIndexCall(CI, CIKind, ChildMeta, ChildAI) &&
- IsValidAIChain(ParentMeta, ParentAI, ChildMeta)) {
- AIChain[CI] = std::make_pair(Call, Kind);
- traceAICall(CI, CIKind, ChildMeta, ChildAI);
+ CallInfo ChildInfo;
+
+ if (IsPreserveDIAccessIndexCall(CI, ChildInfo) &&
+ IsValidAIChain(ParentInfo.Metadata, ParentInfo.AccessIndex,
+ ChildInfo.Metadata)) {
+ AIChain[CI] = std::make_pair(Call, ParentInfo);
+ traceAICall(CI, ChildInfo);
} else {
- BaseAICalls[Call] = Kind;
+ BaseAICalls[Call] = ParentInfo;
}
} else if (auto *GI = dyn_cast<GetElementPtrInst>(Inst)) {
if (GI->hasAllZeroIndices())
- traceGEP(GI, Call, Kind, ParentMeta, ParentAI);
+ traceGEP(GI, Call, ParentInfo);
else
- BaseAICalls[Call] = Kind;
+ BaseAICalls[Call] = ParentInfo;
} else {
- BaseAICalls[Call] = Kind;
+ BaseAICalls[Call] = ParentInfo;
}
}
}
void BPFAbstractMemberAccess::traceBitCast(BitCastInst *BitCast,
- CallInst *Parent, uint32_t Kind,
- const MDNode *ParentMeta,
- uint32_t ParentAI) {
+ CallInst *Parent,
+ CallInfo &ParentInfo) {
for (User *U : BitCast->users()) {
Instruction *Inst = dyn_cast<Instruction>(U);
if (!Inst)
continue;
if (auto *BI = dyn_cast<BitCastInst>(Inst)) {
- traceBitCast(BI, Parent, Kind, ParentMeta, ParentAI);
+ traceBitCast(BI, Parent, ParentInfo);
} else if (auto *CI = dyn_cast<CallInst>(Inst)) {
- uint32_t CIKind;
- const MDNode *ChildMeta;
- uint32_t ChildAI;
- if (IsPreserveDIAccessIndexCall(CI, CIKind, ChildMeta, ChildAI) &&
- IsValidAIChain(ParentMeta, ParentAI, ChildMeta)) {
- AIChain[CI] = std::make_pair(Parent, Kind);
- traceAICall(CI, CIKind, ChildMeta, ChildAI);
+ CallInfo ChildInfo;
+ if (IsPreserveDIAccessIndexCall(CI, ChildInfo) &&
+ IsValidAIChain(ParentInfo.Metadata, ParentInfo.AccessIndex,
+ ChildInfo.Metadata)) {
+ AIChain[CI] = std::make_pair(Parent, ParentInfo);
+ traceAICall(CI, ChildInfo);
} else {
- BaseAICalls[Parent] = Kind;
+ BaseAICalls[Parent] = ParentInfo;
}
} else if (auto *GI = dyn_cast<GetElementPtrInst>(Inst)) {
if (GI->hasAllZeroIndices())
- traceGEP(GI, Parent, Kind, ParentMeta, ParentAI);
+ traceGEP(GI, Parent, ParentInfo);
else
- BaseAICalls[Parent] = Kind;
+ BaseAICalls[Parent] = ParentInfo;
} else {
- BaseAICalls[Parent] = Kind;
+ BaseAICalls[Parent] = ParentInfo;
}
}
}
void BPFAbstractMemberAccess::traceGEP(GetElementPtrInst *GEP, CallInst *Parent,
- uint32_t Kind, const MDNode *ParentMeta,
- uint32_t ParentAI) {
+ CallInfo &ParentInfo) {
for (User *U : GEP->users()) {
Instruction *Inst = dyn_cast<Instruction>(U);
if (!Inst)
continue;
if (auto *BI = dyn_cast<BitCastInst>(Inst)) {
- traceBitCast(BI, Parent, Kind, ParentMeta, ParentAI);
+ traceBitCast(BI, Parent, ParentInfo);
} else if (auto *CI = dyn_cast<CallInst>(Inst)) {
- uint32_t CIKind;
- const MDNode *ChildMeta;
- uint32_t ChildAI;
- if (IsPreserveDIAccessIndexCall(CI, CIKind, ChildMeta, ChildAI) &&
- IsValidAIChain(ParentMeta, ParentAI, ChildMeta)) {
- AIChain[CI] = std::make_pair(Parent, Kind);
- traceAICall(CI, CIKind, ChildMeta, ChildAI);
+ CallInfo ChildInfo;
+ if (IsPreserveDIAccessIndexCall(CI, ChildInfo) &&
+ IsValidAIChain(ParentInfo.Metadata, ParentInfo.AccessIndex,
+ ChildInfo.Metadata)) {
+ AIChain[CI] = std::make_pair(Parent, ParentInfo);
+ traceAICall(CI, ChildInfo);
} else {
- BaseAICalls[Parent] = Kind;
+ BaseAICalls[Parent] = ParentInfo;
}
} else if (auto *GI = dyn_cast<GetElementPtrInst>(Inst)) {
if (GI->hasAllZeroIndices())
- traceGEP(GI, Parent, Kind, ParentMeta, ParentAI);
+ traceGEP(GI, Parent, ParentInfo);
else
- BaseAICalls[Parent] = Kind;
+ BaseAICalls[Parent] = ParentInfo;
} else {
- BaseAICalls[Parent] = Kind;
+ BaseAICalls[Parent] = ParentInfo;
}
}
}
for (auto &BB : F)
for (auto &I : BB) {
- uint32_t Kind;
- const MDNode *TypeMeta;
- uint32_t AccessIndex;
+ CallInfo CInfo;
auto *Call = dyn_cast<CallInst>(&I);
- if (!IsPreserveDIAccessIndexCall(Call, Kind, TypeMeta, AccessIndex) ||
+ if (!IsPreserveDIAccessIndexCall(Call, CInfo) ||
AIChain.find(Call) != AIChain.end())
continue;
- traceAICall(Call, Kind, TypeMeta, AccessIndex);
+ traceAICall(Call, CInfo);
}
}
-/// Get access index from the preserve_*_access_index intrinsic calls.
-bool BPFAbstractMemberAccess::getAccessIndex(const Value *IndexValue,
- uint64_t &AccessIndex) {
+uint64_t BPFAbstractMemberAccess::getConstant(const Value *IndexValue) {
const ConstantInt *CV = dyn_cast<ConstantInt>(IndexValue);
- if (!CV)
- return false;
-
- AccessIndex = CV->getValue().getZExtValue();
- return true;
+ assert(CV);
+ return CV->getValue().getZExtValue();
}
/// Compute the base of the whole preserve_*_access_index chains, i.e., the base
/// pointer of the first preserve_*_access_index call, and construct the access
/// string, which will be the name of a global variable.
Value *BPFAbstractMemberAccess::computeBaseAndAccessKey(CallInst *Call,
+ CallInfo &CInfo,
std::string &AccessKey,
- uint32_t Kind,
MDNode *&TypeMeta) {
Value *Base = nullptr;
std::string TypeName;
- std::stack<std::pair<CallInst *, uint32_t>> CallStack;
+ std::stack<std::pair<CallInst *, CallInfo>> CallStack;
// Put the access chain into a stack with the top as the head of the chain.
while (Call) {
- CallStack.push(std::make_pair(Call, Kind));
- Kind = AIChain[Call].second;
+ CallStack.push(std::make_pair(Call, CInfo));
+ CInfo = AIChain[Call].second;
Call = AIChain[Call].first;
}
while (CallStack.size()) {
auto StackElem = CallStack.top();
Call = StackElem.first;
- Kind = StackElem.second;
+ CInfo = StackElem.second;
if (!Base)
- Base = Call->getArgOperand(0);
+ Base = CInfo.Base;
- MDNode *MDN = Call->getMetadata(LLVMContext::MD_preserve_access_index);
- DIType *Ty = stripQualifiers(cast<DIType>(MDN));
- if (Kind == BPFPreserveUnionAI || Kind == BPFPreserveStructAI) {
+ DIType *Ty = stripQualifiers(cast<DIType>(CInfo.Metadata));
+ if (CInfo.Kind == BPFPreserveUnionAI ||
+ CInfo.Kind == BPFPreserveStructAI) {
// struct or union type
TypeName = Ty->getName();
TypeMeta = Ty;
CallStack.pop();
// BPFPreserveArrayAI
- uint64_t AccessIndex;
- if (!getAccessIndex(Call->getArgOperand(2), AccessIndex))
- return nullptr;
+ uint64_t AccessIndex = CInfo.AccessIndex;
DIType *BaseTy = nullptr;
bool CheckElemType = false;
// and access key construction.
while (CallStack.size()) {
auto StackElem = CallStack.top();
- Call = StackElem.first;
- Kind = StackElem.second;
+ CInfo = StackElem.second;
CallStack.pop();
// Access Index
- uint64_t AccessIndex;
- uint32_t ArgIndex = (Kind == BPFPreserveUnionAI) ? 1 : 2;
- if (!getAccessIndex(Call->getArgOperand(ArgIndex), AccessIndex))
- return nullptr;
+ uint64_t AccessIndex = CInfo.AccessIndex;
AccessKey += ":" + std::to_string(AccessIndex);
- MDNode *MDN = Call->getMetadata(LLVMContext::MD_preserve_access_index);
+ MDNode *MDN = CInfo.Metadata;
// At this stage, it cannot be pointer type.
auto *CTy = cast<DICompositeType>(stripQualifiers(cast<DIType>(MDN)));
uint32_t Tag = CTy->getTag();
/// Call/Kind is the base preserve_*_access_index() call. Attempts to do
/// transformation to a chain of relocable GEPs.
bool BPFAbstractMemberAccess::transformGEPChain(Module &M, CallInst *Call,
- uint32_t Kind) {
+ CallInfo &CInfo) {
std::string AccessKey;
MDNode *TypeMeta;
Value *Base =
- computeBaseAndAccessKey(Call, AccessKey, Kind, TypeMeta);
+ computeBaseAndAccessKey(Call, CInfo, AccessKey, TypeMeta);
if (!Base)
return false;
--- /dev/null
+; 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 t1 {
+; int c;
+; };
+; struct s1 {
+; struct t1 b;
+; };
+; struct r1 {
+; struct s1 a;
+; };
+; #define _(x) __builtin_preserve_access_index(x)
+; void test1(void *p1, void *p2, void *p3);
+; void test(struct r1 *arg) {
+; struct s1 *ps = _(&arg->a);
+; struct t1 *pt = _(&arg->a.b);
+; int *pi = _(&arg->a.b.c);
+; test1(ps, pt, pi);
+; }
+; Compilation flag:
+; clang -target bpf -O2 -g -S -emit-llvm test.c
+
+%struct.r1 = type { %struct.s1 }
+%struct.s1 = type { %struct.t1 }
+%struct.t1 = type { i32 }
+
+; Function Attrs: nounwind
+define dso_local void @test(%struct.r1* %arg) local_unnamed_addr #0 !dbg !7 {
+entry:
+ call void @llvm.dbg.value(metadata %struct.r1* %arg, metadata !22, metadata !DIExpression()), !dbg !29
+ %0 = tail call %struct.s1* @llvm.preserve.struct.access.index.p0s_struct.s1s.p0s_struct.r1s(%struct.r1* %arg, i32 0, i32 0), !dbg !30, !llvm.preserve.access.index !11
+ call void @llvm.dbg.value(metadata %struct.s1* %0, metadata !23, metadata !DIExpression()), !dbg !29
+ %1 = tail call %struct.t1* @llvm.preserve.struct.access.index.p0s_struct.t1s.p0s_struct.s1s(%struct.s1* %0, i32 0, i32 0), !dbg !31, !llvm.preserve.access.index !14
+ call void @llvm.dbg.value(metadata %struct.t1* %1, metadata !25, metadata !DIExpression()), !dbg !29
+ %2 = tail call i32* @llvm.preserve.struct.access.index.p0i32.p0s_struct.t1s(%struct.t1* %1, i32 0, i32 0), !dbg !32, !llvm.preserve.access.index !17
+ call void @llvm.dbg.value(metadata i32* %2, metadata !27, metadata !DIExpression()), !dbg !29
+ %3 = bitcast %struct.s1* %0 to i8*, !dbg !33
+ %4 = bitcast %struct.t1* %1 to i8*, !dbg !34
+ %5 = bitcast i32* %2 to i8*, !dbg !35
+ tail call void @test1(i8* %3, i8* %4, i8* %5) #4, !dbg !36
+ ret void, !dbg !37
+}
+
+; CHECK: .long 1 # BTF_KIND_STRUCT(id = 2)
+
+; CHECK: .ascii "r1" # string offset=1
+; CHECK: .ascii ".text" # string offset=29
+; CHECK: .ascii "0:0" # string offset=72
+; CHECK: .ascii "0:0:0" # string offset=76
+; CHECK: .ascii "0:0:0:0" # string offset=82
+
+; CHECK: .long 12 # OffsetReloc
+; CHECK-NEXT: .long 29 # Offset reloc section string offset=29
+; CHECK-NEXT: .long 3
+; CHECK_NEXT: .long .Ltmp{{[0-9]+}}
+; CHECK_NEXT: .long 2
+; CHECK_NEXT: .long 72
+; CHECK_NEXT: .long .Ltmp{{[0-9]+}}
+; CHECK_NEXT: .long 2
+; CHECK_NEXT: .long 76
+; CHECK_NEXT: .long .Ltmp{{[0-9]+}}
+; CHECK_NEXT: .long 2
+; CHECK_NEXT: .long 82
+
+; Function Attrs: nounwind readnone
+declare %struct.s1* @llvm.preserve.struct.access.index.p0s_struct.s1s.p0s_struct.r1s(%struct.r1*, i32, i32) #1
+
+; Function Attrs: nounwind readnone
+declare %struct.t1* @llvm.preserve.struct.access.index.p0s_struct.t1s.p0s_struct.s1s(%struct.s1*, i32, i32) #1
+
+; Function Attrs: nounwind readnone
+declare i32* @llvm.preserve.struct.access.index.p0i32.p0s_struct.t1s(%struct.t1*, i32, i32) #1
+
+declare dso_local void @test1(i8*, i8*, i8*) local_unnamed_addr #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 = { nounwind readnone }
+attributes #2 = { "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 #3 = { nounwind readnone speculatable willreturn }
+attributes #4 = { nounwind }
+
+!llvm.dbg.cu = !{!0}
+!llvm.module.flags = !{!3, !4, !5}
+!llvm.ident = !{!6}
+
+!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 10.0.0 (https://github.com/llvm/llvm-project.git 42b3328a2368b38fba6bdb0c616fe6c5520e3bc5)", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, nameTableKind: None)
+!1 = !DIFile(filename: "test.c", directory: "/tmp/home/yhs/work/tests/core")
+!2 = !{}
+!3 = !{i32 2, !"Dwarf Version", i32 4}
+!4 = !{i32 2, !"Debug Info Version", i32 3}
+!5 = !{i32 1, !"wchar_size", i32 4}
+!6 = !{!"clang version 10.0.0 (https://github.com/llvm/llvm-project.git 42b3328a2368b38fba6bdb0c616fe6c5520e3bc5)"}
+!7 = distinct !DISubprogram(name: "test", scope: !1, file: !1, line: 12, type: !8, scopeLine: 12, flags: DIFlagPrototyped, isDefinition: true, isOptimized: true, unit: !0, retainedNodes: !21)
+!8 = !DISubroutineType(types: !9)
+!9 = !{null, !10}
+!10 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !11, size: 64)
+!11 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "r1", file: !1, line: 7, size: 32, elements: !12)
+!12 = !{!13}
+!13 = !DIDerivedType(tag: DW_TAG_member, name: "a", scope: !11, file: !1, line: 8, baseType: !14, size: 32)
+!14 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "s1", file: !1, line: 4, size: 32, elements: !15)
+!15 = !{!16}
+!16 = !DIDerivedType(tag: DW_TAG_member, name: "b", scope: !14, file: !1, line: 5, baseType: !17, size: 32)
+!17 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "t1", file: !1, line: 1, size: 32, elements: !18)
+!18 = !{!19}
+!19 = !DIDerivedType(tag: DW_TAG_member, name: "c", scope: !17, file: !1, line: 2, baseType: !20, size: 32)
+!20 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+!21 = !{!22, !23, !25, !27}
+!22 = !DILocalVariable(name: "arg", arg: 1, scope: !7, file: !1, line: 12, type: !10)
+!23 = !DILocalVariable(name: "ps", scope: !7, file: !1, line: 13, type: !24)
+!24 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64)
+!25 = !DILocalVariable(name: "pt", scope: !7, file: !1, line: 14, type: !26)
+!26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !17, size: 64)
+!27 = !DILocalVariable(name: "pi", scope: !7, file: !1, line: 15, type: !28)
+!28 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !20, size: 64)
+!29 = !DILocation(line: 0, scope: !7)
+!30 = !DILocation(line: 13, column: 19, scope: !7)
+!31 = !DILocation(line: 14, column: 19, scope: !7)
+!32 = !DILocation(line: 15, column: 13, scope: !7)
+!33 = !DILocation(line: 16, column: 9, scope: !7)
+!34 = !DILocation(line: 16, column: 13, scope: !7)
+!35 = !DILocation(line: 16, column: 17, scope: !7)
+!36 = !DILocation(line: 16, column: 3, scope: !7)
+!37 = !DILocation(line: 17, column: 1, scope: !7)