こんにちは!IBMメインフレームの世界へようこそ。
JavaやCOBOLの経験はあるけれど、これからPL/Iを触るという方は、「なんだか古い言語だし、難しそうだな…」と身構えてしまうかもしれませんね。でも、大丈夫ですよ。一つずつ蓋を開けて中身を見ていけば、PL/Iがいかに合理的で、マシン(ハードウェア)の能力を極限まで引き出すために作られた素晴らしい言語であるかが分かります。
今回は、そんなPL/Iの数ある機能の中から、データ操作の基本にして極めて重要な「SUBSTR関数」の裏側(内部実装とアセンブラ展開)を覗いてみましょう。
「文字列の一部を切り出すだけの関数でしょ?」と侮るなかれ。ここには、IBMメインフレームのCPU(System z)が誇る、電光石火のメモリ転送最適化のドラマが隠されているのです。
—
1. 他言語とはちょっと違う? PL/Iの「名前」と「変数」のルール
まず、SUBSTRの話に入る前に、PL/Iのちょっとユニークで優しい基本ルールに少しだけ触れておきますね。
JavaやCOBOLでは、システムがあらかじめ予約している単語(予約語:`IF` や `MOVE` など)を、変数名(識別子)として使うことはできませんよね。コンパイラに怒られてしまいます。
しかし、PL/Iには「厳密な意味での予約語」がありません。
「えっ、じゃあ `IF` という名前の変数を作ってもいいの?」
はい、極端な話、コンテキスト(前後の文脈)で判断するため、文法上は許されてしまいます(※実務では混乱の元なので絶対にやめましょうね!)。
この「型破りだけど懐の深い」設計思想こそが、PL/Iがハードウェアのポテンシャルをダイレクトに引き出せる秘密の一つです。変数名や関数名(SUBSTRなど)も、コンパイラが賢く文脈を読んで解釈してくれます。怖くないですよね?
—
2. 主役登場:SUBSTR関数ってどんなもの?
さて、本題の `SUBSTR`(サブストリング)関数です。
例えば、顧客マスターの電文から「生年月日(8桁)」のデータだけをスライスしたいとき、こう書きますよね。
DCL IN_RECORD CHAR(100); / 入力レコード全体(100バイト) /
DCL BIRTH_DATE CHAR(8); / 生年月日格納用(8バイト) /
/ 11文字目から8文字分を切り出して代入する /
BIRTH_DATE = SUBSTR(IN_RECORD, 11, 8);
Javaの `substring(10, 18)` や、COBOLの `MOVE IN-RECORD(11:8) TO BIRTH-DATE` とよく似ています。
しかし、この見慣れたコードが、IBMメインフレームの内部(コンパイラとハードウェア)でどのように料理されているかを知ると、PL/Iを見る目がガラリと変わりますよ。
—
3. 内部実装のロマン:SUBSTRはこうしてアセンブラに変身する
PL/Iコンパイラは、私たちが書いたエレガントなソースコードを、IBMのS/370やZアーキテクチャの機械語(アセンブラ)へと翻訳(コンパイル)します。
このとき、`SUBSTR` を使った代入文がどう変換されるか、その最適化のプロセスを覗いてみましょう。
パターンA:長さが「定数」の場合のスマートな変身(MVC命令)
先ほどのコードのように、切り出す長さが `8` と固定(リテラル)である場合、コンパイラは非常に効率的なコードを生成します。
コンパイラは、次のような思考プロセスでアセンブラを組み立てます。
1. `IN_RECORD` の先頭アドレスに、オフセット(11文字目=10バイト進んだ位置)を足し算する。
2. 転送先 (`BIRTH_DATE`) のアドレスをレジスタにロードする。
3. マシン語の王様である `MVC`(Move Character)命令を一発発行する!
アセンブラのイメージとしては、こんな感じです(※概念的な表現です)。
- SUBSTR(IN_RECORD, 11, 8) のコンパイル結果イメージ
LA R1, 10(,R_IN_REC) 入力アドレスにオフセット(10)を加算
LA R2, BIRTH_DATE 転送先アドレスをレジスタへ
MVC 0(8,R2), 0(R1) 8バイト分をメモリ間で一気にコピー!
余計なループ処理を一切挟まず、CPUのハードウェア命令(MVC)だけでメモリ上のデータを一瞬で引っ越してしまうのです。これがメインフレームの爆速バッチ処理を支える秘密です。
パターンB:長さが「変数」の場合のダイナミックな変身(MVCL命令)
では、切り出す長さが固定ではなく、変数だったらどうなるでしょう?
DCL LEN FIXED BIN(31);
/ LENに入っている長さ分だけ切り出す /
BIRTH_DATE = SUBSTR(IN_RECORD, 11, LEN);
長さが実行時まで分からない場合、コンパイラは固定長の `MVC` 命令が使えなくなります。
ここで登場するのが、より強力で柔軟な`MVCL`(Move Character Long)命令です。
`MVCL` は、レジスタのペア(偶数・奇数レジスタ)を使って、「転送元のアドレスと長さ」「転送先のアドレスと長さ」を指定する、非常にリッチなマシン語命令です。
コンパイラは、以下のようにレジスタを緻密にハンドリングします。
- レジスタ1組に、`IN_RECORD + オフセット` と `変数LEN` をセット。
- もう1組のレジスタに、`BIRTH_DATE` のアドレスと長さをセット。
- `MVCL` 命令を呼び出し、ハードウェアに安全かつ高速にコピーを行わせる。
レガシーの世界では、「変数の長さを扱うとパフォーマンスが落ちる」と敬遠されがちですが、PL/Iコンパイラは、こうしたハードウェアの命令セット(MVCやMVCL)を極限まで効率よく使い分けられるように、賢くコードを生成してくれているのです。
—
4. アーキテクトからのアドバイス:実務で気をつけたいポイント
最後に、実務のマイグレーションや保守でPL/Iを触る際、この `SUBSTR` に関して先輩たちがよくハマる「落とし穴」をこっそりシェアしておきますね。
1. オフセットの数え方に注意!
Javaのインデックスは `0` から始まりますが、PL/Iの `SUBSTR` の第2引数(オフセット)は「1」から始まります(COBOLの桁数指定と同じ感覚です)。「あれ、1文字ずれるぞ?」というバグの多くはここから来ます。
2. 文字コード(EBCDIC)の罠
メインフレームの文字コードは基本EBCDICです。`SUBSTR` は「文字単位」ではなく、基本的に「バイト単位」で切り出しを行います。漢字(全角文字)を扱うシフトJIS(S90コードなど)の環境では、半途半端なバイト位置でスライスして文字化けを起こさないよう、文字数(`GRAPHIC` 属性や `UCS2` など)の扱いに注意してくださいね。
—
まとめ
いかがでしたでしょうか?
一見ただの便利な関数に見える `SUBSTR` も、その裏側ではPL/Iコンパイラがハードウェア(IBMメインフレームのCPU)のスペックを最大限に活かすため、`MVC` や `MVCL` といったアセンブラ命令への華麗な翻訳作業を行っています。
「レガシー言語の裏側は冷たくて難解なもの」ではなく、「機械の性能を骨の髄まで絞り出すための知恵が詰まった、人間味あふれる世界」だということが伝われば嬉しいです。
怖がらなくて大丈夫。PL/Iはあなたの味方です。
次回のシステム改修や移行作業の際にも、この「アセンブラ展開のロマン」を少しだけ思い出していただければ幸いです。それでは、快適なメインフレームライフを!
