extended asm in gcc: ‘asm’ operand has impossible

2020-03-24 07:11发布

问题:

This function "strcpy" aims to copy the content of src to dest, and it works out just fine: display two lines of "Hello_src".

#include <stdio.h>

static inline char * strcpy(char * dest,const char *src)
{
    int d0, d1, d2;
    __asm__ __volatile__("1:\tlodsb\n\t"
                         "stosb\n\t"
                         "testb %%al,%%al\n\t"
                         "jne 1b"
                         : "=&S" (d0), "=&D" (d1), "=&a" (d2)
                         : "0"(src),"1"(dest)
                         : "memory");
    return dest;
}

int main(void) {
    char src_main[] = "Hello_src";
    char dest_main[] = "Hello_des";
    strcpy(dest_main, src_main);
    puts(src_main);
    puts(dest_main);
    return 0;
}
  1. I tried to change the line : "0"(src),"1"(dest) to : "S"(src),"D"(dest), the error occurred: ‘asm’ operand has impossible constraints. I just cannot understand. I thought that "0"/"1" here specified the same constraint as the 0th/1th output variable. the constraint of 0th output is =&S, te constraint of 1th output is =&D. If I change 0-->S, 1-->D, there shouldn't be any wrong. What's the matter with it?

  2. Does "clobbered registers" or the earlyclobber operand(&) have any use? I try to remove "&" or "memory", the result of either circumstance is the same as the original one: output two lines of "Hello_src" strings. So why should I use the "clobbered" things?

回答1:

The earlyclobber & means that the particular output is written before the inputs are consumed. As such, the compiler may not allocate any input to the same register. Apparently using the 0/1 style overrides that behavior.

Of course the clobber list also has important use. The compiler does not parse your assembly code. It needs the clobber list to figure out which registers your code will modify. You'd better not lie, or subtle bugs may creep in. If you want to see its effect, try to trick the compiler into using a register around your asm block:

extern int foo();
int bar()
{
    int x = foo();
    asm("nop" ::: "eax");
    return x;
}

Relevant part of the generated assembly code:

call    foo
movl    %eax, %edx
nop
movl    %edx, %eax

Notice how the compiler had to save the return value from foo into edx because it believed that eax will be modified. Normally it would just leave it in eax, since that's where it will be needed later. Here you can imagine what would happen if your asm code did modify eax without telling the compiler: the return value would be overwritten.



标签: c gcc assembly