error: impossible register constraint in 'asm&

2019-06-05 18:03发布

问题:

I've run into a problem while compiling a package around, I am not really a good coder, but I tried fixing it for my self, and it still won't compile. This is the original bit of code.:

#ifdef __GNUC__
asm("and $3, %%ecx;"
"shl $3 ,%%ecx;"
"ror %%cl, %0"
: "=r" (value)
: "r" (value), "c" (address));
#else

The error is .:

GBAinline.h:139: error: impossible register constraint in 'asm' ( ifdef line is 138 )

And this is how I tried to make it look.:

#ifdef __GNUC__
asm ("and $3 %%ecx,shl $3 %%ecx,ror %%cl, %0" : "=r" (value): "r" (value), "c" (address));

#else

Still, it would not work. It's a gba emulator before anyone ask, VBA, and this is part of GBAinline.h . This assembler is making me crazy already.

Edit.: The problem above was handled fine, I just was not paying attention to which compiler I was using. But now I get this error on this bit of code from a header file, I've put it on pastebin, to keep things here a bit more tidy... ( Sorry if this is wrong, i can change that later )

This is the header that has the lines that results in errors.: http://pastebin.com/k3D4cg0d

And this is the C file it refers to.: http://pastebin.com/Ymg1X5dg

This is giving an error like this.:

/var/tmp/cc3zA0lH.s: Assembler messages: /var/tmp/cc3zA0lH.s:69: Error: bad instruction `sw $3,0(r3)',

And so on for the rest of those lines.

回答1:

That inline assembly is buggy:

  1. It uses multi-line strings which effectively concatenate. Without \n all appears on one line. Whether your assembler accepts statements separated by semicolons makes all the difference there ... some may not.
  2. It specifies the same variable as input/output constraint instead of using "+r"(value) as ordinarily suggested for this situation.

Without seeing the rest of the code it's not quite clear why the inline assembly statement looks the way it does; Personally, I'd suggest to write it like:

asm("ror %%cl, %0" : "+r"(value) : "c"((((uintptr_t)address) & 3) << 3)));

because there's little need to do the calculation itself in assembly. The uintptr_t (from <stdint.h>) cast makes this 32/64bit agnostic as well.

Edit:

If you want it for a different CPU but x86 / x64, then it obviously needs to be different ... For ARM (not Thumb2), it'd be:

asm("ROR %0, %0, %1" : "+r"(value) : "r"((((uintptr_t)address) & 3) << 3)));

since that's how the rotate instruction there behaves.

Edit (add reference):

Regarding the operation performed here as such, this blog post gives an interesting perspective - namely, that the compiler is quite likely to create the same output for:

(a >> shift | a << (8 * sizeof(a) - shift))

as for the x86 inline

asm("ror %%cl, %0" : "+r"(a) : "c"(shift))

Testing this:

#include <stdint.h>

int main(int argc, char **argv)
{
    unsigned int shift = (int)((((uintptr_t)argv) & 3) << 3);
    unsigned int a = argc;
#ifdef USE_ASM
    /*
     * Mark the assembly version with a "nop" instruction in output
     */
    asm("nop\n\t"
        "ror        %%cl, %0" : "+r"(a) : "c"(shift));
    return a;
#else
    return (a >> shift | a << (8 * sizeof(a) - shift));
#endif
}

Compile / disassemble it:

$ gcc -DUSE_ASM -O8 -c tf.c; objdump -d tf.o

tf.o:     file format elf64-x86-64

Disassembly of section .text:

0000000000000000 :
   0:   83 e6 03                and    $0x3,%esi
   3:   8d 0c f5 00 00 00 00    lea    0x0(,%rsi,8),%ecx
   a:   90                      nop
   b:   d3 cf                   ror    %cl,%edi
   d:   89 f8                   mov    %edi,%eax
   f:   c3                      retq
$ gcc -O8 -c tf.c; objdump -d tf.o

tf.o:     file format elf64-x86-64

Disassembly of section .text:

0000000000000000 :
   0:   83 e6 03                and    $0x3,%esi
   3:   8d 0c f5 00 00 00 00    lea    0x0(,%rsi,8),%ecx
   a:   d3 cf                   ror    %cl,%edi
   c:   89 f8                   mov    %edi,%eax
   e:   c3                      retq

Ergo, this inline assembly is unnecessary.