From a7e3339f3b0eb71e43d44e6f59cc8db6a7b110bf Mon Sep 17 00:00:00 2001 From: Sander de Smalen Date: Wed, 6 Jan 2021 09:37:57 +0000 Subject: [PATCH] [AArch64][SVE] Emit DWARF location expression for SVE stack objects. Extend PEI to emit a DWARF expression for StackOffsets that have a fixed and scalable component. This means the expression that needs to be added is either: + offset or: + offset + scalable_offset * scalereg where for SVE, the scale reg is the Vector Granule Dwarf register, which encodes the number of 64bit 'granules' in an SVE vector and which the debugger can evaluate at runtime. Reviewed By: jmorse Differential Revision: https://reviews.llvm.org/D90020 --- llvm/include/llvm/CodeGen/TargetRegisterInfo.h | 10 ++ llvm/lib/CodeGen/PrologEpilogInserter.cpp | 5 +- llvm/lib/CodeGen/TargetRegisterInfo.cpp | 26 +++++ llvm/lib/Target/AArch64/AArch64RegisterInfo.cpp | 30 ++++- llvm/lib/Target/AArch64/AArch64RegisterInfo.h | 3 + .../CodeGen/AArch64/debug-info-sve-dbg-value.mir | 126 +++++++++++++++++++++ 6 files changed, 196 insertions(+), 4 deletions(-) create mode 100644 llvm/test/CodeGen/AArch64/debug-info-sve-dbg-value.mir diff --git a/llvm/include/llvm/CodeGen/TargetRegisterInfo.h b/llvm/include/llvm/CodeGen/TargetRegisterInfo.h index de2c1b0..6f32729 100644 --- a/llvm/include/llvm/CodeGen/TargetRegisterInfo.h +++ b/llvm/include/llvm/CodeGen/TargetRegisterInfo.h @@ -34,6 +34,7 @@ namespace llvm { class BitVector; +class DIExpression; class LiveRegMatrix; class MachineFunction; class MachineInstr; @@ -923,6 +924,15 @@ public: llvm_unreachable("isFrameOffsetLegal does not exist on this target"); } + /// Gets the DWARF expression opcodes for \p Offset. + virtual void getOffsetOpcodes(const StackOffset &Offset, + SmallVectorImpl &Ops) const; + + /// Prepends a DWARF expression for \p Offset to DIExpression \p Expr. + DIExpression * + prependOffsetExpression(const DIExpression *Expr, unsigned PrependFlags, + const StackOffset &Offset) const; + /// Spill the register so it can be used by the register scavenger. /// Return true if the register was spilled, false otherwise. /// If this function does not spill the register, the scavenger diff --git a/llvm/lib/CodeGen/PrologEpilogInserter.cpp b/llvm/lib/CodeGen/PrologEpilogInserter.cpp index 7c38b19..65b2165 100644 --- a/llvm/lib/CodeGen/PrologEpilogInserter.cpp +++ b/llvm/lib/CodeGen/PrologEpilogInserter.cpp @@ -1211,8 +1211,6 @@ void PEI::replaceFrameIndices(MachineBasicBlock *BB, MachineFunction &MF, StackOffset Offset = TFI->getFrameIndexReference(MF, FrameIdx, Reg); - assert(!Offset.getScalable() && - "Frame offsets with a scalable component are not supported"); MI.getOperand(0).ChangeToRegister(Reg, false /*isDef*/); MI.getOperand(0).setIsDebug(); @@ -1238,7 +1236,8 @@ void PEI::replaceFrameIndices(MachineBasicBlock *BB, MachineFunction &MF, // Make the DBG_VALUE direct. MI.getDebugOffset().ChangeToRegister(0, false); } - DIExpr = DIExpression::prepend(DIExpr, PrependFlags, Offset.getFixed()); + + DIExpr = TRI.prependOffsetExpression(DIExpr, PrependFlags, Offset); MI.getDebugExpressionOp().setMetadata(DIExpr); continue; } diff --git a/llvm/lib/CodeGen/TargetRegisterInfo.cpp b/llvm/lib/CodeGen/TargetRegisterInfo.cpp index e89353c..4a190c9 100644 --- a/llvm/lib/CodeGen/TargetRegisterInfo.cpp +++ b/llvm/lib/CodeGen/TargetRegisterInfo.cpp @@ -26,6 +26,7 @@ #include "llvm/CodeGen/VirtRegMap.h" #include "llvm/Config/llvm-config.h" #include "llvm/IR/Attributes.h" +#include "llvm/IR/DebugInfoMetadata.h" #include "llvm/IR/Function.h" #include "llvm/MC/MCRegisterInfo.h" #include "llvm/Support/CommandLine.h" @@ -532,6 +533,31 @@ TargetRegisterInfo::lookThruCopyLike(Register SrcReg, } } +void TargetRegisterInfo::getOffsetOpcodes( + const StackOffset &Offset, SmallVectorImpl &Ops) const { + assert(!Offset.getScalable() && "Scalable offsets are not handled"); + DIExpression::appendOffset(Ops, Offset.getFixed()); +} + +DIExpression * +TargetRegisterInfo::prependOffsetExpression(const DIExpression *Expr, + unsigned PrependFlags, + const StackOffset &Offset) const { + assert((PrependFlags & + ~(DIExpression::DerefBefore | DIExpression::DerefAfter | + DIExpression::StackValue | DIExpression::EntryValue)) == 0 && + "Unsupported prepend flag"); + SmallVector OffsetExpr; + if (PrependFlags & DIExpression::DerefBefore) + OffsetExpr.push_back(dwarf::DW_OP_deref); + getOffsetOpcodes(Offset, OffsetExpr); + if (PrependFlags & DIExpression::DerefAfter) + OffsetExpr.push_back(dwarf::DW_OP_deref); + return DIExpression::prependOpcodes(Expr, OffsetExpr, + PrependFlags & DIExpression::StackValue, + PrependFlags & DIExpression::EntryValue); +} + #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) LLVM_DUMP_METHOD void TargetRegisterInfo::dumpReg(Register Reg, unsigned SubRegIndex, diff --git a/llvm/lib/Target/AArch64/AArch64RegisterInfo.cpp b/llvm/lib/Target/AArch64/AArch64RegisterInfo.cpp index 954b0960..231e8b3 100644 --- a/llvm/lib/Target/AArch64/AArch64RegisterInfo.cpp +++ b/llvm/lib/Target/AArch64/AArch64RegisterInfo.cpp @@ -24,6 +24,7 @@ #include "llvm/CodeGen/MachineRegisterInfo.h" #include "llvm/CodeGen/RegisterScavenging.h" #include "llvm/CodeGen/TargetFrameLowering.h" +#include "llvm/IR/DebugInfoMetadata.h" #include "llvm/IR/DiagnosticInfo.h" #include "llvm/IR/Function.h" #include "llvm/Support/raw_ostream.h" @@ -592,6 +593,33 @@ createScratchRegisterForInstruction(MachineInstr &MI, } } +void AArch64RegisterInfo::getOffsetOpcodes( + const StackOffset &Offset, SmallVectorImpl &Ops) const { + // The smallest scalable element supported by scaled SVE addressing + // modes are predicates, which are 2 scalable bytes in size. So the scalable + // byte offset must always be a multiple of 2. + assert(Offset.getScalable() % 2 == 0 && "Invalid frame offset"); + + // Add fixed-sized offset using existing DIExpression interface. + DIExpression::appendOffset(Ops, Offset.getFixed()); + + unsigned VG = getDwarfRegNum(AArch64::VG, true); + int64_t VGSized = Offset.getScalable() / 2; + if (VGSized > 0) { + Ops.push_back(dwarf::DW_OP_constu); + Ops.push_back(VGSized); + Ops.append({dwarf::DW_OP_bregx, VG, 0ULL}); + Ops.push_back(dwarf::DW_OP_mul); + Ops.push_back(dwarf::DW_OP_plus); + } else if (VGSized < 0) { + Ops.push_back(dwarf::DW_OP_constu); + Ops.push_back(-VGSized); + Ops.append({dwarf::DW_OP_bregx, VG, 0ULL}); + Ops.push_back(dwarf::DW_OP_mul); + Ops.push_back(dwarf::DW_OP_minus); + } +} + void AArch64RegisterInfo::eliminateFrameIndex(MachineBasicBlock::iterator II, int SPAdj, unsigned FIOperandNum, RegScavenger *RS) const { @@ -610,7 +638,7 @@ void AArch64RegisterInfo::eliminateFrameIndex(MachineBasicBlock::iterator II, Register FrameReg; // Special handling of dbg_value, stackmap patchpoint statepoint instructions. - if (MI.isDebugValue() || MI.getOpcode() == TargetOpcode::STACKMAP || + if (MI.getOpcode() == TargetOpcode::STACKMAP || MI.getOpcode() == TargetOpcode::PATCHPOINT || MI.getOpcode() == TargetOpcode::STATEPOINT) { StackOffset Offset = diff --git a/llvm/lib/Target/AArch64/AArch64RegisterInfo.h b/llvm/lib/Target/AArch64/AArch64RegisterInfo.h index d7580d7..1c74d39 100644 --- a/llvm/lib/Target/AArch64/AArch64RegisterInfo.h +++ b/llvm/lib/Target/AArch64/AArch64RegisterInfo.h @@ -135,6 +135,9 @@ public: unsigned SubReg, const TargetRegisterClass *DstRC, unsigned DstSubReg, const TargetRegisterClass *NewRC, LiveIntervals &LIS) const override; + + void getOffsetOpcodes(const StackOffset &Offset, + SmallVectorImpl &Ops) const override; }; } // end namespace llvm diff --git a/llvm/test/CodeGen/AArch64/debug-info-sve-dbg-value.mir b/llvm/test/CodeGen/AArch64/debug-info-sve-dbg-value.mir new file mode 100644 index 0000000..ffce40c --- /dev/null +++ b/llvm/test/CodeGen/AArch64/debug-info-sve-dbg-value.mir @@ -0,0 +1,126 @@ +# RUN: llc -o %t -filetype=obj -start-before=prologepilog %s +# RUN: llvm-dwarfdump --name="value0" %t | FileCheck %s --check-prefix=CHECK0 +# RUN: llvm-dwarfdump --name="value1" %t | FileCheck %s --check-prefix=CHECK1 +# RUN: llvm-dwarfdump --name="value2" %t | FileCheck %s --check-prefix=CHECK2 +# RUN: llvm-dwarfdump --name="value3" %t | FileCheck %s --check-prefix=CHECK3 +# RUN: llvm-dwarfdump --name="value4" %t | FileCheck %s --check-prefix=CHECK4 +# RUN: llvm-dwarfdump --name="value5" %t | FileCheck %s --check-prefix=CHECK5 + +# CHECK0: : DW_OP_breg31 WSP+8, DW_OP_lit16, DW_OP_plus) +# CHECK0: DW_AT_type {{.*}}ty32 +# +# CHECK1: : DW_OP_breg31 WSP+16) +# CHECK1: DW_AT_type {{.*}}ty32 +# +# CHECK2: : DW_OP_breg31 WSP+16, DW_OP_lit16, DW_OP_bregx VG+0, DW_OP_mul, DW_OP_plus) +# CHECK2: DW_AT_type {{.*}}svint32_t +# +# CHECK3: : DW_OP_breg31 WSP+16, DW_OP_lit8, DW_OP_bregx VG+0, DW_OP_mul, DW_OP_plus) +# CHECK3: DW_AT_type {{.*}}svint32_t +# +# CHECK4: : DW_OP_breg31 WSP+16, DW_OP_lit7, DW_OP_bregx VG+0, DW_OP_mul, DW_OP_plus) +# CHECK4: DW_AT_type {{.*}}svbool_t +# +# CHECK5: : DW_OP_breg31 WSP+16, DW_OP_lit6, DW_OP_bregx VG+0, DW_OP_mul, DW_OP_plus) +# CHECK5: DW_AT_type {{.*}}svbool_t + +--- | + ; ModuleID = 'bla.mir' + source_filename = "bla.mir" + target datalayout = "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128" + + define void @foo() #0 !dbg !5 { + entry: + unreachable, !dbg !8 + } + + ; Function Attrs: nounwind readnone speculatable willreturn + declare void @llvm.dbg.value(metadata, metadata, metadata) + + attributes #0 = { "target-features"="+sve" } + + !llvm.dbg.cu = !{!0} + !llvm.debugify = !{!3, !3} + !llvm.module.flags = !{!4} + + !0 = distinct !DICompileUnit(language: DW_LANG_C, file: !1, producer: "debugify", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2) + !1 = !DIFile(filename: "bla.mir", directory: "/") + !2 = !{} + !3 = !{i32 1} + !4 = !{i32 2, !"Debug Info Version", i32 3} + !5 = distinct !DISubprogram(name: "foo", linkageName: "foo", scope: null, file: !1, line: 1, type: !6, scopeLine: 1, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !9) + !6 = !DISubroutineType(types: !2) + !7 = !DIBasicType(name: "ty32", size: 32, encoding: DW_ATE_signed) + !8 = !DILocation(line: 1, column: 1, scope: !5) + !9 = !{!10, !11, !12, !13, !14, !15} + !10 = !DILocalVariable(name: "value0", scope: !5, file: !1, line: 1, type: !7) + !11 = !DILocalVariable(name: "value1", scope: !5, file: !1, line: 1, type: !7) + !12 = !DILocalVariable(name: "value2", scope: !5, file: !1, line: 1, type: !16) + !13 = !DILocalVariable(name: "value3", scope: !5, file: !1, line: 1, type: !16) + !14 = !DILocalVariable(name: "value4", scope: !5, file: !1, line: 1, type: !21) + !15 = !DILocalVariable(name: "value5", scope: !5, file: !1, line: 1, type: !21) + !16 = !DIDerivedType(tag: DW_TAG_typedef, name: "svint32_t", file: !1, line: 1, baseType: !17) + !17 = !DIDerivedType(tag: DW_TAG_typedef, name: "__SVInt32_t", file: !1, baseType: !18) + !18 = !DICompositeType(tag: DW_TAG_array_type, baseType: !7, flags: DIFlagVector, elements: !19) + !19 = !{!20} + !20 = !DISubrange(lowerBound: 0, upperBound: !DIExpression(DW_OP_constu, 2, DW_OP_bregx, 46, 0, DW_OP_mul, DW_OP_constu, 1, DW_OP_minus)) + !21 = !DIDerivedType(tag: DW_TAG_typedef, name: "svbool_t", file: !1, line: 90, baseType: !22) + !22 = !DIDerivedType(tag: DW_TAG_typedef, name: "__SVBool_t", file: !1, baseType: !23) + !23 = !DICompositeType(tag: DW_TAG_array_type, baseType: !24, flags: DIFlagVector, elements: !25) + !24 = !DIBasicType(name: "unsigned char", size: 8, encoding: DW_ATE_unsigned_char) + !25 = !{!26} + !26 = !DISubrange(lowerBound: 0, upperBound: !DIExpression(DW_OP_constu, 1, DW_OP_bregx, 46, 0, DW_OP_mul, DW_OP_constu, 1, DW_OP_minus)) + + +... +--- +name: foo +alignment: 4 +tracksRegLiveness: true +liveins: + - { reg: '$z0' } + - { reg: '$z1' } + - { reg: '$p0' } + - { reg: '$p1' } + - { reg: '$x0' } + - { reg: '$x1' } +frameInfo: + maxAlignment: 16 + adjustsStack: true + hasCalls: true + maxCallFrameSize: 0 + localFrameSize: 4 +stack: + - { id: 0, size: 8, alignment: 8 } + - { id: 1, size: 8, alignment: 8 } + - { id: 2, size: 16, alignment: 16, stack-id: sve-vec } + - { id: 3, size: 16, alignment: 16, stack-id: sve-vec } + - { id: 4, size: 2, alignment: 2, stack-id: sve-vec } + - { id: 5, size: 2, alignment: 2, stack-id: sve-vec } +machineFunctionInfo: {} +body: | + bb.0.entry: + liveins: $p0, $p1, $w0, $x1, $z0, $z1 + + ; Avoid stack slot scavenging. + $lr = IMPLICIT_DEF + + STRXui killed renamable $x0, %stack.0, 0, debug-location !8 + DBG_VALUE %stack.0, $noreg, !10, !DIExpression(DW_OP_constu, 16, DW_OP_plus, DW_OP_deref), debug-location !8 + STRXui killed renamable $x1, %stack.1, 0, debug-location !8 + DBG_VALUE %stack.1, $noreg, !11, !DIExpression(DW_OP_constu, 16, DW_OP_plus, DW_OP_deref), debug-location !8 + + renamable $p2 = PTRUE_S 31, debug-location !DILocation(line: 4, column: 1, scope: !5) + ST1W_IMM renamable $z0, renamable $p2, %stack.2, 0, debug-location !DILocation(line: 5, column: 1, scope: !5) + DBG_VALUE %stack.2, $noreg, !12, !DIExpression(DW_OP_deref), debug-location !DILocation(line: 5, column: 1, scope: !5) + ST1W_IMM renamable $z1, killed renamable $p2, %stack.3, 0, debug-location !DILocation(line: 6, column: 1, scope: !5) + DBG_VALUE %stack.3, $noreg, !13, !DIExpression(DW_OP_deref), debug-location !DILocation(line: 6, column: 1, scope: !5) + + STR_PXI killed renamable $p0, %stack.4, 0, debug-location !DILocation(line: 2, column: 1, scope: !5) + DBG_VALUE %stack.4, $noreg, !14, !DIExpression(DW_OP_deref), debug-location !DILocation(line: 2, column: 1, scope: !5) + STR_PXI killed renamable $p1, %stack.5, 0, debug-location !DILocation(line: 3, column: 1, scope: !5) + DBG_VALUE %stack.5, $noreg, !15, !DIExpression(DW_OP_deref), debug-location !DILocation(line: 3, column: 1, scope: !5) + + RET_ReallyLR implicit $z0, implicit $z1, debug-location !DILocation(line: 7, column: 1, scope: !5) + +... -- 2.7.4