我正在制作某种解释器,并且由于本地标签地址,我正在计算一个静态 const 跳转表。
你知道演习,
static const int JUMP_TABLE[] = { &&case0 - &&case0, &&case1 - &&case0
等等。
由于各种原因,主要是性能,我想在初始化期间将此表复制/压缩到一个对象中。
我的头撞墙了,因为我不知道如何逃避函数的词法范围!
我怎样才能以某种方式从另一个函数引用 &&case0 ?
有人对此有什么好办法吗?
提前致谢
问问题
270 次
2 回答
3
我不知道在纯 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;
}
不幸的是,在这种情况下,您只能收集地址(而不是偏移量),因此您需要在启动时手动计算偏移量。
于 2020-05-17T08:30:00.850 回答
0
有时 Goto 只是最好的解决方案。非常罕见。多年后它仍然是 c++ 的一部分,这是有充分理由的
所以我所做的是创建一个全局布尔值并在初始化地址数组后设置它。所以第一次调用我的解释器函数时,它会加载一个地址结构,因此所有内容都在同一个函数中。
然后通过对汇编程序输出的一些研究,我能够通过如下安排我的代码来节省一些滴答声。
if(is_initialized) .. ex 命令 else ... 初始化东西。转到前命令
使用 goto 跳回顶部并执行命令。我的解释器使用了近 200 个命令。
使用 switch 语句需要 5-1100 个滴答声。取决于命令在列表中的位置
无论命令在列表中的哪个位置,使用 goto functions[command] 都会将其降至 14
这提供了一种纯 c++ 但并非所有编译器都支持的方法
于 2021-09-30T00:15:25.863 回答