2017-01-10 5 views
6

私はコンパイラエクスプローラで遊んでいましたが、これらの2つの関数がgccとclangの両方で異なるアセンブリを生成することがわかりました。私は、インライン化後、彼らは同一の発現樹を生成し、したがって同一で最適なアセンブリを生成すると予想した。これはオプティマイザのクルークですか、または最適化を禁じる言語ルールの結果ですか?

constexpr bool is_nonzero_decimal_digit(char const c) noexcept 
{ 
    return c == '1' || c == '2' || c == '3' || c == '4' || c == '5' 
     || c == '6' || c == '7' || c == '8' || c == '9'; 
} 

bool is_decimal_digit_v1(char const c) noexcept 
{ 
    return c == '0' || is_nonzero_decimal_digit(c); 
} 

bool is_decimal_digit_v2(char const c) noexcept 
{ 
    return c == '0' || c == '1' || c == '2' || c == '3' || c == '4' 
     || c == '5' || c == '6' || c == '7' || c == '8' || c == '9'; 
} 

クラン3.9.1 -std = C++ 1Z -03結果

is_decimal_digit_v1(char): 
    cmp  dil, 48 
    sete cl 
    add  dil, -49 
    cmp  dil, 9 
    setb al 
    or  al, cl 
    ret 

is_decimal_digit_v2(char): 
    add  dil, -48 
    cmp  dil, 10 
    setb al 
    ret 

GCC 6.3は、C++ 1Z -03結果

is_decimal_digit_v1(char): 
    cmp  dil, 48 
    je  .L3 
    sub  edi, 49 
    cmp  dil, 8 
    setbe al 
    ret 
.L3: 
    mov  eax, 1 
    ret 

is_decimal_digit_v2(char): 
    sub  edi, 48 
    cmp  dil, 9 
    setbe al 
    ret 

だから、=であり、-stdこれはオプティマイザの奇抜なこと、または最適化を禁じる言語規則の結果ですか?

+4

おそらく、OR'd比較を1つの減算+比較にマージしたパスは、インライン化パスの前に実行されました。 –

+3

[gcc 7.0スナップショットは、それらを同じコードに最適化します](https://godbolt.org/g/iPXtEi)。私はT.C.正しい。 – Cornstalks

答えて

3

gcc < 7.0とclangオプティマイザの特質です。 Cornstalksがコメントに指摘しているように、gcc 7.0は最適なアセンブリを生成することができます。 VC++ 2015もチェックしました。

is_decimal_digit_v2: 
    sub cl, 48 
    cmp cl, 9 
    setbe al 
    ret 0 
is_decimal_digit_v1: 
    sub cl, 48 
    cmp cl, 9 
    setbe al 
    ret 0 

as T.C.いくつかの最適化パスの後でインライン展開が実行されます。この最適化パスでは、この特定のコードでは、一連の比較がより簡単な範囲チェックにマージされます。インライン化する前にリーフ関数を小さくすると、インライン化の可能性が増します。 V2は非常に単純な形式に変換したのに対し、

bool is_decimal_digit_v3(char const c) noexcept 
{ 
    if (c == 48) return true; 
    // this is what was inlined 
    char tmp = c - 49; 
    return tmp >= 0 && tmp < 9; 
} 

:v3の

bool is_decimal_digit_v4(char const c) noexcept 
{ 
    char tmp = c - 48; 
    return tmp >= 0 && tmp < 10; 
} 

生成されたアセンブリは、V1

#clang 3.9.1 
is_decimal_digit_v3(char):    # @is_decimal_digit_v3(char) 
    cmp  dil, 48 
    sete cl 
    add  dil, -49 
    cmp  dil, 9 
    setb al 
    or  al, cl 
    ret 
# gcc 6.3 
is_decimal_digit_v3(char): 
    cmp  dil, 48 
    je  .L8 
    sub  edi, 49 
    cmp  dil, 8 
    setbe al 
    ret 
.L8: 
    mov  eax, 1 
    ret 
のために生成1に似ている基本的には、v1の機能は、このような何かに形質転換しました

私は、v3をv4に変換するには、gcc 7.0が行うことができる簡単ではない分析が必要だと思います。このバージョンでは、すべての4つのスニペットのために絶対に同じアセンブリを生成します。

is_decimal_digit_v1(char): 
    sub  edi, 48 
    cmp  dil, 9 
    setbe al 
    ret 
is_decimal_digit_v2(char): 
    sub  edi, 48 
    cmp  dil, 9 
    setbe al 
    ret 
is_decimal_digit_v3(char): 
    sub  edi, 48 
    cmp  dil, 9 
    setbe al 
    ret 
is_decimal_digit_v4(char): 
    sub  edi, 48 
    cmp  dil, 9 
    setbe al 
    ret 

VC++ 2015はV4にV3を変換することができず、このアセンブリを生成することは興味深いです:

is_decimal_digit_v3: 
    cmp cl, 48 
    jne SHORT [email protected]_decimal 
    mov al, 1 
    ret 0 
[email protected]_decimal: 
    xor eax, eax 
    sub cl, 49 
    cmp cl, 8 
    setbe al 
    ret 0 

私が持っていた場合私がv1用の最適なコードを生成する理由はv3用ではない理由は、範囲チェックとの比較を減らす前にインライン展開を行うためだと思います。

関連する問題