From a141f2d86293dc202e35bd4d7f832756e1792cdf Mon Sep 17 00:00:00 2001 From: Peter Bergner Date: Sat, 6 Oct 2018 02:12:30 +0000 Subject: [PATCH] re PR rtl-optimization/86939 (IRA incorrectly creates an interference between a pseudo register and a hard register) gcc/ PR rtl-optimization/86939 PR rtl-optimization/87479 * ira.h (non_conflicting_reg_copy_p): New prototype. * ira-lives.c (ignore_reg_for_conflicts): New static variable. (make_hard_regno_dead): Don't add conflicts for register ignore_reg_for_conflicts. (make_object_dead): Likewise. (non_conflicting_reg_copy_p): New function. (process_bb_node_lives): Set ignore_reg_for_conflicts for copies. Remove special conflict handling of REAL_PIC_OFFSET_TABLE_REGNUM. * lra-lives.c (ignore_reg_for_conflicts): New static variable. (make_hard_regno_dead): Don't add conflicts for register ignore_reg_for_conflicts. Remove special conflict handling of REAL_PIC_OFFSET_TABLE_REGNUM. Remove now unused argument check_pic_pseudo_p and update callers. (mark_pseudo_dead): Don't add conflicts for register ignore_reg_for_conflicts. (process_bb_lives): Set ignore_reg_for_conflicts for copies. gcc/testsuite/ PR rtl-optimization/86939 PR rtl-optimization/87479 * gcc.target/powerpc/pr86939.c: New test. * gcc/testsuite/gcc.target/i386/pr49095.c: Fix expected results. From-SVN: r264897 --- gcc/ChangeLog | 21 +++++++ gcc/ira-lives.c | 97 +++++++++++++++++++++--------- gcc/ira.h | 3 + gcc/lra-lives.c | 64 ++++++++++++++------ gcc/testsuite/ChangeLog | 7 +++ gcc/testsuite/gcc.target/i386/pr49095.c | 3 +- gcc/testsuite/gcc.target/powerpc/pr86939.c | 12 ++++ 7 files changed, 160 insertions(+), 47 deletions(-) create mode 100644 gcc/testsuite/gcc.target/powerpc/pr86939.c diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 6d81f73..73f26a9 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,24 @@ +2018-10-05 Peter Bergner + + PR rtl-optimization/86939 + PR rtl-optimization/87479 + * ira.h (non_conflicting_reg_copy_p): New prototype. + * ira-lives.c (ignore_reg_for_conflicts): New static variable. + (make_hard_regno_dead): Don't add conflicts for register + ignore_reg_for_conflicts. + (make_object_dead): Likewise. + (non_conflicting_reg_copy_p): New function. + (process_bb_node_lives): Set ignore_reg_for_conflicts for copies. + Remove special conflict handling of REAL_PIC_OFFSET_TABLE_REGNUM. + * lra-lives.c (ignore_reg_for_conflicts): New static variable. + (make_hard_regno_dead): Don't add conflicts for register + ignore_reg_for_conflicts. Remove special conflict handling of + REAL_PIC_OFFSET_TABLE_REGNUM. Remove now unused argument + check_pic_pseudo_p and update callers. + (mark_pseudo_dead): Don't add conflicts for register + ignore_reg_for_conflicts. + (process_bb_lives): Set ignore_reg_for_conflicts for copies. + 2018-10-05 Andrew Waterman Jim Wilson diff --git a/gcc/ira-lives.c b/gcc/ira-lives.c index f1a7d27..dd8b334 100644 --- a/gcc/ira-lives.c +++ b/gcc/ira-lives.c @@ -84,6 +84,10 @@ static int *allocno_saved_at_call; supplemental to recog_data. */ static alternative_mask preferred_alternatives; +/* If non-NULL, the source operand of a register to register copy for which + we should not add a conflict with the copy's destination operand. */ +static rtx ignore_reg_for_conflicts; + /* Record hard register REGNO as now being live. */ static void make_hard_regno_live (int regno) @@ -101,6 +105,11 @@ make_hard_regno_dead (int regno) { ira_object_t obj = ira_object_id_map[i]; + if (ignore_reg_for_conflicts != NULL_RTX + && REGNO (ignore_reg_for_conflicts) + == (unsigned int) ALLOCNO_REGNO (OBJECT_ALLOCNO (obj))) + continue; + SET_HARD_REG_BIT (OBJECT_CONFLICT_HARD_REGS (obj), regno); SET_HARD_REG_BIT (OBJECT_TOTAL_CONFLICT_HARD_REGS (obj), regno); } @@ -154,12 +163,38 @@ static void make_object_dead (ira_object_t obj) { live_range_t lr; + int ignore_regno = -1; + int end_regno = -1; sparseset_clear_bit (objects_live, OBJECT_CONFLICT_ID (obj)); + /* Check whether any part of IGNORE_REG_FOR_CONFLICTS already conflicts + with OBJ. */ + if (ignore_reg_for_conflicts != NULL_RTX + && REGNO (ignore_reg_for_conflicts) < FIRST_PSEUDO_REGISTER) + { + end_regno = END_REGNO (ignore_reg_for_conflicts); + int src_regno = ignore_regno = REGNO (ignore_reg_for_conflicts); + + while (src_regno < end_regno) + { + if (TEST_HARD_REG_BIT (OBJECT_CONFLICT_HARD_REGS (obj), src_regno)) + { + ignore_regno = end_regno = -1; + break; + } + src_regno++; + } + } + IOR_HARD_REG_SET (OBJECT_CONFLICT_HARD_REGS (obj), hard_regs_live); IOR_HARD_REG_SET (OBJECT_TOTAL_CONFLICT_HARD_REGS (obj), hard_regs_live); + /* If IGNORE_REG_FOR_CONFLICTS did not already conflict with OBJ, make + sure it still doesn't. */ + for (; ignore_regno < end_regno; ignore_regno++) + CLEAR_HARD_REG_BIT (OBJECT_CONFLICT_HARD_REGS (obj), ignore_regno); + lr = OBJECT_LIVE_RANGES (obj); ira_assert (lr != NULL); lr->finish = curr_point; @@ -1022,6 +1057,38 @@ find_call_crossed_cheap_reg (rtx_insn *insn) return cheap_reg; } +/* Determine whether INSN is a register to register copy of the type where + we do not need to make the source and destiniation registers conflict. + If this is a copy instruction, then return the source reg. Otherwise, + return NULL_RTX. */ +rtx +non_conflicting_reg_copy_p (rtx_insn *insn) +{ + rtx set = single_set (insn); + + /* Disallow anything other than a simple register to register copy + that has no side effects. */ + if (set == NULL_RTX + || !REG_P (SET_DEST (set)) + || !REG_P (SET_SRC (set)) + || side_effects_p (set)) + return NULL_RTX; + + int dst_regno = REGNO (SET_DEST (set)); + int src_regno = REGNO (SET_SRC (set)); + machine_mode mode = GET_MODE (SET_DEST (set)); + + /* Computing conflicts for register pairs is difficult to get right, so + for now, disallow it. */ + if ((dst_regno < FIRST_PSEUDO_REGISTER + && hard_regno_nregs (dst_regno, mode) != 1) + || (src_regno < FIRST_PSEUDO_REGISTER + && hard_regno_nregs (src_regno, mode) != 1)) + return NULL_RTX; + + return SET_SRC (set); +} + /* Process insns of the basic block given by its LOOP_TREE_NODE to update allocno live ranges, allocno hard register conflicts, intersected calls, and register pressure info for allocnos for the @@ -1107,22 +1174,7 @@ process_bb_node_lives (ira_loop_tree_node_t loop_tree_node) curr_point); call_p = CALL_P (insn); -#ifdef REAL_PIC_OFFSET_TABLE_REGNUM - int regno; - bool clear_pic_use_conflict_p = false; - /* Processing insn usage in call insn can create conflict - with pic pseudo and pic hard reg and that is wrong. - Check this situation and fix it at the end of the insn - processing. */ - if (call_p && pic_offset_table_rtx != NULL_RTX - && (regno = REGNO (pic_offset_table_rtx)) >= FIRST_PSEUDO_REGISTER - && (a = ira_curr_regno_allocno_map[regno]) != NULL) - clear_pic_use_conflict_p - = (find_regno_fusage (insn, USE, REAL_PIC_OFFSET_TABLE_REGNUM) - && ! TEST_HARD_REG_BIT (OBJECT_CONFLICT_HARD_REGS - (ALLOCNO_OBJECT (a, 0)), - REAL_PIC_OFFSET_TABLE_REGNUM)); -#endif + ignore_reg_for_conflicts = non_conflicting_reg_copy_p (insn); /* Mark each defined value as live. We need to do this for unused values because they still conflict with quantities @@ -1276,20 +1328,9 @@ process_bb_node_lives (ira_loop_tree_node_t loop_tree_node) } } -#ifdef REAL_PIC_OFFSET_TABLE_REGNUM - if (clear_pic_use_conflict_p) - { - regno = REGNO (pic_offset_table_rtx); - a = ira_curr_regno_allocno_map[regno]; - CLEAR_HARD_REG_BIT (OBJECT_CONFLICT_HARD_REGS (ALLOCNO_OBJECT (a, 0)), - REAL_PIC_OFFSET_TABLE_REGNUM); - CLEAR_HARD_REG_BIT (OBJECT_TOTAL_CONFLICT_HARD_REGS - (ALLOCNO_OBJECT (a, 0)), - REAL_PIC_OFFSET_TABLE_REGNUM); - } -#endif curr_point++; } + ignore_reg_for_conflicts = NULL_RTX; if (bb_has_eh_pred (bb)) for (j = 0; ; ++j) diff --git a/gcc/ira.h b/gcc/ira.h index 9df983c..8a8e460 100644 --- a/gcc/ira.h +++ b/gcc/ira.h @@ -210,6 +210,9 @@ extern void ira_adjust_equiv_reg_cost (unsigned, int); /* ira-costs.c */ extern void ira_costs_c_finalize (void); +/* ira-lives.c */ +extern rtx non_conflicting_reg_copy_p (rtx_insn *); + /* Spilling static chain pseudo may result in generation of wrong non-local goto code using frame-pointer to address saved stack pointer value after restoring old frame pointer value. The diff --git a/gcc/lra-lives.c b/gcc/lra-lives.c index a3bc29c..0bf8cd0 100644 --- a/gcc/lra-lives.c +++ b/gcc/lra-lives.c @@ -96,6 +96,10 @@ static bitmap_head temp_bitmap; /* Pool for pseudo live ranges. */ static object_allocator lra_live_range_pool ("live ranges"); +/* If non-NULL, the source operand of a register to register copy for which + we should not add a conflict with the copy's destination operand. */ +static rtx ignore_reg_for_conflicts; + /* Free live range list LR. */ static void free_live_range_list (lra_live_range_t lr) @@ -239,11 +243,9 @@ make_hard_regno_live (int regno) /* Process the definition of hard register REGNO. This updates hard_regs_live, START_DYING and conflict hard regs for living - pseudos. Conflict hard regs for the pic pseudo is not updated if - REGNO is REAL_PIC_OFFSET_TABLE_REGNUM and CHECK_PIC_PSEUDO_P is - true. */ + pseudos. */ static void -make_hard_regno_dead (int regno, bool check_pic_pseudo_p ATTRIBUTE_UNUSED) +make_hard_regno_dead (int regno) { lra_assert (regno < FIRST_PSEUDO_REGISTER); if (! TEST_HARD_REG_BIT (hard_regs_live, regno)) @@ -251,13 +253,12 @@ make_hard_regno_dead (int regno, bool check_pic_pseudo_p ATTRIBUTE_UNUSED) sparseset_set_bit (start_dying, regno); unsigned int i; EXECUTE_IF_SET_IN_SPARSESET (pseudos_live, i) -#ifdef REAL_PIC_OFFSET_TABLE_REGNUM - if (! check_pic_pseudo_p - || regno != REAL_PIC_OFFSET_TABLE_REGNUM - || pic_offset_table_rtx == NULL - || i != REGNO (pic_offset_table_rtx)) -#endif + { + if (ignore_reg_for_conflicts != NULL_RTX + && REGNO (ignore_reg_for_conflicts) == i) + continue; SET_HARD_REG_BIT (lra_reg_info[i].conflict_hard_regs, regno); + } CLEAR_HARD_REG_BIT (hard_regs_live, regno); if (fixed_regs[regno] || TEST_HARD_REG_BIT (hard_regs_spilled_into, regno)) { @@ -294,14 +295,41 @@ static void mark_pseudo_dead (int regno, int point) { lra_live_range_t p; + int ignore_regno = -1; + int end_regno = -1; lra_assert (regno >= FIRST_PSEUDO_REGISTER); lra_assert (sparseset_bit_p (pseudos_live, regno)); sparseset_clear_bit (pseudos_live, regno); sparseset_set_bit (start_dying, regno); + /* Check whether any part of IGNORE_REG_FOR_CONFLICTS already conflicts + with REGNO. */ + if (ignore_reg_for_conflicts != NULL_RTX + && REGNO (ignore_reg_for_conflicts) < FIRST_PSEUDO_REGISTER) + { + end_regno = END_REGNO (ignore_reg_for_conflicts); + int src_regno = ignore_regno = REGNO (ignore_reg_for_conflicts); + + while (src_regno < end_regno) + { + if (TEST_HARD_REG_BIT (lra_reg_info[regno].conflict_hard_regs, + src_regno)) + { + ignore_regno = end_regno = -1; + break; + } + src_regno++; + } + } + IOR_HARD_REG_SET (lra_reg_info[regno].conflict_hard_regs, hard_regs_live); + /* If IGNORE_REG_FOR_CONFLICTS did not already conflict with REGNO, make + sure it still doesn't. */ + for (; ignore_regno < end_regno; ignore_regno++) + CLEAR_HARD_REG_BIT (lra_reg_info[regno].conflict_hard_regs, ignore_regno); + if (complete_info_p || lra_get_regno_hard_regno (regno) < 0) { p = lra_reg_info[regno].live_ranges; @@ -350,7 +378,7 @@ mark_regno_dead (int regno, machine_mode mode, int point) if (regno < FIRST_PSEUDO_REGISTER) { for (last = end_hard_regno (mode, regno); regno < last; regno++) - make_hard_regno_dead (regno, false); + make_hard_regno_dead (regno); } else { @@ -747,6 +775,7 @@ process_bb_lives (basic_block bb, int &curr_point, bool dead_insn_p) } call_p = CALL_P (curr_insn); + ignore_reg_for_conflicts = non_conflicting_reg_copy_p (curr_insn); src_regno = (set != NULL_RTX && REG_P (SET_SRC (set)) ? REGNO (SET_SRC (set)) : -1); dst_regno = (set != NULL_RTX && REG_P (SET_DEST (set)) @@ -858,14 +887,13 @@ process_bb_lives (basic_block bb, int &curr_point, bool dead_insn_p) for (reg = curr_static_id->hard_regs; reg != NULL; reg = reg->next) if (reg->type == OP_OUT && ! reg_early_clobber_p (reg, n_alt) && ! reg->subreg_p) - make_hard_regno_dead (reg->regno, false); + make_hard_regno_dead (reg->regno); if (curr_id->arg_hard_regs != NULL) for (i = 0; (regno = curr_id->arg_hard_regs[i]) >= 0; i++) if (regno >= FIRST_PSEUDO_REGISTER) - /* It is a clobber. Don't create conflict of used - REAL_PIC_OFFSET_TABLE_REGNUM and the pic pseudo. */ - make_hard_regno_dead (regno - FIRST_PSEUDO_REGISTER, true); + /* It is a clobber. */ + make_hard_regno_dead (regno - FIRST_PSEUDO_REGISTER); if (call_p) { @@ -926,8 +954,7 @@ process_bb_lives (basic_block bb, int &curr_point, bool dead_insn_p) make_hard_regno_live (reg->regno); if (curr_id->arg_hard_regs != NULL) - /* Make argument hard registers live. Don't create conflict - of used REAL_PIC_OFFSET_TABLE_REGNUM and the pic pseudo. */ + /* Make argument hard registers live. */ for (i = 0; (regno = curr_id->arg_hard_regs[i]) >= 0; i++) if (regno < FIRST_PSEUDO_REGISTER) make_hard_regno_live (regno); @@ -955,7 +982,7 @@ process_bb_lives (basic_block bb, int &curr_point, bool dead_insn_p) if (reg2->type != OP_OUT && reg2->regno == reg->regno) break; if (reg2 == NULL) - make_hard_regno_dead (reg->regno, false); + make_hard_regno_dead (reg->regno); } if (need_curr_point_incr) @@ -990,6 +1017,7 @@ process_bb_lives (basic_block bb, int &curr_point, bool dead_insn_p) EXECUTE_IF_SET_IN_SPARSESET (unused_set, j) add_reg_note (curr_insn, REG_UNUSED, regno_reg_rtx[j]); } + ignore_reg_for_conflicts = NULL_RTX; if (bb_has_eh_pred (bb)) for (j = 0; ; ++j) diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 818f6ea..f34b167 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,10 @@ +2018-10-05 Peter Bergner + + PR rtl-optimization/86939 + PR rtl-optimization/87479 + * gcc.target/powerpc/pr86939.c: New test. + * gcc/testsuite/gcc.target/i386/pr49095.c: Fix expected results. + 2018-10-05 Bernd Edlinger * gnat.dg/string_merge1.adb: Fix test expectations. diff --git a/gcc/testsuite/gcc.target/i386/pr49095.c b/gcc/testsuite/gcc.target/i386/pr49095.c index 0780719..20175e7 100644 --- a/gcc/testsuite/gcc.target/i386/pr49095.c +++ b/gcc/testsuite/gcc.target/i386/pr49095.c @@ -73,4 +73,5 @@ G (long) /* { dg-final { scan-assembler-not "test\[lq\]" } } */ /* The {f,h}{char,short,int,long}xor functions aren't optimized into a RMW instruction, so need load, modify and store. FIXME eventually. */ -/* { dg-final { scan-assembler-times "\\), %" 8 } } */ +/* { dg-final { scan-assembler-times "\\), %" 57 { target { ia32 } } } } */ +/* { dg-final { scan-assembler-times "\\), %" 45 { target { ! ia32 } } } } */ diff --git a/gcc/testsuite/gcc.target/powerpc/pr86939.c b/gcc/testsuite/gcc.target/powerpc/pr86939.c new file mode 100644 index 0000000..4bc9793 --- /dev/null +++ b/gcc/testsuite/gcc.target/powerpc/pr86939.c @@ -0,0 +1,12 @@ +/* { dg-do compile { target { powerpc*-*-* } } } */ +/* { dg-options "-O2" } */ + +typedef long (*fptr_t) (void); +long +func (fptr_t *p) +{ + if (p) + return (*p) (); + return 0; +} +/* { dg-final { scan-assembler-not {mr %?r?12,} } } */ -- 2.7.4