Index: include/llvm/CodeGen/TargetInstrInfo.h =================================================================== --- include/llvm/CodeGen/TargetInstrInfo.h +++ include/llvm/CodeGen/TargetInstrInfo.h @@ -1681,6 +1681,13 @@ return false; } + /// Produce RHS description of loading instruction \p MI by giving + /// expression \p Expr over \p Op. If we do not manage to find such + /// description return false. + virtual bool describeLoadedLocation(const MachineInstr &MI, + const MachineOperand *&Op, + DIExpression **Expr) const; + private: unsigned CallFrameSetupOpcode, CallFrameDestroyOpcode; unsigned CatchRetOpcode; Index: lib/CodeGen/AsmPrinter/DwarfExpression.cpp =================================================================== --- lib/CodeGen/AsmPrinter/DwarfExpression.cpp +++ lib/CodeGen/AsmPrinter/DwarfExpression.cpp @@ -357,7 +357,14 @@ if (OpNum >= dwarf::DW_OP_lit0 && OpNum <= dwarf::DW_OP_lit31) { emitOp(OpNum); continue; + } else if (OpNum >= dwarf::DW_OP_reg0 && OpNum <= dwarf::DW_OP_reg31) { + if (isLocationParamExpr()) + addBReg(OpNum - dwarf::DW_OP_reg0, 0); + else + emitOp(OpNum); + continue; } + switch (OpNum) { case dwarf::DW_OP_LLVM_fragment: { unsigned SizeInBits = Op->getArg(1); @@ -447,6 +454,16 @@ case dwarf::DW_OP_GNU_entry_value: emitOp(dwarf::DW_OP_GNU_entry_value); break; + case dwarf::DW_OP_regx: + if (isLocationParamExpr()) { + emitOp(dwarf::DW_OP_bregx); + emitUnsigned(Op->getArg(0)); + emitSigned(Op->getArg(1)); + } else { + emitOp(dwarf::DW_OP_regx); + emitUnsigned(Op->getArg(0)); + } + break; default: llvm_unreachable("unhandled opcode found in expression"); } Index: lib/CodeGen/TargetInstrInfo.cpp =================================================================== --- lib/CodeGen/TargetInstrInfo.cpp +++ lib/CodeGen/TargetInstrInfo.cpp @@ -23,6 +23,7 @@ #include "llvm/CodeGen/TargetRegisterInfo.h" #include "llvm/CodeGen/TargetSchedule.h" #include "llvm/IR/DataLayout.h" +#include "llvm/IR/DebugInfoMetadata.h" #include "llvm/MC/MCAsmInfo.h" #include "llvm/MC/MCInstrItineraries.h" #include "llvm/Support/CommandLine.h" @@ -1110,6 +1111,26 @@ return (DefCycle != -1 && DefCycle <= 1); } +bool TargetInstrInfo::describeLoadedLocation(const MachineInstr &MI, + const MachineOperand *&Op, + DIExpression **Expr) const { + int FI; + if (isLoadFromStackSlot(MI, FI)) { + for (unsigned i = 0; i < MI.getNumOperands(); i++) { + const MachineOperand &OpI = MI.getOperand(i); + if (OpI.isFI()) { + Op = &OpI; + break; + } + } + const MachineFunction *MF = MI.getMF(); + *Expr = + DIExpression::get(MF->getFunction().getContext(), {dwarf::DW_OP_deref}); + return true; + } + return false; +} + /// Both DefMI and UseMI must be valid. By default, call directly to the /// itinerary. This may be overriden by the target. int TargetInstrInfo::getOperandLatency(const InstrItineraryData *ItinData, Index: lib/CodeGen/VirtRegMap.cpp =================================================================== --- lib/CodeGen/VirtRegMap.cpp +++ lib/CodeGen/VirtRegMap.cpp @@ -36,6 +36,7 @@ #include "llvm/CodeGen/TargetRegisterInfo.h" #include "llvm/CodeGen/TargetSubtargetInfo.h" #include "llvm/Config/llvm-config.h" +#include "llvm/IR/DebugInfoMetadata.h" #include "llvm/MC/LaneBitmask.h" #include "llvm/Pass.h" #include "llvm/Support/Compiler.h" @@ -498,10 +499,77 @@ assert(MI.isDebugCallSiteParam() && "This method handles only call" && "site params"); + const unsigned TransferingReg = MI.getOperand(0).getReg(); + MachineInstr *CallSite = nullptr; + + auto isValidCandidate = [&](const MachineInstr &MI) { + if (!MI.getNumOperands()) + return false; + return MI.getOperand(0).isReg() && MI.getOperand(0).getReg() && + TRI->regsOverlap(MI.getOperand(0).getReg(), TransferingReg); + }; + + auto RIt = std::next(MI.getReverseIterator()); + do { + if (RIt->isDebugCallSite()) + CallSite = &*RIt; + + if (isValidCandidate(*RIt)) + break; + RIt++; + } while (RIt != MI.getParent()->rend()); + + // We didn't manage to find instruction that loads searched register. + // It must be loaded in some of the previous BB. Maybe extend search + // to just one more previous block? + if (RIt != MI.getParent()->rend()) { + assert( + CallSite && + "There must be call site instr before call site parameter instruction"); + + LLVM_DEBUG( + dbgs() << " trying to recalculate following instruction with dbg info\n" + << MI << " attempting to interpret RHS from instruction " << *RIt + << "\n"); + // Now that we have found instruction that loads parameter carrying register + // try to interpret it and create call site parameter description. + DIExpression *Expr = nullptr; + const MachineOperand *Op = nullptr; + if (TII->describeLoadedLocation(*RIt, Op, &Expr)) { + // TODO: Maybe add check that new description is valid at point of + // instruction MI. There may be need to check whether location + // is not clobbered in range between RIt and MI. + unsigned Reg = MI.getOperand(0).getReg(); + auto *CSP = MI.getDebugCallSiteParam(); + DebugLoc DL = MI.getDebugLoc(); + MachineFunction *MF = MI.getMF(); + MachineInstr *NewInstr; + + if (Op->isReg()) + NewInstr = BuildMI(*MF, DL, MI.getDesc()) + .addReg(Reg, getDebugRegState(true)) + .addMetadata(CSP) + .addReg(Op->getReg()) + .addImm(0) + .addMetadata(Expr); + else + NewInstr = BuildMI(*MF, DL, MI.getDesc()) + .addReg(Reg, getDebugRegState(true)) + .addMetadata(CSP) + .addFrameIndex(Op->getIndex()) + .addImm(0) + .addMetadata(Expr); + + MI.eraseFromBundle(); + MIBundleBuilder(CallSite).append(NewInstr); + return; + } + } + // If we have overlapping registers check whether backup location // description based on variable and expression over variable exists. // If not, delete this call site parameter location. - if(invalidateParamLocation(&MI)) + if (invalidateParamLocation(&MI)) NumCSDeleted++; } Index: lib/IR/DebugInfoMetadata.cpp =================================================================== --- lib/IR/DebugInfoMetadata.cpp +++ lib/IR/DebugInfoMetadata.cpp @@ -854,6 +854,7 @@ case dwarf::DW_OP_constu: case dwarf::DW_OP_consts: case dwarf::DW_OP_plus_uconst: + case dwarf::DW_OP_regx: return 2; default: return 1; @@ -867,8 +868,10 @@ return false; uint64_t Op = I->getOp(); - if (Op >= dwarf::DW_OP_lit0 && Op <= dwarf::DW_OP_lit31) + if ((Op >= dwarf::DW_OP_lit0 && Op <= dwarf::DW_OP_lit31) || + (Op >= dwarf::DW_OP_reg0 && Op <= dwarf::DW_OP_reg31)) continue; + // Check that the operand is valid. switch (Op) { default: @@ -918,6 +921,7 @@ case dwarf::DW_OP_GNU_entry_value: case dwarf::DW_OP_not: case dwarf::DW_OP_dup: + case dwarf::DW_OP_regx: break; } } Index: lib/Target/X86/X86InstrInfo.h =================================================================== --- lib/Target/X86/X86InstrInfo.h +++ lib/Target/X86/X86InstrInfo.h @@ -560,6 +560,10 @@ #define GET_INSTRINFO_HELPER_DECLS #include "X86GenInstrInfo.inc" + bool describeLoadedLocation(const MachineInstr &MI, + const MachineOperand *&Op, + DIExpression **Expr) const override; + protected: /// Commutes the operands in the given instruction by changing the operands /// order and/or changing the instruction's opcode and/or the immediate value Index: lib/Target/X86/X86InstrInfo.cpp =================================================================== --- lib/Target/X86/X86InstrInfo.cpp +++ lib/Target/X86/X86InstrInfo.cpp @@ -30,7 +30,7 @@ #include "llvm/CodeGen/StackMaps.h" #include "llvm/IR/DerivedTypes.h" #include "llvm/IR/Function.h" -#include "llvm/IR/LLVMContext.h" +#include "llvm/IR/DebugInfoMetadata.h" #include "llvm/MC/MCAsmInfo.h" #include "llvm/MC/MCExpr.h" #include "llvm/MC/MCInst.h" @@ -7349,6 +7349,112 @@ } } +bool X86InstrInfo::describeLoadedLocation(const MachineInstr &MI, + const MachineOperand *&Op, + DIExpression **Expr) const { + const MachineFunction *MF = MI.getParent()->getParent(); + CalleeSavedRegChecker CalleeSavedRegs(MF); + switch (MI.getOpcode()) { + case X86::LEA32r: + case X86::LEA64r: + case X86::LEA64_32r: { + // Operand 4 could be global address. For now we do not support + // such situation. + if (!MI.getOperand(4).isImm() || !MI.getOperand(2).isImm()) + return false; + + const MachineOperand &Op1 = MI.getOperand(1); + const MachineOperand &Op2 = MI.getOperand(3); + const TargetRegisterInfo *TRI = &getRegisterInfo(); + assert(Op2.isReg() && + (Op2.getReg() == X86::NoRegister || + TargetRegisterInfo::isPhysicalRegister(Op2.getReg()))); + + // Currently, this function is used for DBG_CALLSITEPARAM salvaging + // and there is no use of describing previous location that is loaded + // into register (call clobbered register) when previous location + // description contains loading register itself. With condition + // bellow we are omitting situations like: + // %rsi = lea %rsi, 4, ..... + // This could be improved by looking further back into basic block + // to find other instruction that loads something into target register + // and try to interpret that instruction recursively. + if ((Op1.isReg() && Op1.getReg() == MI.getOperand(0).getReg()) || + Op2.getReg() == MI.getOperand(0).getReg()) + return false; + else if ((Op1.isReg() && Op1.getReg() != X86::NoRegister && + TRI->regsOverlap(Op1.getReg(), MI.getOperand(0).getReg())) || + (Op2.getReg() != X86::NoRegister && + TRI->regsOverlap(Op2.getReg(), MI.getOperand(0).getReg()))) + return false; + + int64_t Coef = MI.getOperand(2).getImm(); + int64_t Offset = MI.getOperand(4).getImm(); + SmallVector Elements; + + if ((Op1.isReg() && Op1.getReg() != X86::NoRegister)) { + if (CalleeSavedRegs.isCalleeSaved(Op1.getReg())) + Op = &Op1; + else + return false; + } else if (Op1.isFI()) + Op = &Op1; + + if (Op && Op->isReg() && Op->getReg() == Op2.getReg() && Coef > 0) { + Elements.push_back(dwarf::DW_OP_constu); + Elements.push_back(Coef + 1); + Elements.push_back(dwarf::DW_OP_mul); + } else { + if (Op && Op2.getReg() != X86::NoRegister) { + if(!CalleeSavedRegs.isCalleeSaved(Op2.getReg())) + return false; + int dwarfReg = TRI->getDwarfRegNum(Op2.getReg(), false); + if (dwarfReg < 0) + return false; + else if (dwarfReg < 32) + Elements.push_back(dwarf::DW_OP_reg0 + dwarfReg); + else { + Elements.push_back(dwarf::DW_OP_regx); + Elements.push_back(dwarfReg); + } + } else if (!Op) { + assert(Op2.getReg() != X86::NoRegister); + if (!CalleeSavedRegs.isCalleeSaved(Op2.getReg())) + return false; + Op = &Op2; + } + + if (Coef > 1) { + assert(Op2.getReg() != X86::NoRegister); + Elements.push_back(dwarf::DW_OP_constu); + Elements.push_back(Coef); + Elements.push_back(dwarf::DW_OP_mul); + } + + if (((Op1.isReg() && Op1.getReg() != X86::NoRegister) || Op1.isFI()) && + Op2.getReg() != X86::NoRegister) { + Elements.push_back(dwarf::DW_OP_plus); + } + } + + if (Offset < 0) { + Elements.push_back(dwarf::DW_OP_constu); + Elements.push_back(-Offset); + Elements.push_back(dwarf::DW_OP_minus); + } else if (Offset > 0) { + Elements.push_back(dwarf::DW_OP_constu); + Elements.push_back(Offset); + Elements.push_back(dwarf::DW_OP_plus); + } + + *Expr = DIExpression::get(MI.getMF()->getFunction().getContext(), Elements); + return true; + } + default: + return TargetInstrInfo::describeLoadedLocation(MI, Op, Expr); + } +} + /// This is an architecture-specific helper function of reassociateOps. /// Set special operand attributes for new instructions after reassociation. void X86InstrInfo::setSpecialOperandAttr(MachineInstr &OldMI1, Index: test/DebugInfo/MIR/X86/dbgcs-stack-obj-load-salvage.mir =================================================================== --- /dev/null +++ test/DebugInfo/MIR/X86/dbgcs-stack-obj-load-salvage.mir @@ -0,0 +1,218 @@ +# RUN: llc -mtriple=x86_64-unknown-unknown -start-before=greedy -stop-after=virtregrewriter %s -o -| FileCheck %s +# Validate that base method describeLoadedLocation from Target Instr Info for stack loaded objects works. +# CHECK: DBG_CALLSITEPARAM $edi, !38, %stack.0.a, 0, !DIExpression(DW_OP_deref) +--- | + ; ModuleID = 'dbgcs-stack-obj-load-salvage.ll' + source_filename = "dbgcs-stack-obj-load-salvage.c" + target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" + target triple = "x86_64-unknown-linux-gnu" + + define void @fn2(i32 %a) local_unnamed_addr !dbg !6 { + entry: + %a.addr = alloca i32, align 4 + tail call void @llvm.dbg.value(metadata i32 %a, i64 0, metadata !11, metadata !DIExpression()), !dbg !20 + store i32 %a, i32* %a.addr, align 4, !tbaa !21 + tail call void @fn3(i32 %a), !dbg !20, !CallSite !12 + tail call void @llvm.dbg.value(metadata i32* %a.addr, i64 0, metadata !11, metadata !DIExpression()), !dbg !20 + call void @fn4(i32* nonnull %a.addr), !dbg !20, !CallSite !16 + ret void, !dbg !20 + } + + declare void @fn3(i32) local_unnamed_addr + + declare void @fn4(i32*) local_unnamed_addr + + define i32 @fn1(i32 %x, i32 %y) local_unnamed_addr !dbg !25 { + entry: + %a = alloca i32, align 4 + tail call void @llvm.dbg.value(metadata i32 %x, i64 0, metadata !30, metadata !DIExpression()), !dbg !39 + tail call void @llvm.dbg.value(metadata i32 %y, i64 0, metadata !31, metadata !DIExpression()), !dbg !39 + %0 = bitcast i32* %a to i8*, !dbg !39 + call void @llvm.lifetime.start(i64 4, i8* nonnull %0), !dbg !39 + tail call void @llvm.dbg.value(metadata i32 7, i64 0, metadata !32, metadata !DIExpression()), !dbg !39 + store i32 7, i32* %a, align 4, !dbg !39, !tbaa !21 + tail call void @llvm.dbg.value(metadata i32* %a, i64 0, metadata !32, metadata !DIExpression()), !dbg !39 + call void @fn4(i32* nonnull %a), !dbg !39, !CallSite !33 + %1 = load i32, i32* %a, align 4, !dbg !39, !tbaa !21 + call void @llvm.dbg.value(metadata i32 %1, i64 0, metadata !32, metadata !DIExpression()), !dbg !39 + call void @fn2(i32 %1), !dbg !39, !CallSite !36 + call void @llvm.lifetime.end(i64 4, i8* nonnull %0), !dbg !39 + ret i32 0, !dbg !39 + } + + ; Function Attrs: argmemonly nounwind + declare void @llvm.lifetime.start(i64, i8* nocapture) #0 + + ; Function Attrs: argmemonly nounwind + declare void @llvm.lifetime.end(i64, i8* nocapture) #0 + + ; Function Attrs: nounwind readnone + declare void @llvm.dbg.value(metadata, i64, metadata, metadata) #1 + + ; Function Attrs: nounwind + declare void @llvm.stackprotector(i8*, i8**) #2 + + attributes #0 = { argmemonly nounwind } + attributes #1 = { nounwind readnone } + attributes #2 = { nounwind } + + !llvm.dbg.cu = !{!0} + !llvm.module.flags = !{!3, !4} + !llvm.ident = !{!5} + + !0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 4.0.0 ", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2) + !1 = !DIFile(filename: "dbgcs-stack-obj-load-salvage.c", directory: "/") + !2 = !{} + !3 = !{i32 2, !"Dwarf Version", i32 4} + !4 = !{i32 2, !"Debug Info Version", i32 3} + !5 = !{!"clang version 4.0.0"} + !6 = distinct !DISubprogram(name: "fn2", scope: !1, file: !1, line: 14, type: !7, isLocal: false, isDefinition: true, scopeLine: 15, flags: DIFlagPrototyped, isOptimized: true, unit: !0, retainedNodes: !10) + !7 = !DISubroutineType(types: !8) + !8 = !{null, !9} + !9 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) + !10 = !{!11, !12, !16} + !11 = !DILocalVariable(name: "a", arg: 1, scope: !6, file: !1, line: 14, type: !9, flags: DIFlagArgumentNotModified) + !12 = !DICallSite(scope: !6, parameters: !13, calledSubprogram: !15) + !13 = !{!14} + !14 = !DICallSiteParam(argno: 1, variable: !11, expr: !DIExpression()) + !15 = !DISubprogram(name: "fn3", scope: !1, file: !1, line: 9, isLocal: false, isDefinition: false, flags: DIFlagPrototyped, isOptimized: true, retainedNodes: !2) + !16 = !DICallSite(scope: !6, parameters: !17, calledSubprogram: !19) + !17 = !{!18} + !18 = !DICallSiteParam(argno: 1, variable: !11, expr: !DIExpression(DW_OP_push_object_address)) + !19 = !DISubprogram(name: "fn4", scope: !1, file: !1, line: 10, isLocal: false, isDefinition: false, flags: DIFlagPrototyped, isOptimized: true, retainedNodes: !2) + !20 = !DILocation(line: 14, column: 10, scope: !6) + !21 = !{!22, !22, i64 0} + !22 = !{!"int", !23, i64 0} + !23 = !{!"omnipotent char", !24, i64 0} + !24 = !{!"Simple C/C++ TBAA"} + !25 = distinct !DISubprogram(name: "fn1", scope: !1, file: !1, line: 22, type: !26, isLocal: false, isDefinition: true, scopeLine: 23, flags: DIFlagPrototyped, isOptimized: true, unit: !0, retainedNodes: !29) + !26 = !DISubroutineType(types: !27) + !27 = !{!28, !9, !9} + !28 = !DIDerivedType(tag: DW_TAG_const_type, baseType: !9) + !29 = !{!30, !31, !32, !33, !36} + !30 = !DILocalVariable(name: "x", arg: 1, scope: !25, file: !1, line: 22, type: !9, flags: DIFlagArgumentNotModified) + !31 = !DILocalVariable(name: "y", arg: 2, scope: !25, file: !1, line: 22, type: !9, flags: DIFlagArgumentNotModified) + !32 = !DILocalVariable(name: "a", scope: !25, file: !1, line: 24, type: !9, flags: DIFlagArgumentNotModified) + !33 = !DICallSite(scope: !25, parameters: !34, calledSubprogram: !19) + !34 = !{!35} + !35 = !DICallSiteParam(argno: 1, variable: !32, expr: !DIExpression(DW_OP_push_object_address)) + !36 = !DICallSite(scope: !25, parameters: !37, calledSubprogram: !6) + !37 = !{!38} + !38 = !DICallSiteParam(argno: 1, variable: !32, expr: !DIExpression()) + !39 = !DILocation(line: 22, column: 17, scope: !25) + +... +--- +name: fn2 +alignment: 4 +exposesReturnsTwice: false +legalized: false +regBankSelected: false +selected: false +tracksRegLiveness: true +registers: + - { id: 0, class: gr32 } + - { id: 1, class: gr64 } +liveins: + - { reg: '$edi', virtual-reg: '%0' } +frameInfo: + isFrameAddressTaken: false + isReturnAddressTaken: false + hasStackMap: false + hasPatchPoint: false + stackSize: 0 + offsetAdjustment: 0 + maxAlignment: 4 + adjustsStack: false + hasCalls: true + maxCallFrameSize: 0 + hasOpaqueSPAdjustment: false + hasVAStart: false + hasMustTailInVarArgFunc: false +stack: + - { id: 0, name: a.addr, offset: 0, size: 4, alignment: 4 } +body: | + bb.0.entry: + liveins: $edi + + DBG_VALUE debug-use $edi, debug-use $noreg, !11, !DIExpression(), debug-location !20 + %0 = COPY $edi + DBG_VALUE debug-use %0, debug-use $noreg, !11, !DIExpression(), debug-location !20 + MOV32mr %stack.0.a.addr, 1, $noreg, 0, $noreg, %0 :: (store 4 into %ir.a.addr, !tbaa !21) + ADJCALLSTACKDOWN64 0, 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp, debug-location !20 + $edi = COPY %0, debug-location !20 + CALL64pcrel32 @fn3, csr_64, implicit $rsp, implicit $ssp, implicit $edi, implicit-def $rsp, implicit-def $ssp, implicit-def $edi, debug-location !20 + DBG_CALLSITE 0, debug-use $noreg, !12, debug-location !20 { + DBG_CALLSITEPARAM debug-use $edi, !14, debug-use %0, debug-use $noreg, debug-location !20 + } + ADJCALLSTACKUP64 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp, debug-location !20 + ADJCALLSTACKDOWN64 0, 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp, debug-location !20 + %1 = LEA64r %stack.0.a.addr, 1, $noreg, 0, $noreg + DBG_VALUE debug-use %1, debug-use $noreg, !11, !DIExpression(), debug-location !20 + $rdi = COPY %1, debug-location !20 + CALL64pcrel32 @fn4, csr_64, implicit $rsp, implicit $ssp, implicit $rdi, implicit-def $rsp, implicit-def $ssp, implicit-def $rdi, debug-location !20 + DBG_CALLSITE 0, debug-use $noreg, !16, debug-location !20 { + DBG_CALLSITEPARAM debug-use $rdi, !18, %stack.0.a.addr, 0, debug-location !20 + } + ADJCALLSTACKUP64 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp, debug-location !20 + RET 0, debug-location !20 + +... +--- +name: fn1 +alignment: 4 +exposesReturnsTwice: false +legalized: false +regBankSelected: false +selected: false +tracksRegLiveness: true +registers: + - { id: 0, class: gr32 } + - { id: 1, class: gr32 } + - { id: 2, class: gr64 } + - { id: 3, class: gr32 } + - { id: 4, class: gr32 } +frameInfo: + isFrameAddressTaken: false + isReturnAddressTaken: false + hasStackMap: false + hasPatchPoint: false + stackSize: 0 + offsetAdjustment: 0 + maxAlignment: 4 + adjustsStack: false + hasCalls: true + maxCallFrameSize: 0 + hasOpaqueSPAdjustment: false + hasVAStart: false + hasMustTailInVarArgFunc: false +stack: + - { id: 0, name: a, offset: 0, size: 4, alignment: 4 } +body: | + bb.0.entry: + DBG_VALUE debug-use $edi, debug-use $noreg, !30, !DIExpression(), debug-location !39 + DBG_VALUE debug-use $esi, debug-use $noreg, !31, !DIExpression(), debug-location !39 + DBG_VALUE 7, debug-use $noreg, !32, !DIExpression(), debug-location !39 + MOV32mi %stack.0.a, 1, $noreg, 0, $noreg, 7, debug-location !39 :: (store 4 into %ir.a, !tbaa !21) + ADJCALLSTACKDOWN64 0, 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp, debug-location !39 + %2 = LEA64r %stack.0.a, 1, $noreg, 0, $noreg + DBG_VALUE debug-use %2, debug-use $noreg, !32, !DIExpression(), debug-location !39 + $rdi = COPY %2, debug-location !39 + CALL64pcrel32 @fn4, csr_64, implicit $rsp, implicit $ssp, implicit killed $rdi, implicit-def $rsp, implicit-def $ssp, implicit-def $rdi, debug-location !39 + DBG_CALLSITE 0, debug-use $noreg, !33, debug-location !39 { + DBG_CALLSITEPARAM debug-use $rdi, !35, %stack.0.a, 0, debug-location !39 + } + ADJCALLSTACKUP64 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp, debug-location !39 + %3 = MOV32rm %stack.0.a, 1, $noreg, 0, $noreg, debug-location !39 :: (dereferenceable load 4 from %ir.a, !tbaa !21) + DBG_VALUE debug-use %3, debug-use $noreg, !32, !DIExpression(), debug-location !39 + ADJCALLSTACKDOWN64 0, 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp, debug-location !39 + $edi = COPY %3, debug-location !39 + CALL64pcrel32 @fn4, csr_64, implicit $rsp, implicit $ssp, implicit killed $edi, implicit-def $rsp, implicit-def $ssp, implicit-def $edi, debug-location !39 + DBG_CALLSITE 0, debug-use $noreg, !36, debug-location !39 { + DBG_CALLSITEPARAM debug-use $edi, !38, debug-use %3, debug-use $noreg, debug-location !39 + } + ADJCALLSTACKUP64 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp, debug-location !39 + $eax = MOV32r0 implicit-def dead $eflags, debug-location !39 + RET 0, killed $eax, debug-location !39 + +... Index: test/DebugInfo/MIR/X86/dbginfo-lea-salvaging.mir =================================================================== --- /dev/null +++ test/DebugInfo/MIR/X86/dbginfo-lea-salvaging.mir @@ -0,0 +1,252 @@ +# RUN: llc -mtriple=x86_64-unknown-unknown -start-before=greedy -stop-after=virtregrewriter %s -o -| FileCheck %s +# This tests salvaging information from LEA instructions. Test hist part of code that executes +# in Virtual Register Rewriter when we come accros situation that newly register in +# DBG_CALLSITEPARAM instruction overlaps with register that should carry parameter value. +# +# First function call test following test cases +# LEA REG1, 1, REG1, x +# LEA REG1, 1, REG2, x +# LEA REG1, coef, REG2, x +# LEA REG1, 1, noreg, x +# LEA REG1, coef, REG1, x +# We shold have parameter loading instruction array +# CHECK: $rdi = LEA64r $r15, 1, $r15, 0, $noreg +# CHECK-NEXT: $rsi = LEA64r $r14, 1, $r15, 0, $noreg +# CHECK-NEXT: $rdx = LEA64r $r13, 5, $r12, 10, $noreg +# CHECK-NEXT: $rcx = LEA64r $r12, 1, $noreg, 0, $noreg +# CHECK-NEXT: $r8 = LEA64r $r13, 4, $r13, 0, $noreg +# CHECK-NEXT: $rbp = LEA64r $r15, 2, $r12, 0, $noreg +# CHECK-NEXT: $r9 = COPY renamable $rbp, +# CHECK: DBG_CALLSITE +# CHECK-NEXT: DBG_CALLSITEPARAM $r9, {{.*}}, renamable $rbp, 0, +# CHECK-NEXT: DBG_CALLSITEPARAM $rdi, {{.*}}, $r15, 0, !DIExpression(DW_OP_constu, 2, DW_OP_mul) +# CHECK-NEXT: DBG_CALLSITEPARAM $rsi, {{.*}}, $r14, 0, !DIExpression(DW_OP_reg15, DW_OP_plus) +# CHECK-NEXT: DBG_CALLSITEPARAM $rdx, {{.*}}, $r13, 0, !DIExpression(DW_OP_reg12, DW_OP_constu, 5, DW_OP_mul, DW_OP_plus, DW_OP_constu, 10, DW_OP_plus) +# CHECK-NEXT: DBG_CALLSITEPARAM $rcx, {{.*}}, $r12, 0, !DIExpression() +# CHECK-NEXT: DBG_CALLSITEPARAM $r8, {{.*}}, $r13, 0, !DIExpression(DW_OP_constu, 5, DW_OP_mul) +# Second function call test following test cases +# LEA noreg, 1, REG2, x +# LEA noreg, coef, REG2, x +# LEA FI#, 1, noreg, x +# LEA FI#, coef, REG2, x +# LEA FI#, 1, REG2, x +# CHECK: $r9 = LEA64r $noreg, 1, $r15, 10, $noreg +# CHECK-NEXT: $r8 = LEA64r $noreg, 5, $r14, 11, $noreg +# CHECK-NEXT: $rcx = LEA64r %stack.2.arg3.addr, 1, $noreg, 0, $noreg +# CHECK-NEXT: $rdx = LEA64r %stack.3.arg4.addr, 4, $r12, 4, $noreg +# CHECK-NEXT: $rsi = LEA64r %stack.4.arg5.addr, 1, $r14, 14, $noreg +# CHECK-NEXT: $rdi = COPY killed renamable $rbp, debug-location !28 +# CHECK: DBG_CALLSITE +# CHECK-NEXT: DBG_CALLSITEPARAM $rdi, !13, %stack.5.arg6.addr, 0, +# CHECK-NEXT: DBG_CALLSITEPARAM $rsi, !14, %stack.4.arg5.addr, 0, !DIExpression(DW_OP_reg14, DW_OP_plus, DW_OP_constu, 14, DW_OP_plus) +# CHECK-NEXT: DBG_CALLSITEPARAM $rdx, !15, %stack.3.arg4.addr, 0, !DIExpression(DW_OP_reg12, DW_OP_constu, 4, DW_OP_mul, DW_OP_plus, DW_OP_constu, 4, DW_OP_plus) +# CHECK-NEXT: DBG_CALLSITEPARAM $rcx, !16, %stack.2.arg3.addr, 0, !DIExpression() +# CHECK-NEXT: DBG_CALLSITEPARAM $r8, !17, $r14, 0, !DIExpression(DW_OP_constu, 5, DW_OP_mul, DW_OP_constu, 11, DW_OP_plus) +# CHECK-NEXT: DBG_CALLSITEPARAM $r9, !18, $r15, 0, !DIExpression(DW_OP_constu, 10, DW_OP_plus) + +--- | + ; ModuleID = 'dbginfo-lea-salvaging.ll' + source_filename = "dbginfo-lea-salvaging.c" + target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" + target triple = "x86_64-unknown-linux-gnu" + + define void @foo(i32 %arg1, i32 %arg2, i32 %arg3, i32 %arg4, i32 %arg5, i32 %arg6) local_unnamed_addr !dbg !6 { + entry: + %arg1.addr = alloca i32, align 4 + %arg2.addr = alloca i32, align 4 + %arg3.addr = alloca i32, align 4 + %arg4.addr = alloca i32, align 4 + %arg5.addr = alloca i32, align 4 + %arg6.addr = alloca i32, align 4 + store i32 %arg2, i32* %arg2.addr, align 4, !tbaa !34 + store i32 %arg3, i32* %arg3.addr, align 4, !tbaa !34 + store i32 %arg4, i32* %arg4.addr, align 4, !tbaa !34 + store i32 %arg5, i32* %arg5.addr, align 4, !tbaa !34 + store i32 %arg6, i32* %arg6.addr, align 4, !tbaa !34 + %shl = shl i32 %arg1, 2, !dbg !38 + store i32 %shl, i32* %arg1.addr, align 4, !dbg !39, !tbaa !34 + call void @baa(i32* nonnull %arg1.addr, i32* nonnull %arg2.addr, i32* nonnull %arg3.addr, i32* nonnull %arg4.addr, i32* nonnull %arg5.addr, i32* nonnull %arg6.addr), !dbg !40, !CallSite !17 + call void @baa(i32* nonnull %arg6.addr, i32* nonnull %arg5.addr, i32* nonnull %arg4.addr, i32* nonnull %arg3.addr, i32* nonnull %arg2.addr, i32* nonnull %arg1.addr), !dbg !41, !CallSite !26 + ret void, !dbg !42 + } + + declare void @baa(i32*, i32*, i32*, i32*, i32*, i32*) local_unnamed_addr + + ; Function Attrs: nounwind + declare void @llvm.stackprotector(i8*, i8**) #0 + + attributes #0 = { nounwind } + + !llvm.dbg.cu = !{!0} + !llvm.module.flags = !{!3, !4} + !llvm.ident = !{!5} + + !0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 4.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2) + !1 = !DIFile(filename: "dbginfo-lea-salvaging.c", directory: "/") + !2 = !{} + !3 = !{i32 2, !"Dwarf Version", i32 4} + !4 = !{i32 2, !"Debug Info Version", i32 3} + !5 = !{!"clang version 4.0.0"} + !6 = distinct !DISubprogram(name: "foo", scope: !1, file: !1, line: 10, type: !7, isLocal: false, isDefinition: true, scopeLine: 10, flags: DIFlagPrototyped, isOptimized: true, unit: !0, retainedNodes: !10) + !7 = !DISubroutineType(types: !8) + !8 = !{null, !9, !9, !9, !9, !9, !9} + !9 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) + !10 = !{!17, !26} + !17 = !DICallSite(scope: !6, file: !1, parameters: !18, line: 12, calledSubprogram: !25) + !18 = !{!19, !20, !21, !22, !23, !24} + !19 = !DICallSiteParam(argno: 1, expr: !DIExpression()) + !20 = !DICallSiteParam(argno: 2, expr: !DIExpression()) + !21 = !DICallSiteParam(argno: 3, expr: !DIExpression()) + !22 = !DICallSiteParam(argno: 4, expr: !DIExpression()) + !23 = !DICallSiteParam(argno: 5, expr: !DIExpression()) + !24 = !DICallSiteParam(argno: 6, expr: !DIExpression()) + !25 = !DISubprogram(name: "baa", scope: !1, file: !1, line: 8, isLocal: false, isDefinition: false, flags: DIFlagPrototyped, isOptimized: true, retainedNodes: !2) + !26 = !DICallSite(scope: !6, file: !1, parameters: !27, line: 13, calledSubprogram: !25) + !27 = !{!28, !29, !30, !31, !32, !33} + !28 = !DICallSiteParam(argno: 1, expr: !DIExpression()) + !29 = !DICallSiteParam(argno: 2, expr: !DIExpression()) + !30 = !DICallSiteParam(argno: 3, expr: !DIExpression()) + !31 = !DICallSiteParam(argno: 4, expr: !DIExpression()) + !32 = !DICallSiteParam(argno: 5, expr: !DIExpression()) + !33 = !DICallSiteParam(argno: 6, expr: !DIExpression()) + !34 = !{!35, !35, i64 0} + !35 = !{!"int", !36, i64 0} + !36 = !{!"omnipotent char", !37, i64 0} + !37 = !{!"Simple C/C++ TBAA"} + !38 = !DILocation(line: 11, column: 15, scope: !6) + !39 = !DILocation(line: 11, column: 8, scope: !6) + !40 = !DILocation(line: 12, column: 3, scope: !6) + !41 = !DILocation(line: 13, column: 3, scope: !6) + !42 = !DILocation(line: 14, column: 1, scope: !6) + +... +--- +name: foo +alignment: 4 +exposesReturnsTwice: false +legalized: false +regBankSelected: false +selected: false +tracksRegLiveness: true +registers: + - { id: 0, class: gr32 } + - { id: 1, class: gr32, preferred-register: '$esi' } + - { id: 2, class: gr32, preferred-register: '$edx' } + - { id: 3, class: gr32, preferred-register: '$ecx' } + - { id: 4, class: gr32, preferred-register: '$r8d' } + - { id: 5, class: gr32, preferred-register: '$r9d' } + - { id: 6, class: gr32, preferred-register: '$edi' } + - { id: 7, class: gr64, preferred-register: '$rdi' } + - { id: 8, class: gr64, preferred-register: '$rsi' } + - { id: 9, class: gr64, preferred-register: '$rdx' } + - { id: 10, class: gr64, preferred-register: '$rcx' } + - { id: 11, class: gr64, preferred-register: '$r8' } + - { id: 12, class: gr64, preferred-register: '$r9' } + - { id: 13, class: gr64, preferred-register: '$r9' } + - { id: 14, class: gr64, preferred-register: '$r8' } + - { id: 15, class: gr64, preferred-register: '$rcx' } + - { id: 16, class: gr64, preferred-register: '$rdx' } + - { id: 17, class: gr64, preferred-register: '$rsi' } +liveins: + - { reg: '$edi', virtual-reg: '%0' } + - { reg: '$esi', virtual-reg: '%1' } + - { reg: '$edx', virtual-reg: '%2' } + - { reg: '$ecx', virtual-reg: '%3' } + - { reg: '$r8d', virtual-reg: '%4' } + - { reg: '$r9d', virtual-reg: '%5' } +frameInfo: + isFrameAddressTaken: false + isReturnAddressTaken: false + hasStackMap: false + hasPatchPoint: false + stackSize: 0 + offsetAdjustment: 0 + maxAlignment: 4 + adjustsStack: false + hasCalls: true + maxCallFrameSize: 0 + hasOpaqueSPAdjustment: false + hasVAStart: false + hasMustTailInVarArgFunc: false +stack: + - { id: 0, name: arg1.addr, offset: 0, size: 4, alignment: 4 } + - { id: 1, name: arg2.addr, offset: 0, size: 4, alignment: 4 } + - { id: 2, name: arg3.addr, offset: 0, size: 4, alignment: 4 } + - { id: 3, name: arg4.addr, offset: 0, size: 4, alignment: 4 } + - { id: 4, name: arg5.addr, offset: 0, size: 4, alignment: 4 } + - { id: 5, name: arg6.addr, offset: 0, size: 4, alignment: 4 } +body: | + bb.0.entry: + liveins: $edi, $esi, $edx, $ecx, $r8d, $r9d + + %5 = COPY $r9d + %4 = COPY $r8d + %3 = COPY $ecx + %2 = COPY $edx + %1 = COPY $esi + %6 = COPY $edi + MOV32mr %stack.1.arg2.addr, 1, $noreg, 0, $noreg, %1 :: (store 4 into %ir.arg2.addr, !tbaa !34) + MOV32mr %stack.2.arg3.addr, 1, $noreg, 0, $noreg, %2 :: (store 4 into %ir.arg3.addr, !tbaa !34) + MOV32mr %stack.3.arg4.addr, 1, $noreg, 0, $noreg, %3 :: (store 4 into %ir.arg4.addr, !tbaa !34) + MOV32mr %stack.4.arg5.addr, 1, $noreg, 0, $noreg, %4 :: (store 4 into %ir.arg5.addr, !tbaa !34) + MOV32mr %stack.5.arg6.addr, 1, $noreg, 0, $noreg, %5 :: (store 4 into %ir.arg6.addr, !tbaa !34) + %6 = SHL32ri %6, 2, implicit-def dead $eflags, debug-location !38 + MOV32mr %stack.0.arg1.addr, 1, $noreg, 0, $noreg, %6, debug-location !39 :: (store 4 into %ir.arg1.addr, !tbaa !34) + ADJCALLSTACKDOWN64 0, 0, 0, implicit-def dead $rsp, implicit-def dead $ssp, implicit-def dead $eflags, implicit $rsp, implicit $ssp, debug-location !40 + $r15 = MOV64rr $rdi + $r14 = MOV64rr $rsi + $r12 = MOV64rr $rcx + $r13 = MOV64rr $r8 + $rbx = MOV64rr $r9 + %7 = LEA64r $r15, 1, $r15, 0, $noreg + %8 = LEA64r $r14, 1, $r15, 0, $noreg + %9 = LEA64r $r13, 5, $r12, 10, $noreg + %10 = LEA64r $r12, 1, $noreg, 0, $noreg + %11 = LEA64r $r13, 4, $r13, 0, $noreg + %12 = LEA64r $r15, 2, $r12, 0, $noreg + $rdi = COPY %7, debug-location !40 + $rsi = COPY %8, debug-location !40 + $rdx = COPY %9, debug-location !40 + $rcx = COPY %10, debug-location !40 + $r8 = COPY %11, debug-location !40 + $r9 = COPY %12, debug-location !40 + CALL64pcrel32 @baa, csr_64, implicit $rsp, implicit $ssp, implicit $rdi, implicit $rsi, implicit $rdx, implicit $rcx, implicit $r8, implicit $r9, implicit-def $rsp, implicit-def $ssp, debug-location !40 + DBG_CALLSITE 0, debug-use $noreg, !17, debug-location !40 { + DBG_CALLSITEPARAM $rdi, !19, debug-use %7, 0, debug-location !40 + DBG_CALLSITEPARAM $rsi, !20, debug-use %8, 0, debug-location !40 + DBG_CALLSITEPARAM $rdx, !21, debug-use %9, 0, debug-location !40 + DBG_CALLSITEPARAM $rcx, !22, debug-use %10, 0, debug-location !40 + DBG_CALLSITEPARAM $r8, !23, debug-use %11, 0, debug-location !40 + DBG_CALLSITEPARAM $r9, !24, debug-use %12, 0, debug-location !40 + } + ADJCALLSTACKUP64 0, 0, implicit-def dead $rsp, implicit-def dead $ssp, implicit-def dead $eflags, implicit $rsp, implicit $ssp, debug-location !40 + $r15 = ADD64ri8 $r15, 8, implicit-def dead $eflags + $r14 = ADD64ri8 $r14, 9, implicit-def dead $eflags + $r13 = ADD64ri8 $r13, 3, implicit-def dead $eflags + $r12 = ADD64ri8 $r12, 5, implicit-def dead $eflags + $rbx = ADD64ri8 $rbx, 4, implicit-def dead $eflags + ADJCALLSTACKDOWN64 0, 0, 0, implicit-def dead $rsp, implicit-def dead $ssp, implicit-def dead $eflags, implicit $rsp, implicit $ssp, debug-location !41 + %13 = LEA64r $noreg, 1, $r15, 10, $noreg + %14 = LEA64r $noreg, 5, $r14, 11, $noreg + %15 = LEA64r %stack.2.arg3.addr, 1, $noreg, 0, $noreg + %16 = LEA64r %stack.3.arg4.addr, 4, $r12, 4, $noreg + %17 = LEA64r %stack.4.arg5.addr, 1, $r14, 14, $noreg + $rdi = COPY %12, debug-location !41 + $rsi = COPY %17, debug-location !41 + $rdx = COPY %16, debug-location !41 + $rcx = COPY %15, debug-location !41 + $r8 = COPY %14, debug-location !41 + $r9 = COPY %13, debug-location !41 + CALL64pcrel32 @baa, csr_64, implicit $rsp, implicit $ssp, implicit $rdi, implicit $rsi, implicit $rdx, implicit $rcx, implicit $r8, implicit $r9, implicit-def $rsp, implicit-def $ssp, debug-location !41 + DBG_CALLSITE 0, debug-use $noreg, !26, debug-location !41 { + DBG_CALLSITEPARAM $rdi, !28, %stack.5.arg6.addr, 0, debug-location !41 + DBG_CALLSITEPARAM $rsi, !29, debug-use %17, 0, debug-location !41 + DBG_CALLSITEPARAM $rdx, !30, debug-use %16, 0, debug-location !41 + DBG_CALLSITEPARAM $rcx, !31, debug-use %15, 0, debug-location !41 + DBG_CALLSITEPARAM $r8, !32, debug-use %14, 0, debug-location !41 + DBG_CALLSITEPARAM $r9, !33, debug-use %13, 0, debug-location !41 + } + ADJCALLSTACKUP64 0, 0, implicit-def dead $rsp, implicit-def dead $ssp, implicit-def dead $eflags, implicit $rsp, implicit $ssp, debug-location !41 + RET 0, debug-location !42 + +...