Skip to content

Commit 023b507

Browse files
authored
[HeterogeneousDwarf] Support DBG_INSTR_REF salvaging of REG_SEQUENCE (llvm#1886)
Just like COPY, REG_SEQUENCE isn't a good instruction to attach a debug-instr-number to. Dig back through the registers it's composing to find a set of defining instructions, and combine them with a DIOpComposite.
1 parent 59eda75 commit 023b507

File tree

6 files changed

+228
-15
lines changed

6 files changed

+228
-15
lines changed

llvm/include/llvm/CodeGen/MachineFunction.h

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -614,6 +614,8 @@ class LLVM_ABI MachineFunction {
614614
void substituteDebugValuesForInst(const MachineInstr &Old, MachineInstr &New,
615615
unsigned MaxOperand = UINT_MAX);
616616

617+
using SalvageCopySSAResult = std::pair<DebugInstrOperandPair, MachineInstr *>;
618+
617619
/// Find the underlying defining instruction / operand for a COPY instruction
618620
/// while in SSA form. Copies do not actually define values -- they move them
619621
/// between registers. Labelling a COPY-like instruction with an instruction
@@ -625,11 +627,11 @@ class LLVM_ABI MachineFunction {
625627
/// \p MI The copy-like instruction to salvage.
626628
/// \p DbgPHICache A container to cache already-solved COPYs.
627629
/// \returns An instruction/operand pair identifying the defining value.
628-
DebugInstrOperandPair
630+
SalvageCopySSAResult
629631
salvageCopySSA(MachineInstr &MI,
630-
DenseMap<Register, DebugInstrOperandPair> &DbgPHICache);
632+
DenseMap<Register, SalvageCopySSAResult> &DbgPHICache);
631633

632-
DebugInstrOperandPair salvageCopySSAImpl(MachineInstr &MI);
634+
SalvageCopySSAResult salvageCopySSAImpl(MachineInstr &MI);
633635

634636
/// Finalise any partially emitted debug instructions. These are DBG_INSTR_REF
635637
/// instructions where we only knew the vreg of the value they use, not the

llvm/lib/CodeGen/AsmPrinter/DwarfExpression.cpp

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1188,7 +1188,27 @@ std::optional<NewOpResult> DwarfExpression::traverse(DIOp::Select Select,
11881188

11891189
std::optional<NewOpResult> DwarfExpression::traverse(DIOp::Composite Composite,
11901190
ChildrenT Children) {
1191-
return std::nullopt;
1191+
if (IsFragment)
1192+
emitOp(dwarf::DW_OP_lit0);
1193+
1194+
for (auto &Child : Children) {
1195+
auto R = traverse(Child.get(), std::nullopt);
1196+
if (!R)
1197+
return std::nullopt;
1198+
TypeSize Size = R->Ty->getPrimitiveSizeInBits();
1199+
if (!Size.isFixed() || Size.getFixedValue() % 8 != 0)
1200+
return std::nullopt;
1201+
emitOp(dwarf::DW_OP_piece);
1202+
emitUnsigned(Size.getFixedValue() / 8);
1203+
}
1204+
emitUserOp(dwarf::DW_OP_LLVM_piece_end);
1205+
1206+
if (IsFragment) {
1207+
emitOp(dwarf::DW_OP_swap);
1208+
emitOp(dwarf::DW_OP_drop);
1209+
}
1210+
1211+
return NewOpResult{Composite.getResultType(), ValueKind::LocationDesc};
11921212
}
11931213

11941214
std::optional<NewOpResult> DwarfExpression::traverseMathOp(uint8_t DwarfOp,

llvm/lib/CodeGen/MachineFunction.cpp

Lines changed: 143 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
#include "llvm/IR/BasicBlock.h"
4646
#include "llvm/IR/Constant.h"
4747
#include "llvm/IR/DataLayout.h"
48+
#include "llvm/IR/DebugInfoMetadata.h"
4849
#include "llvm/IR/DerivedTypes.h"
4950
#include "llvm/IR/EHPersonalities.h"
5051
#include "llvm/IR/Function.h"
@@ -1052,8 +1053,8 @@ void MachineFunction::substituteDebugValuesForInst(const MachineInstr &Old,
10521053
}
10531054

10541055
auto MachineFunction::salvageCopySSA(
1055-
MachineInstr &MI, DenseMap<Register, DebugInstrOperandPair> &DbgPHICache)
1056-
-> DebugInstrOperandPair {
1056+
MachineInstr &MI, DenseMap<Register, SalvageCopySSAResult> &DbgPHICache)
1057+
-> SalvageCopySSAResult {
10571058
const TargetInstrInfo &TII = *getSubtarget().getInstrInfo();
10581059

10591060
// Check whether this copy-like instruction has already been salvaged into
@@ -1077,7 +1078,7 @@ auto MachineFunction::salvageCopySSA(
10771078
}
10781079

10791080
auto MachineFunction::salvageCopySSAImpl(MachineInstr &MI)
1080-
-> DebugInstrOperandPair {
1081+
-> SalvageCopySSAResult {
10811082
MachineRegisterInfo &MRI = getRegInfo();
10821083
const TargetRegisterInfo &TRI = *MRI.getTargetRegisterInfo();
10831084
const TargetInstrInfo &TII = *getSubtarget().getInstrInfo();
@@ -1175,7 +1176,8 @@ auto MachineFunction::salvageCopySSAImpl(MachineInstr &MI)
11751176
for (auto &MO : Inst->all_defs()) {
11761177
if (MO.getReg() != State.first)
11771178
continue;
1178-
return ApplySubregisters({Inst->getDebugInstrNum(), MO.getOperandNo()});
1179+
return {ApplySubregisters({Inst->getDebugInstrNum(), MO.getOperandNo()}),
1180+
Inst};
11791181
}
11801182

11811183
llvm_unreachable("Vreg def with no corresponding operand?");
@@ -1195,8 +1197,9 @@ auto MachineFunction::salvageCopySSAImpl(MachineInstr &MI)
11951197
if (!TRI.regsOverlap(RegToSeek, MO.getReg()))
11961198
continue;
11971199

1198-
return ApplySubregisters(
1199-
{ToExamine.getDebugInstrNum(), MO.getOperandNo()});
1200+
return {
1201+
ApplySubregisters({ToExamine.getDebugInstrNum(), MO.getOperandNo()}),
1202+
&ToExamine};
12001203
}
12011204
}
12021205

@@ -1217,7 +1220,131 @@ auto MachineFunction::salvageCopySSAImpl(MachineInstr &MI)
12171220
Builder.addReg(State.first);
12181221
unsigned NewNum = getNewDebugInstrNum();
12191222
Builder.addImm(NewNum);
1220-
return ApplySubregisters({NewNum, 0u});
1223+
return {ApplySubregisters({NewNum, 0u}), nullptr};
1224+
}
1225+
1226+
/// The Op operand to the DBG_INSTR_REF instruction DbgInstr is a virtual
1227+
/// register defined by the REG_SEQUENCE instruction RegSeq. In order to
1228+
/// finalize DbgInstr to use instruction references, find the defining
1229+
/// instruction for each register in the sequence and compose them with a
1230+
/// DIOpComposite.
1231+
static bool finalizeInstrRefRegSequenceNew(
1232+
MachineInstr &DbgInstr, MachineOperand &Op, MachineInstr &RegSeq,
1233+
DenseMap<Register, MachineFunction::SalvageCopySSAResult> &DbgPHICache) {
1234+
1235+
const DIExpression *Expr = DbgInstr.getDebugExpression();
1236+
if (Expr->holdsOldElements())
1237+
return false;
1238+
1239+
auto &MF = *DbgInstr.getParent()->getParent();
1240+
auto &Ctx = Expr->getContext();
1241+
auto &TRI = *MF.getSubtarget().getRegisterInfo();
1242+
auto &TII = *MF.getSubtarget().getInstrInfo();
1243+
auto &DL = MF.getDataLayout();
1244+
1245+
struct Part {
1246+
MachineFunction::DebugInstrOperandPair DbgInstrNum;
1247+
unsigned Size;
1248+
unsigned Offset;
1249+
};
1250+
SmallVector<Part> Parts;
1251+
1252+
// Walk through the reg sequence, collecting debug-instr-numbers and
1253+
// subregister piece sizes and offsets into Parts.
1254+
for (unsigned I = 1; I < RegSeq.getNumOperands(); I += 2) {
1255+
Register RegOp = RegSeq.getOperand(I).getReg();
1256+
if (!RegOp.isVirtual())
1257+
return false;
1258+
1259+
unsigned SubReg = RegSeq.getOperand(I + 1).getImm();
1260+
unsigned SubSize = TRI.getSubRegIdxSize(SubReg);
1261+
unsigned SubOffset = TRI.getSubRegIdxOffset(SubReg);
1262+
MachineInstr &DefMI = *MF.getRegInfo().def_instr_begin(RegOp);
1263+
1264+
if (DefMI.isCopyLike() || TII.isCopyInstr(DefMI)) {
1265+
auto P = MF.salvageCopySSA(DefMI, DbgPHICache);
1266+
Parts.push_back({P.first, SubSize, SubOffset});
1267+
continue;
1268+
}
1269+
1270+
// Otherwise, identify the operand number that the VReg refers to.
1271+
unsigned OperandIdx = 0;
1272+
for (const auto &DefMO : DefMI.operands()) {
1273+
if (DefMO.isReg() && DefMO.isDef() && DefMO.getReg() == RegOp)
1274+
break;
1275+
++OperandIdx;
1276+
}
1277+
assert(OperandIdx < DefMI.getNumOperands());
1278+
1279+
// Morph this instr ref to point at the given instruction and operand.
1280+
unsigned ID = DefMI.getDebugInstrNum();
1281+
MachineFunction::DebugInstrOperandPair P{ID, OperandIdx};
1282+
Parts.push_back({P, SubSize, SubOffset});
1283+
}
1284+
1285+
// Line up the Parts and make sure there aren't any gaps, DIOpComposite can't
1286+
// handle that easily.
1287+
std::sort(Parts.begin(), Parts.end(),
1288+
[](auto &LHS, auto &RHS) { return LHS.Offset < RHS.Offset; });
1289+
for (unsigned I = 1, E = Parts.size(); I < E; ++I)
1290+
if (Parts[I - 1].Offset + Parts[I - 1].Size != Parts[I].Offset)
1291+
return false;
1292+
if (Parts.empty() || Parts[0].Offset)
1293+
return false;
1294+
1295+
unsigned ArgNoToReplace = 0;
1296+
unsigned NumArgs = DbgInstr.getNumDebugOperands();
1297+
assert(NumArgs == Expr->getNewNumLocationOperands());
1298+
for (; ArgNoToReplace != NumArgs; ++ArgNoToReplace)
1299+
if (&DbgInstr.getDebugOperand(ArgNoToReplace) == &Op)
1300+
break;
1301+
if (ArgNoToReplace == NumArgs)
1302+
return false;
1303+
1304+
auto Elems = Expr->getNewElementsRef();
1305+
auto NewSize = TypeSize::getFixed(Parts.back().Offset + Parts.back().Size);
1306+
for (DIOp::Variant Elem : *Elems) {
1307+
// Only replace the argument with a composite if it has the same size as the
1308+
// parts.
1309+
if (auto *Arg = std::get_if<DIOp::Arg>(&Elem))
1310+
if (Arg->getIndex() == ArgNoToReplace &&
1311+
DL.getTypeSizeInBits(Arg->getResultType()) != NewSize)
1312+
return false;
1313+
}
1314+
1315+
Op.ChangeToDbgInstrRef(Parts[0].DbgInstrNum.first,
1316+
Parts[0].DbgInstrNum.second);
1317+
if (Parts.size() == 1)
1318+
return true;
1319+
1320+
// Split up the DIOpArg using a DIOpComposite.
1321+
DIExprBuilder B{Ctx};
1322+
for (DIOp::Variant Elem : *Elems) {
1323+
auto *Arg = std::get_if<DIOp::Arg>(&Elem);
1324+
if (!Arg || Arg->getIndex() != ArgNoToReplace) {
1325+
B.append(Elem);
1326+
continue;
1327+
}
1328+
bool FirstPart = true;
1329+
for (const Part &P : Parts) {
1330+
// Since these arguments have to line up with the order of the operands on
1331+
// the DBG_INSTR_REF, recycle Arg's index first, it lines up with the Op
1332+
// that was ChangeToDbgInstrRef'd above.
1333+
unsigned ArgNo = FirstPart ? Arg->getIndex() : NumArgs++;
1334+
FirstPart = false;
1335+
B.append<DIOp::Arg>(ArgNo, IntegerType::get(Ctx, P.Size));
1336+
}
1337+
B.append<DIOp::Composite>(Parts.size(), Arg->getResultType());
1338+
}
1339+
1340+
auto *NewExpr = B.intoExpression();
1341+
for (const Part &P : drop_begin(Parts, 1))
1342+
DbgInstr.addOperand(MachineOperand::CreateDbgInstrRef(
1343+
P.DbgInstrNum.first, P.DbgInstrNum.second));
1344+
DbgInstr.getDebugExpressionOp() = MachineOperand::CreateMetadata(NewExpr);
1345+
assert(NewExpr->getNewNumLocationOperands() ==
1346+
DbgInstr.getNumDebugOperands());
1347+
return true;
12211348
}
12221349

12231350
void MachineFunction::finalizeDebugInstrRefs() {
@@ -1229,15 +1356,16 @@ void MachineFunction::finalizeDebugInstrRefs() {
12291356
MI.setDebugValueUndef();
12301357
};
12311358

1232-
DenseMap<Register, DebugInstrOperandPair> ArgDbgPHIs;
1359+
DenseMap<Register, SalvageCopySSAResult> ArgDbgPHIs;
12331360
for (auto &MBB : *this) {
12341361
for (auto &MI : MBB) {
12351362
if (!MI.isDebugRef())
12361363
continue;
12371364

12381365
bool IsValidRef = true;
12391366

1240-
for (MachineOperand &MO : MI.debug_operands()) {
1367+
for (unsigned I = 0; I < MI.getNumDebugOperands(); ++I) {
1368+
MachineOperand &MO = MI.getDebugOperand(I);
12411369
if (!MO.isReg())
12421370
continue;
12431371

@@ -1259,7 +1387,12 @@ void MachineFunction::finalizeDebugInstrRefs() {
12591387
// for why this is important.
12601388
if (DefMI.isCopyLike() || TII->isCopyInstr(DefMI)) {
12611389
auto Result = salvageCopySSA(DefMI, ArgDbgPHIs);
1262-
MO.ChangeToDbgInstrRef(Result.first, Result.second);
1390+
if (!Result.second || !Result.second->isRegSequence() ||
1391+
!finalizeInstrRefRegSequenceNew(MI, MO, *Result.second,
1392+
ArgDbgPHIs))
1393+
MO.ChangeToDbgInstrRef(Result.first.first, Result.first.second);
1394+
} else if (DefMI.isRegSequence() &&
1395+
finalizeInstrRefRegSequenceNew(MI, MO, DefMI, ArgDbgPHIs)) {
12631396
} else {
12641397
// Otherwise, identify the operand number that the VReg refers to.
12651398
unsigned OperandIdx = 0;

llvm/lib/CodeGen/SelectionDAG/InstrEmitter.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -882,7 +882,8 @@ InstrEmitter::EmitDbgInstrRef(SDDbgValue *SD,
882882
// Avoid copy like instructions: they don't define values, only move them.
883883
// Leave a virtual-register reference until it can be fixed up later, to
884884
// find the underlying value definition.
885-
if (DefMI->isCopyLike() || TII->isCopyInstr(*DefMI)) {
885+
if (DefMI->isCopyLike() || TII->isCopyInstr(*DefMI) ||
886+
(Expr->holdsNewElements() && DefMI->isRegSequence())) {
886887
AddVRegOp(VReg);
887888
continue;
888889
}

llvm/test/DebugInfo/AMDGPU/heterogeneous-dwarf-diop-diexpression-subregs.mir

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
!24 = !DILocalVariable(name: "sgpr", scope: !9, file: !1, line: 1, type: !14)
4141
!25 = !DILocalVariable(name: "vgpr", scope: !9, file: !1, line: 1, type: !14)
4242
!26 = !DILocalVariable(name: "vgpr_frags", scope: !9, file: !1, line: 1, type: !15)
43+
!27 = !DILocalVariable(name: "composite", scope: !9, file: !1, line: 1, type: !15)
4344

4445
...
4546
---
@@ -93,6 +94,11 @@ body: |
9394
DBG_VALUE renamable $vgpr42_vgpr43, $noreg, !26, !DIExpression(DIOpArg(0, i64), DIOpFragment(0, 32)), debug-location !16
9495
DBG_VALUE renamable $vgpr44_vgpr45, $noreg, !26, !DIExpression(DIOpArg(0, i64), DIOpFragment(32, 32)), debug-location !16
9596
97+
; CHECK: DW_TAG_variable
98+
; CHECK-NEXT: DW_AT_location (DW_OP_regx SGPR10, DW_OP_piece 0x4, DW_OP_regx SGPR11, DW_OP_piece 0x4, DW_OP_LLVM_user DW_OP_LLVM_piece_end)
99+
; CHECK-NEXT: DW_AT_name ("composite")
100+
DBG_VALUE_LIST !27, !DIExpression(DIOpArg(0, i32), DIOpArg(1, i32), DIOpComposite(2, i64)), $sgpr10, $sgpr11, debug-location !16
101+
96102
S_ENDPGM 0, debug-location !16
97103
98104
...
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
; NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 5
2+
; RUN: llc -mtriple=amdgcn-amd-amdhsa -mcpu=gfx1030 -start-before=amdgpu-isel -stop-after=amdgpu-isel %s -o - | FileCheck %s
3+
4+
define i64 @test(ptr addrspace(1) %p) !dbg !11 {
5+
; CHECK-LABEL: name: test
6+
; CHECK: bb.0 (%ir-block.0):
7+
; CHECK-NEXT: liveins: $vgpr0, $vgpr1
8+
; CHECK-NEXT: {{ $}}
9+
; CHECK-NEXT: [[COPY:%[0-9]+]]:vgpr_32 = COPY $vgpr1
10+
; CHECK-NEXT: [[COPY1:%[0-9]+]]:vgpr_32 = COPY $vgpr0
11+
; CHECK-NEXT: [[REG_SEQUENCE:%[0-9]+]]:sreg_64 = REG_SEQUENCE [[COPY1]], %subreg.sub0, [[COPY]], %subreg.sub1
12+
; CHECK-NEXT: [[COPY2:%[0-9]+]]:vreg_64 = COPY [[REG_SEQUENCE]]
13+
; CHECK-NEXT: [[GLOBAL_LOAD_DWORD:%[0-9]+]]:vgpr_32 = GLOBAL_LOAD_DWORD killed [[COPY2]], 0, 0, implicit $exec, debug-instr-number 1 :: (load (s32) from %ir.p, addrspace 1)
14+
; CHECK-NEXT: [[V_ASHRREV_I32_e64_:%[0-9]+]]:vgpr_32 = V_ASHRREV_I32_e64 31, [[GLOBAL_LOAD_DWORD]], implicit $exec, debug-instr-number 2
15+
; CHECK-NEXT: [[COPY3:%[0-9]+]]:vgpr_32 = COPY [[V_ASHRREV_I32_e64_]]
16+
; CHECK-NEXT: [[REG_SEQUENCE1:%[0-9]+]]:vreg_64 = REG_SEQUENCE [[GLOBAL_LOAD_DWORD]], %subreg.sub0, killed [[COPY3]], %subreg.sub1
17+
; CHECK-NEXT: DBG_INSTR_REF !17, !DIExpression(DIOpArg(0, i32), DIOpArg(1, i32), DIOpComposite(2, i64)), dbg-instr-ref(1, 0), dbg-instr-ref(2, 0), debug-location !18
18+
; CHECK-NEXT: [[COPY4:%[0-9]+]]:vgpr_32 = COPY [[REG_SEQUENCE1]].sub1
19+
; CHECK-NEXT: $vgpr0 = COPY [[GLOBAL_LOAD_DWORD]]
20+
; CHECK-NEXT: $vgpr1 = COPY [[COPY4]]
21+
; CHECK-NEXT: SI_RETURN implicit $vgpr0, implicit $vgpr1
22+
%load = load i32, ptr addrspace(1) %p, align 4
23+
%conv = sext i32 %load to i64
24+
#dbg_value(i64 %conv, !17, !DIExpression(DIOpArg(0, i64)), !18)
25+
ret i64 %conv
26+
}
27+
28+
!llvm.dbg.cu = !{!0}
29+
!llvm.module.flags = !{!2, !3, !4, !5, !6, !7}
30+
!opencl.ocl.version = !{!8}
31+
!llvm.ident = !{!9, !10}
32+
33+
!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !1, producer: "clang version 21.0.0", isOptimized: false, runtimeVersion: 0, emissionKind: NoDebug)
34+
!1 = !DIFile(filename: "t.cpp", directory: "/")
35+
!2 = !{i32 1, !"amdhsa_code_object_version", i32 600}
36+
!3 = !{i32 1, !"amdgpu_printf_kind", !"hostcall"}
37+
!4 = !{i32 7, !"Dwarf Version", i32 5}
38+
!5 = !{i32 2, !"Debug Info Version", i32 3}
39+
!6 = !{i32 1, !"wchar_size", i32 4}
40+
!7 = !{i32 8, !"PIC Level", i32 2}
41+
!8 = !{i32 2, i32 0}
42+
!9 = !{!"clang version 21.0.0"}
43+
!10 = !{!"clang version 18.0.0"}
44+
!11 = distinct !DISubprogram(name: "test", linkageName: "test", scope: !1, file: !1, line: 6, type: !12, scopeLine: 7, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !16)
45+
!12 = !DISubroutineType(types: !13)
46+
!13 = !{!15, !14}
47+
!14 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !15, size: 64)
48+
!15 = !DIBasicType(name: "long", size: 64, encoding: DW_ATE_signed)
49+
!16 = !{!17}
50+
!17 = !DILocalVariable(name: "var", scope: !11, file: !1, line: 8, type: !15)
51+
!18 = !DILocation(line: 0, scope: !11)

0 commit comments

Comments
 (0)