Index: llvm/lib/CodeGen/LiveDebugValues/InstrRefBasedImpl.cpp =================================================================== --- llvm/lib/CodeGen/LiveDebugValues/InstrRefBasedImpl.cpp +++ llvm/lib/CodeGen/LiveDebugValues/InstrRefBasedImpl.cpp @@ -182,6 +182,7 @@ #include "llvm/Support/Casting.h" #include "llvm/Support/Compiler.h" #include "llvm/Support/Debug.h" +#include "llvm/Support/TypeSize.h" #include "llvm/Support/raw_ostream.h" #include #include @@ -221,14 +222,16 @@ // an offset. struct SpillLoc { unsigned SpillBase; - int SpillOffset; + StackOffset SpillOffset; bool operator==(const SpillLoc &Other) const { - return std::tie(SpillBase, SpillOffset) == - std::tie(Other.SpillBase, Other.SpillOffset); + return std::make_pair(SpillBase, SpillOffset) == + std::make_pair(Other.SpillBase, Other.SpillOffset); } bool operator<(const SpillLoc &Other) const { - return std::tie(SpillBase, SpillOffset) < - std::tie(Other.SpillBase, Other.SpillOffset); + return std::make_tuple(SpillBase, SpillOffset.getFixed(), + SpillOffset.getScalable()) < + std::make_tuple(Other.SpillBase, Other.SpillOffset.getFixed(), + Other.SpillOffset.getScalable()); } }; @@ -765,8 +768,12 @@ } else if (LocIdxToLocID[*MLoc] >= NumRegs) { unsigned LocID = LocIdxToLocID[*MLoc]; const SpillLoc &Spill = SpillLocs[LocID - NumRegs + 1]; + + auto *TRI = MF.getSubtarget().getRegisterInfo(); + auto Decomp = TRI->decomposeStackOffsetForDwarf(Spill.SpillOffset); Expr = DIExpression::prepend(Expr, DIExpression::ApplyOffset, - Spill.SpillOffset); + Decomp.FixedOffset, Decomp.ScalableOffset, + *Decomp.ScaleReg); unsigned Base = Spill.SpillBase; MIB.addReg(Base, RegState::Debug); MIB.addImm(0); @@ -1482,9 +1489,7 @@ const MachineBasicBlock *MBB = MI.getParent(); Register Reg; StackOffset Offset = TFI->getFrameIndexReference(*MBB->getParent(), FI, Reg); - assert(!Offset.getScalable() && - "Frame offsets with a scalable component are not supported"); - return {Reg, Offset.getFixed()}; + return {Reg, Offset}; } /// End all previous ranges related to @MI and start a new range from @MI Index: llvm/lib/CodeGen/LiveDebugValues/VarLocBasedImpl.cpp =================================================================== --- llvm/lib/CodeGen/LiveDebugValues/VarLocBasedImpl.cpp +++ llvm/lib/CodeGen/LiveDebugValues/VarLocBasedImpl.cpp @@ -145,6 +145,7 @@ #include "llvm/Support/Casting.h" #include "llvm/Support/Compiler.h" #include "llvm/Support/Debug.h" +#include "llvm/Support/TypeSize.h" #include "llvm/Support/raw_ostream.h" #include "llvm/Target/TargetMachine.h" #include @@ -292,7 +293,7 @@ // register and an offset. struct SpillLoc { unsigned SpillBase; - int SpillOffset; + StackOffset SpillOffset; bool operator==(const SpillLoc &Other) const { return SpillBase == Other.SpillBase && SpillOffset == Other.SpillOffset; } @@ -323,21 +324,20 @@ /// The value location. Stored separately to avoid repeatedly /// extracting it from MI. - union { + union LocUnion { uint64_t RegNo; SpillLoc SpillLocation; uint64_t Hash; int64_t Immediate; const ConstantFP *FPImm; const ConstantInt *CImm; + LocUnion() : Hash(0) {} } Loc; VarLoc(const MachineInstr &MI, LexicalScopes &LS) : Var(MI.getDebugVariable(), MI.getDebugExpression(), MI.getDebugLoc()->getInlinedAt()), Expr(MI.getDebugExpression()), MI(MI) { - static_assert((sizeof(Loc) == sizeof(uint64_t)), - "hash does not cover all members of Loc"); assert(MI.isDebugValue() && "not a DBG_VALUE"); assert(MI.getNumOperands() == 4 && "malformed DBG_VALUE"); if (int RegNo = isDbgValueDescribedByReg(MI)) { @@ -413,7 +413,7 @@ /// Take the variable described by DBG_VALUE MI, and create a VarLoc /// locating it in the specified spill location. static VarLoc CreateSpillLoc(const MachineInstr &MI, unsigned SpillBase, - int SpillOffset, LexicalScopes &LS) { + StackOffset SpillOffset, LexicalScopes &LS) { VarLoc VL(MI, LS); assert(VL.Kind == RegisterKind); VL.Kind = SpillLocKind; @@ -450,8 +450,12 @@ // Use the original DBG_VALUEs expression to build the spilt location // on top of. FIXME: spill locations created before this pass runs // are not recognized, and not handled here. + auto *TRI = MF.getSubtarget().getRegisterInfo(); + auto Decomp = TRI->decomposeStackOffsetForDwarf( + Loc.SpillLocation.SpillOffset); auto *SpillExpr = DIExpression::prepend( - DIExpr, DIExpression::ApplyOffset, Loc.SpillLocation.SpillOffset); + DIExpr, DIExpression::ApplyOffset, Decomp.FixedOffset, + Decomp.ScalableOffset, Decomp.ScaleReg); unsigned Base = Loc.SpillLocation.SpillBase; return BuildMI(MF, DbgLoc, IID, true, Base, Var, SpillExpr); } @@ -519,7 +523,9 @@ break; case SpillLocKind: Out << printReg(Loc.SpillLocation.SpillBase, TRI); - Out << "[" << Loc.SpillLocation.SpillOffset << "]"; + Out << "[" << Loc.SpillLocation.SpillOffset.getFixed() << " + " + << Loc.SpillLocation.SpillOffset.getScalable() << "x vscale" + << "]"; break; case ImmediateKind: Out << Loc.Immediate; @@ -542,14 +548,44 @@ #endif bool operator==(const VarLoc &Other) const { - return Kind == Other.Kind && Var == Other.Var && - Loc.Hash == Other.Loc.Hash && Expr == Other.Expr; + if (Kind == Other.Kind && Var == Other.Var && Expr == Other.Expr) { + switch (Kind) { + case SpillLocKind: + return Loc.SpillLocation == Other.Loc.SpillLocation; + case RegisterKind: + case ImmediateKind: + case EntryValueKind: + case EntryValueBackupKind: + case EntryValueCopyBackupKind: + return Loc.Hash == Other.Loc.Hash; + default: + llvm_unreachable("Invalid kind"); + } + } } /// This operator guarantees that VarLocs are sorted by Variable first. bool operator<(const VarLoc &Other) const { - return std::tie(Var, Kind, Loc.Hash, Expr) < - std::tie(Other.Var, Other.Kind, Other.Loc.Hash, Other.Expr); + switch (Kind) { + case SpillLocKind: + return std::make_tuple(Var, Kind, Loc.SpillLocation.SpillBase, + Loc.SpillLocation.SpillOffset.getFixed(), + Loc.SpillLocation.SpillOffset.getScalable(), + Expr) < + std::make_tuple( + Other.Var, Other.Kind, Other.Loc.SpillLocation.SpillBase, + Loc.SpillLocation.SpillOffset.getFixed(), + Loc.SpillLocation.SpillOffset.getScalable(), Other.Expr); + case RegisterKind: + case ImmediateKind: + case EntryValueKind: + case EntryValueBackupKind: + case EntryValueCopyBackupKind: + return std::tie(Var, Kind, Loc.Hash, Expr) < + std::tie(Other.Var, Other.Kind, Other.Loc.Hash, Other.Expr); + default: + llvm_unreachable("Invalid kind"); + } } }; @@ -984,9 +1020,7 @@ const MachineBasicBlock *MBB = MI.getParent(); Register Reg; StackOffset Offset = TFI->getFrameIndexReference(*MBB->getParent(), FI, Reg); - assert(!Offset.getScalable() && - "Frame offsets with a scalable component are not supported"); - return {Reg, Offset.getFixed()}; + return {Reg, Offset}; } /// Try to salvage the debug entry value if we encounter a new debug value Index: llvm/test/CodeGen/AArch64/live-debugvalues-sve.mir =================================================================== --- /dev/null +++ llvm/test/CodeGen/AArch64/live-debugvalues-sve.mir @@ -0,0 +1,165 @@ +# RUN: llc -start-before=prologepilog -stop-after=livedebugvalues -o - %s | FileCheck %s +# RUN: llc -start-before=prologepilog -stop-after=livedebugvalues -experimental-debug-variable-locations -o - %s | FileCheck %s +# +# Test that the LiveDebugValues pass can correctly handle the address +# of the SVE spill (at a scalable address location) which is expressed +# with a complex DIExpression. +# +# CHECK: $z2 = ORR_ZZZ killed $z1, $z1 +# CHECK: $z1 = ORR_ZZZ killed $z0, $z0 +# CHECK-DAG: ST1W_IMM renamable $z1, killed renamable $p{{[0-9]+}}, $fp, -[[#%u,OFFSET:]], debug-location !{{[0-9]+}} :: (store unknown-size into %stack.0, align 16) +# CHECK-DAG: DBG_VALUE $fp, 0, !{{[0-9]+}}, !DIExpression(DW_OP_constu, [[#mul(OFFSET,8)]], DW_OP_bregx, 46, 0, DW_OP_mul, DW_OP_minus), +--- | + ; ModuleID = 't.c' + source_filename = "t.c" + target datalayout = "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128" + target triple = "aarch64-unknown-linux-gnu" + + ; Function Attrs: nounwind + define dso_local @foo( %z0, %z1, i32 %c) local_unnamed_addr #0 !dbg !11 { + entry: + call void @llvm.dbg.value(metadata %z0, metadata !27, metadata !DIExpression()), !dbg !30 + call void @llvm.dbg.value(metadata %z1, metadata !28, metadata !DIExpression()), !dbg !30 + call void @llvm.dbg.value(metadata i32 %c, metadata !29, metadata !DIExpression()), !dbg !30 + %tobool.not = icmp eq i32 %c, 0, !dbg !31 + br i1 %tobool.not, label %if.end, label %if.then, !dbg !33 + + if.then: ; preds = %entry + %call = call @bar( %z0, %z0, %z0, %z0, %z0, %z0, %z0, %z0, %z0) #4, !dbg !34 + br label %return, !dbg !35 + + if.end: ; preds = %entry + %0 = call @llvm.aarch64.sve.tuple.create2.nxv8i32.nxv4i32( %z1, %z0), !dbg !36 + br label %return, !dbg !37 + + return: ; preds = %if.end, %if.then + %retval.0 = phi [ %call, %if.then ], [ %0, %if.end ], !dbg !30 + ret %retval.0, !dbg !38 + } + + declare !dbg !39 dso_local @bar(, , , , , , , , ) local_unnamed_addr #1 + + ; Function Attrs: nounwind readonly + declare @llvm.aarch64.sve.tuple.create2.nxv8i32.nxv4i32(, ) #2 + + ; Function Attrs: nounwind readnone speculatable willreturn + declare void @llvm.dbg.value(metadata, metadata, metadata) #3 + + attributes #0 = { nounwind "disable-tail-calls"="false" "frame-pointer"="non-leaf" "less-precise-fpmad"="false" "min-legal-vector-width"="256" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="generic" "target-features"="+neon,+sve" "unsafe-fp-math"="false" "use-soft-float"="false" } + attributes #1 = { "disable-tail-calls"="false" "frame-pointer"="non-leaf" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="generic" "target-features"="+neon,+sve" "unsafe-fp-math"="false" "use-soft-float"="false" } + attributes #2 = { nounwind readonly } + attributes #3 = { nounwind readnone speculatable willreturn } + attributes #4 = { nounwind } + + !llvm.dbg.cu = !{!0} + !llvm.module.flags = !{!3, !4, !5, !6, !7, !8, !9} + !llvm.ident = !{!10} + + !0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 12.0.0 (https://github.com/llvm/llvm-project.git b19275ba870a06c5ef0428af6264ffd28c7cde9e)", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, splitDebugInlining: false, nameTableKind: None) + !1 = !DIFile(filename: "t.c", directory: "/tmp/") + !2 = !{} + !3 = !{i32 7, !"Dwarf Version", i32 4} + !4 = !{i32 2, !"Debug Info Version", i32 3} + !5 = !{i32 1, !"wchar_size", i32 4} + !6 = !{i32 1, !"branch-target-enforcement", i32 0} + !7 = !{i32 1, !"sign-return-address", i32 0} + !8 = !{i32 1, !"sign-return-address-all", i32 0} + !9 = !{i32 1, !"sign-return-address-with-bkey", i32 0} + !10 = !{!"clang version 12.0.0 (https://github.com/llvm/llvm-project.git b19275ba870a06c5ef0428af6264ffd28c7cde9e)"} + !11 = distinct !DISubprogram(name: "foo", scope: !1, file: !1, line: 6, type: !12, scopeLine: 6, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !26) + !12 = !DISubroutineType(types: !13) + !13 = !{!14, !21, !21, !18} + !14 = !DIDerivedType(tag: DW_TAG_typedef, name: "svint32x2_t", file: !15, line: 59, baseType: !16) + !15 = !DIFile(filename: "lib/clang/12.0.0/include/arm_sve.h", directory: "/tmp") + !16 = !DIDerivedType(tag: DW_TAG_typedef, name: "__clang_svint32x2_t", file: !1, baseType: !17) + !17 = !DICompositeType(tag: DW_TAG_array_type, baseType: !18, flags: DIFlagVector, elements: !19) + !18 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) + !19 = !{!20} + !20 = !DISubrange(lowerBound: 0, upperBound: !DIExpression(DW_OP_constu, 4, DW_OP_bregx, 46, 0, DW_OP_mul, DW_OP_constu, 1, DW_OP_minus)) + !21 = !DIDerivedType(tag: DW_TAG_typedef, name: "svint32_t", file: !15, line: 34, baseType: !22) + !22 = !DIDerivedType(tag: DW_TAG_typedef, name: "__SVInt32_t", file: !1, baseType: !23) + !23 = !DICompositeType(tag: DW_TAG_array_type, baseType: !18, flags: DIFlagVector, elements: !24) + !24 = !{!25} + !25 = !DISubrange(lowerBound: 0, upperBound: !DIExpression(DW_OP_constu, 2, DW_OP_bregx, 46, 0, DW_OP_mul, DW_OP_constu, 1, DW_OP_minus)) + !26 = !{!27, !28, !29} + !27 = !DILocalVariable(name: "z0", arg: 1, scope: !11, file: !1, line: 6, type: !21) + !28 = !DILocalVariable(name: "z1", arg: 2, scope: !11, file: !1, line: 6, type: !21) + !29 = !DILocalVariable(name: "c", arg: 3, scope: !11, file: !1, line: 6, type: !18) + !30 = !DILocation(line: 0, scope: !11) + !31 = !DILocation(line: 7, column: 7, scope: !32) + !32 = distinct !DILexicalBlock(scope: !11, file: !1, line: 7, column: 7) + !33 = !DILocation(line: 7, column: 7, scope: !11) + !34 = !DILocation(line: 8, column: 12, scope: !32) + !35 = !DILocation(line: 8, column: 5, scope: !32) + !36 = !DILocation(line: 9, column: 10, scope: !11) + !37 = !DILocation(line: 9, column: 3, scope: !11) + !38 = !DILocation(line: 10, column: 1, scope: !11) + !39 = !DISubprogram(name: "bar", scope: !1, file: !1, line: 3, type: !40, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized, retainedNodes: !2) + !40 = !DISubroutineType(types: !41) + !41 = !{!17, !23, !23, !23, !23, !23, !23, !23, !23, !23} + +... +--- +name: foo +alignment: 4 +tracksRegLiveness: true +liveins: + - { reg: '$z0' } + - { reg: '$z1' } + - { reg: '$w0' } +frameInfo: + maxAlignment: 16 + adjustsStack: true + hasCalls: true + maxCallFrameSize: 0 + savePoint: '%bb.1' + restorePoint: '%bb.1' +stack: + - { id: 0, size: 16, alignment: 16, stack-id: sve-vec } +machineFunctionInfo: {} +body: | + bb.0.entry: + successors: %bb.2(0x30000000), %bb.1(0x50000000) + liveins: $w0, $z0, $z1 + + DBG_VALUE $z0, $noreg, !27, !DIExpression(), debug-location !30 + DBG_VALUE $z1, $noreg, !28, !DIExpression(), debug-location !30 + DBG_VALUE $w0, $noreg, !29, !DIExpression(), debug-location !30 + renamable $z2 = COPY $z1 + renamable $z1 = COPY $z0 + DBG_VALUE $w0, $noreg, !29, !DIExpression(), debug-location !30 + DBG_VALUE $z2, $noreg, !28, !DIExpression(), debug-location !30 + DBG_VALUE $z1, $noreg, !27, !DIExpression(), debug-location !30 + CBZW killed renamable $w0, %bb.2, debug-location !33 + B %bb.1, debug-location !33 + + bb.1.if.then: + liveins: $z1 + + ADJCALLSTACKDOWN 0, 0, implicit-def dead $sp, implicit $sp, debug-location !34 + renamable $p0 = PTRUE_S 31, debug-location !34 + $x0 = ADDXri %stack.0, 0, 0, debug-location !34 + ST1W_IMM renamable $z1, killed renamable $p0, %stack.0, 0, debug-location !34 :: (store unknown-size into %stack.0, align 16) + $z0 = COPY renamable $z1, debug-location !34 + $z2 = COPY renamable $z1, debug-location !34 + $z3 = COPY renamable $z1, debug-location !34 + $z4 = COPY renamable $z1, debug-location !34 + $z5 = COPY renamable $z1, debug-location !34 + $z6 = COPY renamable $z1, debug-location !34 + $z7 = COPY renamable $z1, debug-location !34 + BL @bar, csr_aarch64_sve_aapcs, implicit-def dead $lr, implicit $sp, implicit $z0, implicit $z1, implicit $z2, implicit $z3, implicit $z4, implicit $z5, implicit $z6, implicit $z7, implicit $x0, implicit-def $sp, implicit-def $z0, implicit-def $z1, debug-location !34 + ADJCALLSTACKUP 0, 0, implicit-def dead $sp, implicit $sp, debug-location !34 + renamable $z2 = COPY $z0, debug-location !34 + B %bb.3, debug-location !35 + + bb.2.if.end: + liveins: $z1, $z2 + + + bb.3.return: + liveins: $z1, $z2 + + $z0 = COPY killed renamable $z2, debug-location !38 + RET_ReallyLR implicit $z0, implicit $z1, debug-location !38 + +...