}
};
+ // Target indices used for wasm-specific locations.
+ struct WasmLoc {
+ int Index; // One of TargetIndex values defined in WebAssembly.h
+ int64_t Offset;
+ bool operator==(const WasmLoc &Other) const {
+ return Index == Other.Index && Offset == Other.Offset;
+ }
+ bool operator!=(const WasmLoc &Other) const { return !(*this == Other); }
+ };
+
/// Identity of the variable at this location.
const DebugVariable Var;
InvalidKind = 0,
RegisterKind,
SpillLocKind,
- ImmediateKind
+ ImmediateKind,
+ WasmLocKind
};
enum class EntryValueLocKind {
int64_t Immediate;
const ConstantFP *FPImm;
const ConstantInt *CImm;
+ WasmLoc WasmLocation;
MachineLocValue() : Hash(0) {}
};
switch (Kind) {
case MachineLocKind::SpillLocKind:
return Value.SpillLocation == Other.Value.SpillLocation;
+ case MachineLocKind::WasmLocKind:
+ return Value.WasmLocation == Other.Value.WasmLocation;
case MachineLocKind::RegisterKind:
case MachineLocKind::ImmediateKind:
return Value.Hash == Other.Value.Hash;
Other.Kind, Other.Value.SpillLocation.SpillBase,
Other.Value.SpillLocation.SpillOffset.getFixed(),
Other.Value.SpillLocation.SpillOffset.getScalable());
+ case MachineLocKind::WasmLocKind:
+ return std::make_tuple(Kind, Value.WasmLocation.Index,
+ Value.WasmLocation.Offset) <
+ std::make_tuple(Other.Kind, Other.Value.WasmLocation.Index,
+ Other.Value.WasmLocation.Offset);
case MachineLocKind::RegisterKind:
case MachineLocKind::ImmediateKind:
return std::tie(Kind, Value.Hash) <
} else if (Op.isCImm()) {
Kind = MachineLocKind::ImmediateKind;
Loc.CImm = Op.getCImm();
+ } else if (Op.isTargetIndex()) {
+ Kind = MachineLocKind::WasmLocKind;
+ Loc.WasmLocation = {Op.getIndex(), Op.getOffset()};
} else
llvm_unreachable("Invalid Op kind for MachineLoc.");
return {Kind, Loc};
MOs.push_back(Orig);
break;
}
+ case MachineLocKind::WasmLocKind: {
+ MOs.push_back(Orig);
+ break;
+ }
case MachineLocKind::InvalidKind:
llvm_unreachable("Tried to produce DBG_VALUE for invalid VarLoc");
}
}
#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
- // TRI can be null.
- void dump(const TargetRegisterInfo *TRI, raw_ostream &Out = dbgs()) const {
+ // TRI and TII can be null.
+ void dump(const TargetRegisterInfo *TRI, const TargetInstrInfo *TII,
+ raw_ostream &Out = dbgs()) const {
Out << "VarLoc(";
for (const MachineLoc &MLoc : Locs) {
if (Locs.begin() != &MLoc)
case MachineLocKind::ImmediateKind:
Out << MLoc.Value.Immediate;
break;
+ case MachineLocKind::WasmLocKind: {
+ if (TII) {
+ auto Indices = TII->getSerializableTargetIndices();
+ auto Found =
+ find_if(Indices, [&](const std::pair<int, const char *> &I) {
+ return I.first == MLoc.Value.WasmLocation.Index;
+ });
+ assert(Found != Indices.end());
+ Out << Found->second;
+ if (MLoc.Value.WasmLocation.Offset > 0)
+ Out << " + " << MLoc.Value.WasmLocation.Offset;
+ } else {
+ Out << "WasmLoc";
+ }
+ break;
+ }
case MachineLocKind::InvalidKind:
llvm_unreachable("Invalid VarLoc in dump method");
}
for (const VarLoc &VL : VarLocs) {
Out << " Var: " << VL.Var.getVariable()->getName();
Out << " MI: ";
- VL.dump(TRI, Out);
+ VL.dump(TRI, TII, Out);
}
}
Out << "\n";
if (all_of(MI.debug_operands(), [](const MachineOperand &MO) {
return (MO.isReg() && MO.getReg()) || MO.isImm() || MO.isFPImm() ||
- MO.isCImm();
+ MO.isCImm() || MO.isTargetIndex();
})) {
// Use normal VarLoc constructor for registers and immediates.
VarLoc VL(MI);
ProcessVarLoc(VL);
LLVM_DEBUG({
dbgs() << "Creating VarLoc for register copy:";
- VL.dump(TRI);
+ VL.dump(TRI, TII);
});
return;
}
ProcessVarLoc(VL);
LLVM_DEBUG({
dbgs() << "Creating VarLoc for spill:";
- VL.dump(TRI);
+ VL.dump(TRI, TII);
});
return;
}
ProcessVarLoc(VL);
LLVM_DEBUG({
dbgs() << "Creating VarLoc for restore:";
- VL.dump(TRI);
+ VL.dump(TRI, TII);
});
return;
}
for (VarLoc &VL : VarLocs) {
// Copy OpenRanges to OutLocs, if not already present.
dbgs() << "Add to OutLocs in MBB #" << CurMBB->getNumber() << ": ";
- VL.dump(TRI);
+ VL.dump(TRI, TII);
}
});
VarLocSet &VLS = getVarLocsInMBB(CurMBB, OutLocs);
/// Collect all register defines (including aliases) for the given instruction.
static void collectRegDefs(const MachineInstr &MI, DefinedRegsSet &Regs,
const TargetRegisterInfo *TRI) {
- for (const MachineOperand &MO : MI.operands())
- if (MO.isReg() && MO.isDef() && MO.getReg())
+ for (const MachineOperand &MO : MI.operands()) {
+ if (MO.isReg() && MO.isDef() && MO.getReg() &&
+ Register::isPhysicalRegister(MO.getReg())) {
+ Regs.insert(MO.getReg());
for (MCRegAliasIterator AI(MO.getReg(), TRI, true); AI.isValid(); ++AI)
Regs.insert(*AI);
+ }
+ }
}
/// This routine records the entry values of function parameters. The values
TargetPassConfig *TPC, unsigned InputBBLimit,
unsigned InputDbgValLimit) {
(void)DomTree;
- LLVM_DEBUG(dbgs() << "\nDebug Range Extension\n");
+ LLVM_DEBUG(dbgs() << "\nDebug Range Extension: " << MF.getName() << "\n");
if (!MF.getFunction().getSubprogram())
// VarLocBaseLDV will already have removed all DBG_VALUEs.
--- /dev/null
+# RUN: llc -run-pass livedebugvalues %s -o - | FileCheck %s
+
+# Test if LiveDebugValue analysis works on Wasm.
+
+--- |
+ target triple = "wasm32-unknown-unknown"
+
+ define void @test_consts() !dbg !5 {
+ call void @llvm.dbg.value(metadata i32 0, metadata !9, metadata !DIExpression()), !dbg !10
+ call void @llvm.dbg.value(metadata i32 0, metadata !11, metadata !DIExpression()), !dbg !10
+ call void @llvm.dbg.value(metadata i32 0, metadata !12, metadata !DIExpression()), !dbg !10
+ call void @llvm.dbg.value(metadata i32 0, metadata !13, metadata !DIExpression()), !dbg !10
+ ret void
+ }
+
+ define void @test_target_indices() !dbg !14 {
+ call void @llvm.dbg.value(metadata i32 0, metadata !15, metadata !DIExpression()), !dbg !16
+ call void @llvm.dbg.value(metadata i32 0, metadata !17, metadata !DIExpression()), !dbg !16
+ call void @llvm.dbg.value(metadata i32 0, metadata !18, metadata !DIExpression()), !dbg !16
+ call void @llvm.dbg.value(metadata i32 0, metadata !19, metadata !DIExpression()), !dbg !16
+ call void @llvm.dbg.value(metadata i32 0, metadata !20, metadata !DIExpression()), !dbg !16
+ ret void
+ }
+
+ declare void @llvm.dbg.value(metadata, metadata, metadata)
+
+ !llvm.dbg.cu = !{!0}
+ !llvm.module.flags = !{!2, !3, !4}
+
+ !0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, emissionKind: FullDebug)
+ !1 = !DIFile(filename: "test.c", directory: "")
+ !2 = !{i32 7, !"Dwarf Version", i32 5}
+ !3 = !{i32 2, !"Debug Info Version", i32 3}
+ !4 = !{i32 1, !"wchar_size", i32 4}
+ !5 = distinct !DISubprogram(name: "test_consts", scope: !1, file: !1, line: 1, type: !6, scopeLine: 1, unit: !0)
+ ; CHECK: ![[T0_SP:[0-9]+]] = distinct !DISubprogram(name: "test_consts"
+ !6 = !DISubroutineType(types: !7)
+ !7 = !{!8}
+ !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+ !9 = !DILocalVariable(name: "var0", scope: !5, file: !1, line: 2, type: !8)
+ ; CHECK: ![[T0_VAR0:[0-9]+]] = !DILocalVariable(name: "var0", scope: ![[T0_SP]]
+ !10 = !DILocation(line: 0, scope: !5)
+ !11 = !DILocalVariable(name: "var1", scope: !5, file: !1, line: 2, type: !8)
+ ; CHECK: ![[T0_VAR1:[0-9]+]] = !DILocalVariable(name: "var1", scope: ![[T0_SP]]
+ !12 = !DILocalVariable(name: "var2", scope: !5, file: !1, line: 2, type: !8)
+ ; CHECK: ![[T0_VAR2:[0-9]+]] = !DILocalVariable(name: "var2", scope: ![[T0_SP]]
+ !13 = !DILocalVariable(name: "var3", scope: !5, file: !1, line: 2, type: !8)
+ ; CHECK: ![[T0_VAR3:[0-9]+]] = !DILocalVariable(name: "var3", scope: ![[T0_SP]]
+ !14 = distinct !DISubprogram(name: "test_target_indices", scope: !1, file: !1, line: 10, type: !6, scopeLine: 1, unit: !0)
+ ; CHECK: ![[T1_SP:[0-9]+]] = distinct !DISubprogram(name: "test_target_indices"
+ !15 = !DILocalVariable(name: "var0", scope: !14, file: !1, line: 11, type: !8)
+ ; CHECK: ![[T1_VAR0:[0-9]+]] = !DILocalVariable(name: "var0", scope: ![[T1_SP]]
+ !16 = !DILocation(line: 10, scope: !14)
+ !17 = !DILocalVariable(name: "var1", scope: !14, file: !1, line: 11, type: !8)
+ ; CHECK: ![[T1_VAR1:[0-9]+]] = !DILocalVariable(name: "var1", scope: ![[T1_SP]]
+ !18 = !DILocalVariable(name: "var2", scope: !14, file: !1, line: 11, type: !8)
+ ; CHECK: ![[T1_VAR2:[0-9]+]] = !DILocalVariable(name: "var2", scope: ![[T1_SP]]
+ !19 = !DILocalVariable(name: "var3", scope: !14, file: !1, line: 11, type: !8)
+ ; CHECK: ![[T1_VAR3:[0-9]+]] = !DILocalVariable(name: "var3", scope: ![[T1_SP]]
+ !20 = !DILocalVariable(name: "var4", scope: !14, file: !1, line: 11, type: !8)
+ ; CHECK: ![[T1_VAR4:[0-9]+]] = !DILocalVariable(name: "var4", scope: ![[T1_SP]]
+...
+
+---
+# var0: Its debug value is set once in bb.0 and valid throughout the function.
+# So a new DBG_VALUE will be added to the beginnings of all other BBs
+# (bb.1, bb.2, and bb.3).
+# var1: Its debug value is set in bb.0, but is reset to no info in bb.1. So a
+# new DBG_VALUE will be added in the beginning of bb.1 and bb.2, but bb.3
+# will not, because its two predecesors (bb.1 and bb.2) have different
+# debug values.
+# var2: Its debug value is set both in bb.1 and bb.2 with the same value. bb.3
+# will have a new DBG_VALUE representing that value.
+# var3: Its debug value is set both in bb.1 and bb.2 but with different values.
+# bb.3 will NOT have a new DBG_VALUE instruction because its predecessors
+# don't agree on a value.
+# CHECK-LABEL: name: test_consts
+name: test_consts
+liveins:
+ - { reg: '$arguments' }
+body: |
+ bb.0:
+ successors: %bb.1, %bb.2
+ liveins: $arguments
+ DBG_VALUE 10, $noreg, !9, !DIExpression(), debug-location !10
+ DBG_VALUE 20, $noreg, !11, !DIExpression(), debug-location !10
+ %0:i32 = CONST_I32 1, implicit-def $arguments
+ BR_IF %bb.2, %0:i32, implicit-def $arguments
+
+ ; CHECK: bb.1:
+ ; CHECK-DAG: DBG_VALUE 10, $noreg, ![[T0_VAR0]]
+ ; CHECK-DAG: DBG_VALUE 20, $noreg, ![[T0_VAR1]]
+ bb.1:
+ ; predecessors: %bb.0
+ successors: %bb.3
+ DBG_VALUE $noreg, $noreg, !11, !DIExpression(), debug-location !10
+ DBG_VALUE 30, $noreg, !12, !DIExpression(), debug-location !10
+ DBG_VALUE 30, $noreg, !13, !DIExpression(), debug-location !10
+ BR %bb.3, implicit-def $arguments
+
+ ; CHECK: bb.2:
+ ; CHECK-DAG: DBG_VALUE 10, $noreg, ![[T0_VAR0]]
+ ; CHECK-DAG: DBG_VALUE 20, $noreg, ![[T0_VAR1]]
+ bb.2:
+ ; predecessors: %bb.0
+ successors: %bb.3
+ DBG_VALUE 30, $noreg, !12, !DIExpression(), debug-location !10
+ DBG_VALUE 40, $noreg, !13, !DIExpression(), debug-location !10
+ BR %bb.3, implicit-def $arguments
+
+ ; CHECK: bb.3:
+ ; CHECK-DAG: DBG_VALUE 10, $noreg, ![[T0_VAR0]]
+ ; CHECK-DAG: DBG_VALUE 30, $noreg, ![[T0_VAR2]]
+ ; CHECK-NOT: DBG_VALUE {{[0-9]+}}, $noreg, ![[T0_VAR1]]
+ ; CHECK-NOT: DBG_VALUE {{[0-9]+}}, $noreg, ![[T0_VAR3]]
+ bb.3:
+ ; predecessors: %bb.1, %bb.2
+ RETURN implicit-def dead $arguments
+...
+
+---
+# var0: Its debug value is set once in bb.0 and valid throughout the function.
+# So a new DBG_VALUE will be added to the beginnings of all other BBs
+# (bb.1, bb.2, and bb.3).
+# var1: Its debug value is set in bb.0, but is reset to no info back later in
+# bb.0. So no DBG_VALUEs will be added in other BBs.
+# var2: Its debug value is set both in bb.1 and bb.2 with the same value (The
+# same kind (local) and same local index). bb.3 will have a new DBG_VALUE
+# representing that value.
+# var3: Its debug value is set both in bb.1 and bb.2 but with different values.
+# Both are locals, but their indices are different. bb.3 will NOT have a
+# new DBG_VALUE instruction because its predecessors don't agree on a
+# value.
+# var4: Its debug value is set both in bb.1 and bb.2 but with different values.
+# They have different kinds; one is a local and the other is a stack
+# operand. (Their index is the same, but it doesn't matter.) bb.3 will NOT
+# have a new DBG_VALUE instruction because its predecessors don't agree on
+# a value.
+# CHECK-LABEL: name: test_target_indices
+name: test_target_indices
+liveins:
+ - { reg: '$arguments' }
+body: |
+ bb.0:
+ successors: %bb.1, %bb.2
+ liveins: $arguments
+ DBG_VALUE target-index(wasm-local), $noreg, !15, !DIExpression(), debug-location !16
+ DBG_VALUE target-index(wasm-operand-stack) + 2, $noreg, !17, !DIExpression(), debug-location !16
+ %0:i32 = CONST_I32 1, implicit-def $arguments
+ DBG_VALUE $noreg, $noreg, !17, !DIExpression(), debug-location !16
+ BR_IF %bb.2, %0:i32, implicit-def $arguments
+
+ ; CHECK: bb.1:
+ ; CHECK: DBG_VALUE target-index(wasm-local), $noreg, ![[T1_VAR0]]
+ bb.1:
+ ; predecessors: %bb.0
+ successors: %bb.3
+ DBG_VALUE target-index(wasm-local) + 2, $noreg, !18, !DIExpression(), debug-location !16
+ DBG_VALUE target-index(wasm-local) + 3, $noreg, !19, !DIExpression(), debug-location !16
+ DBG_VALUE target-index(wasm-operand-stack) + 3, $noreg, !20, !DIExpression(), debug-location !16
+ BR %bb.3, implicit-def $arguments
+
+ ; CHECK: bb.2:
+ ; CHECK: DBG_VALUE target-index(wasm-local), $noreg, ![[T1_VAR0]]
+ bb.2:
+ ; predecessors: %bb.0
+ successors: %bb.3
+ DBG_VALUE target-index(wasm-local) + 2, $noreg, !18, !DIExpression(), debug-location !16
+ DBG_VALUE target-index(wasm-local) + 5, $noreg, !19, !DIExpression(), debug-location !16
+ DBG_VALUE target-index(wasm-local) + 3, $noreg, !20, !DIExpression(), debug-location !16
+ BR %bb.3, implicit-def $arguments
+
+ ; CHECK: bb.3:
+ ; CHECK-DAG: DBG_VALUE target-index(wasm-local), $noreg, ![[T1_VAR0]]
+ ; CHECK-DAG: DBG_VALUE target-index(wasm-local) + 2, $noreg, ![[T1_VAR2]]
+ ; CHECK-NOT: DBG_VALUE target-index(wasm-local){{.*}}, $noreg, ![[T1_VAR3]]
+ ; CHECK-NOT: DBG_VALUE target-index(wasm-local){{.*}}, $noreg, ![[T1_VAR4]]
+ bb.3:
+ ; predecessors: %bb.1, %bb.2
+ RETURN implicit-def dead $arguments
+...