在gcc/clang (C++) 中获取函数作用域外的标签地址

Get a label address out of the function scope in gcc/clang (C++)


由于本地标签地址,我正在制作某种解释器并且正在计算静态常量跳转table。
你知道该怎么做, static const int JUMP_TABLE[] = { &&case0 - &&case0, &&case1 - &&case0 等等。
由于各种原因,主要是性能,我想 copy/compress 在初始化期间在一个对象中 table。
我的头撞到墙上了,因为我想不出如何逃避函数的词法范围!

我怎样才能以某种方式从另一个函数引用 &&case0?

有人对此有好的技巧吗?
提前致谢

我不知道在纯 GNU C 中实现此目的的方法,因此下面的方法使用其他机制。

双重编译

您可以编译目标文件两次,第一次收集偏移量 运行 并在第二次使用它们。例如

int foo(int x) {
#ifdef GENERATE_ADDRESSES
    static __attribute__((section(".foo_offsets"))) unsigned offsets[] = { &&case0 - &&case0, &&case1 - &&case0 };
#endif
    switch (x) {
case0:
        case 0:
            return 1;
case1:
        case 1:
            return 2;
    }
    return 0;
}

现在您可以编译,从 .foo_offsets 部分提取字节,并在第二个 运行

上将它们嵌入到您的应用程序中
$ gcc tmp.c -c -DGENERATE_ADDRESSES
$ objcopy -j .foo_offsets -O binary tmp.o
$ xxd -i tmp.o | tee offsets.inc
unsigned char tmp_o[] = {
  0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00
};
unsigned int tmp_o_len = 8;

内联汇编

您可以使用内联汇编来全球化标签:

extern char foo_case0[];
extern char foo_case1[];
const void *foo_addresses[] = { &foo_case0[0], &foo_case1[0] };

int foo(int x) {
    switch (x) {
        case 0:
asm("foo_case0:");
            return 1;
        case 1:
asm("foo_case1:");
            return 2;
    }
    return 0;
}

不幸的是,在这种情况下,您只能收集 ​​ 地址 (不是偏移量),因此您需要在启动时手动计算偏移量。

有时转到只是最好的解决方案。非常罕见。多年后它仍然是 c++ 的一部分,这是有充分理由的

所以我所做的是创建一个全局 bool 并在我初始化我的地址数组后设置它。所以第一次调用我的解释器函数时,它加载了一个地址结构,因此所有内容都在同一个函数中。

然后通过对汇编器输出的一些研究,我能够通过如下安排我的代码来节省一些滴答声。

if(is_initialized) .. ex 命令 否则...初始化东西。转到前命令

使用 goto 跳回顶部并执行命令。 我的解释器使用了将近 200 个命令。

使用 switch 语句需要 5-1100 个滴答。取决于命令在列表中的位置

无论命令在列表中的哪个位置,使用 goto 函数[command] 将其减少到 14

这提供了一种纯 C++ 但并非所有编译器都支持的方法