PL/Iの「FIXED BINARY」を深掘る:2バイトと4バイトの狭間に潜む罠
メインフレームの現場で何十年と生き抜いてきたPL/Iコードを眺めていると、時折、現代のJavaやC#の感覚では到底理解できない「絶妙なチューニング」に出くわします。特に`FIXED BINARY`の扱い。ただの整数型だと高を括っていると、S0C7(データ例外)の深淵に叩き落とされることになります。
今日は、`FIXED BINARY(15)`と`(31)`、この2つの「境界線」に焦点を当て、移行プロジェクトのアーキテクトが絶対に押さえておくべき内部構造と、アベンド回避の定石を語りましょう。
—
1. 内部表現の「揺らぎ」を理解する
まず基本ですが、PL/Iの`FIXED BINARY(p)`は、宣言された精度`p`に応じて、コンパイラが自動的に2バイト(Halfword)か4バイト(Fullword)を選択します。
- FIXED BINARY(15): 2バイト(16ビット)。値の範囲は -32,768 〜 +32,767。
- FIXED BINARY(31): 4バイト(32ビット)。値の範囲は -2,147,483,648 〜 +2,147,483,647。
ここで重要なのは、「計算中の精度」です。PL/Iは式の中間結果において、オペランドの精度を考慮して最適な内部形式を選択しますが、この最適化が時として「算術オーバーフロー」を誘発します。
実務で見かける「危うい」コード例
DCL COUNT_S FIXED BIN(15) INIT(30000);
DCL COUNT_L FIXED BIN(31) INIT(5000);
DCL RESULT FIXED BIN(31);
/ ここで暗黙の変換が発生する /
RESULT = COUNT_S + COUNT_L;
一見すると安全に見えますが、コンパイラオプションや中間生成コードによっては、`COUNT_S + COUNT_L` が `FIXED BIN(15)` の世界に引きずり込まれようとしてオーバーフローを起こすケースがあります。特に古いコンパイラ設定で `LIMIT` 周りの最適化が効いていると、計算結果が期待値とズレる——これがレガシー移行で最も恐ろしい「サイレントなバグ」の正体です。
—
2. パックデシマルとの「混成演算」における地雷
基幹業務では `FIXED BINARY` と `FIXED DECIMAL` (いわゆるパック10進数) が混在します。ここで最も気をつけたいのが、「パックデシマルの内部符号」です。
IBMメインフレームにおいて、パックデシマルは `X’F’` や `X’C’` が正、`X’D’` が負を表しますが、外部からのデータ連携や、古いCOBOLコピーブック経由で読み込んだ際に、この符号ビットが破壊されていることがあります。
DCL WRK_VAL FIXED BIN(31);
DCL PK_VAL FIXED DEC(5,0);
/ パックデシマルからバイナリへの変換は、値のチェックを怠ると即S0C7 /
ON CONVERSION BEGIN;
PUT SKIP LIST(‘データ例外発生:不正なパックデータ’);
SIGNAL ERROR;
END;
WRK_VAL = PK_VAL;
マイグレーション時にJava等へロジックを移植する際、最も苦労するのがこの「不正なパックデータ」のハンドリングです。PL/Iの`ON CONVERSION`ブロックは非常に強力ですが、これをJavaの例外処理で同等に再現するのは至難の業。移行設計フェーズで、ソースコード全域の「符号チェック」を静的解析ツールで洗い出すことを強く推奨します。
—
3. ポインタ演算と動的メモリ操作の闇
システムアーキテクトとして避けて通れないのが、`BASED`変数とポインタを用いた動的領域操作です。CICSオンライン処理などで、大量のレコードをチェーンリストで管理する際、`FIXED BIN(15)`をオフセットとして使用しているコードは要注意です。
DCL P POINTER;
DCL BASED_REC CHAR(100) BASED(P);
DCL OFFSET FIXED BIN(15);
/ OFFSETが32,767を超えた瞬間にアドレッシング例外が飛ぶ /
P = ADDR(BASE_STORAGE) + OFFSET;
もし`OFFSET`が`FIXED BIN(15)`で定義されている場合、そのポインタが指し示す先は最大でも約32KBしか移動できません。現代の64ビット環境(z/Architecture)へ移行する際、この「2バイト制限」がボトルネックとなり、大規模データ処理でアベンドが頻発します。「ポインタには必ずFIXED BIN(31)以上、あるいはADDR64を使用せよ」というのが、長年の経験から得た教訓です。
—
4. ダンプ解析の現場から:アベンド発生時の鉄則
S0C7やS0C1に遭遇した際、コンパイラのリストファイル(`LIST`オプション付きでコンパイルしたもの)と、CEEダンプを照合してください。
- コンパイラは嘘をつかない: どの命令(例えば `CVB` – Convert to Binary)で落ちたかを確認し、その直前のレジスタ値を確認します。
- 符号の確認: レジスタ内の値が負数であるべきか、パックの符号ビットが `X’D’` になっているか。
- 埋め込みSQLの影響: DB2のホスト変数として`FIXED BIN`を使用している場合、DB2側が期待する型との不一致(SQLCODE -301等)がないか、プリコンパイラが生成したコードを一度覗いてみてください。
—
結びに代えて
PL/Iは、極めて厳密かつ寛容な、矛盾を孕んだ言語です。`FIXED BINARY(15)`と`(31)`を意識的に使い分けることは、単なるメモリ節約ではなく、システムの安定性を担保するための「アーキテクトの矜持」と言えます。
もし皆さんが今、レガシー資産のJava化やC#化という荒波の中にいるのであれば、言語仕様の表面だけをなぞるのではなく、こうした「バイナリの深層」まで見通す視点を持ってください。移行とはコードの書き換えではなく、「メインフレームが何十年もかけて培ってきた堅牢な計算モデルの再構築」に他ならないのですから。
さて、次は「`AREA`変数の管理と`OFFSET`ポインタの再帰的構造」について語りましょうか。現場のトラブルは、常にそのあたりから湧き上がってくるものですから。
