Help us understand the problem. What is going on with this article?

C++のinline関数は最適化をかけるとどのようににアセンブルされるのか

概要

最適化オプション別にどのようにメンバ関数のインライン化が行われるのか確認してみた。具体的には以下のような種類のメンバ関数で比較した。

  • Func1: 関数定義時に実装まで書いてしまった場合。
  • Func2: インライン関数としてヘッダファイル内に実装を書いた場合。
  • Func3: 実装を別ファイルに分けた場合。

ソースコード

class_test.h

#ifndef TEST_H_
#define TEST_H_

class TestClass {
public:
  int Func1(){return 77777;}
  int Func2();
  int Func3();
};

inline int TestClass::Func2(){return 88888;}

#endif // TEST_H_

class_test.cc

#include"class_test.h"

int TestClass::Func3(){return 99999;}

test.cc

#include"class_test.h"
#include<iostream>
using namespace std;

int main() {
  TestClass test;
  int a = test.Func1();
  int b = test.Func2();
  int c = test.Func3();
  printf("%d %d %d\n", a,b,c);

  return 0;
}

結果

コンパイルした後、実行ファイルをobjdumpコマンド(-dオプション)で逆アセンブルした。結果はメイン関数の部分のみ。callqで関数を呼び出している。

最適化なし

clang++ -std=c++11 test_class.cc test.cc

インライン化された関数なし

00000000004006c0 <main>:
  4006c0:   55                      push   %rbp
  4006c1:   48 89 e5                mov    %rsp,%rbp
  4006c4:   48 83 ec 20             sub    $0x20,%rsp
  4006c8:   48 8d 7d f8             lea    -0x8(%rbp),%rdi
  4006cc:   c7 45 fc 00 00 00 00    movl   $0x0,-0x4(%rbp)
  4006d3:   e8 48 00 00 00          callq  400720 <_ZN9TestClass5Func1Ev> # Func1を呼び出している
  4006d8:   48 8d 7d f8             lea    -0x8(%rbp),%rdi
  4006dc:   89 45 f4                mov    %eax,-0xc(%rbp)
  4006df:   e8 4c 00 00 00          callq  400730 <_ZN9TestClass5Func2Ev> # Func2を呼び出している
  4006e4:   48 8d 7d f8             lea    -0x8(%rbp),%rdi
  4006e8:   89 45 f0                mov    %eax,-0x10(%rbp)
  4006eb:   e8 c0 ff ff ff          callq  4006b0 <_ZN9TestClass5Func3Ev> # Func3を呼び出している
  4006f0:   48 bf c4 07 40 00 00    movabs $0x4007c4,%rdi
  4006f7:   00 00 00
  4006fa:   89 45 ec                mov    %eax,-0x14(%rbp)
  4006fd:   8b 75 f4                mov    -0xc(%rbp),%esi
  400700:   8b 55 f0                mov    -0x10(%rbp),%edx
  400703:   8b 4d ec                mov    -0x14(%rbp),%ecx
  400706:   b0 00                   mov    $0x0,%al
  400708:   e8 03 fe ff ff          callq  400510 <printf@plt>
  40070d:   31 c9                   xor    %ecx,%ecx
  40070f:   89 45 e8                mov    %eax,-0x18(%rbp)
  400712:   89 c8                   mov    %ecx,%eax
  400714:   48 83 c4 20             add    $0x20,%rsp
  400718:   5d                      pop    %rbp
  400719:   c3                      retq   
  40071a:   66 0f 1f 44 00 00       nopw   0x0(%rax,%rax,1)

最適化オプションO1

インライン化された関数なし

0000000000400690 <main>:
  400690:   55                      push   %rbp
  400691:   41 56                   push   %r14
  400693:   53                      push   %rbx
  400694:   48 83 ec 10             sub    $0x10,%rsp
  400698:   48 8d 5c 24 08          lea    0x8(%rsp),%rbx
  40069d:   48 89 df                mov    %rbx,%rdi
  4006a0:   e8 3b 00 00 00          callq  4006e0 <_ZN9TestClass5Func1Ev> # Func1を呼び出している
  4006a5:   41 89 c6                mov    %eax,%r14d
  4006a8:   48 89 df                mov    %rbx,%rdi
  4006ab:   e8 40 00 00 00          callq  4006f0 <_ZN9TestClass5Func2Ev> # Func2を呼び出している
  4006b0:   89 c5                   mov    %eax,%ebp
  4006b2:   48 89 df                mov    %rbx,%rdi
  4006b5:   e8 c6 ff ff ff          callq  400680 <_ZN9TestClass5Func3Ev> # Func3を呼び出している
  4006ba:   89 c1                   mov    %eax,%ecx
  4006bc:   bf 84 07 40 00          mov    $0x400784,%edi
  4006c1:   31 c0                   xor    %eax,%eax
  4006c3:   44 89 f6                mov    %r14d,%esi
  4006c6:   89 ea                   mov    %ebp,%edx
  4006c8:   e8 43 fe ff ff          callq  400510 <printf@plt>
  4006cd:   31 c0                   xor    %eax,%eax
  4006cf:   48 83 c4 10             add    $0x10,%rsp
  4006d3:   5b                      pop    %rbx
  4006d4:   41 5e                   pop    %r14
  4006d6:   5d                      pop    %rbp
  4006d7:   c3                      retq   
  4006d8:   0f 1f 84 00 00 00 00    nopl   0x0(%rax,%rax,1)
  4006df:   00

最適化オプションO2

Func1とFunc2はインライン化される。

0000000000400680 <main>:
  400680:   50                      push   %rax
  400681:   48 8d 3c 24             lea    (%rsp),%rdi
  400685:   e8 e6 ff ff ff          callq  400670 <_ZN9TestClass5Func3Ev> # Func3を呼び出している
  40068a:   89 c1                   mov    %eax,%ecx
  40068c:   bf 34 07 40 00          mov    $0x400734,%edi
  400691:   be d1 2f 01 00          mov    $0x12fd1,%esi
  400696:   ba 38 5b 01 00          mov    $0x15b38,%edx
  40069b:   31 c0                   xor    %eax,%eax
  40069d:   e8 6e fe ff ff          callq  400510 <printf@plt>
  4006a2:   31 c0                   xor    %eax,%eax
  4006a4:   59                      pop    %rcx
  4006a5:   c3                      retq   
  4006a6:   66 2e 0f 1f 84 00 00    nopw   %cs:0x0(%rax,%rax,1)
  4006ad:   00 00 00

最適化オプションO3

最適化オプションO2の場合と同じ

最適化オプションOfast

最適化オプションO2の場合と同じ

最適化オプションO1 + -flto

最適化オプションO1の場合と同じ

最適化オプションO2 + -flto

Func1,Func2,Func3がインライン化される。

400670: 50                      push   %rax
400671: bf 14 07 40 00          mov    $0x400714,%edi
400676: be d1 2f 01 00          mov    $0x12fd1,%esi
40067b: ba 38 5b 01 00          mov    $0x15b38,%edx
400680: b9 9f 86 01 00          mov    $0x1869f,%ecx
400685: 31 c0                   xor    %eax,%eax
400687: e8 84 fe ff ff          callq  400510 <printf@plt>
40068c: 31 c0                   xor    %eax,%eax
40068e: 59                      pop    %rcx
40068f: c3                      retq

まとめ

Func1とFunc2は同じものとして扱われていた。
-O2でインライン展開を有効にした状態で-flto(リンク時最適化)を有効にすると、ヘッダと実装が分かれていたとしても、インライン化はできる。すべての関数がインライン化されるわけではないはず。

参考

Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away