gcc内联汇编中的PC相对跳转

gcc内联汇编中的PC相对跳转,gcc,x86-64,inline-assembly,Gcc,X86 64,Inline Assembly,我有一个asm循环,保证不会超过128次迭代,我想用PC相对跳跃展开它。其思想是以相反的顺序展开每个迭代,然后跳转到需要的循环中。代码如下所示: #define __mul(i) \ "movq -"#i"(%3,%5,8),%%rax;" \ "mulq "#i"(%4,%6,8);" \ "addq %%rax,%0;" \ "adcq %%rdx,%1;" \ "adcq $0,%2;" asm("jmp (128-count)*size_of_o

我有一个asm循环,保证不会超过128次迭代,我想用PC相对跳跃展开它。其思想是以相反的顺序展开每个迭代,然后跳转到需要的循环中。代码如下所示:

#define __mul(i) \
    "movq -"#i"(%3,%5,8),%%rax;" \
    "mulq "#i"(%4,%6,8);" \
    "addq %%rax,%0;" \
    "adcq %%rdx,%1;" \
    "adcq $0,%2;"

asm("jmp (128-count)*size_of_one_iteration" // I need to figure this jump out
    __mul(127)
    __mul(126)
    __mul(125)
    ...
    __mul(1)
    __mul(0)
    : "+r"(lo),"+r"(hi),"+r"(overflow)
    : "r"(a.data),"r"(b.data),"r"(i-k),"r"(k)
    : "%rax","%rdx");

gcc内联汇编也可以这样做吗?

很抱歉,我不能用ATT语法提供答案,我希望您可以轻松地执行翻译

如果您在RCX中有计数,并且您可以在_mul(0)之后有一个标签,那么您可以执行以下操作:

; rcx must be in [0..128] range.
    imul ecx, ecx, -size_of_one_iteration ; Notice the multiplier is negative (using ecx is faster, the upper half of RCX will be automatically cleared by CPU)
    lea  rcx, [rcx + the_label] ; There is no memory read here
    jmp  rcx
希望这有帮助

编辑: 我昨天犯了一个错误。我假设在[rcx+the_label]中引用标签被解析为[rcx+rip+disp],但事实并非如此,因为没有这样的寻址模式(只有[rip+disp32]存在)

这段代码应该可以工作,另外,它将保持rcx不变,并将破坏rax和rdx(但您的代码似乎在首先写入它们之前没有读取它们):


这不是一个直接的答案,但您是否考虑过使用 而不是内联 装配这将采用switch语句的形式:

switch(iterations) {
  case 128: /* code for i=128 here */
  case 127: /* code for i=127 here */
  case 126: /* code for i=126 here */
  /* ... */
  case 1:   /* code for i=1 here*/
  break;
  default: die("too many cases");
}

在gcc内联汇编中,您可以使用标签并让汇编程序为您排序跳转目标。类似(人为的例子):

这是一件事。避免乘法的另一个方法是让汇编程序为您对齐块,比如说,以32字节的倍数对齐(我认为指令序列不适合16字节),如:

这将简单地用
nop
填充指令流。如果您不选择对齐这些块,您仍然可以在主表达式中使用生成的局部标签来查找部件块的大小:

#ifdef UNALIGNED
__asm__ ("imul $(.Lmul0-.Lmul1), %[label]\n"
#else
__asm__ ("shlq $5, %[label]\n"
#endif
    "leaq .Lmulblkstart, %[dummy]\n"        /* this is PC-relative in 64bit */
    "jmp (%[dummy], %[label])\n"
    ".align 32\n"
    ".Lmulblkstart:\n"
    __mul(127)
    ...
    __mul(0)
    : ... [dummy]"=r"(dummy) : [label]"r"((128-count)))
对于
count
是编译时常量的情况,您甚至可以执行以下操作:

__asm__("jmp .Lmul" #count "\n" ...);
结尾的小提示:


如果自动生成的
\u mul()
东西可以创建不同长度的序列,那么对齐块是个好主意。对于您使用的常量
0..127
,情况并非如此,因为它们都适合一个字节,但如果您将它们放大,它将变为16位或32位值,指令块将随之增长。通过填充指令流,仍然可以使用jumptable技术。

我现在使用的是Duff设备的一种变体,它可以正常工作,但我发布了这篇文章,因为我想切换到仅限asm的方式
#define mul(i)                     \
    ".align 32\n"                  \
    ".Lmul" #i ":\n"               \
    "movq -" #i "(%3,%5,8),%%rax\n"\
    "mulq " #i "(%4,%6,8)\n"       \
    "addq %%rax,%0\n"              \
    "adcq %%rdx,%1\n"              \
    "adcq $0,%2\n"
#ifdef UNALIGNED
__asm__ ("imul $(.Lmul0-.Lmul1), %[label]\n"
#else
__asm__ ("shlq $5, %[label]\n"
#endif
    "leaq .Lmulblkstart, %[dummy]\n"        /* this is PC-relative in 64bit */
    "jmp (%[dummy], %[label])\n"
    ".align 32\n"
    ".Lmulblkstart:\n"
    __mul(127)
    ...
    __mul(0)
    : ... [dummy]"=r"(dummy) : [label]"r"((128-count)))
__asm__("jmp .Lmul" #count "\n" ...);