很久以前,但我可能需要这个作为自己未来的参考...
补充Chris的精彩答案,关键是在输出操作数的数字和百分号之间使用修饰符。例如,"MOV %1, %0"
可能会变成 "MOV %q1, %w0"
。
我在constraints.md中找不到任何内容,但/gcc/config/i386/i386.c在print_reg()
源代码中有这个有用的注释:
/* Print the name of register X to FILE based on its machine mode and number.
If CODE is 'w', pretend the mode is HImode.
If CODE is 'b', pretend the mode is QImode.
If CODE is 'k', pretend the mode is SImode.
If CODE is 'q', pretend the mode is DImode.
If CODE is 'x', pretend the mode is V4SFmode.
If CODE is 't', pretend the mode is V8SFmode.
If CODE is 'h', pretend the reg is the 'high' byte register.
If CODE is 'y', print "st(0)" instead of "st", if the reg is stack op.
If CODE is 'd', duplicate the operand for AVX instruction.
*/
ix86_print_operand()
下方的评论提供了一个例子:
b -- 打印指定操作数所表示寄存器的QImode名称。
如果operands[0]是reg 0,则%b0将打印%al。
GCC Internals文档的输出模板部分列出了几个更有用的选项:
“%cdigit”可用于替换常量值的操作数,而无需通常表示立即操作数的语法。
“%ndigit”与“%cdigit”类似,只是在打印之前对常量的值进行了取反。
“%adigit”可用于将操作数替换为内存引用,实际操作数被视为地址。当输出“加载地址”指令时,这可能很有用,因为通常汇编器语法要求您将操作数写成内存引用的形式。
“%ldigit”用于将label_ref替换为跳转指令。
“%=”输出一个数字,该数字在整个编译中每个指令都是唯一的。这对于使本地标签在生成多个汇编器指令的单个模板中被多次引用非常有用。
“%c2”结构允许使用偏移量正确格式化LEA指令:
#define ASM_LEA_ADD_BYTES(ptr, bytes) \
__asm volatile("lea %c1(%0), %0" : \
/* reads/writes %0 */ "+r" (ptr) : \
/* reads */ "i" (bytes));
请注意 '
%c1
' 中关键但文档稀少的 'c'。这个宏与...等价。
ptr = (char *)ptr + bytes
但是不使用通常的整数算术执行端口。
编辑以添加:
在x64中进行直接调用可能很困难,因为它需要另一个未记录的修饰符:“%P0”(似乎是用于PIC)。
#define ASM_CALL_FUNC(func) \
__asm volatile("call %P0") : \
/* no writes */ : \
/* reads %0 */ "i" (func))
在GCC中,小写的“p”修饰符似乎也具有相同的功能,尽管ICC仅识别大写的“P”。更多细节可能可在
/gcc/config/i386/i386.c找到。搜索“'p'”。
xchg %al, %ah
在英特尔CPU上需要3个微操作,读取16位ax会导致部分寄存器停顿或某些CPU上需要额外的微操作。ror $8, %ax
只需要1个微操作,因此它绝对是更可取的。此外,操作数修饰符现在已经在手册中有所说明(使用相同的示例,这可能不是巧合:P)。另请参阅:矢量寄存器的操作数修饰符:https://dev59.com/plsW5IYBdhLWcg3w66qH - Peter Cordes