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. Return false for symbol reference in jump table check. 4. Update create_trace_edges and rtx_writer::print_rtx_operand_code_0 to handle symbol reference in jump label. 5. Update to final_scan_insn_1 to handle symbol reference in jump table. 6. Update fndecl_abi and collect_fn_hard_reg_usage to support conditional sibling call for callee ABI. 7. 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. * final.cc (final_scan_insn_1): Support symbol reference in jump table. (collect_fn_hard_reg_usage): Also check conditional sibcall. * 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. * 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/final.cc | 26 +++++++++++++++++++++--- gcc/function-abi.cc | 2 +- gcc/jump.cc | 36 ++++++++++++++++++++++++++++++++++ gcc/print-rtl.cc | 2 ++ gcc/rtl.h | 8 ++++++++ gcc/rtlanal.cc | 5 ++++- 9 files changed, 111 insertions(+), 17 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/final.cc b/gcc/final.cc index 12c6eb0ac09..16c2782c01a 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 ((CALL_P (insn) || condsibcall_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..7aa82b3a8cf 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 && (CALL_P (insn) || condsibcall_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..e5a615631a3 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 *); 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