md description for intruction that modifies multiple operands

Richard Earnshaw rearnsha@arm.com
Fri May 30 12:12:00 GMT 2003


> > However, I'm not clear on whether or not the template guarantees that
> > the register allocation will be sequential.  I suspect not.  So we may
> > still have the problem of training the register allocator to ensure
> > that the operands to the block4.m instruction are always some
> > sequential set of four registers out of the possible 16 (m0-m15).
> 
> OK, I answered this myself by trying the example code:
> 
>   typedef int matrix_t __attribute__((__mode__(V16SI)));
> 
>   matrix_t foo (matrix_t t0, matrix_t t1, matrix_t t2, matrix_t t3)
>   {
>     __BLOCK4_M (t0, t1, t2, t3);
>     __BLOCK4_M (t3, t2, t1, t0);
>     return (t0);
>   }
> 
> which generated:
> 
> foo:
>         block4.m        $m0,$m1,$m2,$m3
>         j       $31
>         block4.m        $m3,$m2,$m1,$m0
> 
> when what it needed to do was to shuffle the contents of m0 through m3
> out of the first block4 into a new set of registers, or the same set
> using a temporary and some register swaps.
> 
> Oh well.  This is still a huge improvement over our first cut, which
> uses an ugly set of intermediate instructions that do vector
> concatenations to get a V64SI type for the unspec for block4 (which
> then takes just one V64SI operand), and then a bunch more vector
> splits to pick the result apart as needed.  The register allocator
> does know to put a V64SI type in four sequential matrix registers,
> each of which holds an V16SI type.

If that's the case, then you might be able to make something like the 
following work:

	(set (subreg:V16SI (reg:V64SI tmp1) 0) (reg:V16SI t0))
	(set (subreg:V16SI (reg:V64SI tmp1) 1) (reg:V16SI t1))
	(set (subreg:V16SI (reg:V64SI tmp1) 2) (reg:V16SI t2))
	(set (subreg:V16SI (reg:V64SI tmp1) 3) (reg:V16SI t3))

 	(parallel [(set (reg:V64SI tmp2) (unspec:V64SI [(reg:V64SI tmp1)] 
VEC_MMUL))
		   (use (subreg:V16SI (reg:V64SI tmp2) 0))
		   (use (subreg:V16SI (reg:V64SI tmp2) 1))
		   (use (subreg:V16SI (reg:V64SI tmp2) 2))
		   (use (subreg:V16SI (reg:V64SI tmp2) 3))])

	(set (reg:V16SI t0) (subreg:V16SI (reg:V64SI tmp2) 0))
	(set (reg:V16SI t1) (subreg:V16SI (reg:V64SI tmp2) 1))
	(set (reg:V16SI t2) (subreg:V16SI (reg:V64SI tmp2) 2))
	(set (reg:V16SI t3) (subreg:V16SI (reg:V64SI tmp2) 3))

Where each line (except the body of the parallel) is a separate 
instruction.  Hopefully the register allocator will be able to eliminate 
most of the move instructions by operating directly on the subregs.  
Remember to use operand_subword() to generate the SUBREGs or you might run 
into problems with subregs of subregs...

You would then have a pattern to match the parallel something like

(define_insn "*fm_block4_body"
  (set (match_operand:V64SI 0 "register_operand" "=d")
       (unspec:V64SI 1 "register_operand" "0"))
  (use (match_operand:V16SI 2 "register_operand" "X"))
  (use (match_operand:V16SI 3 "register_operand" "X"))
  (use (match_operand:V16SI 4 "register_operand" "X"))
  (use (match_operand:V16SI 5 "register_operand" "X"))
  ""
  "block4.m\t%2, %3, %4, %5")

Note that the USE operands use the X constraint letter.  This should cause 
the register allocator to ignore these parts of the instruction for 
register allocation purposes.  This shouldn't matter since they are tied 
in the pattern to use tmp2 and exist solely so that we can extract the 
subregs of the V64SI register that finally gets allocated.

Finally, if the above seems to work but produce poor code, then try it 
with the new register allocator (-fnew-ra).

Note, I haven't tried any of the above.  So you will have to experiment.  
There's a possibility that the above will fail completely with the 
register renumbering pass at -O3.

R.




More information about the Gcc mailing list