在内联程序集中使用函数范围的标签

Using function-scoped labels in inline assembly

本文关键字:函数 范围 标签 集中 程序 程序集      更新时间:2023-10-16

我在64位小端Ubuntu 12.04 LTS上使用gcc-4.7和Eclipse CDT和gdb,在C++中玩内联汇编。我尝试做的事情的总体方向是为一些深奥的基于堆栈的编程语言制作某种字节码解释器。

在这个例子中,我一次处理4位指令(在实践中,这将取决于指令),当没有更多的非零指令时(因为0将是nop),我读取接下来的64位字。

不过,我想问一下,如何在内联程序集中使用函数范围的标签

汇编中的标签似乎是全局的,这是不利的,而且我找不到从汇编语句跳到C++函数范围的标签的方法。

以下代码是我尝试做的一个示例(注意注释):

...
register long ip  asm("r8");
register long buf asm("r9");
register long op  asm("r10");
...
fetch:
asm("mov (%r8), %r9");
asm("add $8, %r8");
control:
asm("test %r9, %r9");
asm("jz   fetch"); // undefined reference to `fetch'
asm("shr  $4, %r9");
asm("mov  %r9, %r10");
asm("and  $0xf, %r10");
switch (op) {
...
}
goto control;

注意gcc内联asm文档中的以下注释:

说到标签,不支持从一个"asm"跳到另一个。编译器的优化器不知道这些跳跃,因此在决定如何优化时,他们不能将它们考虑在内。

您也不能依赖于在一个asm中设置的标志在下一个中可用,因为编译器可能会在它们之间插入一些

使用gcc 4.5及更高版本,您可以使用asm goto执行您想要的操作:

fetch:
asm("mov (%r8), %r9");
asm("add $8, %r8");
control:
asm goto("test %r9, %r9nt"
"jz  %l[fetch]" : : : : fetch);

请注意,您的asm的所有其余部分都是完全不安全的,因为它直接使用寄存器,而没有在读/写/删除列表中声明它们,因此编译器可能会决定在其中放入其他内容(尽管vars上有asm声明——它可能会决定这些寄存器已经死了,因为它们从未被使用过)。因此,如果你希望这能真正与-O1或更高版本一起使用,你需要将其写成:

...
long ip;
long buf;
long op;
...
fetch:
asm("mov (%1), %0" : "=r"(buf) : "r"(ip));
asm("add $8, %0" : "=r"(ip) : "0"(ip));
control:
asm goto("test %0, %0nt"
"jz   %l[fetch]" : : "r"(buf) : : fetch);
asm("shr  $4, %0" : "=r"(buf) : "0"(buf));
asm("mov  %1, %0" : "=r"(op) : "r"(buf));
asm("and  $0xf, %0" : "=r"(op) : "r"(op));

在这一点上,只将其写成C代码要容易得多:

long *ip, buf, op;
fetch:
do {
buf = *op++;
control:
} while (!buf);
op = (buf >>= 4) & 0xf;
switch(op) {
:
}  
goto control;

您应该能够做到这一点:

fetch:
asm("afetch: mov(%r8), %r9");
...
asm("jz afetch");

或者,将标签放在单独的asm("afetch:");中也可以。注意不同的名称以避免冲突-我不完全确定这是否有必要,但我怀疑是的。