为什么cmov总是return t_val?

Why does cmov always return t_val?

我想用上面的cmov函数实现的结果是,如果pred=true,returnt_val,否则returnf_val。但是在实际操作中,每次t_val都是returned。

#include<stdio.h>
#include <stdint.h>
#include <stdlib.h>
int cmov(uint8_t pred, uint32_t t_val, uint32_t f_val) {
uint32_t result;
 __asm__ volatile (
 "mov %2, %0;"
 "test %1, %1;"
 "cmovz %3, %0;"
 "test %2, %2;"
 : "=r" (result)
 : "r" (pred), "r" (t_val), "r" (f_val)
 : "cc"
 );
 return result;
 }
 
int main()  {  
  
     int a=1,b=4,c=5,d;
    int res = (a==3); //
    printf("res = %d\n",res);
    d = cmov(res,b,c);
    printf("d = %d\n",d);
    a=3;
    res = (a==3);
    d = cmov(res,b,c);
    printf("d = %d\n",d);
return 0;
};  

您在输出 ("=&r") 上遗漏了 early-clobber,因此 GCC 可能会为输出选择与输入之一相同的寄存器,可能 pred。所以 test %1,%1 可能正在测试 t_val (b)。 Single-step 带有调试器的 asm,and/or 查看 GCC 的 asm 输出。 (在 https://godbolt.org/gcc -S 上)。

这看起来效率很低;对输出使用 "+r"(result) 约束(使用 uint32_t result=t_val;),因此您不需要在 asm 模板中使用 mov;让编译器为您完成 result=t_val,可能只需选择相同的寄存器即可。

test %2,%2之后cmoz也是无所事事;您甚至没有使用 GCC6 flag-output 操作数。这是一个完全浪费的指令。

此外,这不一定是 volatile。输出是输入的纯函数,如果输出未使用,则根本不需要 运行。

对于一个cmov使用内联汇编可能是个坏主意;用 -O3 编译并编写你的源代码,这样 GCC 认为将 if-conversion 做成无分支代码是个好主意。内联 asm 破坏了持续传播并破坏了其他优化。这种方式会强制您在模板中使用 test 指令,而不是读取早期 add 或其他任何设置的 FLAGS,并且不允许编译器为多个 cmov 或其他指令重用相同的 FLAGS 结果。 https://gcc.gnu.org/wiki/DontUseInlineAsm

或者,如果您不能 hand-hold 编译器生成您想要的 asm,请在 asm 中编写更多您真正的 use-case,而不仅仅是 cmov 的包装器。