[BPF] Handle offset reloc endpoint ending in the middle of chain properly

During studying support for bitfield, I found an issue for
an example like the one in test offset-reloc-middle-chain.ll.
  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);
  }

The IR looks like:
  %0 = llvm.preserve.struct.access(base, ...)
  %1 = llvm.preserve.struct.access(%0, ...)
  %2 = llvm.preserve.struct.access(%1, ...)
  using %0, %1 and %2

In this case, we need to generate three relocatiions
corresponding to chains: (%0), (%0, %1) and (%0, %1, %2).
After collecting all the chains, the current implementation
process each chain (in a map) with code generation sequentially.
For example, after (%0) is processed, the code may look like:
  %0 = base + special_global_variable
  // llvm.preserve.struct.access(base, ...) is delisted
  // from the instruction stream.
  %1 = llvm.preserve.struct.access(%0, ...)
  %2 = llvm.preserve.struct.access(%1, ...)
  using %0, %1 and %2

When processing chain (%0, %1), the current implementation
tries to visit intrinsic llvm.preserve.struct.access(base, ...)
to get some of its properties and this caused segfault.

This patch fixed the issue by remembering all necessary
information (kind, metadata, access_index, base) during
analysis phase, so in code generation phase there is
no need to examine the intrinsic call instructions.
This also simplifies the code.

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

llvm-svn: 373621
This commit is contained in:
Yonghong Song 2019-10-03 16:30:29 +00:00
parent ba643691dd
commit 02ac75092d
2 changed files with 227 additions and 118 deletions

View File

@ -90,6 +90,13 @@ public:
static char ID;
BPFAbstractMemberAccess() : ModulePass(ID) {}
struct CallInfo {
uint32_t Kind;
uint32_t AccessIndex;
MDNode *Metadata;
Value *Base;
};
private:
enum : uint32_t {
BPFPreserveArrayAI = 1,
@ -99,34 +106,32 @@ private:
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
@ -192,9 +197,7 @@ static uint32_t calcArraySize(const DICompositeType *CTy, uint32_t StartDim) {
/// 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;
@ -202,30 +205,30 @@ bool BPFAbstractMemberAccess::IsPreserveDIAccessIndexCall(const CallInst *Call,
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;
}
@ -238,8 +241,7 @@ void BPFAbstractMemberAccess::replaceWithGEP(std::vector<CallInst *> &CallList,
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);
@ -265,16 +267,14 @@ bool BPFAbstractMemberAccess::removePreserveAccessIndexIntrinsic(Module &M) {
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);
@ -349,99 +349,94 @@ bool BPFAbstractMemberAccess::IsValidAIChain(const MDNode *ParentType,
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;
}
}
}
@ -452,44 +447,37 @@ void BPFAbstractMemberAccess::collectAICallChains(Module &M, Function &F) {
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;
}
@ -508,14 +496,14 @@ Value *BPFAbstractMemberAccess::computeBaseAndAccessKey(CallInst *Call,
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;
@ -527,9 +515,7 @@ Value *BPFAbstractMemberAccess::computeBaseAndAccessKey(CallInst *Call,
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;
@ -580,18 +566,14 @@ Value *BPFAbstractMemberAccess::computeBaseAndAccessKey(CallInst *Call,
// 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();
@ -615,11 +597,11 @@ Value *BPFAbstractMemberAccess::computeBaseAndAccessKey(CallInst *Call,
/// 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;

View File

@ -0,0 +1,127 @@
; 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)