少しの x86 インライン アセンブリ (GNU C 構文) を気にしない場合は、完全な 33 ビットの結果の上位 32 ビットをレジスタに入れるために、加算の後に、rotate-with-carryを使用するという supercat の提案を利用できます。.
もちろん、通常はinline-asm の使用を気にする必要があります。これは、いくつかの最適化を無効にするためです ( https://gcc.gnu.org/wiki/DontUseInlineAsm )。しかし、とにかくここに行きます:
// works for 64-bit long as well on x86-64, and doesn't depend on calling convention
unsigned average(unsigned x, unsigned y)
{
unsigned result;
asm("add %[x], %[res]\n\t"
"rcr %[res]"
: [res] "=r" (result) // output
: [y] "%0"(y), // input: in the same reg as results output. Commutative with next operand
[x] "rme"(x) // input: reg, mem, or immediate
: // no clobbers. ("cc" is implicit on x86)
);
return result;
}
%
引数が交換可能であることをコンパイラに伝える修飾子は、定数またはポインター deref (メモリオペランド) である y を使用して関数を呼び出して試した場合、実際にはより良い asm を作成するのに役立ちません。おそらく、出力オペランドに一致する制約を使用すると、読み取り/書き込みオペランドでは使用できないため、それが無効になります。
Godbolt コンパイラーの explorerでわかるように、これは正しくコンパイルさunsigned long
れ、同じインライン asm でオペランドを に変更したバージョンも同様にコンパイルされます。ただし、clang3.9 はそれを混乱させ"m"
、制約にオプションを使用することを決定するため"rme"
、メモリに格納してメモリ オペランドを使用します。
RCR-by-one はそれほど遅くはありませんが、Skylake では 2 サイクルのレイテンシで 3 uops です。これは、RCR のレイテンシが 1 サイクルである AMD CPU に最適です。(出典: Agner Fog の命令表、x86 パフォーマンス リンクについてはx86タグ wikiも参照してください)。@sellibitze のバージョンよりも優れていますが、@Sheldon の順序依存バージョンよりも劣っています。(Godbolt のコードを参照)
ただし、inline-asm は定数伝播などの最適化を無効にするため、その場合は純粋な C++ バージョンの方が優れていることに注意してください。