【发布时间】:2020-08-25 13:32:26
【问题描述】:
假设我有一些内联程序集需要 ah、bh、ch 或 dh 中的特定 char 值。我怎样才能告诉 GCC 把它放在那里?我没有看到相关的约束,但是 GCC 手册说“如果您必须使用特定的寄存器,但您的机器约束没有提供足够的控制来选择您想要的特定寄存器,本地寄存器变量可能会提供一个解决方案",所以我试了一下:
void f(char x) {
register char y __asm__("ah") = x;
__asm__ __volatile__(
"# my value ended up in %0" :: "a"(y)
);
}
但它没有用。它把它放在al 中:
movb 4(%esp), %al
# my value ended up in %al
x86 特定的Q 约束看起来也接近我想要的,所以我尝试用它代替a,但结果相同。我还尝试了更通用的r。
有趣的是,当我使用 Clang 而不是 GCC(无论是使用 a、Q 还是 r)进行编译时,我确实得到了想要的结果:
movb 4(%esp), %ah
# my value ended up in %ah
我还尝试用bh、ch 和dh 代替ah,它们的每一种组合都会产生类似的结果。
我还尝试编译为 64 位而不是 32 位。在那里,GCC 仍然做着基本相同的错误:
movl %edi, %eax
# my value ended up in %al
并且 Clang 完全无法使用 Cannot encode high byte register in REX-prefixed instruction 进行编译,除非我关闭了优化(我打开了 LLVM bug #45865 关于),在这种情况下,它最终确实在正确的位置获得了值:
movb %dil, -1(%rsp)
movb -1(%rsp), %al
movb %al, -2(%rsp)
movb -2(%rsp), %ah
# my value ended up in %ah
这是我应该报告的 GCC 中的错误,还是不应该工作而只能在 Clang 中偶然工作的东西?如果是后者,有没有办法做我想做的事,还是我必须自己在程序集中的其他地方接受moving?
【问题讨论】:
-
不应该是
"r"(y)吗? -
更糟糕的是,如果你同时拥有
ah和al... gcc 会默默地忽略其中一个。 -
如果你在EAX中要求
(int)y<<8,你可以在模板中使用%h0和%b0扩展为AH和AL。 (如果 GCC 选择了 EBX,则为 BH 和 BL)。 gcc.gnu.org/onlinedocs/gcc/…。"Q"约束确保%h0修饰符可以通过选择 E[ABCD]X 之一来工作。这就是为什么存在 Q 约束,而不是让 GCC 自己真正pick AH。我希望它与register char foo asm("ah");兼容,但这是另一回事。 -
re: clang: 启用优化我假设它试图发出不可编码的
mov %dil, %ah。 (DIL 需要 REX;AH 只能在没有 REX 的情况下进行编码)。任何 ICE 都可以被认为是一个错误,但是当register asm变量引发它,或者使 clang 像 GCC 一样工作并选择 AL 而不是 AH 时,错误修复可能只是一个更好的警告!或者可能发出rorx $24, %edx, %eax以在一条 BMI2 指令中将 DIL 转换为 AH,同时避免 REX 问题。 -
这完全偏离了另一个方向,但我很好奇是什么迫使你在 AH 中加入一些东西。我可以想到一个用例,但这与 16 位实模式有关。尽管由于错误/缺陷,您无法直接加载 AH 并限制 GCC,但您始终可以在内联程序集中将某些内容移动到 AH 中。或者像彼得提到的那样简单地使用 EAX 并存储
(int)y<<8。你问的是可行的,但不是你想要的直接(有效地)。我不得不处理这个问题的地方是使用内联汇编来进行 BIOS 调用。
标签: c gcc assembly x86 inline-assembly