Bug 115132 - Sibling calls optim should not be performed when builtin_unwind_init is used
Summary: Sibling calls optim should not be performed when builtin_unwind_init is used
Status: UNCONFIRMED
Alias: None
Product: gcc
Classification: Unclassified
Component: middle-end (show other bugs)
Version: 13.2.0
: P3 normal
Target Milestone: ---
Assignee: Not yet assigned to anyone
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2024-05-17 11:47 UTC by akrl
Modified: 2024-05-21 16:35 UTC (History)
3 users (show)

See Also:
Host:
Target: x86_64-*-*
Build:
Known to work:
Known to fail:
Last reconfirmed:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description akrl 2024-05-17 11:47:16 UTC
Hi all,

In GNU Emacs we use (in order to have all callee saved registers pushed on the stack before entering in the garbage collector)  '__builtin_unwind_init'.

If sibling calls optimization is performed the call to gc is moved after the register are popped again making '__builtin_unwind_init' ineffective.

test.c======

void gc ();

void foo (void)
{
  __builtin_unwind_init ();
  gc ();
}
=======

Expected result:

gcc -fno-optimize-sibling-calls -O2 test.c

=======
foo:
        push    rbp
        xor     eax, eax
        mov     rbp, rsp
        push    r15
        push    r14
        push    r13
        push    r12
        push    rbx
        sub     rsp, 8
        call    gc
        add     rsp, 8
        pop     rbx
        pop     r12
        pop     r13
        pop     r14
        pop     r15
        pop     rbp
        ret
========

gcc -02 test.c

========
foo:
        push    rbp
        xor     eax, eax
        mov     rbp, rsp
        push    r15
        push    r14
        push    r13
        push    r12
        push    rbx
        pop     rbx
        pop     r12
        pop     r13
        pop     r14
        pop     r15
        pop     rbp
        jmp     gc
========

I understand '__builtin_unwind_init' it's undocumented and this can be categorized as a non-bug but AFAIU this optimization makes '__builtin_unwind_init' unusable for its scope.

Another very possible scenario is that I don't understand correctly how '__builtin_unwind_init' is supposed to be used.

I see this on trunk but this behavior is present since at least GCC13.

Note: Original Emacs bug <https://debbugs.gnu.org/cgi/bugreport.cgi?bug=63365>

Thanks!

  Andrea
Comment 1 Richard Biener 2024-05-17 11:58:06 UTC
You can do the following as workaround:

void __attribute__((optimize("no-optimize-sibling-calls"))) foo (void)
{
  __builtin_unwind_init ();
  gc ();
}
Comment 2 akrl 2024-05-17 12:15:17 UTC
Hi Richard,

thanks, yes I tried that already, the trouble is that our code looks more like this:

=========
extern void flush_stack_call_func1 (void (*func) (void *arg), void *arg);
extern void mark_threads (void);
extern void
mark_threads_callback (void *ignore);

static inline void
flush_stack_call_func (void (*func) (void *arg), void *arg)
{
  __builtin_unwind_init ();
  flush_stack_call_func1 (func, arg);
}

void
mark_threads (void)
{
  flush_stack_call_func (mark_threads_callback, ((void *)0));
}

[...]
=========

Because we have several places where 'flush_stack_call_func' is used.

Unfortunately as 'flush_stack_call_func' gets inlined the trick does not work.

I'm not ecstatic at making 'flush_stack_call_func' not inlinable nor at marking all the functions where it is called with the attribute, but we'll have to find a way anyway I guess.

Thanks

  Andrea
Comment 3 Alexander Monakov 2024-05-17 14:34:39 UTC
Placing an empty asm is sufficient to prevent undesired tailcalls:

void gc ();

void foo (void)
{
  __builtin_unwind_init ();
  gc ();
  asm ("");
}