コンパイラにサイズ計算させる

以前、こんな話を書いた。

long型のサイズの方がアテにならない

32bitのプロセッサで一般的なILP32と、64bitのプロセッサで一般的なLP64を比較すると、

long型とポインタ型のサイズが異なるという話。

想定されているような使い方ならばコード修正は少なくて済むのだが、

データ構造の都合、サイズが変わっては困るとなればなかなかそうもいかない。


というわけでこのあたりの見直し作業を進めていたのである。

long→intの置き換えで済むのではないかと思ったかも知れないが、

実はいろいろ複雑な事情があり、それだけでは済まない点が多々ある。

この移行作業が正しく出来ているかの確認を何らかの方法でしないとなとは考えていた。


そこで前々から考えていたことを実行に移すことにした。

コンパイラのオプション指定でリストファイルを出力することができる。

ビルド結果をアセンブラのコードで確認出来るというものである。

この機能を有効化してこんなコードをビルドした。

const size_t size_FOO = sizeof(FOO);
const void* addr_DEVX_REGY = &DEVX.REGY;
const size_t ofst_FOO_var1 = (size_t)&((FOO*)NULL)->var1;

size_FOO は FOO型のサイズを格納した定数となる。

マイコンのプログラムではペリフェラルのレジスタ群を構造体で定義して、

#define DEVX (*((DEVX_T *)0x22101000))

のようにして、DEVX.REG1=0x0001; のように使うことがある。

DEVX.REG1のアドレスは定数演算で求められるので、addr_DEVX_REGYはこれを格納した定数となる。

3つ目も構造体のメンバーに着目したもので、構造体内のオフセットアドレスを調べている。

NULLポインタを構造体のポインタに変換して、メンバーのアドレスを取得している。

NULLポインタに構造体先頭からのメンバーまでのオフセットアドレスを加算したものは定数演算で求められる。

NULLポインタ=0なので、構造体内のオフセットアドレスが ofst_FOO_var1に格納されるというわけ。


こんな形で新旧の環境でのビルド結果を並べて比較すると、

ちょこちょこミスが見つかって、多くは単純な計算ミスなのだが……

アライメントの都合で想定外のパディングが行われたり、

定義にそれなりの見直しが必要な点もあった。

というわけで、この作戦はかなりうまくいったのではないかと思う。


もっとひどいミスもあるんじゃないかと思っていたが、

思っていたほどのミスはなかったかな。

でも、このままデバッグに進んでたら惨事だっただろうから、今発見できたのはよかったかな。