序章
Cortex-M4 デバイスのフラッシュが不足しています。コードを分析したところ、コード サイズを削減する最大の機会は、単純に事前定義された定数にあります。
- 例
const Struct option364[] = {
{ "String1", 0x4523, "String2" },
{ "Str3", 0x1123, "S4" },
{ "String 5", 0xAAFC, "S6" }
};
問題
問題は、格納する (多数の) (短い) 文字列があることですが、それらのほとんどはテーブル (数値データと混合された文字列const struct
へのポインターを持つ s の配列) で使用されます。const
各文字列のサイズは可変ですが、ポインターの代わりにstruct
単純な (最大)char
配列を保持するようにポインターを変更することを検討しましたが、大きな違いはありませんでした。コンパイラが新しい文字列を 4 バイト境界で開始しようとしていたことは役に立ちませんでした。それは私に考えさせました...
考え
char
4 バイトのポインターを 2 バイトindex
の文字列テーブルに置き換えることができた場合(事前に定義されindex
た、オフセットが指定されたリンカー セクション)、マイナー コード バンプを犠牲にして、レコードごとに 2 バイトを節約できます。NUL
また、各文字列は前の文字列のバイトの直後に開始される可能性があるため、内側のパディングも避けます。そして、私が賢くなれば、文字列 (または文字列の一部) をインデックスに再利用できます。
しかし、さらに、4 + 2 + 4 (+ 2)
配置を次のように変更します2 + 2 + 2
-さらにスペースを節約します!
- 考慮
もちろん、ソース コード内では、これらすべての文字列と文字列テーブル自体のハウスキーピングは悪夢になるでしょう...コンパイラに助けてもらえない限り? 実際のソース コードの構文を変更することを考えました。文字列を文字列テーブルに入れたい場合は、次のように記述します#"String"
。#
プレフィックスは、文字列テーブルの候補としてフラグを立てます。通常の文字列にはその接頭辞がなく、コンパイラはそれを通常の文字列として扱います。
実装
したがって、これを実装するには、プリプリコンパイラを作成する必要があります。文字列だけを処理し、#""
それらを「魔法の」16ビットオフセットに置き換えてから、他のすべてを実際の(プリ)コンパイラに出力して実際のコンパイルを行うもの。プリプリコンパイラはC
、コンパイラが解析して専用セクションのリンカに提供するために、完全な文字列テーブルを内部に含む新しいファイルを作成する必要があります (ただし、トリックがあります - 以下を参照)。これを呼び出すのは簡単で、-no-integrated-cpp
スイッチを使用して自分のプリプリプロセッサを呼び出し、それが実際のプリプロセッサを呼び出します。
- 問題
誤解しないでください。私は問題があることを知っています。たとえば、部分的なビルドを処理できる必要があります。私の解決策は、変更されたすべてのC
ファイルに対して、(必要に応じて) 並列文字列テーブル ファイルを書き込むことです。「マスター」C
文字列テーブル ファイルは一連の にすぎず#include
、その#include
s の 1 つが変更された場合、または実際に新しい#include
ものが追加された場合、ビルドは再コンパイルの必要性を認識します。
結果
最終的には、すべての (定数) 文字列が 64K 以下のメモリ BLOB にパックされた実行可能ファイルになります (問題ありません!)。コードはindex
、それがそのブロブへのオフセットであることを認識しているため、通常どおり使用する前に、文字列テーブル ポインターの先頭にインデックスを追加します。
質問
私の質問は: それだけの価値はありますか?
- 長所:
- 1トンのスペースを節約できます。上記では定量化しませんでしたが、フラッシュ全体の 5% (!) の節約を想定しています。
- 短所:
- 特注のプリプロセッサを含めるには、ビルド プロセスを変更する必要があります。
- そのプリプロセッサは、プロジェクトではなくツールチェーンの一部としてビルドする必要があります。
- プリプロセッサにはバグや制限がある可能性があります。
- 実際のソース コードは、「そのままでは」コンパイルされません。
今...
アスベスト防護服を着たので...GO!