Hi Torbjorn, > On 14 Sep 2026, at 08:47, Torbjorn SVENSSON <[email protected]> > wrote: > > Hi, > > This patch appears to introduce a regression for Cortex-M3/4/33. Other > Cortex-M targets that I test are either fine or unsupported. > > Testing arm/short-it-ifcvt-2.c > doing compile > Executing on host: /build/r17-3970-g301afaab5b8eaf/bin/arm-none-eabi-gcc > /build/gcc_src/gcc/testsuite/gcc.target/arm/short-it-ifcvt-2.c -mthumb > -march=armv7-m -mcpu=cortex-m3 -mfloat-abi=soft -mfpu=auto > -fdiagnostics-plain-output -O2 -mthumb -mrestrict-it -ffat-lto-objects > -fno-ident -S -o short-it-ifcvt-2.s (timeout = 800) > spawn -ignore SIGHUP /build/r17-3970-g301afaab5b8eaf/bin/arm-none-eabi-gcc > /build/gcc_src/gcc/testsuite/gcc.target/arm/short-it-ifcvt-2.c -mthumb > -march=armv7-m -mcpu=cortex-m3 -mfloat-abi=soft -mfpu=auto > -fdiagnostics-plain-output -O2 -mthumb -mrestrict-it -ffat-lto-objects > -fno-ident -S -o short-it-ifcvt-2.s > pid is 3087 -3087 > pid is -1 > output is status 0 > PASS: gcc.target/arm/short-it-ifcvt-2.c (test for excess errors) > FAIL: gcc.target/arm/short-it-ifcvt-2.c scan-assembler b(gt|le) > > Produced assembler with r17-3970-g301afaab5b8eaf: > .cpu cortex-m3 > .arch armv7-m > .fpu softvfp > .eabi_attribute 20, 1 > .eabi_attribute 21, 1 > .eabi_attribute 23, 3 > .eabi_attribute 24, 1 > .eabi_attribute 25, 1 > .eabi_attribute 26, 1 > .eabi_attribute 30, 2 > .eabi_attribute 34, 1 > .eabi_attribute 18, 4 > .file "short-it-ifcvt-2.c" > .text > .align 1 > .p2align 2,,3 > .global f1 > .syntax unified > .thumb > .thumb_func > .type f1, %function > f1: > @ args = 0, pretend = 0, frame = 0 > @ frame_needed = 0, uses_anonymous_args = 0 > @ link register save eliminated. > cmp r0, #101 > add r3, r0, #1 > itte lt > rsblt r1, r1, #0 > rsblt r3, r0, #0 > rsbge r2, r2, #0 > add r3, r3, r1 > adds r0, r3, r2 > bx lr > .size f1, .-f1 > > Produced assembler with r17-3969-g17b02d8f2b385c: > .cpu cortex-m3 > .arch armv7-m > .fpu softvfp > .eabi_attribute 20, 1 > .eabi_attribute 21, 1 > .eabi_attribute 23, 3 > .eabi_attribute 24, 1 > .eabi_attribute 25, 1 > .eabi_attribute 26, 1 > .eabi_attribute 30, 2 > .eabi_attribute 34, 1 > .eabi_attribute 18, 4 > .file "short-it-ifcvt-2.c" > .text > .align 1 > .p2align 2,,3 > .global f1 > .syntax unified > .thumb > .thumb_func > .type f1, %function > f1: > @ args = 0, pretend = 0, frame = 0 > @ frame_needed = 0, uses_anonymous_args = 0 > @ link register save eliminated. > cmp r0, #100 > iteet gt > addgt r0, r0, #1 > rsble r0, r0, #0 > rsble r1, r1, #0 > rsbgt r2, r2, #0 > add r0, r0, r1 > add r0, r0, r2 > bx lr > .size f1, .-f1 > > I do not know what the correct action here is since the expression, > > /* { dg-final { scan-assembler "b(gt|le)" } } */ > > passes for some targets, like Cortex-M85, but fails for others. > > Let me know if you want me to test something. >
It’s a testism. I’ll fix it with this attached patch. Thanks, Kyrill > Kind regards, > Torbjörn > > On 2026-09-01 07:38, [email protected] wrote: >> From: Kyrylo Tkachov <[email protected]> >> The diamond conversion currently requires one arm's live-out destinations >> to contain the other's. This rejects diamonds in which the arms update >> different values, although a destination can keep its incoming value on the >> path that does not assign it. >> For example, convert >> then: else: >> x = a; z = c; >> y = b; w = d; >> to >> z_tmp = c; >> w_tmp = d; >> x = cond ? a : x; >> y = cond ? b : y; >> z = cond ? z : z_tmp; >> w = cond ? w : w_tmp; >> Build the metadata for both arms once. Append an output entry for the last >> else definition of each live-out destination that the then arm does not >> define. The existing conditional-move loop then selects between the else >> temporary and the incoming value for these entries. It continues to select >> between the then and else values for shared entries. >> Form the union of the live-out destinations and permit arm-only destinations >> and one-set arms. Do not restrict partially overlapping sets based on the >> source of a shared destination. Keep the CFG arm order and evaluate the else >> arm into temporaries. Include the complete live-out union and the appended >> output entries in the conversion limit. >> Add execution and code-generation tests for disjoint and partially >> overlapping outputs, one-set arms, cross-arm incoming values, repeated >> definitions, condition inputs, unequal output counts, condition-code >> clobbers, and the final output limit. >> Bootstrapped and tested on aarch64-none-linux-gnu. >> Ok fro trunk? >> Thanks, >> Kyrill >> gcc/ChangeLog: >> PR tree-optimization/125557 >> * ifcvt.cc (noce_convert_multiple_sets): Build metadata for both arms. >> Append entries for else-only live-out destinations, enforce the final >> output limit, and preserve the then-arm source location. >> (noce_convert_multiple_sets_1): Consume the unified output metadata. >> Select incoming values for destinations defined by only one arm. >> (init_noce_multiple_sets_info): Initialize stable metadata fields. >> (bb_ok_for_noce_convert_multiple_sets): Remove final-source >> classification. >> (noce_process_if_block): Form the union of the arm live-outs, accept >> arm-only destinations and one-set arms without a shared-source gate, and >> keep the CFG arm order. >> gcc/testsuite/ChangeLog: >> PR tree-optimization/125557 >> * gcc.target/aarch64/ifcvt_multiple_sets_diamond_4.c: Test disjoint, >> partially overlapping, and one-set outputs. >> * gcc.target/aarch64/ifcvt_multiple_sets_diamond_5.c: Update expected >> code generation for the fixed arm order. >> * gcc.target/aarch64/ifcvt_multiple_sets_diamond_6.c: New test. >> * gcc.target/aarch64/ifcvt_multiple_sets_diamond_7.c: New test. >> * gcc.target/i386/ifcvt-multiple-sets-diamond-1.c: Test disjoint outputs >> with a condition-code clobber. >> Co-authored-by: Cui Lili <[email protected]> >> Signed-off-by: Kyrylo Tkachov <[email protected]> >> --- >> gcc/ifcvt.cc | 237 +++++++++--------- >> .../aarch64/ifcvt_multiple_sets_diamond_4.c | 83 +++--- >> .../aarch64/ifcvt_multiple_sets_diamond_5.c | 3 +- >> .../aarch64/ifcvt_multiple_sets_diamond_6.c | 100 ++++++++ >> .../aarch64/ifcvt_multiple_sets_diamond_7.c | 26 ++ >> .../i386/ifcvt-multiple-sets-diamond-1.c | 25 +- >> 6 files changed, 315 insertions(+), 159 deletions(-) >> create mode 100644 >> gcc/testsuite/gcc.target/aarch64/ifcvt_multiple_sets_diamond_6.c >> create mode 100644 >> gcc/testsuite/gcc.target/aarch64/ifcvt_multiple_sets_diamond_7.c >> diff --git a/gcc/ifcvt.cc b/gcc/ifcvt.cc >> index b976ac9acc3..049e2560298 100644 >> --- a/gcc/ifcvt.cc >> +++ b/gcc/ifcvt.cc >> @@ -102,7 +102,9 @@ static rtx_insn *block_has_only_trap (basic_block); >> static void init_noce_multiple_sets_info (basic_block, >> auto_delete_vec<noce_multiple_sets_info> &); >> static bool noce_convert_multiple_sets_1 (struct noce_if_info *, >> - auto_delete_vec<noce_multiple_sets_info> &, int *, bool *); >> + auto_delete_vec<noce_multiple_sets_info> &, >> + auto_delete_vec<noce_multiple_sets_info> &, unsigned, >> + const vec<unsigned> &, int *, bool *); >> >> /* Count the number of non-jump active insns in BB. */ >> @@ -3970,7 +3972,7 @@ try_emit_cmove_seq (struct noce_if_info *if_info, rtx >> temp, >> conditional set to use the temporary we introduced earlier. >> IF_INFO contains the useful information about the block structure and >> - jump instructions. For an IF-THEN-ELSE-JOIN, first evaluate the >> secondary >> + jump instructions. For an IF-THEN-ELSE-JOIN, first evaluate the else >> arm's sets into temporaries and retain their final values for the >> conditional moves. Return true if the replacement is valid and >> profitable >> and its CFG changes have been committed, otherwise return false. */ >> @@ -3995,13 +3997,59 @@ noce_convert_multiple_sets (struct noce_if_info >> *if_info) >> auto_delete_vec<noce_multiple_sets_info> insn_info; >> init_noce_multiple_sets_info (then_bb, insn_info); >> + gcc_assert (!insn_info.is_empty ()); >> + unsigned then_count = insn_info.length (); >> + location_t sequence_location >> + = INSN_LOCATION (insn_info.last ()->unmodified_insn); >> + >> + auto_delete_vec<noce_multiple_sets_info> else_insn_info; >> + auto_vec<unsigned> else_only_indices; >> + if (else_bb) >> + { >> + init_noce_multiple_sets_info (else_bb, else_insn_info); >> + >> + /* Add one output entry for each live-out destination that is set only >> + by the else arm. Record the last definition, which holds the >> + value that reaches the join. */ >> + auto_bitmap then_dests, seen; >> + for (unsigned i = 0; i < then_count; ++i) >> + bitmap_set_bit (then_dests, REGNO (insn_info[i]->target)); >> + >> + for (int i = else_insn_info.length () - 1; i >= 0; --i) >> + if (else_insn_info[i]->need_cmov >> + && !bitmap_bit_p (then_dests, >> + REGNO (else_insn_info[i]->target)) >> + && bitmap_set_bit (seen, REGNO (else_insn_info[i]->target))) >> + else_only_indices.safe_push (i); >> + else_only_indices.reverse (); >> + >> + unsigned i, else_index; >> + FOR_EACH_VEC_ELT (else_only_indices, i, else_index) >> + { >> + noce_multiple_sets_info *info = new noce_multiple_sets_info; >> + noce_multiple_sets_info *else_info = else_insn_info[else_index]; >> + info->target = else_info->target; >> + info->temporary = NULL_RTX; >> + info->unmodified_insn = else_info->unmodified_insn; >> + info->need_cmov = true; >> + insn_info.safe_push (info); >> + } >> + } >> + >> + /* Include else-only outputs when enforcing the conversion limit. */ >> + if (insn_info.length () > (unsigned) param_max_rtl_if_conversion_insns) >> + { >> + end_sequence (); >> + return false; >> + } >> int last_needs_comparison = -1; >> bool use_cond_earliest = false; >> bool ok = noce_convert_multiple_sets_1 >> - (if_info, insn_info, &last_needs_comparison, &use_cond_earliest); >> + (if_info, insn_info, else_insn_info, then_count, else_only_indices, >> + &last_needs_comparison, &use_cond_earliest); >> if (!ok) >> return false; >> @@ -4013,17 +4061,14 @@ noce_convert_multiple_sets (struct noce_if_info >> *if_info) >> end_sequence (); >> start_sequence (); >> ok = noce_convert_multiple_sets_1 >> - (if_info, insn_info, &last_needs_comparison, &use_cond_earliest); >> + (if_info, insn_info, else_insn_info, then_count, else_only_indices, >> + &last_needs_comparison, &use_cond_earliest); >> /* Actually we should not fail anymore if we reached here, >> but better still check. */ >> if (!ok) >> return false; >> - /* We must have seen some sort of insn to insert, otherwise we were >> - given an empty BB to convert, and we can't handle that. */ >> - gcc_assert (!insn_info.is_empty ()); >> - >> /* Now fixup the assignments. >> PR116405: Iterate in reverse order and keep track of the targets so >> that >> a move does not overwrite a subsequent value when multiple instructions >> @@ -4092,8 +4137,7 @@ noce_convert_multiple_sets (struct noce_if_info >> *if_info) >> if (noce_clobbers_live_cc_p (test_bb, seq)) >> return false; >> - emit_insn_before_setloc (seq, if_info->jump, >> - INSN_LOCATION (insn_info.last ()->unmodified_insn)); >> + emit_insn_before_setloc (seq, if_info->jump, sequence_location); >> /* Clean up the THEN (and, for a diamond, ELSE) block and the edges >> into and >> out of the if-region. An IF-THEN-ELSE-JOIN has no test->join edge. >> @@ -4123,8 +4167,10 @@ noce_convert_multiple_sets (struct noce_if_info >> *if_info) >> } >> /* Try to emit the multiple-set conversion described by IF_INFO. >> INSN_INFO >> - holds the primary-arm metadata. For a diamond, evaluate the secondary >> arm >> - first and retain its final values for the conditional moves. >> + holds THEN_COUNT then-arm entries followed by entries for the else-only >> + definitions recorded in ELSE_ONLY_INDICES. For a diamond, >> + evaluate ELSE_INSN_INFO first and retain its final values for the >> + conditional moves. >> LAST_NEEDS_COMPARISON is -1 on the first attempt. Record in it the >> last set >> that needs a temporary to preserve the comparison, then use that boundary >> @@ -4135,10 +4181,13 @@ noce_convert_multiple_sets (struct noce_if_info >> *if_info) >> static bool >> noce_convert_multiple_sets_1 (struct noce_if_info *if_info, >> auto_delete_vec<noce_multiple_sets_info> &insn_info, >> + auto_delete_vec<noce_multiple_sets_info> >> + &else_insn_info, >> + unsigned then_count, >> + const vec<unsigned> &else_only_indices, >> int *last_needs_comparison, >> bool *use_cond_earliest) >> { >> - basic_block then_bb = if_info->then_bb; >> rtx_insn *jump = if_info->jump; >> rtx_insn *cond_earliest; >> @@ -4152,19 +4201,16 @@ noce_convert_multiple_sets_1 (struct noce_if_info >> *if_info, >> if (rev_cc_cmp) >> rev_cc_cmp = copy_rtx (rev_cc_cmp); >> - rtx_insn *insn; >> int count = 0; >> bool second_try = *last_needs_comparison != -1; >> *use_cond_earliest = false; >> - auto_delete_vec<noce_multiple_sets_info> else_insn_info; >> /* For an IF-THEN-ELSE-JOIN, emit the else block's computations first >> into >> fresh temporaries. This leaves the incoming register values available >> to >> - the then block. The conditional moves below select the then values or >> - these else values. */ >> + the then arm. The conditional moves below select the then or else >> + values. */ >> if (if_info->else_bb) >> { >> - init_noce_multiple_sets_info (if_info->else_bb, else_insn_info); >> int else_count = 0; >> rtx_insn *before_else = get_last_insn (); >> location_t saved_location = curr_insn_location (); >> @@ -4190,9 +4236,7 @@ noce_convert_multiple_sets_1 (struct noce_if_info >> *if_info, >> set_curr_insn_location (INSN_LOCATION (else_insn)); >> rtx temporary = copy_to_mode_reg (GET_MODE (target), value); >> - info->target = target; >> info->temporary = temporary; >> - info->unmodified_insn = else_insn; >> else_count++; >> } >> @@ -4224,39 +4268,50 @@ noce_convert_multiple_sets_1 (struct noce_if_info >> *if_info, >> } >> } >> - FOR_BB_INSNS (then_bb, insn) >> + for (count = 0; count < (int) insn_info.length (); ++count) >> { >> - /* Skip over non-insns. */ >> - if (!active_insn_p (insn)) >> - continue; >> - >> noce_multiple_sets_info *info = insn_info[count]; >> - >> + rtx_insn *insn = info->unmodified_insn; >> rtx set = single_set (insn); >> gcc_checking_assert (set); >> - rtx target = SET_DEST (set); >> + rtx target = info->target; >> rtx temp; >> + rtx new_val; >> + rtx old_val; >> - rtx new_val = SET_SRC (set); >> + if ((unsigned) count < then_count) >> + { >> + new_val = SET_SRC (set); >> - int i, ii; >> - FOR_EACH_VEC_ELT (info->rewired_src, i, ii) >> - new_val = simplify_replace_rtx (new_val, insn_info[ii]->target, >> - insn_info[ii]->temporary); >> + int i, ii; >> + FOR_EACH_VEC_ELT (info->rewired_src, i, ii) >> + new_val = simplify_replace_rtx (new_val, insn_info[ii]->target, >> + insn_info[ii]->temporary); >> - rtx old_val = target; >> + old_val = target; >> - /* Use the final value assigned to TARGET on the else arm. Scanning >> in >> - reverse is important when the arm assigns the same register more than >> - once. */ >> - if (if_info->else_bb) >> - for (int j = else_insn_info.length () - 1; j >= 0; --j) >> - if (rtx_equal_p (target, else_insn_info[j]->target)) >> - { >> - old_val = else_insn_info[j]->temporary; >> - break; >> - } >> + /* Use the final value assigned to TARGET on the else arm. >> + Scanning in reverse is important when the arm assigns the same >> + register more than once. */ >> + if (if_info->else_bb) >> + for (int j = else_insn_info.length () - 1; j >= 0; --j) >> + if (rtx_equal_p (target, else_insn_info[j]->target)) >> + { >> + old_val = else_insn_info[j]->temporary; >> + break; >> + } >> + } >> + else >> + { >> + unsigned else_index >> + = else_only_indices[(unsigned) count - then_count]; >> + noce_multiple_sets_info *else_info = else_insn_info[else_index]; >> + gcc_checking_assert (rtx_equal_p (target, else_info->target)); >> + >> + new_val = target; >> + old_val = else_info->temporary; >> + } >> /* As we are transforming >> if (x > y) >> @@ -4451,11 +4506,7 @@ noce_convert_multiple_sets_1 (struct noce_if_info >> *if_info, >> emit_insn (seq); >> /* Bookkeeping. */ >> - count++; >> - >> - info->target = target; >> info->temporary = temp_dest; >> - info->unmodified_insn = insn; >> } >> /* Even if we did not actually need the comparison, we want to make sure >> @@ -4515,9 +4566,7 @@ init_noce_multiple_sets_info (basic_block bb, >> continue; >> noce_multiple_sets_info *info = new noce_multiple_sets_info; >> - info->target = NULL_RTX; >> info->temporary = NULL_RTX; >> - info->unmodified_insn = NULL; >> insn_info.safe_push (info); >> rtx set = single_set (insn); >> @@ -4527,6 +4576,8 @@ init_noce_multiple_sets_info (basic_block bb, >> rtx dest = SET_DEST (set); >> gcc_checking_assert (REG_P (dest) && !HARD_REGISTER_P (dest)); >> + info->target = dest; >> + info->unmodified_insn = insn; >> info->need_cmov = bitmap_bit_p (bb_live_out, REGNO (dest)); >> /* Check if the current SET's source mentions any previously seen >> @@ -4547,24 +4598,20 @@ init_noce_multiple_sets_info (basic_block bb, >> /* Return true iff basic block TEST_BB is suitable for conversion to a >> series of conditional moves. Unless REQUIRE_MULTIPLE is false, also >> check >> that we have more than one set (other routines can handle a single set >> - better than we would). A diamond arm may have a single set when it is >> - selected as the secondary arm. Require no more than >> - PARAM_MAX_RTL_IF_CONVERSION_INSNS sets. While going through the insns >> store >> - the sum of their potential costs in COST. On success, if LIVE_OUT_DESTS >> is >> - nonnull, record the distinct pseudo destinations that are live out of >> + better than we would). A diamond arm may have a single set. >> + Require no more than PARAM_MAX_RTL_IF_CONVERSION_INSNS sets. While going >> + through the insns, store the sum of their potential costs in COST. On >> + success, if LIVE_OUT_DESTS is nonnull, record the distinct pseudo >> + destinations that are live out of >> TEST_BB. If HAS_NON_SIMPLE_SRC is nonnull, set it when an instruction >> source is neither a constant nor a register operand. On success, if >> - NONREG_LIVE_OUT_DESTS is nonnull, record live-out destinations whose last >> - RTL definition has a non-register source. Constants are non-register >> - sources. On success, if INSN_COUNT is nonnull, store the number of >> active >> - sets in it. */ >> + INSN_COUNT is nonnull, store the number of active sets in it. */ >> static bool >> bb_ok_for_noce_convert_multiple_sets (basic_block test_bb, unsigned *cost, >> bool require_multiple = true, >> bitmap live_out_dests = NULL, >> bool *has_non_simple_src = NULL, >> - bitmap nonreg_live_out_dests = NULL, >> unsigned *insn_count = NULL) >> { >> rtx_insn *insn; >> @@ -4576,10 +4623,8 @@ bb_ok_for_noce_convert_multiple_sets (basic_block >> test_bb, unsigned *cost, >> bitmap_clear (live_out_dests); >> if (has_non_simple_src) >> *has_non_simple_src = false; >> - if (nonreg_live_out_dests) >> - bitmap_clear (nonreg_live_out_dests); >> bitmap bb_live_out = NULL; >> - if (live_out_dests || nonreg_live_out_dests) >> + if (live_out_dests) >> bb_live_out = df_get_live_out (test_bb); >> FOR_BB_INSNS (test_bb, insn) >> @@ -4619,17 +4664,7 @@ bb_ok_for_noce_convert_multiple_sets (basic_block >> test_bb, unsigned *cost, >> return false; >> if (bb_live_out && bitmap_bit_p (bb_live_out, REGNO (dest))) >> - { >> - if (live_out_dests) >> - bitmap_set_bit (live_out_dests, REGNO (dest)); >> - if (nonreg_live_out_dests) >> - { >> - if (register_operand (src, VOIDmode)) >> - bitmap_clear_bit (nonreg_live_out_dests, REGNO (dest)); >> - else >> - bitmap_set_bit (nonreg_live_out_dests, REGNO (dest)); >> - } >> - } >> + bitmap_set_bit (live_out_dests, REGNO (dest)); >> potential_cost += insn_cost (insn, speed_p); >> @@ -4641,7 +4676,7 @@ bb_ok_for_noce_convert_multiple_sets (basic_block >> test_bb, unsigned *cost, >> /* If we would only put out one conditional move, the other strategies >> this pass tries are better optimized and will be more appropriate, so >> require more than one set unless REQUIRE_MULTIPLE is false. A diamond >> - arm may have one set when it is selected as the secondary arm. >> + arm may have one set. >> Some targets want to strictly limit the number of conditional moves >> that are emitted, they set this through PARAM, we need to respect >> that. */ >> @@ -4697,9 +4732,8 @@ noce_process_if_block (struct noce_if_info *if_info) >> The later patterns require jumps to be more expensive. >> For the diamond case, defer diamonds with only register and constant >> assignments in both arms to the existing simple conditional-move >> - handling. Use an arm that sets at least two distinct live-out pseudos >> as >> - the primary arm. Every live-out destination set by the secondary arm >> - must also be set by the primary arm. >> + handling. Require at least two distinct live-out pseudos across the >> + arms. >> ??? For future expansion, further expand the "multiple X" rules. */ >> /* First look for multiple SETS. >> @@ -4713,8 +4747,6 @@ noce_process_if_block (struct noce_if_info *if_info) >> unsigned ms_then_insn_count = 0, ms_else_insn_count = 0; >> bool ms_then_has_non_simple_src, ms_else_has_non_simple_src; >> auto_bitmap ms_then_live_out_dests, ms_else_live_out_dests; >> - auto_bitmap ms_then_nonreg_live_out_dests; >> - auto_bitmap ms_else_nonreg_live_out_dests; >> noce_if_info ms_if_info = *if_info; >> bool multiple_sets_p = false; >> @@ -4727,48 +4759,25 @@ noce_process_if_block (struct noce_if_info *if_info) >> && bb_ok_for_noce_convert_multiple_sets >> (then_bb, &ms_then_cost, false, >> ms_then_live_out_dests, &ms_then_has_non_simple_src, >> - ms_then_nonreg_live_out_dests, &ms_then_insn_count) >> + &ms_then_insn_count) >> && bb_ok_for_noce_convert_multiple_sets >> (else_bb, &ms_else_cost, false, >> ms_else_live_out_dests, &ms_else_has_non_simple_src, >> - ms_else_nonreg_live_out_dests, &ms_else_insn_count)) >> + &ms_else_insn_count)) >> { >> - /* Keep this path for diamonds that share a live-out whose last RTL >> - definition has a non-register source in at least one arm. This >> - includes a value computed in one arm and forwarded by the >> - other. */ >> - bool has_shared_nonreg_live_out >> - = (bitmap_intersect_p (ms_then_nonreg_live_out_dests, >> - ms_else_live_out_dests) >> - || bitmap_intersect_p (ms_else_nonreg_live_out_dests, >> - ms_then_live_out_dests)); >> - >> - if ((ms_then_has_non_simple_src || ms_else_has_non_simple_src) >> - && has_shared_nonreg_live_out) >> - { >> - if (bitmap_count_bits (ms_then_live_out_dests) >= 2 >> - && !bitmap_intersect_compl_p (ms_else_live_out_dests, >> - ms_then_live_out_dests)) >> - multiple_sets_p = true; >> - else if (bitmap_count_bits (ms_else_live_out_dests) >= 2 >> - && !bitmap_intersect_compl_p (ms_then_live_out_dests, >> - ms_else_live_out_dests)) >> - { >> - /* The branch-target arm is the compatible multi-set >> - superset. Make it the primary arm and reverse the select >> - orientation. */ >> - std::swap (ms_if_info.then_bb, ms_if_info.else_bb); >> - ms_if_info.then_else_reversed >> - = !ms_if_info.then_else_reversed; >> - std::swap (ms_then_cost, ms_else_cost); >> - std::swap (ms_then_insn_count, ms_else_insn_count); >> - multiple_sets_p = true; >> - } >> - } >> + auto_bitmap ms_live_out_dests; >> + bitmap_ior (ms_live_out_dests, ms_then_live_out_dests, >> + ms_else_live_out_dests); >> + unsigned output_count = bitmap_count_bits (ms_live_out_dests); >> + >> + if (output_count >= 2 >> + && output_count <= (unsigned) param_max_rtl_if_conversion_insns >> + && (ms_then_has_non_simple_src || ms_else_has_non_simple_src)) >> + multiple_sets_p = true; >> } >> } >> - /* The diamond conversion evaluates the secondary arm unconditionally. >> + /* The diamond conversion evaluates the else arm unconditionally. >> Honor the target limit on the number of instructions made >> unconditional. */ >> if (multiple_sets_p >> diff --git >> a/gcc/testsuite/gcc.target/aarch64/ifcvt_multiple_sets_diamond_4.c >> b/gcc/testsuite/gcc.target/aarch64/ifcvt_multiple_sets_diamond_4.c >> index 428d7f51cc5..a6c8e400aee 100644 >> --- a/gcc/testsuite/gcc.target/aarch64/ifcvt_multiple_sets_diamond_4.c >> +++ b/gcc/testsuite/gcc.target/aarch64/ifcvt_multiple_sets_diamond_4.c >> @@ -1,41 +1,58 @@ >> /* { dg-do run } */ >> -/* { dg-options "-O2 --param=max-rtl-if-conversion-unpredictable-cost=100 >> -fdump-rtl-ce1" } */ >> +/* { dg-options "-O2 -fno-ssa-phiopt -fno-tree-ter -fno-tree-coalesce-vars >> -fdump-rtl-ce1" } */ >> +/* { dg-additional-options >> "--param=max-rtl-if-conversion-unpredictable-cost=100 >> --param=max-rtl-if-conversion-predictable-cost=100" } */ >> -volatile long ga, gt, ge; >> +volatile long ga, gt, ge, gw; >> __attribute__ ((noipa)) void >> -convertible (long c, long p, long q) >> +disjoint_arm_values (long a, long b, long c, long d, long s, >> + long x, long y, long z, long w) >> { >> - long a, t; >> - if (c > 7) >> + if (s > 0) >> { >> - a = p + 1; >> - t = q + 2; >> + x = a; >> + y = b - 2; >> } >> else >> { >> - a = p - 3; >> - t = q - 4; >> + z = c; >> + w = d - 4; >> } >> - ga = a; >> - gt = t; >> + ga = x; >> + gt = y; >> + ge = z; >> + gw = w; >> } >> +/* Each arm has one set and defines a different live-out destination. */ >> __attribute__ ((noipa)) void >> -reject_arm_only_values (long c, long p, long q, long t, long e) >> +single_set_arms (long c, long a, long b, long x, long y) >> { >> - long a; >> - if (c > 7) >> + if (c > 0) >> + x = a + 1; >> + else >> + y = b - 1; >> + ga = x; >> + gt = y; >> +} >> + >> +/* The arms share X through register copies and compute distinct live-out >> + destinations. */ >> +__attribute__ ((noipa)) void >> +partially_overlapping_values (long c, long a, long b, >> + long x, long t, long e) >> +{ >> + if (c > 0) >> { >> - a = p + 1; >> - t = q + 2; >> + x = a; >> + t = b + 1; >> } >> else >> { >> - a = p - 3; >> - e = q - 4; >> + x = b; >> + e = a - 1; >> } >> - ga = a; >> + ga = x; >> gt = t; >> ge = e; >> } >> @@ -43,23 +60,27 @@ reject_arm_only_values (long c, long p, long q, long t, >> long e) >> __attribute__ ((optimize ("O0"))) int >> main (void) >> { >> - convertible (8, 10, 20); >> - if (ga != 11 || gt != 22) >> + disjoint_arm_values (10, 20, 30, 40, 1, 50, 60, 70, 80); >> + if (ga != 10 || gt != 18 || ge != 70 || gw != 80) >> + __builtin_abort (); >> + disjoint_arm_values (10, 20, 30, 40, 0, 50, 60, 70, 80); >> + if (ga != 50 || gt != 60 || ge != 30 || gw != 36) >> + __builtin_abort (); >> + >> + single_set_arms (1, 10, 20, 30, 40); >> + if (ga != 11 || gt != 40) >> __builtin_abort (); >> - convertible (7, 10, 20); >> - if (ga != 7 || gt != 16) >> + single_set_arms (0, 10, 20, 30, 40); >> + if (ga != 30 || gt != 19) >> __builtin_abort (); >> - reject_arm_only_values (8, 10, 20, 31, 47); >> - if (ga != 11 || gt != 22 || ge != 47) >> + partially_overlapping_values (1, 10, 20, 30, 40, 50); >> + if (ga != 10 || gt != 21 || ge != 50) >> __builtin_abort (); >> - reject_arm_only_values (7, 10, 20, 31, 47); >> - if (ga != 7 || gt != 31 || ge != 16) >> + partially_overlapping_values (0, 10, 20, 30, 40, 50); >> + if (ga != 20 || gt != 40 || ge != 9) >> __builtin_abort (); >> return 0; >> } >> -/* The first diamond is handled by the existing conditional-move path. The >> - second must be rejected because each arm has a live-out value not >> assigned >> - by the other arm. */ >> -/* { dg-final { scan-rtl-dump-not "if-conversion succeeded through >> noce_convert_multiple_sets" "ce1" } } */ >> +/* { dg-final { scan-rtl-dump-times "if-conversion succeeded through >> noce_convert_multiple_sets" 3 "ce1" } } */ >> diff --git >> a/gcc/testsuite/gcc.target/aarch64/ifcvt_multiple_sets_diamond_5.c >> b/gcc/testsuite/gcc.target/aarch64/ifcvt_multiple_sets_diamond_5.c >> index 7490df7ddc0..ccf21441089 100644 >> --- a/gcc/testsuite/gcc.target/aarch64/ifcvt_multiple_sets_diamond_5.c >> +++ b/gcc/testsuite/gcc.target/aarch64/ifcvt_multiple_sets_diamond_5.c >> @@ -23,8 +23,7 @@ f (const unsigned char *p, unsigned long tag) >> } >> /* { dg-final { scan-rtl-dump-times "if-conversion succeeded through >> noce_convert_multiple_sets" 1 "ce1" } } */ >> -/* { dg-final { scan-assembler-times "\tcsinc\t" 1 } } */ >> -/* { dg-final { scan-assembler-times "\tcsel\t" 1 } } */ >> +/* { dg-final { scan-assembler-times "\tcsinc\t" 2 } } */ >> /* { dg-final { scan-assembler-times "\tldrb\t" 1 } } */ >> /* { dg-final { scan-assembler-not {\tb(eq|ne)\t} } } */ >> /* { dg-final { scan-assembler-not {\tcbn?z\t} } } */ >> diff --git >> a/gcc/testsuite/gcc.target/aarch64/ifcvt_multiple_sets_diamond_6.c >> b/gcc/testsuite/gcc.target/aarch64/ifcvt_multiple_sets_diamond_6.c >> new file mode 100644 >> index 00000000000..a6e7be9c22a >> --- /dev/null >> +++ b/gcc/testsuite/gcc.target/aarch64/ifcvt_multiple_sets_diamond_6.c >> @@ -0,0 +1,100 @@ >> +/* Test dependencies in diamonds with arm-only live-out destinations. */ >> +/* { dg-do run } */ >> +/* { dg-options "-O2 -fno-tree-ter -fno-tree-coalesce-vars -fdump-rtl-ce1" >> } */ >> +/* { dg-additional-options >> "--param=max-rtl-if-conversion-unpredictable-cost=100 >> --param=max-rtl-if-conversion-predictable-cost=100" } */ >> + >> +volatile long ga, gt, ge, gu; >> + >> +/* Each arm reads the value that the other arm can replace. The else >> + arm also defines E twice with a dependent live-out value in between. */ >> +__attribute__ ((noipa)) void >> +cross_arm_repeated (long c, long p, long q, long t, long e) >> +{ >> + long k; >> + if (c > 7) >> + { >> + t = e + p; >> + k = q + 1; >> + } >> + else >> + { >> + e = t + q; >> + k = e ^ p; >> + e = k + 3; >> + } >> + ga = k; >> + gt = t; >> + ge = e; >> +} >> + >> +/* C is both a condition input and an arm-only live-out destination. */ >> +__attribute__ ((noipa)) void >> +condition_input (long c, long p, long q, long a) >> +{ >> + long t; >> + if (c > 7) >> + { >> + a = p + 1; >> + t = q + 2; >> + } >> + else >> + { >> + t = p - 3; >> + c = q - 4; >> + } >> + ga = a; >> + gt = t; >> + ge = c; >> +} >> + >> +/* One arm has more live-out destinations. The conversion keeps the CFG arm >> + order. */ >> +__attribute__ ((noipa)) void >> +unequal_output_counts (long c, long p, long q, long t, long e, long u) >> +{ >> + long a; >> + if (__builtin_expect (c > 7, 1)) >> + { >> + a = p + 1; >> + t = q + 2; >> + } >> + else >> + { >> + a = p - 3; >> + e = q - 4; >> + u = p + q; >> + } >> + ga = a; >> + gt = t; >> + ge = e; >> + gu = u; >> +} >> + >> +__attribute__ ((optimize ("O0"))) int >> +main (void) >> +{ >> + cross_arm_repeated (8, 10, 20, 31, 47); >> + if (ga != 21 || gt != 57 || ge != 47) >> + __builtin_abort (); >> + cross_arm_repeated (7, 10, 20, 31, 47); >> + if (ga != 57 || gt != 31 || ge != 60) >> + __builtin_abort (); >> + >> + condition_input (8, 10, 20, 47); >> + if (ga != 11 || gt != 22 || ge != 8) >> + __builtin_abort (); >> + condition_input (7, 10, 20, 47); >> + if (ga != 47 || gt != 7 || ge != 16) >> + __builtin_abort (); >> + >> + unequal_output_counts (8, 10, 20, 31, 47, 59); >> + if (ga != 11 || gt != 22 || ge != 47 || gu != 59) >> + __builtin_abort (); >> + unequal_output_counts (7, 10, 20, 31, 47, 59); >> + if (ga != 7 || gt != 31 || ge != 16 || gu != 30) >> + __builtin_abort (); >> + >> + return 0; >> +} >> + >> +/* { dg-final { scan-rtl-dump-times "if-conversion succeeded through >> noce_convert_multiple_sets" 3 "ce1" } } */ >> diff --git >> a/gcc/testsuite/gcc.target/aarch64/ifcvt_multiple_sets_diamond_7.c >> b/gcc/testsuite/gcc.target/aarch64/ifcvt_multiple_sets_diamond_7.c >> new file mode 100644 >> index 00000000000..f72223f6808 >> --- /dev/null >> +++ b/gcc/testsuite/gcc.target/aarch64/ifcvt_multiple_sets_diamond_7.c >> @@ -0,0 +1,26 @@ >> +/* Include arm-only outputs in the multiple-set conversion limit. */ >> +/* { dg-do compile } */ >> +/* { dg-options "-O2 -fno-tree-ter -fno-tree-coalesce-vars -fdump-rtl-ce1" >> } */ >> +/* { dg-additional-options "--param=max-rtl-if-conversion-insns=3 >> --param=max-rtl-if-conversion-unpredictable-cost=100" } */ >> + >> +void sink3 (long, long, long); >> + >> +void >> +f (long c, long p, long q, long t, long e) >> +{ >> + long a; >> + if (c > 7) >> + { >> + a = p + 1; >> + t = a + q; >> + a = t + 3; >> + } >> + else >> + { >> + a = p - 3; >> + e = q - 4; >> + } >> + sink3 (a, t, e); >> +} >> + >> +/* { dg-final { scan-rtl-dump-not "if-conversion succeeded through >> noce_convert_multiple_sets" "ce1" } } */ >> diff --git a/gcc/testsuite/gcc.target/i386/ifcvt-multiple-sets-diamond-1.c >> b/gcc/testsuite/gcc.target/i386/ifcvt-multiple-sets-diamond-1.c >> index fb4491074f3..c23f9a75967 100644 >> --- a/gcc/testsuite/gcc.target/i386/ifcvt-multiple-sets-diamond-1.c >> +++ b/gcc/testsuite/gcc.target/i386/ifcvt-multiple-sets-diamond-1.c >> @@ -1,27 +1,27 @@ >> /* { dg-do run } */ >> /* { dg-require-effective-target lp64 } */ >> /* { dg-options "-O2 -mtune=generic -fdump-rtl-ce1" } */ >> -/* { dg-additional-options >> "--param=max-rtl-if-conversion-predictable-cost=100" } */ >> +/* { dg-additional-options "--param=max-rtl-if-conversion-insns=3 >> --param=max-rtl-if-conversion-predictable-cost=100 >> --param=max-rtl-if-conversion-unpredictable-cost=100" } */ >> -/* The single-set arm is the likely fallthrough block. Speculative >> arithmetic >> - clobbers FLAGS, so each conditional move must re-materialize the >> +/* The arms have disjoint live-out destinations. The speculative xor >> + clobbers FLAGS, so the conversion must not reuse the incoming >> comparison. */ >> -volatile long ga, gb; >> +volatile long ga, gb, gd; >> __attribute__ ((noipa)) void >> -f (long c, long x, long y, long b) >> +f (long c, long x, long y, long a, long b, long d) >> { >> - long a; >> - if (__builtin_expect (c <= 7, 1)) >> - a = y + 3; >> - else >> + if (__builtin_expect (c > 7, 0)) >> { >> a = x + 1; >> b = y + 2; >> } >> + else >> + d = x ^ y; >> ga = a; >> gb = b; >> + gd = d; >> } >> /* Keep the runtime driver out of noce so that the dump count is specific >> to >> @@ -33,9 +33,10 @@ main (void) >> for (long x = -8; x != 9; ++x) >> for (long y = -8; y != 9; ++y) >> { >> - f (c, x, y, 4); >> - if (ga != (c > 7 ? x + 1 : y + 3) >> - || gb != (c > 7 ? y + 2 : 4)) >> + f (c, x, y, 2, 4, 6); >> + if (ga != (c > 7 ? x + 1 : 2) >> + || gb != (c > 7 ? y + 2 : 4) >> + || gd != (c > 7 ? 6 : (x ^ y))) >> __builtin_abort (); >> } >> return 0; >
0001-testsuite-arm-Fix-short-IT-ifcvt-test.patch
Description: 0001-testsuite-arm-Fix-short-IT-ifcvt-test.patch
