Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

GCC Inline Assembly side effects

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.

like image 203
listerreg Avatar asked Sep 27 '26 00:09

listerreg


1 Answers

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.

like image 61
Florian Weimer Avatar answered Sep 29 '26 17:09

Florian Weimer