reload bug
Jeffrey A Law
law@cygnus.com
Mon Nov 30 17:59:00 GMT 1998
Joern, Bernd, if one of you could dive into this I'm sure everyone would
appreciate it. This is causing the x86 port to mis-compile itself in some
circumstances.
It's another case were we incorrectly delete an output reload. Compile the
attached testcase on an x86 with -O -funroll-all-loops -fomit-frame-pointer
This causes the compiler to mis-compile itself in some circumstances.
--
When reload starts we have the following insns:
[ ... ]
(insn 27 25 30 (set (reg/v:SI 23)
(minus:SI (reg:SI 32)
(reg:SI 33))) 160 {subsi3+1} (insn_list 24 (insn_list 25 (nil)))
(expr_list:REG_DEAD (reg:SI 32)
(expr_list:REG_DEAD (reg:SI 33)
(expr_list:REG_EQUAL (div:SI (reg:SI 28)
(const_int 1009))
(nil)))))
(insn 30 27 32 (set (reg:SI 35)
(ashift:SI (reg/v:SI 23)
(const_int 6))) 235 {ashlsi3} (insn_list 27 (nil))
(expr_list:REG_EQUAL (mult:SI (reg/v:SI 23)
(const_int 64))
(nil)))
(insn 32 30 33 (set (reg:SI 35)
(minus:SI (reg:SI 35)
(reg/v:SI 23))) 160 {subsi3+1} (insn_list 30 (nil))
(expr_list:REG_EQUAL (mult:SI (reg/v:SI 23)
(const_int 63))
(nil)))
We continue and start processing reloads for insn 27:
Reload 0: reload_in (SI) = (reg:SI 0 %eax)
reload_out (SI) = (mem:SI (plus:SI (reg:SI 7 %esp)
(const_int 28)) 0)
GENERAL_REGS, RELOAD_OTHER (opnum = 0)
reload_in_reg: (reg:SI 0 %eax)
reload_out_reg: (reg/v:SI 23)
reload_reg_rtx: (reg:SI 0 %eax)
Note that reload_out references a stack slot.
After completion of emit_reload_insns and subst_reloads for insn 27 we have:
(insn 27 25 222 (set (reg:SI 0 %eax)
(minus:SI (reg:SI 0 %eax)
(reg:SI 1 %edx))) 160 {subsi3+1} (insn_list 24 (insn_list 25 (nil)))
(expr_list:REG_DEAD (reg:SI 0 %eax)
(expr_list:REG_DEAD (reg:SI 1 %edx)
(expr_list:REG_EQUAL (div:SI (reg:SI 2 %ecx)
(const_int 1009))
(nil)))))
(insn 222 27 30 (set (mem:SI (plus:SI (reg:SI 7 %esp)
(const_int 28)) 0)
(reg:SI 0 %eax)) -1 (nil)
(nil))
(insn 30 222 32 (set (reg:SI 0 %eax)
(ashift:SI (reg/v:SI 23)
(const_int 6))) 235 {ashlsi3} (insn_list 27 (nil))
(expr_list:REG_EQUAL (mult:SI (reg/v:SI 23)
(const_int 64))
(nil)))
(insn 32 30 33 (set (reg:SI 0 %eax)
(minus:SI (reg:SI 0 %eax)
(reg/v:SI 23))) 160 {subsi3+1} (insn_list 30 (nil))
(expr_list:REG_EQUAL (mult:SI (reg/v:SI 23)
(const_int 63))
(nil)))
insn 30 has reloads too:
Reload 0: reload_in (SI) = (reg:SI 0 %eax)
reload_out (SI) = (reg:SI 0 %eax)
GENERAL_REGS, RELOAD_OTHER (opnum = 0), can't combine
reload_in_reg: (reg/v:SI 23)
reload_out_reg: (reg:SI 0 %eax)
reload_reg_rtx: (reg:SI 0 %eax)
After completion of emit_reload_insns and subst_reload for insn 30 we have:
(insn 27 25 222 (set (reg:SI 0 %eax)
(minus:SI (reg:SI 0 %eax)
(reg:SI 1 %edx))) 160 {subsi3+1} (insn_list 24 (insn_list 25 (nil)))
(expr_list:REG_DEAD (reg:SI 0 %eax)
(expr_list:REG_DEAD (reg:SI 1 %edx)
(expr_list:REG_EQUAL (div:SI (reg:SI 2 %ecx)
(const_int 1009))
(nil)))))
(insn 222 27 30 (set (mem:SI (plus:SI (reg:SI 7 %esp)
(const_int 28)) 0)
(reg:SI 0 %eax)) -1 (nil)
(nil))
(insn 30 222 32 (set (reg:SI 0 %eax)
(ashift:SI (reg:SI 0 %eax)
(const_int 6))) 235 {ashlsi3} (insn_list 27 (nil))
(expr_list:REG_EQUAL (mult:SI (reg/v:SI 23)
(const_int 64))
(nil)))
(insn 32 30 33 (set (reg:SI 0 %eax)
(minus:SI (reg:SI 0 %eax)
(reg/v:SI 23))) 160 {subsi3+1} (insn_list 30 (nil))
(expr_list:REG_EQUAL (mult:SI (reg/v:SI 23)
(const_int 63))
(nil)))
Note that we've replaced the reg23 in insn 30 with our reload register %eax
which holds the value for reg23. Also note that the value in the stack slot
no longer corresponds to the current value in %eax after the execution of
insn 30. Similarly %eax is no longer equivalent to reg23. after insn 30.
Then we proceed to insn 32 with the following reloads:
Reload 0: reload_in (SI) = (mem:SI (plus:SI (reg:SI 7 %esp)
(const_int 28)) 0)
GENERAL_REGS, RELOAD_FOR_INPUT (opnum = 2), optional, can't combine
reload_in_reg: (reg/v:SI 23)
After finishing emit_reload_insns & subst_reloads for insn 32 we have:
(insn 27 25 222 (set (reg:SI 0 %eax)
(minus:SI (reg:SI 0 %eax)
(reg:SI 1 %edx))) 160 {subsi3+1} (insn_list 24 (insn_list 25 (nil)))
(expr_list:REG_DEAD (reg:SI 0 %eax)
(expr_list:REG_DEAD (reg:SI 1 %edx)
(expr_list:REG_EQUAL (div:SI (reg:SI 2 %ecx)
(const_int 1009))
(nil)))))
(insn 222 27 30 (set (mem:SI (plus:SI (reg:SI 7 %esp)
(const_int 28)) 0)
(reg:SI 0 %eax)) -1 (nil)
(nil))
(insn 30 222 32 (set (reg:SI 0 %eax)
(ashift:SI (reg:SI 0 %eax)
(const_int 6))) 235 {ashlsi3} (insn_list 27 (nil))
(expr_list:REG_EQUAL (mult:SI (reg/v:SI 23)
(const_int 64))
(nil)))
(insn 32 30 33 (set (reg:SI 0 %eax)
(minus:SI (reg:SI 0 %eax)
(mem:SI (plus:SI (reg:SI 7 %esp)
(const_int 28)) 0))) 160 {subsi3+1} (insn_list 30 (nil))
(expr_list:REG_EQUAL (mult:SI (reg/v:SI 23)
(const_int 63))
(nil)))
We're still OK at this point. Note that we now have a read from the memory
location stored in insn 222.
We continue processing reloads for a few more uninteresting instrutions until
we come along this insn:
(insn 37 35 39 (set (mem:SI (plus:SI (reg:SI 7 %esp)
(const_int 28)) 0)
(minus:SI (reg:SI 2 %ecx)
(reg:SI 0 %eax))) 160 {subsi3+1} (insn_list 35 (nil))
(expr_list:REG_DEAD (reg:SI 2 %ecx)
(expr_list:REG_DEAD (reg:SI 0 %eax)
(nil))))
$41 = void
Reload 0: reload_in (SI) = (reg:SI 2 %ecx)
reload_out (SI) = (mem:SI (plus:SI (reg:SI 7 %esp)
(const_int 28)) 0)
GENERAL_REGS, RELOAD_OTHER (opnum = 0)
reload_in_reg: (reg:SI 2 %ecx)
reload_out_reg: (reg/v:SI 23)
reload_reg_rtx: (reg:SI 2 %ecx)
Note that reload_out_reg is reg23.
reg_last_reload_reg[23] = %eax
reg_reloaded_contents[%eax] = 23
spill_reg_store[%eax] = insn 222.
spill_reg_stored_to[%eax] = reg23
We get into this code in emit_reload_insns:
/* If this is an output reload that stores something that is
not loaded in this same reload, see if we can eliminate a previous
store. */
{
rtx pseudo = reload_out_reg[j];
if (pseudo
&& GET_CODE (pseudo) == REG
&& ! rtx_equal_p (reload_in_reg[j], pseudo)
&& REGNO (pseudo) >= FIRST_PSEUDO_REGISTER
&& reg_last_reload_reg[REGNO (pseudo)])
{
int pseudo_no = REGNO (pseudo);
int last_regno = REGNO (reg_last_reload_reg[pseudo_no]);
/* We don't need to test full validity of last_regno for
inherit here; we only want to know if the store actually
matches the pseudo. */
if (reg_reloaded_contents[last_regno] == pseudo_no
&& spill_reg_store[last_regno]
&& rtx_equal_p (pseudo, spill_reg_stored_to[last_regno]))
delete_output_reload (insn, j, last_regno);
}
}
Note the call to delete_output_reload -- it will delete insn 222, which in
turn causes insn 32 to read from an uninitialized memory location.
The testcase:
struct _IO_FILE {
};
typedef struct _IO_FILE _IO_FILE;
struct _IO_FILE_plus;
extern struct _IO_FILE_plus _IO_stdin_, _IO_stdout_, _IO_stderr_;
typedef union rtunion_def
{
struct rtx_def *rtx;
} rtunion;
typedef struct rtx_def
{
unsigned int volatil : 1;
rtunion fld[1];
} *rtx;
enum tree_code {
LAST_AND_UNUSED_TREE_CODE
};
extern char tree_code_type[256 ];
typedef union tree_node *tree;
struct tree_common
{
enum tree_code code : 8;
unsigned public_flag : 1;
};
typedef struct {
int r[(11 + sizeof (int ))/(sizeof (int ))];
} realvaluetype;
struct tree_real_cst
{
struct rtx_def *rtl;
};
struct tree_identifier
{
char *pointer;
};
struct tree_decl
{
struct rtx_def *rtl;
union tree_node *name;
};
union tree_node
{
struct tree_common common;
struct tree_real_cst real_cst;
struct tree_decl decl;
struct tree_identifier identifier;
};
struct _obstack_chunk
{
char *limit;
struct _obstack_chunk *prev;
char contents[4];
};
struct obstack
{
long chunk_size;
struct _obstack_chunk *chunk;
char *object_base;
char *next_free;
char *chunk_limit;
int temp;
int alignment_mask;
struct _obstack_chunk *(*chunkfun) (void *, long);
void (*freefun) (void *, struct _obstack_chunk *);
void *extra_arg;
unsigned use_extra_arg:1;
unsigned maybe_empty_object:1;
unsigned alloc_failed:1;
};
extern struct obstack permanent_obstack;
int const_labelno;
struct addr_const
{
rtx base;
int offset;
};
struct constant_descriptor
{
struct constant_descriptor *next;
char *label;
char contents[1];
};
static struct constant_descriptor *const_hash_table[1009 ];
struct deferred_constant
{
struct deferred_constant *next;
tree exp;
int reloc;
int labelno;
};
static struct deferred_constant *after_function_constants;
static struct deferred_constant *deferred_constants;
static int defer_addressed_constants_flag;
rtx
output_constant_def (exp)
tree exp;
{
register int hash;
register struct constant_descriptor *desc;
char label[256];
char *found = 0;
int reloc;
register rtx def;
hash = const_hash (exp) % 1009 ;
{
do { sprintf ( label , "*.%s%d", "LC" , (unsigned) ( const_labelno )); } while (0) ;
desc->label
= (char *) __extension__ ({ struct obstack *__h = ( &permanent_obstack );
__extension__ ({ struct obstack *__o = ( __h );
int __len = ( ( strlen (label) ) );
if (__o->next_free + __len + 1 > __o->chunk_limit) _obstack_newchunk (__o, __len + 1);
}) ;
__extension__ ({ struct obstack *__o1 = ( __h );
void *value;
if (__o1->next_free == value) __o1->maybe_empty_object = 1;
value;
}) ;
}) ;
const_hash_table[hash] = desc;
}
do {
rtx rtl = (tree_code_type[(int) ( ((enum tree_code) ( exp )->common.code) )] != 'd' ? (( exp ) ->real_cst.rtl) : (( exp ) ->decl.rtl) );
fprintf (((_IO_FILE*)(&_IO_stderr_)) , "Encode %s, public = %d\n", (( (( exp ) ->decl.name) ) ->identifier.pointer) , (( exp )->common.public_flag) );
(( (( rtl )->fld[ 0 ].rtx) )->volatil) = (tree_code_type[(int) ( ((enum tree_code) ( exp )->common.code) )] != 'd' || ! (( exp )->common.public_flag) );
} while (0) ;
}
More information about the Gcc-bugs
mailing list