如何坚持 C 编译器将局部变量放在堆栈上,而不是寄存器中

How to insist a C compiler put local variables on the stack, not in registers

我正在尝试将历史函数式语言解释器(KRC for EMAS)移植到现代系统(C for Unix),它有一个垃圾收集器,希望能够扫描堆栈中的指针以了解堆在 GC 期间移动堆中的对象时,它必须重新定位哪些指针。为此,必须在堆栈中找到指向堆的所有函数参数和局部变量。

现在,有一段时间 "register" 关键字表示 "you can put this variable in a register if you like" 否则它在堆栈上,但现在所有(GCC、Clang、Tinyc/tcc)C 编译器似乎无论如何将局部变量放入寄存器,无法禁用此行为,结果是 GC 丢失了一些属于正在进行的函数的值,无法保留它们并破坏堆。

有没有办法告诉这些编译器使用原始的 C 语义,所有局部变量都在堆栈上,除非你说 "register"?

我有几个疣"solutions":

这一切似乎都可以改善问题,但非常不可靠且不可靠。

是否有更好的方法来实现所需的结果,确保所有函数参数和局部变量都在堆栈上?

好的,这是一个奇怪的 GC;好吧,您可能使用了 volatile 关键字。

它最初是为内存映射设备之类的东西设计的,在这些设备中,您希望强制编译器不要优化变量。它的用途 abuse 一直是一个长期讨论的话题。

Is there a better way to achieve the required result

真的很难回答。一方面:显然,是的:不要让您的 GC 依赖于不能依赖的东西。但这意味着重写它。另一方面:如果需要额外的代码来确保堆栈放置工作,那为什么不去做呢?这不像是为了 性能.

对历史解释器进行代码移植

我想你使用了一种 "mark and sweep" GC。在这种情况下,您只需要在标记阶段开始时保存寄存器。我的建议是检查您的 GC,找到 "mark and sweep" 操作开始的位置,并放置一个代码,将所有寄存器放入此处的可访问内存中。 setjmp 是一种实现此目的的半便携式方法(除非您正在使用 sparc)。

您的问题似乎有一个简单的解决方案:如果 GC 运行 是同步的,例如,如果从分配函数调用,那么其他函数是否将指针存储在堆栈上或在寄存器中,只要在 GC 运行 并扫描堆栈之前的某个时刻将所有寄存器保存到堆栈中。将 GC 代码包装在一个将所有寄存器保存在堆栈上的函数中,您就完成了。为此可能需要内联汇编,但如 Marian 所述,setjmp 应该足够了。

只是想告诉你结果如何。我在代码中发现了一些没有被告知 GC 的数据项,在进入 GC 之前用 setjmp() 将寄存器放在堆栈上,最后一件事是一些堆栈变量指向 "tail" 堆中链表的单元格而不是头部,以便能够快速追加。我忽略了这些,因为它们没有与单元格边界对齐。在这些事情中,我永远不会想到 setjmp() 技巧,非常感谢。您的建议使 interp 工作与不工作之间有所不同。祝福!