That's a syntax error - gcc's inline assembler uses % as operand identifier, and to explicitly use it in register names for x86 you'll have to write:
__asm__ ("movl %%eax, %%db0\n\t");
That'll make it compile.
Correction:
There's multiple issues here:
__asm__ statements without clobbers are highly unusual (and rarely do what you're expecting because there's very few things one can do in assembly that have no side effects). Nonetheless, if and only if the instruction is actually both side-effect-free and has no inputs, it looks to be possible to omit the clobber list.
One consequence of that is that escaping % is no longer necessary; the compiler for this simple example creates the same opcode for __asm__("movl %eax, %db0\n\t"); as it does for the fully-specified __asm__("movl %%eax, %%db0\n\t":::);. This is not necessarily an advantage though, because ...
- The second problem is portability; x86_64 and i386 are similar but not identical, and one of the differences is the register width for the debug regs. This alone is a particularly good reason to actually use inline assembly operands instead of register names directly, because the following code:
__asm__("mov %0, %%db0\n\t" : : "a"((uintptr_t)0) : );
compiles both on 64bit (gcc -m64 ...) and 32bit (gcc -m32 ...) - it creates the same instruction, but the registers used when writing this in assembly are different for 64bit:
208: 0f 23 c0 mov %rax,%db0
while in 32bit, it does:
269: 0f 23 c0 mov %eax,%db0
The use of an input operand here provides the ability to abstract the register width away (uintptr_t comes from <inttypes.h> and is guaranteed to always be full general-purpose register width), so the same inline assembly can be used for 32bit and 64bit compiles.
I admit I had no idea the compiler treats clobber-list-less __asm__ differently from "normal" such statements.
In any case, in reality you will definitely need an argument/clobber list when modifying debug registers, because access to these are serializing instructions which should be implicitly made known to the compiler as memory clobber. Besides, the value you're reading from them / writing to them must go to / come from somewhere ... hence input/output.