This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
[PATCH] Don't emit CLOBBERs for eh registers
- From: Andreas Krebbel <Andreas dot Krebbel at de dot ibm dot com>
- To: gcc-patches at gcc dot gnu dot org
- Cc: iant at google dot com, dberlin at dberlin dot org
- Date: Mon, 11 Sep 2006 12:51:07 +0200
- Subject: [PATCH] Don't emit CLOBBERs for eh registers
Hello,
the attached patch fixes the problem described in:
http://gcc.gnu.org/ml/gcc-patches/2006-07/msg00767.html
The patch is supposed to be the superior fix compared to
http://gcc.gnu.org/ml/gcc-patches/2006-07/msg01060.html
which simply works around the real problem.
The real problem here is that CLOBBERS are misused to teach
flow that the eh regs are set at eh edges. This confuses the df
framework when computing life ranges.
The solution is to not emit these CLOBBERs and handle eh regs
differently. Unfortunately this needs a bit of special handling
in other sensitive locations:
1.) When propagating liveness information within a basic block
flow has to consider the eh registers to be set at basic block
start in order to have proper live ranges for the eh reg uses
in the catch block. I've added the code proposed by Ian to
propagate_block in order to make the eh regs marked as new sets
at bb start.
2.) A throw block ends with a call to __cxa_throw. When call
saved registers are used as eh regs the regs will be live at
basic block end. Hence for subsequent basic blocks cse, combine
and co consider these registers to have the same value as before
the call. It should be considered a property of an eh edge that
the eh registers are modified regardless whether they are call saved
or not. In order to draw that into account the patch circumvents the
liveness propagation of eh registers over eh edges by adding them
to the invalidated_by_call bitmap.
This btw. enables gcc to generate better code since it is
possible to remove more pointless load insns after regrename.
Consider the following example (with r3 as eh register):
bb 1:
insn 1: r1 = r2
insn 2: r3 = r1
live at end: 1 2 3
| \
|EH \
| \
live: 3 live: 1 2 3
bb 2: bb 3:
insn 3: r2 = r3
Regrename now renames r1 with r2 in insn 2 and r3 with r1 in insn 3.
Without the patch insn 2 couldn't be removed since the content of r3
is virtually used in bb 2. But this is not the case since the value
of the eh reg r3 would change at the eh edge anyway. With the patch
r3 isn't live at end of bb 1. After regrename has performed its changes
insn 2 can now be removed.
bb 1:
insn 1: r1 = r2
insn 2: r3 = r2 --> remove
live at end: 1 2
| \
|EH \
| \
live: 3 live: 1 2
bb 2: bb 3:
insn 3: r2 = r1
3.) Another problem occured when the content of an eh register was moved
into a pseudo which is live at start of the catch basic block (as in
g++.old-deja/g++.eh/inline2.C).
In this situation global alloc may decide to allocate the eh reg for the
pseudo without being able to recognize that the value of the eh reg could
have been modified. In order to prevent this the patch records conflicts
between pseudos which are live at bb start and the eh registers for basic
blocks with a eh predecessor edge.
Bootstrapped on i686, s390 and s390x.
No testsuite regressions.
Two testcases fixed on s390x:
FAIL: 22_locale/locale/cons/12352.cc execution test
FAIL: 22_locale/locale/cons/12438.cc execution test
Ok for mainline?
Since this is regression on s390x I really need a solution for gcc 4.2. If
this one is too dodgy for 4.2 I would like to apply the workaround:
http://gcc.gnu.org/ml/gcc-patches/2006-07/msg01060.html
instead and wait with this patch for 4.3.
Bye,
-Andreas-
2006-09-11 Andreas Krebbel <krebbel1@de.ibm.com>
* flow.c (calculate_global_regs_live): Invalidate eh registers
on eh edges.
(propagate_block): Handle eh registers as if they were set at basic
block start.
* except.c (dw2_build_landing_pads): Don't emit clobbers for eh
registers.
* global.c (global_conflicts): Make eh registers to conflict with
pseudo live at basic block begin.
* basic_block.h (bb_has_pred_edge_with_flags_p): New function.
Index: gcc/flow.c
===================================================================
*** gcc/flow.c.orig 2006-09-08 14:19:10.000000000 +0200
--- gcc/flow.c 2006-09-08 14:27:41.000000000 +0200
*************** calculate_global_regs_live (sbitmap bloc
*** 1071,1076 ****
--- 1071,1085 ----
if (TEST_HARD_REG_BIT (regs_invalidated_by_call, i))
SET_REGNO_REG_SET (invalidated_by_call, i);
+ /* The exception handling registers die at eh edges. */
+ for (i = 0; ; ++i)
+ {
+ unsigned regno = EH_RETURN_DATA_REGNO (i);
+ if (regno == INVALID_REGNUM)
+ break;
+ SET_REGNO_REG_SET (invalidated_by_call, regno);
+ }
+
/* Allocate space for the sets of local properties. */
local_sets = XCNEWVEC (bitmap, last_basic_block);
cond_local_sets = XCNEWVEC (bitmap, last_basic_block);
*************** propagate_block (basic_block bb, regset
*** 2214,2219 ****
--- 2223,2252 ----
break;
}
+ #ifdef EH_RETURN_DATA_REGNO
+ {
+ if (bb_has_pred_edge_with_flags_p (bb, EDGE_EH))
+ {
+ unsigned int i;
+ for (i = 0; ; ++i)
+ {
+ unsigned regno = EH_RETURN_DATA_REGNO (i);
+ if (regno == INVALID_REGNUM)
+ break;
+ if (pbi->local_set)
+ {
+ CLEAR_REGNO_REG_SET (pbi->cond_local_set, regno);
+ SET_REGNO_REG_SET (pbi->local_set, regno);
+ }
+ if (REGNO_REG_SET_P (pbi->reg_live, regno))
+ SET_REGNO_REG_SET (pbi->new_set, regno);
+
+ regs_ever_live[regno] = 1;
+ }
+ }
+ }
+ #endif
+
free_propagate_block_info (pbi);
return changed;
Index: gcc/except.c
===================================================================
*** gcc/except.c.orig 2006-09-08 11:04:54.000000000 +0200
--- gcc/except.c 2006-09-08 14:23:56.000000000 +0200
*************** static void
*** 1601,1614 ****
dw2_build_landing_pads (void)
{
int i;
- unsigned int j;
for (i = cfun->eh->last_region_number; i > 0; --i)
{
struct eh_region *region;
rtx seq;
basic_block bb;
- bool clobbers_hard_regs = false;
edge e;
region = VEC_index (eh_region, cfun->eh->region_array, i);
--- 1601,1612 ----
*************** dw2_build_landing_pads (void)
*** 1638,1667 ****
#endif
{ /* Nothing */ }
- /* If the eh_return data registers are call-saved, then we
- won't have considered them clobbered from the call that
- threw. Kill them now. */
- for (j = 0; ; ++j)
- {
- unsigned r = EH_RETURN_DATA_REGNO (j);
- if (r == INVALID_REGNUM)
- break;
- if (! call_used_regs[r])
- {
- emit_insn (gen_rtx_CLOBBER (VOIDmode, gen_rtx_REG (Pmode, r)));
- clobbers_hard_regs = true;
- }
- }
-
- if (clobbers_hard_regs)
- {
- /* @@@ This is a kludge. Not all machine descriptions define a
- blockage insn, but we must not allow the code we just generated
- to be reordered by scheduling. So emit an ASM_INPUT to act as
- blockage insn. */
- emit_insn (gen_rtx_ASM_INPUT (VOIDmode, ""));
- }
-
emit_move_insn (cfun->eh->exc_ptr,
gen_rtx_REG (ptr_mode, EH_RETURN_DATA_REGNO (0)));
emit_move_insn (cfun->eh->filter,
--- 1636,1641 ----
Index: gcc/global.c
===================================================================
*** gcc/global.c.orig 2006-09-08 11:04:54.000000000 +0200
--- gcc/global.c 2006-09-08 14:27:02.000000000 +0200
*************** global_conflicts (void)
*** 743,748 ****
--- 743,776 ----
scan the instruction that makes either X or Y become live. */
record_conflicts (block_start_allocnos, ax);
+ #ifdef EH_RETURN_DATA_REGNO
+ {
+ if (bb_has_pred_edge_with_flags_p (b, EDGE_EH))
+ {
+ unsigned int i;
+ HARD_REG_SET invalidated_by_eh_edge;
+
+ CLEAR_HARD_REG_SET (invalidated_by_eh_edge);
+
+ /* The exception handling registers die at eh edges. */
+ for (i = 0; ; ++i)
+ {
+ unsigned regno = EH_RETURN_DATA_REGNO (i);
+ if (regno == INVALID_REGNUM)
+ break;
+ SET_HARD_REG_BIT (invalidated_by_eh_edge, regno);
+ }
+
+ EXECUTE_IF_SET_IN_REG_SET (old, FIRST_PSEUDO_REGISTER, i, rsi)
+ {
+ int a = reg_allocno[i];
+ if (a >= 0)
+ IOR_HARD_REG_SET (allocno[a].hard_reg_conflicts,
+ invalidated_by_eh_edge);
+ }
+ }
+ }
+ #endif
/* Pseudos can't go in stack regs at the start of a basic block that
is reached by an abnormal edge. Likewise for call clobbered regs,
because caller-save, fixup_abnormal_edges and possibly the table
Index: gcc/basic-block.h
===================================================================
*** gcc/basic-block.h.orig 2006-09-08 11:04:54.000000000 +0200
--- gcc/basic-block.h 2006-09-08 14:29:14.000000000 +0200
*************** extern bool rtx_equiv_p (rtx *, rtx, int
*** 1175,1178 ****
--- 1175,1193 ----
/* In cfgrtl.c */
extern bool condjump_equiv_p (struct equiv_info *, bool);
+ /* Return true when one of the predecessor edges of BB is marked with FLAGS. */
+ static inline bool bb_has_pred_edge_with_flags_p (struct basic_block_def *bb,
+ int flags)
+ {
+ edge e;
+ edge_iterator ei;
+
+ FOR_EACH_EDGE (e, ei, bb->preds)
+ {
+ if ((e->flags & flags) == flags)
+ return true;
+ }
+ return false;
+ }
+
#endif /* GCC_BASIC_BLOCK_H */