c/c++语言开发共享如何使用扩展的gcc程序集指定x87 FPU堆栈的破坏底部?

在我们的代码库中,我发现了这个代码片段,用于在x87上快速,朝向负无穷大1舍入:

inline int my_int(double x) { int r; #ifdef _GCC_ asm ("fldl %1n" "fistpl %0n" :"=m"(r) :"m"(x)); #else // ... #endif return r; } 

我不是非常熟悉GCC扩展汇编语法,但是从我从文档中收集到的内容:

现在,提出我的问题:最终FPU堆栈是平衡的,但如果所有8个位置都已经在使用并且我已经溢出呢? 编译器如何知道它不能信任ST(7)离开它的位置? 应该添加一些clobber吗?

编辑我试图在clobber列表中指定st(7) ,它似乎影响codegen,现在我将等待对此事实的一些确认。


作为旁注:在glibc和MinGW中查看准系统lrint的实现我看到类似的东西

 __asm__ __volatile__ ("fistpl %0" : "=m" (retval) : "t" (x) : "st"); 

我们要求输入直接放在ST(0) (这避免了可能无用的fldl ); 什么是"st" clobber? 文档似乎只提到t (即堆栈的顶部)。


    看着glibc和MinGW中的准系统lrint的实现,我看到了类似的东西

     __asm__ __volatile__ ("fistpl %0" : "=m" (retval) : "t" (x) : "st"); 

    我们要求输入直接放在ST(0) (这避免了可能无用的fldl

    这实际上是将所需代码表示为内联汇编的正确方法。

    要获得生成的最佳代码,您需要使用输入和输出。 不要硬编码必要的加载/存储指令,而是让编译器生成它们。 这不仅引入了消除潜在不必要指令的可能性,而且还意味着编译器可以在需要时更好地调度这些指令(也就是说,它可以在先前的代码序列中交错指令,通常最小化其成本)。

    什么是"st" clobber? 文档似乎只提到t (即堆栈的顶部)。

    "st" clobber是指st(0)寄存器, x87 FPU堆栈的顶部。 英特尔/ MASM表示法称为st(0) ,AT&T / GAS表示法通常指的是简单的st 。 而且,根据GCC的clobbers文档,clobber列表中的项目是“寄存器名称或特殊clobbers”( "cc" (条件代码/标志)和"memory" )。 所以这只意味着内联汇编破坏了(覆盖) st(0)寄存器。 这个clobber是必要的原因是fistpl指令弹出堆栈的顶部,从而破坏了st(0)的原始内容。

    关于此代码,我唯一关心的是文档中的以下段落:

    Clobber描述可能不以任何方式与输入或输出操作数重叠。 例如,在clobber列表中列出该寄存器时,您可能没有描述具有一个成员的寄存器类的操作数。 声明为特定寄存器(参见显式寄存器变量 )并用作asm输入或输出操作数的变量必须没有在clobber描述中提及的部分。 特别是,没有办法指定输入操作数被修改而不将它们指定为输出操作数。

    当编译器选择用于表示输入和输出操作数的寄存器时,它不使用任何被破坏的寄存器。 因此,破坏寄存器可用于汇编代码中的任何用途。

    如您所知, t 约束意味着x87 FPU堆栈的顶部。 问题是,这与st寄存器相同,并且文档非常明确地说我们没有一个clobber指定与输入/输出操作数之一相同的寄存器。 此外,由于文档声明编译器禁止使用任何被破坏的寄存器来表示输入/输出操作数,因此这个内联汇编使得一个不可能的请求 – 将该值加载到x87 FPU堆栈的顶部而不将其放入st

    现在,我假设glibc的作者知道他们在做什么,并且比你或我更熟悉编译器内联汇编的实现,所以这段代码可能合法且合法。

    实际上,x87堆栈式寄存器的exception情况似乎迫使clobbers和操作数之间的正常交互例外。 官方文件说:

    在x86目标上,有一些关于在asm的操作数中使用类似堆栈的寄存器的规则。 这些规则仅适用于类似堆栈的寄存器:

    这完全适合我们的情况。

    官方文档中出现的示例(链接部分的底部)提供了进一步的确认:

    这个asm接受两个输入,由fyl2xp1操作码弹出,并用一个输出替换它们。 st(1) clobber是编译器知道fyl2xp1弹出两个输入所必需的。

     asm ("fyl2xp1" : "=t" (result) : "0" (x), "u" (y) : "st(1)"); 

    这里,clobber st(1)与输入约束u相同,这似乎违反了上面提到的关于clobbers的文档,但是使用和certificate了"st"被用作你的clobber的相同理由原始代码,因为fistpl弹出输入。


    所有这些说,现在你知道如何在内联汇编中正确编写代码,我必须回应以前的评论者,他们建议最好的解决方案是不要使用内联汇编。 只需调用lrint ,它不仅具有您想要的确切语义,而且在某些情况下也可以由编译器进行更好的优化( 例如 ,当目标体系结构支持SSE时将其转换为单个cvtsd2si指令)。

      以上就是c/c++开发分享如何使用扩展的gcc程序集指定x87 FPU堆栈的破坏底部?相关内容,想了解更多C/C++开发(异常处理)及C/C++游戏开发关注(计算机技术网)。

      本文来自网络收集,不代表计算机技术网立场,如涉及侵权请点击右边联系管理员删除。

      如若转载,请注明出处:https://www.ctvol.com/c-cdevelopment/545742.html

      (0)
      上一篇 2021年1月10日
      下一篇 2021年1月10日

      精彩推荐