【发布时间】:2016-09-27 15:18:42
【问题描述】:
在我们的代码库中,我发现了这个 sn-p 用于在 x87 上快速、趋向负无穷1 舍入:
inline int my_int(double x)
{
int r;
#ifdef _GCC_
asm ("fldl %1\n"
"fistpl %0\n"
:"=m"(r)
:"m"(x));
#else
// ...
#endif
return r;
}
我对 GCC 扩展汇编语法不是很熟悉,但从我从文档中收集到的内容来看:
-
r必须是一个内存位置,我正在写回东西; -
x也必须是一个内存位置,数据的来源。 - 没有 clobber 规范,因此编译器可以放心,在 sn-p 结束时,寄存器与他留下的一样。
现在,来回答我的问题:确实,最终 FPU 堆栈是平衡的,但是如果所有 8 个位置都已在使用并且我溢出了怎么办?编译器怎么知道它不能相信ST(7) 是它离开它的地方?是否应该添加一些clobber?
编辑我试图在clobber列表中指定st(7),它似乎会影响代码生成,现在我将等待这个事实的一些确认。
附带说明:查看 glibc 和 MinGW 中准系统 lrint 的实现,我看到类似
__asm__ __volatile__ ("fistpl %0"
: "=m" (retval)
: "t" (x)
: "st");
我们要求将输入直接放在ST(0) 中(这避免了可能无用的fldl); "st" clobber 是什么?文档似乎只提到了t(即堆栈的顶部)。
- 是的,这取决于当前的舍入模式,在我们的应用程序中,该模式应始终“朝向负无穷大”。
【问题讨论】:
-
st是相当于st0或st(0)的clobber -
内联汇编器很难理解和正确(仅在绝对必要时使用它),这已经够糟糕了。当您处理扩展内联汇编如何与 x87 FPU 堆栈交互的特殊性时,情况会更糟。使用 x86-64,您可以解决这个问题,因为您可以使用 SIMD 指令并在很大程度上避免与 x87 FPU 交互。
-
您可能应该摆脱该内联汇编,并让 gcc 从具有所需语义的 C 中为您发出它。您绝对不想要求输入在内存中,因为如果它是计算的结果,那只会让自己陷入困境(所以它已经在 FP 堆栈的顶部)。让 gcc 发出代码将让它选择是
fist还是fistp,如果它仍然想要 FP 值。 -
@PeterCordes:实际上,那段代码引起了我的注意,当时我在做一些事后调试时,注意到一个直接的
fstp/fldl序列,这让我怀疑 gcc 是否已经消失疯狂的。 :-) 但是必须注意,这段代码有很长的路要走,它是 VC++ 6 中使用的 gcc/MinGW 中的“直接端口”(其中内联汇编几乎没有那么强大),并且该代码它本身来自一个更早的项目,它是因为臭名昭著的直系int在 x86 上的有害性能而编写的。 -
嘿,是的,我从您的问题中得到的印象是代码的来源可疑。 :P 但是你问的是如何让它工作,而不是如何用可以可靠地编译成好的代码的东西来替换它。所以我觉得有必要指出这是可能的,至少如果你可以使用
-fno-math-errno。