【问题标题】:Simplest way to remove something from the FPU stack从 FPU 堆栈中删除某些内容的最简单方法
【发布时间】:2011-01-23 04:59:00
【问题描述】:

我最近遇到了一些 FPU 堆栈溢出问题。我设法将其追溯到一个有缺陷的库函数,该函数每次调用时都会将垃圾值推送到 FPU 堆栈上,并且从不清理它。

幸运的是,这很容易重现,我确切地知道是什么条件导致了它。我可以将一个内联 ASM 块放入调用该例程的例程中,以将顶部值从 FPU 堆栈中弹出……除非我不太清楚该写什么。我的 ASM-fu 中等偏上,但没有那么强。

那么,假设它是垃圾数据并且我不关心该值,那么在 x86 汇编中摆脱 FPU 堆栈上的顶部值的最简单方法是什么?

【问题讨论】:

  • 为了记录,x87 已经过时了。现代代码通常会使用 XMM 寄存器,即使是标量 FP 数学,也可以使用 addss(标量单精度)或 addsd (scalar double) 之类的指令。查看任何 FP 函数的编译器生成的代码作为示例。

标签: assembly x86 x87


【解决方案1】:

对于 Delphi/BASM,在我看来,一次弹出 FPU 堆栈的最简单方法是:

asm
 fstp st(0)
end;

【讨论】:

  • 这其实是正确的答案。大多数 x87 实现都针对这种情况进行了优化,甚至不会执行传输,只需将值从堆栈中弹出即可。
【解决方案2】:

如果您知道需要将堆栈调整多少,可以使用fincstp。你还想ffree你递增的寄存器。

但是,可能最简单的解决方案是使用弹出数据传输操作之一,例如fstp。通常,您会将结果存储到内存区域以供以后使用,例如:

mem_area: defs 10         ; ten bytes for 80 bits
          fstp mem_area   ; pop it

但是,如果你知道你只是想扔掉这个值,你可以使用st(0)本身作为目的地,节省内存需求:

fstp st(0)

有关说明的详细指南,请参阅 here(特别是 this bit)。

【讨论】:

  • 请注意,“fstp st(0)”需要一个时钟。没有比这更好的了。
  • @AlbertvanderHorst:超标量 CPU 可以轻松做到比这更好。 fstp/fst 在 Haswell / Skylake 上实际上每 0.5c 吞吐量(即每时钟 2 个)具有一个寄存器目标。 agner.org/optimize。寄存器重命名通过栈顶指针处理依赖关系。
【解决方案3】:

如果 st0 是唯一正在使用的 x87 寄存器,您可以将其清空:

ffree st0

但这与使用多个堆栈寄存器时的普通弹出不同,因为它不会调整堆栈顶部指针(x87 状态字中的 TOP 字段)。

the registers chapter of the Simply FPU x87 tutorial

st1 在释放 st0 而不是弹出后仍将是 st1,因此这通常不是您想要的,并且与 fstp st0 相比没有明显优势。

【讨论】:

  • 这不起作用。 st0 被标记为空,但这不会影响除了 fpu 标记词之外的任何内容。
  • 不确定您还想影响什么,因为它是寄存器中内容的定义。
  • 当然你也想改变堆栈指针。我已经通过实验验证(通过我的 Forth 和 ciforth 的小代码)您的解决方案无法按预期工作。请参阅 PhiS 给出的正确答案。
  • 我为什么要更改 sp?堆栈是空的,无论你从哪里开始填充它都没有关系。
  • @JensBjörnhager:您的更新在技术上是正确的,但对于那些对 x87 不够了解甚至认为使用中唯一的 1 个注册条件是相关的人来说仍然很容易被误解 /重要的。除了代码块之外,我基本上将答案改写为我认为值得投票的内容。
【解决方案4】:

只需使用弹出的任何(快速)指令将其从堆栈中弹出。 8087 instruction set

如果这不起作用,FUCOMPP 会弹出两次。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-11-03
    • 1970-01-01
    • 1970-01-01
    • 2014-04-19
    • 2010-11-20
    • 2018-11-30
    • 2022-11-17
    • 2018-05-02
    相关资源
    最近更新 更多