3

次のコードがあります。

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

long long lzcnt(long long l)
{
    return __lzcnt64(l);
}

int main(int argc, char** argv)
{
    printf("%lld\n", lzcnt(atoll(argv[1])));
    return 0;
}

私が得るさまざまなコンパイラとオプションで実行しています(アセンブリを示しています):

クラン

$ clang -Wall src/test.c -D__LZCNT__ && ./a.out 2047
53

0000000000400560 <lzcnt>:
400560:   55                      push   %rbp
400561:   48 89 e5                mov    %rsp,%rbp
400564:   48 89 7d f0             mov    %rdi,-0x10(%rbp)
400568:   48 8b 7d f0             mov    -0x10(%rbp),%rdi
40056c:   48 89 7d f8             mov    %rdi,-0x8(%rbp)
400570:   48 8b 7d f8             mov    -0x8(%rbp),%rdi
400574:   48 0f bd ff             bsr    %rdi,%rdi
400578:   48 83 f7 3f             xor    $0x3f,%rdi
40057c:   89 f8                   mov    %edi,%eax
40057e:   48 63 c0                movslq %eax,%rax
400581:   5d                      pop    %rbp
400582:   c3                      retq   
400583:   66 66 66 66 2e 0f 1f    data32 data32 data32 nopw %cs:0x0(%rax,%rax,1)
40058a:   84 00 00 00 00 00 

-mlzcnt なしの GCC

$ gcc -Wall src/test.c -D__LZCNT__ && ./a.out 2047
53

0000000000400580 <lzcnt>:
400580: 55                    push   %rbp
400581: 48 89 e5              mov    %rsp,%rbp
400584: 48 89 7d e8           mov    %rdi,-0x18(%rbp)
400588: 48 8b 45 e8           mov    -0x18(%rbp),%rax
40058c: 48 89 45 f8           mov    %rax,-0x8(%rbp)
400590: 48 0f bd 45 f8        bsr    -0x8(%rbp),%rax
400595: 48 83 f0 3f           xor    $0x3f,%rax
400599: 48 98                 cltq   
40059b: 5d                    pop    %rbp
40059c: c3                    retq   

-mlzcnt を使用した GCC

$ gcc -Wall src/test.c -D__LZCNT__ -mlzcnt && ./a.out 2047
10

0000000000400580 <lzcnt>:
400580: 55                    push   %rbp
400581: 48 89 e5              mov    %rsp,%rbp
400584: 48 89 7d e8           mov    %rdi,-0x18(%rbp)
400588: 48 8b 45 e8           mov    -0x18(%rbp),%rax
40058c: 48 89 45 f8           mov    %rax,-0x8(%rbp)
400590: f3 48 0f bd 45 f8     lzcnt  -0x8(%rbp),%rax
400596: 48 98                 cltq   
400598: 5d                    pop    %rbp
400599: c3                    retq   

-mlzcnt なしの G++

$ g++ -Wall src/test.c -D__LZCNT__ && ./a.out 2047
In file included from /usr/lib/gcc/x86_64-redhat-linux/4.8.2/include/immintrin.h:64:0,
                 from /usr/lib/gcc/x86_64-redhat-linux/4.8.2/include/x86intrin.h:62,
                 from src/test.c:3:
/usr/lib/gcc/x86_64-redhat-linux/4.8.2/include/lzcntintrin.h: In function ‘short unsigned int __lzcnt16(short unsigned int)’:
/usr/lib/gcc/x86_64-redhat-linux/4.8.2/include/lzcntintrin.h:38:29: error: ‘__builtin_clzs’ was not declared in this scope
return __builtin_clzs (__X);

-mlzcnt を使用した G++

$ g++ -Wall src/test.c -D__LZCNT__ -mlzcnt  && ./a.out 2047
10

0000000000400640 <_Z5lzcntx>:
400640: 55                    push   %rbp
400641: 48 89 e5              mov    %rsp,%rbp
400644: 48 89 7d e8           mov    %rdi,-0x18(%rbp)
400648: 48 8b 45 e8           mov    -0x18(%rbp),%rax
40064c: 48 89 45 f8           mov    %rax,-0x8(%rbp)
400650: f3 48 0f bd 45 f8     lzcnt  -0x8(%rbp),%rax
400656: 48 98                 cltq   
400658: 5d                    pop    %rbp
400659: c3                    retq   

違いは明らかに -mlzcnt の使用ですが、私は実際に C++ で作業しており、そのオプションがないと g++ でコンパイルされません (clang++ は問題ありません)。-mlzcnt を使用すると、結果は 63-(-mlzct なしの結果) のように見えます。gcc の -mlzcnt オプションに関するドキュメントはありますか (情報ファイルを調べましたが、何も見つかりませんでした)。lzcnt 命令を選択する以上のことはありますか?

4

2 に答える 2

5

まず、clang 3.3 と gcc 4.8.1 の両方で問題を完全に再現できます。

これが私の考えです...私はこれについて約50%しかありません。

  • LZCNT は、お使いのコンピューターでサポートされていない可能性がある命令です。
  • ウィキペディアは、LZCNT には Haswell のサポートが必要であることを示唆しています
  • Linux アプリケーションcpuidを使用して、この情報を確認することができます。(これは Debian、RHEL などに含まれています)。
  • ウィキペディアは、「サポートは CPUID.80000001H:ECX.ABM[Bit 5] フラグを介して示される」ことを再度示唆しています。

私のシステム (Xeon X3430、Lynnfield、Nehalem) を見てみましょう。

[4:48pm][wlynch@apple /tmp] sudo cpuid -1ir | grep 80000001
   0x80000001 0x00: eax=0x00000000 ebx=0x00000000 ecx=0x00000001 edx=0x28100800

したがって、ECX のビット 23 は true ではありません。そのため、私のシステムは LZCNT をサポートしていません。

また、私のマシンがサポートされていない LZCNT を BSR として解釈することもたまたまあるようです。

于 2013-10-30T21:49:28.790 に答える
1

__lzcnt64 を呼び出しているようですが、32 ビット整数を渡しています。おそらく、それはコンパイラを混乱させています。

おそらく、10 を返す人は、レジスターの残りの半分にがらくたを見ているのでしょうか?

代わりにこれを試してください:

    long long int v = __lzcnt64(2047LL);

long longリテラルにしました)。

于 2013-10-30T11:31:29.907 に答える