在GCC风格的扩展内联asm中,是否可以输出"虚拟化"布尔值,例如进位标志?

Dea*_*ode 5 assembly flags gcc boolean inline-assembly

如果我有以下C++代码来比较两个128位无符号整数,使用内联amd-64 asm:

struct uint128_t {
    uint64_t lo, hi;
};
inline bool operator< (const uint128_t &a, const uint128_t &b)
{
    uint64_t temp;
    bool result;
    __asm__(
        "cmpq %3, %2;"
        "sbbq %4, %1;"
        "setc %0;"
        : // outputs:
        /*0*/"=r,1,2"(result),
        /*1*/"=r,r,r"(temp)
        : // inputs:
        /*2*/"r,r,r"(a.lo),
        /*3*/"emr,emr,emr"(b.lo),
        /*4*/"emr,emr,emr"(b.hi),
        "1"(a.hi));
    return result;
}
Run Code Online (Sandbox Code Playgroud)

然后它将非常有效地内联,但有一个缺陷.返回值通过通用寄存器的"接口"完成,值为0或1.这会增加两个或三个不必要的额外指令,并减少比较操作,否则将完全优化.生成的代码看起来像这样:

    mov    r10, [r14]
    mov    r11, [r14+8]
    cmp    r10, [r15]
    sbb    r11, [r15+8]
    setc   al
    movzx  eax, al
    test   eax, eax
    jnz    is_lessthan
Run Code Online (Sandbox Code Playgroud)

如果我使用带有"bo"返回值的"sbb%0,%0"而不是带有"bool"返回值的"setc%0",那么还有两个额外的指令:

    mov    r10, [r14]
    mov    r11, [r14+8]
    cmp    r10, [r15]
    sbb    r11, [r15+8]
    sbb    eax, eax
    test   eax, eax
    jnz    is_lessthan
Run Code Online (Sandbox Code Playgroud)

我想要的是这个:

    mov    r10, [r14]
    mov    r11, [r14+8]
    cmp    r10, [r15]
    sbb    r11, [r15+8]
    jc     is_lessthan
Run Code Online (Sandbox Code Playgroud)

GCC扩展内联asm非常好,否则.但我希望它在各方面都能像内在函数一样好.我希望能够以CPU标志或标志的状态直接返回一个布尔值,而不必将其"渲染"到通用寄存器中.

这是可能的,还是GCC(以及英特尔C++编译器,也允许使用这种形式的内联asm)必须进行修改甚至重构才能实现?

此外,虽然我在这里 - 有没有其他方式我可以改进比较运算符的表述?

Dav*_*erd 7

在这里,我们将近 7 年后,是的,gcc 终于添加了对“输出标志”的支持(在 6.1.0 中添加,于 2016 年 4 月发布)。详细的文档在这里,但简而言之,它看起来像这样:

/* Test if bit 0 is set in 'value' */
char a;

asm("bt $0, %1"
    : "=@ccc" (a)
    : "r" (value) );

if (a)
   blah;
Run Code Online (Sandbox Code Playgroud)

要理解=@ccc:输出约束(需要=)的类型@cc后跟要使用的条件代码(在这种情况下c引用进位标志)。

好的,对于您的特定情况,这可能不再是问题(因为 gcc 现在支持直接比较 128 位数据类型),但是(目前)有 1,326 人查看了这个问题。显然有人对此功能感兴趣。

现在我个人赞成那种说根本不要使用内联汇编的思想流派。但是如果你必须,是的,你可以(现在)'输出'标志。

FWIW。


and*_*fff 6

我不知道有什么方法可以做到这一点。您可能会也可能不会认为这是一种改进:

inline bool operator< (const uint128_t &a, const uint128_t &b)
{
    register uint64_t temp = a.hi;
    __asm__(
        "cmpq %2, %1;"
        "sbbq $0, %0;"
        : // outputs:
        /*0*/"=r"(temp)
        : // inputs:
        /*1*/"r"(a.lo),
        /*2*/"mr"(b.lo),
        "0"(temp));

    return temp < b.hi;
}
Run Code Online (Sandbox Code Playgroud)

它产生类似的东西:

mov    rdx, [r14]
mov    rax, [r14+8]
cmp    rdx, [r15]
sbb    rax, 0
cmp    rax, [r15+8]
jc is_lessthan
Run Code Online (Sandbox Code Playgroud)