https://gcc.gnu.org/bugzilla/show_bug.cgi?id=120608

--- Comment #11 from Jakub Jelinek <jakub at gcc dot gnu.org> ---
Untested patch which fixes the #c5 testcase at -O0 -fsanitize=address.
There all that needs to be done is emit the asan epilogue sequence before the
musttail call(s) instead of disabling the tail call when it is present.

This doesn't fix the #c6 testcase or better
void foo (int *, int *, int *);
void bar (int *, int *, int *);

void
baz (int *x, int *y, int *z)
{
  (void) x; (void) y; (void) z;
  int a = 42, b = -42, c = 0;
  foo (&a, &b, &c);
  [[gnu::musttail]] return bar (0, 0, 0);
}
at -O2 -fsanitize=address, because there we need to deal in the tailc pass with
.ASAN_MARK POISON calls.

--- gcc/cfgexpand.cc.jj 2025-06-12 10:05:14.961227842 +0200
+++ gcc/cfgexpand.cc    2025-06-12 11:38:06.875469134 +0200
@@ -75,6 +75,7 @@ along with GCC; see the file COPYING3.
 #include "builtins.h"
 #include "opts.h"
 #include "gimple-range.h"
+#include "rtl-iter.h"

 /* Some systems use __main in a way incompatible with its use in gcc, in these
    cases use the macros NAME__MAIN to give a quoted symbol and SYMBOL__MAIN to
@@ -4434,9 +4435,10 @@ expand_gimple_stmt (gimple *stmt)
    tailcall) and the normal result happens via a sqrt instruction.  */

 static basic_block
-expand_gimple_tailcall (basic_block bb, gcall *stmt, bool *can_fallthru)
+expand_gimple_tailcall (basic_block bb, gcall *stmt, bool *can_fallthru,
+                       rtx_insn *asan_epilogue_seq)
 {
-  rtx_insn *last2, *last;
+  rtx_insn *last2, *last, *first = get_last_insn ();
   edge e;
   edge_iterator ei;
   profile_probability probability;
@@ -4453,6 +4455,58 @@ expand_gimple_tailcall (basic_block bb,
   return NULL;

  found:
+
+  if (asan_epilogue_seq)
+    {
+      /* We need to emit a copy of the asan_epilogue_seq before
+        the insns emitted by expand_gimple_stmt above.  The sequence
+        can contain labels, which need to be remapped.  */
+      hash_map<rtx, rtx> label_map;
+      start_sequence ();
+      emit_note (NOTE_INSN_DELETED);
+      for (rtx_insn *insn = asan_epilogue_seq; insn; insn = NEXT_INSN (insn))
+       switch (GET_CODE (insn))
+         {
+         case INSN:
+         case CALL_INSN:
+         case JUMP_INSN:
+           emit_copy_of_insn_after (insn, get_last_insn ());
+           break;
+         case CODE_LABEL:
+           label_map.put ((rtx) insn, (rtx) emit_label (gen_label_rtx ()));
+           break;
+         case BARRIER:
+           emit_barrier ();
+           break;
+         default:
+           gcc_unreachable ();
+         }
+      for (rtx_insn *insn = get_insns (); insn; insn = NEXT_INSN (insn))
+       if (JUMP_P (insn))
+         {
+           subrtx_ptr_iterator::array_type array;
+           FOR_EACH_SUBRTX_PTR (iter, array, &PATTERN (insn), ALL)
+             {
+               rtx *loc = *iter;
+               if (LABEL_REF_P (*loc))
+                 {
+                   rtx *lab = label_map.get ((rtx) label_ref_label (*loc));
+                   gcc_assert (lab);
+                   set_label_ref_label (*loc, as_a <rtx_insn *> (*lab));
+                 }
+             }
+           if (JUMP_LABEL (insn))
+             {
+               rtx *lab = label_map.get (JUMP_LABEL (insn));
+               gcc_assert (lab);
+               JUMP_LABEL (insn) = *lab;
+             }
+         }
+      asan_epilogue_seq = NEXT_INSN (get_insns ());
+      end_sequence ();
+      emit_insn_before (asan_epilogue_seq, NEXT_INSN (first));
+    }
+
   /* ??? Wouldn't it be better to just reset any pending stack adjust?
      Any instructions emitted here are about to be deleted.  */
   do_pending_stack_adjust ();
@@ -6118,7 +6172,7 @@ reorder_operands (basic_block bb)
 /* Expand basic block BB from GIMPLE trees to RTL.  */

 static basic_block
-expand_gimple_basic_block (basic_block bb, bool disable_tail_calls)
+expand_gimple_basic_block (basic_block bb, rtx_insn *asan_epilogue_seq)
 {
   gimple_stmt_iterator gsi;
   gimple *stmt = NULL;
@@ -6423,14 +6477,16 @@ expand_gimple_basic_block (basic_block b
        {
          gcall *call_stmt = dyn_cast <gcall *> (stmt);
          if (call_stmt
+             && asan_epilogue_seq
              && gimple_call_tail_p (call_stmt)
-             && disable_tail_calls)
+             && !gimple_call_must_tail_p (call_stmt))
            gimple_call_set_tail (call_stmt, false);

          if (call_stmt && gimple_call_tail_p (call_stmt))
            {
              bool can_fallthru;
-             new_bb = expand_gimple_tailcall (bb, call_stmt, &can_fallthru);
+             new_bb = expand_gimple_tailcall (bb, call_stmt, &can_fallthru,
+                                              asan_epilogue_seq);
              if (new_bb)
                {
                  if (can_fallthru)
@@ -7207,7 +7263,7 @@ pass_expand::execute (function *fun)
   head_end_for_bb.create (last_basic_block_for_fn (fun));
   FOR_BB_BETWEEN (bb, init_block->next_bb, EXIT_BLOCK_PTR_FOR_FN (fun),
                  next_bb)
-    bb = expand_gimple_basic_block (bb, var_ret_seq != NULL_RTX);
+    bb = expand_gimple_basic_block (bb, var_ret_seq);
   disable_ranger (fun);
   FOR_BB_BETWEEN (bb, init_block->next_bb, EXIT_BLOCK_PTR_FOR_FN (fun),
                  next_bb)

Reply via email to