https://gcc.gnu.org/g:80395d8d52da33478a757465dcd3032afc762693
commit r17-5647-g80395d8d52da33478a757465dcd3032afc762693 Author: Takayuki 'January June' Suwa <[email protected]> Date: Tue Oct 27 16:10:05 2026 +0900 xtensa: Implement xtensa_rtl_combine_reached_p() predicate function For example, in the Xtensa ISA, there are no machine instructions for bitwise inversion (1's complement) or immediate-form XOR. Consequently, implementing the former requires either assigning -1 into a scratch register and performing a reg-reg XOR, or adding 1 to the source value and inverting its sign. The former implementation offers opportunities for CSE and loop- invariant hoisting, while the latter results in lower register pressure. Thus, when implementing an instruction that cannot be reduced to a single machine instruction, there are trade-offs regarding optimization depending on whether or not a scratch register containing a constant value is used. Now, looking at the optimization process after the RTL generation pass, we see that loop optimization follows forward propagation and CSE, and then instruction combination optimization. Following this fact, it would be better to use constant-value scratch registers for insns that are material- ized early during RTL passes, such as those following the define_expand MD pattern, while such scratch registers should be avoided whenever possible for non-standard name patterns (often described by define_insn_and_split) found in the later instruction combination pass. Even more conveniently, in the instruction combination pass, registers that have been subjected to CSE or loop-invariant hoisting are not used in the search for instruc- tions to combine. However, defining a insn(_and_split) MD pattern naturally means that the instruction recognizer will begin to recognize that pattern. In practice, because recog() is conveniently invoked during early RTL passes (such as in forwprop), patterns that do not use constant-value scratch registers appear before CSE or loop optimization passes, and the intended optimization may not be achieved. This patch introduces a new target-specific predicate function that is false until the instruction combination RTL pass is reached, thereby providing a means to counteract the above disadvantage. /* example #1 */ int test0(int a) { return ~a; } int test1(int a[]) { int i; for (i = 0; i < 16; ++i) a[i] = ~a[i]; } /* after #1 (-O2) */ test0: entry sp, 32 addi.n a2, a2, 1 ;; "*one_cmplsi2" insn_and_split neg a2, a2 ;; before this patch, ;; "movi.n a8, -1; xor a2, a2, a8" retw.n test1: entry sp, 32 movi.n a10, -1 ;; hoisted out movi.n a9, 0x10 loop a9, .L4_LEND .L4: l32i.n a8, a2, 0 xor a8, a8, a10 ;; "one_cmplsi2" standard generation s32i.n a8, a2, 0 addi.n a2, a2, 4 .L4_LEND: retw.n /* example #2 */ int test0(int a) { return a & 0x0001FFFF; } int test1(int a[]) { int i; for (i = 0; i < 16; ++i) a[i] &= 0x0001FFFF; } /* after #2 (-O2) */ test0: entry sp, 32 slli a2, a2, 15 ;; "*andsi3_const_pow2_minus_one" srli a2, a2, 15 ;; insn_and_split retw.n test1: entry sp, 32 movi.n a10, -1 ;; hoisted out srli a10, a10, 15 ;; movi.n a9, 0x10 loop a9, .L4_LEND .L4: l32i.n a8, a2, 0 and a8, a8, a10 ;; "andsi3" standard generation ;; before this patch, ;; "slli a8, a8, 15; srli a8, a8, 15" s32i.n a8, a2, 0 addi.n a2, a2, 4 .L4_LEND: retw.n gcc/ChangeLog: * config/xtensa/xtensa-passes.def (pass_xtensa_before_combine): New directive that inserts pass_xtensa_before_combine immediately before pass_combine. * config/xtensa/xtensa-protos.h (xtensa_rtl_combine_reached_p, make_pass_xtensa_before_combine): New function prototypes. * config/xtensa/xtensa.cc (machine_function): Add a new member "rtl_combine_reached". (xtensa_rtl_combine_reached_p, make_pass_xtensa_before_combine): New function definitions. (pass_data_xtensa_before_combine, pass_xtensa_before_combine): New target-specific hidden pass definition, whose sole purpose is to set cfun->machine->rtl_combine_reached to true. * config/xtensa/xtensa.md (*subsi3_from_const, *andsi3_const_pow2_minus_one) (*andsi3_const_negative_pow2, *andsi3_const_shifted_mask): Add the condition that xtensa_rtl_combine_reached_p() is true to the "condition" of each MD definition. (*one_cmplsi2): New insn_and_split pattern. Diff: --- gcc/config/xtensa/xtensa-passes.def | 1 + gcc/config/xtensa/xtensa-protos.h | 2 ++ gcc/config/xtensa/xtensa.cc | 54 +++++++++++++++++++++++++++++++++++++ gcc/config/xtensa/xtensa.md | 33 +++++++++++++++++++---- 4 files changed, 85 insertions(+), 5 deletions(-) diff --git a/gcc/config/xtensa/xtensa-passes.def b/gcc/config/xtensa/xtensa-passes.def index e1c636cc4a3f..d4243b683ce6 100644 --- a/gcc/config/xtensa/xtensa-passes.def +++ b/gcc/config/xtensa/xtensa-passes.def @@ -19,3 +19,4 @@ along with GCC; see the file COPYING3. If not see INSERT_PASS_AFTER (pass_postreload_cse, 1, pass_xtensa_largeconst1); INSERT_PASS_BEFORE (pass_regrename, 1, pass_xtensa_largeconst2); +INSERT_PASS_BEFORE (pass_combine, 1, pass_xtensa_before_combine); diff --git a/gcc/config/xtensa/xtensa-protos.h b/gcc/config/xtensa/xtensa-protos.h index 472b6c3d6c45..f5d4b8a2988f 100644 --- a/gcc/config/xtensa/xtensa-protos.h +++ b/gcc/config/xtensa/xtensa-protos.h @@ -62,6 +62,7 @@ extern bool xtensa_postreload_completed_p (void); extern char *xtensa_bswapsi2_output (rtx_insn *, const char *); extern bool xtensa_expand_load_force_l32 (rtx *, machine_mode, machine_mode, int); +extern bool xtensa_rtl_combine_reached_p (void); #ifdef TREE_CODE extern void init_cumulative_args (CUMULATIVE_ARGS *, int); @@ -84,5 +85,6 @@ extern HOST_WIDE_INT xtensa_initial_elimination_offset (int from, int to); extern const char **xtensa_get_config_strings (void); extern rtl_opt_pass *make_pass_xtensa_largeconst1 (gcc::context *); extern rtl_opt_pass *make_pass_xtensa_largeconst2 (gcc::context *); +extern rtl_opt_pass *make_pass_xtensa_before_combine (gcc::context *); #endif /* !__XTENSA_PROTOS_H__ */ diff --git a/gcc/config/xtensa/xtensa.cc b/gcc/config/xtensa/xtensa.cc index d0a5042ba2db..bfc8e8bfa013 100644 --- a/gcc/config/xtensa/xtensa.cc +++ b/gcc/config/xtensa/xtensa.cc @@ -110,6 +110,7 @@ struct GTY(()) machine_function rtx last_logues_a9_content; HARD_REG_SET eliminated_callee_saved; bool postreload_completed; + bool rtl_combine_reached; }; /* Referenced by the REGNO_REG_CLASS() macro. */ @@ -2636,6 +2637,16 @@ xtensa_bswapsi2_output (rtx_insn *insn, const char *output) } +/* Return true if the current optimization pass has reached the RTL insn + combination. */ + +bool +xtensa_rtl_combine_reached_p (void) +{ + return cfun && cfun->machine->rtl_combine_reached; +} + + /* Try to split an integer value into what are suitable for two consecutive immediate addition instructions, ADDI or ADDMI. */ @@ -6901,4 +6912,47 @@ make_pass_xtensa_largeconst2 (gcc::context *ctxt) return new pass_xtensa_largeconst2 (ctxt); } +/* "Hidden" machine-specific pass only in order to detect when the current + pass has reached the RTL instruction combination pass. */ + +namespace +{ + +const pass_data pass_data_xtensa_before_combine = +{ + RTL_PASS, /* type */ + "*xt_before_combine", /* name */ + OPTGROUP_NONE, /* optinfo_flags */ + TV_MACH_DEP, /* tv_id */ + 0, /* properties_required */ + 0, /* properties_provided */ + 0, /* properties_destroyed */ + 0, /* todo_flags_start */ + 0, /* todo_flags_finish */ +}; + +class pass_xtensa_before_combine : public rtl_opt_pass +{ +public: + pass_xtensa_before_combine (gcc::context *ctxt) + : rtl_opt_pass (pass_data_xtensa_before_combine, ctxt) + {} + + /* opt_pass methods: */ + unsigned int execute (function *fun) final override + { + fun->machine->rtl_combine_reached = true; + return 0; + } + +}; // class pass_xtensa_before_combine + +} // anon namespace + +rtl_opt_pass * +make_pass_xtensa_before_combine (gcc::context *ctxt) +{ + return new pass_xtensa_before_combine (ctxt); +} + #include "gt-xtensa.h" diff --git a/gcc/config/xtensa/xtensa.md b/gcc/config/xtensa/xtensa.md index d7fc098eeab2..157236c2c319 100644 --- a/gcc/config/xtensa/xtensa.md +++ b/gcc/config/xtensa/xtensa.md @@ -339,8 +339,9 @@ [(set (match_operand:SI 0 "register_operand" "=a") (minus:SI (match_operand:SI 1 "const_int_operand" "") (match_operand:SI 2 "register_operand" "r")))] - "xtensa_simm8 (-INTVAL (operands[1])) - || xtensa_simm8x256 (-INTVAL (operands[1]))" + "xtensa_rtl_combine_reached_p () + && (xtensa_simm8 (-INTVAL (operands[1])) + || xtensa_simm8x256 (-INTVAL (operands[1])))" "#" "&& 1" [(set (match_dup 0) @@ -757,6 +758,25 @@ DONE; }) +(define_insn_and_split "*one_cmplsi2" + [(set (match_operand:SI 0 "register_operand" "=a") + (not:SI (match_operand:SI 1 "register_operand" "r")))] + "xtensa_rtl_combine_reached_p ()" + "#" + "&& reload_completed" + [(set (match_dup 0) + (plus:SI (match_dup 1) + (const_int 1))) + (set (match_dup 0) + (neg:SI (match_dup 0)))] + "" + [(set_attr "type" "arith") + (set_attr "mode" "SI") + (set (attr "length") + (if_then_else (match_test "TARGET_DENSITY") + (const_int 5) + (const_int 6)))]) + (define_insn "neg<mode>2" [(set (match_operand:SDF 0 "register_operand" "=f") (neg:SDF (match_operand:SDF 1 "register_operand" "f")))] @@ -872,7 +892,8 @@ [(set (match_operand:SI 0 "register_operand" "=a") (and:SI (match_operand:SI 1 "register_operand" "r") (match_operand:SI 2 "const_int_operand" "")))] - "IN_RANGE (exact_log2 (INTVAL (operands[2]) + 1), 17, 31)" + "xtensa_rtl_combine_reached_p () + && IN_RANGE (exact_log2 (INTVAL (operands[2]) + 1), 17, 31)" "#" "&& 1" [(set (match_dup 0) @@ -896,7 +917,8 @@ [(set (match_operand:SI 0 "register_operand" "=a") (and:SI (match_operand:SI 1 "register_operand" "r") (match_operand:SI 2 "const_int_operand" "")))] - "IN_RANGE (exact_log2 (-INTVAL (operands[2])), 12, 31)" + "xtensa_rtl_combine_reached_p () + && IN_RANGE (exact_log2 (-INTVAL (operands[2])), 12, 31)" "#" "&& 1" [(set (match_dup 0) @@ -916,7 +938,8 @@ [(set (match_operand:SI 0 "register_operand" "=a") (and:SI (match_operand:SI 1 "register_operand" "r") (match_operand:SI 2 "shifted_mask_operand" "")))] - "! xtensa_simm12b (INTVAL (operands[2]))" + "xtensa_rtl_combine_reached_p () + && ! xtensa_simm12b (INTVAL (operands[2]))" "#" "&& 1" [(set (match_dup 0)
