为什么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
的包装器。
我想用上面的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
的包装器。