将 0 映射到任何非零值同时保留其他值的无分支方式?

Branchless way to map 0 to any nonzero value while leaving other values alone?

我正在尝试找到该函数的最快实现方式:

uint16_t forbid_zero(uint16_t x)
{
    if(x == 0)
        return SOMETHING_NONZERO;
    return x;
}

SOMETHING_NONZERO 是什么并不重要,只要它不为零即可。除零以外的任何值都应不加修改地通过。这样做最快的位黑客是什么?我假设有一些不错的无分支方式。

对于上下文,我在我的关键路径中有一个算法,其中零作为输入值将触发无限循环和其他不良行为,我很好奇我是否可以将输入修改为始终为非零而不分支检查 0。将不正确的非零值传递给算法的后果几乎没有那么糟糕;该错误将被其他层已经存在的检查发现,因此将 0 映射到任何其他值就足够了。

一种可能的实现方式是:

uint16_t forbid_zero(uint16_t x)
{
    return x | !x;
}

Compile Explorer 显示 x86-64 gcc 8.2 compiles to:

forbid_zero(unsigned short):
        xorl    %eax, %eax
        testw   %di, %di
        sete    %al
        orl     %edi, %eax
        ret

然而,即使您在问题 compiles to branchless code using a conditional move instruction 中使用相同的编译器提供的实现:

forbid_zero(unsigned short):
        testw   %di, %di
        movl    , %eax
        cmovne  %edi, %eax
        ret

...当然也不能保证 !x 不会被编译到分支。