[Bug c/112870] New: incorrect jmp when using goto on a function, causing infinite loop

2023-12-05 Thread grantrwittmann at gmail dot com via Gcc-bugs
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=112870

Bug ID: 112870
   Summary: incorrect jmp when using goto on a function, causing
infinite loop
   Product: gcc
   Version: 13.2.0
Status: UNCONFIRMED
  Severity: normal
  Priority: P3
 Component: c
  Assignee: unassigned at gcc dot gnu.org
  Reporter: grantrwittmann at gmail dot com
  Target Milestone: ---

When first setting a variable to a label, then jumping after that label to a
function (not calling the function), GCC generates incorrect assembly causing
the goto statement to jump to the previous label and not the function.

The code:

void function() {
return;
}

int main() {
void* label_var = &&label;
label:
goto *&function;
}

compiles to this assembly:

function:
pushrbp
mov rbp, rsp
nop
pop rbp
ret
main:
pushrbp
mov rbp, rsp
mov QWORD PTR [rbp-8], OFFSET FLAT:.L4
.L4:
nop
jmp .L4

The last jmp goes back to .L4 (label) even though it should be going to
function. The assembly is compiled correctly when the `label_var` declaration
is removed.

This same code compiles correctly on other compilers like clang (17.0.1):

function:   # @function
pushrbp
mov rbp, rsp
pop rbp
ret
main:   # @main
pushrbp
mov rbp, rsp
mov dword ptr [rbp - 4], 0
lea rax, [rip + .Ltmp2]
mov qword ptr [rbp - 16], rax
.Ltmp2: # Block address taken
.LBB1_1:# =>This Inner Loop Header: Depth=1
lea rax, [rip + function]
mov qword ptr [rbp - 24], rax   # 8-byte Spill
jmp .LBB1_2
.LBB1_2:#   in Loop: Header=BB1_1 Depth=1
mov rax, qword ptr [rbp - 24]   # 8-byte Reload
jmp rax


All examples from this issue were compiled and ran through the godbolt.org
online compiler explorer.

[Bug c/112870] incorrect jmp when using goto on a function, causing infinite loop

2023-12-05 Thread grantrwittmann at gmail dot com via Gcc-bugs
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=112870

--- Comment #2 from Grant Wittman  ---
(In reply to Andrew Pinski from comment #1)
> This is all undefined behavior.
> 
> 
> Please read
> https://gcc.gnu.org/onlinedocs/gcc-13.2.0/gcc/Labels-as-Values.html .
> 
> Specifically:
> You may not use this mechanism to jump to code in a different function.

I think I might get what you mean by 'undefined behavior', but this result is
predictable. I know that specific code snippet does not work, but it was the
shortest example I could make that would recreate the error.

Additionally, that doesn't explain why the last instruction is compiled
completely differently when the line `void* label_var = &&label;`, is removed.
I understand that the `label` is also removed from compilation but I can still
just use this code and it compiles correctly:

void function() {
return;
}

int main() {
goto *&function;
}

Again this code does not run without a segfault, but it compiles correctly.

If I force the compiler to add the labels into the assembly, it will still
compile correctly:

void function() {
return;
}

int one = 1;

int main() {
if(one) goto end;
label:
goto *&function;
end:
if(!one) goto label;
return 0;
}

which compiles to

function:
pushrbp
mov rbp, rsp
nop
pop rbp
ret
one:
.long   1
main:
pushrbp
mov rbp, rsp
mov eax, DWORD PTR one[rip]
testeax, eax
jne .L9
nop
jmp .L4
.L10:
nop
.L4:
mov eax, OFFSET FLAT:function
jmp rax
.L9:
nop
mov eax, DWORD PTR one[rip]
testeax, eax
je  .L10
mov eax, 0
pop rbp
ret

But by adding that declaration of `label_var` back, it breaks again

void function() {
return;
}

int one = 1;

int main() {
void* label_var = &&label; // <-- ADDED STATEMENT
if(one) goto end;
label:
goto *&function;
end:
if(!one) goto label;
return 0;
}

which compiles to 

function:
pushrbp
mov rbp, rsp
nop
pop rbp
ret
one:
.long   1
main:
pushrbp
mov rbp, rsp
mov QWORD PTR [rbp-8], OFFSET FLAT:.L4
mov eax, DWORD PTR one[rip]
testeax, eax
jne .L9
.L4:
nop
jmp .L4 ; <-- GOTO STATEMENT INFINITE LOOP AGAIN
.L9:
nop
mov eax, DWORD PTR one[rip]
testeax, eax
jne .L6
jmp .L4
.L6:
mov eax, 0
pop rbp
ret