我正在尝试将历史功能语言解释器(EMAS的KRC)移植到现代系统(C for Unix),并且它有一个垃圾收集器,它希望能够扫描堆栈以获取指向堆的指针以了解哪些指针在GC期间移动堆中的对象时,它必须重定位。为此,必须在堆栈中找到指向堆的所有函数参数和局部变量。
现在,有一段时间“register”关键字意味着“你可以把这个变量放在寄存器中,如果你喜欢”,否则它就在堆栈上,但现在所有(GCC,Clang,Tinyc / tcc)C编译器似乎无论如何都将局部变量放入寄存器中,无法禁用此行为,结果是GC错过了属于正在进行的函数的某些值,无法保留它们并破坏堆。
有没有办法告诉任何这些编译器使用原始C语义,除非你说“注册”,否则所有局部变量都在堆栈上?
我有一些狡猾的“解决方案”:
这些似乎都可以改善问题,但是非常黑客和不可靠。
是否有更好的方法来实现所需的结果,确保所有函数参数和局部变量都在堆栈中?
答案 0 :(得分:3)
好的,这是一个奇怪的GC;好吧,您可能会使用volatile
关键字。
它最初用于内存映射设备,您希望强制编译器不要优化变量。它的使用和abuse一直是讨论的主题。
是否有更好的方法来实现所需的结果
真的很难回答。一方面:显然,是的:不要让你的GC依赖于那些无法依赖的东西。但这意味着重写它。另一方面:如果需要额外的代码来确保堆栈放置工作,那么为什么不去寻找呢?它并不像您为效果代码移植历史解释器。
答案 1 :(得分:3)
我想你使用了一种“标记和扫描”GC。在这种情况下,您只需在标记阶段开始时保存寄存器。我的建议是检查你的GC,找到“标记和扫描”操作开始的地方,并把代码放在一个可访问的内存中。 setjmp
是实现此目的的半便携式方式(除非您正在使用sparc)。
答案 2 :(得分:0)
对你的问题似乎有一个简单的解决方案:如果GC同步运行,例如,如果从分配函数调用,那么只要所有寄存器,其他函数是否在堆栈或寄存器中存储指针并不重要在GC运行之前的某个时刻保存到堆栈并扫描堆栈。将GC代码包装在一个保存堆栈中所有寄存器的功能中,您就完成了。内联汇编可能是必要的,但是setjmp
应该足够了,如Marian所述。
答案 3 :(得分:0)
我想我会告诉你它是如何被淘汰出局的。我在代码中发现了几个没有被告知GC的数据项,在进入GC之前将寄存器放在堆栈上,并且最后一些是堆栈变量指向“尾部”堆中的链表的单元格而不是头部,以便能够快速追加。我忽略了这些因为没有与细胞边界对齐。在这些事情中,我永远不会有setjmp()技巧,非常感谢。你的建议使得interp工作与否之间产生了差异。祝福!