public inbox for gcc-help@gcc.gnu.org
 help / color / mirror / Atom feed
* Odd error with the "X" inline assembly constraint
@ 2024-01-05 16:30 David Brown
  2024-01-05 17:53 ` LIU Hao
                   ` (2 more replies)
  0 siblings, 3 replies; 11+ messages in thread
From: David Brown @ 2024-01-05 16:30 UTC (permalink / raw)
  To: gcc-help

I was testing a little by looking at some code generation for 
optimisations and re-arrangements when -ffast-math is enabled, using 
code like this in <https://godbolt.org> :

typedef float T;
T test(T a, T b) {
     T x = a + b;
     //asm ("" :: "X" (x));
     asm ("" : "+X" (x));
     //asm ("" : "+X" (x) : "X" (x));
     return x - b;
}

Without the asm statements, gcc - as expected - skips the calculation of 
"x" and can then simplify "a + b - b" to "a".  I have previously used 
inline assembly of the form:

	asm ("" : "+g" (x));

to tell gcc "You need to calculate x before running this assembly and 
put it in a general register or memory, but it might change during the 
assembly so you must forget anything you knew about it before".  I've 
found it useful to force particular orders on calculations, or for 
debugging, or as a kind of fine-tuned alternative to a memory barrier.

But the "+g" operand is not ideal for floating point variables - it 
forces the compiler to move the variable from a floating point register 
into a general-purpose register, then back again.  The ideal choice 
seems to be "+X", since "X" matches any operand whatsoever.

However, when I use just "asm ("" : "+X" (x));", I get an error message 
"error: inconsistent operand constraints in an 'asm'".  I have no idea 
why this is an issue.

Getting weirder, on x86-64, there is no error if I use

	asm ("" : "+X" (x) : "X" (x));

This gives me the desired effect of forcing "x" to be calculated and 
used in the final "x - b".

Even weirder, on 32-bit ARM, this still gives the inconsistent operand 
error.

Weirder still, this works error-free on both targets :

     asm ("" :: "X" (x));
     asm ("" : "+X" (x));

In my (non-exhaustive) testing, this gives optimal results on both 
targets, independent of the compiler version and type T.

I'd imagine that the "X" operand doesn't see much use in real inline 
assembly - on x86 and ARM the assembly instruction template would 
usually depend on where the data is put.  But if anyone can explain this 
behaviour to me, I am very curious to know what is going on.

David


^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: Odd error with the "X" inline assembly constraint
  2024-01-05 16:30 Odd error with the "X" inline assembly constraint David Brown
@ 2024-01-05 17:53 ` LIU Hao
  2024-01-05 18:46 ` Segher Boessenkool
  2024-01-07 23:18 ` Marc Glisse
  2 siblings, 0 replies; 11+ messages in thread
From: LIU Hao @ 2024-01-05 17:53 UTC (permalink / raw)
  To: David Brown, gcc-help


[-- Attachment #1.1: Type: text/plain, Size: 1469 bytes --]

在 2024/1/6 0:30, David Brown via Gcc-help 写道:
> I'd imagine that the "X" operand doesn't see much use in real inline assembly - on x86 and ARM the 
> assembly instruction template would usually depend on where the data is put.  But if anyone can 
> explain this behaviour to me, I am very curious to know what is going on.

I haven't used it at all and know nothing about its original intention. From GCC documentation, it 
looks like `X` specifies that the argument is allowed to be passed via either a register or memory, 
so it should be eligible for overloaded mnemonics, such as the MOV, FLD and ADDPD instructions on x86.

For example, given
(https://gcc.godbolt.org/z/vxb1aTe9c)
```
static int value;

int
test_X(void)
   {
     int a;
     __asm__("mov %0, %1" : "=&r"(a), "+X"(value));
     return a;
   }
```

GCC passes `value` directly via memory
```
test_X:
         mov eax, DWORD PTR value[rip]
         ret
```

and Clang passes it via the ECX register
```
test_X:
         mov     ecx, dword ptr [rip + value]
         mov     eax, ecx
         mov     dword ptr [rip + value], ecx
         ret
```

If `static int value;` is changed to `extern int value;` then GCC will reject it, as the operand no 
longer fits into a single operand; but the clang approach still works, with `value` replaced with 
`value@GOTPCREL`.

It doesn't seem very useful for RISC machines.


-- 
Best regards,
LIU Hao


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 840 bytes --]

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: Odd error with the "X" inline assembly constraint
  2024-01-05 16:30 Odd error with the "X" inline assembly constraint David Brown
  2024-01-05 17:53 ` LIU Hao
@ 2024-01-05 18:46 ` Segher Boessenkool
  2024-01-07 10:04   ` LIU Hao
  2024-01-07 15:10   ` David Brown
  2024-01-07 23:18 ` Marc Glisse
  2 siblings, 2 replies; 11+ messages in thread
From: Segher Boessenkool @ 2024-01-05 18:46 UTC (permalink / raw)
  To: David Brown; +Cc: gcc-help

On Fri, Jan 05, 2024 at 05:30:51PM +0100, David Brown via Gcc-help wrote:
> However, when I use just "asm ("" : "+X" (x));", I get an error message 
> "error: inconsistent operand constraints in an 'asm'".  I have no idea 
> why this is an issue.

The C constraint means "Any operand whatsoever is allowed."  Here you
are saying to use it both as input and as output, and GCC does not know
how to reload wherever it chose to put it.

Using "X" for outputs is strange already, fwiw, but tieing it to an
input is, hrm, let's call it "creative" :-)

> I'd imagine that the "X" operand doesn't see much use in real inline 
> assembly

It is used rather often.  But usually for inputs, and (as you found out)
trying to use if for an input and for an output at the same time does
not work reliably.

"+" really creates two operands, and ties them together.  Writing
  asm("oink" : "+X"(bla));
is shorthand for
  asm("oink" : "=X"(bla) : "0"(bla));


Segher

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: Odd error with the "X" inline assembly constraint
  2024-01-05 18:46 ` Segher Boessenkool
@ 2024-01-07 10:04   ` LIU Hao
  2024-01-07 15:26     ` David Brown
  2024-01-07 18:32     ` Segher Boessenkool
  2024-01-07 15:10   ` David Brown
  1 sibling, 2 replies; 11+ messages in thread
From: LIU Hao @ 2024-01-07 10:04 UTC (permalink / raw)
  To: Segher Boessenkool, David Brown; +Cc: gcc-help


[-- Attachment #1.1: Type: text/plain, Size: 434 bytes --]

在 2024/1/6 2:46, Segher Boessenkool 写道:
> The C constraint means "Any operand whatsoever is allowed."  Here you
> are saying to use it both as input and as output, and GCC does not know
> how to reload wherever it chose to put it.

Would you mind elaborating a bit more? On x86 and x86-64 it's almost always the case that an 
expression can be used as either a source or a destination.


-- 
Best regards,
LIU Hao


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 840 bytes --]

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: Odd error with the "X" inline assembly constraint
  2024-01-05 18:46 ` Segher Boessenkool
  2024-01-07 10:04   ` LIU Hao
@ 2024-01-07 15:10   ` David Brown
  2024-01-07 18:24     ` Segher Boessenkool
  1 sibling, 1 reply; 11+ messages in thread
From: David Brown @ 2024-01-07 15:10 UTC (permalink / raw)
  To: gcc-help

On 05/01/2024 19:46, Segher Boessenkool wrote:
> On Fri, Jan 05, 2024 at 05:30:51PM +0100, David Brown via Gcc-help wrote:
>> However, when I use just "asm ("" : "+X" (x));", I get an error message
>> "error: inconsistent operand constraints in an 'asm'".  I have no idea
>> why this is an issue.
> 
> The C constraint means "Any operand whatsoever is allowed."  Here you
> are saying to use it both as input and as output, and GCC does not know
> how to reload wherever it chose to put it.
> 

It doesn't need to reload it - the "+" says it will be read, then 
possibly changed and returned in the same place.  It is geared towards 
inline assembly that modifies something - you'd use it for the "x" in 
assembly doing "x += a;", unlike for "x = a + b;".  No reloads are 
needed because the same register or memory address is used for input and 
output.

> Using "X" for outputs is strange already, fwiw, but tieing it to an
> input is, hrm, let's call it "creative" :-)

Sure, this is a little "creative".  After all, the assembly template 
itself is intentionally blank.  It is similar in principal to the 
commonly used asm("" ::: "memory") barrier.  It is an example of fully 
portable assembly!

> 
>> I'd imagine that the "X" operand doesn't see much use in real inline
>> assembly
> 
> It is used rather often.  But usually for inputs, and (as you found out)
> trying to use if for an input and for an output at the same time does
> not work reliably.
> 
> "+" really creates two operands, and ties them together.  Writing
>    asm("oink" : "+X"(bla));
> is shorthand for
>    asm("oink" : "=X"(bla) : "0"(bla));
> 

I don't know if these are /exactly/ the same, but they are certainly 
similar.

I can't see any reason for there to be a special issue with "X" here.  I 
have not seen any complications with "g", meaning "general-purpose 
register or memory".  But sometimes data can be in a register that is 
not general-purpose - such as a floating point register, or a SIMD 
register - and then I'd rather not have extra instructions added to 
force it to fit a "g" operand.




^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: Odd error with the "X" inline assembly constraint
  2024-01-07 10:04   ` LIU Hao
@ 2024-01-07 15:26     ` David Brown
  2024-01-07 18:32     ` Segher Boessenkool
  1 sibling, 0 replies; 11+ messages in thread
From: David Brown @ 2024-01-07 15:26 UTC (permalink / raw)
  To: gcc-help

On 07/01/2024 11:04, LIU Hao via Gcc-help wrote:
> 在 2024/1/6 2:46, Segher Boessenkool 写道:
>> The C constraint means "Any operand whatsoever is allowed."  Here you
>> are saying to use it both as input and as output, and GCC does not know
>> how to reload wherever it chose to put it.
> 
> Would you mind elaborating a bit more? On x86 and x86-64 it's almost 
> always the case that an expression can be used as either a source or a 
> destination.
> 

Note that for my usage, there is no assembly instruction and therefore 
it doesn't matter what sources or destinations are possible.  And it is 
not x86 specific - I have used the same arrangement (with "g" rather 
than "X") on several different targets.



^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: Odd error with the "X" inline assembly constraint
  2024-01-07 15:10   ` David Brown
@ 2024-01-07 18:24     ` Segher Boessenkool
  2024-01-08 18:48       ` David Brown
  0 siblings, 1 reply; 11+ messages in thread
From: Segher Boessenkool @ 2024-01-07 18:24 UTC (permalink / raw)
  To: David Brown; +Cc: gcc-help

On Sun, Jan 07, 2024 at 04:10:21PM +0100, David Brown wrote:
> On 05/01/2024 19:46, Segher Boessenkool wrote:
> >>However, when I use just "asm ("" : "+X" (x));", I get an error message
> >>"error: inconsistent operand constraints in an 'asm'".  I have no idea
> >>why this is an issue.
> >
> >The C constraint means "Any operand whatsoever is allowed."  Here you
> >are saying to use it both as input and as output, and GCC does not know
> >how to reload wherever it chose to put it.
> 
> It doesn't need to reload it

The error message is emitted during reloading.

> >"+" really creates two operands, and ties them together.  Writing
> >   asm("oink" : "+X"(bla));
> >is shorthand for
> >   asm("oink" : "=X"(bla) : "0"(bla));
> >
> 
> I don't know if these are /exactly/ the same, but they are certainly 
> similar.

They are the same thing.  The "+" syntactic sugar is modified during
gimplification already, almost nothing in the compiler has to deal with
it.

See "gimplify_asm_expr", see the code after
          /* An input/output operand.  To give the optimizers more
             flexibility, split it into separate input and output
             operands.  */

(Constraints that allow registers get a "0" (or "1" etc.) matching
constraint for the input duplicate, constraints that do not (like "m")
are unmodified.  The output duplicate just gets the "+" changed to "=").


Segher

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: Odd error with the "X" inline assembly constraint
  2024-01-07 10:04   ` LIU Hao
  2024-01-07 15:26     ` David Brown
@ 2024-01-07 18:32     ` Segher Boessenkool
  2024-01-08 18:39       ` David Brown
  1 sibling, 1 reply; 11+ messages in thread
From: Segher Boessenkool @ 2024-01-07 18:32 UTC (permalink / raw)
  To: LIU Hao; +Cc: David Brown, gcc-help

On Sun, Jan 07, 2024 at 06:04:23PM +0800, LIU Hao wrote:
> 在 2024/1/6 2:46, Segher Boessenkool 写道:
> >The C constraint means "Any operand whatsoever is allowed."  Here you
> >are saying to use it both as input and as output, and GCC does not know
> >how to reload wherever it chose to put it.
> 
> Would you mind elaborating a bit more? On x86 and x86-64 it's almost always 
> the case that an expression can be used as either a source or a destination.

This is just GCC internals.  The compiler will never look at the
template string in an asm at all, anyway.

I don't know why exactly you get the error.  Make a compilable
reproducer, file a bug (<https://gcc.gnu.org/bugs.html>), it sounds like
something we can improve :-)


Segher

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: Odd error with the "X" inline assembly constraint
  2024-01-05 16:30 Odd error with the "X" inline assembly constraint David Brown
  2024-01-05 17:53 ` LIU Hao
  2024-01-05 18:46 ` Segher Boessenkool
@ 2024-01-07 23:18 ` Marc Glisse
  2 siblings, 0 replies; 11+ messages in thread
From: Marc Glisse @ 2024-01-07 23:18 UTC (permalink / raw)
  To: David Brown; +Cc: gcc-help

I remember experimenting with "+X" a while ago, see for instance 
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=59155 and 
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=59159.


On Fri, 5 Jan 2024, David Brown via Gcc-help wrote:

> I was testing a little by looking at some code generation for optimisations 
> and re-arrangements when -ffast-math is enabled, using code like this in 
> <https://godbolt.org> :
>
> typedef float T;
> T test(T a, T b) {
>    T x = a + b;
>    //asm ("" :: "X" (x));
>    asm ("" : "+X" (x));
>    //asm ("" : "+X" (x) : "X" (x));
>    return x - b;
> }
>
> Without the asm statements, gcc - as expected - skips the calculation of "x" 
> and can then simplify "a + b - b" to "a".  I have previously used inline 
> assembly of the form:
>
> 	asm ("" : "+g" (x));
>
> to tell gcc "You need to calculate x before running this assembly and put it 
> in a general register or memory, but it might change during the assembly so 
> you must forget anything you knew about it before".  I've found it useful to 
> force particular orders on calculations, or for debugging, or as a kind of 
> fine-tuned alternative to a memory barrier.
>
> But the "+g" operand is not ideal for floating point variables - it forces 
> the compiler to move the variable from a floating point register into a 
> general-purpose register, then back again.  The ideal choice seems to be 
> "+X", since "X" matches any operand whatsoever.
>
> However, when I use just "asm ("" : "+X" (x));", I get an error message 
> "error: inconsistent operand constraints in an 'asm'".  I have no idea why 
> this is an issue.
>
> Getting weirder, on x86-64, there is no error if I use
>
> 	asm ("" : "+X" (x) : "X" (x));
>
> This gives me the desired effect of forcing "x" to be calculated and used in 
> the final "x - b".
>
> Even weirder, on 32-bit ARM, this still gives the inconsistent operand error.
>
> Weirder still, this works error-free on both targets :
>
>    asm ("" :: "X" (x));
>    asm ("" : "+X" (x));
>
> In my (non-exhaustive) testing, this gives optimal results on both targets, 
> independent of the compiler version and type T.
>
> I'd imagine that the "X" operand doesn't see much use in real inline assembly 
> - on x86 and ARM the assembly instruction template would usually depend on 
> where the data is put.  But if anyone can explain this behaviour to me, I am 
> very curious to know what is going on.
>
> David
>
>

-- 
Marc Glisse

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: Odd error with the "X" inline assembly constraint
  2024-01-07 18:32     ` Segher Boessenkool
@ 2024-01-08 18:39       ` David Brown
  0 siblings, 0 replies; 11+ messages in thread
From: David Brown @ 2024-01-08 18:39 UTC (permalink / raw)
  To: gcc-help

On 07/01/2024 19:32, Segher Boessenkool wrote:
> On Sun, Jan 07, 2024 at 06:04:23PM +0800, LIU Hao wrote:
>> 在 2024/1/6 2:46, Segher Boessenkool 写道:
>>> The C constraint means "Any operand whatsoever is allowed."  Here you
>>> are saying to use it both as input and as output, and GCC does not know
>>> how to reload wherever it chose to put it.
>>
>> Would you mind elaborating a bit more? On x86 and x86-64 it's almost always
>> the case that an expression can be used as either a source or a destination.
> 
> This is just GCC internals.  The compiler will never look at the
> template string in an asm at all, anyway.
> 
> I don't know why exactly you get the error.  Make a compilable
> reproducer, file a bug (<https://gcc.gnu.org/bugs.html>), it sounds like
> something we can improve :-)
> 

I've filed it :

<https://gcc.gnu.org/bugzilla/show_bug.cgi?id=113280>

I don't know if this is something that the developer will want to spend 
much time on, but it might appeal to someone's curiosity!

David




^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: Odd error with the "X" inline assembly constraint
  2024-01-07 18:24     ` Segher Boessenkool
@ 2024-01-08 18:48       ` David Brown
  0 siblings, 0 replies; 11+ messages in thread
From: David Brown @ 2024-01-08 18:48 UTC (permalink / raw)
  To: gcc-help

On 07/01/2024 19:24, Segher Boessenkool wrote:
> On Sun, Jan 07, 2024 at 04:10:21PM +0100, David Brown wrote:
>> On 05/01/2024 19:46, Segher Boessenkool wrote:
>>>> However, when I use just "asm ("" : "+X" (x));", I get an error message
>>>> "error: inconsistent operand constraints in an 'asm'".  I have no idea
>>>> why this is an issue.
>>>
>>> The C constraint means "Any operand whatsoever is allowed."  Here you
>>> are saying to use it both as input and as output, and GCC does not know
>>> how to reload wherever it chose to put it.
>>
>> It doesn't need to reload it
> 
> The error message is emitted during reloading.
> 
>>> "+" really creates two operands, and ties them together.  Writing
>>>    asm("oink" : "+X"(bla));
>>> is shorthand for
>>>    asm("oink" : "=X"(bla) : "0"(bla));
>>>
>>
>> I don't know if these are /exactly/ the same, but they are certainly
>> similar.
> 
> They are the same thing.  The "+" syntactic sugar is modified during
> gimplification already, almost nothing in the compiler has to deal with
> it.
> 
> See "gimplify_asm_expr", see the code after
>            /* An input/output operand.  To give the optimizers more
>               flexibility, split it into separate input and output
>               operands.  */
> 
> (Constraints that allow registers get a "0" (or "1" etc.) matching
> constraint for the input duplicate, constraints that do not (like "m")
> are unmodified.  The output duplicate just gets the "+" changed to "=").
> 

Thanks for that explanation - I've learned a bit more now.

So what then does this mean?

	asm("" : "+X" (x) : "0" (x));


On x86-64, I get the error with this :

typedef double T;
T test(T a, T b) {
     T x = a + b;
     asm ("" : "+X" (x));
     return x - b;
}


But not with this:

typedef double T;
T test(T a, T b) {
     T x = a + b;
     asm ("" : "+X" (x) : "0" (x));
     return x - b;
}

(But that still gives an error on ARM-32!)

I'm sure there's a logical pattern here somewhere, but I can't see it yet.

David




^ permalink raw reply	[flat|nested] 11+ messages in thread

end of thread, other threads:[~2024-01-08 18:48 UTC | newest]

Thread overview: 11+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2024-01-05 16:30 Odd error with the "X" inline assembly constraint David Brown
2024-01-05 17:53 ` LIU Hao
2024-01-05 18:46 ` Segher Boessenkool
2024-01-07 10:04   ` LIU Hao
2024-01-07 15:26     ` David Brown
2024-01-07 18:32     ` Segher Boessenkool
2024-01-08 18:39       ` David Brown
2024-01-07 15:10   ` David Brown
2024-01-07 18:24     ` Segher Boessenkool
2024-01-08 18:48       ` David Brown
2024-01-07 23:18 ` Marc Glisse

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).