1

I'm trying out the examples of inline assembly in: http://www.delorie.com/djgpp/doc/brennan/brennan_att_inline_djgpp.html But something is confusing me about clobbering:

  • About behavior of clobber
    Clobbering essentially tells GCC to not trust the values in the specified register/memories.

    "Well, it really helps when optimizing, when GCC can know exactly what you're doing with the registers before and after....It's even smart enough to know that if you tell it to put (x+1) in a register, then if you don't clobber it, and later C code refers to (x+1), and it was able to keep that register free, it will reuse the computation. Whew."

Does this paragraph means clobbering will disable common sub-expression elimination?
  • There's some inconsistency in the tutorial about the clobber list:
    For registers specified in input/output list, there's no need to put them in clobber list as GCC knows; However in the example about rep_movsl (or rep_stosl):

    asm ("cld\n\t" "rep\n\t" "stosl" : /* no output registers */ : "c" (count), "a" (fill_value), "D" (dest) : "%ecx", "%edi" );

although "S, D, c" are in the output operands, they are listed as clobbered again. I tried a simple snippet in C:

#include<stdio.h>
int main()
{
  int a[] = {2, 4, 6};
  int b[3];
  int n = 3;
  int v = 12;
  asm ("cld\n\t"
       "rep\n\t"
       "movsl"
       :
       : "S" (a), "D" (b), "c" (n)
       : );
//     : "%ecx", "%esi", "%edi" );
  printf("%d\n", b[1]);
}

If I use the commented clobber list, GCC will complain:

a.c:8:3: error: can't find a register in class ‘CREG’ while reloading ‘asm’ a.c:8:3: error: ‘asm’ operand has impossible constraints

If I use empty clobber list, it will compile and the output is 4.

Mysticial
  • 464,885
  • 45
  • 335
  • 332
Oxdeadbeef
  • 1,033
  • 2
  • 11
  • 26

1 Answers1

7

The document you are quoting appears to be significantly inaccurate. Here's what asm operand constraints actually mean to GCC:

  • Input: The assembly operation reads from this operand. GCC assumes that all reads happen simultaneously at the very beginning of the assembly operation.
  • Output: The assembly operation writes to this operand; after it completes, the associated variable will have a meaningful value. (There is no way to tell GCC what that value is.) GCC assumes that all writes happen simultaneously at the very end of the assembly operation.
  • Clobber: The assembly operation destroys any meaningful value in this operand. Like writes, all clobbers are assumed to happen simultaneously at the end of the operation.
  • Earlyclobber: Same as clobber except that it happens at the beginning of the operation.

Furthermore, the current (GCC 4.7) manual includes this critical paragraph:

You may not write a clobber description in a way that overlaps with an input or output operand. For example, you may not have an operand describing a register class with one member if you mention that register in the clobber list. Variables declared to live in specific registers (see Explicit Reg Vars), and used as asm input or output operands must have no part mentioned in the clobber description. There is no way for you to specify that an input operand is modified without also specifying it as an output operand. Note that if all the output operands you specify are for this purpose (and hence unused), you will then also need to specify volatile for the asm construct, as described below, to prevent GCC from deleting the asm statement as unused.

This is why attempting to both input and clobber certain registers is failing for you.

Now, inserting rep movsl is kind of silly nowadays -- just use memcpy and let GCC replace that with an optimal instruction sequence for you -- but nonetheless the correct way to write your example is

int main()
{
  int a[] = {2, 4, 6};
  int b[3];
  int n = 3;
  int v = 12;

  int *ap = a, *bp = b;

  asm volatile ("rep movsl" : "+S" (ap), "+D" (bp), "+c" (n) : : "memory");
  printf("%d\n", b[1]);
}

You need the ap and bp intermediate variables because the address of an array is not an lvalue, so it can't appear in the output constraints. The "+r" notation tells GCC that this register is both an input and an output. The 'volatile' is necessary because all of the output operands are unused after the asm, so GCC would otherwise cheerfully delete it (on the theory that it was only there for what it did to the output operands). Putting "memory" in the clobber list is how you tell GCC that the operation modified memory. And finally, a micro-optimization: GCC never ever issues 'std', so you need not 'cld' (this is actually guaranteed by the x86 ABI).

Most of the changes I made would not affect whether a tiny test program like this behaves correctly; however, they are all essential in a full-size program to prevent subtle optimization errors. For instance, if you left out the "memory" clobber, GCC would be within its rights to hoist the load of b[1] above the asm!

zwol
  • 135,547
  • 38
  • 252
  • 361
  • Got it. thanks! The reason for putting "S", "D", "c" in both input and output operands is to examine whether the copy succeeded or not? – Oxdeadbeef Nov 06 '12 at 21:43
  • 1
    No. The reason for describing "S", "D", "c" as both input and output operands is *that's the only way to tell GCC that input registers are destroyed by the asm*. – zwol Nov 06 '12 at 23:48
  • Still a bit confusion here :) The initial loading to input registers will destroy previous values, right? In that sense, input operands, as with output operands, are by default in clobber (or at least earlyclobber) description? – Oxdeadbeef Nov 07 '12 at 01:04
  • Any instructions that may have had to be generated to arrange for the inputs to be where the asm needs them are not part of the asm. They have their own input, output, and clobber sets, which you can see if you turn on RTL dumps. But if a value is in a register marked only as input, GCC will assume that *after the asm* that value is *still* in that register. Therefore that register is *not* clobbered by the asm. – zwol Nov 07 '12 at 01:30
  • Related: [How can I indicate that the memory \*pointed\* to by an inline ASM argument may be used?](https://stackoverflow.com/q/56432259) re: dummy input/output operands to tell GCC which memory is read/written, avoiding a full `"memory"` clobber. – Peter Cordes Nov 17 '22 at 12:22