标签地址(MSVC)

问题描述 投票:0回答:4

我们正在为高级编译语言编写字节码,经过一些分析和优化后,很明显当前最大的性能开销是我们用来跳转到字节码情况的 switch 语句.

我们研究了提取每个 case 标签的地址并将其存储在字节码本身的流中,而不是我们通常打开的指令 ID。如果这样做,我们可以跳过跳转表,直接跳转到当前正在执行的指令的代码位置。这在 GCC 中效果非常好,但是,MSVC 似乎不支持这样的功能。

我们尝试使用内联汇编来获取标签的地址(并跳转到它们),并且它有效,但是,使用内联汇编会导致 MSVC 优化器避免整个函数。

有没有办法让优化器仍然运行代码?不幸的是,我们无法将内联汇编提取到除创建标签的函数之外的另一个函数中,因为即使在内联汇编中也无法引用另一个函数的标签。有什么想法或想法吗?非常感谢您的意见,谢谢!

c++ assembly inline-assembly goto labeled-statements
4个回答
18
投票

在 MSVC 中执行此操作的唯一方法是使用内联汇编(这对于 x64 来说基本上是个麻烦事):

int _tmain(int argc, _TCHAR* argv[])
{
case_1:
    void* p;
    __asm{ mov [p],offset case_1 }
    printf("0x%p\n",p);
    return 0;
}

如果你打算做这样的事情,那么最好的方法是用汇编语言编写整个解释器,然后通过链接器将其链接到主二进制文件(这就是 LuaJIT 所做的,这也是 VM 不可用的主要原因)当它不运行 JIT 代码时,速度快得令人眼花缭乱)。

LuaJIT 是开源的,所以如果你走这条路,你可能会从中学到一些技巧。或者,您可能想查看第四个的来源(其创建者开发了您正在尝试使用的原则),如果有 MSVC 构建,您可以看到他们是如何完成它的,否则您将陷入 GCC(它不是一件坏事,它适用于所有主要平台)。


10
投票

看看 Erlang 在 Windows 上的构建做了什么。他们在大部分构建中使用 MSVC,然后在一个文件中使用 GCC,以利用标签即值扩展。然后对生成的目标代码进行修改,使其与 MSVC 链接器兼容。

http://www.erlang.org/doc/installation_guide/INSTALL-WIN32.html


4
投票

看来您可以将实际代码移至函数,而不是案例标签。然后可以将字节码简单地转换为直接调用。 IE。字节码 1 将翻译为

CALL BC1
。由于您生成直接调用,因此没有函数指针的开销。大多数CPU的管道都可以遵循这种无条件直接分支。

因此,每个字节码的实际实现都得到了优化,并且从字节码到机器码的转换是一个简单的 1:1 转换。由于每个

CALL
都是 5 个字节(假设 x86-32),因此您会得到一些代码扩展,但这不太可能是一个主要问题。


0
投票

我发现的最佳方法是使用 switch 子句,然后为 switch 子句的每个元素编写 goto 调用。

lablcmd1:
// code
goto dispach;
. . .

dispatch:
op = NextOp();
switch(op){
    case CMD1:
      goto lablcmd1;
    // default: __assume(false); // maybe necessary, verify
}

手动工作,但不使用汇编,看起来与MSVC的兼容性很好。

© www.soinside.com 2019 - 2024. All rights reserved.