おい、最近入った若手が「先輩、PL/Iって変数名に `IF` とか `GO` って使えちゃうんですね、これバグらないんですか?」って目を丸くして聞きに来たんだよ。
笑っちまうだろ? 今時、C言語やJavaしか触ったことのない世代からすれば、予約語の概念が希薄なPL/Iの世界は魔窟に見えるらしい。だがな、これこそがIBMメインフレームの歴史と、コンパイラの「懐の深さ」を物語る最大の特長なのさ。
今日は、この「識別子と予約語」というPL/Iの基本構文の面白さに触れつつ、基幹システムのトラブルシューティングで我々シニアが最後の砦として頼る `LIST` コンパイラオプションによるアセンブラコードの対照と検証手法 について、みっちり叩き込んでやる。心して聞けよ。
—
1. 予約語を持たないPL/Iと識別子の魔力
まずは基本のおさらいだ。C言語やJavaには、言語仕様としてガチガチに固定された「予約語(Keyword)」がある。例えば `if` や `while`、`int` などを変数名に使うことは絶対にできない。コンパイルエラーになるからな。
ところが、我らがPL/Iには、厳密な意味での「予約語」が存在しない。
正確に言えば、コンパイラは `IF` や `READ`、`WRITE` といったキーワードを認識するが、それはあくまで「その文脈においてキーワードとして解釈される」だけだ。極端な話、以下のようなコードを書くことも理論上は可能だ。
DCL IF FIXED(31) INIT(10); / 変数名を “IF” にする暴挙 /
IF = IF + 5; / 変数IFに5を加算する /
おいおい、こんなコードを本番のバッチに入れたら、コードレビューで俺に鉄拳制裁をくらうどころか、保守担当者が発狂する。なぜなら、コンパイラは文脈(Context)から「これは変数名か、それとも制御文の `IF` か」を判断しているだけであって、人間にとっては悪夢のような可読性の低さを生むからだ。
しかし、この「文脈依存の解析」という言語仕様の裏側を知ることは、コンパイラが吐き出す機械語(アセンブラコード)を読み解く上で極めて重要な意味を持つ。特に、VSAMのレコード入出力や、ONユニットが絡む複雑な制御フローをデバッグする際、コンパイラがソースコードをどう解釈し、どんなマシン語命令に落とし込んでいるのかを把握しておく必要があるのさ。
—
2. 実践:VSAM制御とONユニットを含むPL/Iバッチの構造
百聞は一見に如かずだ。実際のメインフレーム開発の現場を想定した、ちょっとした実用コードを見せてやろう。
VSAM(KSDS)ファイルからデータを読み込み、数値演算を行い、エラー時にはONユニットでトラップする、よくある夜間バッチの断片だ。大文字ベース、適切なインデント、そして `BUILTIN` 関数の活用を徹底した、模範的なコードだと思ってくれ。
/================================================================/
/ 顧客マスタ(VSAM/KSDS) 逐次読み込みとエラー処理サンプル /
/================================================================/
CUST_UPDATE: PROC OPTIONS(MAIN);
/ — 宣言部 — /
DCL CUST_FILE FILE RECORD SEQUENTIAL INPUT
ENV(VSAM);
/ 顧客レコード構造体 /
DCL 1 CUST_REC,
5 CUST_ID CHAR(8),
5 CUST_NAME CHAR(30),
5 CUST_BAL FIXED DEC(11,2);
DCL IO_EOF BIT(1) INIT(‘0’B);
DCL ERR_COUNT FIXED BIN(31) INIT(0);
/ 組み込み関数(BUILTIN)の明示的宣言 /
DCL (SUBSTR, INDEX, LENGTH) BUILTIN;
/ — 異常系制御(ONユニット) — /
ON ENDFILE(CUST_FILE) IO_EOF = ‘1’B;
ON CONVERSION BEGIN;
/ 数値変換エラー時の例外処理 /
PUT SKIP EDIT (‘数値変換エラー発生: ID = ‘, CUST_ID) (A, A);
ERR_COUNT = ERR_COUNT + 1;
GOTO NEXT_REC;
END;
/ — 処理部:ファイルオープン — /
OPEN FILE(CUST_FILE);
/ メインループ /
READ FILE(CUST_FILE) INTO(CUST_REC);
DO WHILE(^IO_EOF);
/ ビジネスロジックの模倣:残高の検証 /
IF CUST_BAL < 0 THEN DO;
PUT SKIP EDIT ('マイナス残高検知: ', CUST_ID, CUST_BAL) (A, A, F(11,2));
END;
/ ラベル(コンテキスト依存の識別子の例) /
NEXT_REC:
READ FILE(CUST_FILE) INTO(CUST_REC);
END;
/ クローズ処理 /
CLOSE FILE(CUST_FILE);
PUT SKIP EDIT ('正常終了: エラー件数 = ', ERR_COUNT) (A, F(5));
END CUST_UPDATE;
どうだ? 非常に整ったコードだろう。
だが、このソースコードがコンパイルされたとき、IBM ZのCPU(System z)上で実際にどのようなアセンブラ命令(マシン語)に変換されているか、考えたことはあるか?
「動けばいいんだよ」で済むのは新人までだ。ここぞという性能チューニングや、原因不明の異常終了(S0C4やS0C7など)を解析する際には、コンパイルリストに隠されたアセンブラコードを睨みつけるスキルが不可欠になる。
---
3. `LIST` コンパイラオプションによるアセンブラ対照と検証
コンパイル時に `LIST` オプション(JCLであれば `PARM.SYSIN=’LIST,…’` やコンパイラステートメントでの指定)を付与すると、コンパイルリストに 「Source and Object Listing(または Assembler Listing)」 が出力される。
ここには、我々が書いたPL/Iの高級言語命令と、それに対応する390アセンブラ(Assembler H または HLASM)の命令が1対1(あるいは1対多)で対照表示される。
アセンブラコードの読み方の極意
例えば、先ほどのコード内にある、以下の単純な加算処理と条件分岐を思い出してくれ。
ERR_COUNT = ERR_COUNT + 1;
これに対応するアセンブラリストを覗くと、大体以下のような機械語命令が生成されている。
00012A 58 F0 C0 10 L 15,16(,12) ; 変数 ERR_COUNT のアドレスをロード
00012E 41 F0 F0 01 LA 15,1(,15) ; レジスタ内の値に 1 を加算
000132 50 F0 C0 10 ST 15,16(,12) ; 結果をメモリー(ERR_COUNT)にストア
(※実際のオフセットやレジスタ番号は最適化レベルやコンパイラのバージョンによって異なるが、雰囲気は掴めるはずだ)
ここで重要な検証ポイントをいくつか教えよう。
1. 最適化(OPT)の影響を見極めろ
`OPT(0)` でコンパイルした場合、ソースコードの1行1行に忠実なアセンブラが生成されるため非常に追いやすい。しかし、本番用の `OPT(2)` や `OPT(3)` をかけると、ループ展開やレジスタのインライン最適化が働き、アセンブラコードは原形をとどめないほど洗練(あるいは複雑化)される。「ソースを書いた通りに動いているはずだ」という思い込みを捨てて、アセンブラの動きを信じるんだ。
2. VSAM入出力の裏側(OSサービスの呼び出し)
`READ FILE(…)` のような高級言語のI/Oステートメントは、アセンブラレベルでは `BALR` や `SVC`(Supervisor Call)、あるいはLE(Language Environment)のランタイムルーチン(`IGZ…` や `IBM…` 系)への分岐命令に展開される。バッチが無限ループに陥ったとき、プログラムがどのランタイムルーチンのどのあたりで待たされているか(VSAMのENQ待ちか、バッファI/O待ちか)は、この `LIST` リストとブレークポイントを突合せることで初めて正確に特定できる。
3. ONユニットと例外処理のコスト
`ON CONVERSION` のようなトラップ機構は、コンパイラによって内部的な管理テーブルと例外ハンドラの登録コードに変換される。頻繁に通るループの中に重いエラー処理構造を意識せずに組み込むと、アセンブラレベルで無駄なスタック操作やマクロ展開が大量発生し、パフォーマンスがガタ落ちする原因になる。 `LIST` リストを見れば、そのコードパスにどれだけのオーバーヘッドがあるかが一目瞭然というわけだ。
—
4. シニアアーキテクトからのメッセージ
メインフレームの寿命は長い。そして、そこで稼働するPL/Iプログラムもまた、何十年もの間、継ぎ接ぎされながら日本の社会インフラを支え続けている。
「予約語がない」という一見すると泥臭い言語仕様は、裏を返せば、コンパイラがプログラマの意図をどこまで柔軟に汲み取ろうとしたかという歴史の証だ。そして、そのコンパイラが最終的に吐き出すアセンブラコードを `LIST` オプションで覗き込む行為は、言ってみれば「システムの心音を聴聴器で聴く」ようなものなのだよ。
ソースコードの表面的な構文にとらわれるな。コンパイラがどう解釈し、CPUがどう実行するのか。そのレイヤーまで見通せるエンジニアこそが、これからのメインフレーム現場を救う真のスペシャリストだ。
さあ、コーヒーブレイクはここまでだ。次のマイグレーション案件のコンパイルリストが出力される頃だろ? 端末に向かおうか。
