Commit e314a036 by Josef Zlomek Committed by Josef Zlomek

re PR middle-end/14084 (Reg allocator incorrectly changes REG_OFFSET)

	PR middle-end/14084
	* emit-rtl.c (gen_rtx_REG_offset): Adjust the offset according
	to size of decl.

From-SVN: r82313
parent 2140214f
2004-05-27 Josef Zlomek <zlomekj@suse.cz>
PR middle-end/14084
* emit-rtl.c (gen_rtx_REG_offset): Adjust the offset according
to size of decl.
2004-05-26 Aldy Hernandez <aldyh@redhat.com>
PR/14924
......
......@@ -746,13 +746,96 @@ gen_reg_rtx (enum machine_mode mode)
return val;
}
/* Generate a register with same attributes as REG,
but offsetted by OFFSET. */
/* Generate a register with same attributes as REG, but offsetted by OFFSET.
Do the big endian correction if needed. */
rtx
gen_rtx_REG_offset (rtx reg, enum machine_mode mode, unsigned int regno, int offset)
{
rtx new = gen_rtx_REG (mode, regno);
tree decl;
HOST_WIDE_INT var_size;
/* PR middle-end/14084
The problem appears when a variable is stored in a larger register
and later it is used in the original mode or some mode in between
or some part of variable is accessed.
On little endian machines there is no problem because
the REG_OFFSET of the start of the variable is the same when
accessed in any mode (it is 0).
However, this is not true on big endian machines.
The offset of the start of the variable is different when accessed
in different modes.
When we are taking a part of the REG we have to change the OFFSET
from offset WRT size of mode of REG to offset WRT size of variable.
If we would not do the big endian correction the resulting REG_OFFSET
would be larger than the size of the DECL.
Examples of correction, for BYTES_BIG_ENDIAN WORDS_BIG_ENDIAN machine:
REG.mode MODE DECL size old offset new offset description
DI SI 4 4 0 int32 in SImode
DI SI 1 4 0 char in SImode
DI QI 1 7 0 char in QImode
DI QI 4 5 1 1st element in QImode
of char[4]
DI HI 4 6 2 1st element in HImode
of int16[2]
If the size of DECL is equal or greater than the size of REG
we can't do this correction because the register holds the
whole variable or a part of the variable and thus the REG_OFFSET
is already correct. */
decl = REG_EXPR (reg);
if ((BYTES_BIG_ENDIAN || WORDS_BIG_ENDIAN)
&& decl != NULL
&& offset > 0
&& GET_MODE_SIZE (GET_MODE (reg)) > GET_MODE_SIZE (mode)
&& ((var_size = int_size_in_bytes (TREE_TYPE (decl))) > 0
&& var_size < GET_MODE_SIZE (GET_MODE (reg))))
{
int offset_le;
/* Convert machine endian to little endian WRT size of mode of REG. */
if (WORDS_BIG_ENDIAN)
offset_le = ((GET_MODE_SIZE (GET_MODE (reg)) - 1 - offset)
/ UNITS_PER_WORD) * UNITS_PER_WORD;
else
offset_le = (offset / UNITS_PER_WORD) * UNITS_PER_WORD;
if (BYTES_BIG_ENDIAN)
offset_le += ((GET_MODE_SIZE (GET_MODE (reg)) - 1 - offset)
% UNITS_PER_WORD);
else
offset_le += offset % UNITS_PER_WORD;
if (offset_le >= var_size)
{
/* MODE is wider than the variable so the new reg will cover
the whole variable so the resulting OFFSET should be 0. */
offset = 0;
}
else
{
/* Convert little endian to machine endian WRT size of variable. */
if (WORDS_BIG_ENDIAN)
offset = ((var_size - 1 - offset_le)
/ UNITS_PER_WORD) * UNITS_PER_WORD;
else
offset = (offset_le / UNITS_PER_WORD) * UNITS_PER_WORD;
if (BYTES_BIG_ENDIAN)
offset += ((var_size - 1 - offset_le)
% UNITS_PER_WORD);
else
offset += offset_le % UNITS_PER_WORD;
}
}
REG_ATTRS (new) = get_reg_attrs (REG_EXPR (reg),
REG_OFFSET (reg) + offset);
return new;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment