From: Jeremy Morse Date: Fri, 6 Sep 2019 10:08:22 +0000 (+0000) Subject: [DebugInfo] LiveDebugValues: explicitly terminate overwritten stack locations X-Git-Tag: llvmorg-11-init~9851 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=5d9cd3b4ca457e55c3c21094cfea5e49dddef36c;p=platform%2Fupstream%2Fllvm.git [DebugInfo] LiveDebugValues: explicitly terminate overwritten stack locations If a stack spill location is overwritten by another spill instruction, any variable locations pointing at that slot should be terminated. We cannot rely on spills always being restored to registers or variable locations being moved by a DBG_VALUE: the register allocator is entitled to spill a value and then forget about it when it goes out of liveness. To address this, scan for memory writes to spill locations, even those we don't consider to be normal "spills". isSpillInstruction and isLocationSpill distinguish the two now. After identifying spill overwrites, terminate the open range, and insert a $noreg DBG_VALUE for that variable. Differential Revision: https://reviews.llvm.org/D66941 llvm-svn: 371193 --- diff --git a/llvm/lib/CodeGen/LiveDebugValues.cpp b/llvm/lib/CodeGen/LiveDebugValues.cpp index ac96c73..0bdc62a 100644 --- a/llvm/lib/CodeGen/LiveDebugValues.cpp +++ b/llvm/lib/CodeGen/LiveDebugValues.cpp @@ -361,8 +361,18 @@ private: } }; - bool isSpillInstruction(const MachineInstr &MI, MachineFunction *MF, - unsigned &Reg); + /// Tests whether this instruction is a spill to a stack location. + bool isSpillInstruction(const MachineInstr &MI, MachineFunction *MF); + + /// Decide if @MI is a spill instruction and return true if it is. We use 2 + /// criteria to make this decision: + /// - Is this instruction a store to a spill slot? + /// - Is there a register operand that is both used and killed? + /// TODO: Store optimization can fold spills into other stores (including + /// other spills). We do not handle this yet (more than one memory operand). + bool isLocationSpill(const MachineInstr &MI, MachineFunction *MF, + unsigned &Reg); + /// If a given instruction is identified as a spill, return the spill location /// and set \p Reg to the spilled register. Optional isRestoreInstruction(const MachineInstr &MI, @@ -779,16 +789,8 @@ void LiveDebugValues::transferRegisterDef( } } -/// Decide if @MI is a spill instruction and return true if it is. We use 2 -/// criteria to make this decision: -/// - Is this instruction a store to a spill slot? -/// - Is there a register operand that is both used and killed? -/// TODO: Store optimization can fold spills into other stores (including -/// other spills). We do not handle this yet (more than one memory operand). bool LiveDebugValues::isSpillInstruction(const MachineInstr &MI, - MachineFunction *MF, unsigned &Reg) { - SmallVector Accesses; - + MachineFunction *MF) { // TODO: Handle multiple stores folded into one. if (!MI.hasOneMemOperand()) return false; @@ -797,6 +799,14 @@ bool LiveDebugValues::isSpillInstruction(const MachineInstr &MI, return false; // This is not a spill instruction, since no valid size was // returned from either function. + return true; +} + +bool LiveDebugValues::isLocationSpill(const MachineInstr &MI, + MachineFunction *MF, unsigned &Reg) { + if (!isSpillInstruction(MI, MF)) + return false; + auto isKilledReg = [&](const MachineOperand MO, unsigned &Reg) { if (!MO.isReg() || !MO.isUse()) { Reg = 0; @@ -865,7 +875,39 @@ void LiveDebugValues::transferSpillOrRestoreInst(MachineInstr &MI, LLVM_DEBUG(dbgs() << "Examining instruction: "; MI.dump();); - if (isSpillInstruction(MI, MF, Reg)) { + // First, if there are any DBG_VALUEs pointing at a spill slot that is + // written to, then close the variable location. The value in memory + // will have changed. + VarLocSet KillSet; + if (isSpillInstruction(MI, MF)) { + Loc = extractSpillBaseRegAndOffset(MI); + for (unsigned ID : OpenRanges.getVarLocs()) { + const VarLoc &VL = VarLocIDs[ID]; + if (VL.Kind == VarLoc::SpillLocKind && VL.Loc.SpillLocation == *Loc) { + // This location is overwritten by the current instruction -- terminate + // the open range, and insert an explicit DBG_VALUE $noreg. + // + // Doing this at a later stage would require re-interpreting all + // DBG_VALUes and DIExpressions to identify whether they point at + // memory, and then analysing all memory writes to see if they + // overwrite that memory, which is expensive. + // + // At this stage, we already know which DBG_VALUEs are for spills and + // where they are located; it's best to fix handle overwrites now. + KillSet.set(ID); + MachineInstr *NewDebugInstr = + BuildMI(*MF, VL.MI.getDebugLoc(), VL.MI.getDesc(), + VL.MI.isIndirectDebugValue(), 0, // $noreg + VL.MI.getDebugVariable(), VL.MI.getDebugExpression()); + Transfers.push_back({&MI, NewDebugInstr}); + } + } + OpenRanges.erase(KillSet, VarLocIDs); + } + + // Try to recognise spill and restore instructions that may create a new + // variable location. + if (isLocationSpill(MI, MF, Reg)) { TKind = TransferKind::TransferSpill; LLVM_DEBUG(dbgs() << "Recognized as spill: "; MI.dump();); LLVM_DEBUG(dbgs() << "Register: " << Reg << " " << printReg(Reg, TRI) diff --git a/llvm/test/DebugInfo/MIR/X86/live-debug-values-stack-clobber.mir b/llvm/test/DebugInfo/MIR/X86/live-debug-values-stack-clobber.mir new file mode 100644 index 0000000..52917dd --- /dev/null +++ b/llvm/test/DebugInfo/MIR/X86/live-debug-values-stack-clobber.mir @@ -0,0 +1,200 @@ +# RUN: llc -mtriple=x86_64-unknown-unknown %s -o - -run-pass=livedebugvalues | FileCheck %s +# +# Fix some of PR42772. Consider the code below: the arguments are forced onto +# the stack by the FORCE_SPILL macro, and go out of liveness if the +# "bees == 2" conditional is not taken. A spill slot is then re-used to +# preserve quux over the second FORCE_SPILL, over-writing the value of either +# "a" or "b". LiveDebugValues should detect when this happens, and terminate +# stack-spill locations when they get overwritten by a new value. +# +# --------8<-------- +# #define FORCE_SPILL() \ +# __asm volatile("" : : : \ +# "rax", "rbx", "rcx", "rdx", "rsi", "rdi", "rbp", "r8", \ +# "r9", "r10", "r11", "r12", "r13", "r14", "r15") +# +# volatile int bees = 0; +# +# long int f(long int a, long int b) { +# if (bees == 12) +# return 3; +# +# FORCE_SPILL(); +# if (bees == 2) +# return a - b; +# +# int quux = sum(1, 2); +# FORCE_SPILL(); +# bees++; +# return quux; +# } +# -------->8-------- +# +# CHECK: ![[ANUM:[0-9]+]] = !DILocalVariable(name: "a" +# CHECK: ![[BNUM:[0-9]+]] = !DILocalVariable(name: "b" +# +# These variables should be spilt, +# CHECK-LABEL: bb.1.if.end: +# CHECK: MOV64mr $rsp, 1, $noreg, 16, $noreg, killed renamable $rsi +# CHECK-NEXT: DBG_VALUE $rsp, 0, ![[BNUM]], !DIExpression( +# CHECK-NEXT: MOV64mr $rsp, 1, $noreg, 8, $noreg, killed renamable $rdi +# CHECK-NEXT: DBG_VALUE $rsp, 0, ![[ANUM]], !DIExpression( +# CHECK-NEXT: INLINEASM +# +# Then the location of "a" should be terminated when overwritten +# CHECK-LABEL: bb.3.if.end3: +# CHECK: CALL64pcrel32 @sum +# CHECK-NEXT: MOV64mr $rsp, 1, $noreg, 8, $noreg, $rax +# CHECK-NEXT: DBG_VALUE $noreg, $noreg, ![[ANUM]], !DIExpression() +# CHECK-NEXT: INLINEASM + +--- | + target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" + target triple = "x86_64-unknown-linux-gnu" + + @bees = external global i32, !dbg !0 + + ; Function Attrs: noinline norecurse nounwind readnone uwtable + declare i64 @sum(i64, i64) + + ; Function Attrs: noinline nounwind uwtable + define i64 @f(i64 %a, i64 %b) !dbg !12 { + entry: + br label %if.end + if.end: + br label %if.then2 + if.then2: + br label %if.end3 + if.end3: + br label %return + return: + ret i64 0 + } + + !llvm.dbg.cu = !{!2} + !llvm.module.flags = !{!8, !9, !10} + + !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) + !1 = distinct !DIGlobalVariable(name: "bees", scope: !2, file: !3, line: 6, type: !6, isLocal: false, isDefinition: true) + !2 = distinct !DICompileUnit(language: DW_LANG_C99, file: !3, producer: "clang", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !4, globals: !5, nameTableKind: None) + !3 = !DIFile(filename: "pr42772.c", directory: ".") + !4 = !{} + !5 = !{!0} + !6 = !DIDerivedType(tag: DW_TAG_volatile_type, baseType: !7) + !7 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) + !8 = !{i32 2, !"Dwarf Version", i32 4} + !9 = !{i32 2, !"Debug Info Version", i32 3} + !10 = !{i32 1, !"wchar_size", i32 4} + !12 = distinct !DISubprogram(name: "f", scope: !3, file: !3, line: 15, type: !13, scopeLine: 15, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !2, retainedNodes: !16) + !13 = !DISubroutineType(types: !14) + !14 = !{!15, !15, !15} + !15 = !DIBasicType(name: "long int", size: 64, encoding: DW_ATE_signed) + !16 = !{!17, !18, !19} + !17 = !DILocalVariable(name: "a", arg: 1, scope: !12, file: !3, line: 15, type: !15) + !18 = !DILocalVariable(name: "b", arg: 2, scope: !12, file: !3, line: 15, type: !15) + !19 = !DILocalVariable(name: "quux", scope: !12, file: !3, line: 23, type: !7) + !28 = !DILocation(line: 1, column: 1, scope: !12) + +... +--- +name: f +tracksRegLiveness: true +liveins: + - { reg: '$rdi', virtual-reg: '' } + - { reg: '$rsi', virtual-reg: '' } +frameInfo: + stackSize: 72 + offsetAdjustment: -72 + maxAlignment: 8 + adjustsStack: true + hasCalls: true + cvBytesOfCalleeSavedRegisters: 48 +fixedStack: + - { id: 0, type: spill-slot, offset: -56, size: 8, alignment: 8, stack-id: default, + callee-saved-register: '$rbx', callee-saved-restored: true, debug-info-variable: '', + debug-info-expression: '', debug-info-location: '' } + - { id: 1, type: spill-slot, offset: -48, size: 8, alignment: 16, stack-id: default, + callee-saved-register: '$r12', callee-saved-restored: true, debug-info-variable: '', + debug-info-expression: '', debug-info-location: '' } + - { id: 2, type: spill-slot, offset: -40, size: 8, alignment: 8, stack-id: default, + callee-saved-register: '$r13', callee-saved-restored: true, debug-info-variable: '', + debug-info-expression: '', debug-info-location: '' } + - { id: 3, type: spill-slot, offset: -32, size: 8, alignment: 16, stack-id: default, + callee-saved-register: '$r14', callee-saved-restored: true, debug-info-variable: '', + debug-info-expression: '', debug-info-location: '' } + - { id: 4, type: spill-slot, offset: -24, size: 8, alignment: 8, stack-id: default, + callee-saved-register: '$r15', callee-saved-restored: true, debug-info-variable: '', + debug-info-expression: '', debug-info-location: '' } + - { id: 5, type: spill-slot, offset: -16, size: 8, alignment: 16, stack-id: default, + callee-saved-register: '$rbp', callee-saved-restored: true, debug-info-variable: '', + debug-info-expression: '', debug-info-location: '' } +stack: + - { id: 0, name: '', type: spill-slot, offset: -72, size: 8, alignment: 8, + stack-id: default, callee-saved-register: '', callee-saved-restored: true, + debug-info-variable: '', debug-info-expression: '', debug-info-location: '' } + - { id: 1, name: '', type: spill-slot, offset: -64, size: 8, alignment: 8, + stack-id: default, callee-saved-register: '', callee-saved-restored: true, + debug-info-variable: '', debug-info-expression: '', debug-info-location: '' } +body: | + bb.0.entry: + successors: %bb.4, %bb.1 + liveins: $rdi, $rsi, $rbp, $r15, $r14, $r13, $r12, $rbx + + DBG_VALUE $rdi, $noreg, !17, !DIExpression(), debug-location !28 + DBG_VALUE $rdi, $noreg, !17, !DIExpression(), debug-location !28 + DBG_VALUE $rsi, $noreg, !18, !DIExpression(), debug-location !28 + DBG_VALUE $rsi, $noreg, !18, !DIExpression(), debug-location !28 + frame-setup PUSH64r killed $rbp, implicit-def $rsp, implicit $rsp, debug-location !28 + frame-setup PUSH64r killed $r15, implicit-def $rsp, implicit $rsp, debug-location !28 + frame-setup PUSH64r killed $r14, implicit-def $rsp, implicit $rsp, debug-location !28 + frame-setup PUSH64r killed $r13, implicit-def $rsp, implicit $rsp, debug-location !28 + frame-setup PUSH64r killed $r12, implicit-def $rsp, implicit $rsp, debug-location !28 + frame-setup PUSH64r killed $rbx, implicit-def $rsp, implicit $rsp, debug-location !28 + $rsp = frame-setup SUB64ri8 $rsp, 24, implicit-def dead $eflags + renamable $ecx = MOV32rm $rip, 1, $noreg, @bees, $noreg, debug-location !28 :: (volatile dereferenceable load 4 from @bees) + $eax = MOV32ri 3, implicit-def $rax + CMP32ri8 killed renamable $ecx, 12, implicit-def $eflags, debug-location !28 + JCC_1 %bb.4, 4, implicit $eflags, debug-location !28 + + bb.1.if.end: + successors: %bb.2, %bb.3 + liveins: $rdi, $rsi + + MOV64mr $rsp, 1, $noreg, 16, $noreg, killed renamable $rsi :: (store 8 into %stack.1) + MOV64mr $rsp, 1, $noreg, 8, $noreg, killed renamable $rdi :: (store 8 into %stack.0) + INLINEASM &"", 1, 12, implicit-def dead early-clobber $rax, 12, implicit-def dead early-clobber $rbx, 12, implicit-def dead early-clobber $rcx, 12, implicit-def dead early-clobber $rdx, 12, implicit-def dead early-clobber $rsi, 12, implicit-def dead early-clobber $rdi, 12, implicit-def dead early-clobber $rbp, 12, implicit-def dead early-clobber $r8, 12, implicit-def dead early-clobber $r9, 12, implicit-def dead early-clobber $r10, 12, implicit-def dead early-clobber $r11, 12, implicit-def dead early-clobber $r12, 12, implicit-def dead early-clobber $r13, 12, implicit-def dead early-clobber $r14, 12, implicit-def dead early-clobber $r15, 12, implicit-def dead early-clobber $df, 12, implicit-def dead early-clobber $fpsw, 12, implicit-def dead early-clobber $eflags, debug-location !28 + renamable $eax = MOV32rm $rip, 1, $noreg, @bees, $noreg, debug-location !28 :: (volatile dereferenceable load 4 from @bees) + CMP32ri8 killed renamable $eax, 2, implicit-def $eflags, debug-location !28 + JCC_1 %bb.3, 5, implicit killed $eflags, debug-location !28 + + bb.2.if.then2: + successors: %bb.4 + + renamable $rax = MOV64rm $rsp, 1, $noreg, 8, $noreg :: (load 8 from %stack.0) + renamable $rax = SUB64rm killed renamable $rax, $rsp, 1, $noreg, 16, $noreg, implicit-def dead $eflags, debug-location !28 :: (load 8 from %stack.1) + JMP_1 %bb.4 + + bb.3.if.end3: + successors: %bb.4 + + $edi = MOV32ri 1, implicit-def $rdi, debug-location !28 + $esi = MOV32ri 2, implicit-def $rsi, debug-location !28 + CALL64pcrel32 @sum, csr_64, implicit $rsp, implicit $ssp, implicit $rdi, implicit $rsi, implicit-def $rsp, implicit-def $ssp, implicit-def $rax, debug-location !28 + MOV64mr $rsp, 1, $noreg, 8, $noreg, $rax :: (store 8 into %stack.0) + INLINEASM &"", 1, 12, implicit-def dead early-clobber $rax, 12, implicit-def dead early-clobber $rbx, 12, implicit-def dead early-clobber $rcx, 12, implicit-def dead early-clobber $rdx, 12, implicit-def dead early-clobber $rsi, 12, implicit-def dead early-clobber $rdi, 12, implicit-def dead early-clobber $rbp, 12, implicit-def dead early-clobber $r8, 12, implicit-def dead early-clobber $r9, 12, implicit-def dead early-clobber $r10, 12, implicit-def dead early-clobber $r11, 12, implicit-def dead early-clobber $r12, 12, implicit-def dead early-clobber $r13, 12, implicit-def dead early-clobber $r14, 12, implicit-def dead early-clobber $r15, 12, implicit-def dead early-clobber $df, 12, implicit-def dead early-clobber $fpsw, 12, implicit-def dead early-clobber $eflags, debug-location !28 + ADD32mi8 $rip, 1, $noreg, @bees, $noreg, 1, implicit-def dead $eflags, debug-location !28 :: (volatile store 4 into @bees), (volatile dereferenceable load 4 from @bees) + renamable $rax = MOVSX64rm32 $rsp, 1, $noreg, 8, $noreg, debug-location !28 :: (load 4 from %stack.0, align 8) + + bb.4.return: + liveins: $rax + + $rsp = frame-destroy ADD64ri8 $rsp, 24, implicit-def dead $eflags, debug-location !28 + $rbx = frame-destroy POP64r implicit-def $rsp, implicit $rsp, debug-location !28 + $r12 = frame-destroy POP64r implicit-def $rsp, implicit $rsp, debug-location !28 + $r13 = frame-destroy POP64r implicit-def $rsp, implicit $rsp, debug-location !28 + $r14 = frame-destroy POP64r implicit-def $rsp, implicit $rsp, debug-location !28 + $r15 = frame-destroy POP64r implicit-def $rsp, implicit $rsp, debug-location !28 + $rbp = frame-destroy POP64r implicit-def $rsp, implicit $rsp, debug-location !28 + RETQ $rax, debug-location !28 + +...