4

RBバイトのスワッピングをABGR単語に実装する単純な試みとして、次のコードを試しました

#include <stdio.h>
#include <stdint.h>

uint32_t ABGR_to_ARGB(uint32_t abgr)
{
  return ((abgr ^= (abgr >> 16) & 0xFF) ^= (abgr & 0xFF) << 16) ^= (abgr >> 16) & 0xFF;
}

int main() 
{
    uint32_t tmp = 0x11223344;
    printf("%x %x\n", tmp, ABGR_to_ARGB(tmp));
}

驚いたことに、このコードは C++17 モードの GCC で「機能」しました - バイトがスワップされました

http://coliru.stacked-crooked.com/a/43d0fc47f5539746

しかし、バイトをスワップすることは想定されていません! C++17 では、代入の RHS が LHS の前に [完全に] シーケンスされることになっていることが明確に述べられており、これは複合代入にも適用されます。これは、上記の式で、それぞれの各 RHS が^=の元の値を使用することになっていることを意味しますabgr。したがって、最終的な結果は、abgr単純にBバイトごとに XOR されたRバイトである必要があります。これは、Clang が生成するように見えるものです (面白いことに、シーケンスの警告が表示されます)。

http://coliru.stacked-crooked.com/a/eb9bdc8ced1b5f13

GCC アセンブリの概要

https://godbolt.org/g/1hsW5a

それが逆にシーケンスされているように見えることを明らかにします: RHS の前に LHS です。これはバグですか?それとも、これはGCC側のある種の意識的な決定ですか? それとも私は何かを誤解していますか?

4

2 に答える 2

0

必要以上に複雑にしないでください。言語の暗い隅に自分自身を描くことなく、かなり簡単な方法で 2 つのコンポーネントを交換できます。

uint32_t ABGR_to_ARGB(uint32_t abgr) {
    constexpr uint32_t mask = 0xff00ff00;
    uint32_t grab = abgr >> 16 | abgr << 16;
    return (abgr & mask) | (grab & ~mask);
}

また、元のバージョンよりもはるかに優れたアセンブリを生成します。x86 ではrol、3 つのビット単位の演算子に単一の命令を使用して生成しgrabます。

ABGR_to_ARGB(unsigned int):
        mov     eax, edi
        and     edi, -16711936
        rol     eax, 16
        and     eax, 16711935
        or      eax, edi
        ret
于 2018-07-25T09:59:51.043 に答える