PL/Iの「SELECT」構造とコンパイラ最適化の深淵:ジャンプテーブルが明かす実行の真実
メインフレームの現場で、長年「なぜこのバッチ処理のCPU負荷が急増したのか」と頭を悩ませた経験はないだろうか。特に、PL/Iの `SELECT-WHEN-OTHERWISE` 構造は、一見するとJavaの `switch` やC#の `switch` と同義に思えるが、その内部実装、特にコンパイラによる最適化の挙動を知らずにコードを書くのは、エンジンの特性を無視してF1を走らせるようなものだ。
今日は、IBMメインフレームにおけるPL/Iの「SELECT」文が、どのように機械語の分岐テーブルへ変換されるのか、その裏側の挙動を解剖していこう。
—
SELECT文がジャンプテーブルに昇華する瞬間
PL/Iには、他の言語に見られる「予約語(Reserved Words)」という概念が希薄だ。これは歴史的な設計思想だが、コンパイラにとっては「文脈を解析しなければ単語の意味が確定しない」という過酷な状況を意味する。
`SELECT` 文において、コンパイラは主に2つの戦略を使い分ける。
1. 逐次比較(Linear Search): `WHEN` 条件が少ない、あるいは評価値が不連続な場合。単なる `CMP`(比較)と `BE`(Branch if Equal)の連打になる。
2. ジャンプテーブル(Jump Table / Branch Table): `WHEN` 条件が一定数を超え、かつ値が連続的(あるいは近接)である場合。コンパイラはメモリ上にオフセットの配列を構築し、インデックス計算だけでターゲットアドレスを算出する。
実践的なPL/Iコード例:最適化の分水嶺
/ パフォーマンスの要所:定数の範囲が連続しているかが鍵 /
SELECT (STATUS_CODE);
WHEN (1) CALL PROCESS_INIT;
WHEN (2) CALL PROCESS_RUN;
WHEN (3) CALL PROCESS_TERM;
OTHERWISE CALL PROCESS_ERR;
END;
このコードにおいて、`STATUS_CODE` がパックデシマル(`PIC S9(4) COMP-3`)である場合、注意が必要だ。コンパイラは内部的にパックデシマルをバイナリ(`FIXED BIN`)に変換して比較を行う。ここで、符号反転(F→C/D)の判定ミスや、上位ニブルのパディングが適切でない場合、期待したジャンプテーブルへのインデックスが生成されず、デバッグ不可能なアベンド(S0C7など)を誘発することがある。
パフォーマンスを左右する「WHEN」の数と最適化オプション
コンパイラオプション `OPTIMIZE(2)` や `(3)` を指定すると、コンパイラは `WHEN` 句の評価順序を並べ替え、頻出する条件を先頭に持ってくるような最適化を試みる。しかし、これが曲者だ。
- エッジケース: `CICS` 環境でのオンライン処理において、頻繁に呼び出されるトランザクションが `OTHERWISE` に落ちるような設計だと、ジャンプテーブルの恩恵は皆無となる。
- データ制御: もし `WHEN` の条件に `SQLCA.SQLCODE` 等のDB2戻り値を使う場合、コンパイラはジャンプテーブルの作成を諦め、逐次比較を選択することが多い。これはDB2の戻り値がプログラマの制御外で変動しうるためだ。
マイグレーションの現場で陥る「地雷」
JavaやC#へのマイグレーションを担当する際、PL/Iの「動的メモリ操作」をどう翻訳するかが最大の障壁となる。
/ ポインタを用いた動的アクセス(PL/Iの真骨頂) /
DCL P_REC PTR;
DCL BASED_REC CHAR(100) BASED(P_REC);
/ 特定の構造体をベース変数としてキャストする /
P_REC = ADDR(STORAGE_BUFFER);
IF BASED_REC = ‘VALID’ THEN …
このポインタ操作をJavaのオブジェクトに置き換える際、PL/I特有の「アライメント」を無視すると、マイグレーション先で予期せぬオフセットズレが発生する。特に、CICSの通信エリア(COMMAREA)を直接ポインタで解析している既存システムの場合、データ構造のパディング(埋め込み)を厳密に再現しない限り、移行後のシステムは崩壊する。
アベンド(ABEND)解析の極意
もし本番環境でPL/Iプログラムがアベンドしたら、まず見るべきは `CEE3DMP`(Language Environment Dump)だ。
1. レジスタの確認: `R15`(戻り値レジスタ)が不正なアドレスを指していないか。
2. 基底レジスタの欠落: `USING` 句で指定されたベースレジスタが、実行時に別の処理で上書きされていないか。
3. パックデシマルのバグ: `S0C7` が発生した場合、それはデータが「パック形式ではない」可能性が高い。特に外部ファイルから読み込んだデータがEBCDICのまま混入していないか、`DUMP` 出力の該当領域を16進数で追跡する。
結び:技術の継承と刷新
PL/Iの `SELECT` 文一つとっても、コンパイラの裏側にはIBMエンジニアたちの執念に近い最適化ロジックが詰まっている。マイグレーションを行う際、コードを「翻訳」するのではなく、この「論理的な挙動」を理解し、ターゲット言語のメモリモデルに再構築する。これこそが、レガシー移行のプロジェクトを成功に導く唯一の道だ。
次にバッチを改修する際、`SELECT` の `WHEN` を単なる条件分岐と捉えず、「コンパイラがどの機械語を生成しているか」を想像してみてほしい。そこには、40年前のシステム設計思想が、今なお最高速度で走り続けている姿が見えるはずだ。
