How the LLVM handle the debug location information of continue keyword and right brace(loop end location)?

Let me show you the following simple C code:

int main()
{
  int i;

  for (i = 0; i < 256; i++)
  {
      i++;
  }
}

In this simple C code, if we use Clang to compile it and debug it: We will get something like this:

(gdb) b main
Breakpoint 1 at 0x100000f7b: file a.c, line 5.
(gdb) r
Starting program: a.out 
[New Thread 0x1403 of process 23435]
warning: unhandled dyld version (15)

Thread 2 hit Breakpoint 1, main () at a.c:5
5     for (i = 0; i < 256; i++)
(gdb) n
7         i++;
(gdb) 
5     for (i = 0; i < 256; i++)
(gdb) 
7         i++;

That is to say, the right brace of location LLVM doesn’t emit. However if we have the continue keyword:

int main()
{
  int i;

  for (i = 0; i < 256; i++)
  {
    continue;
    i++;
  }
}

Then we compile and debug it:

Thread 2 hit Breakpoint 1, main () at a.c:5
5     for (i = 0; i < 256; i++)
(gdb) n
7       continue;
(gdb) 
5     for (i = 0; i < 256; i++)
(gdb) 
7       continue;
(gdb) 
5     for (i = 0; i < 256; i++)
(gdb) 
7       continue;

We will stop the line of continue. But if we compare the LLVM IR between them:

The right brace and continue keyword both are the following the br instruction and !dbg !23. Except that the line number of !dbg !23 not the same.

; :6: ; preds = %3 br label %7, !dbg !23

The question is how LLVM know whether to generate the debug location(generate for the continue keyword line, but not for the loop’s right brace)? Because they are the same br instruction and others are the same.

The braces around the body of the ‘for’ statement indicate grouping, but don’t have any other semantic significance. If you look at the abstract syntax tree (AST) constructed for this by any compiler, it is highly unlikely to have an explicit representation of the braces, because the structure of the AST already describes the grouping. On the other hand, a ‘continue’ statement will be explicitly represented in the AST because it is a statement in its own right.

If I modify your for-loop body to this:

{

i++;

#ifdef CONTINUE

continue;

#endif

}

and compile it both ways, then I see the same thing you do: The generated IR is the same for both cases except for the source-location of the branch. This makes sense to me as follows.

A ‘for’ loop has four parts: initialization, condition, increment, and body. Clang emits the condition and increment parts in their own basic-blocks, for convenience. This means it implicitly needs to add a branch at the end of the “body” block to the “increment” block. However, if the “body” block already ends with an explicit branch (of any kind), then it can omit the implicit branch. The ‘continue’ statement will obviously be generated as an explicit branch to the “increment” block.

The difference in debug-info is that the explicit branch is associated with the ‘continue’ statement, while the implicit branch is associated with the ‘for’ statement, and these statements have different source locations. Even though the sequence of instructions is the same, the reason they were emitted is different, and the debug info reflects that difference.

–paulr

The braces around the body of the ‘for’ statement indicate grouping, but don’t have any other semantic significance. If you look at the abstract syntax tree (AST) constructed for this by any compiler, it is highly unlikely to have an explicit representation of the braces, because the structure of the AST already describes the grouping. On the other hand, a ‘continue’ statement will be explicitly represented in the AST because it is a statement in its own right.

If I modify your for-loop body to this:

{

i++;

#ifdef CONTINUE

continue;

#endif

}

and compile it both ways, then I see the same thing you do: The generated IR is the same for both cases except for the source-location of the branch. This makes sense to me as follows.

A ‘for’ loop has four parts: initialization, condition, increment, and body. Clang emits the condition and increment parts in their own basic-blocks, for convenience. This means it implicitly needs to add a branch at the end of the “body” block to the “increment” block. However, if the “body” block already ends with an explicit branch (of any kind), then it can omit the implicit branch. The ‘continue’ statement will obviously be generated as an explicit branch to the “increment” block.

The difference in debug-info is that the explicit branch is associated with the ‘continue’ statement, while the implicit branch is associated with the ‘for’ statement, and these statements have different source locations. Even though the sequence of instructions is the same, the reason they were emitted is different, and the debug info reflects that difference.

–paulr

Simply because continue keyword is a part of the AST, while ‘}’ is not.

And you don’t break on “natural” terminators. Also, would you expect a different behavior between:

for(i=0;i<N;++i)
doStuff();

And:

for(i=0;i<N;++i) {
doStuff();
}

While they should be identical on AST level?

Cheers,
Marcin

Hi Marcin:
I don’t expect stop the right brace } and expect stop at continue keyword statement. My question is continue keyword statement is the same as right brace } statement in the LLVM IR except the !dbg!23 has different line number. I don’t know how the LLVM backend distinguish it.

在 2017-06-03 19:20:46,“Marcin Słowik” me@marandil.pl 写道:

If without any brace, the br should correspond to doStuff() in your case. However, maybe I don’t list my concern and question very clearly, it is my mistake and I apologize for it.

Let me show you more details:

1.int main()
2.{
3. int i;
4.
5. for (i = 0; i < 256; i++) {
6. }
7.}

; :6: ; preds = %3
br label %7, !dbg !23

; :7: ; preds = %6
%8 = load i32, i32* %2, align 4, !dbg !25
%9 = add nsw i32 %8, 1, !dbg !25
store i32 %9, i32* %2, align 4, !dbg !25
br label %3, !dbg !27, !llvm.loop !28

!23 = !DILocation(line: 6, column: 1, scope: !24)

You can see that this br instruction corresponds to right brace(i.e. line 6).

Let us see:

1.int main()
2.{
3. int i;
4.
5. for (i = 0; i < 256; i++) {
6. continue;
7. }
8. }

; :6: ; preds = %3
br label %7, !dbg !23

; :7: ; preds = %6
%8 = load i32, i32* %2, align 4, !dbg !25
%9 = add nsw i32 %8, 1, !dbg !25
store i32 %9, i32* %2, align 4, !dbg !25
br label %3, !dbg !27, !llvm.loop !28

!23 = !DILocation(line: 6, column: 1, scope: !24)

You can see that this br instruction corresponds to continue statement (i.e. line 6).

But, the result is: the first case line 6 right brace will not generated(it make sense and just group), but the latter case is line 6 continue and will generate location information for debugger. My question is they are the same but will be treated differently, I do not know how LLVM backend treat these two IR. Maybe I think these two cases are not related with Clang.