Could someone explain me (in other words) the following section from GCC doc:
Here is a fictitious sum of squares instruction, that takes two pointers to floating point values in memory and produces a floating point register output. Notice that x, and y both appear twice in the asm parameters, once to specify memory accessed, and once to specify a base register used by the asm. You won’t normally be wasting a register by doing this as GCC can use the same register for both purposes. However, it would be foolish to use both %1 and %3 for x in this asm and expect them to be the same. In fact, %3 may well not be a register. It might be a symbolic memory reference to the object pointed to by x.
asm ("sumsq %0, %1, %2"
: "+f" (result)
: "r" (x), "r" (y), "m" (*x), "m" (*y));
Here is a fictitious *z++ = *x++ * *y++ instruction. Notice that the x, y and z pointer registers must be specified as input/output because the asm modifies them.
asm ("vecmul %0, %1, %2"
: "+r" (z), "+r" (x), "+r" (y), "=m" (*z)
: "m" (*x), "m" (*y));
In the first example what is the point for listing *x and *y in the input operands? The same doc states:
In particular, there is no way to specify that input operands get modified without also specifying them as output operands.
In the second example why use the input operands section at all? None of its operands is used in the assembly statement anyway.
And as a bonus, how could one change the following example from this SO post so there was no need for the volatile keyword?
void swap_2 (int *a, int *b)
{
int tmp0, tmp1;
__asm__ volatile (
"movl (%0), %k2\n\t" /* %2 (tmp0) = (*a) */
"movl (%1), %k3\n\t" /* %3 (tmp1) = (*b) */
"cmpl %k3, %k2\n\t"
"jle %=f\n\t" /* if (%2 <= %3) (at&t!) */
"movl %k3, (%0)\n\t"
"movl %k2, (%1)\n\t"
"%=:\n\t"
: "+r" (a), "+r" (b), "=r" (tmp0), "=r" (tmp1) :
: "memory" /* "cc" */ );
}
Thanks in advance. I'm struggling with this for two days now.
In the first example, *x and *y have to be listed as input operands so that GCC knows that the outcome of the instruction depends on them. Otherwise, GCC could move stores to *x and *y past the inline assembly fragment, which would then access uninitialized memory. This can be seen by compiling this example:
double
f (void)
{
double result;
double a = 5;
double b = 7;
double *x = &a;
double *y = &b;
asm ("sumsq %0, %1, %2"
: "+X" (result)
: "r" (x), "r" (y) /*, "m" (*x), "m" (*y)*/);
return result;
}
Which results in:
f:
leaq -16(%rsp), %rax
leaq -8(%rsp), %rdx
pxor %xmm0, %xmm0
#APP
# 8 "t.c" 1
sumsq %xmm0, %rax, %rdx
# 0 "" 2
#NO_APP
ret
The two leaq instructions just set up the registers to point into the uninitialized red zone on the stack. The assignments are gone.
The same is true for the second example as well.
I think you can use the same trick to eliminate the volatile. But I think it is not actually necessary here because there already is a "memory" clobber, which tells GCC that memory is read or written from inline assembly.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With