加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

c – __builtin_unreachable有哪些优化方便?

发布时间:2020-12-16 09:22:56 所属栏目:百科 来源:网络整理
导读:从 gcc的文档来看 If control flow reaches the point of the __builtin_unreachable ,the program is undefined. 我认为__builtin_unreachable可以用作各种创造性方式的优化器提示.所以我做了一个小实验 void stdswap(int x,int y){ std::swap(x,y);}void b
从 gcc的文档来看

If control flow reaches the point of the __builtin_unreachable,the program is undefined.

我认为__builtin_unreachable可以用作各种创造性方式的优化器提示.所以我做了一个小实验

void stdswap(int& x,int& y)
{
    std::swap(x,y);
}

void brswap(int& x,int& y)
{
    if(&x == &y)
        __builtin_unreachable();
    x ^= y;
    y ^= x;
    x ^= y;
}

void rswap(int& __restrict x,int& __restrict y)
{
    x ^= y;
    y ^= x;
    x ^= y;
}

gets compiled to(g -O2)

stdswap(int&,int&):
        mov     eax,DWORD PTR [rdi]
        mov     edx,DWORD PTR [rsi]
        mov     DWORD PTR [rdi],edx
        mov     DWORD PTR [rsi],eax
        ret
brswap(int&,DWORD PTR [rdi]
        xor     eax,eax
        xor     eax,DWORD PTR [rsi]
        mov     DWORD PTR [rsi],eax
        xor     DWORD PTR [rdi],eax
        ret
rswap(int&,DWORD PTR [rsi]
        mov     edx,DWORD PTR [rdi]
        mov     DWORD PTR [rdi],eax
        mov     DWORD PTR [rsi],edx
        ret

我假设从优化器的角度来看,stdswap和rswap是最佳的.为什么不将brswap编译成同一个东西?我能用__builtin_unreachable将它编译成同样的东西吗?

解决方法

__builtin_unreachable的目的是帮助编译器删除死代码(程序员知道永远不会被执行)并通过让编译器知道路径是“冷”来线性化代码.考虑以下:

void exit_if_true(bool x);

int foo1(bool x)
{
    if (x) {
        exit_if_true(true);
        //__builtin_unreachable(); // we do not enable it here
    } else {
        std::puts("reachable");
    }

    return 0;
}
int foo2(bool x)
{
    if (x) {
        exit_if_true(true);
        __builtin_unreachable();  // now compiler knows exit_if_true
                                  // will not return as we are passing true to it
    } else {
        std::puts("reachable");
    }

    return 0;
}

生成的代码:

foo1(bool):
        sub     rsp,8
        test    dil,dil
        je      .L2      ; that jump is going to change as branches will be swapped
        mov     edi,1
        call    exit_if_true(bool)
        xor     eax,eax ; that tail is going to be removed
        add     rsp,8
        ret
.L2:
        mov     edi,OFFSET FLAT:.LC0
        call    puts
        xor     eax,eax
        add     rsp,8
        ret
foo2(bool):
        sub     rsp,dil
        jne     .L9
        mov     edi,8
        ret
.L9:
        mov     edi,1
        call    exit_if_true(bool)

注意差异:

> xor eax,eax和ret被删除,因为现在编译器知道这是一个死代码.
>编译器交换了分支的顺序:现在首先是分支与puts调用,因此条件跳转可以更快(即使预测,未采用的分支也更快).

这里的假设是以noreturn函数调用或__builtin_unreachable结尾的分支将只执行一次或导致longjmp调用或异常抛出,这两种情况都很少见,并且在优化期间不需要优先处理.

您正在尝试将其用于不同的目的 – 通过提供有关别名的编译器信息(您可以尝试对齐进行相同操作).不幸的是,GCC不理解这种地址检查.

正如您所注意到的那样,添加__restrict__会有所帮助.所以__restrict__适用于别名,__ builtin_unreachable不适用.

请看以下使用__builtin_assume_aligned的示例:

void copy1(int *__restrict__ dst,const int *__restrict__ src)
{
    if (reinterpret_cast<uintptr_t>(dst) % 16 == 0) __builtin_unreachable();
    if (reinterpret_cast<uintptr_t>(src) % 16 == 0) __builtin_unreachable();

    dst[0] = src[0];
    dst[1] = src[1];
    dst[2] = src[2];
    dst[3] = src[3];
}

void copy2(int *__restrict__ dst,const int *__restrict__ src)
{
    dst = static_cast<int *>(__builtin_assume_aligned(dst,16));
    src = static_cast<const int *>(__builtin_assume_aligned(src,16));

    dst[0] = src[0];
    dst[1] = src[1];
    dst[2] = src[2];
    dst[3] = src[3];
}

生成的代码:

copy1(int*,int const*):
        movdqu  xmm0,XMMWORD PTR [rsi]
        movups  XMMWORD PTR [rdi],xmm0
        ret
copy2(int*,int const*):
        movdqa  xmm0,XMMWORD PTR [rsi]
        movaps  XMMWORD PTR [rdi],xmm0
        ret

您可以假设编译器可以理解dst%16 == 0表示指针是16字节对齐的,但事实并非如此.因此使用未对齐的存储和加载,而第二个版本生成更快的指令,需要对齐地址.

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读