GCC は、コンパイル内のデッド コードを見つけるのに役立ちます。複数のコンパイル単位でデッド コードを検出できるとしたら、私は驚きます。コンパイル ユニット内の関数または変数のファイル レベル宣言は、他のコンパイル ユニットがそれを参照する可能性があることを意味します。したがって、ファイルの最上位で宣言されたものはすべて、GCC は一度に 1 つのコンパイル単位しか認識しないため、削除できません。
問題が難しくなります。コンパイル単位 A が関数 a を宣言し、コンパイル単位 B が a を呼び出す関数 b を持っているとします。死人ですか?一見、いいえ。しかし、実際には状況によります。b が死んでいて、a への唯一の参照が b にある場合、a も死んでいます。b が単に& a を取り、それを配列 X に入れると、同じ問題が発生します。 a が死んでいるかどうかを判断するには、システム全体でポイントツー分析を行い、a へのポインターがどこでも使用されているかどうかを確認する必要があります。
この種の正確な「デッド」情報を取得するには、コンパイル ユニットのセット全体のグローバル ビューが必要であり、ポイント ツー分析を計算し、その後、そのポイント ツー分析に基づいてコール グラフを構築する必要があります。 . 関数 a は、コール グラフ (ルートとして main を持つツリーとして) がどこかでそれを参照しない場合にのみ、死んでいます。(いくつかの注意が必要です: 分析が何であれ、実際問題としては保守的でなければならないため、分析の完全なポイントでさえ、機能が死んでいると正しく識別できない場合があります。また、外部からの C アーティファクトの使用についても心配する必要があります。 C 関数のセット (たとえば、アセンブラ コードの一部からの呼び出し)。
スレッディングはこれをさらに悪化させます。各スレッドには、おそらく呼び出し DAG の先頭にあるいくつかのルート関数があります。スレッドがどのように開始されるかは C コンパイラによって定義されていないため、マルチスレッド C アプリケーションにデッド コードがあるかどうかを判断するには、何らかの方法で分析をスレッド ルート関数に通知するか、次の方法でそれらを発見する方法を通知する必要があることは明らかです。スレッド初期化プリミティブを探しています。
正しい答えを得る方法について、多くの回答が得られていません。オープン ソースではありませんが、C フロント エンドを備えたDMS Software Reengineering Toolkitには、C パーサー、制御およびデータフロー分析、ローカルおよびグローバル ポイントツー分析、グローバル コール グラフ構築など、これを行うためのすべての機構が備わっています。 . DMS は、アセンブラーからの外部呼び出し、および/またはスレッド初期化呼び出しであるスレッド ルートまたは特定のソース パターンのリストなどの追加情報を含めるように簡単にカスタマイズできます。いくつかの大規模な組み込みエンジン コントローラーについては、実際にそれを (簡単に) 実行しました。何百万行ものコードで。DMS は、このようなコール グラフを作成する目的で、2,600 万行のコード (約 18,000 のコンパイル ユニット) にも及ぶシステムに適用されています。
[余談ですが、個々のコンパイル ユニットを処理する際に、DMS はスケーリング上の理由から、そのコンパイル ユニットで使用されていないシンボルと関連コードを実質的に削除します。インクルード ファイルのネストに通常隠れている氷山を考慮すると、驚くべきことに、これによりコードの約 95% が削減されます。Cソフトウェアは通常、インクルードファイルを十分に分解していないと述べています。皆さんはすでにそれを知っていると思います。]
GCC などのツールは、コンパイル中にデッド コードを削除します。これは役に立ちますが、デッド コードはコンパイル ユニットのソース コードにまだ散らばっていて、開発者の注意を奪っています (デッド コードかどうかも確認する必要があります)。プログラム変換モードの DMS は、一部のプリプロセッサの問題をモジュロとして、そのデッド コードをソースから実際に削除するように構成できます。非常に大規模なソフトウェア システムでは、手動でこれを行うことはあまり望ましくありません。