diff --git a/llvm/lib/CodeGen/LiveDebugValues/LiveDebugValues.cpp b/llvm/lib/CodeGen/LiveDebugValues/LiveDebugValues.cpp --- a/llvm/lib/CodeGen/LiveDebugValues/LiveDebugValues.cpp +++ b/llvm/lib/CodeGen/LiveDebugValues/LiveDebugValues.cpp @@ -18,6 +18,7 @@ #include "llvm/Pass.h" #include "llvm/PassRegistry.h" #include "llvm/Support/CommandLine.h" +#include "llvm/Target/TargetMachine.h" /// \file LiveDebugValues.cpp /// @@ -72,11 +73,6 @@ /// Calculate the liveness information for the given machine function. bool runOnMachineFunction(MachineFunction &MF) override; - MachineFunctionProperties getRequiredProperties() const override { - return MachineFunctionProperties().set( - MachineFunctionProperties::Property::NoVRegs); - } - void getAnalysisUsage(AnalysisUsage &AU) const override { AU.setPreservesCFG(); MachineFunctionPass::getAnalysisUsage(AU); @@ -106,6 +102,14 @@ } bool LiveDebugValues::runOnMachineFunction(MachineFunction &MF) { + // Except for Wasm, all targets should be only using physical register at this + // point. Wasm only use virtual registers throught its pipeline, but its + // virtual registers don't participate in this LiveDebugValues analysis; only + // its target indices do. + assert(MF.getTarget().getTargetTriple().isWasm() || + MF.getProperties().hasProperty( + MachineFunctionProperties::Property::NoVRegs)); + bool InstrRefBased = MF.useDebugInstrRef(); // Allow the user to force selection of InstrRef LDV. InstrRefBased |= ForceInstrRefLDV; diff --git a/llvm/lib/CodeGen/LiveDebugValues/VarLocBasedImpl.cpp b/llvm/lib/CodeGen/LiveDebugValues/VarLocBasedImpl.cpp --- a/llvm/lib/CodeGen/LiveDebugValues/VarLocBasedImpl.cpp +++ b/llvm/lib/CodeGen/LiveDebugValues/VarLocBasedImpl.cpp @@ -299,6 +299,16 @@ } }; + // 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; @@ -313,7 +323,8 @@ InvalidKind = 0, RegisterKind, SpillLocKind, - ImmediateKind + ImmediateKind, + WasmLocKind }; enum class EntryValueLocKind { @@ -332,6 +343,7 @@ int64_t Immediate; const ConstantFP *FPImm; const ConstantInt *CImm; + WasmLoc WasmLocation; MachineLocValue() : Hash(0) {} }; @@ -348,6 +360,8 @@ 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; @@ -366,6 +380,11 @@ 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) < @@ -429,6 +448,9 @@ } 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}; @@ -562,6 +584,10 @@ 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"); } @@ -654,8 +680,9 @@ } #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) @@ -674,6 +701,22 @@ 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 &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"); } @@ -1201,7 +1244,7 @@ for (const VarLoc &VL : VarLocs) { Out << " Var: " << VL.Var.getVariable()->getName(); Out << " MI: "; - VL.dump(TRI, Out); + VL.dump(TRI, TII, Out); } } Out << "\n"; @@ -1339,7 +1382,7 @@ 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); @@ -1452,7 +1495,7 @@ ProcessVarLoc(VL); LLVM_DEBUG({ dbgs() << "Creating VarLoc for register copy:"; - VL.dump(TRI); + VL.dump(TRI, TII); }); return; } @@ -1465,7 +1508,7 @@ ProcessVarLoc(VL); LLVM_DEBUG({ dbgs() << "Creating VarLoc for spill:"; - VL.dump(TRI); + VL.dump(TRI, TII); }); return; } @@ -1478,7 +1521,7 @@ ProcessVarLoc(VL); LLVM_DEBUG({ dbgs() << "Creating VarLoc for restore:"; - VL.dump(TRI); + VL.dump(TRI, TII); }); return; } @@ -1816,7 +1859,7 @@ 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); @@ -2056,10 +2099,14 @@ /// 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 @@ -2100,7 +2147,7 @@ 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. diff --git a/llvm/test/DebugInfo/WebAssembly/live-debug-values.mir b/llvm/test/DebugInfo/WebAssembly/live-debug-values.mir new file mode 100644 --- /dev/null +++ b/llvm/test/DebugInfo/WebAssembly/live-debug-values.mir @@ -0,0 +1,181 @@ +# 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 +...