是否可以为移动 GC 实现生成带有类型信息的 ansi C 函数?
Is it possible to generate ansi C functions with type information for a moving GC implementation?
我想知道有哪些方法可以将类型化信息添加到生成的 C 方法中。我正在将一种高级编程语言转换为 C,我想添加一个移动垃圾收集器。但是要做到这一点,我需要方法变量具有类型信息,否则我可以修改一个看起来像指针的原始值。
一个明显的方法是将所有(原始和非原始)变量封装在一个结构中,该结构具有用于键入信息的额外(枚举)变量,但是这会导致内存和性能开销,转译后的代码是适用于嵌入式平台。如果我接受内存开销,显而易见的选择是对所有对象使用堆句柄,然后我就可以自由移动堆块。但是我想知道是否有更有效更好的方法。
我想出了一个潜在的解决方案,即根据变量是否是基元来预先声明和分组变量(我可以在转译器中做到这一点),并在最后为每个方法添加一个偏移量变量(我需要在扫描堆栈区域时能够准确地找到它),它告诉我非原始变量从哪里开始,在哪里结束,所以我只能扫描那些。这意味着每个方法都将使用额外的 16/32 位(取决于架构)内存,但是这仍然比堆句柄方法更有效。
示例:
void my_func() {
int i = 5;
int z = 3;
bool b = false;
void* person;
void* person_info = ...;
.... // logic
volatile int offset = 0x034;
}
我的目标是实现跨 GCC 编译器通用的东西,因此我担心的是:
- 编译器能否根据变量的声明方式对变量进行重新排序
源代码?
- 我可以强制编译器将一些数据放入
方法的堆栈框架(使用 volatile)?
- 扫描堆栈时能否准确找到偏移量?
我想避免汇编,因此这种方法可以(默认情况下)跨多个平台工作,但是我愿意接受方法,即使它们涉及汇编(如果它们可靠的话)。
键入信息可以以某种方式编码在 C 函数名称中;这是由 C++ 和其他实现完成的,并称为 name mangling.
实际上,由于生成了所有 C 代码,您可以决定采用不同的约定:生成长 C 标识符,这些标识符实际上是唯一的,并且在程序范围内是随机的,例如 tiziw_7oa7eIzzcxv03TmmZ
和将他们的打字信息保存在别处(例如某些数据库)。在 Linux 上,这种方法对两个 libbacktrace and dlsym(3) + dladdr(3) (and of course nm(1) or readelf(1) or gdb(1)), so used in both bismon and RefPerSys 项目都很友好。
键入信息实际上与 calling conventions and ABIs. For example, the x86-64 ABI for Linux mandates different processor registers 相关,用于传递浮点数或指针。
阅读Garbage Collection handbook or at least P.Wilson Uniprocessor Garbage Collection Techniques survey. You could decide to use tagged integers instead of boxing them, and you could decide to have a conservative GC (e.g. Boehm's GC) instead of a precise one. In my old GCC MELT project I generated C or C++ code for a generational copying GC. Similar techniques are used both in Bismon and in RefPerSys。
由于您要转译为 C,因此还要考虑替代方案,例如 libgccjit or LLVM. Look into libjit and asmjit。
研究其他转译器(C 编译器)的实现,包括Chicken/Scheme and Bigloo。
Can the GCC compiler reorder the variables from how they're declared in the source code?
当然可以,具体取决于您要求的优化。有些变量甚至不存在于二进制文件中(例如那些留在寄存器中的变量)。
Can I force the compiler to put some data in the method's stack frame (using volatile)?
最好生成一个包含所有语言变量的 struct
变量,并将优化留给编译器。您会感到惊讶(参见 this 报告草稿)。
Can I find the offset accurately when scanning the stack?
这是最难的,并且很大程度上取决于compiler optimizations (e.g. if you run gcc
with -O1
or -O3
on the generated C code; in some cases a recent GCC -e.g GCC 9 or GCC 10 on x86-64 for Linux- is capable of tail-call optimizations; check by compiling using gcc -O3 -S -fverbose-asm
then looking into the produced assembler code). If you accept some small target processor and compiler specific tricks, this is doable. Study the implementation of the Ocaml编译器。
给我(到 basile@starynkevitch.net
)发一封电子邮件进行讨论。请在其中提及您的问题 URL。
如果你想有一个高效的多线程分代复制 GC,事情变得非常棘手。那么问题是你能负担得起多少年的开发。
如果您的语言有例外情况,也要格外小心。您可以非常谨慎地生成对 longjmp
.
的调用
当然见我的this answer。
使用转译技术,细节决定成败
在 Linux(特别是!)另见我的 manydl.c program. It demonstrates that on a Linux x86-64 laptop you could generate, in practice, hundred of thousands of dlopen(3)-ed plugins. Read then How to write shared libraries
研究 SBCL and of GNU Prolog 的实现,至少是为了获得灵感。
PS。完全架构中立和操作系统独立的转译器的梦想是一种幻想。
我想知道有哪些方法可以将类型化信息添加到生成的 C 方法中。我正在将一种高级编程语言转换为 C,我想添加一个移动垃圾收集器。但是要做到这一点,我需要方法变量具有类型信息,否则我可以修改一个看起来像指针的原始值。
一个明显的方法是将所有(原始和非原始)变量封装在一个结构中,该结构具有用于键入信息的额外(枚举)变量,但是这会导致内存和性能开销,转译后的代码是适用于嵌入式平台。如果我接受内存开销,显而易见的选择是对所有对象使用堆句柄,然后我就可以自由移动堆块。但是我想知道是否有更有效更好的方法。
我想出了一个潜在的解决方案,即根据变量是否是基元来预先声明和分组变量(我可以在转译器中做到这一点),并在最后为每个方法添加一个偏移量变量(我需要在扫描堆栈区域时能够准确地找到它),它告诉我非原始变量从哪里开始,在哪里结束,所以我只能扫描那些。这意味着每个方法都将使用额外的 16/32 位(取决于架构)内存,但是这仍然比堆句柄方法更有效。
示例:
void my_func() {
int i = 5;
int z = 3;
bool b = false;
void* person;
void* person_info = ...;
.... // logic
volatile int offset = 0x034;
}
我的目标是实现跨 GCC 编译器通用的东西,因此我担心的是:
- 编译器能否根据变量的声明方式对变量进行重新排序 源代码?
- 我可以强制编译器将一些数据放入 方法的堆栈框架(使用 volatile)?
- 扫描堆栈时能否准确找到偏移量?
我想避免汇编,因此这种方法可以(默认情况下)跨多个平台工作,但是我愿意接受方法,即使它们涉及汇编(如果它们可靠的话)。
键入信息可以以某种方式编码在 C 函数名称中;这是由 C++ 和其他实现完成的,并称为 name mangling.
实际上,由于生成了所有 C 代码,您可以决定采用不同的约定:生成长 C 标识符,这些标识符实际上是唯一的,并且在程序范围内是随机的,例如 tiziw_7oa7eIzzcxv03TmmZ
和将他们的打字信息保存在别处(例如某些数据库)。在 Linux 上,这种方法对两个 libbacktrace and dlsym(3) + dladdr(3) (and of course nm(1) or readelf(1) or gdb(1)), so used in both bismon and RefPerSys 项目都很友好。
键入信息实际上与 calling conventions and ABIs. For example, the x86-64 ABI for Linux mandates different processor registers 相关,用于传递浮点数或指针。
阅读Garbage Collection handbook or at least P.Wilson Uniprocessor Garbage Collection Techniques survey. You could decide to use tagged integers instead of boxing them, and you could decide to have a conservative GC (e.g. Boehm's GC) instead of a precise one. In my old GCC MELT project I generated C or C++ code for a generational copying GC. Similar techniques are used both in Bismon and in RefPerSys。
由于您要转译为 C,因此还要考虑替代方案,例如 libgccjit or LLVM. Look into libjit and asmjit。
研究其他转译器(C 编译器)的实现,包括Chicken/Scheme and Bigloo。
Can the GCC compiler reorder the variables from how they're declared in the source code?
当然可以,具体取决于您要求的优化。有些变量甚至不存在于二进制文件中(例如那些留在寄存器中的变量)。
Can I force the compiler to put some data in the method's stack frame (using volatile)?
最好生成一个包含所有语言变量的 struct
变量,并将优化留给编译器。您会感到惊讶(参见 this 报告草稿)。
Can I find the offset accurately when scanning the stack?
这是最难的,并且很大程度上取决于compiler optimizations (e.g. if you run gcc
with -O1
or -O3
on the generated C code; in some cases a recent GCC -e.g GCC 9 or GCC 10 on x86-64 for Linux- is capable of tail-call optimizations; check by compiling using gcc -O3 -S -fverbose-asm
then looking into the produced assembler code). If you accept some small target processor and compiler specific tricks, this is doable. Study the implementation of the Ocaml编译器。
给我(到 basile@starynkevitch.net
)发一封电子邮件进行讨论。请在其中提及您的问题 URL。
如果你想有一个高效的多线程分代复制 GC,事情变得非常棘手。那么问题是你能负担得起多少年的开发。
如果您的语言有例外情况,也要格外小心。您可以非常谨慎地生成对 longjmp
.
当然见我的this answer。
使用转译技术,细节决定成败
在 Linux(特别是!)另见我的 manydl.c program. It demonstrates that on a Linux x86-64 laptop you could generate, in practice, hundred of thousands of dlopen(3)-ed plugins. Read then How to write shared libraries
研究 SBCL and of GNU Prolog 的实现,至少是为了获得灵感。
PS。完全架构中立和操作系统独立的转译器的梦想是一种幻想。