Index: llvm/include/llvm/CodeGen/MIRYamlMapping.h =================================================================== --- llvm/include/llvm/CodeGen/MIRYamlMapping.h +++ llvm/include/llvm/CodeGen/MIRYamlMapping.h @@ -441,6 +441,36 @@ static const bool flow = true; }; +/// Serializable representation of debug value substitutions. +struct DebugValueSubstitution { + unsigned SrcInst; + unsigned SrcOp; + unsigned DstInst; + unsigned DstOp; + + bool operator==(const DebugValueSubstitution &Other) const { + return std::tie(SrcInst, SrcOp, DstInst, DstOp) == + std::tie(Other.SrcInst, Other.SrcOp, Other.DstInst, Other.DstOp); + } +}; + +template <> struct MappingTraits { + static void mapping(IO &YamlIO, DebugValueSubstitution &Sub) { + YamlIO.mapRequired("srcinst", Sub.SrcInst); + YamlIO.mapRequired("srcop", Sub.SrcOp); + YamlIO.mapRequired("dstinst", Sub.DstInst); + YamlIO.mapRequired("dstop", Sub.DstOp); + } + + static const bool flow = true; +}; +} // namespace yaml +} // namespace llvm + +LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::yaml::DebugValueSubstitution) + +namespace llvm { +namespace yaml { struct MachineConstantPoolValue { UnsignedValue ID; StringValue Value; @@ -625,6 +655,7 @@ std::vector Constants; /// Constant pool. std::unique_ptr MachineFuncInfo; std::vector CallSitesInfo; + std::vector DebugValueSubstitutions; MachineJumpTable JumpTableInfo; BlockStringValue Body; }; @@ -653,6 +684,8 @@ std::vector()); YamlIO.mapOptional("callSites", MF.CallSitesInfo, std::vector()); + YamlIO.mapOptional("debugValueSubstitutions", MF.DebugValueSubstitutions, + std::vector()); YamlIO.mapOptional("constants", MF.Constants, std::vector()); YamlIO.mapOptional("machineFunctionInfo", MF.MachineFuncInfo); Index: llvm/include/llvm/CodeGen/MachineFunction.h =================================================================== --- llvm/include/llvm/CodeGen/MachineFunction.h +++ llvm/include/llvm/CodeGen/MachineFunction.h @@ -440,6 +440,30 @@ /// you're deserializing this data. void setDebugInstrNumberingCount(unsigned Num); + /// Pair of instruction number and operand number. + using DebugInstrOperandPair = std::pair; + + /// Substitution map: from one pair to another. Used to + /// record changes in where a value is defined, so that debug variable + /// locations can find it later. + std::map + DebugValueSubstitutions; + + /// Create a substitution between one value to a different, + /// new value. + void makeDebugValueSubstitution(DebugInstrOperandPair, DebugInstrOperandPair); + + /// Create substitutions for any tracked values in \p Old, to point at + /// \p New. Needed when we re-create an instruction during optimization, + /// which has the same signature (i.e., def operands in the same place) but + /// a modified instruction type, flags, or otherwise. An example: X86 moves + /// are sometimes transformed into equivalent LEAs. + /// If the two instructions are not the same opcode, limit which operands to + /// examine for substitutions to the first N operands by setting + /// \p MaxOperand. + void substituteDebugValuesForInst(const MachineInstr &Old, MachineInstr &New, + unsigned MaxOperand = UINT_MAX); + MachineFunction(Function &F, const LLVMTargetMachine &Target, const TargetSubtargetInfo &STI, unsigned FunctionNum, MachineModuleInfo &MMI); Index: llvm/lib/CodeGen/MIRParser/MIRParser.cpp =================================================================== --- llvm/lib/CodeGen/MIRParser/MIRParser.cpp +++ llvm/lib/CodeGen/MIRParser/MIRParser.cpp @@ -405,17 +405,22 @@ return false; } -void MIRParserImpl::setupDebugValueTracking(MachineFunction &MF, - PerFunctionMIParsingState &PFS, const yaml::MachineFunction &YamlMF) { - // For now, we only compute the value of the "next instruction number" - // field. +void MIRParserImpl::setupDebugValueTracking( + MachineFunction &MF, PerFunctionMIParsingState &PFS, + const yaml::MachineFunction &YamlMF) { + // Compute the value of the "next instruction number" field. unsigned MaxInstrNum = 0; for (auto &MBB : MF) for (auto &MI : MBB) MaxInstrNum = std::max((unsigned)MI.peekDebugInstrNum(), MaxInstrNum); MF.setDebugInstrNumberingCount(MaxInstrNum); -} + // Load any substitutions. + for (auto &Sub : YamlMF.DebugValueSubstitutions) { + MF.makeDebugValueSubstitution(std::make_pair(Sub.SrcInst, Sub.SrcOp), + std::make_pair(Sub.DstInst, Sub.DstOp)); + } +} bool MIRParserImpl::initializeMachineFunction(const yaml::MachineFunction &YamlMF, Index: llvm/lib/CodeGen/MIRPrinter.cpp =================================================================== --- llvm/lib/CodeGen/MIRPrinter.cpp +++ llvm/lib/CodeGen/MIRPrinter.cpp @@ -220,6 +220,10 @@ convert(MST, YamlMF.FrameInfo, MF.getFrameInfo()); convertStackObjects(YamlMF, MF, MST); convertCallSiteObjects(YamlMF, MF, MST); + for (auto &Sub : MF.DebugValueSubstitutions) + YamlMF.DebugValueSubstitutions.push_back({Sub.first.first, Sub.first.second, + Sub.second.first, + Sub.second.second}); if (const auto *ConstantPool = MF.getConstantPool()) convert(YamlMF, *ConstantPool); if (const auto *JumpTableInfo = MF.getJumpTableInfo()) Index: llvm/lib/CodeGen/MachineFunction.cpp =================================================================== --- llvm/lib/CodeGen/MachineFunction.cpp +++ llvm/lib/CodeGen/MachineFunction.cpp @@ -947,6 +947,42 @@ DebugInstrNumberingCount = Num; } +void MachineFunction::makeDebugValueSubstitution(DebugInstrOperandPair A, + DebugInstrOperandPair B) { + auto Result = DebugValueSubstitutions.insert(std::make_pair(A, B)); + (void)Result; + assert(Result.second && "Substitution for an already substituted value?"); +} + +void MachineFunction::substituteDebugValuesForInst(const MachineInstr &Old, + MachineInstr &New, + unsigned MaxOperand) { + // If the Old instruction wasn't tracked at all, there is no work to do. + unsigned OldInstrNum = Old.peekDebugInstrNum(); + if (!OldInstrNum) + return; + + // Iterate over all operands looking for defs to create substitutions for. + // Avoid creating new instr numbers unless we create a new substitution. + // While this has no functional effect, it risks confusing someone reading + // MIR output. + // Examine all the operands, or the first N specified by the caller. + MaxOperand = std::min(MaxOperand, Old.getNumOperands()); + for (unsigned int I = 0; I < MaxOperand; ++I) { + const auto &OldMO = Old.getOperand(I); + auto &NewMO = New.getOperand(I); + (void)NewMO; + + if (!OldMO.isReg() || !OldMO.isDef()) + continue; + assert(NewMO.isDef()); + + unsigned NewInstrNum = New.getDebugInstrNum(); + makeDebugValueSubstitution(std::make_pair(OldInstrNum, I), + std::make_pair(NewInstrNum, I)); + } +} + /// \} //===----------------------------------------------------------------------===// Index: llvm/test/DebugInfo/MIR/InstrRef/substitusions-roundtrip.mir =================================================================== --- /dev/null +++ llvm/test/DebugInfo/MIR/InstrRef/substitusions-roundtrip.mir @@ -0,0 +1,26 @@ +# RUN: llc %s -march=x86-64 -run-pass=machineverifier \ +# RUN: -experimental-debug-variable-locations -o - 2>&1 | FileCheck %s +# +# REQUIRES: x86-registered-target +# +# CHECK: debugValueSubstitutions: +# CHECK-NEXT: - { srcinst: 1, srcop: 0, dstinst: 2, dstop: 0 } +# +# CHECK: MOV64rr $rdi, debug-instr-number 2 +# CHECK-NEXT: DBG_INSTR_REF 1, 0 +--- +name: test +tracksRegLiveness: true +liveins: + - { reg: '$rdi', virtual-reg: '' } +debugValueSubstitutions: + - { srcinst: 1, srcop: 0, dstinst: 2, dstop: 0 } +body: | + bb.0: + liveins: $rdi, $rax + $rbp = MOV64rr $rdi, debug-instr-number 2 + DBG_INSTR_REF 1, 0 + dead $rcx = MOV64ri 0 + CMP64ri8 renamable $rax, 1, implicit-def $eflags + RETQ $rax +...