PL/Iコンパイラが吐き出す「真実」を追う:LISTオプションによる低レイヤー解析の実践
基幹システムの保守・移行において、我々が対峙するのは単なるコードの集合体ではない。数十年前に記述された、コンパイラの「解釈」という名のブラックボックスだ。
特にPL/Iにおいては、ソースコードの記述と、実際にIBMメインフレームのCPU(z/Architecture)が実行する命令列との間に、時に絶望的な乖離が生じることがある。JavaやC#といった高レイヤー言語の感覚でコードを追いかけても、本質的なバグ—例えば、パックデシマル(COMP-3)の内部表現における不正な符号や、ポインタ操作に伴うストレージ破壊—は決して見抜けない。
今回は、コンパイラオプション `LIST` を駆使し、生成されたアセンブラコード相当の出力(Object Listing)を解析することで、プログラムの挙動を「裸」にする技術について語ろう。
1. なぜ今、LISTオプションなのか
マイグレーションプロジェクトにおいて、「既存資産のロジックを正確に移植せよ」という要求は、往々にして不可能だ。コンパイラが暗黙的に行っている最適化や、`OPTIONS(MAIN)` で定義された初期化ルーチンが、新しいプラットフォームで再現できないケースが多々あるからだ。
`LIST`オプションは、PL/Iの各ステートメントが、どの機械語命令に変換されたかを明示する。これを見ることは、システムアーキテクトにとって、外科医がMRI画像を見るのと同じことだ。
実践:動的メモリ操作の裏側を覗く
例えば、以下のコードを見てほしい。ベース変数を用いた構造体の動的な割り当てとポインタ操作だ。
/i
/ ポインタを用いた動的なストレージ確保の例 /
DECLARE P_DATA POINTER;
DECLARE 1 DYNAMIC_BUF BASED(P_DATA),
2 HEADER CHAR(4),
2 VALUE FIXED DEC(7,2);
/ ストレージの確保と初期化 /
ALLOCATE DYNAMIC_BUF;
HEADER = ‘HEAD’;
VALUE = 123.45; / ここでパックデシマルへ変換される /
このコードを`LIST`オプション付きでコンパイルすると、生成コードには `CVB` (Convert to Binary) や `PACK` 命令、あるいは `MVC` (Move Character) 命令の連鎖が並ぶはずだ。もし、`VALUE`に期待値が入らない場合、リストを追えば「どこで符号(Sign Nibble)が破壊されたか」が瞬時に判明する。
2. コンパイラ最適化の罠:`OPTIMIZE(2)`と生成コード
多くのエンジニアは、性能向上のために`OPTIMIZE(2)`を安易に設定する。しかし、アーキテクトの視点で見れば、これは「デバッグの難易度を跳ね上げる行為」だ。
最適化が働くと、コンパイラはレジスタの再利用を積極的に行い、命令の順序を入れ替える(Instruction Scheduling)。これにより、ダンプ解析時に「プログラムカウンタが指している位置」と「変数の状態」が一致しなくなる。
- 解析のポイント: `LIST`で出力されたリストと、障害時のダンプ(SYSUDUMP/SYSMDUMP)を突き合わせろ。最適化によって変数がレジスタ上にしか存在しない場合、ダンプ上のストレージ領域には値が残っていない。この「消失した変数」をレジスタダンプから復元する技術こそ、熟練のメインフレーマーの真骨頂である。
3. 現場で遭遇する「エッジケース」への対応
特にCICSオンライン処理において、`CICS COMMAND`とPL/Iのロジックが混在する環境では、スタックの汚染(Storage Overlay)が頻発する。
パックデシマルの符号反転バグ
PL/Iの`FIXED DEC`は、内部的にパックデシマルとして扱われる。稀に、古いデータセットからの読み込みや、不正なマッピングによって、符号部(最下位バイトのニブル)が `C` (正) や `D` (負) 以外になることがある。
- 解析手法: `LIST`で出力された生成コード内で、算術演算(`AP`: Add Packed等)が実行される直前のレジスタ値を追跡せよ。演算命令が落ちる直前のデータを確認することで、どの入力ソースが不正な符号を持っているかを特定できる。
埋め込みSQL (DB2) の戻り値
DB2のSQL文はプリコンパイラによってPL/Iのサブルーチン呼び出しに置換される。
- アーキテクトの視点: SQLエラー発生時、PL/I側で定義された`SQLCA`の内容をダンプで見るのは当然だが、さらに`LIST`出力を見て、プリコンパイラが生成した`CALL`命令の引数渡し(Register 1の指すパラメータリスト)を確認せよ。Javaへの移行時に「なぜかSQLエラーになる」という相談の9割は、この引数定義(`VARCHAR`構造体の長さ設定の誤りなど)の不一致に起因している。
結論:コードの「深層」に触れるということ
PL/Iのコードを読み解くことは、コンピュータの歴史を読み解くことと同義だ。`LIST`オプションによって出力される、無機質なアセンブラ命令列の裏には、設計者の意図と、コンパイラ開発者の最適化への執念が詰まっている。
我々のようなシステムアーキテクトが目指すべきは、ツールに頼り切るのではなく、CPUがメモリ上のビット列をどのように解釈し、どのように演算しているかという「根本的な物理現象」を理解することだ。
移行案件において、マイグレーション先がJavaであろうと、基幹システムの心臓部であるPL/Iの挙動を完全に掌握していれば、それはもはや「怖れる対象」ではなく、「制御可能な資産」へと変わるはずだ。
次は、`MAP`オプションと組み合わせたストレージレイアウトの最適化について掘り下げていこうか。メインフレームの深淵は、まだまだ尽きることがない。
