Index: llvm/docs/LangRef.rst =================================================================== --- llvm/docs/LangRef.rst +++ llvm/docs/LangRef.rst @@ -4784,8 +4784,9 @@ - ``DW_OP_stack_value`` marks a constant value. - If an expression is marked with ``DW_OP_LLVM_entry_value`` all register and memory read operations refer to the respective value at the function entry. - The first operand of ``DW_OP_LLVM_entry_value`` is the size of following - DWARF expression. + The first operand of ``DW_OP_LLVM_entry_value`` is the number of operations + in the DIExpression that are covered by the entry value, plus the + value/address operand. ``DW_OP_LLVM_entry_value`` may appear after the ``LiveDebugValues`` pass. LLVM only supports entry values for function parameters that are unmodified throughout a function and that are described as Index: llvm/include/llvm/CodeGen/DIE.h =================================================================== --- llvm/include/llvm/CodeGen/DIE.h +++ llvm/include/llvm/CodeGen/DIE.h @@ -550,6 +550,14 @@ return *static_cast(Last ? Last->Next.getPointer() : nullptr); } + void takeNodes(IntrusiveBackList &Other) { + for (auto &N : Other) { + N.Next.setPointerAndInt(&N, true); + push_back(N); + } + Other.Last = nullptr; + } + class const_iterator; class iterator : public iterator_facade_base { @@ -685,6 +693,10 @@ return addValue(Alloc, DIEValue(Attribute, Form, std::forward(Value))); } + /// Take ownership of the nodes in \p Other, and append them to the back of + /// the list. + void takeValues(DIEValueList &Other) { List.takeNodes(Other.List); } + value_range values() { return make_range(value_iterator(List.begin()), value_iterator(List.end())); } Index: llvm/lib/CodeGen/AsmPrinter/ByteStreamer.h =================================================================== --- llvm/lib/CodeGen/AsmPrinter/ByteStreamer.h +++ llvm/lib/CodeGen/AsmPrinter/ByteStreamer.h @@ -75,12 +75,12 @@ SmallVectorImpl &Buffer; SmallVectorImpl &Comments; +public: /// Only verbose textual output needs comments. This will be set to /// true for that case, and false otherwise. If false, comments passed in to /// the emit methods will be ignored. - bool GenerateComments; + const bool GenerateComments; -public: BufferByteStreamer(SmallVectorImpl &Buffer, SmallVectorImpl &Comments, bool GenerateComments) Index: llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp =================================================================== --- llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp +++ llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp @@ -1217,7 +1217,7 @@ if (DIExpr->isEntryValue()) { DwarfExpr.setEntryValueFlag(); - DwarfExpr.addEntryValueExpression(Cursor); + DwarfExpr.beginEntryValueExpression(Cursor); } const TargetRegisterInfo &TRI = *Asm->MF->getSubtarget().getRegisterInfo(); Index: llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp =================================================================== --- llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp +++ llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp @@ -170,26 +170,26 @@ static constexpr unsigned ULEB128PadSize = 4; void DebugLocDwarfExpression::emitOp(uint8_t Op, const char *Comment) { - BS.EmitInt8( + getActiveStreamer().EmitInt8( Op, Comment ? Twine(Comment) + " " + dwarf::OperationEncodingString(Op) : dwarf::OperationEncodingString(Op)); } void DebugLocDwarfExpression::emitSigned(int64_t Value) { - BS.EmitSLEB128(Value, Twine(Value)); + getActiveStreamer().EmitSLEB128(Value, Twine(Value)); } void DebugLocDwarfExpression::emitUnsigned(uint64_t Value) { - BS.EmitULEB128(Value, Twine(Value)); + getActiveStreamer().EmitULEB128(Value, Twine(Value)); } void DebugLocDwarfExpression::emitData1(uint8_t Value) { - BS.EmitInt8(Value, Twine(Value)); + getActiveStreamer().EmitInt8(Value, Twine(Value)); } void DebugLocDwarfExpression::emitBaseTypeRef(uint64_t Idx) { assert(Idx < (1ULL << (ULEB128PadSize * 7)) && "Idx wont fit"); - BS.EmitULEB128(Idx, Twine(Idx), ULEB128PadSize); + getActiveStreamer().EmitULEB128(Idx, Twine(Idx), ULEB128PadSize); } bool DebugLocDwarfExpression::isFrameRegister(const TargetRegisterInfo &TRI, @@ -198,6 +198,28 @@ return false; } +void DebugLocDwarfExpression::enableTemporaryBuffer() { + assert(!IsBuffering && "Already buffering?"); + IsBuffering = true; +} + +void DebugLocDwarfExpression::disableTemporaryBuffer() { IsBuffering = false; } + +unsigned DebugLocDwarfExpression::getTemporaryBufferSize() { + return TmpBytes.size(); +} + +void DebugLocDwarfExpression::commitTemporaryBuffer() { + for (auto Byte : enumerate(TmpBytes)) { + const char *Comment = (Byte.index() < TmpComments.size()) + ? TmpComments[Byte.index()].c_str() + : ""; + OutBS.EmitInt8(Byte.value(), Comment); + } + TmpBytes.clear(); + TmpComments.clear(); +} + bool DbgVariable::isBlockByrefVariable() const { assert(getVariable() && "Invalid complex DbgVariable!"); return getVariable()->getType()->isBlockByrefStruct(); @@ -2217,7 +2239,7 @@ if (DIExpr->isEntryValue()) { DwarfExpr.setEntryValueFlag(); - DwarfExpr.addEntryValueExpression(Cursor); + DwarfExpr.beginEntryValueExpression(Cursor); } const TargetRegisterInfo &TRI = *AP.MF->getSubtarget().getRegisterInfo(); Index: llvm/lib/CodeGen/AsmPrinter/DwarfExpression.h =================================================================== --- llvm/lib/CodeGen/AsmPrinter/DwarfExpression.h +++ llvm/lib/CodeGen/AsmPrinter/DwarfExpression.h @@ -13,6 +13,7 @@ #ifndef LLVM_LIB_CODEGEN_ASMPRINTER_DWARFEXPRESSION_H #define LLVM_LIB_CODEGEN_ASMPRINTER_DWARFEXPRESSION_H +#include "ByteStreamer.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/None.h" #include "llvm/ADT/Optional.h" @@ -26,7 +27,6 @@ class AsmPrinter; class APInt; -class ByteStreamer; class DwarfCompileUnit; class DIELoc; class TargetRegisterInfo; @@ -95,6 +95,13 @@ /// Base class containing the logic for constructing DWARF expressions /// independently of whether they are emitted into a DIE or into a .debug_loc /// entry. +/// +/// Some DWARF operations, e.g. DW_OP_entry_value, need to calculate the size +/// of a succeeding DWARF block before the latter is emitted to the output. +/// To handle such cases, data can conditionally be emitted to a temporary +/// buffer, which can later on be committed to the main output. The size of the +/// temporary buffer is queryable, allowing for the size of the data to be +/// emitted before the data is committed. class DwarfExpression { protected: /// Holds information about all subregisters comprising a register location. @@ -104,6 +111,9 @@ const char *Comment; }; + /// Whether we are currently emitting an entry value operation. + bool IsEmittingEntryValue = false; + DwarfCompileUnit &CU; /// The register location, if any. @@ -178,6 +188,22 @@ virtual void emitBaseTypeRef(uint64_t Idx) = 0; + /// Start emitting data to the temporary buffer. The data stored in the + /// temporary buffer can be committed to the main output using + /// commitTemporaryBuffer(). + virtual void enableTemporaryBuffer() = 0; + + /// Disable emission to the temporary buffer. This does not commit data + /// in the temporary buffer to the main output. + virtual void disableTemporaryBuffer() = 0; + + /// Return the emitted size, in number of bytes, for the data stored in the + /// temporary buffer. + virtual unsigned getTemporaryBufferSize() = 0; + + /// Commit the data stored in the temporary buffer to the main output. + virtual void commitTemporaryBuffer() = 0; + /// Emit a normalized unsigned constant. void emitConstu(uint64_t Value); @@ -237,6 +263,10 @@ /// expression. See PR21176 for more details. void addStackValue(); + /// Finalize an entry value by emitting its size operand, and committing the + /// DWARF block which has been emitted to the temporary buffer. + void finalizeEntryValue(); + ~DwarfExpression() = default; public: @@ -287,8 +317,11 @@ DIExpressionCursor &Expr, unsigned MachineReg, unsigned FragmentOffsetInBits = 0); - /// Emit entry value dwarf operation. - void addEntryValueExpression(DIExpressionCursor &ExprCursor); + /// Begin emission of an entry value dwarf operation. The entry value's + /// first operand is the size of the DWARF block (its second operand), + /// which needs to be calculated at time of emission, so we don't emit + /// any operands here. + void beginEntryValueExpression(DIExpressionCursor &ExprCursor); /// Emit all remaining operations in the DIExpressionCursor. /// @@ -308,31 +341,56 @@ /// DwarfExpression implementation for .debug_loc entries. class DebugLocDwarfExpression final : public DwarfExpression { - ByteStreamer &BS; + BufferByteStreamer &OutBS; + SmallString<32> TmpBytes; + SmallVector TmpComments; + BufferByteStreamer TmpBS; + bool IsBuffering = false; + + /// Return the byte streamer that currently is being emitted to. + ByteStreamer &getActiveStreamer() { return IsBuffering ? TmpBS : OutBS; } void emitOp(uint8_t Op, const char *Comment = nullptr) override; void emitSigned(int64_t Value) override; void emitUnsigned(uint64_t Value) override; void emitData1(uint8_t Value) override; void emitBaseTypeRef(uint64_t Idx) override; + + void enableTemporaryBuffer() override; + void disableTemporaryBuffer() override; + unsigned getTemporaryBufferSize() override; + void commitTemporaryBuffer() override; + bool isFrameRegister(const TargetRegisterInfo &TRI, unsigned MachineReg) override; - public: - DebugLocDwarfExpression(unsigned DwarfVersion, ByteStreamer &BS, DwarfCompileUnit &CU) - : DwarfExpression(DwarfVersion, CU), BS(BS) {} + DebugLocDwarfExpression(unsigned DwarfVersion, BufferByteStreamer &BS, + DwarfCompileUnit &CU) + : DwarfExpression(DwarfVersion, CU), OutBS(BS), + TmpBS(TmpBytes, TmpComments, BS.GenerateComments) {} }; /// DwarfExpression implementation for singular DW_AT_location. class DIEDwarfExpression final : public DwarfExpression { -const AsmPrinter &AP; - DIELoc &DIE; + const AsmPrinter &AP; + DIELoc &OutDIE; + DIELoc TmpDIE; + bool IsBuffering = false; + + /// Return the DIE that currently is being emitted to. + DIELoc &getActiveDIE() { return IsBuffering ? TmpDIE : OutDIE; } void emitOp(uint8_t Op, const char *Comment = nullptr) override; void emitSigned(int64_t Value) override; void emitUnsigned(uint64_t Value) override; void emitData1(uint8_t Value) override; void emitBaseTypeRef(uint64_t Idx) override; + + void enableTemporaryBuffer() override; + void disableTemporaryBuffer() override; + unsigned getTemporaryBufferSize() override; + void commitTemporaryBuffer() override; + bool isFrameRegister(const TargetRegisterInfo &TRI, unsigned MachineReg) override; public: @@ -340,7 +398,7 @@ DIELoc *finalize() { DwarfExpression::finalize(); - return &DIE; + return &OutDIE; } }; Index: llvm/lib/CodeGen/AsmPrinter/DwarfExpression.cpp =================================================================== --- llvm/lib/CodeGen/AsmPrinter/DwarfExpression.cpp +++ llvm/lib/CodeGen/AsmPrinter/DwarfExpression.cpp @@ -254,6 +254,9 @@ addOpPiece(Reg.Size); } + if (isEntryValue()) + finalizeEntryValue(); + if (isEntryValue() && !isParameterValue() && DwarfVersion >= 4) emitOp(dwarf::DW_OP_stack_value); @@ -313,14 +316,28 @@ return true; } -void DwarfExpression::addEntryValueExpression(DIExpressionCursor &ExprCursor) { +void DwarfExpression::beginEntryValueExpression( + DIExpressionCursor &ExprCursor) { auto Op = ExprCursor.take(); assert(Op && Op->getOp() == dwarf::DW_OP_LLVM_entry_value); assert(!isMemoryLocation() && "We don't support entry values of memory locations yet"); + assert(!IsEmittingEntryValue && "Already emitting entry value?"); + assert(Op->getArg(0) == 1 && + "Can currently only emit entry values covering a single operation"); emitOp(CU.getDwarf5OrGNULocationAtom(dwarf::DW_OP_entry_value)); - emitUnsigned(Op->getArg(0)); + IsEmittingEntryValue = true; + enableTemporaryBuffer(); +} + +void DwarfExpression::finalizeEntryValue() { + assert(IsEmittingEntryValue && "Entry value not open?"); + disableTemporaryBuffer(); + unsigned Size = getTemporaryBufferSize(); + emitUnsigned(Size); + commitTemporaryBuffer(); + IsEmittingEntryValue = false; } /// Assuming a well-formed expression, match "DW_OP_deref* DW_OP_LLVM_fragment?". Index: llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp =================================================================== --- llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp +++ llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp @@ -47,31 +47,42 @@ #define DEBUG_TYPE "dwarfdebug" DIEDwarfExpression::DIEDwarfExpression(const AsmPrinter &AP, - DwarfCompileUnit &CU, - DIELoc &DIE) - : DwarfExpression(AP.getDwarfVersion(), CU), AP(AP), - DIE(DIE) {} + DwarfCompileUnit &CU, DIELoc &DIE) + : DwarfExpression(AP.getDwarfVersion(), CU), AP(AP), OutDIE(DIE) {} void DIEDwarfExpression::emitOp(uint8_t Op, const char* Comment) { - CU.addUInt(DIE, dwarf::DW_FORM_data1, Op); + CU.addUInt(getActiveDIE(), dwarf::DW_FORM_data1, Op); } void DIEDwarfExpression::emitSigned(int64_t Value) { - CU.addSInt(DIE, dwarf::DW_FORM_sdata, Value); + CU.addSInt(getActiveDIE(), dwarf::DW_FORM_sdata, Value); } void DIEDwarfExpression::emitUnsigned(uint64_t Value) { - CU.addUInt(DIE, dwarf::DW_FORM_udata, Value); + CU.addUInt(getActiveDIE(), dwarf::DW_FORM_udata, Value); } void DIEDwarfExpression::emitData1(uint8_t Value) { - CU.addUInt(DIE, dwarf::DW_FORM_data1, Value); + CU.addUInt(getActiveDIE(), dwarf::DW_FORM_data1, Value); } void DIEDwarfExpression::emitBaseTypeRef(uint64_t Idx) { - CU.addBaseTypeRef(DIE, Idx); + CU.addBaseTypeRef(getActiveDIE(), Idx); } +void DIEDwarfExpression::enableTemporaryBuffer() { + assert(!IsBuffering && "Already buffering?"); + IsBuffering = true; +} + +void DIEDwarfExpression::disableTemporaryBuffer() { IsBuffering = false; } + +unsigned DIEDwarfExpression::getTemporaryBufferSize() { + return TmpDIE.ComputeSize(&AP); +} + +void DIEDwarfExpression::commitTemporaryBuffer() { OutDIE.takeValues(TmpDIE); } + bool DIEDwarfExpression::isFrameRegister(const TargetRegisterInfo &TRI, unsigned MachineReg) { return MachineReg == TRI.getFrameRegister(*AP.MF); Index: llvm/test/DebugInfo/ARM/entry-value-multi-byte-expr.ll =================================================================== --- /dev/null +++ llvm/test/DebugInfo/ARM/entry-value-multi-byte-expr.ll @@ -0,0 +1,91 @@ +; RUN: llc -debug-entry-values -filetype=asm -o - %s | FileCheck %s + +; Verify that the size operands of the DW_OP_GNU_entry_value operations are +; correct for the multi-byte DW_OP_regx expressions. + +; Based on the following C reproducer: +; +; extern void clobber(); +; double global; +; int f(double a, double b) { +; global = a + b; +; clobber(); +; return 1; +; } + +; This test checks the assembly output rather than the output from +; llvm-dwarfdump, as the latter printed the DW_OP_regx D0 correctly, even +; though the entry value's size operand did not fully cover that: + +; DW_OP_GNU_entry_value(DW_OP_regx D0), DW_OP_stack_value +; +; whereas readelf interpreted it as an DW_OP_GNU_entry_value covering one byte, +; resulting in garbage data: +; +; DW_OP_GNU_entry_value: (DW_OP_regx: 0 (r0)); DW_OP_breg16 (r16): 2; DW_OP_stack_value + +; CHECK: .byte 243 @ DW_OP_GNU_entry_value +; CHECK-NEXT: .byte 3 @ 3 +; CHECK-NEXT: .byte 144 @ DW_OP_regx +; CHECK-NEXT: .byte 128 @ 256 +; CHECK-NEXT: .byte 2 @ +; CHECK-NEXT: .byte 159 @ DW_OP_stack_value + +; CHECK: .byte 243 @ DW_OP_GNU_entry_value +; CHECK-NEXT: .byte 3 @ 3 +; CHECK-NEXT: .byte 144 @ DW_OP_regx +; CHECK-NEXT: .byte 129 @ 257 +; CHECK-NEXT: .byte 2 @ +; CHECK-NEXT: .byte 159 @ DW_OP_stack_value + +target datalayout = "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64" +target triple = "armv7-unknown-unknown" + +@global = common global double 0.000000e+00, align 8, !dbg !0 + +; Function Attrs: nounwind +define arm_aapcs_vfpcc i32 @f(double %a, double %b) #0 !dbg !12 { +entry: + call void @llvm.dbg.value(metadata double %a, metadata !17, metadata !DIExpression()), !dbg !19 + call void @llvm.dbg.value(metadata double %b, metadata !18, metadata !DIExpression()), !dbg !19 + %add = fadd double %a, %b, !dbg !20 + store double %add, double* @global, align 8, !dbg !20 + tail call arm_aapcs_vfpcc void @clobber(), !dbg !21 + ret i32 1, !dbg !22 +} + +declare arm_aapcs_vfpcc void @clobber() + +; Function Attrs: nounwind readnone speculatable willreturn +declare void @llvm.dbg.value(metadata, metadata, metadata) #1 + +attributes #0 = { nounwind } +attributes #1 = { nounwind readnone speculatable willreturn } + +!llvm.dbg.cu = !{!2} +!llvm.module.flags = !{!7, !8, !9, !10} +!llvm.ident = !{!11} + +!0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) +!1 = distinct !DIGlobalVariable(name: "global", scope: !2, file: !3, line: 4, type: !6, isLocal: false, isDefinition: true) +!2 = distinct !DICompileUnit(language: DW_LANG_C99, file: !3, producer: "clang version 10.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !4, globals: !5, nameTableKind: None) +!3 = !DIFile(filename: "entry-value-multi-byte-expr.c", directory: "/") +!4 = !{} +!5 = !{!0} +!6 = !DIBasicType(name: "double", size: 64, encoding: DW_ATE_float) +!7 = !{i32 2, !"Dwarf Version", i32 4} +!8 = !{i32 2, !"Debug Info Version", i32 3} +!9 = !{i32 1, !"wchar_size", i32 4} +!10 = !{i32 1, !"min_enum_size", i32 4} +!11 = !{!"clang version 10.0.0"} +!12 = distinct !DISubprogram(name: "f", scope: !3, file: !3, line: 6, type: !13, scopeLine: 6, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !2, retainedNodes: !16) +!13 = !DISubroutineType(types: !14) +!14 = !{!15, !6, !6} +!15 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!16 = !{!17, !18} +!17 = !DILocalVariable(name: "a", arg: 1, scope: !12, file: !3, line: 6, type: !6, flags: DIFlagArgumentNotModified) +!18 = !DILocalVariable(name: "b", arg: 2, scope: !12, file: !3, line: 6, type: !6, flags: DIFlagArgumentNotModified) +!19 = !DILocation(line: 0, scope: !12) +!20 = !DILocation(line: 7, scope: !12) +!21 = !DILocation(line: 8, scope: !12) +!22 = !DILocation(line: 9, scope: !12) Index: llvm/test/DebugInfo/Sparc/entry-value-complex-reg-expr.ll =================================================================== --- /dev/null +++ llvm/test/DebugInfo/Sparc/entry-value-complex-reg-expr.ll @@ -0,0 +1,78 @@ +; RUN: llc -debug-entry-values -filetype=asm -o - %s | FileCheck %s + +; Verify that the entry value covers both of the DW_OP_regx pieces. Previously +; the size operand of the entry value would be hardcoded to one. +; +; XXX: Is this really what should be emitted, or should we instead emit one +; entry value operation per DW_OP_regx? + +target datalayout = "E-m:e-i64:64-n32:64-S128" +target triple = "sparc64" + +; Based on the following C reproducer: +; +; volatile long double global; +; extern void clobber(); +; int foo(long double p) { +; global = p; +; clobber(); +; return 123; +; } + +; CHECK: .byte 243 ! DW_OP_GNU_entry_value +; CHECK-NEXT: .byte 8 ! 8 +; CHECK-NEXT: .byte 144 ! sub-register DW_OP_regx +; CHECK-NEXT: .byte 72 ! 72 +; CHECK-NEXT: .byte 147 ! DW_OP_piece +; CHECK-NEXT: .byte 8 ! 8 +; CHECK-NEXT: .byte 144 ! sub-register DW_OP_regx +; CHECK-NEXT: .byte 73 ! 73 +; CHECK-NEXT: .byte 147 ! DW_OP_piece +; CHECK-NEXT: .byte 8 ! 8 +; CHECK-NEXT: .byte 159 ! DW_OP_stack_value + +@global = common global fp128 0xL00000000000000000000000000000000, align 16, !dbg !0 + +; Function Attrs: nounwind +define signext i32 @foo(fp128 %p) #0 !dbg !12 { +entry: + call void @llvm.dbg.value(metadata fp128 %p, metadata !17, metadata !DIExpression()), !dbg !18 + store volatile fp128 %p, fp128* @global, align 16, !dbg !19 + tail call void @clobber(), !dbg !20 + ret i32 123, !dbg !21 +} + +declare void @clobber() + +; Function Attrs: nounwind readnone speculatable willreturn +declare void @llvm.dbg.value(metadata, metadata, metadata) #1 + +attributes #0 = { nounwind } +attributes #1 = { nounwind readnone speculatable willreturn } + +!llvm.dbg.cu = !{!2} +!llvm.module.flags = !{!8, !9, !10} +!llvm.ident = !{!11} + +!0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) +!1 = distinct !DIGlobalVariable(name: "global", scope: !2, file: !3, line: 1, type: !6, isLocal: false, isDefinition: true) +!2 = distinct !DICompileUnit(language: DW_LANG_C99, file: !3, producer: "clang version 10.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !4, globals: !5, nameTableKind: None) +!3 = !DIFile(filename: "entry-value-complex-reg-expr.c", directory: "/") +!4 = !{} +!5 = !{!0} +!6 = !DIDerivedType(tag: DW_TAG_volatile_type, baseType: !7) +!7 = !DIBasicType(name: "long double", size: 128, encoding: DW_ATE_float) +!8 = !{i32 2, !"Dwarf Version", i32 4} +!9 = !{i32 2, !"Debug Info Version", i32 3} +!10 = !{i32 1, !"wchar_size", i32 4} +!11 = !{!"clang version 10.0.0"} +!12 = distinct !DISubprogram(name: "foo", scope: !3, file: !3, line: 3, type: !13, scopeLine: 3, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !2, retainedNodes: !16) +!13 = !DISubroutineType(types: !14) +!14 = !{!15, !7} +!15 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!16 = !{!17} +!17 = !DILocalVariable(name: "p", arg: 1, scope: !12, file: !3, line: 3, type: !7, flags: DIFlagArgumentNotModified) +!18 = !DILocation(line: 0, scope: !12) +!19 = !DILocation(line: 4, scope: !12) +!20 = !DILocation(line: 5, scope: !12) +!21 = !DILocation(line: 6, scope: !12)