【问题标题】:Conditional move instruction on a NULL check before dereferencing a pointer [duplicate]在取消引用指针之前进行 NULL 检查的条件移动指令 [重复]
【发布时间】:2019-02-06 16:22:53
【问题描述】:

我正在研究 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(即第 8 代 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

有没有可能有这样一个编译成条件移动而不跳转的函数?


编辑:正如cmets所说,CMOVxx系列指令无条件加载操作数,只有实际的赋值操作是有条件的,所以放*p(或@ 987654332@)作为CMOV的源操作数,对吗?

声明在this page,但我认为它无效。

【问题讨论】:

  • 优化无效,因为 cmov 从其源操作数执行无条件加载,即使最终没有被使用也会出错。
  • 编辑后的代码没有解决。即使条件为假,也会访问源位置。
  • @iBug 喜欢 any cmov?对于一些 C 代码?这里:godbolt.org/z/UkonTX
  • @iBug 您更新的代码不会在这里为我生成cmovegodbolt.org/z/C_cGjB,但它与-O1 一起生成。另请注意,您不需要 z 成为 static
  • 除了 fuz 和其他人指出的正确性问题之外,即使您可以使用条件移动,也不太可能对简单分支进行优化。为什么?分支预测。只要分支是可预测的,并且空指针检查是完全可预测的,现代处理器上的分支预测就会对您有利。您会发现英特尔的编译器更喜欢在这种情况下只进行测试和分支,即使它可以使用条件移动。

标签: c gcc assembly conditional


【解决方案1】:

这是一个无分支版本:

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-07-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-10-30
    • 2020-07-14
    • 1970-01-01
    相关资源
    最近更新 更多