如何让GCC在ah/bh/ch/dh中放置字符?

如何让GCC在ah/bh/ch/dh中放置字符?,c,gcc,assembly,x86,inline-assembly,C,Gcc,Assembly,X86,Inline Assembly,假设我有一些内联程序集,需要在ah、bh、ch或dh中使用特定的char值。我怎么能告诉GCC把它放在那里?我看不出相关的约束可以做到这一点,但GCC手册说,如果您必须使用特定寄存器,但您的机器约束不能提供足够的控制来选择所需的特定寄存器,本地寄存器变量可能会提供解决方案,因此我尝试: 无效fchar x{ 寄存器字符y\uuuu asm\uuuuu ah=x; __asm\uuuuuuuuuuuuuuuuuu挥发性__ 我的值以%0::ay结束 ; } 但它不起作用。它将其放在al中: x86

假设我有一些内联程序集,需要在ah、bh、ch或dh中使用特定的char值。我怎么能告诉GCC把它放在那里?我看不出相关的约束可以做到这一点,但GCC手册说,如果您必须使用特定寄存器,但您的机器约束不能提供足够的控制来选择所需的特定寄存器,本地寄存器变量可能会提供解决方案,因此我尝试:

无效fchar x{ 寄存器字符y\uuuu asm\uuuuu ah=x; __asm\uuuuuuuuuuuuuuuuuu挥发性__ 我的值以%0::ay结束 ; } 但它不起作用。它将其放在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完全无法编译,无法在REX前缀指令中对高字节寄存器进行编码,除非我关闭了我打开的优化,在这种情况下,它最终得到了正确位置的值:

        movb    %dil, -1(%rsp)
        movb    -1(%rsp), %al
        movb    %al, -2(%rsp)
        movb    -2(%rsp), %ah
        # my value ended up in %ah
这是我应该报告的GCC中的一个bug,还是不应该工作的东西,只是在叮当声中偶然工作?如果是后者,是否有办法做我想做的事情,或者我必须满足于自己从大会内部的其他地方把它搬到那里


显然,约束不允许选择嵌套寄存器,但可以向指令引用添加h修饰符。上的文档中提到了这一点。比如说,

无效fchar x{ 字符a; __asm\uuuuuuuuuuuuuuuuuu挥发性__ mov%0,%h1::Xx,aa ; } 产生

f:
        xorl    %eax, %eax
        mov  4(%esp), %ah
        ret
我一直无法摆脱清除eax的xor。我猜代码生成器正在将%h1解释为设置了8位的32位字,而不是字符寄存器引用。例如,这:

char f(char x) {
    char a;
    __asm__ __volatile__(
        "movb  %0, %h1" :: "X"(x), "a"(a)
    );
    return a;
}

。。。编译为相同的代码,即使它返回\0,也不是很直观。

它不应该是ry吗?更糟糕的是,如果您同时拥有ah和al。。。gcc会默默地忽略其中一个。如果您要求输入intyre:clang:with optimization enabled,我假设它试图发出mov%dil,%ah,这是不可编码的。迪尔需要一只雷克斯;AH只有在没有REX的情况下才可以编码。任何ICE都可能被认为是一个bug,但是当寄存器asm变量触发它时,这个bug修复可能只是一个更好的警告,或者让叮当像GCC一样工作,并选择AL而不是AH!或者可能发出rorx$24、%edx、%eax,以便在一条BMI2指令中将DIL放入AH,同时避免REX问题。这完全是在另一个方向上,但我很好奇是什么迫使您在AH中放入某些内容。我可以想到一个用例,但它与16位实模式有关。尽管由于bug/缺陷,您无法直接加载带有GCC约束的AH,但您始终可以在内联程序集中将某些内容移动到AH中。或者简单地使用EAX和store INTYY您的第二个C代码段看起来像是直接的未定义行为,因为它返回的是a,而从未设置过。您的示例没有太大意义,因为您将两个寄存器都作为输入操作数。但无论如何,寄存器修饰符会导致生成的程序集引用%ah,但编译器仍然不知道,并将尝试将输入值加载到%al中。您无法在示例中看到这一点,因为您没有初始化。但是如果您确实初始化它,如中所示,您会看到生成的代码不是您想要的。换句话说,我认为%h1对于代码生成器来说是完全不透明的。编译器生成周围的代码,就像您刚刚编写了纯%1一样,然后只更改程序集中寄存器的名称。因此,这并不能解决问题。我认为寄存器修饰符功能适用于将值加载到32位寄存器中,但程序集只想对其中的一部分进行操作的情况。例如,无符号a=…,x;asmmovb$0x5a,%h0:=Q x:0 a将是在一条指令中实现x=a&0xffff00ff | 0x5a00的有效方法。@NateEldredge mov指令集a。
char f(char x) {
    char a;
    __asm__ __volatile__(
        "movb  %0, %h1" :: "X"(x), "a"(a)
    );
    return a;
}