GCC内联汇编程序,混合寄存器大小(x86)

时间:2008-09-23 01:59:01

标签: gcc assembly x86 gas

有谁知道如何摆脱以下汇编程序警告?

代码是x86,32位:

int test (int x)
{
  int y;
  // do a bit-rotate by 8 on the lower word. leave upper word intact.
  asm ("rorw $8, %0\n\t": "=q"(y) :"0"(x));
  return y;
}

如果我编译它,我得到以下(非常有效)的警告:

Warning: using `%ax' instead of `%eax' due to `w' suffix

我正在寻找的是告诉编译器/汇编器我想要访问%0的低16位子寄存器的方法。访问字节子寄存器(在本例中为AL和AH)也很好。

我已经选择了“q”修饰符,因此编译器被迫使用EAX,EBX,ECX或EDX。我已经确保编译器必须选择一个具有子寄存器的寄存器。

我知道我可以强制asm-code使用特定的寄存器(及其子寄存器),但我想将寄存器分配作业留给编译器。

5 个答案:

答案 0 :(得分:19)

如果我没记错的话你可以使用%w0。我也测试了它。 : - )

int
test(int x)
{
    int y;
    asm ("rorw $8, %w0" : "=q" (y) : "0" (x));
    return y;
}

编辑:为了回应OP,是的,您也可以执行以下操作:

int
test(int x)
{
    int y;
    asm ("xchg %b0, %h0" : "=Q" (y) : "0" (x));
    return y;
}

目前,我所知道的唯一地方(我知道)是gcc/config/i386/i386.md,而不是任何标准文件。

答案 1 :(得分:9)

很久以前,但我可能需要这个以供将来参考...

再加上克里斯的精彩答案说,关键是在'%'之间使用修饰符。和输出操作数的数量。例如,"MOV %1, %0"可能会成为"MOV %q1, %w0"

我无法在constraints.md中找到任何内容,但/gcc/config/i386/i386.cprint_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名称。

     如果操作数[0]为reg 0,

%b0将打印%al。

Output Template文档的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));

请注意关键但却记录不多的' c'在' %c1'。这个宏相当于

ptr = (char *)ptr + bytes

但没有使用通常的整数算术执行端口。

编辑添加:

在x64中进行直接调用可能很困难,因为它需要另一个未记录的修饰符:' %P0' (这似乎是PIC)

#define ASM_CALL_FUNC(func)                                         \
    __asm volatile("call %P0") :                                    \
              /* no writes */ :                                     \
              /* reads %0 */ "i" (func))                           

小写' p'修饰符似乎在GCC中的功能相同,尽管只有首都' P'被ICC认可。更多细节可能会在/gcc/config/i386/i386.c处获得。搜索"''""。

答案 2 :(得分:1)

在我考虑的时候......你应该用克里斯第二个解决方案中的大写“Q”约束来替换“q”约束:

int
test(int x)
{
    int y;
    asm ("xchg %b0, %h0" : "=Q" (y) : "0" (x));
    return y;
}

“q”和“Q”在64位模式下略有不同,您可以获得所有整数寄存器的最低字节(ax,bx,cx,dx,si,di,sp,bp,r8) - R 15)。但是你只能获得四个原始386寄存器(ax,bx,cx,dx)的第二低字节(例如啊)。

答案 3 :(得分:0)

显然有一些技巧可以做到这一点......但它可能效率不高。在处理通用寄存器中的16位数据时,32位x86处理器通常。如果性能很重要,你应该对它进行基准测试。

除非这是(a)表现至关重要且(b)证明要快得多,否则我会为自己省去一些维护麻烦并且只是在C中这样做:

uint32_t y, hi=(x&~0xffff), lo=(x&0xffff);
y = hi + (((lo >> 8) + (lo << 8))&0xffff);

使用GCC 4.2和-O2,这可以优化到六条指令......

答案 4 :(得分:0)

疑难杂症。好吧,如果它是一个原始的例程,你将要反复重用,我没有任何争论......克里斯指出的寄存器命名技巧是一个很好的,我将不得不记住。< / p>

如果它也成为标准的GCC文档会很好!