vsk created this revision. vsk added reviewers: djtodoro, aprantl, dstenb. Herald added subscribers: lldb-commits, hiraditya. Herald added projects: LLDB, LLVM.
A struct argument can be passed-by-value to a callee via a pointer to a temporary stack copy. Add support for emitting an entry value DBG_VALUE when an indirect parameter DBG_VALUE becomes unavailable. This is done by omitting DW_OP_stack_value from the entry value expression, to make the expression describe the location of an object. rdar://63373691 Repository: rG LLVM Github Monorepo https://reviews.llvm.org/D80345 Files: lldb/test/API/functionalities/param_entry_vals/basic_entry_values/main.cpp llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp llvm/lib/CodeGen/AsmPrinter/DwarfExpression.cpp llvm/lib/CodeGen/AsmPrinter/DwarfExpression.h llvm/lib/CodeGen/LiveDebugValues.cpp llvm/test/DebugInfo/MIR/AArch64/dbgcall-site-indirect-param.mir
Index: llvm/test/DebugInfo/MIR/AArch64/dbgcall-site-indirect-param.mir =================================================================== --- /dev/null +++ llvm/test/DebugInfo/MIR/AArch64/dbgcall-site-indirect-param.mir @@ -0,0 +1,143 @@ +# RUN: llc -emit-call-site-info -start-before=livedebugvalues -filetype=obj -o - %s \ +# RUN: | llvm-dwarfdump - | FileCheck %s -implicit-check-not=DW_OP_entry_value + +# // Original Source +# struct fat_ptr { +# int *ptr, *low, *high; +# }; +# extern int baz(int x); +# int bar(struct fat_ptr f) { +# return baz(baz(*f.ptr)); +# } + +# After w0 is clobbered, we should get an indirect parameter entry value for "f". + +# CHECK-LABEL: DW_TAG_formal_parameter +# CHECK-NEXT: DW_AT_location +# CHECK-NEXT: [0x0000000000000000, 0x0000000000000010): DW_OP_breg0 W0+0 +# CHECK-NEXT: [0x0000000000000010, 0x000000000000001c): DW_OP_entry_value(DW_OP_reg0 W0)) +# CHECK-NEXT: DW_AT_name ("f") + +--- | + target datalayout = "e-m:o-i64:64-i128:128-n32:64-S128" + target triple = "arm64-apple-ios10.0.0" + + %struct.fat_ptr = type { i32*, i32*, i32* } + + define i32 @bar(%struct.fat_ptr* nocapture readonly %f) local_unnamed_addr !dbg !13 { + entry: + call void @llvm.dbg.declare(metadata %struct.fat_ptr* %f, metadata !23, metadata !DIExpression()), !dbg !24 + %ptr2 = bitcast %struct.fat_ptr* %f to i32**, !dbg !25 + %0 = load i32*, i32** %ptr2, align 8, !dbg !25 + %1 = load i32, i32* %0, align 4, !dbg !31 + %call = tail call i32 @baz(i32 %1), !dbg !34 + %call1 = tail call i32 @baz(i32 %call), !dbg !35 + ret i32 %call1, !dbg !36 + } + + declare void @llvm.dbg.declare(metadata, metadata, metadata) + + declare !dbg !4 i32 @baz(i32) local_unnamed_addr optsize + + !llvm.dbg.cu = !{!0} + !llvm.module.flags = !{!8, !9, !10, !11} + !llvm.ident = !{!12} + + !0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, retainedTypes: !3, nameTableKind: None, sysroot: "/") + !1 = !DIFile(filename: "indirect.c", directory: "/tmp/fatptr") + !2 = !{} + !3 = !{!4} + !4 = !DISubprogram(name: "baz", scope: !1, file: !1, line: 4, type: !5, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized, retainedNodes: !2) + !5 = !DISubroutineType(types: !6) + !6 = !{!7, !7} + !7 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) + !8 = !{i32 7, !"Dwarf Version", i32 4} + !9 = !{i32 2, !"Debug Info Version", i32 3} + !10 = !{i32 1, !"wchar_size", i32 4} + !11 = !{i32 7, !"PIC Level", i32 2} + !12 = !{!"clang"} + !13 = distinct !DISubprogram(name: "bar", scope: !1, file: !1, line: 5, type: !14, scopeLine: 5, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !22) + !14 = !DISubroutineType(types: !15) + !15 = !{!7, !16} + !16 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "fat_ptr", file: !1, line: 1, size: 192, elements: !17) + !17 = !{!18, !20, !21} + !18 = !DIDerivedType(tag: DW_TAG_member, name: "ptr", scope: !16, file: !1, line: 2, baseType: !19, size: 64) + !19 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) + !20 = !DIDerivedType(tag: DW_TAG_member, name: "low", scope: !16, file: !1, line: 2, baseType: !19, size: 64, offset: 64) + !21 = !DIDerivedType(tag: DW_TAG_member, name: "high", scope: !16, file: !1, line: 2, baseType: !19, size: 64, offset: 128) + !22 = !{!23} + !23 = !DILocalVariable(name: "f", arg: 1, scope: !13, file: !1, line: 5, type: !16) + !24 = !DILocation(line: 5, column: 24, scope: !13) + !25 = !DILocation(line: 6, column: 23, scope: !13) + !31 = !DILocation(line: 6, column: 20, scope: !13) + !34 = !DILocation(line: 6, column: 16, scope: !13) + !35 = !DILocation(line: 6, column: 12, scope: !13) + !36 = !DILocation(line: 6, column: 5, scope: !13) + +... +--- +name: bar +alignment: 4 +exposesReturnsTwice: false +legalized: false +regBankSelected: false +selected: false +failedISel: false +tracksRegLiveness: true +hasWinCFI: false +registers: [] +liveins: + - { reg: '$x0', virtual-reg: '' } +frameInfo: + isFrameAddressTaken: false + isReturnAddressTaken: false + hasStackMap: false + hasPatchPoint: false + stackSize: 16 + offsetAdjustment: 0 + maxAlignment: 8 + adjustsStack: true + hasCalls: true + stackProtector: '' + maxCallFrameSize: 0 + cvBytesOfCalleeSavedRegisters: 0 + hasOpaqueSPAdjustment: false + hasVAStart: false + hasMustTailInVarArgFunc: false + localFrameSize: 0 + savePoint: '' + restorePoint: '' +fixedStack: [] +stack: + - { id: 0, name: '', type: spill-slot, offset: -8, size: 8, alignment: 8, + stack-id: default, callee-saved-register: '$lr', callee-saved-restored: true, + debug-info-variable: '', debug-info-expression: '', debug-info-location: '' } + - { id: 1, name: '', type: spill-slot, offset: -16, size: 8, alignment: 8, + stack-id: default, callee-saved-register: '$fp', callee-saved-restored: true, + debug-info-variable: '', debug-info-expression: '', debug-info-location: '' } +callSites: + - { bb: 0, offset: 9, fwdArgRegs: + - { arg: 0, reg: '$w0' } } + - { bb: 0, offset: 11, fwdArgRegs: + - { arg: 0, reg: '$w0' } } +constants: [] +machineFunctionInfo: + hasRedZone: false +body: | + bb.0.entry: + liveins: $x0, $lr + + DBG_VALUE $x0, 0, !23, !DIExpression(), debug-location !24 + DBG_VALUE $x0, 0, !23, !DIExpression(), debug-location !24 + early-clobber $sp = frame-setup STPXpre killed $fp, killed $lr, $sp, -2 :: (store 8 into %stack.1), (store 8 into %stack.0) + $fp = frame-setup ADDXri $sp, 0, 0 + frame-setup CFI_INSTRUCTION def_cfa $w29, 16 + frame-setup CFI_INSTRUCTION offset $w30, -8, debug-location !25 + frame-setup CFI_INSTRUCTION offset $w29, -16, debug-location !25 + renamable $x8 = LDRXui killed renamable $x0, 0, debug-location !25 :: (load 8 from %ir.ptr2) + renamable $w0 = LDRWui killed renamable $x8, 0, debug-location !31 :: (load 4 from %ir.0) + BL @baz, csr_aarch64_aapcs, implicit-def dead $lr, implicit $sp, implicit $w0, implicit-def $sp, implicit-def $w0, debug-location !34 + early-clobber $sp, $fp, $lr = frame-destroy LDPXpost $sp, 2, debug-location !35 :: (load 8 from %stack.1), (load 8 from %stack.0) + TCRETURNdi @baz, 0, csr_aarch64_aapcs, implicit $sp, implicit $w0, debug-location !35 + +... Index: llvm/lib/CodeGen/LiveDebugValues.cpp =================================================================== --- llvm/lib/CodeGen/LiveDebugValues.cpp +++ llvm/lib/CodeGen/LiveDebugValues.cpp @@ -1612,10 +1612,6 @@ if (MI.getDebugLoc()->getInlinedAt()) return false; - // Do not consider indirect debug values (TODO: explain why). - if (MI.isIndirectDebugValue()) - return false; - // Only consider parameters that are described using registers. Parameters // that are passed on the stack are not yet supported, so ignore debug // values that are described by the frame or stack pointer. @@ -1630,7 +1626,7 @@ return false; // TODO: Add support for parameters that have a pre-existing debug expressions - // (e.g. fragments, or indirect parameters using DW_OP_deref). + // (e.g. fragments). if (MI.getDebugExpression()->getNumElements() > 0) return false; Index: llvm/lib/CodeGen/AsmPrinter/DwarfExpression.h =================================================================== --- llvm/lib/CodeGen/AsmPrinter/DwarfExpression.h +++ llvm/lib/CodeGen/AsmPrinter/DwarfExpression.h @@ -19,6 +19,7 @@ #include "llvm/ADT/Optional.h" #include "llvm/ADT/SmallVector.h" #include "llvm/IR/DebugInfoMetadata.h" +#include "llvm/MC/MachineLocation.h" #include <cassert> #include <cstdint> #include <iterator> @@ -143,10 +144,14 @@ enum { Unknown = 0, Register, Memory, Implicit }; /// The flags of location description being produced. - enum { EntryValue = 1, CallSiteParamValue }; + enum { + EntryValue = 1 << 0, + IndirectEntryValue = 1 << 1, + CallSiteParamValue = 1 << 2 + }; unsigned LocationKind : 3; - unsigned LocationFlags : 2; + unsigned LocationFlags : 3; unsigned DwarfVersion : 4; public: @@ -160,6 +165,10 @@ bool isEntryValue() const { return LocationFlags & EntryValue; } + bool isIndirectEntryValue() const { + return LocationFlags & IndirectEntryValue; + } + bool isParameterValue() { return LocationFlags & CallSiteParamValue; } Optional<uint8_t> TagOffset; @@ -296,7 +305,9 @@ } /// Lock this down to become an entry value location. - void setEntryValueFlag() { LocationFlags |= EntryValue; } + void setEntryValueFlagFromLoc(MachineLocation Loc) { + LocationFlags |= Loc.isIndirect() ? IndirectEntryValue : EntryValue; + } /// Lock this down to become a call site parameter location. void setCallSiteParamValueFlag() { LocationFlags |= CallSiteParamValue; } Index: llvm/lib/CodeGen/AsmPrinter/DwarfExpression.cpp =================================================================== --- llvm/lib/CodeGen/AsmPrinter/DwarfExpression.cpp +++ llvm/lib/CodeGen/AsmPrinter/DwarfExpression.cpp @@ -249,14 +249,14 @@ // location, emit it with addBReg and offset 0, because we should emit a DWARF // expression representing a value, rather than a location. if (!isMemoryLocation() && !HasComplexExpression && - (!isParameterValue() || isEntryValue())) { + (!isParameterValue() || isEntryValue() || isIndirectEntryValue())) { for (auto &Reg : DwarfRegs) { if (Reg.DwarfRegNo >= 0) addReg(Reg.DwarfRegNo, Reg.Comment); addOpPiece(Reg.SubRegSize); } - if (isEntryValue()) + if (isEntryValue() || isIndirectEntryValue()) finalizeEntryValue(); if (isEntryValue() && !isParameterValue() && DwarfVersion >= 4) Index: llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp =================================================================== --- llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp +++ llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp @@ -786,6 +786,8 @@ // Emit the call site parameter's value as an entry value. if (ShouldTryEmitEntryVals) { // Create an expression where the register's entry value is used. + // FIXME: This produces unusable descriptions when the register contains + // a pointer to a temporary copy of a struct passed by value. DIExpression *EntryExpr = DIExpression::get( MF->getFunction().getContext(), {dwarf::DW_OP_LLVM_entry_value, 1}); for (auto RegEntry : ForwardedRegWorklist) { @@ -2399,12 +2401,12 @@ DwarfExpr.addUnsignedConstant(Value.getInt()); } else if (Value.isLocation()) { MachineLocation Location = Value.getLoc(); - if (Location.isIndirect()) + if (Location.isIndirect() && !DIExpr->isEntryValue()) DwarfExpr.setMemoryLocationKind(); DIExpressionCursor Cursor(DIExpr); if (DIExpr->isEntryValue()) { - DwarfExpr.setEntryValueFlag(); + DwarfExpr.setEntryValueFlagFromLoc(Location); DwarfExpr.beginEntryValueExpression(Cursor); } Index: llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp =================================================================== --- llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp +++ llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp @@ -1285,13 +1285,13 @@ DIEDwarfExpression DwarfExpr(*Asm, *this, *Loc); const DIExpression *DIExpr = DV.getSingleExpression(); DwarfExpr.addFragmentOffset(DIExpr); - if (Location.isIndirect()) + if (Location.isIndirect() && !DIExpr->isEntryValue()) DwarfExpr.setMemoryLocationKind(); DIExpressionCursor Cursor(DIExpr); if (DIExpr->isEntryValue()) { - DwarfExpr.setEntryValueFlag(); + DwarfExpr.setEntryValueFlagFromLoc(Location); DwarfExpr.beginEntryValueExpression(Cursor); } Index: lldb/test/API/functionalities/param_entry_vals/basic_entry_values/main.cpp =================================================================== --- lldb/test/API/functionalities/param_entry_vals/basic_entry_values/main.cpp +++ lldb/test/API/functionalities/param_entry_vals/basic_entry_values/main.cpp @@ -137,6 +137,30 @@ target_no_tailcall(sink, 123); } +/// A structure that is guaranteed -- when passed to a callee by value -- to be +/// passed via a pointer to a temporary copy in the caller. On x86_64 & aarch64 +/// only. +struct StructPassedViaPointerToTemporaryCopy { + // Under the 64-bit AAPCS, a struct larger than 16 bytes is not SROA'd, and + // is instead passed via pointer to a temporary copy. + long a, b, c; + StructPassedViaPointerToTemporaryCopy() : a(1), b(2), c(3) {} + + // Failing that, a virtual method forces passing via pointer to a temporary + // copy under the common calling conventions (e.g. 32/64-bit x86, Linux/Win, + // according to https://www.agner.org/optimize/calling_conventions.pdf). + virtual void add_vtable() {} +}; + +__attribute__((noinline)) void func15(StructPassedViaPointerToTemporaryCopy S) { + use<StructPassedViaPointerToTemporaryCopy &>(S); + use<int &>(dummy); + + ++global; + //% self.filecheck("expr S", "main.cpp", "-check-prefix=FUNC15-EXPR") + // FUNC15-EXPR: (a = 1, b = 2, c = 3) +} + __attribute__((disable_tail_calls)) int main() { int sink = 0; S1 s1; @@ -169,5 +193,9 @@ // Test that evaluation can "see through" an indirect tail call. func14(sink, func13); + // Test evaluation of an entry value that dereferences a temporary stack + // slot set up by the caller for a StructPassedViaPointerToTemporaryCopy. + func15(StructPassedViaPointerToTemporaryCopy()); + return 0; }
_______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits