Upgrade to Pro — share decks privately, control downloads, hide ads and more …

未定義動作でFizz Buzz / Undefined Fizz Buzz

kaityo256
December 06, 2023

未定義動作でFizz Buzz / Undefined Fizz Buzz

kaityo256

December 06, 2023
Tweet

More Decks by kaityo256

Other Decks in Programming

Transcript

  1. 1
    19
    未定義動作でFizzBuzz
    2023年12月6日 C++ MIX #8
    kaityo256

    View full-size slide

  2. 2
    19
    自己紹介
    ロボ太/kaityo256
    ※画像はイメージです
    コンパイラいじめ芸人

    View full-size slide

  3. 3
    19
    コンパイラいじめってなに?
    括弧で34087重に囲んだ関数を食わせる
    とg++が死ぬ
    ※画像はイメージです

    View full-size slide

  4. 4
    19
    コンパイラいじめってなに?
    printfに4285個アスタリスクをつける
    とclang++が死ぬ
    ※画像はイメージです

    View full-size slide

  5. 5
    19
    コンパイラいじめってなに?
    GCCに27958段ネストした関数を
    食わせると死ぬ
    ※画像はイメージです

    View full-size slide

  6. 6
    19
    コンパイラをいじめてるとどうなるの?
    A. 翻訳AIもいじめはじめる
    ※画像は本物です

    View full-size slide

  7. 7
    19
    Fizz Buzz
    Fizz Buzzが組める能力、
    実運用で必要か?
    Fizz Buzzも組めない
    プログラマがいる!
    定期的に話題になる
    話題になる度に変態超絶解法も話題に

    View full-size slide

  8. 8
    19
    Fizz Buzz
    僕もなんか変態超絶解法やってみたい!

    View full-size slide

  9. 9
    19
    Fizz Buzz
    C/C++の未定義動作を使って
    Fizz Buzzっぽいことをやってみよう
    ※画像はイメージです

    View full-size slide

  10. 10
    19
    未定義動作でFizz Buzz
    #include
    int main(){
    int a = 0, b = 0;
    a = --a + ++a + ++a;
    b = ++b + ++b + a;
    for (int i=1;i<16;i++){
    if (i%b==0){
    printf("%s¥n",a?"buzz":"fizz");
    }else{
    printf("%d¥n",i);
    }
    }
    }
    こんなコードを書いてみた

    View full-size slide

  11. 11
    19
    未定義動作でFizz Buzz
    $ clang++ fizz.cpp; ./a.out
    1
    2
    fizz
    4
    5
    fizz
    7
    8
    fizz
    10
    11
    fizz
    13
    14
    fizz
    $ g++ fizz.cpp; ./a.out
    1
    2
    3
    4
    buzz
    6
    7
    8
    9
    buzz
    11
    12
    13
    14
    buzz
    clang++でコンパイルすると
    3の倍数の時だけfizzがでる
    g++でコンパイルすると
    5の倍数の時だけbuzzがでる
    ※ Fizz Buzzの処理?シェルでなんとかすればいいんじゃん?

    View full-size slide

  12. 12
    19
    なにが起きたか?
    int b = 0;
    b = ++b + ++b + a;
    int a = 0;
    a = --a + ++a + ++a;
    clang++ではa=0に、g++ではa=1になる
    clang++ではb=3に、g++ではb=5になる
    GCCかどうかのフラグとして使える
    整数剰余に使える

    View full-size slide

  13. 13
    19
    なにが起きたか?
    int a = 1;
    int b = ++a + ++a;
    複数の前置インクリメントを式に入れたら動作は未定義
    int a = 1;
    int tmp1 = a + 1;
    a = tmp1;
    int tmp2 = a + 1;
    a = tmp2;
    int b = tmp1 + tmp2;
    int a = 1;
    a = a + 1;
    a = a + 1;
    int tmp = a + a
    int b = tmp;
    clang++の解釈 g++の解釈

    View full-size slide

  14. 14
    19
    clang++の気持ち
    int func(int a){
    return ++a;
    }
    $ clang -emit-llvm -S test.c
    ; Function Attrs: noinline nounwind optnone
    uwtable
    define dso_local i32 @func(i32 %0) #0 {
    %2 = alloca i32, align 4
    store i32 %0, i32* %2, align 4
    %3 = load i32, i32* %2, align 4
    %4 = add nsw i32 %3, 1
    store i32 %4, i32* %2, align 4
    ret i32 %4
    }
    clangの気持ちの調べ方
    LLVM中間コードを吐く
    中間コードを
    読み解く

    View full-size slide

  15. 15
    19
    clang++の気持ち
    int a = 1;
    int b = ++a + ++a;
    int a = 1;
    int tmp1 = a + 1;
    a = tmp1;
    int b = tmp1 + ++a;
    int a = 1;
    int tmp1 = a + 1;
    a = tmp1;
    int tmp2 = a + 1;
    a = tmp2;
    int b = tmp1 + tmp2;
    ++aを再帰的に
    tmp = a + 1;
    に展開

    View full-size slide

  16. 16
    19
    g++の気持ち
    int func(int a){
    return ++a + ++a;
    }
    GCCの気持ちの調べ方
    $ gcc -c -fdump-tree-all test.c
    int func (int a)
    {
    int D.2719;
    a = a + 1;
    a = a + 1;
    D.2719 = a * 2;
    return D.2719;
    }
    GIMPLE中間表現を吐く
    中間表現を
    読み解く

    View full-size slide

  17. 17
    19
    g++の気持ち
    int a = 1;
    int b = ++a + ++a;
    int a = 1;
    a = a + 1;
    a = a + 1;
    tmp = a + a
    int b = tmp;
    二項演算子
    (++a + ++a)
    ごとに再帰的に展開
    ++a + ++a + ++a
    (++a + ++a) + ++a
    tmp = (++a + ++a)
    tmp + ++a
    複数あったら
    二個ずつ処理

    View full-size slide

  18. 18
    19
    まとめ
    • 同じソースでclang++とg++で異なる動作をする
    コードを書き、Fizz Buzzもどきをやってみた
    • 他の言語処理系でも起きる
    • clang派(PHP, D, JavaScript)
    • GCC派(Perl)
    • 多くの後発言語はC/C++を反面教師として設計さ
    れている
    • インクリメント演算子廃止(Ruby, Python)
    • 後置のみにして文に(Go)
    • 未定義動作について学ぶと、言語処理系について
    の理解が進む

    View full-size slide

  19. 19
    19










































    View full-size slide