Index: llvm/include/llvm/CodeGen/TargetInstrInfo.h =================================================================== --- llvm/include/llvm/CodeGen/TargetInstrInfo.h +++ llvm/include/llvm/CodeGen/TargetInstrInfo.h @@ -1789,9 +1789,10 @@ } /// Produce the expression describing the \p MI loading a value into - /// the parameter's forwarding register. - virtual Optional - describeLoadedValue(const MachineInstr &MI) const; + /// the physical register \p Reg. This hook should only be used with + /// \p MIs belonging to VReg-less functions. + virtual Optional describeLoadedValue(const MachineInstr &MI, + Register Reg) const; private: unsigned CallFrameSetupOpcode, CallFrameDestroyOpcode; Index: llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp =================================================================== --- llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp +++ llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp @@ -595,7 +595,6 @@ Implicit.push_back(FwdReg); else Explicit.push_back(FwdReg); - break; } } } @@ -636,39 +635,33 @@ for (auto Reg : concat(ExplicitFwdRegDefs, ImplicitFwdRegDefs)) ForwardedRegWorklist.erase(Reg); - // The describeLoadedValue() hook currently does not have any information - // about which register it should describe in case of multiple defines, so - // for now we only handle instructions where a forwarded register is (at - // least partially) defined by the instruction's single explicit define. - if (I->getNumExplicitDefs() != 1 || ExplicitFwdRegDefs.empty()) - continue; - unsigned ParamFwdReg = ExplicitFwdRegDefs[0]; - - if (auto ParamValue = TII->describeLoadedValue(*I)) { - if (ParamValue->first.isImm()) { - int64_t Val = ParamValue->first.getImm(); - DbgValueLoc DbgLocVal(ParamValue->second, Val); - finishCallSiteParam(DbgLocVal, ParamFwdReg); - } else if (ParamValue->first.isReg()) { - Register RegLoc = ParamValue->first.getReg(); - // TODO: For now, there is no use of describing the value loaded into the - // register that is also the source registers (e.g. $r0 = add $r0, x). - if (ParamFwdReg == RegLoc) - continue; - - unsigned SP = TLI->getStackPointerRegisterToSaveRestore(); - Register FP = TRI->getFrameRegister(*MF); - bool IsSPorFP = (RegLoc == SP) || (RegLoc == FP); - if (TRI->isCalleeSavedPhysReg(RegLoc, *MF) || IsSPorFP) { - DbgValueLoc DbgLocVal(ParamValue->second, - MachineLocation(RegLoc, - /*IsIndirect=*/IsSPorFP)); + for (auto ParamFwdReg : ExplicitFwdRegDefs) { + if (auto ParamValue = TII->describeLoadedValue(*I, ParamFwdReg)) { + if (ParamValue->first.isImm()) { + int64_t Val = ParamValue->first.getImm(); + DbgValueLoc DbgLocVal(ParamValue->second, Val); finishCallSiteParam(DbgLocVal, ParamFwdReg); - // TODO: Add support for entry value plus an expression. - } else if (ShouldTryEmitEntryVals && - ParamValue->second->getNumElements() == 0) { - ForwardedRegWorklist.insert(RegLoc); - RegsForEntryValues[RegLoc] = ParamFwdReg; + } else if (ParamValue->first.isReg()) { + Register RegLoc = ParamValue->first.getReg(); + // TODO: For now, there is no use of describing the value loaded into the + // register that is also the source registers (e.g. $r0 = add $r0, x). + if (ParamFwdReg == RegLoc) + continue; + + unsigned SP = TLI->getStackPointerRegisterToSaveRestore(); + Register FP = TRI->getFrameRegister(*MF); + bool IsSPorFP = (RegLoc == SP) || (RegLoc == FP); + if (TRI->isCalleeSavedPhysReg(RegLoc, *MF) || IsSPorFP) { + DbgValueLoc DbgLocVal(ParamValue->second, + MachineLocation(RegLoc, + /*IsIndirect=*/IsSPorFP)); + finishCallSiteParam(DbgLocVal, ParamFwdReg); + // TODO: Add support for entry value plus an expression. + } else if (ShouldTryEmitEntryVals && + ParamValue->second->getNumElements() == 0) { + ForwardedRegWorklist.insert(RegLoc); + RegsForEntryValues[RegLoc] = ParamFwdReg; + } } } } Index: llvm/lib/CodeGen/TargetInstrInfo.cpp =================================================================== --- llvm/lib/CodeGen/TargetInstrInfo.cpp +++ llvm/lib/CodeGen/TargetInstrInfo.cpp @@ -1121,16 +1121,62 @@ } Optional -TargetInstrInfo::describeLoadedValue(const MachineInstr &MI) const { +TargetInstrInfo::describeLoadedValue(const MachineInstr &MI, + Register Reg) const { const MachineFunction *MF = MI.getMF(); + const TargetRegisterInfo *TRI = MF->getSubtarget().getRegisterInfo(); DIExpression *Expr = DIExpression::get(MF->getFunction().getContext(), {}); int64_t Offset; + // To simplify the sub-register handling, verify that we only need to + // consider physical registers. + assert(MF->getProperties().hasProperty( + MachineFunctionProperties::Property::NoVRegs)); + if (auto DestSrc = isCopyInstr(MI)) { - return ParamLoadedValue(*DestSrc->Source, Expr); + Register DestReg = DestSrc->Destination->getReg(); + const MachineOperand *Source = DestSrc->Source; + + // If the copy writes to the whole destination register, simply return the + // source register. + if (Reg == DestReg) + return ParamLoadedValue(*Source, Expr); + + // If the described register is a super-register of the destination + // register, and the instruction fully defines the register, assume it's a + // zero-extending copy. + if (TRI->isSuperRegisterEq(DestReg, Reg) && MI.definesRegister(Reg, TRI)) + return ParamLoadedValue(*Source, Expr); + + // If the described register is a sub-register of the destination register, + // return the corresponding sub-register in the source register (if such a + // register exists). + if (unsigned SubRegIdx = TRI->getSubRegIndex(DestReg, Reg)) + if (unsigned SrcSubReg = TRI->getSubReg(Source->getReg(), SubRegIdx)) + return ParamLoadedValue(MachineOperand::CreateReg(SrcSubReg, false), + Expr); + + return None; } else if (auto DestSrc = isAddImmediate(MI, Offset)) { + Register DestReg = DestSrc->Destination->getReg(); + const MachineOperand *Source = DestSrc->Source; + Expr = DIExpression::prepend(Expr, DIExpression::ApplyOffset, Offset); - return ParamLoadedValue(*DestSrc->Source, Expr); + + // If the add immediate writes to the whole destination register, simply + // return the source register plus the offset. + if (Reg == DestReg) + return ParamLoadedValue(*Source, Expr); + + // If the described register is a super-register of the destination + // register, and the instruction fully defines the register, assume it's a + // zero-extending add immediate. + if (TRI->isSuperRegisterEq(DestReg, Reg) && MI.definesRegister(Reg, TRI)) + return ParamLoadedValue(*Source, Expr); + + // TODO: Is there anything that can be done in cases where the described + // register is a sub-register of the destination register? + return None; } else if (MI.hasOneMemOperand()) { // Only describe memory which provably does not escape the function. As // described in llvm.org/PR43343, escaped memory may be clobbered by the @@ -1150,6 +1196,11 @@ if (!TII->getMemOperandWithOffset(MI, BaseOp, Offset, TRI)) return None; + assert(MI.getNumExplicitDefs() == 1 && + "Can currently only handle mem instructions with a single define"); + + // TODO: In what way do we need to take Reg into consideration here? + Expr = DIExpression::prepend(Expr, DIExpression::DerefAfter, Offset); return ParamLoadedValue(*BaseOp, Expr); } Index: llvm/lib/Target/AArch64/AArch64InstrInfo.h =================================================================== --- llvm/lib/Target/AArch64/AArch64InstrInfo.h +++ llvm/lib/Target/AArch64/AArch64InstrInfo.h @@ -268,8 +268,8 @@ Optional isAddImmediate(const MachineInstr &MI, int64_t &Offset) const override; - Optional - describeLoadedValue(const MachineInstr &MI) const override; + Optional describeLoadedValue(const MachineInstr &MI, + Register Reg) const override; #define GET_INSTRINFO_HELPER_DECLS #include "AArch64GenInstrInfo.inc" Index: llvm/lib/Target/AArch64/AArch64InstrInfo.cpp =================================================================== --- llvm/lib/Target/AArch64/AArch64InstrInfo.cpp +++ llvm/lib/Target/AArch64/AArch64InstrInfo.cpp @@ -5776,10 +5776,18 @@ } Optional -AArch64InstrInfo::describeLoadedValue(const MachineInstr &MI) const { +AArch64InstrInfo::describeLoadedValue(const MachineInstr &MI, + Register Reg) const { + const MachineFunction *MF = MI.getMF(); + const TargetRegisterInfo *TRI = MF->getSubtarget().getRegisterInfo(); switch (MI.getOpcode()) { case AArch64::MOVZWi: case AArch64::MOVZXi: + // MOVZWi may be used for producing zero-extended 32-bit immediates in + // 64-bit parameters, so we need to consider super-registers. + if (!TRI->isSuperRegisterEq(MI.getOperand(0).getReg(), Reg)) + return None; + if (!MI.getOperand(1).isImm()) return None; int64_t Immediate = MI.getOperand(1).getImm(); @@ -5787,7 +5795,7 @@ return ParamLoadedValue(MachineOperand::CreateImm(Immediate << Shift), nullptr); } - return TargetInstrInfo::describeLoadedValue(MI); + return TargetInstrInfo::describeLoadedValue(MI, Reg); } #define GET_INSTRINFO_HELPERS Index: llvm/lib/Target/X86/X86InstrInfo.h =================================================================== --- llvm/lib/Target/X86/X86InstrInfo.h +++ llvm/lib/Target/X86/X86InstrInfo.h @@ -522,8 +522,8 @@ return MI.getDesc().TSFlags & X86II::LOCK; } - Optional - describeLoadedValue(const MachineInstr &MI) const override; + Optional describeLoadedValue(const MachineInstr &MI, + Register Reg) const override; protected: /// Commutes the operands in the given instruction by changing the operands Index: llvm/lib/Target/X86/X86InstrInfo.cpp =================================================================== --- llvm/lib/Target/X86/X86InstrInfo.cpp +++ llvm/lib/Target/X86/X86InstrInfo.cpp @@ -7551,14 +7551,28 @@ } Optional -X86InstrInfo::describeLoadedValue(const MachineInstr &MI) const { +X86InstrInfo::describeLoadedValue(const MachineInstr &MI, Register Reg) const { + const MachineFunction *MF = MI.getMF(); const MachineOperand *Op = nullptr; DIExpression *Expr = nullptr; + const TargetRegisterInfo *TRI = &getRegisterInfo(); + + auto SignExtend = [&MF](unsigned FromBits, unsigned ToBits) { + SmallVector Ops( + {dwarf::DW_OP_LLVM_convert, FromBits, dwarf::DW_ATE_signed, + dwarf::DW_OP_LLVM_convert, ToBits, dwarf::DW_ATE_signed}); + return DIExpression::get(MF->getFunction().getContext(), Ops); + }; + switch (MI.getOpcode()) { case X86::LEA32r: case X86::LEA64r: case X86::LEA64_32r: { + // We may need to describe a 64-bit parameter with a 32-bit LEA. + if (!TRI->isSuperRegisterEq(MI.getOperand(0).getReg(), Reg)) + return None; + // Operand 4 could be global address. For now we do not support // such situation. if (!MI.getOperand(4).isImm() || !MI.getOperand(2).isImm()) @@ -7566,7 +7580,6 @@ const MachineOperand &Op1 = MI.getOperand(1); const MachineOperand &Op2 = MI.getOperand(3); - const TargetRegisterInfo *TRI = &getRegisterInfo(); assert(Op2.isReg() && (Op2.getReg() == X86::NoRegister || Register::isPhysicalRegister(Op2.getReg()))); @@ -7635,13 +7648,33 @@ case X86::MOV64ri32: return ParamLoadedValue(MI.getOperand(1), Expr); case X86::XOR32rr: { + // 64-bit parameters are zero-materialized using XOR32rr, so also consider + // super-registers. + if (!TRI->isSuperRegisterEq(MI.getOperand(0).getReg(), Reg)) + return None; if (MI.getOperand(1).getReg() == MI.getOperand(2).getReg()) return ParamLoadedValue(MachineOperand::CreateImm(0), Expr); return None; } + case X86::MOVSX64rr32: { + // We may need to describe the lower 32 bits of the MOVSX; for example, in + // cases like this: + // + // $ebx = [...] + // $rdi = MOVSX64rr32 $ebx + // $esi = MOV32rr $edi + if (!TRI->isSubRegisterEq(MI.getOperand(0).getReg(), Reg)) + return None; + + if (X86MCRegisterClasses[X86::GR64RegClassID].contains(Reg)) + Expr = SignExtend(32, 64); + else + Expr = DIExpression::get(MI.getMF()->getFunction().getContext(), {}); + return ParamLoadedValue(MI.getOperand(1), Expr); + } default: assert(!MI.isMoveImmediate() && "Unexpected MoveImm instruction"); - return TargetInstrInfo::describeLoadedValue(MI); + return TargetInstrInfo::describeLoadedValue(MI, Reg); } } Index: llvm/test/DebugInfo/MIR/AArch64/dbgcall-site-zext-addimm.mir =================================================================== --- /dev/null +++ llvm/test/DebugInfo/MIR/AArch64/dbgcall-site-zext-addimm.mir @@ -0,0 +1,118 @@ +# RUN: llc -start-after=livedebugvalues -debug-entry-values -filetype=obj -o - %s | llvm-dwarfdump - | FileCheck %s + +# Based on the following C reproducer: +# +# extern void callee(unsigned long); +# +# unsigned caller(unsigned p1, unsigned p2) { +# callee(p2 + 1); +# return p2; +# } + +--- | + target datalayout = "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128" + target triple = "aarch64" + + ; Function Attrs: nounwind + define i32 @caller(i32 %p1, i32 returned %p2) #0 !dbg !12 { + entry: + call void @llvm.dbg.value(metadata i32 %p1, metadata !17, metadata !DIExpression()), !dbg !19 + call void @llvm.dbg.value(metadata i32 %p2, metadata !18, metadata !DIExpression()), !dbg !19 + %add = add i32 %p2, 123, !dbg !20 + %conv = zext i32 %add to i64, !dbg !20 + tail call void @callee(i64 %conv), !dbg !20 + ret i32 %p2, !dbg !21 + } + + declare !dbg !4 dso_local void @callee(i64) local_unnamed_addr + + ; Function Attrs: nounwind readnone speculatable willreturn + declare void @llvm.dbg.value(metadata, metadata, metadata) #1 + + attributes #0 = { nounwind } + attributes #1 = { nounwind readnone speculatable willreturn } + + !llvm.dbg.cu = !{!0} + !llvm.module.flags = !{!8, !9, !10} + !llvm.ident = !{!11} + + !0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 10.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, retainedTypes: !3, nameTableKind: None) + !1 = !DIFile(filename: "pass.c", directory: "/") + !2 = !{} + !3 = !{!4} + !4 = !DISubprogram(name: "callee", scope: !1, file: !1, line: 1, type: !5, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized, retainedNodes: !2) + !5 = !DISubroutineType(types: !6) + !6 = !{null, !7} + !7 = !DIBasicType(name: "long unsigned int", size: 64, encoding: DW_ATE_unsigned) + !8 = !{i32 2, !"Dwarf Version", i32 5} + !9 = !{i32 2, !"Debug Info Version", i32 3} + !10 = !{i32 1, !"wchar_size", i32 4} + !11 = !{!"clang version 10.0.0"} + !12 = distinct !DISubprogram(name: "caller", scope: !1, file: !1, line: 3, type: !13, scopeLine: 3, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !16) + !13 = !DISubroutineType(types: !14) + !14 = !{!15, !15, !15} + !15 = !DIBasicType(name: "unsigned int", size: 32, encoding: DW_ATE_unsigned) + !16 = !{!17, !18} + !17 = !DILocalVariable(name: "p1", arg: 1, scope: !12, file: !1, line: 3, type: !15, flags: DIFlagArgumentNotModified) + !18 = !DILocalVariable(name: "p2", arg: 2, scope: !12, file: !1, line: 3, type: !15, flags: DIFlagArgumentNotModified) + !19 = !DILocation(line: 0, scope: !12) + !20 = !DILocation(line: 4, scope: !12) + !21 = !DILocation(line: 5, scope: !12) + +... +--- +name: caller +tracksRegLiveness: true +liveins: + - { reg: '$w1' } +stack: + - { id: 0, type: spill-slot, offset: -8, size: 8, alignment: 8, callee-saved-register: '$x19' } + - { id: 1, type: spill-slot, offset: -16, size: 8, alignment: 8, callee-saved-register: '$lr' } +callSites: + - { bb: 0, offset: 11, fwdArgRegs: + - { arg: 0, reg: '$x0' } } +body: | + bb.0.entry: + liveins: $w1, $x19, $lr + + DBG_VALUE $w0, $noreg, !17, !DIExpression(), debug-location !19 + DBG_VALUE $w1, $noreg, !18, !DIExpression(), debug-location !19 + early-clobber $sp = frame-setup STPXpre killed $lr, killed $x19, $sp, -2 :: (store 8 into %stack.1), (store 8 into %stack.0) + frame-setup CFI_INSTRUCTION def_cfa_offset 16 + frame-setup CFI_INSTRUCTION offset $w19, -8 + frame-setup CFI_INSTRUCTION offset $w30, -16 + $w19 = ORRWrs $wzr, killed $w1, 0 + renamable $w0 = ADDWri $w19, 123, 0, implicit-def $x0, debug-location !20 + DBG_VALUE $w0, $noreg, !17, !DIExpression(DW_OP_LLVM_entry_value, 1), debug-location !19 + DBG_VALUE $w19, $noreg, !18, !DIExpression(), debug-location !19 + DBG_VALUE $w19, $noreg, !18, !DIExpression(), debug-location !19 + BL @callee, csr_aarch64_aapcs, implicit-def dead $lr, implicit $sp, implicit killed $x0, implicit-def $sp, debug-location !20 + $w0 = ORRWrs $wzr, killed $w19, 0, debug-location !21 + early-clobber $sp, $lr, $x19 = frame-destroy LDPXpost $sp, 2, debug-location !21 :: (load 8 from %stack.1), (load 8 from %stack.0) + DBG_VALUE $w1, $noreg, !18, !DIExpression(DW_OP_LLVM_entry_value, 1), debug-location !19 + RET undef $lr, implicit killed $w0, debug-location !21 + +... + +# Verify that a call site value is produced for the 64-bit parameter whose +# value is produced by the zero-extending ADDWri. + +# CHECK: DW_TAG_call_site_parameter +# CHECK-NEXT: DW_AT_location (DW_OP_reg0 W0) +# CHECK-NEXT: DW_AT_call_value (DW_OP_breg19 W19+123) + +# To trigger this case, the MIR has been manually modified from: +# +# renamable $w0 = ADDWri $w1, 123, 0, implicit-def $x0, debug-location !20 +# $w19 = ORRWrs $wzr, killed $w1, 0 +# +# to: +# +# $w19 = ORRWrs $wzr, killed $w1, 0 +# renamable $w0 = ADDWri $w19, 123, 0, implicit-def $x0, debug-location !20 +# +# in order to make the parameter described by a preserved register, so that a +# call site value is produced. The call site framework should ideally be able +# to understand that the value of the scratch register $w1 is available in the +# preserved register $w19 during the call, and create a call site value using +# that register. Index: llvm/test/DebugInfo/MIR/X86/dbgcall-site-copy-super-sub.mir =================================================================== --- /dev/null +++ llvm/test/DebugInfo/MIR/X86/dbgcall-site-copy-super-sub.mir @@ -0,0 +1,184 @@ +# RUN: llc -debug-entry-values -start-after=livedebugvalues -filetype=obj %s -o -| llvm-dwarfdump -| FileCheck %s + +# Based on the following reproducer: +# +# #include +# +# extern uint32_t value32(void); +# extern uint64_t value64(void); +# extern void call32(uint32_t); +# extern void call64(uint64_t); +# +# uint32_t test_sub_reg() { +# uint32_t local = value32(); +# call64(local); +# return local; +# } +# +# uint64_t test_super_reg() { +# uint64_t local = value64(); +# call32(local); +# return local; +# } + +--- | + ; ModuleID = 'ext.c' + source_filename = "ext.c" + target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" + target triple = "x86_64-unknown-linux-gnu" + + ; Function Attrs: minsize nounwind optsize uwtable + define i32 @test_sub_reg() #0 !dbg !22 { + entry: + %call = tail call i32 @value32(), !dbg !29 + call void @llvm.dbg.value(metadata i32 %call, metadata !28, metadata !DIExpression()), !dbg !30 + %conv = zext i32 %call to i64, !dbg !31 + tail call void @call64(i64 %conv), !dbg !31 + ret i32 %call, !dbg !32 + } + + declare !dbg !4 i32 @value32() + + declare !dbg !8 void @call64(i64) + + ; Function Attrs: minsize nounwind optsize uwtable + define i64 @test_super_reg() #0 !dbg !33 { + entry: + %call = tail call i64 @value64(), !dbg !39 + call void @llvm.dbg.value(metadata i64 %call, metadata !38, metadata !DIExpression()), !dbg !40 + %conv = trunc i64 %call to i32, !dbg !41 + tail call void @call32(i32 %conv), !dbg !41 + ret i64 %call, !dbg !42 + } + + declare !dbg !12 i64 @value64() + + declare !dbg !15 void @call32(i32) + + ; Function Attrs: nounwind readnone speculatable willreturn + declare void @llvm.dbg.value(metadata, metadata, metadata) #1 + + attributes #0 = { minsize nounwind optsize uwtable } + attributes #1 = { nounwind readnone speculatable willreturn } + + !llvm.dbg.cu = !{!0} + !llvm.module.flags = !{!18, !19, !20} + !llvm.ident = !{!21} + + !0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 10.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, retainedTypes: !3, nameTableKind: None) + !1 = !DIFile(filename: "ext.c", directory: "/") + !2 = !{} + !3 = !{!4, !8, !12, !15} + !4 = !DISubprogram(name: "value32", scope: !1, file: !1, line: 3, type: !5, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized, retainedNodes: !2) + !5 = !DISubroutineType(types: !6) + !6 = !{!7} + !7 = !DIBasicType(name: "unsigned int", size: 32, encoding: DW_ATE_unsigned) + !8 = !DISubprogram(name: "call64", scope: !1, file: !1, line: 6, type: !9, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized, retainedNodes: !2) + !9 = !DISubroutineType(types: !10) + !10 = !{null, !11} + !11 = !DIBasicType(name: "long unsigned int", size: 64, encoding: DW_ATE_unsigned) + !12 = !DISubprogram(name: "value64", scope: !1, file: !1, line: 4, type: !13, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized, retainedNodes: !2) + !13 = !DISubroutineType(types: !14) + !14 = !{!11} + !15 = !DISubprogram(name: "call32", scope: !1, file: !1, line: 5, type: !16, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized, retainedNodes: !2) + !16 = !DISubroutineType(types: !17) + !17 = !{null, !7} + !18 = !{i32 2, !"Dwarf Version", i32 4} + !19 = !{i32 2, !"Debug Info Version", i32 3} + !20 = !{i32 1, !"wchar_size", i32 4} + !21 = !{!"clang version 10.0.0"} + !22 = distinct !DISubprogram(name: "test_sub_reg", scope: !1, file: !1, line: 8, type: !23, scopeLine: 8, flags: DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !27) + !23 = !DISubroutineType(types: !24) + !24 = !{!25} + !25 = !DIDerivedType(tag: DW_TAG_typedef, name: "uint32_t", file: !26, line: 52, baseType: !7) + !26 = !DIFile(filename: "/usr/include/stdint.h", directory: "") + !27 = !{!28} + !28 = !DILocalVariable(name: "local", scope: !22, file: !1, line: 9, type: !25) + !29 = !DILocation(line: 9, scope: !22) + !30 = !DILocation(line: 0, scope: !22) + !31 = !DILocation(line: 10, scope: !22) + !32 = !DILocation(line: 11, scope: !22) + !33 = distinct !DISubprogram(name: "test_super_reg", scope: !1, file: !1, line: 14, type: !34, scopeLine: 14, flags: DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !37) + !34 = !DISubroutineType(types: !35) + !35 = !{!36} + !36 = !DIDerivedType(tag: DW_TAG_typedef, name: "uint64_t", file: !26, line: 56, baseType: !11) + !37 = !{!38} + !38 = !DILocalVariable(name: "local", scope: !33, file: !1, line: 15, type: !36) + !39 = !DILocation(line: 15, scope: !33) + !40 = !DILocation(line: 0, scope: !33) + !41 = !DILocation(line: 16, scope: !33) + !42 = !DILocation(line: 17, scope: !33) + +... +--- +name: test_sub_reg +tracksRegLiveness: true +callSites: + - { bb: 0, offset: 3 } + - { bb: 0, offset: 7, fwdArgRegs: + - { arg: 0, reg: '$rdi' } } +body: | + bb.0.entry: + liveins: $rbx + + frame-setup PUSH64r killed $rbx, implicit-def $rsp, implicit $rsp, debug-location !29 + CFI_INSTRUCTION def_cfa_offset 16 + CFI_INSTRUCTION offset $rbx, -16 + CALL64pcrel32 @value32, csr_64, implicit $rsp, implicit $ssp, implicit-def $rsp, implicit-def $ssp, implicit-def $eax, debug-location !29 + $ebx = MOV32rr $eax, debug-location !29 + DBG_VALUE $ebx, $noreg, !28, !DIExpression(), debug-location !30 + renamable $edi = MOV32rr $ebx, implicit-def $rdi, debug-location !31 + CALL64pcrel32 @call64, csr_64, implicit $rsp, implicit $ssp, implicit $rdi, implicit-def $rsp, implicit-def $ssp, debug-location !31 + $eax = MOV32rr killed $ebx, debug-location !32 + $rbx = frame-destroy POP64r implicit-def $rsp, implicit $rsp, debug-location !32 + CFI_INSTRUCTION def_cfa_offset 8, debug-location !32 + RETQ killed $eax, debug-location !32 + +... + +# Verify that a call site value is emitted for the 64-bit parameter that is +# loaded using the zero-extending MOV32rr. + +# CHECK: DW_TAG_GNU_call_site_parameter +# CHECK-NEXT: DW_AT_location (DW_OP_reg5 RDI) +# CHECK-NEXT: DW_AT_GNU_call_site_value (DW_OP_breg3 RBX+0) + +--- +name: test_super_reg +tracksRegLiveness: true +callSites: + - { bb: 0, offset: 3 } + - { bb: 0, offset: 7, fwdArgRegs: + - { arg: 0, reg: '$hdi' } } +body: | + bb.0.entry: + liveins: $rbx + + frame-setup PUSH64r killed $rbx, implicit-def $rsp, implicit $rsp, debug-location !39 + CFI_INSTRUCTION def_cfa_offset 16 + CFI_INSTRUCTION offset $rbx, -16 + CALL64pcrel32 @value64, csr_64, implicit $rsp, implicit $ssp, implicit-def $rsp, implicit-def $ssp, implicit-def $rax, debug-location !39 + $rbx = MOV64rr killed $rax, debug-location !39 + DBG_VALUE $rbx, $noreg, !38, !DIExpression(), debug-location !40 + $edi = MOV32rr $ebx, debug-location !41 + CALL64pcrel32 @call32, csr_64, implicit $rsp, implicit $ssp, implicit killed $edi, implicit-def $rsp, implicit-def $ssp, debug-location !41 + $rax = MOV64rr killed $rbx, debug-location !42 + $rbx = frame-destroy POP64r implicit-def $rsp, implicit $rsp, debug-location !42 + CFI_INSTRUCTION def_cfa_offset 8, debug-location !42 + RETQ killed $rax, debug-location !42 + +... + +# Verify that the parameter which is passed in a sub-register ($hdi) of the +# copy instruction's destination register ($edi), is described using the source +# register's corresponding sub-register ($hbx). Please note that the call site +# information has been manually updated so that the parameter is said to be +# passed in $hdi rather than $edi. A more natural situation would be a 32-bit +# parameter which is described using a MOV64rr, but as the 32-bit sub-registers +# are described using the 64-bit registers in DWARF, we would not be able to +# verify that the right sub-register was picked by looking at the +# llvm-dwarfdump output. + +# CHECK: DW_TAG_GNU_call_site_parameter +# CHECK-NEXT: DW_AT_location (DW_OP_reg5 RDI, DW_OP_bit_piece 0x10 0x10) +# CHECK-NEXT: DW_AT_GNU_call_site_value (DW_OP_breg3 RBX+0, DW_OP_bit_piece 0x10 0x10) Index: llvm/test/DebugInfo/MIR/X86/dbgcall-site-lea-interpretation.mir =================================================================== --- llvm/test/DebugInfo/MIR/X86/dbgcall-site-lea-interpretation.mir +++ llvm/test/DebugInfo/MIR/X86/dbgcall-site-lea-interpretation.mir @@ -121,11 +121,11 @@ renamable $edx = nsw IMUL32rr killed renamable $edx, killed renamable $edi, implicit-def dead $eflags, debug-location !14 MOV32mr $rsp, 1, $noreg, 4, $noreg, renamable $edx, debug-location !14 :: (store 4 into %ir.local1) renamable $rcx = LEA64r $r14, 1, $r15, 0, $noreg - renamable $rdi = LEA64r $r14, 2, $r15, 4, $noreg - renamable $rsi = LEA64r $r14, 1, $noreg, 0, $noreg + renamable $edi = LEA64_32r $r14, 2, $r15, 4, $noreg + renamable $esi = LEA64_32r $r14, 1, $noreg, 0, $noreg renamable $rdx = LEA64r $r14, 4, $r14, 8, $noreg renamable $r8 = LEA64r $noreg, 2, $r15, 8, $noreg - renamable $r9 = LEA64r $noreg, 1, $r15, 10, $noreg, implicit-def $r9d + renamable $r9d = LEA64_32r $noreg, 1, $r15, 10, $noreg, implicit-def $r9d CALL64pcrel32 @foo, csr_64, implicit $rsp, implicit $ssp, implicit $edi, implicit $esi, implicit $rdx, implicit $rcx, implicit $r8, implicit $r9d, implicit-def $rsp, implicit-def $ssp, implicit-def $eax, debug-location !14 ADD32mr $rsp, 1, $noreg, 4, $noreg, killed renamable $eax, implicit-def dead $eflags, debug-location !14 :: (store 4 into %ir.local1), (dereferenceable load 4 from %ir.local1) $rdi = LEA64r $r14, 1, killed $r14, 0, $noreg Index: llvm/test/DebugInfo/MIR/X86/dbgcall-site-two-fwd-reg-defs.mir =================================================================== --- /dev/null +++ llvm/test/DebugInfo/MIR/X86/dbgcall-site-two-fwd-reg-defs.mir @@ -0,0 +1,141 @@ +# RUN: llc -O1 -debug-entry-values -start-after=livedebugvalues -filetype=obj %s -o - | llvm-dwarfdump - | FileCheck %s + +# Based on the following C reproducer: +# +# extern void call(long, int); +# extern int a, b, c, d; +# +# int e() { return a; } +# +# int main() { +# d = c; +# b = e(); +# call(c, c); +# return 0; +# } + +--- | + target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" + target triple = "x86_64" + + @a = external global i32, align 4 + @c = external global i32, align 4 + @d = external global i32, align 4 + @b = external global i32, align 4 + + ; Function Attrs: noinline norecurse nounwind readonly + define i32 @e() #0 !dbg !13 { + entry: + %0 = load i32, i32* @a, align 4, !dbg !16 + ret i32 %0, !dbg !16 + } + + ; Function Attrs: noinline nounwind + define i32 @main() #1 !dbg !17 { + entry: + %0 = load i32, i32* @c, align 4, !dbg !19 + store i32 %0, i32* @d, align 4, !dbg !19 + %call = tail call i32 @e(), !dbg !20 + store i32 %call, i32* @b, align 4, !dbg !20 + %conv = sext i32 %0 to i64, !dbg !21 + tail call void @call(i64 %conv, i32 %0), !dbg !21 + ret i32 0, !dbg !22 + } + + declare !dbg !4 void @call(i64, i32) + + attributes #0 = { noinline norecurse nounwind readonly "frame-pointer"="all" } + attributes #1 = { noinline nounwind "frame-pointer"="all" } + + !llvm.dbg.cu = !{!0} + !llvm.module.flags = !{!9, !10, !11} + !llvm.ident = !{!12} + + !0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 10.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, retainedTypes: !3, nameTableKind: None) + !1 = !DIFile(filename: "x86_two_defs.c", directory: "/") + !2 = !{} + !3 = !{!4} + !4 = !DISubprogram(name: "call", scope: !1, file: !1, line: 1, type: !5, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized, retainedNodes: !2) + !5 = !DISubroutineType(types: !6) + !6 = !{null, !7, !8} + !7 = !DIBasicType(name: "long int", size: 64, encoding: DW_ATE_signed) + !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) + !9 = !{i32 2, !"Dwarf Version", i32 5} + !10 = !{i32 2, !"Debug Info Version", i32 3} + !11 = !{i32 1, !"wchar_size", i32 4} + !12 = !{!"clang version 10.0.0"} + !13 = distinct !DISubprogram(name: "e", scope: !1, file: !1, line: 5, type: !14, scopeLine: 5, flags: DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !2) + !14 = !DISubroutineType(types: !15) + !15 = !{!8} + !16 = !DILocation(line: 6, scope: !13) + !17 = distinct !DISubprogram(name: "main", scope: !1, file: !1, line: 9, type: !14, scopeLine: 9, flags: DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !2) + !19 = !DILocation(line: 10, scope: !17) + !20 = !DILocation(line: 11, scope: !17) + !21 = !DILocation(line: 12, scope: !17) + !22 = !DILocation(line: 13, scope: !17) + +... +--- +name: e +tracksRegLiveness: true +body: | + bb.0.entry: + frame-setup PUSH64r killed $rbp, implicit-def $rsp, implicit $rsp + CFI_INSTRUCTION def_cfa_offset 16 + CFI_INSTRUCTION offset $rbp, -16 + $rbp = frame-setup MOV64rr $rsp + CFI_INSTRUCTION def_cfa_register $rbp + renamable $eax = MOV32rm $rip, 1, $noreg, @a, $noreg, debug-location !16 :: (dereferenceable load 4 from @a) + $rbp = frame-destroy POP64r implicit-def $rsp, implicit $rsp, debug-location !16 + CFI_INSTRUCTION def_cfa $rsp, 8, debug-location !16 + RETQ $eax, debug-location !16 + +... +--- +name: main +tracksRegLiveness: true +callSites: + - { bb: 0, offset: 10 } + - { bb: 0, offset: 14, fwdArgRegs: + - { arg: 0, reg: '$rdi' } + - { arg: 1, reg: '$esi' } } +body: | + bb.0.entry: + liveins: $rbx + + frame-setup PUSH64r killed $rbp, implicit-def $rsp, implicit $rsp + CFI_INSTRUCTION def_cfa_offset 16 + CFI_INSTRUCTION offset $rbp, -16 + $rbp = frame-setup MOV64rr $rsp + CFI_INSTRUCTION def_cfa_register $rbp + frame-setup PUSH64r killed $rbx, implicit-def $rsp, implicit $rsp, debug-location !19 + frame-setup PUSH64r undef $rax, implicit-def $rsp, implicit $rsp + CFI_INSTRUCTION offset $rbx, -24 + renamable $ebx = MOV32rm $rip, 1, $noreg, @c, $noreg, debug-location !19 :: (dereferenceable load 4 from @c) + MOV32mr $rip, 1, $noreg, @d, $noreg, renamable $ebx, debug-location !19 :: (store 4 into @d) + CALL64pcrel32 @e, csr_64, implicit $rsp, implicit $ssp, implicit-def $rsp, implicit-def $ssp, implicit-def $eax, debug-location !20 + MOV32mr $rip, 1, $noreg, @b, $noreg, killed renamable $eax, debug-location !20 :: (store 4 into @b) + renamable $rdi = MOVSX64rr32 killed renamable $ebx, debug-location !21 + $esi = MOV32rr $edi, debug-location !21 + CALL64pcrel32 @call, csr_64, implicit $rsp, implicit $ssp, implicit $rdi, implicit killed $esi, implicit-def $rsp, implicit-def $ssp, debug-location !21 + $eax = XOR32rr undef $eax, undef $eax, implicit-def dead $eflags, debug-location !22 + $rsp = frame-destroy ADD64ri8 $rsp, 8, implicit-def dead $eflags, debug-location !22 + $rbx = frame-destroy POP64r implicit-def $rsp, implicit $rsp, debug-location !22 + $rbp = frame-destroy POP64r implicit-def $rsp, implicit $rsp, debug-location !22 + CFI_INSTRUCTION def_cfa $rsp, 8, debug-location !22 + RETQ $eax, debug-location !22 + +... + +# Verify that call site entries are emitted for both parameters in the call to +# %call. Both parameters should be described by the preserved $ebx register, +# and the value for the first parameter (passed in $rdi) should be +# sign-extended to 64 bits. + +# CHECK: DW_TAG_call_site_parameter +# CHECK-NEXT: DW_AT_location (DW_OP_reg4 RSI) +# CHECK-NEXT: DW_AT_call_value (DW_OP_breg3 RBX+0) + +# CHECK: DW_TAG_call_site_parameter +# CHECK-NEXT: DW_AT_location (DW_OP_reg5 RDI) +# CHECK-NEXT: DW_AT_call_value (DW_OP_breg3 RBX+0, DW_OP_constu 0xffffffff, DW_OP_and, DW_OP_convert ({{.*}}) "DW_ATE_signed_32", DW_OP_convert ({{.*}}) "DW_ATE_signed_64")