Re: [PATCH] Hard register asm constraint
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
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
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
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
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
> 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
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
> 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
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
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
> 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
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
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
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