日本-日本語 |
|
|
|
OpenVMS マニュアル |
|
HP OpenVMS MACRO コンパイラ
|
目次 | 索引 |
3.2.9 MOVPSL 命令の使用 |
MOVPSL 命令は, OpenVMS Alpha の PS をフェッチします。この内容は, VAX の PSL (プロセッサ・ステータス・ロングワード) と異なります。たとえば, OpenVMS Alpha と OpenVMS I64 の PS には条件コード・ビットが含まれていません。 OpenVMS Alpha の PS についての詳細は, Alpha Architecture Reference Manual を参照してください。
MOVSPL 命令はめったに使用されません。コードに MOVPSL 命令が含まれている場合, OpenVMS Alpha システムまたは OpenVMS I64 システムに移植するためには,コードを書き換える必要があります。
VAX Macro-32 の一部のオペレーション・コードは, Alpha Macro-32 ではビルトインになり (機能を実行するために PAL またはルーチン呼び出しにコンパイルされることがあります), Itanium ではマクロとなります (これもルーチン呼び出しに展開されることがあります)。
マクロとなったこれらのオペレーション・コードは,命令とマクロの構文上の細かい違いが原因でエラーとなることがあります。通常,オペレーション・コードのパラメータは,引数内に式がある可能性があるため,コンマで区切る必要があります。マクロの引数は単純にテキストであるため,コンマだけでなくスペースやタブも引数のセパレータと見なされます。 (一部のマクロ引数は式のように見えますが,評価されるコンテキストでのマクロ展開で使用されるまでは, Macro-32 で式としては扱われません。)
不要なスペースがなくなるように再フォーマットすることで,ソース・コードを修正することができます。
次の VAX MACRO ファイルはこの違いを示します。
3.2.10 マクロによって実装される命令
.macro Prober_mac a,b,c prober a,b,c .endm prober one, two-too(r2), three(r3) ; This one works. Prober_mac one, two-too(r2), three(r3) ; This one works. prober one, two - too(r2), three(r3) ; This one works. Prober_mac one, two - too(r2), three(r3) ; This one fails ; because the spaces are ; argument separators. prober one, - ; This one works. two - - ; Note the second argument is too(r2), - ; separated onto two lines. three(r3) Prober_mac - ; This one fails. one, - two - - ; Note the second argument is too(r2), - ; separated onto two lines. three(r3) Prober_mac - ; This one works because there one, - ; are no spaces or tabs before two-- ; or after the continuation too(r2), - ; line break. three(r3) Prober_mac - ; This one works, too. r1, - r2, - r3 .end |
VAX MACRO で使用される一部のフロー制御メカニズムは, OpenVMS Alpha システムや OpenVMS I64 システムでは,望ましい結果になりません。そのため,若干のコード変更が推奨または必要となります。
制御のフローを変更するために, JSB ルーチン内でスタック上の戻りアドレスを変更するコードがこのカテゴリに含まれます。これにはいくつかのバリエーションがあり,よく使用されます。すべて再コーディングが必要です。
3.3.1 条件コードによる通信
JSB 命令の直後に条件分岐があったり,ルーチンの最初の命令が条件分岐になっていると,コンパイラはこれを検出してエラー・メッセージを出力します。
条件コードによる暗黙的な通信を行う代わりに,状態値またはフラグ・パラメータを返します。
例を示します。
BSBW GET_CHAR BNEQ ERROR ; Or BEQL, or BLSS or BGTR, etc |
これを,次のように書き替えます。
BSBW GET_CHAR BLBC R0, ERROR ; Or BLBS |
すでに R0 を使用している場合は,スタックにプッシュし,エラーを処理し終えてから復元する必要があります。
3.3.2 JSB ルーチンから CALL ルーチン内への分岐
JSB ルーチンから CALL ルーチン内への分岐があり, .JSB_ENTRY ルーチンがレジスタを保存する場合,情報レベル・メッセージが出力されます。このような分岐に対してメッセージが出力される理由は,保存されているレジスタを復元するための,プロシージャのエピローグ・コードが実行されないためです。レジスタを復元する必要がない場合は,変更は不要です。
.JSB_ENTRY ルーチンは,おそらく呼び出し元に代わって RET を実行しようとしていると考えられます。 JSB_ENTRY によって保存されたレジスタを, RET を実行する前に復元する必要がある場合は, .CALL_ENTRY 内の共通コードを .JSB_ENTRY に変更し,両方のルーチンから呼び出すようにします。
たとえば,次のコード例があるとします。
Rout1: .CALL_ENTRY . . X: . . . RET Rout2: .JSB_ENTRY INPUT=<R1,R2>, OUTPUT=<R4>, PRESERVE=<R3> . . BRW X . . RSB |
このコードを OpenVMS Alpha システムまたは OpenVMS I64 システムに移植するには,次のように .CALL_ENTRY ルーチンを 2 つのルーチンに分割します。
Rout1: .CALL_ENTRY . . JSB X RET X: .JSB_ENTRY INPUT=<R1,R2>, OUTPUT=<R4>, PRESERVE=<R3> . . RSB Rout2: .JSB_ENTRY INPUT=<r1,r2>, OUTPUT=<R4>, PRESERVE=<R3> . . JSB X RET . . RSB |
コンパイラは,たとえば (PUSHAB label) のように,アドレスをスタックにプッシュし,以降の RSB でその場所から実行を再開しようとする操作をすべて検出し,エラーとして出力します。 ( OpenVMS VAX システムでは,次の RSB でルーチンの呼び出し元に戻ります。)
アドレスの PUSH を削除し,現在のルーチンの RSB の直前に分岐先ラベルへの明示的な JSB を追加します。これで同じ制御フローとなります。分岐先ラベルは .JSB_ENTRY ポイントとして宣言します。
たとえば,次のコードではソース・コードの変更が必要としてメッセージが出力されます。
Rout: .JSB_ENTRY . . PUSHAB continue_label . . RSB |
明示的な JSB 命令を追加することで,次のようにコードを変更します。 JSB を RSB の直前に配置している点に注意してください。この前のコードでは, PUSHAB がどこにあっても, continue_label に制御を渡すのは RSB 命令でした。新しいコードでは PUSHAB が削除されています。
Rout: .JSB_ENTRY . . . JSB continue_label RSB |
コンパイラは,スタック上の戻りアドレスの削除 (たとえば TSTL#(SP)+) を検出して,エラーとして出力します。 VAX コードでは,戻りアドレスを削除することで,呼び出し元のさらに呼び出し元に戻ることができます。
呼び出し元に対して,その呼び出し元に戻る必要があることを示す状態値を返すように,ルーチンを書き換えてください。また,最初の呼び出し元から継続ルーチンのアドレスを渡し,最下位のルーチンがそこに JSB で分岐する方法もあります。継続ルーチンが RSB で最下位のルーチンに戻ったら,最下位のルーチンが RSB で戻ります。
たとえば,次のコードに対しては,ソースの変更が必要であるというメッセージがコンパイラから出力されます。
Rout1: .JSB_ENTRY . . JSB Rout2 . RSB Rout2: .JSB_ENTRY . . JSB Rout3 ; May return directly to Rout1 . RSB Rout3: .JSB_ENTRY . . TSTL (SP)+ ; Discard return address RSB ; Return to caller's caller |
次のように,状態値を返すようにコードを書き換えることができます。
Rout2: .JSB_ENTRY . . JSB Rout3 BLBS R0, No_ret ; Check Rout3 status return RSB ; Return immediately if 0 No_ret: . . RSB Rout3: .JSB_ENTRY . . CLRL R0 ; Specify immediate return from caller RSB ; Return to caller's caller |
コンパイラは,スタック上の戻りアドレスを変更する操作をすべて検出し,エラーとして出力します。
スタック上の戻りアドレスを変更するコードは,呼び出し元に状態値を返すように書き換えてください。状態値を受け取った呼び出し元が特定の場所に分岐したり,状態値として特別な .JSB_ENTRY ルーチンのアドレスを返して,呼び出し元からそのルーチンを呼び出すようにすることができます。後者の場合,呼び出し元は,特別な .JSB_ENTRY ルーチンに対して JSB を実行した直後に, RSB を実行する必要があります。
たとえば,次のコードに対して,コンパイラはソースの変更が必要である旨出力します。
3.3.5 戻りアドレスの変更
Rout1: .JSB_ENTRY . . JSB Rout2 ; Might not return . RSB Rout2: .JSB_ENTRY . . MOVAB continue_label, (SP) ; Change return address . RSB |
次のように,戻り値を返すようにコードを書き換えます。
Rout1: .JSB_ENTRY . . JSB Rout2 TSTL R0 ; Check for alternate return BEQL No_ret ; Continue normally if 0 JSB (R0) ; Call specified routine RSB ; and return No_ret: . . RSB Rout2: .JSB_ENTRY CLRL R0 . . MOVAB continue_label, R0 ; Specify alternate return RSB |
2 つのルーチン間でのコルーチン呼び出しは,一般にそれぞれのルーチン内で一連の JSB 命令として実装されます。それぞれの JSB は,スタック上の戻りアドレスに制御を渡し,その過程でその戻りアドレスを削除します (たとえば,JSB @(SP)+)。コンパイラはコルーチン呼び出しを検出し,エラーとして出力します。
コルーチン・リンケージを開始するルーチンを,明示的なコールバック・ルーチン・アドレスを他のルーチンに渡すように書き換える必要があります。その後,コルーチンを開始するルーチンは, JSB 命令で他のルーチンを呼び出します。
例として,次のコルーチン・リンケージを考えます。
Rout1: .JSB_ENTRY . JSB Rout2 ; Rout2 will call back as a coroutine . JSB @(SP)+ ; Coroutine back to Rout2 . RSB Rout2: .JSB_ENTRY . JSB @(SP)+ ; coroutine back to Rout1 . RSB |
このようなコルーチン・リンケージに参加しているルーチンを,次のように,明示的なコールバック・ルーチン・アドレス (ここでは R6 と R7 内) を交換するように変更します。
Rout1: .JSB_ENTRY . . MOVAB Rout1_callback, R6 JSB Rout2 RSB Rout1_callback: .JSB_ENTRY . . JSB (R7) ; Callback to Rout2 . RSB Rout2: .JSB_ENTRY . . MOVAB Rout2_callback, R7 JSB (R6) ; Callback to Rout1 RSB Rout2_callback: .JSB_ENTRY . RSB |
レジスタの消費を避けるには,ルーチンの入口でコールバック・ルーチンのアドレスをスタックにプッシュします。次に,JSB @(SP)+ 命令を使用して, "直接"コールバック・ルーチンに JSB で分岐します。次の例では,コールバック・ルーチンのアドレスを R0 で渡していますが,ルーチンの入口ですぐにプッシュしています。
Rout1: .JSB_ENTRY . . MOVAB Rout1_callback, R0 JSB rout2 RSB Rout1_callback: .JSB_ENTRY PUSHL R0 ; Push callback address received in R0 . . JSB @(SP)+ ; Callback to rout2 . . RSB Rout2: .JSB_ENTRY PUSHL R0 ; Push callback address received in R0 . . MOVAB Rout2_callback, R0 JSB @(SP)+ ; Callback to Rout1 RSB Rout2_callback: .JSB_ENTRY . . RSB |
VAX での REI 命令の一般的な用途は,明示的な移行先 PC と PSL をスタックにプッシュすることで,モードを変更することです。以下の理由から,このコードは,ソース・コードをいくらか変更しないと, OpenVMS Alpha システムや OpenVMS I64 システムではコンパイルできません。
OpenVMS Alpha システムや OpenVMS I64 システムで同等の機能を実行するために,システム・ルーチン EXE$REI_INIT_STACK が作成されました。このルーチンは,新しいモードとコールバック・ルーチンのアドレスをパラメータとして受け取ります。このルーチンには,高級言語からも使用できるという利点があります。
このルーチン呼び出しを使用できるように,既存のコードを再構成する必要があります。実行を継続するコード・ラベルは,システム・ルーチンから呼び出されるため,エントリ・ポイント指示文で宣言する必要があります。
次の例は, VAX MACRO コードで RET 命令を使用してモードを変更する 2 つの方法と, OpenVMS Alpha または OpenVMS I64 において,同じことを EXE$REI_INIT_STACK ルーチンを使用して実現する 1 つの方法を示します。
PUSHL new_PSL PUSHL new_PC REI |
PUSHL new_PSL JSB 10$ . . . CONTINUE ;With new PSL . . . 10$: REI |
PUSHL Continuation_routine PUSHL new_mode ;Not a PSL CALLS #2, exe$rei_init_stack . . . Continuation_routine: .JSB_ENTRY |
プログラムが継続ルーチンに到達すると,新しいモードで新しい場所から実行され,古いモードのスタックは再初期化されます。メモリ・スタックと ( OpenVMS I64 の場合) レジスタ・スタックのベースは,新しいモードのものに設定されます。
外部モードのルーチンに引数を渡すという問題がある点に注意してください。 OpenVMS Alpha システムでは,ほとんどのレジスタは REI にまたがって保持されます。 OpenVMS I64 システムでは, R26,R27,および R10 (Alpha レジスタ R8,R9,R10 に相当) だけが保持されます。より多くのデータを渡す必要がある場合,内部モード・スタックにデータ構造を作成し,レジスタを通じてそのアドレスを外部モード・ルーチンに渡すことはできません。内部モード・スタックは, EXE$REI_INIT_STACK によってベースにリセットされるためです。代わりに,MFPR_xSP および MTPR_xSP を使用して外部モード・スタックに領域を割り当て,そこにデータを格納する必要があります。
3.3.8 ループのネスト制限
コンパイラは,正しくコードを生成するために,プログラム・コード中のループを検出する必要があります。ループの中に別のループがあったり,部分的に別のループと重なると, "ネストした"ループとなります。ループのネストが 32 レベルを超えると,コンパイラは回復不可能なエラーが発生したことを報告し,コンパイルが停止します。 VAX MACRO アセンブラではループを検出する必要がないため,このような制限はありませんでした。
コンパイラによってこのエラーが報告された場合は,この制限を超えないようにコードを再構成してください。
目次 | 索引 |
|