[SROA][DebugInfo][GSoC] Testing SROA on amalgamated sqlite source

Introduction

Introduction

`SROA' is an early stage pass running at the very beginning of the
pipeline in `-O{1,2,3}'. Greg Bedwell's report from his DExTer tool
shows SROA on function as one of the major culprits of Debug Info
loss.

With debugify-each partially done I tried testing this on the
amalgamated sqlite source.

The steps are as follows:
,----
> # generate IR file without debug info and optnone
> clang -O0 -Xclang -disable-O0-optnone -S -emit-llvm sqlite3.c -o sqlite
>
> # run opt
> opt -sroa -debugify-each sqlite -disable-output 2> results
`----

Results

[Here] is the full results file.

The results were gather by `grep -c`'ing the results file according to
the error's message.

  SROA runs | FAIL | PASS | empty DebugLoc | Missing line warnings
-----------|------|------|----------------|-----------------------
       1978 | 1100 | 878 | 5214 | 75166

What's worth noticing is that the only error that SROA produces is
that of empty debug locations. It does not report any missing
DILocalVariables or llvm.dbg.value intrinsics. Thus all of the 1100
SROA runs that failed are due to empty debug locations.

Also, while skimming through the results file I noticed that the
instructions with missing DL were `phi' instructions. To confirm this
I did:

   $ grep "ERROR: Instruction" results | grep phi -c

witch gives us a result of `5214' and this means that all the
instructions with missing Debug Loc are `phi' instructions.

[Here] https://gramanas.github.io/sroa-results.csv

Conclusion

This implies SROA does a pretty good job of preserving debug values.

That is good to hear / expected, since we went though great lengths to ensure that SROA preserves variable debug info.

Have you looked at the location-less phi instructions? Does empty mean line 0 or do you mean they have no debug location whatsoever? Are they empty for good reasons or does it look more like an oversight in the implementation?

-- adrian

  `SROA' is an early stage pass running at the very beginning of the
  pipeline in `-O{1,2,3}'. Greg Bedwell's report from his DExTer tool
  shows SROA on function as one of the major culprits of Debug Info
  loss.

The methodology I used is with the opt-bisect-limit option on clang, so
it's not strictly the case that the results presented show that the loss in
"debugability" occurs during the SROA pass itself directly but rather that
the effect of running SROA is some loss in debugability to the final
program. I didn't make that point clear enough during my lightning talk
session!

Looking at some of the results in more detail, I think the reason that the
reason our results differ is because what I'm measuring is not actually
variable info loss directly, but as a consequence of the impact on
stepping. My standard disclaimer with all results from this tool is that a
non-perfect debugging experience score is not necessarily indicative of
something wrong, but should be looked at in conjunction with all the other
factors such as what the pass is trying to achieve optimization-wise, etc
etc. That is to say, it's not necessarily a bug, but it can be (especially
when observed as a regression between compiler revisions).

Using the standard Fibonacci example with clang and lldb from this
afternoon:

1 #ifdef _MSC_VER
2 # define DEX_NOINLINE __declspec(noinline)
3 #else
4 # define DEX_NOINLINE __attribute__((__noinline__))
5 #endif
6
7 DEX_NOINLINE
8 void Fibonacci(int terms, int& total)
9 {
10 int first = 0;
11 int second = 1;
12
13 for (int i = 0; i < terms; ++i)
14 {
15 int next = first + second; // DexWatch('i', 'first', 'second',
'total')
16 total += first; // DexWatch('i', 'first', 'second',
'total', 'next')
17 first = second; // DexWatch('i', 'first', 'second',
'total', 'next')
18 second = next; // DexWatch('i', 'first', 'second',
'total', 'next')
19 }
20 }
21
22 int main()
23 {
24 int total = 0;
25 Fibonacci(5, total);
26 return total;
27 }

not running "SROA on function (_Z9FibonacciiRi)" the DExTer trace output
is:

## BEGIN ##
[1, "main", "tests/fibonacci/test.cpp", 24, 6, "BREAKPOINT", "FUNC", {}]
[2, "main", "tests/fibonacci/test.cpp", 25, 2, "BREAKPOINT", "FORWARD", {}]
. [3, "Fibonacci(int, int&)", "tests/fibonacci/test.cpp", 10, 6,
"BREAKPOINT", "FUNC", {}]
. [4, "Fibonacci(int, int&)", "tests/fibonacci/test.cpp", 11, 6,
"BREAKPOINT", "FORWARD", {}]
. [5, "Fibonacci(int, int&)", "tests/fibonacci/test.cpp", 13, 11,
"BREAKPOINT", "FORWARD", {}]
. [6, "Fibonacci(int, int&)", "tests/fibonacci/test.cpp", 15, 14,
"BREAKPOINT", "FORWARD", {"i": "0", "second": "1", "total": "0", "first":
"0"}]
. [7, "Fibonacci(int, int&)", "tests/fibonacci/test.cpp", 16, 12,
"BREAKPOINT", "FORWARD", {"i": "0", "second": "1", "total": "0", "first":
"0", "next": "1"}]
. [8, "Fibonacci(int, int&)", "tests/fibonacci/test.cpp", 17, 11,
"BREAKPOINT", "FORWARD", {"i": "0", "second": "1", "total": "0", "first":
"0", "next": "1"}]
. [9, "Fibonacci(int, int&)", "tests/fibonacci/test.cpp", 18, 12,
"BREAKPOINT", "FORWARD", {"i": "0", "second": "1", "total": "0", "first":
"1", "next": "1"}]
. [10, "Fibonacci(int, int&)", "tests/fibonacci/test.cpp", 13, 29,
"STEP", "BACKWARD", {}]
. [11, "Fibonacci(int, int&)", "tests/fibonacci/test.cpp", 15, 14,
"BREAKPOINT", "FORWARD", {"i": "1", "second": "1", "total": "0", "first":
"1"}]
. [12, "Fibonacci(int, int&)", "tests/fibonacci/test.cpp", 16, 12,
"BREAKPOINT", "FORWARD", {"i": "1", "second": "1", "total": "0", "first":
"1", "next": "2"}]
. [13, "Fibonacci(int, int&)", "tests/fibonacci/test.cpp", 17, 11,
"BREAKPOINT", "FORWARD", {"i": "1", "second": "1", "total": "1", "first":
"1", "next": "2"}]
. [14, "Fibonacci(int, int&)", "tests/fibonacci/test.cpp", 18, 12,
"BREAKPOINT", "FORWARD", {"i": "1", "second": "1", "total": "1", "first":
"1", "next": "2"}]
. [15, "Fibonacci(int, int&)", "tests/fibonacci/test.cpp", 13, 29,
"STEP", "BACKWARD", {}]
. [16, "Fibonacci(int, int&)", "tests/fibonacci/test.cpp", 15, 14,
"BREAKPOINT", "FORWARD", {"i": "2", "second": "2", "total": "1", "first":
"1"}]
. [17, "Fibonacci(int, int&)", "tests/fibonacci/test.cpp", 16, 12,
"BREAKPOINT", "FORWARD", {"i": "2", "second": "2", "total": "1", "first":
"1", "next": "3"}]
. [18, "Fibonacci(int, int&)", "tests/fibonacci/test.cpp", 17, 11,
"BREAKPOINT", "FORWARD", {"i": "2", "second": "2", "total": "2", "first":
"1", "next": "3"}]
. [19, "Fibonacci(int, int&)", "tests/fibonacci/test.cpp", 18, 12,
"BREAKPOINT", "FORWARD", {"i": "2", "second": "2", "total": "2", "first":
"2", "next": "3"}]
. [20, "Fibonacci(int, int&)", "tests/fibonacci/test.cpp", 13, 29,
"STEP", "BACKWARD", {}]
. [21, "Fibonacci(int, int&)", "tests/fibonacci/test.cpp", 15, 14,
"BREAKPOINT", "FORWARD", {"i": "3", "second": "3", "total": "2", "first":
"2"}]
. [22, "Fibonacci(int, int&)", "tests/fibonacci/test.cpp", 16, 12,
"BREAKPOINT", "FORWARD", {"i": "3", "second": "3", "total": "2", "first":
"2", "next": "5"}]
. [23, "Fibonacci(int, int&)", "tests/fibonacci/test.cpp", 17, 11,
"BREAKPOINT", "FORWARD", {"i": "3", "second": "3", "total": "4", "first":
"2", "next": "5"}]
. [24, "Fibonacci(int, int&)", "tests/fibonacci/test.cpp", 18, 12,
"BREAKPOINT", "FORWARD", {"i": "3", "second": "3", "total": "4", "first":
"3", "next": "5"}]
. [25, "Fibonacci(int, int&)", "tests/fibonacci/test.cpp", 13, 29,
"STEP", "BACKWARD", {}]
. [26, "Fibonacci(int, int&)", "tests/fibonacci/test.cpp", 15, 14,
"BREAKPOINT", "FORWARD", {"i": "4", "second": "5", "total": "4", "first":
"3"}]
. [27, "Fibonacci(int, int&)", "tests/fibonacci/test.cpp", 16, 12,
"BREAKPOINT", "FORWARD", {"i": "4", "second": "5", "total": "4", "first":
"3", "next": "8"}]
. [28, "Fibonacci(int, int&)", "tests/fibonacci/test.cpp", 17, 11,
"BREAKPOINT", "FORWARD", {"i": "4", "second": "5", "total": "7", "first":
"3", "next": "8"}]
. [29, "Fibonacci(int, int&)", "tests/fibonacci/test.cpp", 18, 12,
"BREAKPOINT", "FORWARD", {"i": "4", "second": "5", "total": "7", "first":
"5", "next": "8"}]
. [30, "Fibonacci(int, int&)", "tests/fibonacci/test.cpp", 13, 29,
"STEP", "BACKWARD", {}]
. [31, "Fibonacci(int, int&)", "tests/fibonacci/test.cpp", 20, 1,
"BREAKPOINT", "FORWARD", {}]
[32, "main", "tests/fibonacci/test.cpp", 26, 9, "BREAKPOINT", "FUNC", {}]
## END (32 steps) ##

So we do a couple of steps in main, before stepping into Fibonacci(int,
int&) at step 3. It's fairly easy to see that we iterate the loop 5 times,
stepping on lines 13, 15, 16, 17 and 18 in order each time.

After "SROA on function (_Z9FibonacciiRi)" we now get:

## BEGIN ##
[1, "main", "tests/fibonacci/test.cpp", 24, 6, "BREAKPOINT", "FUNC", {}]
[2, "main", "tests/fibonacci/test.cpp", 25, 2, "BREAKPOINT", "FORWARD", {}]
. [3, "Fibonacci(int, int&)", "tests/fibonacci/test.cpp", 13, 20,
"BREAKPOINT", "FUNC", {}]
. [4, "Fibonacci(int, int&)", "tests/fibonacci/test.cpp", 15, 20,
"BREAKPOINT", "FORWARD", {"i": "0", "second": "1", "total": "0", "first":
"0"}]
. [5, "Fibonacci(int, int&)", "tests/fibonacci/test.cpp", 16, 9,
"BREAKPOINT", "FORWARD", {"i": "0", "second": "1", "total": "0", "first":
"0", "next": "1"}]
. [6, "Fibonacci(int, int&)", "tests/fibonacci/test.cpp", 13, 29, "STEP",
"BACKWARD", {}]
. [7, "Fibonacci(int, int&)", "tests/fibonacci/test.cpp", 13, 20,
"BREAKPOINT", "BACKWARD", {}]
. [8, "Fibonacci(int, int&)", "tests/fibonacci/test.cpp", 15, 20,
"BREAKPOINT", "FORWARD", {"i": "1", "second": "1", "total": "0", "first":
"1"}]
. [9, "Fibonacci(int, int&)", "tests/fibonacci/test.cpp", 16, 9,
"BREAKPOINT", "FORWARD", {"i": "1", "second": "1", "total": "0", "first":
"1", "next": "2"}]
. [10, "Fibonacci(int, int&)", "tests/fibonacci/test.cpp", 13, 29,
"STEP", "BACKWARD", {}]
. [11, "Fibonacci(int, int&)", "tests/fibonacci/test.cpp", 13, 20,
"BREAKPOINT", "BACKWARD", {}]
. [12, "Fibonacci(int, int&)", "tests/fibonacci/test.cpp", 15, 20,
"BREAKPOINT", "FORWARD", {"i": "2", "second": "2", "total": "1", "first":
"1"}]
. [13, "Fibonacci(int, int&)", "tests/fibonacci/test.cpp", 16, 9,
"BREAKPOINT", "FORWARD", {"i": "2", "second": "2", "total": "1", "first":
"1", "next": "3"}]
. [14, "Fibonacci(int, int&)", "tests/fibonacci/test.cpp", 13, 29,
"STEP", "BACKWARD", {}]
. [15, "Fibonacci(int, int&)", "tests/fibonacci/test.cpp", 13, 20,
"BREAKPOINT", "BACKWARD", {}]
. [16, "Fibonacci(int, int&)", "tests/fibonacci/test.cpp", 15, 20,
"BREAKPOINT", "FORWARD", {"i": "3", "second": "3", "total": "2", "first":
"2"}]
. [17, "Fibonacci(int, int&)", "tests/fibonacci/test.cpp", 16, 9,
"BREAKPOINT", "FORWARD", {"i": "3", "second": "3", "total": "2", "first":
"2", "next": "5"}]
. [18, "Fibonacci(int, int&)", "tests/fibonacci/test.cpp", 13, 29,
"STEP", "BACKWARD", {}]
. [19, "Fibonacci(int, int&)", "tests/fibonacci/test.cpp", 13, 20,
"BREAKPOINT", "BACKWARD", {}]
. [20, "Fibonacci(int, int&)", "tests/fibonacci/test.cpp", 15, 20,
"BREAKPOINT", "FORWARD", {"i": "4", "second": "5", "total": "4", "first":
"3"}]
. [21, "Fibonacci(int, int&)", "tests/fibonacci/test.cpp", 16, 9,
"BREAKPOINT", "FORWARD", {"i": "4", "second": "5", "total": "4", "first":
"3", "next": "8"}]
. [22, "Fibonacci(int, int&)", "tests/fibonacci/test.cpp", 13, 29,
"STEP", "BACKWARD", {}]
. [23, "Fibonacci(int, int&)", "tests/fibonacci/test.cpp", 13, 20,
"BREAKPOINT", "BACKWARD", {}]
. [24, "Fibonacci(int, int&)", "tests/fibonacci/test.cpp", 20, 1,
"BREAKPOINT", "FORWARD", {}]
[25, "main", "tests/fibonacci/test.cpp", 26, 9, "BREAKPOINT", "FUNC", {}]
## END (25 steps) ##

So we've now gained some extra steps at line 13, but at different columns
(the "++i" and "i < terms" are now treated as separate steps), but we're
now also no longer ever stepping onto lines 17 or 18 at all. Whether these
are the expected effects of SROA are not something DExTer is in the
business of deciding, but it does score it as a difference because it now,
at no point, sees the expression "first" evaluate to a value of 5, or
"total" to a value of 8 which it did previously. From the source-level
debugging experience, the variables now just get updated between iterations.

So in short, it seems SROA is having some effect on debugging but it seems
that it's not in the form of failing to preserve variable info.

is in the business of deciding, but it does score it as a difference
because it now, at no point, sees the expression "first" evaluate to a
value of 5, or "total" to a value of 8 which it did previously. From the
source-level debugging experience, the variables now just get updated
between iterations.

Obviously should've been 7 for that value of "total".

To try and be a bit more helpful there's definitely something dodgy
happening to variable visibility on the fibonacci example in LICM.

With "BISECT: NOT running pass (32) Loop Invariant Code Motion on loop":

## BEGIN ##
[1, "main", "tests/fibonacci/test.cpp", 24, 6, "BREAKPOINT", "FUNC", {}]
[2, "main", "tests/fibonacci/test.cpp", 25, 2, "BREAKPOINT", "FORWARD", {}]
. [3, "Fibonacci(int, int&)", "tests/fibonacci/test.cpp", 13, 20,
"BREAKPOINT", "FUNC", {}]
. [4, "Fibonacci(int, int&)", "tests/fibonacci/test.cpp", 15, 20,
"BREAKPOINT", "FORWARD", {"i": "0", "second": "1", "total": "0", "first":
"0"}]
. [5, "Fibonacci(int, int&)", "tests/fibonacci/test.cpp", 16, 9,
"BREAKPOINT", "FORWARD", {"i": "0", "second": "1", "total": "0", "first":
"0", "next": "1"}]
. [6, "Fibonacci(int, int&)", "tests/fibonacci/test.cpp", 13, 29, "STEP",
"BACKWARD", {}]
. [7, "Fibonacci(int, int&)", "tests/fibonacci/test.cpp", 15, 20,
"BREAKPOINT", "FORWARD", {"i": "1", "second": "1", "total": "0", "first":
"1"}]
. [8, "Fibonacci(int, int&)", "tests/fibonacci/test.cpp", 16, 9,
"BREAKPOINT", "FORWARD", {"i": "1", "second": "1", "total": "0", "first":
"1", "next": "2"}]
. [9, "Fibonacci(int, int&)", "tests/fibonacci/test.cpp", 13, 29, "STEP",
"BACKWARD", {}]
. [10, "Fibonacci(int, int&)", "tests/fibonacci/test.cpp", 15, 20,
"BREAKPOINT", "FORWARD", {"i": "2", "second": "2", "total": "1", "first":
"1"}]
. [11, "Fibonacci(int, int&)", "tests/fibonacci/test.cpp", 16, 9,
"BREAKPOINT", "FORWARD", {"i": "2", "second": "2", "total": "1", "first":
"1", "next": "3"}]
. [12, "Fibonacci(int, int&)", "tests/fibonacci/test.cpp", 13, 29,
"STEP", "BACKWARD", {}]
. [13, "Fibonacci(int, int&)", "tests/fibonacci/test.cpp", 15, 20,
"BREAKPOINT", "FORWARD", {"i": "3", "second": "3", "total": "2", "first":
"2"}]
. [14, "Fibonacci(int, int&)", "tests/fibonacci/test.cpp", 16, 9,
"BREAKPOINT", "FORWARD", {"i": "3", "second": "3", "total": "2", "first":
"2", "next": "5"}]
. [15, "Fibonacci(int, int&)", "tests/fibonacci/test.cpp", 13, 29,
"STEP", "BACKWARD", {}]
. [16, "Fibonacci(int, int&)", "tests/fibonacci/test.cpp", 15, 20,
"BREAKPOINT", "FORWARD", {"i": "4", "second": "5", "total": "4", "first":
"3"}]
. [17, "Fibonacci(int, int&)", "tests/fibonacci/test.cpp", 16, 9,
"BREAKPOINT", "FORWARD", {"i": "4", "second": "5", "total": "4", "first":
"3", "next": "8"}]
. [18, "Fibonacci(int, int&)", "tests/fibonacci/test.cpp", 13, 29,
"STEP", "BACKWARD", {}]
. [19, "Fibonacci(int, int&)", "tests/fibonacci/test.cpp", 20, 1,
"BREAKPOINT", "FORWARD", {}]
[20, "main", "tests/fibonacci/test.cpp", 26, 9, "BREAKPOINT", "FUNC", {}]
## END (20 steps) ##

But with: "BISECT: running pass (32) Loop Invariant Code Motion on loop":

## BEGIN ##
[1, "main", "tests/fibonacci/test.cpp", 24, 6, "BREAKPOINT", "FUNC", {}]
[2, "main", "tests/fibonacci/test.cpp", 25, 2, "BREAKPOINT", "FORWARD", {}]
. [3, "Fibonacci(int, int&)", "tests/fibonacci/test.cpp", 13, 20,
"BREAKPOINT", "FUNC", {}]
. [4, "Fibonacci(int, int&)", "tests/fibonacci/test.cpp", 16, 9,
"BREAKPOINT", "FORWARD", {"i": "0", "second": "1", "total": "0", "first":
"0", "next": null}]
. [5, "Fibonacci(int, int&)", "tests/fibonacci/test.cpp", 13, 2, "STEP",
"BACKWARD", {}]
. [6, "Fibonacci(int, int&)", "tests/fibonacci/test.cpp", 15, 20,
"BREAKPOINT", "FORWARD", {"i": "0", "second": "1", "total": "0", "first":
"0"}]
. [7, "Fibonacci(int, int&)", "tests/fibonacci/test.cpp", 16, 9, "STEP",
"FORWARD", {"i": "0", "second": "1", "total": "0", "first": "0", "next":
"1"}]
. [8, "Fibonacci(int, int&)", "tests/fibonacci/test.cpp", 13, 29, "STEP",
"BACKWARD", {}]
. [9, "Fibonacci(int, int&)", "tests/fibonacci/test.cpp", 15, 20,
"BREAKPOINT", "FORWARD", {"i": "1", "second": "1", "total": "0", "first":
"1"}]
. [10, "Fibonacci(int, int&)", "tests/fibonacci/test.cpp", 16, 9, "STEP",
"FORWARD", {"i": "1", "second": "1", "total": "0", "first": "1", "next":
"2"}]
. [11, "Fibonacci(int, int&)", "tests/fibonacci/test.cpp", 13, 29,
"STEP", "BACKWARD", {}]
. [12, "Fibonacci(int, int&)", "tests/fibonacci/test.cpp", 15, 20,
"BREAKPOINT", "FORWARD", {"i": "2", "second": "2", "total": "0", "first":
"1"}]
. [13, "Fibonacci(int, int&)", "tests/fibonacci/test.cpp", 16, 9, "STEP",
"FORWARD", {"i": "2", "second": "2", "total": "0", "first": "1", "next":
"3"}]
. [14, "Fibonacci(int, int&)", "tests/fibonacci/test.cpp", 13, 29,
"STEP", "BACKWARD", {}]
. [15, "Fibonacci(int, int&)", "tests/fibonacci/test.cpp", 15, 20,
"BREAKPOINT", "FORWARD", {"i": "3", "second": "3", "total": "0", "first":
"2"}]
. [16, "Fibonacci(int, int&)", "tests/fibonacci/test.cpp", 16, 9, "STEP",
"FORWARD", {"i": "3", "second": "3", "total": "0", "first": "2", "next":
"5"}]
. [17, "Fibonacci(int, int&)", "tests/fibonacci/test.cpp", 13, 29,
"STEP", "BACKWARD", {}]
. [18, "Fibonacci(int, int&)", "tests/fibonacci/test.cpp", 15, 20,
"BREAKPOINT", "FORWARD", {"i": "4", "second": "5", "total": "0", "first":
"3"}]
. [19, "Fibonacci(int, int&)", "tests/fibonacci/test.cpp", 16, 9, "STEP",
"FORWARD", {"i": "4", "second": "5", "total": "0", "first": "3", "next":
"8"}]
. [20, "Fibonacci(int, int&)", "tests/fibonacci/test.cpp", 13, 29,
"STEP", "BACKWARD", {}]
. [21, "Fibonacci(int, int&)", "tests/fibonacci/test.cpp", 16, 9, "STEP",
"FORWARD", {"i": "5", "second": "8", "total": "0", "first": "5", "next":
"8"}]
. [22, "Fibonacci(int, int&)", "tests/fibonacci/test.cpp", 20, 1,
"BREAKPOINT", "FORWARD", {}]
[23, "main", "tests/fibonacci/test.cpp", 26, 9, "BREAKPOINT", "FUNC", {}]
## END (23 steps) ##

The difference here that really sticks out as a problem to me is reported
by the heuristic like this:

  test.cpp:15-18 [total] [21/21]
    expected encountered values:
      0

    missing values:
      1 [+6]
      2 [+6]
      4 [+6]
      7 [+3]

So, "total" evaluates to 0 for the duration of Fibonacci(int, int&) which
definitely smells wrong to me. Prior to that it was evaluating to 0, 1, 2
and 4 in order (we stopped seeing 7 after SROA).

-Greg

That is good to hear / expected, since we went though great lengths to ensure that SROA preserves variable debug info.

Have you looked at the location-less phi instructions?
Does empty mean line 0 or do you mean they have no debug location whatsoever?

Most of them have line 0, only 12 are locationless.

Are they empty for good reasons or does it look more like an oversight in the implementation?

How can I approach this? My guess is that all these phi's are with line 0 because they are made
of stores and allocas that aren't actual code, and are instead generated by clang. Like here
https://reviews.llvm.org/D47097 at test1

Thanks,
These are very helpful.

As I understand it, SROA and LICM render some variables
"useless" by optimizing the code to not use them. Hence we can't debug them.

My guess is that the ones with line 0 are intentional and deliberately set the location to line 0 because someone already put thought into this, and the ones without location are more likely to be bugs. Since there are only 12 you can probably just look at them individually and determine where they are generated and then we can decide whether the code is behaving correctly or not.

-- adrian

FWIW, I’ve raised the LICM issue here: https://bugs.llvm.org/show_bug.cgi?id=37682

Hi Greg,

FWIW, I've raised the LICM issue here: https://bugs.llvm.org/show_bug.cgi?id=37682

LICM's behavior seems reasonable/desirable to me. I've shared my thoughts on llvm.org/PR37682. To summarize, I don't think it's feasible to assert that the debugger show updated values for 'total' within this loop, because it amounts to a demand that LICM never happen. Alternatives such as marking 'total' as unavailable, or making it appear as a non-pointer type, have serious tradeoffs and/or can't properly model the effects of aliasing.

vedant

LICM's behavior seems reasonable/desirable to me. I've shared my

thoughts on llvm.org/PR37682. To summarize, I don't think it's feasible to
assert that the debugger show updated values for 'total' within this loop,
because it

amounts to a demand that LICM never happen. Alternatives such as marking

'total' as unavailable, or making it appear as a non-pointer type, have
serious tradeoffs and/or can't properly model the effects of aliasing.

Thanks for taking a look. Let's continue the discussion if there's any
more follow-up in the bug to prevent derailing this thread. I agree with
everything you say, but it still makes me feel uneasy that we have a
situation where we have the information available according to the
semantics of the code the user wrote (that "total" gets updated on every
iteration, where we have that value in a register) but we aren't able to
present that to the user because it conflicts with the reality of the
optimized code ("total" is a memory location that has the value 0 until the
loop terminates when the contents of the register are stored to it). I'd
love a third option where we could somehow convey that information to the
user. If that's not an option currently, we should consider a solution for
-Og mode at least. As I mentioned in the bug, we come across poorly
compared to MSVC here in terms of quality of debugging, but vaguely
amusingly purely because it doesn't appear to hoist the store.

-Greg