我正在研究CSAPP的练习3.61,该练习要求编写一个非常简单的函数,以在尝试取消引用之前检查指针是否为NULL
,这应该基于条件移动指令而不是跳转。这是我在网上找到的示例:
long cond(long* p) {
return (!p) ? 0 : *p;
}
根据声明,该函数可以编译为以下程序集:
cond:
xor eax, eax
test rdi, rdi
cmovne rax, QWORD PTR [rdi]
ret
我正在WSL的Ubuntu 18.04上运行GCC 7.3.0(来自APT软件包gcc/bionic-updates,now 4:7.3.0-3ubuntu2.1 amd64
)。该计算机在Intel Coffee Lake(即第八代Core-i)处理器上运行。
我尝试了以下命令:
gcc -S a.c -O3
gcc -S a.c -O3 -march=x86-64
gcc -S a.c -O3 -march=core2
gcc -S a.c -O3 -march=k8
老实说,我无法观察到生成的a.s
文件中的任何差异,因为它们看起来都一样
cond:
xorl %eax, %eax
testq %rdi, %rdi
je .L1
movq (%rdi), %rax
.L1:
ret
是否有可能将这种功能编译为有条件的动作而不会跳转?
编辑:如评论中所述,CMOVxx系列指令无条件地加载操作数,并且只有实际的赋值操作是有条件的,因此运气*p
(或(%rdi)
)作为CMOV的源操作数,对吗?
有关索赔this page,但我认为这是无效的。
答案 0 :(得分:0)
这是无分支版本:
inline long* select(long* p, long* q) {
uintptr_t a = (uintptr_t)p;
uintptr_t b = (uintptr_t)q;
uintptr_t c = !a;
uintptr_t r = (a & (c - 1)) | (b & (!c - 1));
return (long*)r;
}
long cond(long* p) {
long t = 0;
return *select(p, &t);
}
gcc-8.2的组装:
cond(long*):
mov QWORD PTR [rsp-8], 0
xor eax, eax
test rdi, rdi
sete al
lea rdx, [rax-1]
neg rax
and rdi, rdx
lea rdx, [rsp-8]
and rax, rdx
or rdi, rax
mov rax, QWORD PTR [rdi]
ret
组装clang-7:
cond(long*): # @cond(long*)
mov qword ptr [rsp - 8], 0
xor eax, eax
test rdi, rdi
lea rcx, [rsp - 8]
cmovne rcx, rax
or rcx, rdi
mov rax, qword ptr [rcx]
ret