gcc内联汇编中的PC相对跳转
我有一个asm循环,保证不会超过128次迭代,我想用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
#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" ...);