Re: [PATCH] Hard register asm constraint

2024-06-28 Thread Stefan Schulze Frielinghaus
On Fri, Jun 28, 2024 at 11:46:08AM +0200, Georg-Johann Lay wrote:
> Am 27.06.24 um 10:51 schrieb Stefan Schulze Frielinghaus:
> > On Thu, Jun 27, 2024 at 09:45:32AM +0200, Georg-Johann Lay wrote:
> > > Am 24.05.24 um 11:13 Am 25.06.24 um 16:03 schrieb Paul Koning:
> > > > > On Jun 24, 2024, at 1:50 AM, Stefan Schulze Frielinghaus 
> > > > >  wrote:
> > > > > On Mon, Jun 10, 2024 at 07:19:19AM +0200, Stefan Schulze Frielinghaus 
> > > > > wrote:
> > > > > > On Fri, May 24, 2024 at 11:13:12AM +0200, Stefan Schulze 
> > > > > > Frielinghaus wrote:
> > > > > > > This implements hard register constraints for inline asm.  A hard 
> > > > > > > register
> > > > > > > constraint is of the form {regname} where regname is any valid 
> > > > > > > register.  This
> > > > > > > basically renders register asm superfluous.  For example, the 
> > > > > > > snippet
> > > > > > > 
> > > > > > > int test (int x, int y)
> > > > > > > {
> > > > > > >register int r4 asm ("r4") = x;
> > > > > > >register int r5 asm ("r5") = y;
> > > > > > >unsigned int copy = y;
> > > > > > >asm ("foo %0,%1,%2" : "+d" (r4) : "d" (r5), "d" (copy));
> > > > > > >return r4;
> > > > > > > }
> > > > > > > 
> > > > > > > could be rewritten into
> > > > > > > 
> > > > > > > int test (int x, int y)
> > > > > > > {
> > > > > > >asm ("foo %0,%1,%2" : "+{r4}" (x) : "{r5}" (y), "d" (y));
> > > > > > >return x;
> > > > > > > }
> > > > 
> > > > I like this idea but I'm wondering: regular constraints specify what 
> > > > sort of value is needed, for example an int vs. a short int vs. a 
> > > > float.  The notation you've shown doesn't seem to have that aspect.
> > > > 
> > > > The other comment is that I didn't see documentation updates to reflect 
> > > > this new feature.
> > > > 
> > > > paul
> > > > 
> > >   Stefan Schulze Frielinghaus:
> > > > This implements hard register constraints for inline asm.  A hard 
> > > > register
> > > > constraint is of the form {regname} where regname is any valid 
> > > > register.  This
> > > > basically renders register asm superfluous.  For example, the snippet
> > > > 
> > > > int test (int x, int y)
> > > > {
> > > > register int r4 asm ("r4") = x;
> > > > register int r5 asm ("r5") = y;
> > > > unsigned int copy = y;
> > > > asm ("foo %0,%1,%2" : "+d" (r4) : "d" (r5), "d" (copy));
> > > > return r4;
> > > > }
> > > > 
> > > > could be rewritten into
> > > > 
> > > > int test (int x, int y)
> > > > {
> > > > asm ("foo %0,%1,%2" : "+{r4}" (x) : "{r5}" (y), "d" (y));
> > > > return x;
> > > > }
> > > 
> > > Hi, can this also be used in machine descriptions?
> > > 
> > > It would make some insn handling much simpler, for example in
> > > the avr backend.
> > > 
> > > That backend has insns that represent assembly sequences in libgcc
> > > which have a smaller register footprint than plain calls.  However
> > > this requires that such insns have explicit description of which regs
> > > go in and out.
> > > 
> > > The current solution uses hard regs, which works, but a proper
> > > implementation would use register constraints.  I tries that a while
> > > ago, and register constraints lead to a code bloat even in places that
> > > don't use these constraints due to the zillions of new register classes
> > > like R22_1, R22;2, R22_4, R20_1, R20_2, R20_4 etc. that were required.
> > > 
> > > Your approach would allow to use hard register constraints in insns,
> > > and so far the only problem is to determine how much hard regs are
> > > used by the constraint.  The gen tools that generates cc code from md
> > > would use the operand's machine mode to infer the number of hard regs.
> > 
> > I have this on my todo list but ignored it for the very first draft.  At
> > the moment this already fails because genoutput cannot parse the
> > constraint format.
> > 
> > In my "alpha draft" I implemented this feature by emitting moves to hard
> > registers during expand.  This had the limitation that I couldn't
> 
> One problem is that you cannot just introduce hard registers at that
> time because a hard reg may live across the sequence, see for example
> avr.cc::avr_emit3_fix_outputs() and avr_fix_operands().

Yea I was fearing this.  I did some testing on x86_64 and s390 including
explicit function calls, sanitizers etc. but of course this was not
complete which is why I think that the current draft is more robust.

> 
> > support multiple alternatives in combination with hard-register
> > constraints.  I'm still not sure whether this is a feature we really
> > want or whether it should be rather denied.  Anyhow, with this kind of
> > implementation I doubt that this would be feasible for machine
> > descriptions.  I moved on with my current draft where the constraint
> > manifests during register allocation.  This also allows multiple
> > alternatives.  I think one of the (major?) advantages of doing it this
> > way is that operands are kept in pseudos which means 

Re: [PATCH] Hard register asm constraint

2024-06-28 Thread Georg-Johann Lay

Am 27.06.24 um 10:51 schrieb Stefan Schulze Frielinghaus:

On Thu, Jun 27, 2024 at 09:45:32AM +0200, Georg-Johann Lay wrote:

Am 24.05.24 um 11:13 Am 25.06.24 um 16:03 schrieb Paul Koning:

On Jun 24, 2024, at 1:50 AM, Stefan Schulze Frielinghaus 
 wrote:
On Mon, Jun 10, 2024 at 07:19:19AM +0200, Stefan Schulze Frielinghaus wrote:

On Fri, May 24, 2024 at 11:13:12AM +0200, Stefan Schulze Frielinghaus wrote:

This implements hard register constraints for inline asm.  A hard register
constraint is of the form {regname} where regname is any valid register.  This
basically renders register asm superfluous.  For example, the snippet

int test (int x, int y)
{
   register int r4 asm ("r4") = x;
   register int r5 asm ("r5") = y;
   unsigned int copy = y;
   asm ("foo %0,%1,%2" : "+d" (r4) : "d" (r5), "d" (copy));
   return r4;
}

could be rewritten into

int test (int x, int y)
{
   asm ("foo %0,%1,%2" : "+{r4}" (x) : "{r5}" (y), "d" (y));
   return x;
}


I like this idea but I'm wondering: regular constraints specify what sort of 
value is needed, for example an int vs. a short int vs. a float.  The notation 
you've shown doesn't seem to have that aspect.

The other comment is that I didn't see documentation updates to reflect this 
new feature.

paul


  Stefan Schulze Frielinghaus:

This implements hard register constraints for inline asm.  A hard register
constraint is of the form {regname} where regname is any valid register.  This
basically renders register asm superfluous.  For example, the snippet

int test (int x, int y)
{
register int r4 asm ("r4") = x;
register int r5 asm ("r5") = y;
unsigned int copy = y;
asm ("foo %0,%1,%2" : "+d" (r4) : "d" (r5), "d" (copy));
return r4;
}

could be rewritten into

int test (int x, int y)
{
asm ("foo %0,%1,%2" : "+{r4}" (x) : "{r5}" (y), "d" (y));
return x;
}


Hi, can this also be used in machine descriptions?

It would make some insn handling much simpler, for example in
the avr backend.

That backend has insns that represent assembly sequences in libgcc
which have a smaller register footprint than plain calls.  However
this requires that such insns have explicit description of which regs
go in and out.

The current solution uses hard regs, which works, but a proper
implementation would use register constraints.  I tries that a while
ago, and register constraints lead to a code bloat even in places that
don't use these constraints due to the zillions of new register classes
like R22_1, R22;2, R22_4, R20_1, R20_2, R20_4 etc. that were required.

Your approach would allow to use hard register constraints in insns,
and so far the only problem is to determine how much hard regs are
used by the constraint.  The gen tools that generates cc code from md
would use the operand's machine mode to infer the number of hard regs.


I have this on my todo list but ignored it for the very first draft.  At
the moment this already fails because genoutput cannot parse the
constraint format.

In my "alpha draft" I implemented this feature by emitting moves to hard
registers during expand.  This had the limitation that I couldn't


One problem is that you cannot just introduce hard registers at that
time because a hard reg may live across the sequence, see for example
avr.cc::avr_emit3_fix_outputs() and avr_fix_operands().


support multiple alternatives in combination with hard-register
constraints.  I'm still not sure whether this is a feature we really
want or whether it should be rather denied.  Anyhow, with this kind of
implementation I doubt that this would be feasible for machine
descriptions.  I moved on with my current draft where the constraint
manifests during register allocation.  This also allows multiple
alternatives.  I think one of the (major?) advantages of doing it this
way is that operands are kept in pseudos which means they are
automagically saved/restored over function boundaries and what not.  Or
in other words, the register constraint manifests at the asm boundary
which is probably what users expect and should be less error prone


As far as I know, a local register variable is only supposed to be
loaded to the specified register when the variable is used as an
operand to some inline asm.  Only in such asm statements, the
variable will live in the specified register.  So "surviving" a
function call is not even a problem to solve with the current local
regvar semantic?


(again just thinking of implicit code which gets injected as e.g. by
sanitizers introducing calls etc.).

So long story short, I would like to look into this but currently it
doesn't work.  I'm also not sure to which extend this could be used.
However, once I have some more time I will have a look at the avr
backend for examples.

Cheers,
Stefan


Great.  When you have any questions about the avr backend, don't
hesitate to ask me.

Cheers,
Johann



Re: [PATCH] Hard register asm constraint

2024-06-27 Thread Stefan Schulze Frielinghaus
On Thu, Jun 27, 2024 at 09:45:32AM +0200, Georg-Johann Lay wrote:
> 
> 
> Am 24.05.24 um 11:13 Am 25.06.24 um 16:03 schrieb Paul Koning:
> > 
> > 
> > > On Jun 24, 2024, at 1:50 AM, Stefan Schulze Frielinghaus 
> > >  wrote:
> > > 
> > > Ping.
> > > 
> > > On Mon, Jun 10, 2024 at 07:19:19AM +0200, Stefan Schulze Frielinghaus 
> > > wrote:
> > > > Ping.
> > > > 
> > > > On Fri, May 24, 2024 at 11:13:12AM +0200, Stefan Schulze Frielinghaus 
> > > > wrote:
> > > > > This implements hard register constraints for inline asm.  A hard 
> > > > > register
> > > > > constraint is of the form {regname} where regname is any valid 
> > > > > register.  This
> > > > > basically renders register asm superfluous.  For example, the snippet
> > > > > 
> > > > > int test (int x, int y)
> > > > > {
> > > > >   register int r4 asm ("r4") = x;
> > > > >   register int r5 asm ("r5") = y;
> > > > >   unsigned int copy = y;
> > > > >   asm ("foo %0,%1,%2" : "+d" (r4) : "d" (r5), "d" (copy));
> > > > >   return r4;
> > > > > }
> > > > > 
> > > > > could be rewritten into
> > > > > 
> > > > > int test (int x, int y)
> > > > > {
> > > > >   asm ("foo %0,%1,%2" : "+{r4}" (x) : "{r5}" (y), "d" (y));
> > > > >   return x;
> > > > > }
> > 
> > I like this idea but I'm wondering: regular constraints specify what sort 
> > of value is needed, for example an int vs. a short int vs. a float.  The 
> > notation you've shown doesn't seem to have that aspect.
> > 
> > The other comment is that I didn't see documentation updates to reflect 
> > this new feature.
> > 
> > paul
> > 
>  Stefan Schulze Frielinghaus:
> > This implements hard register constraints for inline asm.  A hard register
> > constraint is of the form {regname} where regname is any valid register.  
> > This
> > basically renders register asm superfluous.  For example, the snippet
> > 
> > int test (int x, int y)
> > {
> >register int r4 asm ("r4") = x;
> >register int r5 asm ("r5") = y;
> >unsigned int copy = y;
> >asm ("foo %0,%1,%2" : "+d" (r4) : "d" (r5), "d" (copy));
> >return r4;
> > }
> > 
> > could be rewritten into
> > 
> > int test (int x, int y)
> > {
> >asm ("foo %0,%1,%2" : "+{r4}" (x) : "{r5}" (y), "d" (y));
> >return x;
> > }
> 
> Hi, can this also be used in machine descriptions?
> 
> It would make some insn handling much simpler, for example in
> the avr backend.
> 
> That backend has insns that represent assembly sequences in libgcc
> which have a smaller register footprint than plain calls.  However
> this requires that such insns have explicit description of which regs
> go in and out.
> 
> The current solution uses hard regs, which works, but a proper
> implementation would use register constraints.  I tries that a while
> ago, and register constraints lead to a code bloat even in places that
> don't use these constraints due to the zillions of new register classes
> like R22_1, R22;2, R22_4, R20_1, R20_2, R20_4 etc. that were required.
> 
> Your approach would allow to use hard register constraints in insns,
> and so far the only problem is to determine how much hard regs are
> used by the constraint.  The gen tools that generates cc code from md
> would use the operand's machine mode to infer the number of hard regs.

I have this on my todo list but ignored it for the very first draft.  At
the moment this already fails because genoutput cannot parse the
constraint format.

In my "alpha draft" I implemented this feature by emitting moves to hard
registers during expand.  This had the limitation that I couldn't
support multiple alternatives in combination with hard-register
constraints.  I'm still not sure whether this is a feature we really
want or whether it should be rather denied.  Anyhow, with this kind of
implementation I doubt that this would be feasible for machine
descriptions.  I moved on with my current draft where the constraint
manifests during register allocation.  This also allows multiple
alternatives.  I think one of the (major?) advantages of doing it this
way is that operands are kept in pseudos which means they are
automagically saved/restored over function boundaries and what not.  Or
in other words, the register constraint manifests at the asm boundary
which is probably what users expect and should be less error prone
(again just thinking of implicit code which gets injected as e.g. by
sanitizers introducing calls etc.).

So long story short, I would like to look into this but currently it
doesn't work.  I'm also not sure to which extend this could be used.
However, once I have some more time I will have a look at the avr
backend for examples.

Cheers,
Stefan


Re: [PATCH] Hard register asm constraint

2024-06-27 Thread Georg-Johann Lay




Am 24.05.24 um 11:13 Am 25.06.24 um 16:03 schrieb Paul Koning:




On Jun 24, 2024, at 1:50 AM, Stefan Schulze Frielinghaus 
 wrote:

Ping.

On Mon, Jun 10, 2024 at 07:19:19AM +0200, Stefan Schulze Frielinghaus wrote:

Ping.

On Fri, May 24, 2024 at 11:13:12AM +0200, Stefan Schulze Frielinghaus wrote:

This implements hard register constraints for inline asm.  A hard register
constraint is of the form {regname} where regname is any valid register.  This
basically renders register asm superfluous.  For example, the snippet

int test (int x, int y)
{
  register int r4 asm ("r4") = x;
  register int r5 asm ("r5") = y;
  unsigned int copy = y;
  asm ("foo %0,%1,%2" : "+d" (r4) : "d" (r5), "d" (copy));
  return r4;
}

could be rewritten into

int test (int x, int y)
{
  asm ("foo %0,%1,%2" : "+{r4}" (x) : "{r5}" (y), "d" (y));
  return x;
}


I like this idea but I'm wondering: regular constraints specify what sort of 
value is needed, for example an int vs. a short int vs. a float.  The notation 
you've shown doesn't seem to have that aspect.

The other comment is that I didn't see documentation updates to reflect this 
new feature.

paul


 Stefan Schulze Frielinghaus:

This implements hard register constraints for inline asm.  A hard register
constraint is of the form {regname} where regname is any valid register.  This
basically renders register asm superfluous.  For example, the snippet

int test (int x, int y)
{
   register int r4 asm ("r4") = x;
   register int r5 asm ("r5") = y;
   unsigned int copy = y;
   asm ("foo %0,%1,%2" : "+d" (r4) : "d" (r5), "d" (copy));
   return r4;
}

could be rewritten into

int test (int x, int y)
{
   asm ("foo %0,%1,%2" : "+{r4}" (x) : "{r5}" (y), "d" (y));
   return x;
}


Hi, can this also be used in machine descriptions?

It would make some insn handling much simpler, for example in
the avr backend.

That backend has insns that represent assembly sequences in libgcc
which have a smaller register footprint than plain calls.  However
this requires that such insns have explicit description of which regs
go in and out.

The current solution uses hard regs, which works, but a proper
implementation would use register constraints.  I tries that a while
ago, and register constraints lead to a code bloat even in places that
don't use these constraints due to the zillions of new register classes
like R22_1, R22;2, R22_4, R20_1, R20_2, R20_4 etc. that were required.

Your approach would allow to use hard register constraints in insns,
and so far the only problem is to determine how much hard regs are
used by the constraint.  The gen tools that generates cc code from md
would use the operand's machine mode to infer the number of hard regs.

Johann



As a side-effect this also solves the problem of call-clobbered registers.
That being said, I was wondering whether we could utilize this feature in order
to get rid of local register asm automatically?  For example, converting

// Result will be in r2 on s390
extern int bar (void);

void test (void)
{
   register int x asm ("r2") = 42;
   bar ();
   asm ("foo %0\n" :: "r" (x));
}

into

void test (void)
{
   int x = 42;
   bar ();
   asm ("foo %0\n" :: "{r2}" (x));
}

in order to get rid of the limitation of call-clobbered registers which may
lead to subtle bugs---especially if you think of non-obvious calls e.g.
introduced by sanitizer/tracer/whatever.  Since such a transformation has the
potential to break existing code do you see any edge cases where this might be
problematic or even show stoppers?  Currently, even

int test (void)
{
   register int x asm ("r2") = 42;
   register int y asm ("r2") = 24;
   asm ("foo %0,%1\n" :: "r" (x), "r" (y));
}

is allowed which seems error prone to me.  Thus, if 100% backwards
compatibility would be required, then automatically converting every register
asm to the new mechanism isn't viable.  Still quite a lot could be transformed.
Any thoughts?

Currently I allow multiple alternatives as demonstrated by
gcc/testsuite/gcc.target/s390/asm-hard-reg-2.c.  However, since a hard register
constraint is pretty specific I could also think of erroring out in case of
alternatives.  Are there any real use cases out there for multiple
alternatives where one would like to use hard register constraints?

With the current implementation we have a "user visible change" in the sense
that for

void test (void)
{
   register int x asm ("r2") = 42;
   register int y asm ("r2") = 24;
   asm ("foo   %0,%1\n" : "=r" (x), "=r" (y));
}

we do not get the error

   "invalid hard register usage between output operands"

anymore but rather

   "multiple outputs to hard register: %r2"

This is due to the error handling in gimplify_asm_expr ().  Speaking of errors,
I also error out earlier as before which means that e.g. in pr87600-2.c only
the first error is reported and processing is stopped afterwards which means
the subsequent tests fail.

I've been skimming through all targets and it looks to me as 

Re: [PATCH] Hard register asm constraint

2024-06-27 Thread Stefan Schulze Frielinghaus
On Wed, Jun 26, 2024 at 11:10:38AM -0400, Paul Koning wrote:
> 
> 
> > On Jun 26, 2024, at 8:54 AM, Stefan Schulze Frielinghaus 
> >  wrote:
> > 
> > On Tue, Jun 25, 2024 at 01:02:39PM -0400, Paul Koning wrote:
> >> 
> >> 
> >>> On Jun 25, 2024, at 12:04 PM, Stefan Schulze Frielinghaus 
> >>>  wrote:
> >>> 
> >>> On Tue, Jun 25, 2024 at 10:03:34AM -0400, Paul Koning wrote:
>  
> >>> ...
> >>> could be rewritten into
> >>> 
> >>> int test (int x, int y)
> >>> {
> >>> asm ("foo %0,%1,%2" : "+{r4}" (x) : "{r5}" (y), "d" (y));
> >>> return x;
> >>> }
>  
>  I like this idea but I'm wondering: regular constraints specify what 
>  sort of value is needed, for example an int vs. a short int vs. a float. 
>   The notation you've shown doesn't seem to have that aspect.
> >>> 
> >>> As Maciej already pointed out the type of the expression should suffice.
> >>> My assumption was that an asm can deal with a value as is or its
> >>> promoted value.  At least for integer values this should be fine and
> >>> AFAICS is also the case for simple constraints like "r" which do not
> >>> define any mode.  I've probably overseen something but which constraint
> >>> differentiates between int vs short?  However, you have a good point
> >>> with this and I should test this more.
> >> 
> >> I thought there was but I may be confused.  On the other hand, there 
> >> definitely are (machine dependent) constraints that distinguish, say, 
> >> float from integer registers; pdp11 is an example.  If you were to use an 
> >> "a" constraint, that means a floating point register and the compiler will 
> >> detect attempts to pass non-float operands ("Inconsistent operand 
> >> constraints...").
> >> 
> >> I see that the existing "register int ..." syntax appears to check that 
> >> the register is the right type for the data type given for it, so for 
> >> example on pdp11, 
> >> 
> >>register int ac1 asm ("ac1") = i;
> >> 
> >> fails ("register ... isn't suitable for data type").  I assume your new 
> >> syntax would perform the same check and produce roughly the same error 
> >> message.  You might verify that.  On pdp11, trying to use, for example, 
> >> "r0" for a float, or "ac0" for an int, would produce that error.
> > 
> > Right, so far I don't error out here which I will change.  It basically
> > results in bit casting floats to ints currently.
> 
> That would be bad.  For one thing, a PDP11 float doesn't fit in an integer 
> register.
> 
> That also brings up another point (which applies to more mainstream targets 
> as well): for data types that require multiple registers, say a register pair 
> for a double length value, how is that handled?  One possible answer is to 
> reject that.  Another would be to load a register pair.
> 
> This case applies to a "long int" on pdp11, or 32 bit MIPS, and probably a 
> bunch of others.

Absolutely, also on mainstream targets you could think of 128-bit integers
or long doubles which typically don't fit in (single) GPRs.  I should
definitely add error handling for this.  Similar, I don't error out for
non-primitive data types.

I will give register pairs a try.

Thanks for all your comments so far :)

Cheers,
Stefan


Re: [PATCH] Hard register asm constraint

2024-06-26 Thread Paul Koning



> On Jun 26, 2024, at 8:54 AM, Stefan Schulze Frielinghaus 
>  wrote:
> 
> On Tue, Jun 25, 2024 at 01:02:39PM -0400, Paul Koning wrote:
>> 
>> 
>>> On Jun 25, 2024, at 12:04 PM, Stefan Schulze Frielinghaus 
>>>  wrote:
>>> 
>>> On Tue, Jun 25, 2024 at 10:03:34AM -0400, Paul Koning wrote:
 
>>> ...
>>> could be rewritten into
>>> 
>>> int test (int x, int y)
>>> {
>>> asm ("foo %0,%1,%2" : "+{r4}" (x) : "{r5}" (y), "d" (y));
>>> return x;
>>> }
 
 I like this idea but I'm wondering: regular constraints specify what sort 
 of value is needed, for example an int vs. a short int vs. a float.  The 
 notation you've shown doesn't seem to have that aspect.
>>> 
>>> As Maciej already pointed out the type of the expression should suffice.
>>> My assumption was that an asm can deal with a value as is or its
>>> promoted value.  At least for integer values this should be fine and
>>> AFAICS is also the case for simple constraints like "r" which do not
>>> define any mode.  I've probably overseen something but which constraint
>>> differentiates between int vs short?  However, you have a good point
>>> with this and I should test this more.
>> 
>> I thought there was but I may be confused.  On the other hand, there 
>> definitely are (machine dependent) constraints that distinguish, say, float 
>> from integer registers; pdp11 is an example.  If you were to use an "a" 
>> constraint, that means a floating point register and the compiler will 
>> detect attempts to pass non-float operands ("Inconsistent operand 
>> constraints...").
>> 
>> I see that the existing "register int ..." syntax appears to check that the 
>> register is the right type for the data type given for it, so for example on 
>> pdp11, 
>> 
>>  register int ac1 asm ("ac1") = i;
>> 
>> fails ("register ... isn't suitable for data type").  I assume your new 
>> syntax would perform the same check and produce roughly the same error 
>> message.  You might verify that.  On pdp11, trying to use, for example, "r0" 
>> for a float, or "ac0" for an int, would produce that error.
> 
> Right, so far I don't error out here which I will change.  It basically
> results in bit casting floats to ints currently.

That would be bad.  For one thing, a PDP11 float doesn't fit in an integer 
register.

That also brings up another point (which applies to more mainstream targets as 
well): for data types that require multiple registers, say a register pair for 
a double length value, how is that handled?  One possible answer is to reject 
that.  Another would be to load a register pair.

This case applies to a "long int" on pdp11, or 32 bit MIPS, and probably a 
bunch of others.

paul



Re: [PATCH] Hard register asm constraint

2024-06-26 Thread Stefan Schulze Frielinghaus
On Tue, Jun 25, 2024 at 01:02:39PM -0400, Paul Koning wrote:
> 
> 
> > On Jun 25, 2024, at 12:04 PM, Stefan Schulze Frielinghaus 
> >  wrote:
> > 
> > On Tue, Jun 25, 2024 at 10:03:34AM -0400, Paul Koning wrote:
> >> 
> > ...
> > could be rewritten into
> > 
> > int test (int x, int y)
> > {
> > asm ("foo %0,%1,%2" : "+{r4}" (x) : "{r5}" (y), "d" (y));
> > return x;
> > }
> >> 
> >> I like this idea but I'm wondering: regular constraints specify what sort 
> >> of value is needed, for example an int vs. a short int vs. a float.  The 
> >> notation you've shown doesn't seem to have that aspect.
> > 
> > As Maciej already pointed out the type of the expression should suffice.
> > My assumption was that an asm can deal with a value as is or its
> > promoted value.  At least for integer values this should be fine and
> > AFAICS is also the case for simple constraints like "r" which do not
> > define any mode.  I've probably overseen something but which constraint
> > differentiates between int vs short?  However, you have a good point
> > with this and I should test this more.
> 
> I thought there was but I may be confused.  On the other hand, there 
> definitely are (machine dependent) constraints that distinguish, say, float 
> from integer registers; pdp11 is an example.  If you were to use an "a" 
> constraint, that means a floating point register and the compiler will detect 
> attempts to pass non-float operands ("Inconsistent operand constraints...").
> 
> I see that the existing "register int ..." syntax appears to check that the 
> register is the right type for the data type given for it, so for example on 
> pdp11, 
> 
>   register int ac1 asm ("ac1") = i;
> 
> fails ("register ... isn't suitable for data type").  I assume your new 
> syntax would perform the same check and produce roughly the same error 
> message.  You might verify that.  On pdp11, trying to use, for example, "r0" 
> for a float, or "ac0" for an int, would produce that error.

Right, so far I don't error out here which I will change.  It basically
results in bit casting floats to ints currently.

Just one thing to note: this is not a novel feature but pretty similar
to Rust's explicit register operands:
https://doc.rust-lang.org/rust-by-example/unsafe/asm.html#explicit-register-operands

Cheers,
Stefan


Re: [PATCH] Hard register asm constraint

2024-06-25 Thread Paul Koning



> On Jun 25, 2024, at 12:04 PM, Stefan Schulze Frielinghaus 
>  wrote:
> 
> On Tue, Jun 25, 2024 at 10:03:34AM -0400, Paul Koning wrote:
>> 
> ...
> could be rewritten into
> 
> int test (int x, int y)
> {
> asm ("foo %0,%1,%2" : "+{r4}" (x) : "{r5}" (y), "d" (y));
> return x;
> }
>> 
>> I like this idea but I'm wondering: regular constraints specify what sort of 
>> value is needed, for example an int vs. a short int vs. a float.  The 
>> notation you've shown doesn't seem to have that aspect.
> 
> As Maciej already pointed out the type of the expression should suffice.
> My assumption was that an asm can deal with a value as is or its
> promoted value.  At least for integer values this should be fine and
> AFAICS is also the case for simple constraints like "r" which do not
> define any mode.  I've probably overseen something but which constraint
> differentiates between int vs short?  However, you have a good point
> with this and I should test this more.

I thought there was but I may be confused.  On the other hand, there definitely 
are (machine dependent) constraints that distinguish, say, float from integer 
registers; pdp11 is an example.  If you were to use an "a" constraint, that 
means a floating point register and the compiler will detect attempts to pass 
non-float operands ("Inconsistent operand constraints...").

I see that the existing "register int ..." syntax appears to check that the 
register is the right type for the data type given for it, so for example on 
pdp11, 

register int ac1 asm ("ac1") = i;

fails ("register ... isn't suitable for data type").  I assume your new syntax 
would perform the same check and produce roughly the same error message.  You 
might verify that.  On pdp11, trying to use, for example, "r0" for a float, or 
"ac0" for an int, would produce that error.

With all that, I think your approach does work right and the question I raised 
isn't actually a problem.

>> The other comment is that I didn't see documentation updates to reflect this 
>> new feature.
> 
> I didn't came up with documentation yet since I was not sure whether
> such a proposal would be accepted at all, i.e., just wanted to hear
> whether you see some show stoppers or not.  Assuming this goes well I
> guess it should be documented under simple constraints
> https://gcc.gnu.org/onlinedocs/gcc/Simple-Constraints.html
> 
> Thanks,
> Stefan

That seems right.  I think it would be good for one of the global maintainers, 
or people of similar stature, to say whether Stefan's proposal is one that 
should be considered.  My 2c worth is that it should be.

The documentation might want to mention that the compiler will confirm that the 
register specified is suitable for the type given to it, just as it does for 
the old syntax.

paul



Re: [PATCH] Hard register asm constraint

2024-06-25 Thread Stefan Schulze Frielinghaus
On Tue, Jun 25, 2024 at 10:03:34AM -0400, Paul Koning wrote:
> 
> 
> > On Jun 24, 2024, at 1:50 AM, Stefan Schulze Frielinghaus 
> >  wrote:
> > 
> > Ping.
> > 
> > On Mon, Jun 10, 2024 at 07:19:19AM +0200, Stefan Schulze Frielinghaus wrote:
> >> Ping.
> >> 
> >> On Fri, May 24, 2024 at 11:13:12AM +0200, Stefan Schulze Frielinghaus 
> >> wrote:
> >>> This implements hard register constraints for inline asm.  A hard register
> >>> constraint is of the form {regname} where regname is any valid register.  
> >>> This
> >>> basically renders register asm superfluous.  For example, the snippet
> >>> 
> >>> int test (int x, int y)
> >>> {
> >>>  register int r4 asm ("r4") = x;
> >>>  register int r5 asm ("r5") = y;
> >>>  unsigned int copy = y;
> >>>  asm ("foo %0,%1,%2" : "+d" (r4) : "d" (r5), "d" (copy));
> >>>  return r4;
> >>> }
> >>> 
> >>> could be rewritten into
> >>> 
> >>> int test (int x, int y)
> >>> {
> >>>  asm ("foo %0,%1,%2" : "+{r4}" (x) : "{r5}" (y), "d" (y));
> >>>  return x;
> >>> }
> 
> I like this idea but I'm wondering: regular constraints specify what sort of 
> value is needed, for example an int vs. a short int vs. a float.  The 
> notation you've shown doesn't seem to have that aspect.

As Maciej already pointed out the type of the expression should suffice.
My assumption was that an asm can deal with a value as is or its
promoted value.  At least for integer values this should be fine and
AFAICS is also the case for simple constraints like "r" which do not
define any mode.  I've probably overseen something but which constraint
differentiates between int vs short?  However, you have a good point
with this and I should test this more.

> The other comment is that I didn't see documentation updates to reflect this 
> new feature.

I didn't came up with documentation yet since I was not sure whether
such a proposal would be accepted at all, i.e., just wanted to hear
whether you see some show stoppers or not.  Assuming this goes well I
guess it should be documented under simple constraints
https://gcc.gnu.org/onlinedocs/gcc/Simple-Constraints.html

Thanks,
Stefan


Re: [PATCH] Hard register asm constraint

2024-06-25 Thread Maciej W. Rozycki
On Tue, 25 Jun 2024, Paul Koning wrote:

> >>> could be rewritten into
> >>> 
> >>> int test (int x, int y)
> >>> {
> >>>  asm ("foo %0,%1,%2" : "+{r4}" (x) : "{r5}" (y), "d" (y));
> >>>  return x;
> >>> }
> 
> I like this idea but I'm wondering: regular constraints specify what 
> sort of value is needed, for example an int vs. a short int vs. a float.  

 Isn't that inferred from the data type of the associated expression used, 
the types of `x' and `y' in this case?  Then the constraints only tell the 
middle end where that data comes from or goes to at the boundaries of an 
asm.

  Maciej


Re: [PATCH] Hard register asm constraint

2024-06-25 Thread Paul Koning



> On Jun 24, 2024, at 1:50 AM, Stefan Schulze Frielinghaus 
>  wrote:
> 
> Ping.
> 
> On Mon, Jun 10, 2024 at 07:19:19AM +0200, Stefan Schulze Frielinghaus wrote:
>> Ping.
>> 
>> On Fri, May 24, 2024 at 11:13:12AM +0200, Stefan Schulze Frielinghaus wrote:
>>> This implements hard register constraints for inline asm.  A hard register
>>> constraint is of the form {regname} where regname is any valid register.  
>>> This
>>> basically renders register asm superfluous.  For example, the snippet
>>> 
>>> int test (int x, int y)
>>> {
>>>  register int r4 asm ("r4") = x;
>>>  register int r5 asm ("r5") = y;
>>>  unsigned int copy = y;
>>>  asm ("foo %0,%1,%2" : "+d" (r4) : "d" (r5), "d" (copy));
>>>  return r4;
>>> }
>>> 
>>> could be rewritten into
>>> 
>>> int test (int x, int y)
>>> {
>>>  asm ("foo %0,%1,%2" : "+{r4}" (x) : "{r5}" (y), "d" (y));
>>>  return x;
>>> }

I like this idea but I'm wondering: regular constraints specify what sort of 
value is needed, for example an int vs. a short int vs. a float.  The notation 
you've shown doesn't seem to have that aspect.

The other comment is that I didn't see documentation updates to reflect this 
new feature.

paul



Re: [PATCH] Hard register asm constraint

2024-06-24 Thread Stefan Schulze Frielinghaus
Ping.

On Mon, Jun 10, 2024 at 07:19:19AM +0200, Stefan Schulze Frielinghaus wrote:
> Ping.
> 
> On Fri, May 24, 2024 at 11:13:12AM +0200, Stefan Schulze Frielinghaus wrote:
> > This implements hard register constraints for inline asm.  A hard register
> > constraint is of the form {regname} where regname is any valid register.  
> > This
> > basically renders register asm superfluous.  For example, the snippet
> > 
> > int test (int x, int y)
> > {
> >   register int r4 asm ("r4") = x;
> >   register int r5 asm ("r5") = y;
> >   unsigned int copy = y;
> >   asm ("foo %0,%1,%2" : "+d" (r4) : "d" (r5), "d" (copy));
> >   return r4;
> > }
> > 
> > could be rewritten into
> > 
> > int test (int x, int y)
> > {
> >   asm ("foo %0,%1,%2" : "+{r4}" (x) : "{r5}" (y), "d" (y));
> >   return x;
> > }
> > 
> > As a side-effect this also solves the problem of call-clobbered registers.
> > That being said, I was wondering whether we could utilize this feature in 
> > order
> > to get rid of local register asm automatically?  For example, converting
> > 
> > // Result will be in r2 on s390
> > extern int bar (void);
> > 
> > void test (void)
> > {
> >   register int x asm ("r2") = 42;
> >   bar ();
> >   asm ("foo %0\n" :: "r" (x));
> > }
> > 
> > into
> > 
> > void test (void)
> > {
> >   int x = 42;
> >   bar ();
> >   asm ("foo %0\n" :: "{r2}" (x));
> > }
> > 
> > in order to get rid of the limitation of call-clobbered registers which may
> > lead to subtle bugs---especially if you think of non-obvious calls e.g.
> > introduced by sanitizer/tracer/whatever.  Since such a transformation has 
> > the
> > potential to break existing code do you see any edge cases where this might 
> > be
> > problematic or even show stoppers?  Currently, even
> > 
> > int test (void)
> > {
> >   register int x asm ("r2") = 42;
> >   register int y asm ("r2") = 24;
> >   asm ("foo %0,%1\n" :: "r" (x), "r" (y));
> > }
> > 
> > is allowed which seems error prone to me.  Thus, if 100% backwards
> > compatibility would be required, then automatically converting every 
> > register
> > asm to the new mechanism isn't viable.  Still quite a lot could be 
> > transformed.
> > Any thoughts?
> > 
> > Currently I allow multiple alternatives as demonstrated by
> > gcc/testsuite/gcc.target/s390/asm-hard-reg-2.c.  However, since a hard 
> > register
> > constraint is pretty specific I could also think of erroring out in case of
> > alternatives.  Are there any real use cases out there for multiple
> > alternatives where one would like to use hard register constraints?
> > 
> > With the current implementation we have a "user visible change" in the sense
> > that for
> > 
> > void test (void)
> > {
> >   register int x asm ("r2") = 42;
> >   register int y asm ("r2") = 24;
> >   asm ("foo %0,%1\n" : "=r" (x), "=r" (y));
> > }
> > 
> > we do not get the error
> > 
> >   "invalid hard register usage between output operands"
> > 
> > anymore but rather
> > 
> >   "multiple outputs to hard register: %r2"
> > 
> > This is due to the error handling in gimplify_asm_expr ().  Speaking of 
> > errors,
> > I also error out earlier as before which means that e.g. in pr87600-2.c only
> > the first error is reported and processing is stopped afterwards which means
> > the subsequent tests fail.
> > 
> > I've been skimming through all targets and it looks to me as if none is 
> > using
> > curly brackets for their constraints.  Of course, I may have missed 
> > something.
> > 
> > Cheers,
> > Stefan
> > 
> > PS: Current state for Clang: https://reviews.llvm.org/D105142
> > 
> > ---
> >  gcc/cfgexpand.cc  |  42 ---
> >  gcc/genpreds.cc   |   4 +-
> >  gcc/gimplify.cc   | 115 +-
> >  gcc/lra-constraints.cc|  17 +++
> >  gcc/recog.cc  |  14 ++-
> >  gcc/stmt.cc   | 102 +++-
> >  gcc/stmt.h|  10 +-
> >  .../gcc.target/s390/asm-hard-reg-1.c  | 103 
> >  .../gcc.target/s390/asm-hard-reg-2.c  |  29 +
> >  .../gcc.target/s390/asm-hard-reg-3.c  |  24 
> >  gcc/testsuite/lib/scanasm.exp |   4 +
> >  11 files changed, 407 insertions(+), 57 deletions(-)
> >  create mode 100644 gcc/testsuite/gcc.target/s390/asm-hard-reg-1.c
> >  create mode 100644 gcc/testsuite/gcc.target/s390/asm-hard-reg-2.c
> >  create mode 100644 gcc/testsuite/gcc.target/s390/asm-hard-reg-3.c
> > 
> > diff --git a/gcc/cfgexpand.cc b/gcc/cfgexpand.cc
> > index 557cb28733b..47f71a2e803 100644
> > --- a/gcc/cfgexpand.cc
> > +++ b/gcc/cfgexpand.cc
> > @@ -2955,44 +2955,6 @@ expand_asm_loc (tree string, int vol, location_t 
> > locus)
> >emit_insn (body);
> >  }
> >  
> > -/* Return the number of times character C occurs in string S.  */
> > -static int
> > -n_occurrences (int c, const char *s)
> > -{
> > -  

Re: [PATCH] Hard register asm constraint

2024-06-09 Thread Stefan Schulze Frielinghaus
Ping.

On Fri, May 24, 2024 at 11:13:12AM +0200, Stefan Schulze Frielinghaus wrote:
> This implements hard register constraints for inline asm.  A hard register
> constraint is of the form {regname} where regname is any valid register.  This
> basically renders register asm superfluous.  For example, the snippet
> 
> int test (int x, int y)
> {
>   register int r4 asm ("r4") = x;
>   register int r5 asm ("r5") = y;
>   unsigned int copy = y;
>   asm ("foo %0,%1,%2" : "+d" (r4) : "d" (r5), "d" (copy));
>   return r4;
> }
> 
> could be rewritten into
> 
> int test (int x, int y)
> {
>   asm ("foo %0,%1,%2" : "+{r4}" (x) : "{r5}" (y), "d" (y));
>   return x;
> }
> 
> As a side-effect this also solves the problem of call-clobbered registers.
> That being said, I was wondering whether we could utilize this feature in 
> order
> to get rid of local register asm automatically?  For example, converting
> 
> // Result will be in r2 on s390
> extern int bar (void);
> 
> void test (void)
> {
>   register int x asm ("r2") = 42;
>   bar ();
>   asm ("foo %0\n" :: "r" (x));
> }
> 
> into
> 
> void test (void)
> {
>   int x = 42;
>   bar ();
>   asm ("foo %0\n" :: "{r2}" (x));
> }
> 
> in order to get rid of the limitation of call-clobbered registers which may
> lead to subtle bugs---especially if you think of non-obvious calls e.g.
> introduced by sanitizer/tracer/whatever.  Since such a transformation has the
> potential to break existing code do you see any edge cases where this might be
> problematic or even show stoppers?  Currently, even
> 
> int test (void)
> {
>   register int x asm ("r2") = 42;
>   register int y asm ("r2") = 24;
>   asm ("foo %0,%1\n" :: "r" (x), "r" (y));
> }
> 
> is allowed which seems error prone to me.  Thus, if 100% backwards
> compatibility would be required, then automatically converting every register
> asm to the new mechanism isn't viable.  Still quite a lot could be 
> transformed.
> Any thoughts?
> 
> Currently I allow multiple alternatives as demonstrated by
> gcc/testsuite/gcc.target/s390/asm-hard-reg-2.c.  However, since a hard 
> register
> constraint is pretty specific I could also think of erroring out in case of
> alternatives.  Are there any real use cases out there for multiple
> alternatives where one would like to use hard register constraints?
> 
> With the current implementation we have a "user visible change" in the sense
> that for
> 
> void test (void)
> {
>   register int x asm ("r2") = 42;
>   register int y asm ("r2") = 24;
>   asm ("foo   %0,%1\n" : "=r" (x), "=r" (y));
> }
> 
> we do not get the error
> 
>   "invalid hard register usage between output operands"
> 
> anymore but rather
> 
>   "multiple outputs to hard register: %r2"
> 
> This is due to the error handling in gimplify_asm_expr ().  Speaking of 
> errors,
> I also error out earlier as before which means that e.g. in pr87600-2.c only
> the first error is reported and processing is stopped afterwards which means
> the subsequent tests fail.
> 
> I've been skimming through all targets and it looks to me as if none is using
> curly brackets for their constraints.  Of course, I may have missed something.
> 
> Cheers,
> Stefan
> 
> PS: Current state for Clang: https://reviews.llvm.org/D105142
> 
> ---
>  gcc/cfgexpand.cc  |  42 ---
>  gcc/genpreds.cc   |   4 +-
>  gcc/gimplify.cc   | 115 +-
>  gcc/lra-constraints.cc|  17 +++
>  gcc/recog.cc  |  14 ++-
>  gcc/stmt.cc   | 102 +++-
>  gcc/stmt.h|  10 +-
>  .../gcc.target/s390/asm-hard-reg-1.c  | 103 
>  .../gcc.target/s390/asm-hard-reg-2.c  |  29 +
>  .../gcc.target/s390/asm-hard-reg-3.c  |  24 
>  gcc/testsuite/lib/scanasm.exp |   4 +
>  11 files changed, 407 insertions(+), 57 deletions(-)
>  create mode 100644 gcc/testsuite/gcc.target/s390/asm-hard-reg-1.c
>  create mode 100644 gcc/testsuite/gcc.target/s390/asm-hard-reg-2.c
>  create mode 100644 gcc/testsuite/gcc.target/s390/asm-hard-reg-3.c
> 
> diff --git a/gcc/cfgexpand.cc b/gcc/cfgexpand.cc
> index 557cb28733b..47f71a2e803 100644
> --- a/gcc/cfgexpand.cc
> +++ b/gcc/cfgexpand.cc
> @@ -2955,44 +2955,6 @@ expand_asm_loc (tree string, int vol, location_t locus)
>emit_insn (body);
>  }
>  
> -/* Return the number of times character C occurs in string S.  */
> -static int
> -n_occurrences (int c, const char *s)
> -{
> -  int n = 0;
> -  while (*s)
> -n += (*s++ == c);
> -  return n;
> -}
> -
> -/* A subroutine of expand_asm_operands.  Check that all operands have
> -   the same number of alternatives.  Return true if so.  */
> -
> -static bool
> -check_operand_nalternatives (const vec )
> -{
> -  unsigned len = constraints.length();
> -  if (len > 0)
> -{
> -  int nalternatives = 

[PATCH] Hard register asm constraint

2024-05-24 Thread Stefan Schulze Frielinghaus
This implements hard register constraints for inline asm.  A hard register
constraint is of the form {regname} where regname is any valid register.  This
basically renders register asm superfluous.  For example, the snippet

int test (int x, int y)
{
  register int r4 asm ("r4") = x;
  register int r5 asm ("r5") = y;
  unsigned int copy = y;
  asm ("foo %0,%1,%2" : "+d" (r4) : "d" (r5), "d" (copy));
  return r4;
}

could be rewritten into

int test (int x, int y)
{
  asm ("foo %0,%1,%2" : "+{r4}" (x) : "{r5}" (y), "d" (y));
  return x;
}

As a side-effect this also solves the problem of call-clobbered registers.
That being said, I was wondering whether we could utilize this feature in order
to get rid of local register asm automatically?  For example, converting

// Result will be in r2 on s390
extern int bar (void);

void test (void)
{
  register int x asm ("r2") = 42;
  bar ();
  asm ("foo %0\n" :: "r" (x));
}

into

void test (void)
{
  int x = 42;
  bar ();
  asm ("foo %0\n" :: "{r2}" (x));
}

in order to get rid of the limitation of call-clobbered registers which may
lead to subtle bugs---especially if you think of non-obvious calls e.g.
introduced by sanitizer/tracer/whatever.  Since such a transformation has the
potential to break existing code do you see any edge cases where this might be
problematic or even show stoppers?  Currently, even

int test (void)
{
  register int x asm ("r2") = 42;
  register int y asm ("r2") = 24;
  asm ("foo %0,%1\n" :: "r" (x), "r" (y));
}

is allowed which seems error prone to me.  Thus, if 100% backwards
compatibility would be required, then automatically converting every register
asm to the new mechanism isn't viable.  Still quite a lot could be transformed.
Any thoughts?

Currently I allow multiple alternatives as demonstrated by
gcc/testsuite/gcc.target/s390/asm-hard-reg-2.c.  However, since a hard register
constraint is pretty specific I could also think of erroring out in case of
alternatives.  Are there any real use cases out there for multiple
alternatives where one would like to use hard register constraints?

With the current implementation we have a "user visible change" in the sense
that for

void test (void)
{
  register int x asm ("r2") = 42;
  register int y asm ("r2") = 24;
  asm ("foo %0,%1\n" : "=r" (x), "=r" (y));
}

we do not get the error

  "invalid hard register usage between output operands"

anymore but rather

  "multiple outputs to hard register: %r2"

This is due to the error handling in gimplify_asm_expr ().  Speaking of errors,
I also error out earlier as before which means that e.g. in pr87600-2.c only
the first error is reported and processing is stopped afterwards which means
the subsequent tests fail.

I've been skimming through all targets and it looks to me as if none is using
curly brackets for their constraints.  Of course, I may have missed something.

Cheers,
Stefan

PS: Current state for Clang: https://reviews.llvm.org/D105142

---
 gcc/cfgexpand.cc  |  42 ---
 gcc/genpreds.cc   |   4 +-
 gcc/gimplify.cc   | 115 +-
 gcc/lra-constraints.cc|  17 +++
 gcc/recog.cc  |  14 ++-
 gcc/stmt.cc   | 102 +++-
 gcc/stmt.h|  10 +-
 .../gcc.target/s390/asm-hard-reg-1.c  | 103 
 .../gcc.target/s390/asm-hard-reg-2.c  |  29 +
 .../gcc.target/s390/asm-hard-reg-3.c  |  24 
 gcc/testsuite/lib/scanasm.exp |   4 +
 11 files changed, 407 insertions(+), 57 deletions(-)
 create mode 100644 gcc/testsuite/gcc.target/s390/asm-hard-reg-1.c
 create mode 100644 gcc/testsuite/gcc.target/s390/asm-hard-reg-2.c
 create mode 100644 gcc/testsuite/gcc.target/s390/asm-hard-reg-3.c

diff --git a/gcc/cfgexpand.cc b/gcc/cfgexpand.cc
index 557cb28733b..47f71a2e803 100644
--- a/gcc/cfgexpand.cc
+++ b/gcc/cfgexpand.cc
@@ -2955,44 +2955,6 @@ expand_asm_loc (tree string, int vol, location_t locus)
   emit_insn (body);
 }
 
-/* Return the number of times character C occurs in string S.  */
-static int
-n_occurrences (int c, const char *s)
-{
-  int n = 0;
-  while (*s)
-n += (*s++ == c);
-  return n;
-}
-
-/* A subroutine of expand_asm_operands.  Check that all operands have
-   the same number of alternatives.  Return true if so.  */
-
-static bool
-check_operand_nalternatives (const vec )
-{
-  unsigned len = constraints.length();
-  if (len > 0)
-{
-  int nalternatives = n_occurrences (',', constraints[0]);
-
-  if (nalternatives + 1 > MAX_RECOG_ALTERNATIVES)
-   {
- error ("too many alternatives in %");
- return false;
-   }
-
-  for (unsigned i = 1; i < len; ++i)
-   if (n_occurrences (',', constraints[i]) != nalternatives)
- {
-   error ("operand constraints for % differ "
-  "in number