Inline asm unknown

static inline void *__memset(void *s, char c, size_t n) {
int d0, d1;
asm volatile (
    "rep; stosb;"
    : "=&c" (d0), "=&D" (d1)
    : "0" (n), "a" (c), "1" (s)
    : "memory");
return s;
}

      

What are "d0" and "d1" used for? Could you please fully explain the whole code? Thank!

+3


source to share


2 answers


What are "d0" and "d1" for?

Basically, it says that the final values %ecx

, %edi

(assuming 32-bit) are stored in d0

, d1

respectively. This serves the purposes:

This lets the compiler know that, as outputs, these registers are effectively knocked down. By assigning them to temporary variables, the optimization compiler also knows that there is no need to perform a "store" operation.

"= &" defines them as early shard operands. They can be written before all inputs are consumed. Therefore, if the compiler is free to choose the input register, it should not be an alias for the two.

It is not technically necessary for %ecx

, since it is explicitly named input: "0" (n)

- counter 'rep' in this case. I'm not sure what this is needed for %edi

, as it cannot be updated before the input is used "1" (s)

and the statement is executed. Again, since it is explicitly named input, the compiler is not allowed to choose a different register. In short, "= &" doesn't hurt here, but it doesn't do anything.



Because it "a" (c)

specifies an input-only register %eax

set to (c)

, the compiler can assume that %eax

it still stores that value after "asm", which is indeed the case with "rep; stosb;"

.

"memory"

indicates that the memory can be modified in a way unknown to the compiler, which in this case is true, it sets the (n)

bytes value starting with (r)

to (c)

- assuming the direction flag to clear, which it should. This results in a forced reload of the values, as the compiler cannot assume that the registers reflect the memory values ​​they should be using more. It doesn't hurt, and it may be necessary to make it safe for the general case memset

, but it often overflows.

Edit: Input operands may not overlap clobber operands. It doesn't make sense to specify something just as input and . I don't think the compiler allows this, and it would be unwise to use an ambiguous BOM even if it did. From the manual:

You cannot write a clobber description in such a way that it overlaps with an input or output operand. For example, you may not have an operand describing a single-member register class if you specify that register in the clobber list.

0


source


You need to understand gcc's extended built-in asm format:

  • The first part is the actual assembly. In this case, there are only 2 instructions
  • The second part indicates the output limits and the third part indicates the input limits. The fourth part indicates that the assembly will compress memory.

Output

  • "=&c"

    binds d0 to ecx register and marks it for write only. &

    means it can be changed before the end of the code.
  • "=&D"

    means the same thing, for edi register

Input

  • "0" (n)

    binds n to the first mentioned register. In your case with ecx
  • "a" (c)

    binds c to eax
  • "1" (s)

    associates with edi


Assembly

So there you have it. Repeat this ecx time (n times): store eax (c) in edi (s) then increment it.


So why unused d0

and d1

? I'm not sure. I also think they are useless in this case, and the entire output section may be left blank. But I don't think you can specify "writable" and " early -clobbered" in the input constraints . So I think d0

and d1

can make &

it possible.

I would try to write it like this:

asm volatile (
    "rep\n"
    "stosb\n"
    :
    : "c" (n), "a" (c), "D" (s)
    : "%ecx", "%edi", "memory"
);

      

+4


source







All Articles