Conditional and unconditional branch targets can be either a label or
a symbol.  For conditional jump:

(jump_insn 7 6 14 2 (set (pc)
        (if_then_else (eq (reg:CCZ 17 flags)
                (const_int 0 [0]))
            (label_ref:DI 23)
            (pc))) "x.c":8:5 1458 {jcc}
     (expr_list:REG_DEAD (reg:CCZ 17 flags)
        (int_list:REG_BR_PROB 217325348 (nil)))
...
(code_label 23 20 8 4 4 (nil) [1 uses])
(note 8 23 9 4 [bb 4] NOTE_INSN_BASIC_BLOCK)
(call_insn/j 9 8 10 4 (call (mem:QI (symbol_ref:DI ("bar") [flags 0x41]  
<function_decl 0x7f4cff3c0b00 bar>) [0 bar S1 A8])
        (const_int 0 [0])) "x.c":8:14 discrim 1 1469 {sibcall_di}
     (expr_list:REG_CALL_DECL (symbol_ref:DI ("bar") [flags 0x41]  <function_dec
l 0x7f4cff3c0b00 bar>)
        (nil))
    (nil))

they can be changed to

(jump_insn 7 6 14 2 (set (pc)
        (if_then_else (eq (reg:CCZ 17 flags)
                (const_int 0 [0]))
            ((symbol_ref:DI ("bar") [flags 0x41] <function_decl 0x7fffe99c0c00 
foo>)
            (pc))) "x.c":8:5 1458 {jcc}
     (expr_list:REG_DEAD (reg:CCZ 17 flags)
        (int_list:REG_BR_PROB 217325348 (nil)))

if the call is a sibcall.  For jump table:

(jump_table_data 16 15 17 (addr_vec:DI [
            (label_ref:DI 18)
            (label_ref:DI 22)
            (label_ref:DI 26)
            (label_ref:DI 30)
            (label_ref:DI 34)
        ]))
...
(code_label 30 17 31 4 5 (nil) [1 uses])
(note 31 30 32 4 [bb 4] NOTE_INSN_BASIC_BLOCK)
(call_insn/j 32 31 33 4 (call (mem:QI (symbol_ref:DI ("bar3") [flags 0x41]  
<function_decl 0x7f21be3c0e00 bar3>) [0 bar3 S1 A8])
        (const_int 0 [0])) "j.c":15:13 1469 {sibcall_di}
     (expr_list:REG_CALL_DECL (symbol_ref:DI ("bar3") [flags 0x41]  
<function_decl 0x7f21be3c0e00 bar3>)
        (nil))
    (nil))

They can be changed to

(jump_table_data 16 15 17 (addr_vec:DI [
            (symbol_ref:DI ("bar0") [flags 0x41]  <function_decl 0x7f4f1c5c0b00 
bar0>)
            (symbol_ref:DI ("bar1") [flags 0x41]  <function_decl 0x7f4f1c5c0c00 
bar1>)
            (symbol_ref:DI ("bar2") [flags 0x41]  <function_decl 0x7f4f1c5c0d00 
bar2>)
            (symbol_ref:DI ("bar3") [flags 0x41]  <function_decl 0x7f4f1c5c0e00 
bar3>)
            (symbol_ref:DI ("bar4") [flags 0x41]  <function_decl 0x7f4f1c5c0f00 
bar4>)
        ]))

if bar0/bar1/bar2/bar3/bar4 calls are sibcalls.

Instead of supporting symbol reference in jump label and jump table in
the full RTL optimization pipeline, which requires very invasive changes
to GCC RTL infrastructure, support symbol reference in jump label and
jump table for the pass which turning REG_EH_REGION notes back into
NOTE_INSN_EH_REGION notes and after:

1. Add a set_jump_target method to assign symbol reference to jump label.
2. Add condsibcall_p for conditional sibling call.
3. Add anycall_p to return true for call and conditional sibcall.
4. Replace CALL_P with anycall_p in except.cc, final.cc and function-abi.cc
to support conditional sibcall.
5. Return false for symbol reference in jump table check.
6. Update create_trace_edges and rtx_writer::print_rtx_operand_code_0 to
handle symbol reference in jump label.
7. Update to final_scan_insn_1 to handle symbol reference in jump table.
8. Document limitation of symbol reference support in jump label.

        * dwarf2cfi.c (create_trace_edges): Skip symbol reference in
        jump table and in JUMP_LABEL.  Short-circuit JUMP for the pure
        sibcall.
        * except.cc (sjlj_mark_call_sites): Replace CALL_P with
        anycall_p.
        (finish_eh_generation): Likewise.
        (insn_could_throw_p): Likewise.
        (can_nonlocal_goto): Likewise.
        (set_nothrow_function_flags): Also call condsibcall_p to check
        conditional sibcall.
        * final.cc (final_scan_insn_1): Support symbol reference in jump
        table.
        (collect_fn_hard_reg_usage): Replace CALL_P with anycall_p.
        * function-abi.cc (insn_callee_abi): Likewise.
        * jump.cc (condsibcall_p): New.
        * print-rtl.cc (rtx_writer::print_rtx_operand_code_0): Support
        symbol reference in JUMP_LABEL.
        * rtl.h (rtx_jump_insn::set_jump_target): New, with the rtx
        argument.
        * rtl.h (condsibcall_p): New.
        (anycall_p): Likewise.
        * rtlanal.cc (tablejump_p): Return false if JUMP_LABEL is a
        symbol reference.
        * config/i386/i386-expand.cc (ix86_notrack_prefixed_insn_p):
        Likewise.
        * doc/rtl.texi (addr_vec): Also allow symbol reference.
        (JUMP_LABEL): Likewise.

Signed-off-by: H.J. Lu <hjl.to...@gmail.com>
---
 gcc/config/i386/i386-expand.cc |  5 ++++-
 gcc/doc/rtl.texi               | 24 +++++++++++++----------
 gcc/dwarf2cfi.cc               | 20 ++++++++++++++++++-
 gcc/except.cc                  | 11 ++++++-----
 gcc/final.cc                   | 26 +++++++++++++++++++++---
 gcc/function-abi.cc            |  2 +-
 gcc/jump.cc                    | 36 ++++++++++++++++++++++++++++++++++
 gcc/print-rtl.cc               |  2 ++
 gcc/rtl.h                      | 32 ++++++++++++++++++++++++++++++
 gcc/rtlanal.cc                 |  5 ++++-
 10 files changed, 141 insertions(+), 22 deletions(-)

diff --git a/gcc/config/i386/i386-expand.cc b/gcc/config/i386/i386-expand.cc
index 7f0fdb6fa9e..0d0802692d1 100644
--- a/gcc/config/i386/i386-expand.cc
+++ b/gcc/config/i386/i386-expand.cc
@@ -25501,7 +25501,10 @@ ix86_notrack_prefixed_insn_p (rtx_insn *insn)
   if (JUMP_P (insn) && !flag_cet_switch)
     {
       rtx target = JUMP_LABEL (insn);
-      if (target == NULL_RTX || ANY_RETURN_P (target))
+      if (target == NULL_RTX
+         || ANY_RETURN_P (target)
+         /* Also check for conditional sibcall.  */
+         || SYMBOL_REF_P (target))
        return false;
 
       /* Check the jump is a switch table.  */
diff --git a/gcc/doc/rtl.texi b/gcc/doc/rtl.texi
index 089bb1c4ede..0976c9c82dd 100644
--- a/gcc/doc/rtl.texi
+++ b/gcc/doc/rtl.texi
@@ -3459,8 +3459,9 @@ insn, inside a @code{parallel}, or inside an expression.
 @findex addr_vec
 @item (addr_vec:@var{m} [@var{lr0} @var{lr1} @dots{}])
 Represents a table of jump addresses.  The vector elements @var{lr0},
-etc., are @code{label_ref} expressions.  The mode @var{m} specifies
-how much space is given to each address; normally @var{m} would be
+etc., are @code{label_ref} or @code{symbol_ref} expressions.  The mode
+@var{m} specifies how much space is given to each address; normally
+@var{m} would be
 @code{Pmode}.
 
 @findex addr_diff_vec
@@ -3780,14 +3781,17 @@ instruction to return from the current function, it is 
recorded as a
 accessed in the same way and in addition contain a field
 @code{JUMP_LABEL} which is defined once jump optimization has completed.
 
-For simple conditional and unconditional jumps, this field contains
-the @code{code_label} to which this insn will (possibly conditionally)
-branch.  In a more complex jump, @code{JUMP_LABEL} records one of the
-labels that the insn refers to; other jump target labels are recorded
-as @code{REG_LABEL_TARGET} notes.  The exception is @code{addr_vec}
-and @code{addr_diff_vec}, where @code{JUMP_LABEL} is @code{NULL_RTX}
-and the only way to find the labels is to scan the entire body of the
-insn.
+For unconditional jumps, this field contains the @code{code_label} to
+which this insn will branch.  For simple conditional jumps, this field
+contains the @code{code_label} or @code{symbol_ref} to which this insn
+will branch possibly conditionally.  @code{symbol_ref} should only be
+generated immediately before the pass which turning REG_EH_REGION notes
+back into NOTE_INSN_EH_REGION notes.  In a more complex jump,
+@code{JUMP_LABEL} records one of the labels that the insn refers to;
+other jump target labels are recorded as @code{REG_LABEL_TARGET} notes.
+The exception is @code{addr_vec} and @code{addr_diff_vec}, where
+@code{JUMP_LABEL} is @code{NULL_RTX} and the only way to find the labels
+is to scan the entire body of the insn.
 
 Return insns count as jumps, but their @code{JUMP_LABEL} is @code{RETURN}
 or @code{SIMPLE_RETURN}.
diff --git a/gcc/dwarf2cfi.cc b/gcc/dwarf2cfi.cc
index a5353d39e7e..d8f25123284 100644
--- a/gcc/dwarf2cfi.cc
+++ b/gcc/dwarf2cfi.cc
@@ -2646,6 +2646,8 @@ create_trace_edges (rtx_insn *insn)
   if (JUMP_P (insn))
     {
       rtx_jump_table_data *table;
+      bool sibcall_p = false;
+      bool label_p = false;
 
       if (find_reg_note (insn, REG_NON_LOCAL_GOTO, NULL_RTX))
        return;
@@ -2657,8 +2659,17 @@ create_trace_edges (rtx_insn *insn)
          n = GET_NUM_ELEM (vec);
          for (i = 0; i < n; ++i)
            {
-             rtx_insn *lab = as_a <rtx_insn *> (XEXP (RTVEC_ELT (vec, i), 0));
+             rtx l = RTVEC_ELT (vec, i);
+             if (SYMBOL_REF_P (l))
+               {
+                 /* A symbol reference must be a sibcall.  Skip it.  */
+                 sibcall_p = true;
+                 continue;
+               }
+             l = XEXP (l, 0);
+             rtx_insn *lab = as_a <rtx_insn *> (l);
              maybe_record_trace_start (lab, insn);
+             label_p = true;
            }
 
          /* Handle casesi dispatch insns.  */
@@ -2687,12 +2698,19 @@ create_trace_edges (rtx_insn *insn)
              maybe_record_trace_start (lab, insn);
            }
        }
+      /* A symbol reference must be a sibcall.  */
+      else if (SYMBOL_REF_P (JUMP_LABEL (insn)))
+       sibcall_p = true;
       else
        {
          rtx_insn *lab = JUMP_LABEL_AS_INSN (insn);
          gcc_assert (lab != NULL);
          maybe_record_trace_start (lab, insn);
        }
+
+      /* Check for the pure sibcall.  */
+      if (sibcall_p && !label_p)
+       return;
     }
   else if (CALL_P (insn))
     {
diff --git a/gcc/except.cc b/gcc/except.cc
index 0fe1e093489..2f37e4652b9 100644
--- a/gcc/except.cc
+++ b/gcc/except.cc
@@ -1154,7 +1154,7 @@ sjlj_mark_call_sites (void)
 
       /* Don't separate a call from it's argument loads.  */
       before = insn;
-      if (CALL_P (insn))
+      if (anycall_p (insn))
        before = find_first_parameter_load (insn, NULL);
 
       start_sequence ();
@@ -1567,7 +1567,7 @@ finish_eh_generation (void)
          gcc_assert (BB_HEAD (e->dest) == label_rtx (lp->post_landing_pad));
 
          redirect_edge_succ (e, BLOCK_FOR_INSN (lp->landing_pad));
-         e->flags |= (CALL_P (BB_END (bb))
+         e->flags |= (anycall_p (BB_END (bb))
                       ? EDGE_ABNORMAL | EDGE_ABNORMAL_CALL
                       : EDGE_ABNORMAL);
        }
@@ -1763,7 +1763,7 @@ insn_could_throw_p (const_rtx insn)
 {
   if (!flag_exceptions)
     return false;
-  if (CALL_P (insn))
+  if (anycall_p (insn))
     return true;
   if (INSN_P (insn) && cfun->can_throw_non_call_exceptions)
     return may_trap_p (PATTERN (insn));
@@ -1980,7 +1980,7 @@ insn_nothrow_p (const_rtx insn)
 bool
 can_nonlocal_goto (const rtx_insn *insn)
 {
-  if (nonlocal_goto_handler_labels && CALL_P (insn))
+  if (nonlocal_goto_handler_labels && anycall_p (insn))
     {
       rtx note = find_reg_note (insn, REG_EH_REGION, NULL_RTX);
       if (!note || INTVAL (XEXP (note, 0)) != INT_MIN)
@@ -2020,7 +2020,8 @@ set_nothrow_function_flags (void)
       {
         crtl->nothrow = 0;
 
-       if (!CALL_P (insn) || !SIBLING_CALL_P (insn))
+       if ((!CALL_P (insn) || !SIBLING_CALL_P (insn))
+           && !condsibcall_p (insn))
          {
            crtl->all_throwers_are_sibcalls = 0;
            return 0;
diff --git a/gcc/final.cc b/gcc/final.cc
index 12c6eb0ac09..bff6bf3fd86 100644
--- a/gcc/final.cc
+++ b/gcc/final.cc
@@ -2524,13 +2524,33 @@ final_scan_insn_1 (rtx_insn *insn, FILE *file, int 
optimize_p ATTRIBUTE_UNUSED,
              }
 #else
            vlen = XVECLEN (body, GET_CODE (body) == ADDR_DIFF_VEC);
+#ifdef ASM_OUTPUT_ADDR_VEC_ELT
+           const char *asm_op = integer_asm_op (POINTER_SIZE_UNITS, true);
+#endif
            for (idx = 0; idx < vlen; idx++)
              {
                if (GET_CODE (body) == ADDR_VEC)
                  {
 #ifdef ASM_OUTPUT_ADDR_VEC_ELT
-                   ASM_OUTPUT_ADDR_VEC_ELT
-                     (file, CODE_LABEL_NUMBER (XEXP (XVECEXP (body, 0, idx), 
0)));
+                   rtx l = XVECEXP (body, 0, idx);
+                   if (SYMBOL_REF_P (l))
+                     {
+                       gcc_assert (asm_op != NULL);
+                       fprintf (file, "%s", asm_op);
+                       assemble_external (SYMBOL_REF_DECL (l));
+#ifdef ASM_OUTPUT_SYMBOL_REF
+                       ASM_OUTPUT_SYMBOL_REF (file, l);
+#else
+                       assemble_name (file, XSTR (l, 0));
+#endif
+                       putc ('\n', file);
+                     }
+                   else
+                     {
+                       l = XEXP (l, 0);
+                       ASM_OUTPUT_ADDR_VEC_ELT
+                         (file, CODE_LABEL_NUMBER (l));
+                     }
 #else
                    gcc_unreachable ();
 #endif
@@ -4604,7 +4624,7 @@ collect_fn_hard_reg_usage (void)
       if (!NONDEBUG_INSN_P (insn))
        continue;
 
-      if (CALL_P (insn)
+      if (anycall_p (insn)
          && !self_recursive_call_p (insn))
        function_used_regs
          |= insn_callee_abi (insn).full_and_partial_reg_clobbers ();
diff --git a/gcc/function-abi.cc b/gcc/function-abi.cc
index d500657b49e..3cabe67af73 100644
--- a/gcc/function-abi.cc
+++ b/gcc/function-abi.cc
@@ -218,7 +218,7 @@ fndecl_abi (const_tree fndecl)
 function_abi
 insn_callee_abi (const rtx_insn *insn)
 {
-  gcc_assert (insn && CALL_P (insn));
+  gcc_assert (insn && anycall_p (insn));
 
   if (flag_ipa_ra)
     if (tree fndecl = get_call_fndecl (insn))
diff --git a/gcc/jump.cc b/gcc/jump.cc
index 02df75ab08e..b7768a8b53f 100644
--- a/gcc/jump.cc
+++ b/gcc/jump.cc
@@ -1011,6 +1011,42 @@ jump_to_label_p (const rtx_insn *insn)
   return (JUMP_P (insn)
          && JUMP_LABEL (insn) != NULL && !ANY_RETURN_P (JUMP_LABEL (insn)));
 }
+
+/* Return true if INSN has a conditional sibling call.  */
+
+bool
+condsibcall_p (const rtx_insn *insn)
+{
+  if (!JUMP_P (insn))
+    return false;
+
+  rtx label = JUMP_LABEL (insn);
+  if (label == nullptr || ANY_RETURN_P (label))
+    return false;
+
+  /* Check the direct conditional sibling call.  */
+  if (SYMBOL_REF_P (label))
+    return true;
+
+  /* Check jump table with the indirect conditional sibling call.  */
+  rtx_insn *target = as_a<rtx_insn *> (label);
+  rtx_insn *table = next_insn (target);
+  if (!table
+      || !JUMP_TABLE_DATA_P (table)
+      || GET_CODE (PATTERN (table)) != ADDR_VEC)
+    return false;
+
+  rtx body = PATTERN (table);
+  unsigned int len = XVECLEN (body, 0);
+  for (unsigned int i = 0; i < len; i++)
+    {
+      rtx target = XVECEXP (body, 0, i);
+      if (SYMBOL_REF_P (target))
+       return true;
+    }
+
+  return false;
+}
 
 /* Find all CODE_LABELs referred to in X, and increment their use
    counts.  If INSN is a JUMP_INSN and there is at least one
diff --git a/gcc/print-rtl.cc b/gcc/print-rtl.cc
index 033f7e7aab0..3ed7130ee1c 100644
--- a/gcc/print-rtl.cc
+++ b/gcc/print-rtl.cc
@@ -308,6 +308,8 @@ rtx_writer::print_rtx_operand_code_0 (const_rtx in_rtx 
ATTRIBUTE_UNUSED,
        fprintf (m_outfile, "return");
       else if (GET_CODE (JUMP_LABEL (in_rtx)) == SIMPLE_RETURN)
        fprintf (m_outfile, "simple_return");
+      else if (SYMBOL_REF_P (JUMP_LABEL (in_rtx)))
+       print_rtx_operand_code_0 (JUMP_LABEL (in_rtx), idx);
       else
        fprintf (m_outfile, "%d", INSN_UID (JUMP_LABEL (in_rtx)));
     }
diff --git a/gcc/rtl.h b/gcc/rtl.h
index cc25aed1f49..0d665b74c7a 100644
--- a/gcc/rtl.h
+++ b/gcc/rtl.h
@@ -625,6 +625,7 @@ public:
   /* Set jump target.  */
 
   inline void set_jump_target (rtx_code_label *);
+  inline void set_jump_target (rtx);
 };
 
 struct GTY(()) rtx_call_insn : public rtx_insn
@@ -1895,6 +1896,12 @@ inline void rtx_jump_insn::set_jump_target 
(rtx_code_label *target)
   JUMP_LABEL (this) = target;
 }
 
+inline void rtx_jump_insn::set_jump_target (rtx target)
+{
+  gcc_assert (SYMBOL_REF_P (target) || LABEL_P (target));
+  JUMP_LABEL (this) = target;
+}
+
 /* Once basic blocks are found, each CODE_LABEL starts a chain that
    goes through all the LABEL_REFs that jump to that label.  The chain
    eventually winds up at the CODE_LABEL: it is circular.  */
@@ -4175,6 +4182,7 @@ extern bool jump_to_label_p (const rtx_insn *);
 extern bool condjump_p (const rtx_insn *);
 extern bool any_condjump_p (const rtx_insn *);
 extern bool any_uncondjump_p (const rtx_insn *);
+extern bool condsibcall_p (const rtx_insn *);
 extern rtx pc_set (const rtx_insn *);
 extern rtx condjump_label (const rtx_insn *);
 extern bool simplejump_p (const rtx_insn *);
@@ -4635,6 +4643,30 @@ word_register_operation_p (const_rtx x)
     }
 }
 
+/* Return true if INSN is a conditional sibcall.  */
+
+inline bool
+condsibcall_p (const_rtx insn)
+{
+  return condsibcall_p (as_a <const rtx_insn *> (insn));
+}
+
+/* Return true if INSN is a call or a conditional sibcall.  */
+
+inline bool
+anycall_p (const rtx_insn *insn)
+{
+  return CALL_P (insn) || condsibcall_p (insn);
+}
+
+/* Return true if INSN is a call or a conditional sibcall.  */
+
+inline bool
+anycall_p (const_rtx insn)
+{
+  return CALL_P (insn) || condsibcall_p (insn);
+}
+
 /* Holds an rtx comparison to simplify passing many parameters pertaining to a
    single comparison.  */
 
diff --git a/gcc/rtlanal.cc b/gcc/rtlanal.cc
index 86a5e473308..915a5d4634d 100644
--- a/gcc/rtlanal.cc
+++ b/gcc/rtlanal.cc
@@ -3539,7 +3539,10 @@ tablejump_p (const rtx_insn *insn, rtx_insn **labelp,
     return false;
 
   rtx target = JUMP_LABEL (insn);
-  if (target == NULL_RTX || ANY_RETURN_P (target))
+  if (target == NULL_RTX
+      || ANY_RETURN_P (target)
+      /* Also check for conditional sibcall.  */
+      || SYMBOL_REF_P (target))
     return false;
 
   rtx_insn *label = as_a<rtx_insn *> (target);
-- 
2.49.0

Reply via email to