llvm / llvm-project

The LLVM Project is a collection of modular and reusable compiler and toolchain technologies.
http://llvm.org
Other
28.55k stars 11.8k forks source link

Loop Deletion(loop-deletion) pass might generate misunderstanding debug information #89012

Open edumoot opened 6 months ago

edumoot commented 6 months ago

Problems: Loop Deletion (loop-deletion) might generate unexpected debug information.

The variables, function arguments - 'paramA' and 'paramC', that we could use later are optimized out on line 13. However, they appear again on line 20, with inappropriate values - 'paramA = 0' and 'paramC = 0'. The condition of outer loop is false, so the inner loop could be not executed. That means 'paramA' cannot be 0.

After several experiments, we can confirm that (1) the problem on line 13 is caused by 'deadargelim' (Dead Arguments Elimination) and (2) 'loop-deletion'(Loop Deletion) is in charge of the problem on line 20.

* thread #1, name = 'case.out', stop reason = breakpoint 1.1
    frame #0: 0x0000555555555154 case.out`calculateValue(paramA=<unavailable>, paramC=<unavailable>) at case.c:13:18
   10   int calculateValue(int  paramA,  int paramB, int  paramC, unsigned  paramD)
   11   { 
   12       int localVar = 2;
-> 13       globalVar1 = globalArray[2];
   14       for (paramC = 0; paramC == 9; ++paramC)
   15           for (paramA = 0; paramA < 0; paramA = paramA + 9)
   16           { 
(lldb) fr v
(int) paramA = <variable not available>

(int) paramC = <variable not available>

(int) localVar = 2
(lldb) s
Process 59718 stopped
* thread #1, name = 'case.out', stop reason = step in
    frame #0: 0x0000555555555160 case.out`calculateValue(paramA=0, paramC=0) at case.c:20:20
   17               unsigned localArray[6] ;
   18               localArray[4] = addValues(paramA, paramB * paramC);
   19           }
-> 20       return localVar+globalVar1;
   21   }
   22   
   23   int  computeResult()
(lldb) fr v
(int) paramA = 0
(int) paramC = 0
(int) localVar = 2

Reproduce with:

clang -g -O0 case.c -Xclang -disable-O0-optnone -emit-llvm -c -o case.bc
opt -passes='{passes}' case.bc  -o case.opt.bc
llc --relocation-model=pic case.opt.bc -o case.s
clang case.s -o case.out

In LLVM 18.1.2 (commit 261aae0c), we set passes to:

cgscc(devirt<4>(inline,function-attrs<skip-non-recursive-function-attrs>,argpromotion,openmp-opt-cgscc,function<eager-inv;no-rerun>(sroa<modify-cfg>,early-cse<memssa>,speculative-execution<only-if-divergent-target>,jump-threading,correlated-propagation,simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-sink-common-insts;speculate-blocks;simplify-cond-branch>,instcombine<max-iterations=1;no-use-loop-info;no-verify-fixpoint>,aggressive-instcombine,libcalls-shrinkwrap,tailcallelim,simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-sink-common-insts;speculate-blocks;simplify-cond-branch>,reassociate,constraint-elimination,loop-mssa(loop-instsimplify,loop-simplifycfg,licm<no-allowspeculation>,loop-rotate<header-duplication;no-prepare-for-lto>,licm<allowspeculation>,simple-loop-unswitch<nontrivial;trivial>),simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-sink-common-insts;speculate-blocks;simplify-cond-branch>,instcombine<max-iterations=1;no-use-loop-info;no-verify-fixpoint>,loop(loop-idiom,indvars,loop-deletion,loop-unroll-full),sroa<modify-cfg>,vector-combine,mldst-motion<no-split-footer-bb>,gvn<>,sccp,bdce,instcombine<max-iterations=1;no-use-loop-info;no-verify-fixpoint>,jump-threading,correlated-propagation,adce,memcpyopt,dse,move-auto-init,loop-mssa(licm<allowspeculation>),coro-elide,simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;hoist-common-insts;sink-common-insts;speculate-blocks;simplify-cond-branch>,instcombine<max-iterations=1;no-use-loop-info;no-verify-fixpoint>),function-attrs,function(require<should-not-run-function-passes>),coro-split)),deadargelim

In LLVM 17.0.6 (commit, 6009fe18), we set passes to:

cgscc(devirt<4>(inline<only-mandatory>,inline,function-attrs<skip-non-recursive>,argpromotion,openmp-opt-cgscc,function<eager-inv;no-rerun>(sroa<modify-cfg>,early-cse<memssa>,speculative-execution,jump-threading,correlated-propagation,simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-sink-common-insts;speculate-blocks;simplify-cond-branch>,instcombine<max-iterations=1000;no-use-loop-info>,aggressive-instcombine,constraint-elimination,libcalls-shrinkwrap,tailcallelim,simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-sink-common-insts;speculate-blocks;simplify-cond-branch>,reassociate,loop-mssa(loop-instsimplify,loop-simplifycfg,licm<no-allowspeculation>,loop-rotate<header-duplication;no-prepare-for-lto>,licm<allowspeculation>,simple-loop-unswitch<nontrivial;trivial>),simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-sink-common-insts;speculate-blocks;simplify-cond-branch>,instcombine<max-iterations=1000;no-use-loop-info>,loop(loop-idiom,indvars,loop-deletion,loop-unroll-full),sroa<modify-cfg>,vector-combine,mldst-motion<no-split-footer-bb>,gvn<>,sccp,bdce,instcombine<max-iterations=1000;no-use-loop-info>,jump-threading,correlated-propagation,adce,memcpyopt,dse,move-auto-init,loop-mssa(licm<allowspeculation>),coro-elide,simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;hoist-common-insts;sink-common-insts;speculate-blocks;simplify-cond-branch>,instcombine<max-iterations=1000;no-use-loop-info>),function-attrs,function(require<should-not-run-function-passes>),coro-split)),deadargelim

The results of LLDB are shown above. GDB is also similar.

Breakpoint 1, calculateValue (paramA=<optimised out>, paramC=<optimised out>) at case.c:13
13      globalVar1 = globalArray[2];
(gdb) info args
paramA = <optimised out>
paramC = <optimised out>
(gdb) s
20      return localVar+globalVar1;
(gdb) info args
paramA = 0
paramC = 0

cat case.c

#include <stdio.h>
static unsigned globalVar1 = 15;
static unsigned globalArray[3] = {3, 3, 3};

int  addValues(int  param1, int  param2)
{   
    return globalArray[1]+param1+param2;
}

int calculateValue(int  paramA,  int paramB, int  paramC, unsigned  paramD)
{ 
    int localVar = 2;
    globalVar1 = globalArray[2];
    for (paramC = 0; paramC == 9; ++paramC)
        for (paramA = 0; paramA < 0; paramA = paramA + 9)
        { 
            unsigned localArray[6] ;
            localArray[4] = addValues(paramA, paramB * paramC);
        }
    return localVar+globalVar1;
}

int  computeResult()
{ 
    int varA = 55;
    int varB = 8888;
    int varC = 1111;
    varB = calculateValue(varA, varB, varC, varA + varB + varC) && addValues(varA, varC);
    return varB;
}

int main (void)
{  
    printf("%d",computeResult());
    return 0;
}
llvmbot commented 6 months ago

@llvm/issue-subscribers-debuginfo

Author: Yachao Zhu (edumoot)

Problems: Loop Deletion (loop-deletion) might generate unexpected debug information. The variables, function arguments - 'paramA' and 'paramC', that we could use later are optimized out on line 13. However, they appear again on line 20, with inappropriate values - 'paramA = 0' and 'paramC = 0'. The condition of outer loop is false, so the inner loop could be not executed. That means 'paramA' cannot be 0. After several experiments, we can confirm that (1) the problem on line 13 is caused by 'deadargelim' (Dead Arguments Elimination) and (2) 'loop-deletion'(Loop Deletion) is in charge of the problem on line 20. ``` * thread #1, name = 'case.out', stop reason = breakpoint 1.1 frame #0: 0x0000555555555154 case.out`calculateValue(paramA=<unavailable>, paramC=<unavailable>) at case.c:13:18 10 int calculateValue(int paramA, int paramB, int paramC, unsigned paramD) 11 { 12 int localVar = 2; -> 13 globalVar1 = globalArray[2]; 14 for (paramC = 0; paramC == 9; ++paramC) 15 for (paramA = 0; paramA < 0; paramA = paramA + 9) 16 { (lldb) fr v (int) paramA = <variable not available> (int) paramC = <variable not available> (int) localVar = 2 (lldb) s Process 59718 stopped * thread #1, name = 'case.out', stop reason = step in frame #0: 0x0000555555555160 case.out`calculateValue(paramA=0, paramC=0) at case.c:20:20 17 unsigned localArray[6] ; 18 localArray[4] = addValues(paramA, paramB * paramC); 19 } -> 20 return localVar+globalVar1; 21 } 22 23 int computeResult() (lldb) fr v (int) paramA = 0 (int) paramC = 0 (int) localVar = 2 ``` Reproduce with: ```compiling pipeline clang -g -O0 case.c -Xclang -disable-O0-optnone -emit-llvm -c -o case.bc opt -passes='{passes}' case.bc -o case.opt.bc llc --relocation-model=pic case.opt.bc -o case.s clang case.s -o case.out ``` In LLVM 18.1.2 (commit [261aae0c](https://github.com/llvm/llvm-project/commit/26a1d6601d727a96f4301d0d8647b5a42760ae0c)), we set passes to: ``` cgscc(devirt<4>(inline,function-attrs<skip-non-recursive-function-attrs>,argpromotion,openmp-opt-cgscc,function<eager-inv;no-rerun>(sroa<modify-cfg>,early-cse<memssa>,speculative-execution<only-if-divergent-target>,jump-threading,correlated-propagation,simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-sink-common-insts;speculate-blocks;simplify-cond-branch>,instcombine<max-iterations=1;no-use-loop-info;no-verify-fixpoint>,aggressive-instcombine,libcalls-shrinkwrap,tailcallelim,simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-sink-common-insts;speculate-blocks;simplify-cond-branch>,reassociate,constraint-elimination,loop-mssa(loop-instsimplify,loop-simplifycfg,licm<no-allowspeculation>,loop-rotate<header-duplication;no-prepare-for-lto>,licm<allowspeculation>,simple-loop-unswitch<nontrivial;trivial>),simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-sink-common-insts;speculate-blocks;simplify-cond-branch>,instcombine<max-iterations=1;no-use-loop-info;no-verify-fixpoint>,loop(loop-idiom,indvars,loop-deletion,loop-unroll-full),sroa<modify-cfg>,vector-combine,mldst-motion<no-split-footer-bb>,gvn<>,sccp,bdce,instcombine<max-iterations=1;no-use-loop-info;no-verify-fixpoint>,jump-threading,correlated-propagation,adce,memcpyopt,dse,move-auto-init,loop-mssa(licm<allowspeculation>),coro-elide,simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;hoist-common-insts;sink-common-insts;speculate-blocks;simplify-cond-branch>,instcombine<max-iterations=1;no-use-loop-info;no-verify-fixpoint>),function-attrs,function(require<should-not-run-function-passes>),coro-split)),deadargelim ``` In LLVM 17.0.6 (commit, [6009fe18](https://github.com/llvm/llvm-project/commit/6009708b4367171ccdbf4b5905cb6a803753fe18)), we set passes to: ``` cgscc(devirt<4>(inline<only-mandatory>,inline,function-attrs<skip-non-recursive>,argpromotion,openmp-opt-cgscc,function<eager-inv;no-rerun>(sroa<modify-cfg>,early-cse<memssa>,speculative-execution,jump-threading,correlated-propagation,simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-sink-common-insts;speculate-blocks;simplify-cond-branch>,instcombine<max-iterations=1000;no-use-loop-info>,aggressive-instcombine,constraint-elimination,libcalls-shrinkwrap,tailcallelim,simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-sink-common-insts;speculate-blocks;simplify-cond-branch>,reassociate,loop-mssa(loop-instsimplify,loop-simplifycfg,licm<no-allowspeculation>,loop-rotate<header-duplication;no-prepare-for-lto>,licm<allowspeculation>,simple-loop-unswitch<nontrivial;trivial>),simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-sink-common-insts;speculate-blocks;simplify-cond-branch>,instcombine<max-iterations=1000;no-use-loop-info>,loop(loop-idiom,indvars,loop-deletion,loop-unroll-full),sroa<modify-cfg>,vector-combine,mldst-motion<no-split-footer-bb>,gvn<>,sccp,bdce,instcombine<max-iterations=1000;no-use-loop-info>,jump-threading,correlated-propagation,adce,memcpyopt,dse,move-auto-init,loop-mssa(licm<allowspeculation>),coro-elide,simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;hoist-common-insts;sink-common-insts;speculate-blocks;simplify-cond-branch>,instcombine<max-iterations=1000;no-use-loop-info>),function-attrs,function(require<should-not-run-function-passes>),coro-split)),deadargelim ``` The results of LLDB are shown above. GDB is also similar. ``` Breakpoint 1, calculateValue (paramA=<optimised out>, paramC=<optimised out>) at case.c:13 13 globalVar1 = globalArray[2]; (gdb) info args paramA = <optimised out> paramC = <optimised out> (gdb) s 20 return localVar+globalVar1; (gdb) info args paramA = 0 paramC = 0 ``` `cat case.c` ``` #include <stdio.h> static unsigned globalVar1 = 15; static unsigned globalArray[3] = {3, 3, 3}; int addValues(int param1, int param2) { return globalArray[1]+param1+param2; } int calculateValue(int paramA, int paramB, int paramC, unsigned paramD) { int localVar = 2; globalVar1 = globalArray[2]; for (paramC = 0; paramC == 9; ++paramC) for (paramA = 0; paramA < 0; paramA = paramA + 9) { unsigned localArray[6] ; localArray[4] = addValues(paramA, paramB * paramC); } return localVar+globalVar1; } int computeResult() { int varA = 55; int varB = 8888; int varC = 1111; varB = calculateValue(varA, varB, varC, varA + varB + varC) && addValues(varA, varC); return varB; } int main (void) { printf("%d",computeResult()); return 0; } ```