Analysis of high priority PR c/2454
Jim Wilson
wilson@redhat.com
Tue Jul 2 20:37:00 GMT 2002
>Not really. For example, we could create patterns which matched the
>load-with-{zero,sign} extension.
It is a little more complicated than that. Suppose we have something like
static unsigned char in;
static unsigned char out1;
static unsigned int out2;
sub ()
{
register unsigned char tmp;
tmp = in;
out1 = tmp;
out2 = tmp;
}
The RTL would look like
(set (reg:QI tmp1) (mem:QI (symbol_ref:SI in)))
(set (mem:QI (symbol_ref:SI out1)) (reg:QI tmp1))
(set (reg:SI tmp2) (zero_extend:SI (reg:QI tmp1)))
(set (mem:SI (symbol_ref:SI out2)) (reg:SI tmp2))
This is four instructions, but on a typical RISC, this is really only 3
instructions because a byte-load always modifies the entire register. So we
know we can do better. You might have to change "unsigned" to "signed" for
this example to work on your favorite RISC target.
We can't easily get rid of the explicit zero_extend, because both tmp1 and
tmp2 are used. We can merge the first and third instructions only if we
rewrite the second to use (subreg:QI (reg:SI tmp2)), and neither cse nor
combine can do this.
Simply adding explicit extending load patterns doesn't solve this problem.
The current code in combine solves this problem by giving special meaning
to paradoxical subregs when WORD_REGISTER_OPERATIONS and LOAD_EXTEND_OP are
both set. Now we can merge the third and fourth instructions in combine to be
(set (mem:SI (symbol_ref:SI out2)) (subreg:SI (reg:QI tmp1)))
and we have the 3 instruction sequence we want. Unfortunately, we have a
confusing RTL meaning that has caused lots of bugs.
You can reproduce the behaviour I described using the i960 target.
An alternative that should work would be to modify the movqi pattern to emit
two instructions. This has the benefit that it more closely matches what the
hardware does. It looks like a strange thing to do, but it does make sense.
So the output of movqi would be:
(set (reg:SI tmp1a) (zero_extend:SI (mem:QI (symbol_ref:SI in))))
(set (reg:QI tmp1) (subreg:QI (reg:SI tmp1a)))
So now we have a 5 insn sequence
(set (reg:SI tmp1a) (zero_extend:SI (mem:QI (symbol_ref:SI in))))
(set (reg:QI tmp1) (subreg:QI (reg:SI tmp1a)))
(set (mem:QI (symbol_ref:SI out1)) (reg:QI tmp1))
(set (reg:SI tmp2) (zero_extend:SI (reg:QI tmp1)))
(set (mem:SI (symbol_ref:SI out2)) (reg:SI tmp2))
The second insn can be cse'd into the third insn, and then we can combine
the second, fourth, and fifth instructions into one, giving the desired 3
insn sequence.
(set (reg:SI tmp1a) (zero_extend:SI (mem:QI (symbol_ref:SI in))))
(set (mem:QI (symbol_ref:SI out1)) (subreg:QI (reg:QI tmp1a)))
(set (mem:SI (symbol_ref:SI out2)) (reg:SI tmp1a))
I don't know of anyone that has tried this, and it isn't clear if it would
always work. If the optimizer doesn't do what we expect, we might end up
with extra unnecessary extension instructions at the end.
I noticed that this happens by accident for the IA-64 target, as a side-effect
of PROMOTE_MODES. PROMOTE_MODES causes the first load to automatically be
extended, and then everything falls out from that. The i960 has explicit
extending loads, but does not define PROMOTE_MODES.
So if we define PROMOTE_MODES for every RISC target, then we may be able to
get rid of the funny paradoxical subreg treatment in combine.c without losing
any significant performance. We also need the explicit extending load
instructions, but I would expect that most RISC targets already have patterns
for this.
Coincidentally, I notice that the PA port does not define PROMOTE_MODES.
Jim
More information about the Gcc
mailing list