PL/Iの制御フローを制する者は、基幹システムの保守を制す:ITERATEとLEAVEの深淵
長年、汎用機の最前線でコードを読み続けていると、「なぜここでITERATEを使ったのか」という設計者の意図が、その後の数十年間の保守性にどれほど影響を与えるかを痛感します。
PL/Iは、C言語の`continue`や`break`に相当する`ITERATE`と`LEAVE`を標準で備えていますが、単なる制御フローの構文と侮ってはいけません。特に、複雑なCICSオンラインプログラムや、数万行に及ぶ巨大なDB2バッチ処理において、これらの制御文は「どこを脱出するのか」というスコープの解釈を誤ると、致命的な論理バグを生む温床となります。
今回は、この制御フローの挙動を、コンパイラ最適化や動的メモリ操作の観点から掘り下げて解説します。
—
1. ラベル付きLEAVE/ITERATEによる制御範囲の明示
PL/Iにおいて、ネストされたDOループの制御は非常に強力です。特に「ラベル(Label)」を活用することで、多重ループを一気に脱出することが可能です。
/i
MAIN_PROC: PROCEDURE OPTIONS(MAIN);
/ 外部ループにラベルを付与 /
OUTER_LOOP: DO I = 1 TO 100;
INNER_LOOP: DO J = 1 TO 10;
/ 特定の条件下で処理をスキップ /
IF WORK_TABLE(I, J).STATUS = ‘SKIP’ THEN
ITERATE INNER_LOOP; / 内側のループの次へ /
/ エラー発生時、外部ループごと脱出 /
IF WORK_TABLE(I, J).ERROR_CODE = ’99’ THEN
LEAVE OUTER_LOOP; / OUTER_LOOPの終了地点へジャンプ /
END INNER_LOOP;
END OUTER_LOOP;
END MAIN_PROC;
なぜラベルが必須なのか
マイグレーション現場で、ラベルを省略した古いコードに出くわすことがあります。しかし、複雑な処理では「どのループを抜けているのか」が読み手(あるいは未来の自分)にとってブラックボックス化します。特に、後述するポインタ操作や領域確保がループ内で行われている場合、意図しない`LEAVE`は、確保されたメモリの解放漏れやポインタの無効化(Dangling Pointer)を引き起こし、運が悪いと数ヶ月後にS0C4アベンドという形で牙を剥きます。
—
2. 動的メモリ操作と制御フローの罠
PL/Iの`ALLOCATE`文を使ってヒープメモリを確保し、`BASED`変数で操作している場合、`LEAVE`や`ITERATE`の扱いには細心の注意が必要です。
例えば、ループ内で`ALLOCATE`を実行し、エラー判定で`LEAVE`した際、その領域の`FREE`を誰が行うのか。Javaであればガーベッジコレクションが回収してくれますが、PL/Iの世界では、制御フローを途中で断ち切るということは、「後始末の責任をどこで取るか」を再設計することを意味します。
ダンプ解析の観点から
アベンド発生時のSYSUDUMPを解析していると、しばしば「ポインタが指し示す先が既にFREEされている」という事態に直面します。これは多くの場合、制御フローの途中で期待しない脱出が発生し、解放処理をスキップしていることが原因です。`LEAVE`を使う際は、必ず直前にクリーンアップ・ルーチンを呼ぶか、`ON ENDFILE`や`ON ERROR`ブロックでの後処理を統合する設計が、大規模システムにおける鉄則です。
—
3. 移行設計における「パックデシマル」の落とし穴
レガシー移行で最も恐ろしいのが、PL/I特有の`FIXED DECIMAL`(パックデシマル)の内部表現と、Java等の演算結果の不一致です。
特に、ループ内でカウンターや集計を行う際、コンパイラ最適化オプション(`OPTIMIZE(2)`以上)を有効にすると、レジスタへの展開タイミングによって符号ビットの反転やオーバーフローチェックの挙動が変わるケースがあります。
- Tips: マイグレーション先で「数値が合わない」という事象が発生した場合、まずは当該ループ内の演算子に`TRUNC`や`ROUND`が明示されているか確認してください。PL/Iのコンパイラは、型変換の際、非常に寛容(あるいは無慈悲)な暗黙の変換を行います。
—
4. アーキテクトからの提言:移行を成功させるために
JavaやC#へマイグレーションを行う際、PL/Iの`ITERATE`や`LEAVE`をそのまま単純置換する手法は危険です。
1. スコープの可視化: `LEAVE`している箇所に、その脱出が「正常な業務終了」なのか「異常検知」なのかをコメントで明記する。
2. 定型化: 可能な限り、ループの出口を一つにまとめる(Single Point of Exit)設計へのリファクタリングを推奨します。これにより、後続の言語変換が劇的に容易になります。
3. コンパイラオプションの再確認: 現行資産のコンパイラオプション(`RULES`や`LIMITS`)が、現在のソースコードのロジックをどこまで補完しているかを理解してください。
PL/Iは、その言語仕様の柔軟さゆえに「書き手を選ぶ」言語です。しかし、そのコードの裏側にある「計算機資源をどう制御するか」という設計思想は、現代のクラウドネイティブな開発においても、極めて重要な示唆を与えてくれます。
制御フローを完全に理解し、バグを未然に防ぐ。それが、汎用機という巨大な獣を乗りこなすアーキテクトの矜持です。
