This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
Another NaT bit propagation bug.
- To: gcc-bugs at gcc dot gnu dot org, gcc-patches at gcc dot gnu dot org
- Subject: Another NaT bit propagation bug.
- From: amacleod at simba dot cygnus dot com
- Date: Wed, 7 Nov 2001 06:13:47 -0800
When compiling linux, we run into a case where setting bitfields in
a structure result in a NaT bit being incorrectly propagated. The
test case is:
typedef union {
unsigned all :16;
struct {
unsigned low :8; /* LSB */
unsigned high :8; /* MSB */
} b;
} idefloppy_bcount_reg_t;
extern unsigned char get_char();
extern int yyy(void);
void xxx(void)
{
idefloppy_bcount_reg_t bcount;
bcount.all = 0;
bcount.b.high = get_char();
bcount.b.low = get_char();
if (bcount.all)
yyy();
}
The set to 0 of bcount.all gets optimized away since all the fields in
the structure are eventually set. However, since the fields are all set using
subregs and zero_extracts, the NaT bit from the register being used gets
propagated incorrectly.
The simplest way to handle this is to look for any registers which
are live on entry to the function, check to see if their first use is
in a zero_extract or subreg expression, and if so, set them to 0 immediately
following the prologue. Since this is specific to ia64, I've put the
code into ia64_reorg.
This is required to build the OS on ia64. Is this OK to check into
mainline? It bootstraps on both ia64 and x86, and introduces no new
failures in c-torture.
Andrew
* config/ia64/ia64.c (find_regno_partial): Find subregs in an expr.
(initialize_uninitialized_subregs): Find live on entry subreg sets, and
initialize them to 0.
(ia64_reorg): When optimizing, call initialize_uninitialized_subregs ().
Index: config/ia64/ia64.c
===================================================================
RCS file: /cvs/gcc/egcs/gcc/config/ia64/ia64.c,v
retrieving revision 1.123
diff -c -p -r1.123 ia64.c
*** ia64.c 2001/09/21 01:27:05 1.123
--- ia64.c 2001/11/07 15:05:12
*************** ia64_emit_nops ()
*** 6652,6657 ****
--- 6652,6767 ----
}
}
+ int initialize_uninitialized_subregs PARAMS ((void));
+ int find_regno_partial PARAMS ((rtx *, void *));
+
+ static rtx find_regno_retval; /* Used to return a value from func. */
+
+ /* Find the rtx for the reg numbers specified in 'data' if it is
+ part of an expression which only uses part of the register. Return
+ it in func_retval. */
+ int find_regno_partial (ptr, data)
+ rtx *ptr;
+ void *data;
+ {
+ unsigned reg = *((unsigned *)data);
+ if (*ptr == NULL_RTX)
+ return 0;
+
+ switch (GET_CODE (*ptr))
+ {
+ case ZERO_EXTRACT:
+ case SIGN_EXTRACT:
+ case STRICT_LOW_PART:
+ if (GET_CODE (XEXP (*ptr, 0)) == REG && REGNO (XEXP (*ptr, 0)) == reg)
+ {
+ find_regno_retval = XEXP (*ptr, 0);
+ return 1;
+ }
+ break;
+
+ case SUBREG:
+ if (GET_CODE (SUBREG_REG (*ptr)) == REG
+ && REGNO (SUBREG_REG (*ptr)) == reg)
+ {
+ find_regno_retval = XEXP (*ptr, 0);
+ return 1;
+ }
+ break;
+ }
+
+ return 0;
+ }
+
+ /* Process all immediate successors of the entry block looking for pseudo
+ registers which are live on entry. Find all of those whose first
+ instance is a partial register reference of some kind, and initialize
+ them to 0 after the entry block. This will prevent bit sets within
+ registers whose value is unknown, and may contain some kind of sticky
+ bits we don't want. */
+
+ int
+ initialize_uninitialized_subregs ()
+ {
+ rtx insn, x;
+ edge e;
+ int reg, did_something = 0;
+ for (e = ENTRY_BLOCK_PTR->succ; e; e = e->succ_next)
+ {
+ basic_block bb = e->dest;
+ regset map = bb->global_live_at_start;
+ rtx insert_after = NULL_RTX;
+
+ /* Now skip any frame_related insns we encounter. We want to be sure
+ that we issue our instruction after the alloc, if there is one.
+ If we don't find any frame insns, we'll insert it on the edge. */
+ insn = next_real_insn (bb->head);
+ while (insn && RTX_FRAME_RELATED_P (insn))
+ {
+ insert_after = insn;
+ insn = next_real_insn (insn);
+ }
+
+ EXECUTE_IF_SET_IN_REG_SET (map, 0, reg,
+ {
+ rtx i;
+ int n_input = current_frame_info.n_input_regs;
+
+ /* Input registers look like they are live on entry, but aren't. */
+ if (n_input == 0 || (reg < IN_REG (0) || reg > IN_REG (n_input - 1)))
+ {
+ /* Find an insn which mentions the register we are looking for.
+ Its preferable to have an instance of the register's rtl since
+ there may be various flags set which we need to duplicate.
+ If we can't find it, its probably an automatic whose initial
+ value doesn't matter, or something we hopefully don't care
+ about. */
+ for (i = get_insns (); i ; i = NEXT_INSN (i))
+ {
+ find_regno_retval = NULL_RTX;
+ for_each_rtx (&i, find_regno_partial, ®);
+ if (find_regno_retval != NULL_RTX)
+ {
+ x = find_regno_retval;
+ insn = gen_move_insn (x, CONST0_RTX (GET_MODE (x)));
+ if (insert_after)
+ emit_insn_after (insn, insert_after);
+ else
+ insert_insn_on_edge (insn, e);
+ did_something = 1;
+ break;
+ }
+ }
+ }
+ });
+ }
+
+ if (did_something)
+ commit_edge_insertions ();
+ return did_something;
+ }
+
+
/* Perform machine dependent operations on the rtl chain INSNS. */
void
*************** ia64_reorg (insns)
*** 6666,6671 ****
--- 6776,6788 ----
for emit_predicate_relation_info. */
find_basic_blocks (insns, max_reg_num (), NULL);
life_analysis (insns, NULL, PROP_DEATH_NOTES);
+
+ if (optimize > 0 && initialize_uninitialized_subregs ())
+ {
+ /* Insns were inserted, so things might look a bit different. */
+ insns = get_insns();
+ life_analysis (insns, NULL, PROP_REG_INFO);
+ }
if (ia64_flag_schedule_insns2)
{