コンパイラの機能を知りたい場合は、コンパイラのドキュメントを参照することをお勧めします。最適化については、たとえばLLVM の分析パスと変換パスを調べることができます。
1) sin(3.141592) // コンパイル時に評価されますか?
おそらく。IEEE float 計算には非常に正確なセマンティクスがあります。ちなみに、実行時にプロセッサ フラグを変更すると、これは驚くべきことかもしれません。
2) int a = 0; a = exp(18), cos(1.57), 2;
場合によります:
- 関数
exp
とcos
がインラインかどうか
- そうでない場合は、正しく注釈が付けられているかどうか (したがって、コンパイラは副作用がないことを認識します)
C または C++ 標準ライブラリから取得した関数については、正しく認識/注釈を付ける必要があります。
計算の省略については、次のとおりです。
-adce
: 積極的なデッドコードの排除
-dce
: デッドコードの除去
-die
: デッド命令の削除
-dse
: デッドストア消去
コンパイラは役に立たないコードを見つけるのが大好きです:)
3)
2)
実際に似ています。ストアの結果は使用されず、副作用はありません。
-loop-deletion
: デッドループを削除
最後に、コンパイラーをテストしてみませんか?
#include <math.h>
#include <stdio.h>
int main(int argc, char* argv[]) {
double d = sin(3.141592);
printf("%f", d);
int a = 0; a = (exp(18), cos(1.57), 2); /* need parentheses here */
printf("%d", a);
for (size_t i = 0; i < 10; ++i) {
int a = 10 + i;
}
return 0;
}
Clang は、コンパイル中にすでに役立つように努めています。
12814_0.c:8:28: warning: expression result unused [-Wunused-value]
int a = 0; a = (exp(18), cos(1.57), 2);
^~~ ~~~~
12814_0.c:12:9: warning: unused variable 'a' [-Wunused-variable]
int a = 10 + i;
^
発行されたコード (LLVM IR):
@.str = private unnamed_addr constant [3 x i8] c"%f\00", align 1
@.str1 = private unnamed_addr constant [3 x i8] c"%d\00", align 1
define i32 @main(i32 %argc, i8** nocapture %argv) nounwind uwtable {
%1 = tail call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([3 x i8]* @.str, i64 0, i64 0), double 0x3EA5EE4B2791A46F) nounwind
%2 = tail call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([3 x i8]* @.str1, i64 0, i64 0), i32 2) nounwind
ret i32 0
}
私たちは次のように述べています。
- 予測どおり、
sin
計算はコンパイル時に解決されました
- 予想通り、
exp
完全cos
に剥ぎ取られました。
- 予測どおり、ループも削除されました。
コンパイラの最適化についてさらに詳しく知りたい場合は、次のことをお勧めします。
- IR の読み方を学ぶ (信じられないほど簡単で、アセンブリのほうがはるかに簡単です)
- LLVM Try Out ページを使用して、仮定をテストします