This file documents the use and the internals of the GNU compiler.

Copyright (C) 1988 Free Software Foundation, Inc.

Permission is granted to make and distribute verbatim copies of this manual provided the copyright notice and this permission are preserved on all copies.

Permission is granted to copy and distribute modified versions of this manual under the conditions for verbatim copying, provided also that the section entitled ``GNU CC General Public License'' is included exactly as in the original, and provided that the entire resulting derived work is distributed under the terms of a permission notice identical to this one.

Permission is granted to copy and distribute translations of this manual into another language, under the above condition for modified versions, except that the section entitled ``GNU CC General Public License'' may be included in a translation approved by the author instead of in the original English.

Introduction
************

This manual documents how to run, install and port the GNU C compiler, as well as its new features and incompatibilities, and how to report bugs.

GNU CCの一般公開許可条項
***************

(1988年2月11日公示)

ほとんどのソフトウェア企業の、ソフトウェア使用許可契約(License Agreements)は、企業側の為すがままです。それと対照的に、私たちの一般公開許可(general public license)は、 GNU CCを共有する権利を誰にでも与えることをねらいとします。あなたに確実にこの権利を持っていただきたいために、私たちには、誰かがあなたにこの権利を拒絶することを禁じ、この権利の放棄をあなたに迫ることを禁ずる、という制約を設ける必要があります。これが、この一般公開条項の目的です。

とりわけ私たちは、あなたにGNU CCのコピーを他の人にあげる権利があること、ソースコードを受け取ることができ、必要なら入手できること、 GNU CCを変更したり、その一部を新たな無料のプログラムの中で使用できること、そして、これらのことができるのだと、あなたに分かっていること、以上を確実化したいと思います。

誰もが確実にそういう権利を持てるために、あなたが誰かからこれらの権利を奪うことを、私たちは禁じなければなりません。たとえば、あなたがGNU CCのコピーを配布するときには、あなたが持っている権利をすべて、受け取る人々に与えなければなりません。あなたは、彼らがソースコードも同時に受け取るか、または確実に入手できるよう図らなければなりません。

また、私たち自身を守るために、 GNU CCには瑕疵保証(warranty)がないという点をみんなに分かってもらわなければなりません。誰かがGNU CCを変えて他の人に渡す場合は、他の人の導入した問題が私たちの評価に影響を及ぼさないよう、どこをどう変えたのかが分かるようにしておいてください。

したがって、私たち(Richard StallmanとFree Software Foundation, Inc.)は、GNU CCの再配布や変更を行う場合の要件として、以下の条項を定めます。

コピーの方針
======

 1. GNU CCのソースコードは、それを受け取ったときと同じ状態で、どんな媒体で配布してもよい。ただし、それぞれのコピーには、 ``Copyright (c) 1988 Free Software Foundation, Inc.'' という著作権注記を明確かつ適切に付けること(年代は適宜)。すべてのファイル上の、使用許可契約(License Agreement)や瑕疵保証がないこと (absence of any warranty) に関する注記を、削除・変更等しないこと。 GNU CCプログラムを受け取る人は、それとともに、使用許可契約のコピーも受け取ること。コピーの転送にかかる物理的な経費は相手に請求してよい。

 2. GNU CCのどの部分を変更してもよいし、変更したものを上記の1.に従って配布してもよい。ただしその場合は、以下のことを守ること。

       * 変更したファイルには、変更したという注記を、変更の日付とともに明記すること。

       * 配布または刊行する作品が、全体的または部分的にGNU CCまたはGNU CCの一部を含むか、またはそれらから派生したものである場合は、本使用許可契約中のものと同一の条項に基づいて、すべてのサードパーティーの一部または全体に対して無料で使用許可が与えられること (ただし、保証からの保護を、サードパーティーの一部または全体に対して拡張することはしてもよい)。

       * 転送にかかる物理的な経費は、配布費用として請求してよい。

     配布媒体に他のプログラムがこのプログラム (またはこのプログラムからの派生品) と共に、ただ単に存在する場合は、他のプログラムはこれらの条項の対象にはならない。

 3. GNU CC (または、パラグラフ2によりその部分または派生品)は、以下の条件により、上記のパラグラフ1と2に基づいて、オブジェクトコードまたは実行形式にてコピーや配布をしてよい。

       * 上記のパラグラフ1と2の条項に従って配布されるべき、機械可読な完全なソースコードを付随させること。

       * または、上記のパラグラフ1と2の条項に従って配布されるソースコードをいかなるサードパーティーにも (郵送実費等を除き) 無料で提供するむねの、少なくとも3年間は有効な書面を付随させること。

       * または、ソースコードの入手先に関する情報を受け取っている場合には、その情報を付随させること (この方式は、プログラムをオブジェクトコードまたは実行形式でのみ受け取っている、商用でない配布にかぎって認められる)。

     実行形式ファイルに関しては、完全なソースコードとは、その実行形式ファイルに含まれるすべてのモジュールのソースコードを意味するが、しかし特別の例外として、その実行形式ファイルがその上で走るオペレーティングシステムに付随している、標準ライブラリのモジュールのソースコードは含まれなくてもよい。

 4. GNU CCのコピー、サブライセンス、配布、または転送は、この使用許可契約(License Agreement)を付随させずに行ってはならない。使用許可契約を付けずにGNU CCをコピー、サブライセンス、配布、または転送しようとする試みは法的に無効とし、当該者は本契約の下でプログラムを使用する権利を失うものとする。しかしながら、使用許可契約を付けてプログラムを受け取ったパーティーは、これら条項を遵守するかぎり、許可を失わないものとする。

 5. GNU CCの一部を、他の配布条件が異なる無料プログラムに使用する者は、 Free Software Foundation 675 Mass Ave, Cambridge, MA 02139 に報告せよ。われわれは、この件に関する単純なルールを未だ制定していないが、そういうケースは許可する場合が多い。その際に指針となるのは、われわれのフリーなソフトウェアからの派生品すべてに対して、やはりフリーなステータス(free status)を維持したいということ、および、ソフトウェアの共有化や利用の社会的広まりを促進したい、という目標である。

われわれのライセンシング方針や、ソフトウェアに関するあなたの意見を歓迎する！宛先は、 Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139 または電話で(617)876-3296まで。

瑕疵保証の放棄(NO WARRANTY)

GNU CCは無料でライセンスされるので、われわれは、法が許可しているいかなる瑕疵保証をも提供しない。特に注記のないかぎりは、 Free Software Foundation, Inc., Richard M. Stallman および他のパーティーは、 GNU CCを、いかなる種類の保証もないas isベース(現品ベース)にて提供する。いかなる種類の保証もとは、明示的な保証と暗黙の保証の両方を含み、それの商品性に関する保証、特定の目的への適合性の保証、等々、いっさいがっさいの保証を意味する。 GNU CCの品質と性能に関する全リスクは、ユーザの負担とする。 GNU CCに欠陥に由来する、修理等にかかわる全費用はユーザの負担とする。

Richard M. Stallman, Free Software Foundation, Inc., および他のパーティーは、 GNU CCを上記の条件に基づいて変更する際に、変更によってユーザに生じた損害に対し、特に法律に定めがないかぎりは免責とする。その損害とは、利益の喪失、金品の喪失、あるいはGNU CCの使用または使用不能から生ずるその他の、特殊な、事故的な、あるいは当然の損害をも含むものとする。またそれら損害には、データの喪失、データの劣化、サードパーティーによって生じた損失、当プログラムが他のプログラムと共働できなかったことに由来する過失、等が含まれるものとする。他のパーティーより、そのような損害の可能性について事前にアドバイスや告知等を受けていた場合にも、この無保証条項は適用されるものとする。

GNU CCのコマンドオプション
****************

GNU Cコンパイラのコマンドシンタクスは、UNIX Cコンパイラのそれとよく似ています。プログラムgccは、オプションとファイル名をオペランドとして受け付けます。複数の一文字オプションを一箇所にまとめて書くことはできません。たとえば、-drと書くと、それは-d -rと同じではありません。

起動したGNU CCは通常、プリプロセス、コンパイル、アセンブル、そしてリンクをします。名前が.cで終わっているファイルはCのソースファイルとみなされて、プリプロセスされ、コンパイルされます。そして、コンパイルの出力ファイルと、名前が.sで終わっているファイルはアセンブルされます。さらにその結果のオブジェクトファイル、およびその他の入力ファイルが、ともにリンクされて実行ファイルを作り出します。

コマンドオプションによってこの処理を、途中の段階で止めさせることができます。たとえば、-cオプションは、リンクをするな(リンカを起動するな)という指定です。この指定をすると、アセンブラから出力されるオブジェクトファイルが出力ファイルになります。

その他のコマンドオプションは、どれかひとつの段階に作用します。たとえば、プリプロセッサを制御するオプションがあり、コンパイラを制御するオプションがあります。また、アセンブラやリンカを制御するためのオプションもありますが、これらはこの文献では扱いません。 GNUのアセンブラとリンカはまだリリースされていないからです [訳注:この文献の発行日付は1988年8月17日]。

以下のオプションは、コンパイル過程を制御するオプションであり、リンクやアセンブルなどをする・しないのオプションも含まれます。

-o FILE
     出力をFILEに出力します。このオプションは、出力が実行ファイル、オブジェクトファイル、アセンブラへのファイル、プリプロセスされたCのコードなど、何の場合でも適用されます。

     -oの指定がないと、実行ファイルはデフォルトでa.outに出力され、 source.cのオブジェクトファイルはsource.o、アセンブラファイルはsource.sとなり、プリプロセス後のCファイルは標準出力に出力されます。

-c
     ソースファイルをコンパイルないしアセンブルしますが、リンクをしません。名前が.cや.sで終わっている入力ファイルから、 .oで終わっているオブジェクトファイルを作り出します。入力ファイルにオブジェクトファイルを指定すると、それに対しては何もしません。

-S
     アセンブラへのコードへとコンパイルし、アセンブルをしません。名前が.cで終わっている入力ファイルから、 .sで終わっているアセンブラファイルを作り出します。入力ファイルとしてアセンブラのソースファイルやオブジェクトファイルを指定すると、それに対しては何もしません。

-E
     Cのプリプロセッサだけを実行します。指定されたCのソースファイルをすべてプリプロセスし、結果を標準出力に出力します。

-V
     コンパイラのドライバプログラムが、プリプロセッサ、コンパイラ本体、アセンブラ、およびリンカを実行させていくとき、その実行コマンドを表示します。これらの個別プログラムのバージョン番号が表示される場合もあります。

-BPREFIX
     コンパイルのドライバプログラムが、プリプロセッサ(cpp)、コンパイラ本体(cc1)、アセンブラ(as)、およびリンカ(ld)を実行させていくとき、 PREFIXがこれらのプログラムのプリフィクス(共通前置名)だとみなします。

     これらの個別プログラムを実行させるとき、ドライバは最初に-Bオプションを探します。そして、PREFIXで指定されている名前が存在しないか、または-Bの指定がない場合は、/usr/lib/gcc-および /usr/local/lib/gcc-という２つの標準プリフィクスを探します。このどちらでもファイル名が見つからないときは、環境変数PATHで指定されているディレクトリに、プリフィクスの付かないプログラム名を探します。

     ランタイムのサポートファイルであるgnulibも、 -Bプリフィクスによるによる探索をします。その名前で見つからないときは、上述の２つの標準プリフィクスで探し、それで終わりです。以上の方法でファイルが見つからなければ、リンクされないままになります。ほとんどの場合、そしてほとんどのマシン上で、このファイルは無くて済みます。

以下のオプションは、Cのコンパイル過程を制御します。

-ansi
     ANSI規格に合致するCプログラムをサポートします。

     これによって、GNU CのANSI Cと非互換な機能、たとえばasm、inline、 typeofといったキーワードや、使用システムを識別するためのunix、 vaxといった事前定義マクロが無効になります。またこれによって、あの感心しない、そしてめったに使われない、 ANSIのトライグラフが有効になります。

     -ansiオプションによって、 ANSI流でないプログラムがむやみに拒絶されることはありません。そうさせるためには、-ansiに加えて-pedanticが必要です。

     -ansiオプションを指定すると、 __STRICT_ANSI__というマクロが事前定義されます。一部のヘッダファイルがこのマクロの制御を受け、 ANSI規格が要請していない関数やマクロなどの宣言を抑止します。これは、それらの名前を他のことに使っているプログラムへの、悪影響を防ぐためです。

-traditional
     伝統的なCコンパイラの特性をサポートしようとする試みです。特に以下のことが挙げられます。

       * extern宣言はすべて、それが関数定義の内部に書かれているときでも、グローバルに効果する。これには暗黙的な関数宣言(明示的な宣言をしないで関数を使うこと)も含まれる。

       * typeof、inline、signed、const、およびvolatileキーワードは認識されない。

       * ポインタと整数との比較は常に許される。

       * 浮動小数点数の値域外の定数はエラーにならない。

       * プリプロセッサにおいて、コメントは一個のスペースには変換されず、むしろ何にも変換されない。これによって、コメントを利用しての、伝統的なトークン連結が可能になる。

       * プリプロセッサにおいて、マクロをスキャンしているときには単引用符文字と二重引用符文字は無視されるので、マクロの引数は文字列や文字定数中にあっても置換される。また、条件ディレクティブ内の条件に合わなかった側のテキストをスキップするときにも、引用符文字は無視される。

-O
     最適化を行います。最適化を伴うコンパイルは、より時間を要し、大きな関数の場合はより多量のメモリを要します。

     -Oがないときは、コンパイラはコンパイルのコストの低減を優先する処理をし、デバッグの際にコードの外見どおりの結果が出るようにします。この場合は、文はお互いに他の文に依存しないので、文と文との間のブレークポイントでプログラムを停止して、そこで新たな値を変数に代入したり、プログラムカウンタの値を変えて関数中の他の文に実行を移したりしても、結果はソースコード(の外見的な姿)から期待されるとおりになります。

     -Oがないと、 registerと宣言された変数だけがレジスタに割り振られます。コンパイル結果のコードは、-OなしのPCCが生成するものよりも、やや悪くなります。

     -Oを指定すると、コンパイラはコードサイズと実行時間の削減に努めます。

     後で述べる-fオプションによって、特定の種類の最適化をonにしたりoffにしたりできます。

-g
     オペレーティングシステムのネイティブフォーマットでのデバッグ用情報を生成します(DBXやSDB用)。

     他の多くのコンパイラと違って、GNU CCでは-gと-Oを併用できます。最適化によってコードの一部が省略されたりして、プログラマをびっくりさせることがあります。宣言したはずの変数がなかったり、予期せぬ所で制御の流れが変わったり、一部の文は演算結果が既知の定数の値と同じなので実行されなかったり、あるいはループの外に出されたために ---ソース中に書いた場所とは--- 違う場所で文が実行されたりします。にもかかわらず、最適化された出力をデバッグすることは可能なのです。このように、バグがあるかもしれないプログラムをオプティマイザに通すことが、ときには理にかなっている場合があるのです。

-gg
     GDBのフォーマットでのデバッグ用情報を生成します。このオプションが有効なためには、GNUのアセンブラとリンカが必要です。

-w
     警告メッセージ(ウォーニング)をすべて抑止します。

-W
     以下の事象に関する、拡張警告メッセージを出力します。

       * 初期化されていない自動変数が使われている。この警告は最適化コンパイルを指定したときだけ出ます。この警告を出すためにはデータフローに関する情報が必要ですが、それは最適化のときだけ求めるからです。この警告は、レジスタ変数の候補となるような変数だけを対象とします。したがって、volatileと宣言された変数や、アドレスがある変数、そしてサイズが1、2、4、または8バイト以外の変数には適用されません。また、構造体、共用体、および配列に対しは、それらがレジスタ上にあるときでも適用されません。

          値を計算するために使われるが、その値が全然使われていないという場合の変数に関しては、警告が出ないこともあるという点に注意してください。そのような計算は、警告が出力される以前のフロー解析のパスで、削除されることがあるからです。

          エラーのように見えるコードが実は正しいという状況をすべて理解できるほど、 GCCはお利口ではないので、この警告をオプションで指定するようにしたのです。そういう状況の例を下に示します。

               {
                 int x;
                 switch( y ) {
                 case 1: x = 1; break;
                 case 2: x = 2; break;
                 case 3: x = 5;
                 }
                 foo( x );
               }

          yの値が常に必ず1、2または3なら、xは常に初期化されますが、でもこのことはGNU CCには分かりません。もうひとつのよくあるケースは以下のようになります。

               {
                 int save_y;
                 if( change_y ) save_y = y, y = new_y;
                 ...
                 if( change_y ) y = save_y;
               }

          save_yは値セットされたときだけ使われる(式の右値になる)ので、これにはバグはないのです。

       * 非揮発性(volatileでない)自動変数が、 longjmpのコールによって変えられるかもしれない場合は、この警告は最適化コンパイルのときだけ可能です。

          コンパイラに見えるのはsetjmpのコールだけです。 longjmpがどこでコールされるのかコンパイラには分からないし、事実、シグナルハンドラはコード中の不特定箇所でそれをコールできます。その結果、実際には問題がない場合でも警告が出ることがあるでしょう。 longjmpは、絶対に問題を起こす箇所でコールされてはならないからです。

       * 関数が帰値を返しても返さなくてもよいように書かれている (単純に関数の最後まで達した場合は、その関数は帰値を返さないとみなされます)。

          たとえば、次の関数はこの警告を出します。

               foo (a)
               {
                 if (a > 0)
                   return a;
               }

          GNU CCは、リターンしなくても正常という関数(abortやlongjmpなど) を見分けることができないので、無意味な警告を出すこともあります。

     将来は、この他の有益な警告も、このオプションで可能にする予定です。

-Wimplicit
     関数の宣言が暗黙裡である(=明示的な宣言がない)ときに警告を出します。

-Wreturn-type
     デフォルトでintとなるような帰値が定義されている関数に関して警告を出します。また帰値がvoid型ではない関数で、return文に帰値がない場合にも警告を出します。

-Wcomment
     コメント中にコメント開始文字列/*があるときに警告します。

-Wall
     以上の-Wオプションをすべて指定します。

-p
     分析プログラムprof用のプロファイル情報をコード中に書き込みます。

-pg
     分析プログラムgprof用のプロファイル情報をコード中に書き込みます。

-lLIBRARY
     LIBRARYという名前のライブラリ (実際のファイル名はlibLIBRARY.a)を、標準ディレクトリリストから探します。リンカは、正確にその名前のファイルをリンクしようとします。

     探索されるディレクトリは、処理系の標準的なディレクトリと、ユーザが-Lで指定したディレクトリです。

     通常は、こうして見つかるファイルはライブラリファイル(オブジェクトファイルのアーカイブファイル)です。リンカはアーカイブファイルをスキャンして、コード中で参照されている未定義シンボルが、定義されているオブジェクトファイルを探します。見つけたのがアーカイブファイルではなく、普通のオブジェクトファイルなら、それは普通にリンクされます。-lオプションを使うことと、ファイル名を指定することとの唯一の違いは、-lはいくつかのディレクトリを探索するという点です。

-LDIR
     -lで探すディレクトリのリストにディレクトリDIRを加えます。

-nostdlib
     リンク時に、処理系の標準ライブラリとスタートアップファイルを使いません。ユーザが指定したファイル(とgnulib)だけが、リンカに渡されます。

-mMACHINESPEC
     ターゲットのマシンのタイプに関する指定をする、マシン依存のオプションです。これらのオプションは、マシン記述ファイル中のTARGETSWITCHESというマクロで定義されます。このオプションのデフォルトも、このマクロで定義され、また、デフォルトを変えることもできます。

     以下は68000のマシン記述ファイル中で定義される-mオプションです。

     -m68020
          68020用の出力(68000ではなく)を生成します。これが、無変更のソースを使う場合のデフォルトです。

     -m68000
          68000用の出力(68020ではなく)を生成します。

     -m68881
          浮動小数点用に68881の命令を含む出力を生成します。これが、無変更のソースを使う場合のデフォルトです。

     -msoft-float
          浮動小数点用にライブラリコールをする出力を生成します。

     -mshort
          int型がint shortと同じく16ビットとみなします。

     -mnobitfield
          ビットフィールド命令を使いません。 -m68000は、-mnobitfieldを含みます。

     -mbitfield
          ビットフィールド命令を使います。 -m68020は、-mbitfieldを含みます。これが、無変更のソースを使う場合のデフォルトです。

     -mrtd
          引数の数が決まっている関数がRTD命令を使うリターンを行い、リターン時に引数をポップします。これにより、コール側が引数をポップする命令を省略できます。

          このリターン方法は、UNIXで普通使われている方法と非互換です。したがって、UNIXのコンパイラでコンパイルしたライブラリをコールする場合には、このオプションを使えません。

          また、可変個の引数をとる関数(printfなど)についてはすべて、関数プロトタイプを提供しなければなりません。そうしないと、そういう関数のコールで不正なコードが生成されるでしょう。

          さらに、関数に渡した引数の個数が多すぎた場合には、深刻に不正なコードが生成されるでしょう(通常は、余計な引数は無視され無害です)。

          RTD命令は68010と68020プロセッサがサポートしており、68000にはありません。

     以下の-mオプションは、VAXのマシン記述で定義されています。

     -munix
          VAX用のUNIXアセンブラが長い分岐幅を扱えないため、一部のジャンプ命令(aobleqなどの)を出力しません。

     -mgnu
          アセンブラはGNUのアセンブラを使うという前提で、上記のジャンプ命令を出力します。

     -mg
          d-フォーマットではなく、g-フォーマットの浮動小数点用のコードを出力します。

-fFLAG
     マシン非依存のフラグを指定します。以下がそれらのフラグです。

     -ffloat-store
          浮動小数点変数をレジスタにストアしません。これは浮動小数点数用の (FPPの)レジスタが、普通のdoubleよりも高い精度の値を保持してしまう、 68000(FPPは68881)などのマシンに対して、そういう望ましくない過剰な精度を抑止します。

          多くのプログラムにとって過剰な精度は歓迎ですが、浮動小数点の表現が厳密にIEEEの定義どおりであることに依存するプログラムも一部にはあります。そういうプログラム用には-ffloat-storeを指定してください。

     -fno-asm
          asm、inline、およびtypeofをキーワードとして認識しません。そこでこれらの語を名前として使えます。

     -fno-defer-pop
          個々の関数コールのたびに、その関数のリターン後すぐに、その関数への引数をポップします。通常(最適化時の)コンパイラは、複数の関数コールの引数をスタックに積んでおき、一度にポップします。

     -fcombine-regs
          結合パス(combine pass)において、レジスタをコピーする命令を他の命令と結合(一体化)します。これは-Oオプションと併用したとき、良いコードを生成するかどうか定かでありません。このフラグの効果については色々経験談をお聞かせください。

     -fforce-mem
          メモリ上のオペランドを、レジスタにコピーしてから算術演算します。これはメモリ参照をすべて、共通副次式(common subexpression、共通部分式)の一種だと見なすことであり、より良いコードを生成する場合があるでしょう。共通副次式でない場合でも、命令の結合によって、個々毎回のレジスタロードは省略できるはずです。このフラグの効果についてはユーザの経験談を色々お聞かせください。

     -fforce-addr
          フレームポインタを必要としない関数に関しては、フレームポインタをレジスタ上に保持しません。これによってフレームポインタをセーブ、セットアップ、およびリストアする命令が不要になります。それに、レジスタを他の目的に使えるという利点が生きる関数も多いでしょう。なお、これによって、スタックフレームに関するデバッグができなくなります。

          VAXなど一部のマシンでは、標準の関数コール手順でフレームポインタが自動的に処理され、その有無を問題にすることが無意味になるので、このフラグは何の効果も持ちません。 FRAME_POINTER_REQUIREDというマシン記述マクロによって、ターゲットのマシンがこのフラグをサポートするかどうかが決まります。See レジスタ。

     -finline-functions
          単純な関数はすべて、それをコールしている関数中に統合します。関数が、このような統合をしてよいほど単純かどうかを、コンパイラはヒューリスティック的に判定します。

          その関数へのコールがすべて統合化でき、そしてその関数がstaticと宣言されていたら、それはもはや独立の関数としてはアセンブラのコードへとコンパイルされません。

     -fkeep-inline-function
          ある関数へのコールがすべてコール側に統合化でき、そしてその関数がstaticと宣言されていても、ランタイムにコール可能な独立の関数としてコンパイルします。

     -fwritable-strings
          文字列定数を書き込み可能なデータセグメントにストアし、それらのユニーク化をしません。これは、文字列定数への書き込みができることを前提としている、古いプログラムへの互換性のためです。文字列定数に書き込みをすることは、きわめて悪質です。定数は文字通り定数であるべきです。

     -fno-function-cse
          関数のアドレスをレジスタに置かず、定関数をコールする個々の命令に、その関数のアドレスが明示的に存在するようにします。

          このフラグは非効率なコードを生成しますが、アセンブラの出力を (ハンドオプティマイズにより)変えるような場合は、関数をコールする命令に関数のアドレスがオペランドとしてないと困る場合もあるでしょう。

     -fvolatile
          ポインタによるすべてのメモリ参照をvolatileと見なします。

     -funsigned-char
          char型を無符号型、すなわちunsigned charのように扱います。

          charの扱いはマシンのデフォルトとして決まっています。それは unsigned charであるか、signed charであるかのどちらかです (現状では事実上、常にsignedがデフォルトですが)。

          char型は、signed charまたはunsigned charのどちらかと実質的に同じであっても、常に独立別個のデータ型として扱われます。

     -fsigned-char
          char型を無符号型、すなわちsigned charのように扱います。

     -ffixed-REG
          レジスタREGを固定レジスタとして扱います。生成されるコード中でそのレジスタは、スタックポインタ、フレームポインタなどの、固定的な役割にのみ使われます。

          REGはレジスタ名です。レジスタ名はマシン固有であり、マシン記述マクロファイルの中のREGISTER_NAMESマクロに定義されているものです。

     -fcall-used-REG
          レジスタREGを、コールした関数中で使うことによって中身が破壊されてもよいレジスタとして扱います。そのレジスタは、関数コール後には必要のない一時変数や作業用変数用として使われます。関数は、この指定をされたレジスタのセーブとリストアをしません。

          スタックポインタやフレームポインタなど、そのマシンの実行モデルにおいて、固定的かつ広域的な役割を担っているレジスタにこのフラグを指定すると、悲惨な結果になるでしょう。

     -fcall-saved-REG
          レジスタREGを、変数用として使ってもよいが、関数コールに際してセーブされるレジスタとして扱います。一時変数や作業用変数として使っても、関数コールに際して保全されます。関数は、この指定をされているレジスタを使用する場合に、セーブとリストアを行います。

          スタックポインタやフレームポインタなど、そのマシンの実行モデルにおいて、固定的かつ広域的な役割を担っているレジスタにこのフラグを指定すると、悲惨な結果になるでしょう。

          関数が帰値を返すために使うレジスタにこのフラグを指定すると、上とは別の意味で惨事を招くでしょう。

-d文字
     文字で指定される時期にデバッグ用のダンプ出力をします。文字とその意味は下記のとおりです。

     r
          RTLの生成の後にダンプします。

     j
          最初の分岐最適化の後にダンプします。

     J
          最後の分岐最適化の後にダンプします。

     s
          共通副次式の排除(とその後の分岐最適化)をした後にダンプします。

     L
          ループ最適化の後にダンプします。

     f
          フロー解析の後にダンプします。

     c
          命令結合の後にダンプします。

     l
          ローカルレジスタ変数の割り当て後にダンプします。

     g
          グローバルレジスタ変数の割り当て後にダンプします。

     m
          実行後に、メモリ利用に関する統計を表示します。

-pedantic
     ANSI Cの標準規格に厳密に従って、それに関する警告をすべて発行します。 ANSI Cにない拡張機能を使っているプログラムはすべて拒否します。

     ANSI Cに準拠しているプログラムは、このオプションの有無に関らず正しくコンパイルされるはずです(ごく一部は-ansiを必要としますが)。ただし、このオプションがないと、GNU特有の拡張機能や、伝統的なCの機能もサポートされます。このオプションを付けると、それらは拒否されます。このオプションを使わなければならない理由は何もありません。これは、学者ぶった杓子定規な人物(pedants)を満足させるためにのみ存在しているのです。

以下は、Cのプリプロセッサを制御するオプションです。プリプロセッサはコンパイル前の個々のCソースファイルに対して実行されます。-Eオプションを指定すると、 プリプロセスだけを行います。以下のオプションの一部は、プリプロセッサの出力が(コンパイラへ行かずに)実際に出力される-Eオプションと、併用したときのみ有意味です。

-C
     コメントを捨てないようプリプロセッサに指示します。 -Eオプションとともに使います。

-IDIR
     インクルードファイルをディレクトリDIRに探します。

-I-
     この-I-オプションの前の-I-オプションで指定したディレクトリを、 #include "FILE"に関してのみ探し、#include <FILE>に関しては探しません。

     -I-オプションの後にさらに-I-オプションでディレクトリが指定されていると、そのディレクトリはすべての#includeディレクティブに関して探します(通常の-I-オプションは、このように扱われます)。

     さらに、-I-オプションを指定すると、#include "FILE"に関しカレントディレクトリを探しません。したがって、あらためて-Iオプションで指定したときだけ、カレントディレクトリを探します。-I-と-Iを両方使うことによって、ディレクトリをカレントディレクトリよりも前に探させるか、後に探させるかといったことを、細かく制御できます。

-nostdinc
     ヘッダファイルを、処理系の標準ディレクトリには探しません。-Iオプションで指定したディレクトリ(および、できればカレントディレクトリ)だけを探します。 -nostdincと-Iの間にディレクトリ指定をはさむことによって、指定以外のすべてのディレクトリを探させないようにできます。

-M
     各ソースファイルの依存関係を記述した、makeにとって便利なルールを出力するように、プリプロセッサに指示します。プリプロセッサはそれぞれのソースファイルに関して、そのソースファイルのオブジェクトファイル名をターゲットとし、依存関係をそのすべての#includeファイルで表したmake用ルールを出力します。このルールは一行、または\と改行でつなげた複数行です。

     -Mは-Eを含みます。

-MM
     -Mと似ていますが、ユーザ定義のヘッダファイルを示す #include "FILE"のファイルだけを記述したmakeルールを出力します。#include <FILE>でインクルードされる処理系のヘッダファイルは、省略されます。

     -MMは-Eを含みます。

-DMACRO
     マクロMACROを空の文字列として定義します。

-DMACRO=DEFN
     マクロMACROをDEFNとして定義します。

-UMACRO
     マクロMACROを未定義とします。

-T
     ANSI Cのトライグラフをサポートします。この脳障害(brain-damage)については知りたくもないでしょう。-ansiオプションも同じ効果を持ちます。

GNU CCのインストール
*************

以下に、GNU CCをUNIXシステムにインストールする場合の手順を述べます (VMSについては後述します)。

 1. Makefileをエディットします。HPUXまたは何らかの形式のsystem Vを使っているときは、ファイルの冒頭に記されている、いくつかの変更を行わなければなりません。

 2. Sequentのシステム上では、バークレー(Berkeley)の世界へ行ってください。

 3. コンフィギュレーションファイルを選びます。

       * 使用するマシン用のトップレベルのコンフィギュレーションファイルへの、 config.hという名のシンボリックリンクを作ります (see コンフィギュレーション)。このファイルがホストのマシンに関する情報を定義する役割を担います。それにはtm.hが含まれます。

          ファイル名はconfig-MACHINE.hとします。ただし以下は既定です。

          config-vms.h
               …VMSが走るVAX用

          config-vaxv.h
               …system Vが走るVAX用

          config-i386v.h
               …system Vが走る80386用

          config-sun4.h
               …Sun OS v.4が走るSun(モデル3または4)用

          config-hp9k3.h
               …HP 9000 series 300用

          config-gnx.h
               …Genixが走るns 32000用

          シンボリックリンクをサポートしていないマシンの場合は、config.hの中に適切なファイルの#includeが含まれるようにセットアップします。

       * 使用するマシン用のマシン記述マクロファイル(その名前はtm-MACHINE.h でなければならない)への、tm.hという名のシンボリックリンクを作ります。

          68000のシステムの場合は、ファイルtm-m68k.hのディレクトリを使わず、以下のどれかを使います。

          tm-sun3.h
               …Sun 3用

          tm-sun2.h
               …Sun 2用

          tm-3b1.h
               …AT&T 3b1(またの名7300 UNIX PC)用

          tm-isi68.h
               …Integrated Solutionのシステム用

          tm-news800.h

          tm-hp9k320.h
               …HPUXのシステムで、アセンブラとリンカもシステムのものを使うとき用

          tm-hp9k320g.h
               …HPUXのシステムで、6NUのアセンブラとリンカ、およびその他のユーティリティを使用するとき用。この動作モードで必要なGNUのソフトウェアは、まだすべては配布されていません。したがって、詳細は将来の版で記述します。

          VAX用には、BSD UNIX上ではtm-vax.h、system V上ではtm-vaxv.h、 VMS上ではtm-vms.hを使います。

          SPARC(Sun 4)用には、tm-sparc.hを使います。

          モトローラ88000用にはtm-m88k.hを使います。88000のサポートには、出力を実行する方法がないため、未完成な点がいくつかあります。ブランチテーブルと、関数の前始末(prologue)・後始末(epilogue)にバグがあると思われます。

          80386用には、tm-i386ディレクトリを使ってはいけません。ターゲットのマシンがsystem Vを走らせているなら、tm-i386v.h、Sequent 386システム用にはtm-seq386.h、Compaqtm-compaq.hを使ってください。 32000用には、Sequentのマシンならtm-sequent.h、Encoreのマシンなら tm-encore.h、Genix version 3ならtm-gnx.hを使います。それ以外は tm-ns32k.hでたぶん大丈夫です。

          Genixはallocaとmallocにバグがあるので注意してください。これらは、6NU Emacsからコンパイルされたバージョンを得て、GNU CCの、 Makefileをエディットしてから、使わなければなりません。

          EncoreのシステムはBSDの下で使うという前提でのみサポートされていますので、注意してください。

       * マシン記述パターンファイル(その名前はMACHINE.md)にmdという名のシンボリックリンクを作ります。

       * 使用するマシン用の出力サブルーチンファイル(その名前は output-MACHINE.cへの、aux-outputという名のシンボリックファイルを作ります。

 4. パーサジェネレータBisonがインストールされていることを確認します (Bisonの出力ファイルc-parse.tab.cとcexp.cが、c-parse.yと cexp.yより新しく、.yファイルを変えるつもりがなければ、これは不要です)。

     Bisonの版が古いと、%expectディレクティブの行でエラーになります。その場合はc-parse.yからその行を削除してください。

 5. Sunを使う場合は、環境変数FLOAT_OPTIONが設定されていないことを確認してください。 gnulibのコンパイル時にこれがf68881に設定されていると、その結果のコードはリンク時に特殊なスタートアップファイル要し、正しくリンクするのに苦労するでしょう。

 6. コンパイラを作ります。コンパイラのディレクトリでmakeとタイプするだけです。

 7. このコマンドで、第一ステージのオブジェクトファイルと実行ファイルをサブディレクトリに移します。

          make stage1

     ファイルはstage1というサブディレクトリに移動します。インストールの完了後は、これらのファイルをrm -r stage1で削除して構いません。

 8. コンパイラをコンパイラ自身でコンパイルします。そのコマンドを次に示します。

          make CC=stage1/gcc CFLAGS="-g -O -Bstage1/"

     浮動小数点用のハードウェアのない68000または68020のシステムでは、デフォルトでそういうハードウェアの不在を前提としているtm.hを使っていないかぎり、以下のコマンドを使います。

          make CC=stage1/gcc CFLAGS="-g -O -Bstage1/ -mfost-float"

 9. もう一度コンパイラをコンパイルすることによって、コンパイラをテストしたいときは、次のようにします。

          make stage2
          Make CC=stage2/gcc CFLAGS="-g -O -Bstage2/"
          foreach file (*.o)
          cmp $file stage2/$file
          end

     これによって、このステージ3のオブジェクトファイルのどれかが、ステージ2からのものと違っていると、メッセージが出ます。違いがあると、それがどんなみ無害なものであっても、ステージ2のコンパイラがGNU CCを不正にコンパイルしたこと、したがって深刻なバグの可能性があるので、調査し報告しなければならないことを示しています(see バグ)。

     -Bオプション以外は、オプションはステージ2と同じでなければなりません。

 10. コンパイラドライバ、コンパイラのパスおよびランタイムサポートをインストールします。次のコマンドを使えます。

          make install

     これによって、ファイルgcc、cppおよびgnulibが、ディレクトリ /usr/local/lib中のファイルgcc-cc1、gcc-cppおよび gcc-gnulibにコピーされます。またコンパイラのドライバプログラムは、これらのファイルを探します。またドライバプログラムgccをディレクトリ /usr/localにコピーし、それが通常の実行探索パスにあるようにします。

     -traditionalオプションを指定していないと、GNU CPPはioctl.h、 ttychars.hおよび処理系のその他のヘッダファイルを扱えません。バグはヘッダファイルの側にあります。一部のマシンは、ANSI Cと非互換な動作に依存しています。その動作とは、マクロの引数が文字定数中にあるときでも置換してしまうことです。-traditionalオプションを指定すると、GNU CCに、これらのヘッダの期待通りに振る舞うよう、指示したことになります。

     この問題があるため、システムに固有のCプリプロセッサを使うようにGNU CCを構成したいと思うこともあるでしょう。そうするためには、ファイル /usr/local/lib/gcc-cppを/lib/cppへのリンクにします。

     逆に、少なくともSun OSと4.3BSDでは、シェルスクリプトfixincludesを走らせてインクルードファイルを修正できます。これによって、ioctl.h、 ttychars.h等々の正しいコピーを、GNU CCだけが探す特殊なディレクトリにインストールします。

     修正を要するファイルの一覧は、ファイルfixincludesを見てください。

/usr/local/libにコンパイラのパスとランタイムサポートをインストールできないときは、-Bオプションを使って、それらを見つけることのできるプリフィクスを指定します。コンパイラはそのプリフィクスを、cpp、 cc1およびgnulibの前に付けます。たとえば、ファイルをディレクトリ /usr/foo/gccに置いて、GNU CCの実行時に-B/usr/foo/gcc/と指定するのです。

また、GNU CCを作るときのMakeの変数libdirによって、これらのファイルのデフォルトのディレクトリを指定することもできます。

GNU CCのVMS上へのインストール
===================

GNU CCのVMS版は、複数のテープファイルから成る特殊なテープフォーマットで配布されています。最初のものはコマンドファイル、2つ目はUNIXのtar フォーマットを読む実行プログラム、3つ目はこのプログラムを使ってテープの残りの部分を読む、もうひとつのコマンドファイルです。

テープをロードするには、それを/foreignにマウントして、次に@mta0 でテープの先頭のコマンドファイルを実行するだけです。

テープにはソースとともに、実行プログラムとオブジェクトファイルも入っており、ソースを変えないならコンパイルは不要です(これは良いことです。VMSユーザはたぶん他のCコンパイラを持っていないから)。再コンパイルするときは、次のようにします。

 1. ファイルtm-vms.hをtm.hに、config-vms.hをconfig.h に、vax.mdをmd.に、そしてoutput-vax.cをaux-output.c に、それぞれコピーします。

 2. @makeとタイプして、すべてを再コンパイルします。

GCCコマンドをインストールして、VMS Cコンパイラと同じくらい簡単にこのコンパイラを使えるようにするには、次のようにして、GNU CC用のVMS CLDファイルをインストールしなければなりません。

 1. VMSの論理名GNU_CCとGNU_CC_INCLUDEを、GNU CCの実行ファイル (gcc-cpp、gcc-1等々)とCのインクルードファイルのあるディレクトリを指すように定義します。それは次のコマンドで行います。

          $ assign /super /system disk:[gcc] gnu cc
          $ assign /super /system disk:[gcc.include] gnu_cc_include

     ディスク名とディレクトリ名は適切な名前を指定します。これらのコマンドをシステムのスタートアップファイルに入れておき、ブート時に必ず実行されるようにすることもできます。

 2. 以下のコマンド行で、GCCコマンドをインストールします。

          $ set command /table=sys$library:dcltables gnu_cc:gcc

     これで、gcc /verbose file.cのようなコマンドでコンパイラを起動できるようになりました。これは、UNIXのコマンドgcc -v -c file.cに相当するものです。

インストール時の問題点
***********

以下は、GNU CCをうまくインストールできなかった人々向けの情報です。

  * 一部のシステムでは、何らかの環境変数、たとえば、CCなどが定義されていると、makeの動作を妨害します。

  * 一部のマシン用のクロスコンパイルは問題を起こします。それは、ターゲットのマシンのアセンブラが、プログラムの特定部分で、浮動小数点数が整数定数として書かれることを要請しているからです。

     コンパイラは、その浮動小数点数の値が整数であることを調べ、それを整数として出力することによって、そういう整数定数を書き出します。これは、浮動小数点数の表現の詳細に依存せず、簡単に書けるからです。しかし、この方法は、コンパイラが走っているマシンの浮動小数点数の形式がターゲットと違っていたり、あるいはバイト並び順が違うだけでも、無効となります。

     ターゲットのマシンの浮動小数点数表現を理解する、マシン非依存のコードを書くことによって、この件を修正することができます。でも、私はアセンブラのバグ対策のために大仕事をしたくはありません。

GNU CCの非互換な面
************

GNU Cと既存の多くの(非ANSIの)Cとの間には、いくつかの注意すべき非互換性があります。

-traditionalオプションを指定すると、GNU Cにその他のCコンパイラのように振る舞うようよう指示したことになり、非互換性のほとんどが解消します。これが私たちとしての最終的な解決策だと思っています。

  * GNU CCは通常、文字列定数をリードオンリーとします。同じ文字列が複数使われていると、GNU CCはひとつだけをストアします。

     その結果、文字列定数を引数としてmktempをコールできなくなります。関数mktempは常に、その引数がポイントしている文字列を書き換えるからです。

     また、一部のシステムでは、sscanfに書式制御文字列として文字列定数を渡すと、動作しないことになります。sscanfは、その文字列定数への書き込みをしようとするからです。

     これらの問題に対する最良の解決方法は、これらの目的のためには文字列定数ではなく、初期化用の文字列を伴うcharの配列変数を使うことです。これが不可能な場合には、-fwritable-stringsフラグを指定して、 GNU CCが多くのCコンパイラを同じように文字列定数を扱うように指示します。

  * GNU CCは文字列定数中のマクロ引数を置換しません。たとえば、GNU CCで次のマクロ、

          #define foo(a) "a"

     は、引数aが何であれ、"a"を作り出します。

     -traditionalオプションを指定すると、(とりわけ)こういうケースを、昔流儀(非ANSI流儀)で扱うよう、GNU CCに指示します。

  * setjmpとlongjmpを使用する場合は、有効性が保証される自動変数は volatileと宣言されたものだけです。これはGCCが変数のレジスタへの割り振りを自動的に行うためです。以下の関数を見てください。

          jump_buf j;

          foo()
          {
              int a, b;

              a = fun1();
              if( setjmp( j ))
                  return a;

              a = fun2();
              /* fun3の中でlongjmp( j )が起こりえる */
              return a + fun3();
          }

     この場合、longjmpが起きると、aの最初の値がリストアされないこともあり得ます。aがレジスタに割り振られていると、その最初の値がリストアされます。そうでない場合は、最後にストアされた値を保持しています。

     -Wオプションと-Oオプションを併用すると、GNU CCがこういう問題の可能性を検出したとき、警告メッセージが出ます。

  * ブロック中の外部変数や外部関数の宣言は、その宣言のあるブロックにのみ適用されます。言い換えるとそれらのスコープは、同じ場所のその他の宣言と同じです。

     他の一部のCコンパイラは、extern宣言はたとえブロック中にあっても、ファイル全体に対して有効になります。GNU Cでは、-traditionalオプションを指定すると、ブロック中のextern宣言がそういうグローバルな解釈になります。

  * 従来のCでは、次に示すようにlongなどの型名をtypedef名とつなげることができました。

          typedef int foo;
          typedef long foo bar;

     ANSI Cではこれは許されません。longなどの型修飾子は、intを明示的に必要とします。この基準はCのコードではなく、Bisonの文法ルールが表現しているものなので、-traditionalオプションで変えることはできません。

  * PCCではtypedef名を関数のパラメータとして使えます。この件に関しては、すぐ上に書いたことと同じ規則が適用されます。

  * floatを返す関数はPCCではdoubleを返すようにコンパイルされます。 GNU CCではまさしくfloatを返します。PCCとの互換性を必要とする場合は、その関数をdoubleを返すように宣言すべきです。つまり、必要なことは (何らかの機構に任せるより)自分ではっきり書いた方が良いのです。

  * 構造体や共用体を返す関数をコンパイルするとき、GNU CCの出力コードは、UNIXのほとんどのバージョンで使われているのとは違う方法を使っています。その結果、 GNU CCでコンパイルしたコードは、PCCでコンパイルした構造体返しの関数をコールできず、またその逆もできません。

     GCCが使っている方法は以下のとおりです。サイズが1、2、4、8バイトの構造体または共用体は、一個のスカラーのように返されます。その他のサイズの構造体または共用体は、コール側が指定したアドレスにストアされます。

     PCCは通常、どのサイズの構造体および共用体も、その値を入れたスタティックなメモリ領域のアドレスとして返します。この方法は遅くて、かつリエントラントでないので、 GCCでは使われていません。

     PCC流儀のシステム上では、PCCでコンパイルされる関数を、構造体や共用体そのものではなく、構造体や共用体へのポインタを返すように宣言することによって、 GCCでコンパイルしたコードからコールできるようになります。たとえば、

          strict foo nextfoo();

     のように書く代わりに、次のように書くのです。

          strict foo *nextfoo();
          #define nextfoo *nextfoo

     (ここでは-traditionalではなくGNUのプリプロセッサを想定しています。つまり、マクロ展開の非再帰というANSIのルールが適用されます。)

C言語へのGNUの拡張
***********

GNU CにはANSIの標準Cにはない機能がいくつか盛り込まれています(-pedantic オプションを指定しているとき、これらの機能を使うと警告メッセージが出ます)。これらの機能が使えることを条件付きコンパイルでテストするには、GNU CCでは常に定義されている事前定義マクロ__GNUC__をチェックします。

式中の文と宣言
=======

GNU Cでは、括弧(複数)で括った複文が、ひとつの式の内側にあっても構いません。これによって、たとえば、次のように式中で変数を宣言できます。

     ( { int y = foo(); int z;
         if( y > 0 ) z = y;
         else z = -y;
         z;} )

これは(必要以上に複雑ですが)、foo()の絶対値を求める式として有効です。

この機能は特に、マクロ定義を``安全''にする(各オペランドを正しく一度だけ評価させる)ために有益です。たとえば、``最大値''を求める関数は、標準的なCでは普通、次のように定義されます。

     #define max(a, b) ((a) > (b)? (a) : (b))

しかし、この定義ではaまたはbを二度計算することになり、オペランドに副作用がある場合は良からぬ結果になります。GNU Cでは、オペランドの型が分かっているとき(ここではintとしましょう)は、このマクロを次のように安全に定義できます。

     #define maxint(a, b) \
         ({ int _a = (a), _b = (b); _a > _b ? _a : _b;})

列挙型の定数の値や、ビットフィールドの幅、スタティック変数の初期値などの定数式の中には、複文を埋め込むことは許されません。

オペランドの型が分かっていなくても、このことはできますが、ただしtypeof(see typeof)または、データ型命名(see データ型命名)を使わなければなりません。

式のデータ型の命名(Naming an Expression's Type)
======================================

初期化式にtypeof宣言を使うことによって、式のデータ型に名前を付けることができます。以下は、式EXPのデータ型の名前としてNAMEを定義するときの書きかたです。

     typedef name = exp;

これを、式中に複文を書ける機能と併用すると便利です。以下に、この両機能を使って、どんなデータ型にも適用できる安全な``最大値''マクロを定義する例を示します。

     #define max(a,b) \
         ({typedef _ta = (a), _tb = (b); \
           _ta _a = (a); _tb _b = (b);   \
           _a > _b ? _a : _b;})

ローカル変数用にアンダスコアの付いた名前を使うのは、aとbに関して置換される式の中の変数名との、衝突を防ぐためです。将来的には私たちは、変数のスコープがその変数の初期化文の後にしか成立しないような、新しい形式のシンタクスを設計したいと願っています。これによって、このような衝突を確実に防げるようになります。

typeofによるデータ型への参照
=================

式のデータ型を参照するもうひとつの方法が、typeofです。このキーワードの使い方のシンタクスはsizeofに似ていますが、構文の意味的機能は typeofで定義した型名と似ています。

typeofの引数の書き方は二通りあり、それは式またはデータ型です。式として書く場合の例は、次のようになります。

     typeof (x[0](1))

xは関数の配列です。そして、上のtypeof文は、関数の値のデータ型を参照するものです。

次は、型名を引数とする場合の例です。

     typeof (int *)

これで参照されるデータ型は、intへのポインタのそれです。

typeof構文はtypedef名が使えるところならどこでも使えます。たとえば、宣言の中、キャストの中、さらに、sizeofやtypeofの中で使うことができます。

  * 以下はyのデータ型をxがポイントするもののデータ型として宣言します。

          typeof (*x) y;

  * 以下は、yの上記のデータ型の配列として宣言します。

          typeof (*x) y[4];

  * 以下は、yをcharへのポインタの配列として宣言します。

          typeof (typeof (char *)[4]) y;

     上記は、次のようなCの伝統的な宣言と等価です。

          char *y[4];

typeofを使う宣言の意味と、それがなぜ便利なのかを理解するために、上記を以下のマクロを使って書き換えてみましょう。

     #define pointer(T)  typeof(T *)
     #define array(T, N) typeof (T, [N])

宣言を書き換えると次のようになります。

     array (pointer (char), 4) y;

つまり、array (pointer (char), 4)は、charへのポインタ4つの配列というデータ型です。

左値の一般化(Generalized Lvalues)
===========================

複合式、条件式、およびキャストは、そのオペランドが左値であれば左値として許されます。このことは、これらの式等のアドレスを求めたり、それらに値をストアできることを意味します。

たとえば、複合式は、その並びの最後の式が左値なら、代入ができます。次の2つの式は等価です。

     (a, b) += 5
     a, b += 5

同様に複合式のアドレスを求めることもできます。以下の2つの式は等価です。

     &(a, b)
     a, &b

条件式は、その型がvoidでなく、真と偽の分岐がどちらも左値として有効であれば、左値として有効です。キャストのアドレスを求めることは、キャストなしでアドレスを求めることと同じですが、ただし結果のデータ型が違います。たとえば、次の2つの式は等価です(しかし、2つ目はaがint *へのキャストのできないデータ型のときに有効でしょう)。

     &(int *)a
     (int **)&a

左辺がキャストであるような単純な代入式は、まず右辺を指定の型に変換し、次に左辺の式の内部表現の型へと変換します。これがストアされた後で、値は指定の型に再度変換されて、代入式の値となります。したがって、aがchar * であるなら、次の2つの式は等価です。

     (int)a = 5
     (int)(a = (char *)5)

+=のような算術演算を伴う代入がキャストに適用される場合は、キャストの結果としての型を使って演算が行われ、それから上記のような手順となります。したがって、以下の2つの式は等価です。

     (int)a += 5
     (int)(a = (char *)((int)a + 5))

条件式の中間オペランドの省略
==============

条件式の中間オペランド(middle operand)は省略できます。したがって、第一オペランドが非ゼロなら、その値がその条件式の値となります。

したがって、式、

     x ? : y

の値は、xが非ゼロならx、そうでなければyとなります。

上記の例は、次の式と完全に等価です。

     x ? x : y

この単純な例では、中間オペランドを省略できることの便利さが、あまりはっきりしません。これが便利になるのは、第一式が副作用を有する場合、または(マクロの引数の場合)副作用を有しうる場合です。その場合、オペランドを中間オペランドとして繰り返すと、副作用を二度実行することになります。中間オペランドを省略できることによって、再計算による良からぬ結果を生ずるまでもなく、計算済みの値を使うことができるのです。

長さゼロの配列
=======

GNU Cでは長さゼロの配列が許されます。これは、構造体のメンバとして可変長オブジェクトのヘッダを定義するときなどに非常に有効です。

     struct line{
         int length;
         char contents[0];
     };

     {
     struct line *thisline
       = (struct line *)malloc( sizeof(struct line) + this_length );
     thisline -> length = this_length;

標準的なCでは、contentsに長さ1を与えることになり、スペースを無駄にするか、または、mallocへの引数を複雑化するかのどちらかになります。

可変長配列
=====

GNU Cでは可変長の自動配列(automatic arrays)が使えます。配列の宣言の仕方は普通の自動配列と同じですが、その長さが定数式ではないのです。メモリは、宣言のあるブレースに入るときアロケートされ、それを出るときに開放されます。たとえば次のようになります。

     FILE *concat_fopen(char *s1, char *s2, char *mode)
     {
         char str[ strlen( s1 ) + strlen( s2 ) + 1];
         strcpy( str, s1 );
         strcat( str, s2 );
         return fopen( str, mode);
     }

可変長配列を構造体のメンバに定義し、その構造体を引数や関数の値として使うこともできます。その例を下に示します。

     int foo;

     struct entry{
         char data[ foo ];
     };

     struct entry tester( struct entry arg )
     {
         struct entry new;
         int i;

         for( i = 0; i < foo; i++ )
             new.data[i] = arg.data[i] + 1;
         return new;
     }

(将来的には、配列のサイズ…を表す変数…を同じ構造体のメンバにすることも、できるようになるでしょう。)

配列の長さはブレースで囲まれた宣言ブロックに入るときに計算され、それへのsizeofによるアクセスは、その配列のスコープ内でのみ有効です。

その配列名のスコープ外に出ると、メモリは開放されます。逆に、そのスコープ内へジャンプして入ることは許されません。それをするとエラーメッセージが出ます。

関数allocaを使って、可変長配列と同じ効果を得ることができます。関数allocaは他の多くのC処理系でも使えます(しかし、すべてではありません)。それでも、可変長配列の方がよりエレガントです。

この2つの方法には、さらに違いがあります。allocaでアロケートされたメモリは、それのある関数がリターンするまで存在しています。可変長配列のスペースは、配列名のスコープの外に出ると解放されます(同じ関数内で可変長配列とallocaの両方を使っているときは、可変長配列の解放によって、それよりも新しいallocaコールによるメモリもすべて解放されてしまいます)。

左値でない配列に添字を付けてよい
================

左値でない配列への添字付けが許されます。ただし、それに単項演算子&を適用することはできません。たとえば、以下はGNU Cでは有効ですが、他のC方言では無効です。

     struct foo  {int a[ 4 ];}

     struct foo f();

     bar( int index )
     {
         return f().a[ index ];
     }

voidポインタと関数へのポインタの算術演算
======================

GNU Cでは、voidへのポインタと関数へのポインタに対する加算と減算がサポートされています。その際、voidや関数のサイズを1として扱います。

その結果sizeofをvoid型や関数型にも適用でき、結果は1となります。

非定数による初期化
=========

一括的初期化文(aggregate initializer)の成分は、GNU Cでは定数式でなくても構いません。以下は、ランタイムに変化する要素を使った初期化文の例です。

     foo( float f, float g )
     {
         float beat_freqa[ 2 ] = { f - g, f + g  };
         ...
     }

コンストラクタ式
========

GNU Cコンストラクタ式をサポートします。コンストラクタは、初期化文のあるキャストのような姿をしています。その値はキャストで指定したデータ型のオブジェクトであり、初期化文で指定する要素がその内容です。データ型は構造体、共用体、または配列型でなければなりません。

struct fooとstructureが以下のように宣言されているとします。

     struct foo { int a; char b[ 2 ]; } structure;

以下は、コンストラクタを使ってひとつのstruct fooを構築する例です。

     structure = ( ( struct foo )  { x + y, 'a', 0 });

上記は、次のように書くのと等価です。

     {
         struct foo temp = { x + y, 'a', 0 };
         structure = temp;
     }

同じようにして配列を構築することもできます。コンストラクタのすべての要素が、初期化文で使うのに適した単純な定数式(でできている)であれば、そのコンストラクタは左値であり、次に示すように、その最初の成分へのポインタを強制できます。

     char **foo = (char *[]){ "x", "y", "z" };

要素が単純な定数ではない配列コンストラクタは、あまり役に立ちません。そのコンストラクタは、左値ではないからです。その有効な使い方は2つだけ、添字付けをするか、またはそれで配列変数初期化するかです。前者はたぶんswitch文より遅いでしょうし、後者は普通のCの初期化文と同じです。

名前の文字にドル記号を使える
==============

GNU Cでは、識別子の名前の中でドル記号を使えます。これは、古くからのCの処理系の多くが、そういう識別子を認めているからです。

データ型や変数のアラインメントを問い合わせる
======================

キーワード__alignofによって、オブジェクトの整列の具合を調べたり、あるデータ型の整列要件を知ることができます。そのシンタクスはsizeofと同じです。

たとえば、ターゲットのマシンがdoubleが8バイト境界に整列することを要請しているなら、__alignof( double )は8です。この要件は、多くのRISCマシンに見られます。従来のマシンでは、__alignof( double )は4、ときには2です。

整列をまったく要請しないマシンもあります。そういうマシンでは、どんなデータ型でも奇数アドレスに参照できます。その値は、その左値のデータ型に由来するものか、または、構造体の一部であるために構造体の整列を継承しているものです。たとえば、

     struct foo { int x; int y; } foo1;

という宣言の後には、__alignof( foo1.y )の値はたぶん、 __alignof( int )の結果と同じ2か4になります。このとき、foo1.yのデータ型自体が整列を要請しているわけではないのです。

マクロのように高速なinline関数
==================

関数をinlineと宣言することによって、その関数のコードをコール側のコードの中に統合するよう、GNU CCに指示します。これにより、関数コールのオーバヘッドがなくなる分だけ高速になります。さらに、実引数のどれかの値が定数なら、それはコンパイル字時にその定数の値に置換されるので、inlineとして展開されるコードの量が、それだけ少なくなります。

関数をinlineと宣言するには、次のように宣言時にキーワードinlineを使います。

     inline int
     inc( int *a )
     {
         ( *a )++;
     }

オプション-inline-functionsを指定すると、単純な関数はすべてinline展開されます。関数の定義のされ方や使われ方によっては、 inline展開に適さない場合もあるので注意してください。

関数がinlineでかつstaticであり、その関数へのコールがすべてコール側に統合されると、その関数自体のアセンブラコードはもはや誰も参照しません。この場合、GNU CCはその関数のアセンブラコードを、 -fkeep-inline-functionsオプションが指定されていなければ出力しません。色々な理由で、コール側への統合ができないケースがあります(特に、コールよりも定義が後ろにある場合---前方参照---は統合できませんし、定義中の再帰コールも統合できません)。コール側に統合することができないコールがあると、その関数は普通にアセンブラコードへとコンパイルされます。

コンパイラは、inline関数がstaticでない場合には、他のソースファイルからのコールがあると想定しなければなりません。グローバルなシンボルはどのプログラムでも、どこかで一度しか定義できないので、関数を別のソースファイル中で再度定義することはできず、ゆえに、そこでのコールは統合できません。したがって、staticでないinline関数は、常に独立の関数へとコンパイルされます。

Cの式のオペランドを使用するアセンブラの命令
======================

asmを使用するアセンブラ命令の中で、Cの式のオペランドを書けます。つまり、もはや使用するデータについて、物理的なレジスタやメモリ番地を、気にする必要がないということです。

アセンブラ命令のテンプレートの書き方は、マシン記述の書き方と似ています。それに、オペランドを制約する文字列が付きます。

たとえば、以下は68881のfsinx命令の使い方です。

     asm( "fsinx %1, %0" : "=f"( result ) : "f"( angle ) );

ここで、angleは入力オペランドを表すCの式、resultは出力オペランドです。どちらにも、オペランドを制約する文字列"f"が付いていますが、これはFPPのレジスタを必要とするという意味です。制約用の語彙は、マシン記述で用いるのと同じです(see 制約)。

オペランドはそれぞれ、オペランドを制約する文字列に続けて、Cの式を括弧中に書きます。コロンによって、アセンブラテンプレートとオペランドの間、および入・出力オペランド間を区切ります。オペランドの数は、その機械語命令が取りうるオペランドの数に制限されます。

出力オペランドの式は左値でなければなりません。コンパイラはこの点をチェックします。入力オペランドは左値でなくても構いません。オペランドのデータ型が、命令に対して妥当かどうかを、コンパイラはチェックできません。

出力オペランドがなくて入力オペランドがある場合は、 2つのコロンを続けて書くことによって、そのことを明記しなければなりません。

出力オペランドはライトオンリーでなければなりません。GNU CCは、これらのオペランドの値が命令の実行前には無用なゴミであると想定します。リード/ライト、または、一部のビットに書き込まれて、その他のビットには有用な情報がある、というオペランドの場合は、それをひとつの入力オペランドとひとつのライトオンリー出力オペランド、という2つのオペランドに論理的に分割しなければなりません。両者の結びつきは、命令の実行時には両者が同じ番地であると指定する制約文で表現します。両オペランドにCの同一の式を使えるし、それぞれ違う式も使えます。たとえば、以下の例はbarをリードオンリーのソースオペランド、fooをリード/ライトのデスティネーションとして、combineという(架空の)命令を書く場合の例です。

     asm( "combine %2, %0" : "=r"( foo ) : "0"( foo ), "g"( bar ));

オペランド1用の制約指定"0"は、オペランド0と同じ番地を占めるという意味です。

出力オペランドに制約修飾子&が付いていなければ、GNU CCは、入力は出力が生成される前に用済みになったと想定して、それを入力オペランドに使われていたレジスタに割り振ることがあります。この想定は、アセンブラのコードが実際は複数の命令から成っていた場合には、正しくないことがあり得ます。そういう場合は各出力オペランドに&を使用して、入力と重ならないようにします。See 修飾子。

通常、asm命令を使ういちばん便利な方法は、たとえば次のように、それらを関数のような姿をしたマクロの中に封じ込めることです。

     #define sin(x) \
     ({ double __value, __arg = (x); \
        asm( "fsinx %1, %0" : "=f"( __value ) : "f"( __arg )); \
        __value;  });

ここでは変数__argを使って、命令が確実に、正しくdouble型の値を扱うようにしています。また、引数xが自動的にdoubleに変換できるデータである場合にのみ受け付けるようにしています。

命令のオペランドのデータ型を確実に正しくするためのもうひとつの方法は、asmの中でキャストを使うことです。これは変数__argを使うよりも、もっと広範なデータ型を変換できます。たとえば、必要なデータ型がintなら、引数をintにキャストすればポインタでもエラーメッセージを出さずに受け付けます。上記の__arg方式だと、コール側が明示的にキャストしていないかぎり、(intの変数定義並びの初期化用の値としてポインタが使われるのだから)警告メッセージが出るでしょう。

GNU CCは最適化の際の前提として、これらの命令が出力オペランドを変える以外の副作用を発揮しないと想定します。これは、副作用を持つ命令は使えないという意味ではなく、出力オペランドが何にも使われていないときはコンパイラがそれを除去したり、ループの外に出したり、2つのオペランドがひとつの共通副次式を構成しているなら2つをひとつに置き換えたりするので注意してほしいという意味です。また命令の副作用によって変数の値が変化する場合(コードに値変更操作を明記せず副作用による変化を期待している場合)には、その変数の前の値がレジスタにあったりすると、命令実行後も前の値が再使用されることをがあり得ます。

asm命令が削除、移動、結合などされるのを防ぐには、asmの後にvolatileを書きます。たとえば、次のようになります。

     #define set_priority( x ) \
     asm volatile( "set_priority %1" : \
                   "=m"( *( char * ) 0 ) : "g"( x ))

提供した出力オペランドが命令中では実際には使われない、という点に注目してください。これは、asmが少なくともひとつの出力オペランドを要請するからです。この要請は、処理系の内部事情によるものであり、将来的にはなくせると思っています。

このケースでは、副作用として出力オペランドがメモリに書き込まれるような外見になります。その結果GNU CCは、前にメモリからフェッチしたデータが後で再び必要とされたら、再度フェッチしなければならないと想定します。volatileを付けるべき変数宣言にvolatileを付けなかった場合には、この方が望ましいでしょう。

アセンブラコードの中で使われる名前の制御
====================

Cの関数名や変数名をアセンブラコードの中で使うときの名前(別名)を、宣言の後にasmキーワードを書くことによって指定できます。

     int foo asm( "myfoo" ) = 2;

これは、変数foo用にアセンブラコード中で使用する名前が、_fooではなくてmyfooであるという指定です。

Cの関数名や変数名の前にアンダスコアを付けるシステムでは、この機能によって、アンダスコアで始まる名前を受け付けないリンカ用に名前を定義できます。

asmのこの使い方は、関数の定義に対してはできません。ただし定義の前の宣言にasmを次のように使えば、同等の効果が得られます。

     extern func() asm( "FUNC" );

     func( x, y )
         int x, y;
     ...

このようにして指定したアセンブラコード用の名前が、その他のアセンブラシンボルと衝突しないようにするのは、プログラマの責任です。また、レジスタ名を使ってはいけません。それをすると、完全に無効なアセンブラコードが作り出されます。 GNU CCはまだ、スタティック変数をレジスタにストアする能力を持っていません。将来は持つようになると思います。

バグの報告
*****

GNU CCの信頼性を高めていくためには、ユーザからのバグ報告が欠かせません。

バグを報告したことによって、その問題が必ず解決されるとは限りません。しかし、いかなる場合でも、バグを報告することの重要な意義は、GNU CCの次の版に改善することによって、ユーザ全体の役に立つことにあります。バグ報告はGNU CCのメンテナンスへの、ユーザからの貢献です。

バグ報告がこの目的に役立つためには、バグフィクスに役立つ情報を提供して頂かなくてはなりません。

たしかにバグだろうか?
===========

バグかどうか自信がないときは、以下を参考にしてください。

 1. 何らかの入力に対してコンパイラがハングアップまたは暴走などするなら、それはコンパイラのバグです。まともなコンパイラは決してクラッシュしません。

 2. コンパイラが(asm文を除く)何らかの入力に対して無効なアセンブリコードを出力するなら、アセンブラの実行を正しく阻止するエラーメッセージ(単なる警告ではなく)をコンパイラが出さないかぎり、それはコンパイラのバグです。

 3. コンパイラからの出力が有効なアセンブリコードであっても、それが入力ソースを正しく実行しないコードであるならば、それはコンパイラのバグです。

     ただし、この件では二重のチェックが必要です。というのも、原因がGNU Cと従来のCとの非互換性にあるかもしれないからです(see 非互換性)。これらの非互換性がバグと見なされることもありますが、これらは、重要な機能を盛り込んだための避けられぬ結果なのです。

     また、プログラムの記述の中に未定義動作が含まれていて、他のCコンパイラではたまたま正しい動作をしたということも考えられます。

     たとえば、最適化をしないコンパイラでは、関数の最後にreturn x;ではなく x;と書けることが多いのですが、GNU CCではreturnがないと関数の値は不定となります。したがって、変な値を返しても、それはGNU CCのバグではありません。

     (*p++, *p++)のように、増分演算子が2つ並んでいる式で、問題が起きることがよくあります。それまで使っていたコンパイラが、この式を期待どおりに解釈し、GNU CCが違う風に解釈したとしても、それはどちらのコンパイラのバグでもありません。

     バグのあるソース行を特定できたなら、こういうことのチェックも容易です。プログラムが正しくて曖昧性がないなら、それはコンパイラのバグです。

 4. 有効な入力に対してコンパイラがエラーメッセージを出すなら、それはコンパイラのバグです。以下は有効な入力ではなく、したがって、これに対するエラーメッセージはバグではないことに注意してください。

          int foo( char );

          int
          foo( x )
              char x;
          { ... }

     プロトタイプはcharを渡すように書かれていて、定義はintを渡してその値をcharとして扱うよう書かれています。これはANSI規格で許されている合法な書き方なのです。

 5. 無効な入力に対してコンパイラがエラーメッセージを出さない場合は、それはコンパイラのバグです。ただし、あなたが``無効な入力''と思ったものが、私から見れば``拡張機能''であったり、``古い貫行のサポート''であることもあり得るという点に注意してください。

 6. あなたがCコンパイラの使用経験の豊富な方なら、GNU CCに関するどんな改善アイデアでも歓迎します。

バグ報告の仕方
=======

GNU Cのバグ報告は、以下のどれかのアドレスに送ってください。

     bug-gcc@prep.ai.mit.edu
     {ucbvax|mit-eddie|uunet}!prep.ai.mit.edu!bug-gcc

最後の手段としては、下記に郵送してください。

     GNU Compiler Bugs 545 Tech Sq Cambridge, MA 02139

有益なバグ報告の基本原則は、全事実を報告せよです。その事実を言おうか言うまいか躊躇した場合は、断固、言うべきです!

人々は往々にして、自分では原因が分かっていると思ったり、あるいは取るに足らぬことと判断して、事実を省略することがあります。たとえば、変数の名前はどうでも良いことだと思い込むことがあります。しかし、どうでも良くないかもしれないのです。不正なメモリ参照が、その変数名がストアされている番地からフェッチしている、というバグかもしれません。名前が違っていれば、その番地の内容がコンパイラを騙して、バグではなくて正しいことをしている、と誤信させているかもしれません。ごく普通の条件でテストし、その正確な結果を報告してください。

私にバグを直させたいなら、以下のものをすべて提供してください。

 1. GNU CCのバージョン番号。これは、-vオプションで走らせると得られます。これがないと、そのバグがGNU CCの現在の版でもあるかどうかを判断できません。

 2. バグを再現できるための、完全な入力ファイル。バグがCのプリプロセッサにあるなら、それが必要とするソースファイルとヘッダファイルを送ってください。バグがコンパイラ本体(cc1)にあるならば、 gcc -ESOURCEFILE >OUTFILEでソースファイルをプリプロセッサに通し、バグ報告にはそのOUTFILEを付けてください(実際のコンパイル時に-I、-D、-Uなどのオプションを使ったときは、 outfileを得るときもやはり同じオプションを指定してください)。

     実例としてひとつの文だけでは不十分です。それをコンパイルするためには、その文は関数定義の中に書かれていなければならず、そして、その書かれ方に問題があることもあるからです。

     実際にコンパイルできる実例が提供されていないと、そのバグについては運を天に任せるしかなくなります。バグの原因を推測することも徒労です。たとえば、レジスタ使用に伴うバグは、それが起きる関数のあらゆる細かい細部に依存しているのです。

 3. そのバグが観察されたき、GNU CCに与えたコマンド行引数。たとえば、-Oを指定しましたか?自分の判断で省略したりせずに、指定したオプション等はすべて報告してください。

     コマンド行引数について私が推測することになると、その推測が違うために、そのバグが起きないということもあり得ます。

 4. コンパイラをインストールしたときにtm.hとmd用に使ったファイルの名前。

 5. 使用しているマシンのタイプ、オペレーティングシステムの名前とそのバージョン番号。

 6. あなたがオカシイと思う現象の、言葉による説明。たとえば、「コンパイラがハングする」または「出力中に不正なアセンブラ命令がある」、など。

     もちろん、コンパイラがハングアップ・暴走するというバグなら、私にもはっきりと分かるでしょう。しかし、不正な出力というバグは、よほど明白なものでないかぎり、私が気付かないこともあり得ます。どこが悪いのかを知らない状態で、50行のCのプログラムからコンパイルされたアセンブラコードを、隅から隅まで調べるようなことはとてもできません。

     問題がハングアップや暴走の場合でも、そのことをはっきり明記すべきです。コンパイラの、あなたのコピーが不良品であったとか、あなたのシステムのCのライブラリのバグ、などの、特異な事故の可能性もあります(実際にあったことです)。同じバージョンで、私のコピーはクラッシュしないかもしれません。報告に、クラッシュすると書かれていて、そして私のがクラッシュしないときは、それは私のせいによるバグではない、ということになるでしょう。クラッシュすると書かれていないときは、私は私の観察結果から何も結論することができないでしょう。

     GNU CCが不正なコードを生成する場合には、小さな完全なサンプルプログラムを送ってくだされば、デバッガの下でそのプログラムを走らせて、私は自分でエラーを見つけることができるでしょう。大きなプログラム、または大きなプログラムの一部を送ってきた場合はそれができません。コンパイル後のプログラムをあなたがデバッグして、その行に関して生成されるコードのどういう点が不正と思うのかを書いてください。

 7. GNU CCからの出力例を送るときは、それを作るときに-gを指定してください。デバッグ用情報として行番号が含まれ、それは出力と入力を対照させるために欠かせません。

 8. GNU CCのソースの変更を提案するときは、コンテキストの違いとして提案してください。また、GNU CCのソース中の何かについて議論するときも、行番号ではなくコンテキストで指摘してください。

     行番号は版ごとに同じではないからです。行番号では、ほとんど何も分かりません。

以下は、不必要なものの一部です。

  * バグの関連事項に関する説明

     バグに出会った人はしばしば、入力を様々に変えてみて、それによるバグの再現の有無を調べることに、多大な時間を費やします。

     これは時間の無駄であることが多く、あまり有益ではありません。というのも、私のバグの見つけ方は、たったひとつの例をデバッガの下でブレークポイントを指定して走らせてみるという方法であり、複数の事例からの単純な推論ではないからです。

     もちろん、同じバグをトリガする、より単純な例を見つけても、害になることはありません。出力中のエラーが見つけやすくなり、デバッガの下での実行も時間が少なくて済む、などの効果があります。プログラム例を単純化する容易な方法は、バグを起こす箇所以外の関数定義をすべて削除することです。初めはファイル中にあったものを、外部宣言に置き換えることもできます。

     しかし、単純化は必要なことではありません。そうするのが面倒でも、とにかくバグの報告だけは送ってください。

  * バグへのパッチ当て

     バグへのパッチ当ては、良質なものであれば私も助かります。ただし、テストケースのような必須の情報を省略しないでください。パッチに問題を見つけて、見当違いな方向でバグフィクスをすることもあり得るからです。

     GNU CCのような複雑なプログラムでは、それに特定のコードを実行させるためのプログラム例を作ることが、非常に困難な場合があります。したがって、ユーザから送られたプログラム例がなければ、私はバグがフィクスされたことをベリファイできないでしょう。

  * バグとその原因に関する推測

     そういう推測はたいてい間違っています。私ですら、デバッガを使って事実を見つけなくては、正しい推測はできません。推測は、何の役にも立たないのです。

GNU CCと可搬性
**********

GNU CCの主要目的は、GNUシステムが目指す実行環境---8ビットのバイトをアドレスする32ビット機で複数の汎用レジスタがあるもの---のクラスのマシン用の、良質で高速なコンパイラを作ることでした。エレガンスや、理論的な力、単純さなどは二義的な問題です。

GNU CCはターゲットのマシンに関する情報のほとんどを、マシン記述から得ます。マシン記述はマシンの各命令を、代数的な公式で記述しているものです。これはターゲットを記述するきわめてクリーンな方法です。しかし、この方法では表現することが困難な情報を、コンパイラが必要としたときには、私はためらうことなく、その場しのぎの(add-hoc)パラメータを定義して、マシン記述に加えました。可搬性が目指すものは、コンパイラに対して必要な作業を減らすことですから、GNU CCの場合は、それ自体が優先的な関心事項になることはありませんでした。

GNU CCにはマシン依存のコードはありませんが、複数バイトデータのバイト並び順 (endianness、1ワードの最上位バイトが最上位アドレスになるか、最下位アドレスになるか)や、自動インクリメントのアドレシングの可不可など、マシンの特性に依存するコードは含まれています。RTL生成のパスでは、ある種の構文木のコード生成に関しては、こういった特性の違いに応じて複数の戦略を取ることがしばしば必要です。

あり得るすべての可能性に対応するということはやりませんでしたが、実際によく使わているものや、私が実際に経験したものに関しては対応を図りました。その結果、新しいターゲットは別な戦略を必要とするでしょう。そういう場合、コンパイラはabortをコールするので、新戦略が必要なことがユーザにも分かるはずです。幸いにしてその新戦略は、マシン非依存なやり方で追加することができ、それを必要とするターゲットのマシンのみ影響を及ぼすものです。

GNU CCの出力へのインタフェース
******************

GNU CCは正規には、ターゲットのシステムで通常使われる関数コールの方法をそのまま使うように構成されています。これは、マシン記述マクロによって行われます(see マシンマクロ)。

ただし、構造体や共用体の値を返す場合は、違うやり方で行います。その結果、こういうデータ型を返す関数をPCCでコンパイルしたものコードを、GNU CCでコンパイルしたコードからコールすることはできないし、またその逆も不可です。 UNIXのライブラリルーチンは構造体や共用体を返さないので、このことは普通は問題になりません。

大きさが1、2、4または8バイトの構造体や共用体は、intやdoubleを返すために使われるのと同じレジスタを使って返されます(GNU CCでは普通、このようなデータ型の変数をレジスタに割り振ります)。その他のサイズの構造体および共用体の帰値は、コール側からレジスタで渡されたアドレスへとストアします。この方法はPCCが通常使っている方法よりも速いし、しかもリエントラントです。アドレスを渡すのに使われるレジスタは、マシン記述マクロSTRUCT_VALUEで指定します。

GNU CCは引数を常にスタック上に渡します。レジスタで渡すことが標準的なコール方法であるマシンでは、引数をレジスタで渡す場合もあります。この方法を他のマシンに応用することも可能です。しかし、そうするとPCC完全に非互換になってしまいます。 GNUシステムが全体的に完成して、ライブラリをGNU CCでコンパイルできるようになったら、レジスタ渡しを本格的に採り入れようと思います。

longjmpをする場合は、自動変数に注意してください。ANSI Cは、 volatileと宣言されていない自動変数の値は、longjmpの後では不定であるとしています。GNU CCでもまさしくそうです。というのは、レジスタ変数を正しくリストアするのはきわめて困難であり、GNU CCの特徴のひとつとして、プログラマが指定しなくても変数は勝手にレジスタ上に置かれてしまうからです。

longjmpによって変数を変えられたくなく、しかも、それまで使っていたコンパイラが古くてvolatileキーワードをサポートしていないときは、変数のアドレスを求めます。いったん変数のアドレスを求めたなら、単に求めただけでそれを何に使わくても、その変数はレジスタには行きません。

     {
         int careful;
         &careful;
         ...
     }

GNU CCでコンパイルしたコードは一部のライブラリルーチンをコールできます。VAXと 68000上で必要なルーチンはファイルgnulib.cにあります。このファイルはGNU CCではなく普通のCコンパイラでコンパイルし、GNU CCでコンパイルしたプログラムとリンクします(実際には、通常の関数コールインタフェースが使われます)。Cのライブラリの標準的な部分の一部、たとえば、bcopyなども、やはり自動的にコールされます。

コンパイラのパスとファイル
*************

コンパイラの全体的な制御構造は、toplev.cに書かれています。このファイルが引数の初期化とデコード、ファイルのオープンとクローズ、および各パスの順次的な継起を担当します。

パーシングのパスは一度だけ起動され、全入力をパースします。関数のRTL中間コードは、その関数がパースされるときに一文ずつ生成されます。文はそれぞれ構文木へと読み込まれ、それからRTLに変換され、次いで、その文の木に使われていたメモリは他へと再利用されます。データ型(およびそのサイズを表す式)、宣言そして結合様式 (binding contours、変数等のレジスタ等への割り振りの形勢)とネスティングの様子、以上のデータ用のメモリは、その関数のコンパイルの終了まで残ります。これらは、すべてデバッグ情報用に必要です。

パーシングのパスがひとつの完全な関数定義またはトップレベルの宣言を読むたびに、 toplev.cにある関数rest_of_compilationまたは rest_of_decl_compilationをコールし、これらの関数がその後の必要な処理をすべて担当して、アセンブリ言語の出力で終わります。コンパイラのその他のパスはすべて、rest_of_compilationの中で順に行われます。ひとつの関数定義のコンパイルから関数(rest_of_compilation)がリターンすると、それが inline関数(see inline)でなければ、その関数定義のコンパイルに使われていたメモリは完全に解放されます。

以下は、コンパイラのパスのすべてと、それ用のソースファイルの一覧です。また-dオプションでデバッグ用のダンプを要請できる箇所も記します。

  * 【パーシング】このパスは関数定義の全テキストを読んで、部分的な構文木を作ります。このパスとRTL生成パスは、今では完全に別個のパスではないのですが(以前はそうでしたが)、別扱いした方が考えやすいのです。

     木の表現はCのシンタクスに全面的に忠実ではありません。それは、その他の言語をサポートすることも企図しているからです。

     Cのデータ型の解析もこのパスで行い、ひとつの式を表す木の各ノードに、ひとつのデータ型が結び付けられます。変数は宣言ノードとして表現されます。

     定数折り畳み(constant folding)と結合法則の単純化 (associative-law simplification)も、このパスで行われます。

     パーシング用のソースファイルは、c-parse.y、c-decl.c、 c-typeck、c-convert.c、stor-layout.c、 fold-const.c、およびtree.cです。最後の3つのファイルは、言語非依存をねらいとしています。ヘッダファイルc-parse.h、 c-tree.h、およびtree.defも使います。最後の2つは、木表現のフォーマットを定義しています。

  * 【RTLの生成】ここで構文木をRTLコードに変換します。これは実際には、パーシングの間に一文ごとに行われるのですが、別のパスと考えることができ、そう考えた方が便利な場合が多いのです。

     ここには、ターゲットの特性に依存するコードが大量にあります。どういう命令が使えるかによって、戦略が違ってくることが多いからです。名前を付けた命令パターンの目的は、この情報をRTL生成パスに提供することです。

     比較やブール演算、あるいは条件式によるif条件文の最適化が、このパスで行われます。末尾再帰(tail recursion)も、このときに検出されます。ループとswitch文の最適出力の決定も行われます。

     RTL生成用のソースファイルは、stmt.c、expr.c、explow.c、 expmed.c、optabs.c、およびemit-rtl.cです。マシン記述からプログラムgenemitで生成されるファイルinsn-emit.cもこのパスで使われます。このパスの中でコミュニケーション用に、ヘッダファイルexpr.hが使われます。

     マシン記述からプログラムgenflagsとgencodesで生成される、ヘッダファイルinsn-flags.hとinsn-codes.hは、使える名前とそれに対応するパターンをこのパスに教えます。

     デバッグ用情報の出力は別として、以降のパスはどれも、関数の木構造表現を参照しません(その一部分だけがセーブされます)。

     関数をそのコール側でinline展開すべきか否かも、RTL生成の最後に決まります。その決定基準は目下のところ、関数のサイズ、データ型、およびパラメータの数です。この関数にはループやgotoがあってもよく、自分を再帰コールしていてもよいのです(末尾再帰をしている関数もinlineにできるのです!)。要するに、GNU CCがサポートしているすべての構文が認められます。

     オプション-drによって、このパスの後のRTLコードをデバッグ用にダンプできます。このダンプファイルの名前は、入力ファイル名に、.rtlを付けたものです。

  * 【分岐の最適化】このパスは、直後の命令へのジャンプ、ジャンプを飛び越すジャンプ、ジャンプへのジャンプ、などを最適化します。参照されないラベルや、到達不能のコードの削除もします。ただし、その到達不能コードの中にループがあって、そのループがこのパスで到達不能と検出されない場合は、削除されません(このようなループは、後で基本ブロック解析のところで削除されます)。

     分岐最適化は2〜3回行われます。最初はRTL生成の直後です。二度目は共通副次式の排除 (CSE、common subexpression elimination)の後ですが、それはCSEによってジャンプの最適化が再び必要になった場合のみです。最後に、最終パスの直後にも分岐最適化を行います。そのときは、上記の最適化とともに、交差ジャンプと、無操作(no-op)の転送命令(move命令)の削除も行われます。

     このパスのソースファイルはjump.cです。

     オプション-djで、このパスの最初の実行後のRTLコードをデバッグ用にダンプします。このダンプファイルの名前は、入力ファイル名に.jumpを付けたものです。

  * 【レジスタスキャン】このパスは共通副次式排除の指針として、各レジスタの最初の用途と最後の用途を見つけます。そのソースはregclass.cにあります。

  * 【共通副次式】このパスは定数伝播(constant propagation)も行います。ソースファイルはcse.cです。定数伝播によって条件分岐が無条件になったり、無操作になると、このパスの終了後に再度、分岐最適化が行われます。

     オプション-dsによって、このパスの後のRTLコードをデバッグ用にダンプします。このダンプファイルの名前は、入力ファイル名に.cseを付けたものです。

  * 【ループ最適化】このパスは定数式をループの外に出します。ソースファイルはloop.cです。

     オプション-dLにより、このパスの後のRTLコードをデバッグ用にダンプできます。このダンプファイルの名前は、入力ファイル名に.loopを付けたものです。

  * 【愚鈍な(stupid)レジスタ割り当て】最適化をしないコンパイルでは、これがこの時点で行われます。ここで、少々のデータフロー解析も行います。愚鈍なレジスタ割り当てを行うときは、その次に実行されるパスは再調整パスです。その間のパスはスキップされます。ソースファイルはstupid.cです。

  * 【データフロー解析】このパスはプログラムを基本ブロックに分割します(その過程で到達不能コードを削除します)。次いで、プログラムの各地点でどの疑似レジスタが生きているかを判断し、その値を使用する最初の命令を作りますソースファイルはflow.cです。

     このパスは、結果が使用されない計算の削除もし、またメモリ参照と加算または減算命令を結合して、オートインクリメントまたはオートデクリメントのアドレシングを作ります。オプション-dfで、このパスの後のRTLコードのデバッグ用ダンプをします。このダンプファイルの名前は、入力ファイル名に.flowを付けたものです。愚鈍なレジスタ割り当てが用いられているときは、このダンプファイルには、その割り当ての結果が出ます。

  * 【命令結合】このパスはデータフローの面で密接に関係している2〜3の命令を、ひとつの命令へと結合します。それは、そういう命令のRTL式を置換によって結合し、結果を代数的に単純化し、さらにその結果をマシン記述と照合します。ソースファイルはcombine.cです。オプション-dで、このパスの後のRTLコードのデバッグ用ダンプをします。このダンプファイルの名前は、入力ファイル名に.combineを付けたものです。

  * 【レジスタクラスの選択】 RTLコードをスキャンして、各疑似レジスタに最も合ったレジスタコードを見つけます。ソースファイルはregclass.cです。

  * 【ローカルレジスタの割り当て】このパスは、ひとつの基本ブロックの中だけで使われている疑似レジスタを、ハードウェアのレジスタに割り当てます。基本ブロックはリニアーなので、高速で強力な技法を使って、高い効率を達成できます。ソースファイルはlocal-alloc.cです。

     オプション-dlで、このパスの後のRTLコードのデバッグ用ダンプをします。このダンプファイルの名前は、入力ファイル名に.lregを付けたものです。

  * 【グローバルレジスタの割り当て】このパスは、残りの疑似レジスタ(その寿命がひとつの基本ブロック内に収まらないもの)を、ハードウェアのレジスタに割り当てます。ソースファイルはglobal-alloc.cです。

  * 【再調整(reloading、再ロード)】このパスは、疑似レジスタのハードウェアレジスタへの割り当てを調整します。ハードウェアレジスタを取得できなかった疑似レジスタには、スタック上の位置を与えます。次に、ハードウェアレジスタが得られなかったため、または正しくない種類のレジスタが割り当てられらために、無効な命令になった命令を見つけます。これらは、問題となる値を一時的にレジスタに乗せる(reloadする)ことによって、修復します。レジスタへの複写をするための命令を、追加します。ソースファイルはreload.cとreload1.c、および両者間のコミュニケーションに使われるヘッダreload.hです。

     オプション-dgで、このパスの後のRTLコードをデバッグ用にダンプします。このダンプファイルの名前は、入力ファイル名に.gregを付けたものです。

  * 【分岐の最適化(繰り返し)】今回は、交差ジャンプと、無操作のmove命令の最適化も行います。同時に、個々のマシンの仕様に応じた、先読み最適化(peephole optimization)も行われます。オプション-dJで、このパスの後のRTLコードのデバッグ用ダンプをします。このダンプファイルの名前は、入力ファイル名に.jump2を付けたものです。

  * 【最終パス】このパスが関数のアセンブラコードを出力します。また、ここで、無意味なtestやcompare命令を見つけます。関数の入口と出口のシーケンスは、ここで直接にアセンブラコードとして生成されます。これらはRTL中には存在しません。

     ソースファイルはfinal.cとinsn-output.cで、後者はツールgenoutputによって、マシン記述から自動的に生成されます。ヘッダファイルconditions.hが、これらのファイル間のコミュニケーション用に使われます。

  * 【デバッグ情報の出力】これは、ハードウェアレジスタを取得しなかった疑似レジスタのスタック位置も出力しなければならないので、最終パスの後に実行されます。ソースファイルは、 DBXのシンボルテーブルフォーマット用にはdbxout.c、GDBのシンボルテーブルフォーマット用にはsymout.cです。

すべてのパスまたは多くのパスで、いくつかの補助的なファイルが使われます。

 1. すべてのパスが、machmode.defを使います。これはマシンのモードを定義しています。

 2. RTLを扱うすべてのパスが、ヘッダファイルrtl.hおよびrtl.defと、ファイルrtl.cの中のサブルーチンを使用します。一連のgen...ツールも、これらのファイルを使ってマシン記述のRTLのリードや処理をします。

 3. いくつかのパスが、ヘッダファイルinsn-config.hを使います。これには、ツールgenconfigによってマシン記述RTLから自動的に生成されたパラメータ(Cのマクロ定義)がいくつか入っています。

 4. いくつかのパスが、命令認識部(instruction recognizer)を使います。これは、recog.cとrecog.h、およびツールgenrecogと genextractによってマシン記述から自動的に生成されるinsn-recog.cと insn-extract.cから成ります。

 5. いくつかのパスが、疑似レジスタの使い方に関する情報を定義しているヘッダファイル regs.hと、基本ブロックに関する情報を定義しているbasic-block.hを使用します。

 6. hard-reg-set.hが、ハードウェアレジスタのビットの配列である、 HARD_REG_SETというデータ型と、それを操作するためのマクロ集を定義しています。このデータ型は、ハードウェアレジスタが少ないマシンでは単にintです。レジスタの多いマシンでは、それはintの配列で、マクロの一部はループに展開されます。

RTL式
****

コンパイラの処理のほとんどが、レジスタ転送言語 (register transfer language, RTL)と呼ばれる中間言語に対して行われます。出力される命令のほぼひとつひとつごとの動作を、この言語による代数的な形式で記述します。

RTLはLispのリストがヒントになって作られました。その内部形式は、構造体が他の構造体をポイントしているという形であり、そのテキストの形式が、マシン記述や、デバッグ情報のダンプにも使われます。それは、多重括弧によって内部形式中のポインタを示す、というテキスト形式です(Lispのソースに類似)。

RTLオブジェクトのタイプ
=============

RTLは、式、整数、文字列、配列(ベクタ)という4種類のオブジェクトを使用します。式が最も重要です。RTLの式(``RTX''と略称します)は、Cの構造体ですが、それは普通はポインタで参照され、typedefによりrtx型というデータ型名を与えられています。

整数は単純に一個のintであり、文字列はchar *です。RTLコードの中では、文字列はsymbol_ref式の中にだけ登場しますが、マシン記述を構成するRTL式の中のコンテキストにも現れます。その場合は、10進数の数字で書かれます。

文字列は一連の文字です。内部的には普通のCのchar *として表現され、Cのシンタクスで書かれます。しかしRTLの文字列はヌルあってはいけません。マシン記述に空の文字列を書くと、それは内部的にはヌル文字へのポインタではなくて、ヌルポインタとして表現されます。コンテキストによっては、文字列に代わるこれらのヌルポインタが有効です。

配列は、式へのポインタの配列です。配列の中で、その成分の数も明記します。書き方としては、成分をスペースで区切って並べたものをブラケット ([...])で囲みます。長さゼロの配列が作られることはなく、そういうときはヌルポインタが使われます。

式は式コード(RTXコードとも呼ぶ)によって分類されます。式コードは、 rtl.defに定義されている名前で、それは(大文字による)Cの列挙型定数です。式コードとその意味は、マシンによって違います。RTXのコードは、マクロGET_CODE(x)で取り出し、PUT_CODE(x, newcode)で変えられます。

式コードは、式のオペランドの数と、オペランドのオブジェクトの種類を決めます。 RTLではLispと違って、オペランドを見ただけでは、それがどういう種類のオペランドかは分かりません。むしろそれは、コンテキストから、その式のコードから、判断しなければなりません。たとえば、subregというコードの式では、最初のオペランドは式、第二のオペランドは整数と見なされます。symbol_ref式では、オペランドはひとつ、そしてそれは文字列と見なされます。

式は括弧の中に式のタイプの名前、必要ならフラグとマシンモード、そして式のオペランド(スペースで区切って)を書きます。

mdファイル中の式コードの名前は、小文字で書かれていますが、それらがCのコード中に現れるときは大文字で書かれます。本マニュアル中ではconst_int、のように書きます。

ごく一部のコンテキストでは、式が必要な場所にヌルポインタを書いても有効です。それらは(nil)と書きます。

オペランドへのアクセス
===========

式のタイプのそれぞれに対して、rtl.defが、そのオペランドの数とオペランドの種類を指定しています。種類は、式(実際は式へのポインタ)を意味するe、整数のi、文字列を意味するs、そして式の配列を意味するEの4種類です。オペランドを表す文字の並びをフォーマットと呼びます。たとえば、subregのフォーマットはeiです。

ときどき、uおよびOというフォーマット文字も使います。 uはeと同じですが、デバッグ用ダンプで違った出力になります。そして、Oは通常の種類のオペランドではないことを意味します。 Oに関してはダンプで何も出力されず、コンパイルのごく一部で特殊な使われ方をします。

式コードの、オペランドの数と、フォーマットを取得するためのマクロがあります。

GET_RTX_LENGTH(CODE)
     コードCODEのRTXのオペランドの数

GET_RTX_FORMAT(CODE)
     コードCODEのRTXのフォーマットをCの文字列で

式のオペランドには、マクロXEXP、XINTおよびXSTRを使ってアクセスします。これらのマクロはいずれも、式ポインタ(RTX)とオペランド番号 (ゼロから数え始める)という2つの引数をとります。したがって、

     XEXP(x, 2)

は、式xのオペランド2に、式としてアクセスします。

     XINT(x, 2)

は、同じオペランドに整数としてアクセスします。XSTRも同じように使い、オペランドに文字列としてアクセスします。

どのオペランドでも、整数、式、あるいは文字列としてアクセスできます。オペランドの実態に即した正しいアクセスをするのは、これらのマクロを使う側の責任です。オペランドのデータ型は、式の式コードで分かります。またこれにより、オペランドの数も分かります。

たとえば、xがsubreg式なら、オペランドは2つで、正しくはXEXP(x, 0)とXINT(x, 1)でアクセスできます。 XINT(x, 0)と書くと、式オペランドのアドレスを整数にキャストしたものであり、これは、必要な場合には(int)XEXP(x, 0)と明確に書くべきものです。XEXP(x, 1)もエラーにならずにコンパイルされ、 2つ目の整数オペランドを式ポインタにキャストしたものを返し、それにアクセスしたら暴走〜ハングという結末になるでしょう。 X(x, 28)と書いてもエラーにはなりませんが、これは式の終端以降のメモリにアクセスすることになり、その結果がどうなるかは分かりません。

配列のオペランドへのアクセスはもっと複雑です。マクロXVECを使って配列のポインタそのものを得るか、またはマクロXVECEXPとXVECLENを使って配列の成分と長さにアクセスします。

XVEC(EXP, IDX)
     式EXPのIDX番のオペランドである配列ポインタにアクセス。

XVECLEN(EXP, IDX)
     式EXPのIDX番のオペランドである配列の長さ(成分の個数)にアクセス。

XVECEXP(EXP, IDX, ELTNUM)
     式EXPのIDX番のオペランドである配列の、 ELTNUM番の成分にアクセス。

     この値はRTXです。

     ELTNUMは非負かつXVECLEN(EXP, IDX)未満でなければなりません。

この章で定義されるマクロはすべて左値に展開され、したがって、それらへのアクセスとともに、オペランド、長さ、配列成分などの代入もできます。

RTL式中のフラグ
=========

一部のRTL式には(それぞれ1ビットの)フラグがいくつかあります。

used
     このフラグはRTL生成の最後にときどき使われるだけです。関数の中で、ある式が命令中に何度現れるかを数えるために使います。複数回現れる式は、共有構造体の規則に従ってコピーされます(see 共有化)。

volatil
     このフラグは、memおよびreg式と命令中で使われます。 RTLのダンプファイルでは、これは/vとプリントされます。

     mem式の中では、これはメモリ参照がvolatileなら1です。 volatileなメモリ参照は、削除、順序変え、結合等をしてはいけません。

     reg式は、これは値がユーザレベルの変数なら1です。0なら、コンパイラが内部的に使用する一時変数です。

     命令中では、1はその命令が削除されていることを意味します。

in_struct
     このフラグはmem式中で使われます。これは参照されるメモリデータが構造体または配列の一部または全体なら1です。スカラー変数なら0です。 Cのポインタによる参照は0です。ポインタがポイントするものはスカラー変数だからです。

     この情報は、コンパイラがエイリアシングの可能性を判断するために利用されます。

     RTLのダンプでは、このフラグは/sと表現されます。

unchanging
     このフラグはregとmem式の中で使われます。1は式の値が (少なくとも現在の関数中では)不変であることを意味します。

     RTLのダンプでは、このフラグは/sと表現されます。

integrated
     命令を含む一部の式の中では、このフラグはそのRTLが、手続きの統合によって作り出されたことを意味します。

     reg式の中では、このフラグは、式の値を保持しているレジスタが、現在の関数が返す帰値でなければならないことを示します。パラメータをレジスタで渡すマシン上では、同じレジスタをパラメータ用にも使えますが、そういう用途のときはこのフラグはセットされません。

マシンモード
======

マシンモードで、データオブジェクトのサイズとそれの表現のされ方を記述します。 Cのコード中では、マシンモードは、machmode.defで定義されている列挙型enum machine_modeで表されます。RTL式にはマシンモード用の場所があり、また一部の種類のツリー式(正確に言うと宣言と型)にもあります。

デバッグ用ダンプとマシン記述の中では、RTL式のマシンモードは、式コードの後にコロンで隔てて書かれます。マシンモード名の最後のmodeという文字は省略されます。たとえば、reg:SI 38は、マシンモードがSImodeの reg式です。モードがVOIDmodeなら何も書かれません。

以下がマシンモードの一覧です。

QImode
     intのサイズが(標準的な32ビット機の場合の)1/4(Quarter Integer)であるモード、つまりintが1バイトであるモードです。

HImode
     intのサイズが1/2(Half Integer)であるモード、つまりintが2バイトであるモードです。

SImode
     intのサイズが1(Single Integer)であるモード、つまりintが4バイトであるモードです。

DImode
     intのサイズが2(Double Integer)であるモード、つまりintが8バイトであるモードです。

TImode
     intのサイズが4(Tetra Integer)であるモード、つまりintが16バイトであるモードです。

SFmode
     単精度(4バイト)の浮動小数点数(Single Floating)のモードです。

DFmode
     倍精度(8バイト)の浮動小数点数(Double Floating)のモードです。

TFmode
     4倍精度(16バイト)の浮動小数点数(Tetra Floating)のモードです。

BLKmode
     一連の値を合わせたもの(Block)の値が、上記のどのモードにも該当しない場合のモードです。RTLでは、このモードになるのはメモリ参照だけで、それもストリングの移動やベクタ命令の場合のみです。そういう命令のないマシンでは、 RTLにBLKmodeは登場しません。

VOIDmode
     VOIDmodeとは、モードの不在または無指定を意味します。たとえば、式コードがconst_intのRTL式は、コンテキストが要請するどんなモードでも取ると見なされるので、VOIDmodeになります。RTLのデバッグ用ダンプでは、 VOIDmodeはモードがないものと表現されます。

EPmode
     これは``Entry Pointer''モードであり、Pascalなどのブロック構造言語における、関数変数用に使うことがねらいです。そういう値には、関数のアドレスと、外側レベルの自動変数にアクセスするためのstaticなチェーンポインタの両方が入っています。Cでは使用しないので、このマシンモードは部分的にしかインプリメントされていません。

CSImode
     一対のSImodeのintでもって複素数を表現する(Complex Single Integer) モードです。intや浮動小数点数のモードはどれも、頭にCを付けて複素数モードを表すことができます。たとえば、CQImode、CSFmode、 CDFmodeなどです。Cは複素数をサポートしていないので、このマシンモードは部分的にしかインプリメントされていません。

BImode
     これは構造体中のビットフィールドのマシンモードです。これは構文木の中でだけ使い、RTLの中では使いません。そして、構文木の中でも宣言ノードにのみ現れます。 Cの中では、ビットのサイズで定義されている構造体のフィールド用のFIELD_DECLノードにのみ登場します。

マシン記述でCのマクロPmodeを定義し、これがアドレス用のマシンモードに展開されます。これは通常はSImodeです。

マシン記述がサポートしなければならないモードは、QImode、 SImode、SFmode、およびDFmodeだけです。コンパイラは 2ワードの構造体や共用体にはDImodeを使おうとしますが、そうさせないようなプログラムを書くのは難しくないでしょう。同様に、Cのデータ型short intが HImodeを使わないようにすることもできます。将来的には、使えるマシンモードの集合をマシン依存にして、特定のマシンモードに関する想定やその使用を、コンパイラのマシン非依存のコードから排除するのが望ましいでしょう。

以下は、マシンモードに関連するCのマクロの一部です。

GET_MODE(X)
     RTX Xのマシンモードを返します。

PUT_MODE(X, NEWMODE)
     RTX XのマシンモードをNEWMODEに変えます。

GET_MODE_SIZE(M)
     モードMのデータのサイズをバイト数で返します。

GET_MODE_BITSIZE(M)
     モードMのデータのサイズをビット数で返します。

GET_MODE_UNIT_SIZE(M)
     モードMのデータの部分単位のサイズをビット数で返します。これは、複素数モードとEPmode以外では、GET_MODE_SIZEと同じです。複素数モードでは部分単位は実数部または虚数部であり、EPmodeでは、関数ポインタまたはコンテキストポインタです。

定数式型
====

最も単純なRTL式は、定数値を表現するものです。

(const_int I)
     この型の式は、整数値Iを表現します。Iは貫例的に、マクロINTVALでINTVAL(EXP)のようにアクセスされますが、これはXINT(EXP, 0)と同じです。

     整数値ゼロ用には、唯一の式オブジェクトがあり、それは変数const0_rtxの値です。同じく、整数値1用の唯一の式は、const1_rtxになります。式コードがconst_intで、値がゼロまたは1の式は、const0_rtxまたは const1_rtxを返します。

(const_double:M I0 I1)
     モードM用の浮動小数点定数値を表現します。 2つの整数I0とI1に、ひとつのdoubleの値のビットを入れます。これらをdoubleに変換するには、

          union{ double d; int i[2]; } u;
          u.i[0] = XINT(x, 0);
          u.i[1] = XINT(x, 1);

     として、u.dを参照します。定数値は、表現される値が単精度でも、このやり方でdoubleとして表します。

     グローバル変数dconst0_rtxとfconst0_rtxが、それぞれ DFmodeとSFmodeの、値0のconst_double式を保持します。

(symbol_ref SYMBOL)

     アセンブラの、データ用ラベルの値を表現します。SYMBOLは、そのアセンブララベルの名前を記述する文字列です。それが、*で始まっていると、ラベルはSYMBOLの*を含まない残りの部分です。そうでない場合は、ラベルはSYMBOLの頭に_を付けたものです。

(label_ref LABEL)
     アセンブラの、コード用ラベルの値を表現します。それにはひとつのオペランドがあり、そのオペランドは式であり、その式はcode_labelであり、命令列の中に現れてラベルの行き先を示すものです。

     コードラベルの参照に特別の式をを使う理由は、分岐の最適化がそれらを見分けることができるためです。

(const EXP)
     アセンブリ時の算術演算の結果である定数を表現します。オペランドEXPは、定数のみを含む式(const_int、symbol_ref、およびlabel_ref式)に、plusとminusを結び付けたものです。ただし、どの結びつきでも有効とはかぎりません。アセンブラは、リロケータブルなシンボルに対する自由な算術演算ができないからです。

レジスタとメモリ
========

以下は、マシンのレジスタと主記憶へのアクセスを記述するRTL式の型です。

(reg:M N)

     小さな値(FIRST_PSEUDO_REGISTER未満)の整数Nに関しては、これはNのマシンレジスタ、すなわちひとつのハードレジスタへの参照の意味です。Nの大きい値に関しては、これは一時的な値、すなわち疑似レジスタの意味です。コンパイラは、こういう疑似レジスタが無限個あるという想定でコードを生成し、後でそれらをハードレジスタまたはメモリ参照に変換します。

     シンボルFIRST_PSEUDO_REGISTERは、マシン記述で定義されます。それは、マシンのハードレジスタの数は、そのマシンの特性として不変だからです。ただし、マシンレジスタのすべてが汎用レジスタではない点に注意しなければなりません。データの保持に使ってよいマシンレジスタには、特定の命令中や、特定のデータ型についてしか、データを保持できないものも含めて、それらすべてにハードレジスタ番号を与えます。

     関数のRTLコード中で使われるそれぞれの疑似レジスタ番号は、それぞれ独自なreg式で表現されます。

     Mは、この参照のマシンモードです。各マシンは一般的に、レジスタを色々なモードで参照するので、これが必要です。たとえば、ひとつのレジスタに一個の完全なワードがあっても、命令によってはそれを半ワードや1バイトとして参照することがあります。浮動小数点として参照する命令も、その精度は色々です。マシンが唯一のモードでしかアクセスできないレジスタに関しても、モードは常に指定しなければなりません。ひとつのハードレジスタはひとつの関数の中で様々なモードでアクセスされますが、疑似レジスタには、その自然的なモードが決まっていて、そのモードでしかアクセスされません。それ以外のモードでの疑似レジスタへのアクセスを記述する必要があるときは、subreg式を使用します。

     1ワード以上のデータを指定しているマシンモードのreg式は、実際には複数の連続的なレジスタを意味していることがあります。このときさらに、レジスタ番号がハードレジスタを指定していれば、それは実際には、指定のレジスタから始まる複数のハードレジスタを意味していることになります。

     そういう複数ワードのハードレジスタのreg式は、ひとつの基本ブロックの中だけの寿命しか持たないことがあります。複数の連続的なレジスタが使われていることを正しく記録する方法が寿命解析パスに分からなくて、したがってレジスタ割り当てが混乱することがあり得ます。CSEパスは独自な対策により、そうい状況が起きないようにしなければなりません。

(subreg:M REG WORDNUM)
     subreg式は、レジスタを、そのレジスタにとって自然なマシンモード以外のマシンモードで参照する場合や、複数のレジスタを参照する複数ワードregの 1レジスタを参照する場合に使います。

     疑似レジスタにはそれぞれ、その自然なマシンモードが決まっています。それに対して違った操作が必要になったとき、たとえば、1バイトを保持している疑似レジスタへの 1ワードの転送命令を実行するときは、その疑似レジスタをsubreg式の中に入れなければなりません。その場合は、WORDNUMはゼロです。

     subregの他の使い方は、複数レジスタの値からひとつのレジスタを取り出すことです。DImodeやEPmodeなどのマシンモードは、1ワードよりも長い値を示しており、通常は2つの連続的なレジスタを要します。それらのレジスタのひとつにアクセスするには、モードをSImodeとし、WORDNUMでレジスタを指定したsubregを使います。

     コンパイル時パラメータWORDS_BIG_ENDIANが定義されていると、ワード番号ゼロが値の上位部分、定義されていないと下位部分です。

     結合パスと再調整パスの間には、最初のオペランドがregではなくmemの subregがあり得ます。再調整パスは、そのmemを適切なレジスタに再割り振りすることによって、それらを排除します。

     SFmodeのsubregを使ってDFmodeの値の上位部分は、単精度の浮動小数点数と同じフォーマットではありません。

(cc0)
     これは、マシンのコンディションコードレジスタへの参照です。これはオペランドがなく、マシンモードもありません。これを有効に使えるのは、代入のデスティネーションとして(比較命令やテスト命令の場合)、またはゼロ(値ゼロのconst_int、すなわちconst0_rtx)との比較をする比較演算子の中、という2つのコンテキストにおいてのみです。

     コードcc0の式オブジェクトはただひとつだけあります。それは、変数cc0_rtxの値です。コードcc0の式を作ろうとすると、常にcc0_rtxが返されます。

     コンディションコードレジスタの特殊事情として、命令によって暗黙裡にセットされることがあります。多くのマシンの、ほとんどすべての命令が、その実行結果に基づいてコンディションコードをセットします。RTLの中ではこれらの動作を、特に記録する必要はありません。そういう命令を認識するための指示が (マクロNOTICE_UPDATE_CCによって)、マシン記述に含まれているからです。コンディションコードをセットすることが唯一の目的である命令と、コンディションコードを使用する命令だけが、(cc0)の記述を必要とします。

(pc)
     これは、マシンのプログラムカウンタを表現します。これには、オペランドもマシンモードもありません。(pc)は分岐命令の特殊なコンテキストにおいてのみ有効に使えます。

     コードpcの式オブジェクトはただひとつだけあります。それは、変数pc_rtxの値です。コードpcの式を作ろうとすると、常にpc_rtxが返されます。

     分岐をしない命令では必ず、プログラムカウンタが暗黙裡にインクリメントされて変わりますが、そのことはRTLの中で記される必要はありません。

(mem:M ADDR)
     このRTXは、主記憶の、式ADDRで表現されるアドレスの参照を表します。 Mはアクセスするメモリ単位の大きさを指定します。

算術演算用のRTL式
==========

(plus:M X Y)
     XとYで表される式の、マシンモードMで実行される和を表現します。これはXとYがともに、モードMに対して有効である場合にのみ有効です。

(minus:M X Y)
     plusと似ていますが、引き算を表します。

(minus X Y)
     比較を目的としてXからYを引いた結果を表現します。 minus式にマシンモードがない場合は、あたかも精度が無限で、オーバーフローが生じないということを示します。

     もちろん、無限精度の引き算はできませんが、結果の符号だけを使用し、結果が(cc0)にストアされる場合は、そう振る舞えます。この種の式が有効に使えるのは、コンディションコードにストアされる値としてのみです。

(neg:M X)
     Xで表される値の、マシンモードMで実行される、否定(ゼロから引いた値) を表します。XはモードMに対して有効でなければなりません。

(mult:M X Y)
     XとYで表される値の、マシンモードMで実行される、有符号積を表現します。 XとYがともに、モードMに対して有効であれば、これは通常のサイズ保存乗算です。またXとYがともに、 Mよりも狭いモードに適合する値であるとしてもよいのです。その場合はオペランドよりも大きな幅の積を生成すにみ有効する乗算を表現します。幅拡大乗算と同サイズ乗算はそれぞれ、完全に別個であり、それをサポートするマシン命令も異なります。マシンが(この型のRTL式に対して)サポートできるのはどちらか一方です。

     multは、浮動小数点数の乗算にも使えます。そのときMは浮動小数点数のマシンモードです。

(umult:M X Y)
     umultと似ていますが、無符号乗算を表現します。multのように、同サイズ形式と拡大形式の両方で使用できます。umultは固定小数点乗算にのみ使われます。

(div:M X Y)
     マシンモードMで実行される、XをYで割る有符号除算の商を表現します。Mが浮動小数点数のモードなら正確な商を表現します。それ以外の場合は、整数化された商を表現します。 XとYがともに、モードMに対して有効であれば、これは通常のサイズ保存除算です。一部のマシンには、オペランドと商の幅が同じでない除算命令があります。そのような命令は、マシン命令が同じでない div式(複数)で表現されます。

(udiv:M X Y)
     umultと似ていますが、無符号除算を表現します。

(mod:M X Y)
(umod:M X Y)
     divおよびudivと似ていますが、商ではなく法(余り)を表現します。

(not:M X)
     Xで表される値の、モードMで実行される、ビットごとの補数を表現します。Mは固定小数点のマシンモードでなければなりません。XはMに対して有効でなければなりません。

(and:M X Y)
     Xで表される値の、マシンモードMで実行される、ビットごとの論理積を表現します。これは、Mが必ず固定小数点のモードであり、そしてXとYがともにMに対して有効であるときのみ有効です。

(ior:M X Y)
     Xで表される値の、マシンモードMで実行される、ビットごとの論理和を表現します。これは、Mが必ず固定小数点のモードであり、そしてXとYがともにMに対して有効であるときのみ有効です。

(xor:M X Y)
     Xで表される値の、マシンモードMで実行される、ビットごとの排他的論理和を表現します。これは、Mが必ず固定小数点のモードであり、そしてXとYがともにMに対して有効であるときのみ有効です。

(lshift:M X C)
     XをCビット分、論理左シフトした結果を表現します。

(and:M X Y)
     Mは必ず固定小数点のマシンモードで、 XはMに対して有効でなければなりません。 Cは固定小数点のモードに対して有効でなければなりませんが、そのモードは、左シフト命令用にマシン記述で決められているモードです。たとえば、VAX上では、 CのモードはMが何であれQImodeです。

     一部のマシンでは、Cが負数でも有効です。論理左シフトと算術左シフトを区別するのは、このためです。たとえば、VAXには右シフト命令がなく、右シフトは、カウントを負の定数または、(前の計算で)否定(negation)によって計算される値とする、左シフト命令として表現されます。

(ashift:M X C)
     lshiftと似ていますが、算術左シフトです。

(lshiftrt:M X C)
(ashiftrt:M X C)
     lshiftおよびashiftと似ていますが、右シフト用です。

(rotate:M X C)
(rotatert:M X C)
     上と似ていますが、左と右のローテートを表現します。

(abs:M X)
     Xの、モードMで計算される絶対値を表現します。 XはMに対して有効でなければなりません。

(sqrt:M X)
     Xの、モードMで計算される平方根を表現します。 XはMに対して有効でなければなりません。ほとんどの場合、Mは浮動小数点数のモードです。

(ffs:M X)
     モードMの整数として表現されるXの、最下位の1ビットのインデクスに1をプラスしたものを表現します(Xがゼロなら値はゼロです)。ターゲットのマシンによっては、XのモードはMである必要はなく、色々なモードの組み合わせが可能です。

比較演算
====

比較演算子は2つのオペランドに関して、ある関係の存否を調べ、その関係が存在すれば値1を表現し、存在しなければゼロを表現すると見なされます。比較のモードはオペランドで決まり、両方のオペランドがともにひとつの共通のマシンモードに対して有効でなければなりません。両方のオペランドが定数である比較は、それからはマシンモードを導けないので無効ですが、そのような比較は、定数折り畳みによってRTL中には決して存在しないはずです。

不等性の比較には、有符号と無符号という2つのフレーバ(flavors, 趣)があります。したがって、``より大きい''を表す式コードも、有符号と無符号とでは別々であり、それぞれgtとgtuです。両者は同じ一対の整数に対して、違う結果を生ずることがあります。たとえば、有符号では1は-1より大きいですが、無符号では-1は0xffffffffという無符号数になるので、 1より大きいことになります。

有符号比較は、浮動小数点数の値に対しても使われます。浮動小数点数の比較は、オペランドのマシンモードで見分けられます。

比較演算子は、(eq (cc0) (const_int 0))のように、コンディションコード(cc0)との比較にも使えます。このような構文は実際は、前の命令でコンディションコードがセットされたかどうかという結果を調べるものです。上の例は、コンディションコードが``ゼロ''ないし``等しい''と言っていれば1を意味し、そうでなければ0です。本物のデータ同士を比較するときと同じ比較演算子が使われていますが、マシン記述によってコンテキストが識別されるので、混同されるおそれはありません。

(eq X Y)
     XとYで表される値が等しければ1、等しくなければ0です。

(ne X Y)
     XとYで表される値が等しくなければ1、等しければ0です。

(gt X Y)
     XがYより大きければ1です。XとYが固定小数点数ならば、

(gtu X Y)
     gtと似ていますが、無符号比較を行い、固定小数点数だけを対象とします。

(lt X Y)
(ltu X Y)
     gtおよびgtuと似ていますが、``より小さい''をテストします。

(ge X Y)
(geu X Y)
     gtおよびgtuと似ていますが、``以上''をテストします。

(le X Y)
(leu X Y)
     gtおよびgtuと似ていますが、``以下''をテストします。

(if_then_else COND THEN ELSE)
     これは比較ではありませんが、常に比較演算と一緒に使われるので、ここに掲げます。正しく言うと、CONDが比較式です。この式はTHENとELSEで表される2つの値から、CONDに応じて選択することを表します。

     ほとんどのマシン上で、if_then_else式は、条件分岐を表現する場合にのみ有効です。

ビットフィールド
========

ビットフィールド命令表現するための専用の式コードがあります。これらのタイプの式は、RTL中の左値であり、代入の左辺に現れて、指定のビットフィールドへの値の挿入を指示するものです。

(sign_extract:SI LOC SIZE POS)
     これは、LOC(メモリまたはレジスタ参照)にある、またはLOCから始まる、符号拡張ビットフィールドへの参照を表現します。このビットフィールドの幅はSIZEビット、そして開始ビット位置がPOSビットです。メモリ単位のどちら側からPOSが数え始まるかは、コンパイルオプションBITS_BIG_ENDIANで指定します。

     LOCに対して有効なマシンモードはマシンに依りますが、普通はLOCは、メモリの中なら単一のバイト、レジスタの中なら全1ワードです。

(zero_extract:SI LOC SIZE POS)
     sign_extractと似ていますが、無符号ないしゼロ拡張のビットフィールドを参照します。同じ一連のビットを特定しますが、残りの部分はワード全体に渡ってゼロを充填し、符号拡張を行いません。

変換
==

マシンモード間の変換はすべて、明示的な変換操作によって表現しなければなりません。たとえば、1バイトと1ワードの和を表す式は、 (plus:SI (reg:QI 34) (reg:SI 80))とは書けません。 plus演算は同じマシンモードの2つのオペランドを必要とするからです。したがって、バイトサイズのオペランドを、次のように変換操作で囲まなければなりません。

     (plus:SI (sign_extend:SI (reg:QI 34)) (reg:SI 80))

変換操作は単なるプレースホールダーではありません。あるモードから別のモードへの変換をするのに、複数の方法がありえるからです。変換操作のコードで方法を指定します。

(sign_extend:M X)
     値XをマシンモードMに符号拡張した結果を表現します。 Mは固定小数点モードで、 XはMよりも狭いモードの固定小数点値でなければなりません。

(zero_extend:M X)
     値XをマシンモードMにゼロ拡張した結果を表現します。 Mは固定小数点モードで、 XはMよりも狭いモードの固定小数点値でなければなりません。

(float_extend:M X)
     値XをマシンモードMに拡張した結果を表現します。 Mは浮動小数点モードで、 XはMよりも狭いモードの浮動小数点値でなければなりません。

(truncate:M X)
     値XをマシンモードMに切り詰めた結果を表現します。 Mは固定小数点モードで、 XはMよりも広いモードの固定小数点値でなければなりません。

(float_truncate:M X)
     値XをマシンモードMに切り詰めた結果を表現します。 Mは固定小数点モードで、 XはMよりも広いモードの固定小数点値でなければなりません。

(float:M X)
     有符号値と見なされる固定小数点値Xを、浮動小数点モードMに変換した結果を表現します。

(unsigned_float:M X)
     無符号値と見なされる固定小数点値Xを、浮動小数点モードMに変換した結果を表現します。

(fix:M X)
     Mが固定小数点モードのときは、浮動小数点値Xを、有符号のモードMに変換した結果を表現します。丸めの方法は特定されていないので、 Cのコードのコンパイル時にこの操作が有効に使えるのは、整数値のオペランドに対してのみです。

(unsigned_fix:M X)
     浮動小数点値Xを、無符号の固定小数点モードMに変換した結果を表現します。丸めの方法は特定されていません。

(fix:M X)
     Mが浮動小数点モードのときは、 (モードMに有効な)浮動小数点値Xを整数に変換した結果を、小数点以下を切り捨てた浮動小数点モードの値として表現します。

宣言
==

宣言の式コードは算術演算を表現せず、むしろそれらのオペランドの確定ステートを表現します。

(strict_low_part (subreg:M (reg:N R) 0))
     この式コードは唯一、set式のオペランド0、というコンテキストでのみ使われます。さらに、この式のオペランドはsubreg式でなければなりません。

     strict_low_partが存在すると、レジスタの、モードNで有意味だが、モードMの部分ではない部分を変えてはならないという意味になります。通常は、Mがワードより小さいときには、レジスタのそういう残り部分の値は不定とされます。

副作用式
====

ここまで説明した式コードは、動作ではなく値を表現します。しかし、マシンの命令は決して値を作り出すことはなく、マシンのステートに対する副作用にのみ、命令の意味があります。副作用を表現するために、専用の式コードを使用します。

命令の本体は常に、これらの式コードのどれかです。上に説明した、値を表現するコードは、これらのオペランドとしてのみ現れます。

(set LVAL X)
     値XをLVALで表される場所にストアする動作を表現します。 LVALは、reg(またはsubregまたはstrict_low_part)、 mem、pc、またはcc0にストアできる場所を表す式でなければなりません。

     LVALがreg、subreg、またはmemのときは、マシンモードがあります。このときXは、そのモードに対して有効でなければなりません。

     LVALがregで、そのマシンモードがレジスタの全幅より小さい場合、それは、レジスタのマシンモードで指定されている部分に指定の値を与え、レジスタの残りの部分の値は不定となるという意味です。同様に、 LVALがsubregで、そのマシンモードがSImodeより狭いときは、レジスタの残り部分の値は不定となります。

     LVALがsubregのstrict_low_partなら、レジスタの、 subregのマシンモードで指定される部分に値Xが与えられ、レジスタの残りの部分は変わりません。

     LVALが(cc0)ならマシンモードはなく、 Xはどのモードでも構いません。これは``test''や``compare''命令を表します。

     LVALが(pc)なら、分岐命令であり、 Xに許される値はきわめて制限されます。 Xとして許されるのはlabel_ref式(無条件分岐)や、 if_then_else式です。後者の場合は、その第二・第三オペランドのどちらかが(pc) (分岐しないケース)で、他方がlabel_ref (分岐するケース)でなければなりません。 Xとしてmemや、 Yをregまたはmemとする(plus:SI (pc) Y)も許されます。このような異例なパターンは、ブランチテーブルを介する分岐を表現するために使います。

(return)
     現在の関数からのリターンを表現しますが、それはVAXのように、リターンを1命令で実行できる場合です。関数からのリターンに複数命令の``エピローグ(後始末)''の実行を要するマシンでは、リターンはエピローグが始まる箇所のラベルへの分岐によって行われ、式コードreturnは使われません。

(call FUNCTION NARGS)
     関数コールを表現します。 FUNCTIONはmem式で、そのアドレスがコールする関数のアドレスです。 NARGSは引数のワード数を表す式です。

     FUNCTIONが持つべき標準的なマシンモードが、各マシンごとに決まっています。マシン記述はマクロFUNCTION_MODEを定義しており、これが必要なモード名に展開されます。このモードの目的は、許されるアドレシングがマシンモードによって違うマシンの上での、許されるアドレシングを指定することです。

(clobber X)
     reg式またはmem式であるXに、予期されぬ、未記述の値がストアされること、またはストアされ得ることを表現します。

     これが使われるのは、たとえば、一定の値を特定のハードレジスタにストアするような、ストリング命令においてです。ストアされるそういう値は特に記述する必要がなくても、レジスタの値が変わることをコンパイラに伝えて、そのストリング命令の間にレジスタにデータが保持されないようにすることは必須です。

     Xには式ではなくてヌル(Cのヌルポインタ)も使えます。この(clobber (null))式は、すべてのメモリ番地が破壊され得ることを意味します。マシン記述で一部のハードレジスタを ``コールによって破壊される(call-clobbered)'' と指定することに注意してください。関数コール命令はすべて、デフォルトではこれらのレジスタを破壊すると想定されるので、そのことを示すためのclobber式は不要です。また、どのメモリ番地も、関数コールによって変えられる可能性があるものと想定されます。

(use X)
     値Xを使用することを表現します。これは、Xにある値が、必要な理由が明白でなくても、プログラムのこの時点で必要であることを示します。したがってコンパイラは、値をXにストアするだけの命令を削除しません。 Xはreg式でなければなりません。

(parallel [X0 X1 ...])
     負数の副作用が並列に実行されることを表現します。ブラケット(角括弧)は配列を表し、 parallelのオペランドは式の配列です。 X0、X1、等々は個々の副作用、つまり式コードがset、call、return、clobber、またはuseの式です。

     ``並列に''ということの意味は、まず個々の副作用で使われる値がすべて計算され、次に実際の副作用がすべて実行されることです。たとえば、

          (parallel [(set (reg:SI 1) (mem:SI 1)))
                     (set (mem:SI (reg:SI 1)) (reg:SI 1))])

     は、1番のハードレジスタの値と、それによるメモリアドレスが交換されることを、明白に表しています。メモリアドレスとして(reg:SI 1)が現れる両方の箇所で、それは命令の実行の前にレジスタ1の値を参照します。

(sequence [INSNS ...])
     一連の命令を表現します。配列中に現れる各命令は、命令連鎖の中に現れるのが妥当な命令でなければなりません。すなわちそれは、insn、jump_insn、call_insn、 code_label、barrier、またはnoteでなければなりません。

     RTL式sequenceは、実際の命令中に現れることはありません。それは、define_expandからの結果としての一連の命令を表し、それらの命令がemit_insnに渡されて命令連鎖中に挿入される前の状態です。実際の挿入時には、命令は個々に取り扱われて、 sequenceは忘れられます。副作用の代わりに、命令の本体として現れる式コードが3つあります。ただしこれらは厳密に言うと、副作用を記述しているのではありません。

(asm_input S)
     文字列Sで記述される、その通りのアセンブラコードを表現します。

(addr_vec:M [LR0 LR1 ...])
     分岐アドレスのテーブルを表現します。 LR0などの配列成分は、label_ref式です。モードMは、各アドレスに与えられるスペースの大きさを指定します。通常MはPmodeです。

(addr_diff_vec:M BASE [LR0 LR1 ...])
     BASEからのオフセットとして表される、分岐アドレスのテーブルを表現します。 LR0などの配列成分とBASEは、label_ref式です。モードMは、各々のアドレス差値に与えられるスペースの大きさを指定します。

アドレスに対する既定(embedded、内蔵)の副作用
===========================

メモリアドレスとして現れる4つの特殊な副作用式コードがあります。 [訳注:embeddedな副作用とは、命令の各アドレシングモード(の作用)の中に既に含まれている、ポストインクリメント、プレデクリメント、等々、アドレスオペランドに生ずる副作用のことを言っている。]

(pre_dec:M X)
     Xを一定量デクリメントする副作用を表現し、また、デクリメントされた後のXの値を表します。

     Xはregまたはmemでなければなりませんが、しかし、ほとんどのマシンではregしか使えません。 Mは、使われるマシンのポインタ用のマシンモードでなければなりません。 Xがデクリメントされる量は、この式がアドレスを提供しているメモリ参照のあるマシンモードのバイト長です。以下はその使い方の例です。

          (mem:DF (pre_dec:SI (reg:SI 39)))

     上記は、疑似レジスタ39をDFmodeの値の長さだけデクリメントし、その結果をDFmodeの値をアドレスするために使うことを表しています。

(pre_inc:M X)
     上と同様ですが、Xをデクリメントするのではなくインクリメントします。

(post_dec:M X)
     pre_decrementと同じ副作用を表しますが、値が違います。ここで表現される値は、デクリメントされる前のXの値です。

(post_inc:M X)
     上と同様ですが、Xをデクリメントするのではなくインクリメントします。

これらの処理系既定の副作用は、注意して使わなければなりません。命令パターンが、それらを使わないこともあります。コンパイラのflowパスまでは、それらが単にスタックへのプッシュを表すこともあります。 flowパスは、ひとつの命令中でレジスタがインクリメントまたはデクリメントされ、そのすぐ前か後にそれがアドレスとして使われているケースを見つけます。次いでこれらのケースは、プリまたはポストのインクリメントまたはデクリメントを使うよう変えられます。

スタックからのポップの指定を、これらの既定副作用演算子を使って表現できますが、ただしそれは安全ではありません。命令の組み合わせによっては、そのポップがプッシュを相殺して、コードの意味を変えてしまうことがありえます。

これらの既定副作用演算子で表現できる命令は、付加的にsetのあるparallelを使って、アドレスレジスタの変え方を記述する方法でも表現できます。しかし、これはこういう操作のできるマシンはもっぱら、メモリアドレスが求められる箇所ではどこであろうとそれができるのですから、行われていません。これらを並列ストアとして追加すると、マシン記述の項目数が倍増してしまうでしょう。

式としてのアセンブラ命令
============

asm_operandsというRTXコードが、ユーザ指定のアセンブラ命令が作り出す値を表現します。それは、引数を伴うasm文を表現するために使われます。次のような、出力オペランドをひとつ伴うasm文は、

     asm("foo %1,%2,%0" : "a" (outputvar) : "g" (x + y), "di" (*z));

となります。 outputvarにストアされる値を表現するひとつのasm_operands RTXを用いて表現されます。

     (set RTX-FOR-OUTPUTVAR
         (asm_operands "foo %1,%2,%0" "a" 0
                 [rtx-for-addition-result rtx-for-*z]
                 [(asm_input:m1 "g")
                  (asm_input:m2 "di")]))

上記では、 asm_operands RTXのオペランドがアセンブラのテンプレート文字列と、出力オペランドの制約、指定の出力オペランド(複数)の中でのこの出力オペランド(単数)のインデクス番号、入力オペランドRTXのベクタ、および、入力オペランドのモードと制約の配列です。モードm1は、和x + yのモード、m2は*zのモードです。

asm文が複数の出力値を伴うときは、その命令にはひとつのparallelの中にいくつかのそういうset RTXがあることになります。それぞれのsetにひとつのasm_operandsがあり、これらのすべてが同じアセンブラテンプレートとベクタを共有しますが、ただしそれぞれに、その出力オペランド用の制約があります。それらはまた、出力オペランドのインデクス番号でも区別され、番号は一連の出力オペランドに対して0、1、...というように付けられます。

Insns(命令)
=========

関数のコードのRTL表現は、 insnsと呼ばれるオブジェクトのダブルリンクトリストのチェーンです。 insnsは、その他の目的には使われない特殊なコードによる式です。一部のinsnsは実際の命令で、その他はswitch文用のデスパッチテーブル、ジャンプ先ラベル、各種の宣言情報、などを表現します。

各insnは自分の固有のデータに加え、自分を現在の関数内の他のすべてのinsnsから区別するユニークなID番号と、自分の前と後を指すチェーンポインタを必ず持ちます。これら3つのフィールドは、 insnコードとかかわりなく、どのinsnでも同じ位置にあります。それらにXEXPとXINTでアクセスできますが、実際は3つの特殊なマクロが常に使われます。

INSN_UID(I)
     IというinsnのユニークなIDにアクセスする。

PREV_INSN(I)
     Iの前を指すチェーンポインタにアクセスする。 Iが最初のinsnなら、これはヌルポインタである。

NEXT_INSN(I)
     Iの後を指すチェーンポインタにアクセスする。 Iが最後のinsnなら、これはヌルポインタである。

NEXT_INSNとPREV_INSNのポインタは常に正しい関係でなければなりません。つまりIが最初のinsnでなければ、

     NEXT_INSN( PREV_INSN( INSN)) == INSN

が常に真です。

各insnには、以下の6つの式コードのどれかがあります。

insn
     式コードinsnは、ジャンプや関数コールをしない命令用に使われます。コードinsnのあるinsnsには、上記3つの必須フィールドに続いて、さらに4つのフィールドがあります。これらの4つを、下表で説明します。

     以下は、このinsn insnsそして次のjump_insnおよびcall_insnのinsnsの3つに共通する追加フィールドの表です。

     PATTERN (I)
          このinsnが実行する副作用のための式

     REG_NOTES (I)
          このinsn中でのレジスタの使い方に関する情報を与えるリスト (expr_list式のチェーン)。このリストはフロー解析パスによってセットアップされ、それまではヌルポインタである。

     LOG_LINKS (I)
          前の``関連''insnsのリスト(insn_listのチェーン)。それらは、このinsnによって初めて使われる値を、レジスタにストアしたinsnsである (これに加わる制約としては、関連insnsの間にジャンプやラベルがあってはならない)。このリストはフロー解析パスによってセットアップされ、それまではヌルポインタである。

     INSN_CODE (I)
          マシン記述のどのパターンがこのinsnに合致するかを表す整数である。あるいは、マッチングをまだ試みてないときは-1である。このようなマッチングは、insnのパターンが、単一のuse、 clobber、asm、addr_vec、またはaddr_diff_vec式から成るときには試みられることがなく、このフィールドは使用されない。

jump_insn
     式コードjump_insnは、ジャンプをする命令 (より一般的には、label_ref式のある命令) 用に使われます。 jump_insnのinsnsには、 insnのinsnsと同じ追加フィールド(上表)があり、同じようにアクセスされます。

call_insn
     式コードcall_insnは、関数コールをする命令用に使われます。これらの命令を特別扱いすることは重要です。これらによって、一部のレジスタやメモリ番地の内容が、予測不可能に変わってしまうことがありえるからです。

     call_insnのinsnsには、 insnのinsnsと同じ追加フィールド(上表)があり、同じようにアクセスされます。

code_label
     code_labelのinsnは、ジャンプinsnのジャンプ先を表現します。それには、3つの標準のフィールドに加え、データ用の専用フィールドがあります。そのフィールドはラベル番号を入れるためのもので、それは(現在の関数内だけでなく)そのコンパイル全体の中で、そのラベルをユニークに識別するための番号です。最終的には、ラベルはアセンブラ出力の中でLNというアセンブララベルになりますが、このNがラベル番号です。

barrier
     barriersは、命令ストリームの中の、無条件ジャンプの後に入れて、ジャンプが無条件であることを示します。それらには、3つの標準フィールドがあるだけです。

note
     noteのinsnsは、デバッグ情報や宣言情報を付加するために使われます。それらには2つの特殊フィールドがあり、そのひとつはマクロNOTE_LINE_NUMBERでアクセスされる整数、他はNOTE_SOURCE_FILEでアクセスされる文字列です。

     NOTE_LINE_NUMBERが正ならば、そのnoteはひとつのソース行の位置を表し、 NOTE_SOURCE_FILEが、その行のあるソースファイルの名前を表します。これらのnotesは、アセンブラ出力における行番号データの生成を制御します。

     NOTE_LINE_NUMBERが負ならば、それは行番号ではなくて、以下の値の内のどれかです (そしてNOTE_SOURCE_FILEにはヌルポインタが入っていなければなりません)。

     NOTE_INSN_DELETED
          こういうnoteは完全に無視できます。コンパイラのどれかのパスでinsnsが削除されて、このようなnotesに変えられたのです。

     NOTES_INSN_BLOCK_BEG
     NOTES_INSN_BLOCK_END
          これらのタイプのnotesは、変数名のスコープレベルの初めと終わりを示します。それらは、デバッグ情報の出力を制御します。

     NOTES_INSN_LOOP_BEG
     NOTES_INSN_LOOP_END
          これらのタイプのnotesは、 whileループやforループの初めと終わりを示します。それらは、ループオプティマイザがループを素早く見つけられるようにします。

ひとつのinsnのREG_NOTESフィールドは、同様のチェーンですが、 insn_list式ではなくexpr_list式です。レジスタnoteは4種類あり、それはexpr_listのマシンモードで区別され、そのレジスタnoteは実はひとつのenum reg_note型の値を表しています。 expr_listの最初のオペランドopは、 noteの種類によって意味が決まるデータです。その4種類は以下のとおりです。

REG_DEAD
     レジスタOPがこのinsn中で死にます。つまり、このinsnの直後で値を変えても、プログラムの将来の振る舞いに影響を与えません。

REG_INC
     レジスタOPが、このinsn中の既定の副作用によってインクリメントまたはデクリメントされます (インクリメントかデクリメントかは、この段階では区別されません)。つまり、それがPOST_INC、PRE_INC、POST_DEC、またはPRE_DECのRTX中に現れることを意味しています。

REG_EQUIV
     このinsnによってセットされるレジスタが、ランタイムにOPと等しくなり、その出現をすべてOPで置換することが有効となります (ここで``有効''とはプログラムのデータフローを指し、単純な置換は一部のinsnsを無効にすることがありえます)。

     このinsnがレジスタ中に明示的にコピーする値が、OPと違っていても、ランタイムには等しくなるのです。

     たとえば、他の値を代入されることのない定数がレジスタにロードされると、この種類のnoteが使われます。

     関数へのエントリに際してパラメータが疑似レジスタにコピーされるとき、この種類のnoteが、パラメータが渡されたスタックスロットと、そのレジスタが等しいことを記録します。この場合レジスタは他のinsnsによってセットされることもありますが、それでもその関数に関してレジスタをスタックスロットで置換して有効となります。

REG_EQUAL
     このinsnによってセットされるレジスタが、このinsnの終わりのランタイムに、 OPと等しくなります (関数のその他の部分で等しいとはかぎらない)。

     OPというRTXは、普通は算術式です。たとえば、ライブラリコールのような一連のinsnsを使って算術演算を実行するとき、この種類のnoteが最終値を作り出す、ないしコピーするinsnに付けられます。それはCSEパスに、その値の扱い方を教えるのです。

REG_RETVAL
     このinsnはライブラリコールの値をコピーし、 OPはそのライブラリコール用の引数をセットアップするために生成された最初のinsnです。

     フロー解析(のパス)はこのnoteを使って、結果が死んでいるようなライブラリコールをすべて削除します。

REG_WAS_0
     このinsnの前にレジスタOPにはゼロがあります。このnoteは、存在するときには信頼できますが、不在のときには(不在であることは)何も意味していません。

(式コードinsn_listとexpr_listの唯一の違いは、 insn_listの最初のオペランドがinsnと見なされて、デバッグ用ダンプでそのinsnのユニークなIDとしてプリントされるのに対し、 expr_listの最初のオペランドは、式として普通にプリントされることです。)

関数コールinsnのRTX式
==============

サブルーチンをコールするinsnsのRTL式コードは、 call_insnです。これらのinsnは、特別なルールを満たすとともに、その本体がcallという特殊なRTL式を使わなければなりません。

call式には、次のように2つのオペランドがあります。

     (call NBYTES (mem:FM ADDR))

上記でNBYTESは、サブルーチンに渡される引数のバイト数を表すオペランド、 FMはマシンモード (これはマシン記述の中のFUNCTION_MODEマクロの定義と等しくなければならない)、そしてADDRはサブルーチンのアドレスを表します。

値を返さないサブルーチンに関しては、上記に示すcall RTXがinsnの全本体です。

モードがBLKmodeの値を返すサブルーチンに関しては、値はハードレジスタで返されます。このレジスタ番号をRとすると、CALL insnの本体は次のようになります。

     (set (reg:M R)
          (call NBYTES (mem:FM ADDR)))

上記のRTL式を見れば、このinsnの中では適当なレジスタが有効な値を受け取ることが、 (オプティマイザのパスにとって)明らかとなります。

RTLの生成の直後に、サブルーチンの値が実際に使われるなら、このcall insnの直後には常に、registerRを参照するinsnがあります。このことは、クロスジャンプが生ずるまで、オプティマイザパスの全体に渡って一貫して真です。

以下のinsnは、2つの形式の内のひとつです。つまり、

     (set (reg:M P) (reg:M R))

のように値を疑似レジスタにコピーするか、あるいは(コール側の関数がコールを作り出した値を単に無差別に返す場合で、しかもそのために何の操作も必要ない場合には)、

     (use (reg:M R))

となります。call insnと、それに続くこのinsnとの間にあってもよいのは、スタック調整のinsnだけです(そしてたぶん何らかのnote insn)。

サブルーチンがBLKmodeの値を返すとき、それは、その値をストアする場所のアドレスをサブルーチンに渡す、というやり方で扱われます。そこでcall insn自身は何も値を``返さない''ので、何も返さないcallと同じRTLの形式をしています。

構造体共有の仮定事項
==========

コンパイラは、ある種のRTLはユニークであり、同じ値をしている別々の2つのオブジェクトは存在しないと仮定します。その他のケースでは、その逆に、ある種のRTL式オブジェクトは、それが入っている構造体中では一箇所にしか現れないと仮定します。

これらの仮定事項は、単一の関数に関するものです。グローバル変数と外部関数を記述しているRTLオブジェクトを除いては、いかなるRTLオブジェクトも2つの関数に共通ではありません。

  * それぞれの疑似レジスタには、それを表現するひとつきりのregオブジェクトがあります。したがって、マシンモードもひとつだけです。

  * どのシンボルラベルに関しても、それを参照するsymbol_refオブジェクトはひとつだけです。

  * 値ゼロのconst_int式はひとつだけであり、値1のものもひとつだけです。

  * pc式はひとつだけです。

  * cc0式はひとつだけです。

  * モードがSFmodeで値ゼロのconst_double式はひとつだけであり、 DFmodeで値ゼロもひとつです。

  * RTL構造体の中に、label_refは一箇所にしか現れません。言い換えると、関数中ですべてのinsnsのツリーを、同一のlabel_refが二度以上あることはないと仮定して辿っても大丈夫であるということです。

  * 個々のstatic変数やスタックスロットに対して、通常はひとつだけmemオブジェクトが作られるので、これらのオブジェクトはそれが現れるあらゆる場所で頻繁に共有されます。ときには、これらの変数に対して、同じだが物理的には別というオブジェクトが作られます。

  * 上記以外のRTL構造体の中では、いかなるRTLオブジェクトも複数の場所に現れることはありません。コンパイラ各パスは、この仮定に依存して、他のinsnsへの予期せざる副作用を起こすことなく、正しい場所のRTLオブジェクトを変更できるのです。

  * 初期のRTL生成の間には、共有構造体が自由に導入されます。ひとつの関数用のすべてのRTLが生成された後は、すべての共有構造体が emit-rtl.cの中のunshare_all_rtlによってコピーされ、その後、上記のルールの遵守が保証されます。

  * コンバイナパス(combiner pass、結合パス)の間には、 insnを伴う共有構造体が存在できます。しかし、その共有構造体は、コンバイナがそのinsnに対する処理を終える前にコピーされます。これはcombine.cのcopy_substitutionsが行います。

マシン記述
*****

マシン記述は、命令パターン用のファイル(.mdファイル)と、マクロ定義のCのヘッダファイルの二部分からなります。

ターゲットマシンの.mdファイルには、そのターゲットマシンがサポートしている各命令 (または少なくともコンパイラに知らせる価値のある各命令)のパターンがあります。それにはコメントも入れられます。セミコロンから行末までは、そのセミコロンが引用文字列の中でなければ、コメントと見なされます。

Cのヘッダファイルに関する情報は、次の章を見てください。

命令パターンとは何か
==========

各命令パターンの中では、未完成のRTL式(後から各欄が填められる)と、それら各欄の填められ方を規定するオペランドの制約、およびひとつの出力パターン、またはアセンブラ出力を生成するためのCコード、以上すべてがひとつのdefine_insn式の中に包まれて存在します。

ひとつのdefine_insnは、オペランドが以下のように4つあるRTL式です。

 1. 名前(オプション)

     名前があると、この命令パターンが、コンパイラのRTL生成パスに対して何らかの標準的なジョブを実行できることを示します。このパスはいくつかの名前を知っていて、そしてその名前がマシン記述で定義されていれば、それらの名前の付いた命令パターンを使用します [特定の標準的なジョブに関しては、そういう名前の付いた命令パターンを使用する]。

     名前を書く場所に空文字列を書くと、名前がないという意味になります。名なしの命令パターンは、RTLコードの生成には使われませんが、それらを使って、後で複数の単純なinsnsを結合(combine)することができます。

     名前がコンパイラに知られていなくて、RTL生成で使われないものは、何の効果もありません。それらは無名と同様です。

 2. RTLテンプレート(see RTL Template)

     RTLテンプレートは、未完成なRTL式の配列であり、それらが命令の形式を表します。それには、命令のオペランドの代わりに match_operand式と match_dup式があるので、未完成なのです。

     配列の成分がただひとつなら、その成分が命令の形式を示します。配列に複数の成分があれば、その命令は、そういう多数の成分を含むparallel式の形式です。

 3. 条件

     これは、ひとつのinsn本体の、このパターンへのマッチを最終的にテストするための、 Cの式の文字列です。

     名前のあるパターンに関しては、条件は(それが存在すれば)、マッチされるinsn中のデータには依存せずに、ターゲットマシンタイプ(target-machine-type)のフラグ(複数)にのみ依存します。コンパイラはこれらの条件を初期化時にテストして、そのときの起動条件下では、どの名前つき命令を使えるのかを正確に知る必要があります。

     無名のパターンに関しては、条件は個々のinsnをマッチングするときのみ適用されます。それも、そのinsnがパターンの認知テンプレートにマッチした後だけです。 insnのオペランドは配列operandsにあります。

 4. 出力テンプレート

     マッチしたinsnの、アセンブラコードとしての出力の形式を示す文字列です。この文字列の中の%が、オペランドの値を置く場所を示します。See Output Template。オペランドを単純に置くだけでは済まない場合は、出力を計算するためのCコード片を指定できます。See Output Statement。

define_insnの例
=============

以下は、68000/68020の実際の命令パターンの例です。

     (define_insn "tstsi"
       [(set (cc0)
             (match_operand:SI 0 "general_operand" "rm"))]
       ""
       "*
     {if(TARGET_68020 || ! ADDRESS_REG_P (operands[0]))
         return \"tstl %0\";
       return \"cmpl #0,%0\";}")

これはオペランドの値に基づいてコンディションコードの値をセットする命令です [68000/020のTSTまたはCMP命令で、オペランドのサイズ指定がl (long word)]。これには条件がないので、 RTLの記述がこの形式のinsnならどれでも、このパターンに従って扱われます。 tstsiという名前は、 ``SImodeの値''を意味し、そういう値を調べる必要があるときはRTL生成パスに対して、このパターンを使えばそれをする命令が構築できることを教えます。

出力制御文字列は、オペランドの種類とCPUのタイプに応じて、どの出力テンプレートを返すかを選ぶ、Cのコード片です。

"rm"はオペランドの制約です。その意味は後述します。

insnsを生成し認識するためのRTLテンプレート
=========================

RTLテンプレートを使って、どのinsnsが特定のパターンにマッチし、また、それらのオペランドをどうやって見つけるかを定義します。名前つきのパターンの場合は、RTLテンプレートが、指定のオペランドからのinsnの構築方法も指定します。

insnの構築は、指定のオペランドをテンプレートの指定位置に置くことです。マッチングは、insn中でオペランドの役を担う値が、 (オペランドとして) マッチしていることを判定することです。これら両方の動きが特殊な式タイプによって制御されて、オペランドのマッチングと挿入を進めていきます。

(match_operand:M N TESTFN CONSTRAINT)
     この式は、insnのオペランド▼番号Nの位置にオペランドが入ります。 insnをマッチングするときは、この場所にあるものがオペランド番号Nのオペランドであると見なされますが、ただしTESTFNを満たさなければ、この命令パターンはまったくマッチしません。

     オペランド番号は、各命令パターンの中でゼロから順に付けます。パターン中の各オペランド番号に対して、 match_operand式はひとつだけです。普通は、オペランドはmatch_operand式の中に現れる順に番号づけされます。

     TESTFNは、マシンモードとひとつの式という2つの引数をとるCの関数の、名前を表す文字列です。マッチングを行なうとき、この関数がモードの引数をM、推定オペランドをもうひとつの引数としてコールされます。それがゼロを返したら、この命令パターンはマッチに失敗です。 TESTFNは空の文字列でもよく、そのときはオペランドに対するテストを行なわないことを意味します。

     ほとんどの場合、TESTFNは"general_operand"です。それは、推定オペランドが定数、レジスタ、またはメモリ参照であって、モードMにとって有効であることをチェックします。

     必ずレジスタでなければならないオペランドの場合は、 TESTFNは"register_operand"でなければなりません。これによって、GNU CCがこれらのオペランドの中にメモリ参照のあるinsns、再調整パス(reload パス)で取り外されるだけのinsnsを作り出すことを防ぎます。

     必ず定数でなければならないオペランドの場合は、 TESTFNが"immediate_operand"であるか、または、命令パターンの追加条件によって定数をチェックするか、あるいはこの両方でなけばなりません。

     CONSTRAINTについては後で説明します (see Constraints)。

(match_dup N)
     この式も、オペランド番号N用のプレースホールダーです。それはinsn中でオペランドが複数回登場する必要があるときに使われます。構築では、match_dupはmatch_operandとまったく同じ振る舞いをし、構築されるinsnの中へオペランドが置かれます。しかし、マッチングでは、match_dupの振る舞いは異なります。それは、オペランド番号Nがすでに認識テンプレートの初めの方のmatch_operandによって決まっていると仮定して、同じ外見の式だけをマッチします。

(address (match_operand:M N "address_operand" ""))
     この複合式は、 ``load address''命令におけるオペランド番号Nのためのプレースホールダーです。オペランドは通常の方法でメモリ番地を指定しますが、それの実際のオペランドの値は番地の内容ではなくて、番地のアドレスが使われます。

     address式はRTLコードの中に現れることはなく、マシン記述の中にのみ現れます。オペランドの制約が使われているときは、制約の中の文字pがこの目的の役を担います。

     Mは、アドレスされるメモリ番地のマシンモードであり、アドレスそれ自体のマシンモードではありません。そのモードはひとつのターゲットマシンの上では常に同じなので (それはPmodeで、通常はSImode)、それを書いても意味がありません。そこで、マシンモードはaddress式の中には書かれません。将来、いろいろな種類のオブジェクトのアドレスの、現れ方や使われ方が様々であるようなマシン (PDP-10 のような) をサポートする場合には、マシンモードの異なる様々なフォーマットが必要となり、 address式にもこれらのモードを書くことになるでしょう。

出力テンプレートとオペランドの書き入れ
===================

出力テンプレートは、命令パターンをアセンブラコードとして出力するときの形を指定する文字列です。テンプレートの大半の部分は、そのまま出力される固定文字列です。オペランドを実際に書き入れる場所は、 %文字で指定されます。この文字はまた、アセンブラの違いによってシンタクスが異なる箇所を示すためにも使われます。

最も単純なケースでは、%に数値Nが続くと、文字列のその位置にオペランドNを出力するという意味になります。

%に文字と数値が続くと、オペランドを別の形式で出力するという意味になります。 4つの文字が決まっており、それらの意味は下記のとおりです。マシン記述マクロPRINT_OPERANDを使って、これら以外の文字の意味を定義できます。

%c数値
     即値オペランドを示す通常のシンタクスなしで、定数値のオペランドを書き入れるために使います。

%n数値
     %c数値と似ていますが、定数の値がネゲートされてから出力されます。

%a数値
     実際のオペランドがアドレスとして扱われ、オペランドがあたかもメモリ参照であるかのように書き入れられます。これは``load address''命令を出力するときには便利です。というのも、こういう命令用のアセンブラのシンタクスは、オペランドをメモリ参照であるかのように書くことを要請している場合が多いからです。

%l数値

     label_refをジャンプ命令で置き換えるために使われます。

%文字に句読点文字が続くと、オペランドのない書き入れ事項を示します。標準で決まっているのは%%だけで、これはアセンブラのコードに%を出力します。その他の文字については、PRINT_OPERANDマクロで定義できます。

テンプレートにより、複数のアセンブラ命令を出力できます。命令の各テキストを\;で区切って書きます。

RTLに2つのオペランドがあって、両者の合致を制約が要請しているとき、出力テンプレートは番号の低い方のオペランドだけを指示していなければなりません。合致するオペランドは常に同一物とはかぎらず、低番号の出力用の正しいRTL式を置き入れる仕事は、コンパイラ自身が担当します。

%に続く特別な句読点文字を定義して、同じマシン用の複数のアセンブラ言語を区別することに利用できます。たとえば、68000用のモトローラのシンタクスとMITのシンタクスです。モトローラのシンタクスは、ほとんどのオペコード名の中にピリオドを要求していますが、 MITのシンタクスは違います。たとえば、MITのシンタクスでmovelと書くシンタクスは、モトローラのシンタクスではmove.lです。同じパターンのファイルを両方の出力用に使いますが、ただしモトローラのシンタクスでピリオドが必要な箇所には、 %.の文字シーケンスを使います。モトローラのシンタクス用のPRINT_OPERANDマクロは、このシーケンスをピリオドの出力と定義し、 MITのシンタクス用のマクロでは、何もしないよう定義します。

アセンブラ出力を生成するためのCの文
==================

ひとつの命令の使われ方の様々なケースにすべて対応して、正しい効率的なアセンブラコードを出力することが、定型の単純なテンプレートを使ってはできないことがよくあります。たとえば、オペコードがオペランドの種類によって変わる命令とか、オペランドの特殊な組み合わせによっては、別のマシン命令を要するような場合です。

出力制御文字列が*で始まっていると、それは出力テンプレートではなくて、テンプレートを作り出すための小さなCプログラムです。それは、 return文を実行して必要とするテンプレート文字列を返すものでなければなりません。そういうテンプレートのほとんどがCの文字列定数を使用するので、デリミタとしての二重引用符を必要とします。文字列の中に二重引用符を含めるためにはその前に\を書きます。

オペランドは配列operandsの中にあり、そのCのデータ型はrtx[]です。

サブルーチンoutput_asm_insnを使えば、ひとつのアセンブラ命令を出力してから、それに続けてさらに命令の出力やプログラムによる生成を行なうことができます。このサブルーチンは、テンプレート文字列とオペランド配列という2つの引数をとります。配列はoperandsまたは、その他のローカルに宣言されて初期化されているrtxの配列です。

insnのパターンの制約に複数のタイプがあるときは、アセンブラコードの姿は、合致するタイプによって決まる場合がほとんどです。こういう場合、Cのコードを利用して変数which_alternativeを調べます。この変数は制約の各タイプを示す順序数です (0が最初のタイプの制約、1がその次、等々)。

たとえば、ゼロをストアするオペコードが2つあり、レジスタ用はclrreg、メモリ番地用はclrmemだとしましょう。以下にひとつのパターンにwhich_alternativeを使って、両者を選び分けるやり方を示します。

     (define_insn ""
       [(set (match_operand:SI 0 "general_operand" "r,m")
             (const_int 0))]
       ""
       "*
     return( which_alternative == 0
             ? \"clrreg %0\" : \"clrmem %0\" );
     ")

オペランドの制約
========

ひとつの命令パターンの中のそれぞれのmatch_operandが、オペランドのタイプに関する制約を指定できます。制約で指定できる事柄は、オペランドがレジスタ上にあってもよいか、どの種類のレジスタを使えるか、オペランドがメモリ参照でもよいか、どの種類のアドレスを使えるか、オペランドは即値定数でもよいか、どういう値ならよいか、などです。また、2つのオペランドの合致を制約で要請することもできます。

単純な制約
-----

最も単純な種類の制約は、単なる文字列で、その各文字が、 (その命令で)使えるオペランドの一種類を記述します。使用できる文字は以下のとおりです。

m
     そのマシンが一般的にサポートしている何らかのアドレスによる、メモリオペランドが使えます。

o
     オフセット可能な(offsetable)アドレスによるメモリオペランドが使えます。オフセット可能とは、アドレスに小さな整数 (それは実際は、マシンモードによって決まる、オペランドの幅のバイト数) を足した結果が、メモリアドレスとして有効という意味です。

     たとえば、定数であるアドレスはオフセット可能で、したがってレジスタと定数との和であるアドレスもそうです (大きめの定数でもそれが、マシンがサポートしているアドレスオフセットの範囲内にあるかぎりは)。しかしオートインクリメントやオートデクリメントのアドレスは、オフセット可能ではありません。もっと複雑な間接アドレスやインデクスドアドレスは、マシンがサポートしているその他のアドレシングモード如何で、オフセット可能であったり、なかったりします。

     別のオペランドとマッチされる出力オペランドの中では、制約文字oは、< (ターゲットマシンにプリデクリメントのアドレシングがあるとき) と> (ターゲットマシンにプリインクリメントのアドレシングがあるとき) を伴うときだけ有効という点に注意してください。

<
     オートデクリメントのアドレシング (プリデクリメントまたはポストデクリメント) によるメモリオペランドが使えます。

>
     オートインクリメントのアドレシング (プリインクリメントまたはポストインクリメント) によるメモリオペランドが使えます。

r
     レジスタオペランド (ただし汎用レジスタにかぎる) が使えます。

d、a、r、...
     それぞれのマシンに特有のレジスタの種類を、その他の文字で制御できます。 d、aおよびfは、 68000/68020上ではそれぞれ、データレジスタ、アドレスレジスタ、浮動小数点レジスタの意味で定義されています。

i
     即値定数オペランド (定数値のもの) が使えます。これには、値がアセンブル時点にならないと分からないような、シンボル定数も含まれます。

n
     値が分かっている即値定数オペランドが使えます。アセンブル時点で値が決まる定数は、ワード幅に満たないものについてはサポートされていないシステムが多いのです。こういうオペランドの制約は、 iでなくnで表します。

I、J、K、...
     IからMまでの文字は、それぞれのマシンに固有の形式で、一定の値域内の明示的な整数値の即値定数オペランドが使えることを定義します。たとえば、68000の上では、 Iは1から8までの値域の意味です。これは、シフト命令でシフトカウントとして許される値の範囲なのです。

F
     浮動小数点数の即値(式コードconst_double)が使えます。

G、H
     GとHは、使える浮動小数点即値に関して、それぞれのマシン特有の値域を定義します。

s
     値が明示的でない即値整数オペランドが使えます。これは、ヘンに思われるかもしれませんね。あるinsnで、コンパイル時に値が分からない定数オペランドが使えるのなら、それはどんな値でもよいことになるではありませんか。ならば、iでなくsを使うことの意味は何でしょうか。それは、そうするとより良いコードが生成されることがときどきあるからです。

     たとえば、 68000ではフルワード (32ビットワード) の命令では即値オペランドを使うことが可能です。しかし、その即値が-32から31までの範囲のときは、値をレジスタにロードして、そのレジスタを使う方が、良いコードになるのです。これは、レジスタへのロードをmoveq命令で行なうことができるからです。私たちは、こういうことを起こさせるために、文字Kを``値域が-32〜31以外の整数''を意味するよう定義し、そしてオペランドの制約としてKsを指定しています。

g
     レジスタ(ただし汎用レジスタのみ)、メモリ、および即値整数オペランドが使えます。

数値
     この番号のオペランド番号にマッチするオペランドが使えます。数値と文字を併記するときは、数値を後に書きます。

     これはマッチング制約(matching constraint)と呼ばれ、その実際の意味は、RTL insnの中では別々と見なされている2つの役目が、アセンブラの段階では単一のオペランドになるということです。たとえば、加算命令ではRTL中で2つの入力オペランドとひとつの出力オペランドがありますが、ほとんどのマシンで、加算命令は実際にはオペランドを2つしか使わず、そのどちらかが入出力用オペランドとして使われます。

     マッチング制約は、そういう加算命令のような状況でのみ有効です。より厳密には、マッチング制約は入力のみのオペランドで使われるべきであり、それがマッチするオペランドは、より低い番号の出力のみのオペランドでなければなりません。

     オペランドがあるひとつのケースでマッチするということは、それらのオペランドが同じような姿のRTL式であるという意味です。ただし、ごく少数の特殊ケースでは、特定の非類似性が許されます。たとえば、入力オペランドとしての*xは、出力オペランドとしての*x++とマッチします。こういうケースで正しい結果得るためには、出力テンプレートは常に、オペランドを出力するとき、出力オペランドの番号を使わなければなりません。

p
     有効なメモリアドレスが、オペランドとして使えます。これは、``load address''と``push address''命令用です。

     制約中にpがあると、 match_operandのテスト関数はaddress_operandでなければなりません。

正しいアセンブラコードを得るためには、各オペランドがその制約を満たしていなければなりません。しかし、それをしなくても、パターンがinsnに適用されなくなることはありません。そういう場合は、コンパイラがコードを変更して、制約が満たされるようにします。これは通常、オペランドをレジスタにコピーすることによって行なわれます。

したがって、以下の2つの命令を対照してみると、

     (define_insn ""
       [(set (match_operand:SI 0 "general_operand""r")
        (plus:SI (match_dup 0)
                 (match_operand:SI 1 "general_operand" "r")))]
       ""
       "...")

上記は2つのオペランドがあって、その内のひとつは二箇所に現れますが、

     (define_insn ""
       [(set (match_operand:SI 0 "general_operand" "r")
             (plus:SI (match_operand:SI 1 "general_operand" "0")
                      (match_operand:SI 2 "general_operand" "r")))]
       ""
       "...")

こちらは3つのオペランドがあって、その内の2つは制約によって同一であることが要請されます。次のような形式のinsnを考えると、

     (insn N PREV NEXT
       (set (reg:SI 3)
            (plus:SI (reg:SI 6) (reg:SI 109)))
       ...)

このinsnでは2つの同一の部分式が正しい場所にありませんから、最初のパターンはまったく適用されません。最初のパターンは「これは加算命令の姿をしていない。別のパターンを試みよ」と言うでしょう。第二のパターンは「たしかに加算命令だが、どこかがヘンだ」と言うでしょう。それはコンパイラの再調整パスで、追加的なinsnsを生成させることによって、制約を真にしようとします。その結果は、次のような姿になるでしょう。

     (insn N2 PREV N
       (set (reg:SI 3) (reg:SI 6))
       ...)

     (insn N N2 NEXT
       (set (reg:SI 3)
            (plus:SI (reg:SI 3) (reg:SI 109)))
       ...)

制約に適合していないinsnsは、オペランドをレジスタにロードすることによって修復されるので、各命令パターンの制約は、すべてのオペランドがレジスタ上にある、というケースを認めていなければなりません。それは、レジスタの全種類である必要はなく、コンパイラは命令を有効とするために、レジスタを正しい種類のレジスタにコピーすることができます。しかし、レジスタの使用がまったく許されていないとコンパイラは身動きできません。コンパイラは、命令を有効にするためにレジスタをメモリにセーブするという芸当まではできません。レジスタを拒否している命令パターンは、問題のオペランドがレジスタの場合には insnとのマッチをまったく拒絶するような条件式を付けることによって有効化できます。

複数ケースのある制約
----------

ひとつの命令で使えるオペランド集合に、複数のタイプがあるという場合がときどきあります。たとえば、 68000では論理和命令はレジスタまたは即値をメモリへ結合することができ、あるいは、どんなオペランドでもレジスタへ結合することができますが、しかし、メモリ番地から別のメモリ番地へという結合はできません [ソースオペランドとターゲットオペランドがともにメモリ参照という使い方ができない]。

こういう制約は、複数種類の制約として表現します。一種類の制約を、各オペランド用の一連の文字として記述できます。ひとつのオペランドに対する全体としての制約は、最初の種類の制約の文字群、コンマ、第二の種類の制約の文字群、コンマ、…、のように記述されます。以下は、68000のフルワードの論理和の場合です。

     (define_insn "iorsi3"
       [(set (match_operand:SI 0 "general_operand" "=%m,d")
             (ior:SI (match_operand:SI 1 "general_operand" "0,0")
                (match_operand:SI 2 "general_operand" "dKs,dmKs")))]
       ...)

最初の制約は、オペランド0に対してはm(メモリ)、オペランド1に対しては0(意味はオペランド0とマッチしなければならない)、オペランド2に対してはdKsです。第二に種類の制約は、オペランド0に対してはd(データレジスタ)、オペランド1に対しては0、そしてオペランド2に対してはdmKsです。オペランド0用の制約にある=と%は、どの制約の一部でもありません。それらの意味は次の節で説明します。

すべてのオペランドが、どれかひとつの種類の制約に適合すれば、その命令は有効です。そうでない場合は、オペランドをコピーして制約を満たすために必要な、追加命令の数をコンパイラが数えます。その際、必要なコピー数が最少の制約が選ばれます。必要なコピー数が同じのときは、最初の方の制約が選ばれます。その選択は、 ?と!の文字で変えることができます。

?
     どの制約にも正確に適合しないときの候補として、 ?のある制約はあまり適していないと見なす。コンパイラはこの制約を、その中の?一個につき1単位コスト高であると見なす。

!
     !のある制約は、候補としてほとんど適していないと見なす。オペランドをレジスタにコピーしなければならないときは、コンパイラは絶対にこの制約を採用しない。

命令パターンに複数種類の制約があるときは、アセンブラコードの姿は、その制約にマッチしたかで決まることが多いといえます。そういう場合は、アセンブラコードを書き出すためのCコードは、変数which_alternativeを利用できます。これは、実際に満たされた制約を表す順序数です (最初のが0、第二の制約が1、等々)。たとえば、以下のようになります。

     (define_insn ""
       [(set (match_operand:SI 0 "general_operand" "r,m")
             (const_int 0))]
       ""
       "*
       return( which_alternative == 0
               ? \"clrreg %0\" : \"clrmem %0\" );
       ")

レジスタの種類の選好
----------

オペランドの制約にはもうひとつの機能があります。疑似レジスタを、どのハードウェアレジスタに割り当てると最良かを、コンパイラに決めさせる機能です。コンパイラは疑似レジスタを使用するinsnsに適用される制約を調べて、レジスタの種類を指定しているdやaなどのマシン固有の文字を探します。疑似レジスタは、最大の``票数''を集めたレジスタ種類へと置かれます。制約文字gとrも票数に入ります。これらは汎用レジスタへの一票です。どのレジスタが汎用レジスタかは、マシン記述が定義します。

もちろん、一部のマシンではすべてのレジスタが同等で、レジスタの種類というものが定義されません。そのときは、こういう問題がまったく存在しません。

制約修飾文字
======

#
     このオペランドがこの命令に関してライトオンリーであることを意味します。前の値が捨てられて、出力データで置換されます。

+
     このオペランドが命令によって、リードとライトの両方をされることを意味します。

     コンパイラが制約を満たすためにオペランドを修復するとき、どのオペランドが命令への入力で、どれが命令からの出力かを知る必要があります。 =は出力を示し、 +は入力と出力の両方であるオペランドを示します。それ以外のオペランドは、すべて入力のみとなります。

&
     (その特定の制約において)このオペランドが、命令が入力オペランドに対する処理を終える前に書き込まれることを意味します。したがって、このオペランドは、入力オペランドや、メモリアドレスの一部として使われるレジスタにあってはいけません。

     &は、ライトされるオペランドの制約に対してだけ適用されます。複数の制約がある場合は、ひとつの制約が&を必要とし、他は必要としないことがあります。たとえば、68000のmovdf insnを見てください。

     &があっても、 =を省略することはできません。

%
     命令が、このオペランドと、それに続くオペランドに関して可換であることを宣言します。すなわちコンパイラは、 2つのオペランドを交換することが、すべてのオペランドを制約に適合させるための最も安価な方法であるならばそうできます。これは、実際にオペランドが2つしかない加算命令のパターンでよく使われます。加算の結果がどちらかの引数に入るのです。たとえば、68000のハーフワード(16ビットワード)の加算命令は、次のように定義されます。

          (define_insn "addhi3"
            [(set (match_operand:HI 0 "general_operand" "=m,r")
                  (plus:HI (match_operand:HI 1 "general_operand" "%0,0")
              (match_operand:HI 2 "general_operand" "di,g")))]
            ...)

     GNU CCの前のバージョンでは、制約修飾文字%は、無差別に常にオペランド1とオペランド2に適用されていました。通常のやり型では、それはオペランド0に書きます。しかし、今では、交換されるオペランドが1と2ならば、オペランド1に書かなければなりません。

#
     これ以降から次のコンマまでの文字を、制約としては無視することを意味します。それらは、レジスタを種類を選ぶ場合にのみ有意です。

*
     レジスタを選択する際、これ以降の文字を無視するよう指示します。本来の制約にとっては、 *は何の効果もありません。

     以下は実例です。 68000には、データレジスタ上のハーフワードを符号拡張する命令があり、値をアドレスレジスタへコピーすることによっても符号拡張ができます。このように、どちらの種類のレジスタでも使えるのですが、デスティネーションのアドレスレジスタに対する制約はやや緩いため、レジスタの割り当てに際してはアドレスレジスタを狙うのがベストです。したがって、レジスタの選択を決めるときには制約文字d (データレジスタ)が無視されるように、 *を使うのです。

          (define_insn "extendhisi2"
            [(set (match_operand:SI 0 "general_operand" "=*d, a")
                  (sign_extend:SI
                  (match_operand:HI 1 "general_operand" "0,g")))]
            ...)

制約を使わない場合
---------

一部の、きわめてクリーンなマシンでは制約が不要です。たとえば、VAXでは、あるコンテキストで有効なオペランドは、他のコンテキストでも有効です。そのようなマシンでは、オペランドの制約はどれもgとなるでしょうが、ただし``load address''命令だけはそうはなりません。この命令はメモリ番地の内容を参照するような書かれ方をしますが、実際はそのアドレスを参照するからです。したがってその制約はpでしょう。

このようなマシンに対しては、制約として一々gやpを書かずに、制約が空のマシン記述を書くことができます。そしてmatch_operandの制約としてはすべて""を書きます。アドレスオペランドは、制約によってではなく、 match_operandを囲むaddress式を書くことによって識別されます。

マシン記述に空の制約しかないときは、コンパイル過程の一部がスキップされるのでコンパイル速度が速くなります。

RTLの生成に使われるパターンの標準名
===================

以下は、コンパイラのRTL生成パスで意味を持つ命令名の一覧です。これらの名前のどれかを命令パターンに与えると、 RTL生成パスに対して、あるタスクの実現にはその命令を使えと教えることになります。

movM
     ここでMは、小文字二文字で表すマシンモード名です。この命令パターンは、そのマシンモードのデータを、オペランド1からオペランド0へと転送します。たとえば、movsiはフルワードのデータを転送します。

     オペランド0が、本来のモードがMよりも広いレジスタの、モードMのsubregならば、この命令の効果は、指定の値をレジスタの、モードMに対応する部分にストアすることになります。レジスタのその他の部分に対する効果は未定義です。

     この種類のパターンは、いくつかの点で特殊です。何よりまず、これらの名前のそれぞれを定義しなければなりません。これらの他には、データをひとつの場所から他の場所へコピーする方法がないからです。第二に、これらのパターンはRTL生成パスでのみ使われるのではありません。再調整パスでもmove命令が生成されて、値をスタックスロットからテンポラリレジスタへコピーしたりすることがあります。そのときは、オペランドの一方はハードレジスタで、他方はそれに再ロードされるオペランドです。

     したがって、こういう対(つい)のオペランドを与えられたら、パターンはテンポラリレジスタを必要としないRTL、オペランド以外のレジスタを必要としないRTLを生成しなければなりません。たとえば、 define_expandでパターンをサポートするなら、そういうケースではforce_regなどの、新たな疑似レジスタを生成する関数をコールしてはいけません。

     この要件は、RISCマシンのサブワードモードに関しても存在します。 RISCマシンではサブワードモードのフェッチが、複数の命令と何個かのテンポラリレジスタを必要とします。要件をどのようにして満たしているのかは、 spur.mdを覗いてください。

     再ロードされるオペランドの種類は、全体としてのマシン記述で決まりますが、 RISCマシンでのその典型的な場合は、ハードレジスタを得られなかい疑似レジスタのみであり、他方、それ以外のマシンでは明示的なメモリ参照がオプションとして再ロードされます。

     さらに、制約は、どのハードレジスタからどのハードレジスタへの転送でも、許容していなければなりません (それぞれのレジスタに対してHARD_REGNO_MODE_OKがモードMを許容しているかぎりは)。

movstrictM
     movmと同じです。ただし、オペランド0が本来のモードがMよりも広いレジスタの、モードMのsubregならば、そのレジスタのモードMに属する部分以外を変更しないことが保証されます。

addM3
     オペランド2とオペランド1を加算して、結果をオペランド0にストアします。オペランドはすべて、モードMです。これは、オペランド1と0が同じ番地という制約を課することによって、 2アドレス方式のマシンでも使えます。

subM3、mulM3
divM3、udivM3、modM3、umodM3
sminM3、smaxM3、uminM3、umaxM3
andM3、iorM3、xorM3

     それぞれの演算に関して上と同様です。

adncbM3
     オペランド1と、オペランド2の補数とのビットごとの論理積をとり、結果をオペランド0にストアします。

mulhisi3
     モードがHImodeであるオペランド1と2を乗じ、 SImodeの積をオペランド0にストアします。

mulqihi3、mulsidi3
     上と同様で、 QIからHI、SIからDIへという幅拡張乗算です。

umulqihi3、umulhisi3、umulsidi3
     上と同様の幅拡張乗算ですが、無符号数の乗算をします。

divmodM4
     商と余りの両方を作り出す有符号除算です。オペランド1をオペランド2で割り、商をオペランド0に、余りをオペランド3にストアします。

udivmodM4
     上と同様ですが、除算は無符号除算です。

divmodMN4
     divmodM4と同様ですが、ただし被除数だけがモードMで、除数と商と除りはモードNです。たとえば、VAXにはdivmoddisi4という命令があります (これはしかし、マシン記述には入れていません。なぜなら、この命令はあまりに遅いので、この命令が使えないときにコンパイラが使うまわりくどい方法の方が、余りの計算は速いのです)。

ashlM3
     オペランド1をオペランド2で指定されるビット数だけ算術右シフトし、結果をオペランド0にストアします。オペランド2のモードはSImodeであり、 Mではありません。

ashrM3、lshlM3、lshrM3、rotlM3、rotrM3
     その他のシフトおよびローテート命令です。

     論理左シフトと算術左シフトは同じです。負数のシフトカウントを指定できないマシンでは、シフト命令は左シフトしかないマシンが多いようです。そういうマシンでは、ashlM3という名前のパターンを定義して、 lshlM3は未定義にしておかなければなりません。

negM2
     オペランド1をネゲートして、結果をオペランド0にストアします。

absM2
     オペランド1の絶対値をオペランド0にストアします。

sqrtM2
     オペランド1の平方根をオペランド0にストアします。

ffsM2
     オペランド1の最下位の1のビットのインデクス(ビット位置)+1という値を、オペランド0にストアします。オペランド1がゼロならゼロをストアします。 Mはオペランド0のモードです。オペランド1のモードは命令パターンによって指定され、コンパイラは命令を生成する前にオペランドをそのモードに変換します。

one_cmplM2
     オペランドのビットことの補数を、オペランド0にストアします。

cmpM2
     オペランド0とオペランド1を比較して、コンディションコードをセットします。 RTLパターンは次のようになります。

          (set (cc0) (minus (match_operand:m 0 ...)
                            (match_operand:m 1 ...)))

     モードMの整数に対する、マシン記述中のこの形の定義のそれぞれに対応して、 tstMのパターンが存在しなければなりません。なぜなら、オペランド1がゼロのときは、オプティマイゼーションは比較をテストへと単純化するからです。 [68000等のTST命令=オペランドをゼロと比較する。]

tstM
     オペランド0をゼロと比較して、コンディションコードをセットします。 RTLパターンは次のようになります。

          (set (cc0) (match_operand:m 0 ...))

movstrM
     ブロック転送命令です。デスティネーションとソースストリングのアドレスが最初の2つのオペランドで、どちらもモードがPmodeです。転送バイト数が第三のオペランドで、モードはMです。

cmpstrM
     ブロック比較命令で、オペランドはmovstrMと同様ですが、 2つのメモリブロックが1バイトずつ、辞書的順序に関して比較されます。この命令の効果は、コンディションコードがセットされることです。

floatMN2
     オペランド1(固定小数点モードMの有効値)を、モードNの浮動小数点数に変換して、オペランド0(モードN)にストアします。

fixMN2
     オペランド1(浮動小数点モードMの有効値)を、有符号数のモードNの固定小数点数に変換して、オペランド0(モードN)にストアします。この命令の結果は、オペランド1の値が整数のときだけ定義されます。

fixunsMN2
     オペランド1(浮動小数点モードMの有効値)を、無符号数のモードNの固定小数点数に変換して、オペランド0(モードN)にストアします。この命令の結果は、オペランド1の値が整数のときだけ定義されます。

ftruncM2
     オペランド1(浮動小数点モードMの有効値)を、浮動小数点モードMで表現される整数に変換し、オペランド0(浮動小数点モードMに有効)にストアします。

fix_truncMN2
     fixMN2と同様ですが、モードMのどのような浮動小数点数にも作用して、値を整数に変換します。

fixuns_truncMN2
     fixunsMN2と同様ですが、モードMのどのような浮動小数点数にも作用して、値を整数に変換します。

truncMN
     オペランド1(モードMに有効)をモードNに丸めて、オペランド0(モードN)にストアします。モードは両方とも、固定小数点または浮動小数点でなければなりません。

extendMN
     オペランド1(モードMに有効)をモードNに符号拡張して、オペランド0(モードN)にストアします。モードは両方とも、固定小数点または浮動小数点でなければなりません。

zero_extendMN
     オペランド1(モードMに有効)をモードNにゼロ拡張して、オペランド0(モードN)にストアします。モードは両方とも、固定小数点または浮動小数点でなければなりません。

extv
     オペランド1 (レジスタまたはメモリオペランド) からビットフィールドを取り出しますが、そのためにオペランド2がビットの幅、オペランド3がスタートビットを指定して、オペランド0にストアします。オペランド0はSImodeでなければなりません。オペランド1はQImodeかSImodeですが、 SImodeはレジスタにしか使えない場合がよくあります。オペランド2と3はSImodeに有効でなければなりません。

     RTL生成パスは、オペランド2と3が定数のときのみ、この命令を生成します。

     ビットフィールドの値は、フルワード整数へと符号拡張されてから、オペランド0にストアされます。

extzv
     extvと同様ですが、ビットフィールドの値がゼロ拡張されます。

insv
     オペランド3(SImodeに有効)を、オペランド0のビットフィールドにストアしますが、そのためにオペランド1がビットの幅、オペランド2がスタートビットを指定します。オペランド0はQImodeかSImodeですが、 SImodeはレジスタにしか使えない場合がよくあります。オペランド1と2はSImodeに有効でなければなりません。

     RTL生成パスは、オペランド1と2が定数のときのみ、この命令を生成します。

sCOND
     コンディションコードに応じて、ゼロまたは非ゼロをオペランドにストアします。ストアされる値は、条件CONDが真ならば非ゼロです。 CONDは、eq、lt、leuなど、比較演算式の名前です。

     match_operand式を書くとき、オペランドが持つべきモードを指定します。コンパイラはその指定を見て、そのモードのオペランドを自動的に供給します。

     真の条件でストアされる値は、最下位ビットが1でなければなりません。それをしない命令は不適切なため、マシン記述から排除しなければなりません。マクロSTORE_FLAG_VALUEを定義することによって、ストアする正確な値をコンパイラに教えなければなりません。

bCOND
     条件分岐命令です。オペランド0は、分岐先のラベルを示すlabel_refです。コンディションコードが条件CONDに合えば分岐します。

call
     値を返さないサブルーチンコール命令です。オペランド0がコールする関数、オペランド1はプッシュする引数のバイト数 (それが通常ひとつのconst_intでない場合は、モードSImodeの)、オペランド2はオペランドとして使用されるレジスタの数です。

     ほとんどのマシンで、オペランド2は実際はRTLパターンにストアされません。

     オペランド0は必ずmem RTXで、そのアドレスは関数のアドレスでなければなりません。

call_value
     値を返すサブルーチンコール命令です。オペランド0は返し値を入れるためのハードレジスタです。その他3つのオペランドがあり、それらはcall命令の3つのオペランドと同じです (ただし番号がそれぞれ1多い)。

return
     サブルーチンリターン命令です。この命令パターン名は、関数からのリターンのすべての仕事がたったひとつの命令でできる場合にのみ、定義すべきものです。

casesi
     ディスパッチテーブルを介してのジャンプ命令で、境界チェックを含みます。この命令はオペランドを5つとります。

      1. ディスパッチ元のインデクスで、モードはSImode。

      2. テーブルインデクスの下限境界、整数定数。

      3. テーブルインデクスの上限境界、整数定数。

      4. インデクスが境界外の値なら、ジャンプ先ラベル (マシン記述マクロCASE_DROPS_THROUGHが定義されていれば、境界外のインデクスは、このラベルにはジャンプせずに、ジャンプテーブルに続くコードへと落ちます。その場合は、このラベルはcasesi命令によって実際には使われませんが、オペランドとしては常に伴われます)。

      5. テーブルそのもののラベル。

     テーブルは、 jump_insnの中のaddr_vecまたはaddr_diff_vecです。テーブルの成分数は、上限境界-下限境界+1です。

tablejump
     可変アドレスへのジャンプ命令です。これは、 casesiパターンがないときにはディスパチテーブルを実装するために使う、低レベル機能です。

     このパターンは2つのオペランドを必要とします。アドレスないしオフセットと、ジャンプテーブルの直前に付くラベルです。マクロCASE_VECTOR_PC_RELATIVEが定義されていると、最初のオペランドはジャンプ先の絶対アドレス、定義されていないと、テーブルのアドレスからのオフセットです。

     tablejump insnは常に、それが使用するジャンプテーブルより以前の、最後のinsnです。そのアセンブラコードは通常、第二オペランドを使う必要がありませんが、 RTLパターンには必ず入れて、ジャンプオプティマイザがテーブルを、到達不能コードとして削除しないようにします。

パターンの順序に意味がある場合
===============

ときどき、ひとつのinsnが複数の命令パターンにマッチすることがあります。そういうときは、マシン記述の中で最初にあるパターンが使われます。したがって、特定性がより強くて(マッチする事項が少ない)、より高速な命令(マッチしたときにより良いコードを作り出すもの)を、記述の最初に置くべきです。

パターンの順序が持つ効果を利用して、有効でないパターンを隠す場合があります。たとえば、68000にはフルワードを浮動小数点数に変換する命令と、 1バイトを浮動小数点数に変換する命令があります。整数を浮動小数点数に変換する命令は、このどちらかにマッチします。そこで、フルワードを変換するパターンを最初に置いて、こちらが必ず使われるようにします (そうでないと、大きな整数が1バイトの即値として生成され、目的を達しません)。このパターンの順序を利用する以外に、バイトを変換する方のパターンをうまく作って、どんな定数値でも正しく扱うようにすることが可能です。

複数のパターン間の相互依存関係
===============

どのマシン記述にも、それぞれの条件分岐命令bCOND用の名前つきパターンがなければなりません。認識テンプレートは、常に次の形式です。

     (set (pc)
          (if_then_else (COND (cc0) (const_int 0))
                                    (label_ref (match_operand 0 "" ""))
                                    (pc)))

加えて、どのマシン記述にも、ありうる逆条件分岐用に、無名のパターンがなければなりません。こちたのパターンの形式は、次のようになります。

     (set (pc)
          (if_then_else (COND (cc0) (const_int 0))
                        (pc)
                        (label_ref (match_operand 0 "" ""))))

それらが必要なのは、ジャンプオプティマイゼーションで、直接条件分岐を逆条件分岐に変えられるためです。

コンパイラはRTLに関して、それをパターンから作り出してパターンを認識するだけでなく、オペランドの値として定数値が決まる場合は、そのための算術式コードの実行もします。その結果、ときどきひとつのパターンのために別のパターンが必要になることがあります。たとえば、 VAXにはand命令がありません。しかし、and not命令があります。以下は、それらのひとつの定義です。

     (define_insn "andcbsi2"
       [(set (match_operand:SI 0 "general_operand" "")
             (and:SI (match_dup 0)
                     (not:SI (match_operand:SI
                               1 "general_operand" ""))))]
       ""
       "bicl2 %1,%0")

オペランド1が明示的な整数定数なら、上記のパターンを使って構築される命令は、次のようにandへと単純化できます。

     (set (reg:SI 41)
          (and:SI (reg:SI 41)
                  (const_int 0xffff7fff)))

上記で整数定数は、 (元の命令に現れるものの1の補数です)。

致命的エラーを避けるために、コンパイラはそういう命令を認識するパターンを持たなければなりません。以下が使われているものです。

     (define_insn ""
       [(set (match_operand:SI 0 "general_operand" "")
             (and:SI (match_dup 0)
                     (match_operand:SI 1 "general_operand" "")))]
       "GET_CODE (operands[1]) == CONST_INT"
       "*
     { operands[1]
         = gen_rtx (CONST_INT, VOIDmode, ~INTVAL (operands[1]));
       return \"bicl2 %1,%0\";
     }")

一般のand命令にマッチするパターンはVAX上ではサポートできないものの、上記のパターンは、定数の第二引数とのみマッチするので可能です。 and not命令として出力できる特殊なケースです。

RTLが、

     (set (cc0) (minus OPERAND (const_int 0)))

のような姿のcompare命令は、オプティマイゼーションにより、次のようなtestへと単純化できます。

     (set (cc0) OPERAND)

そこで、マシン記述の中では、それぞれの整数モード用のcompare命令に対応して、こういう単純化うぃ可能にするためのtestパターンがなければなりません。

マシンが、オペランドのマシンモードが違うだけの、同一の命令をサポートしている場合があります。たとえば、次のようなパターンの``ハーフワードの符号拡張'' と``バイトの符号拡張''がありえます。

     (set (match_operand:SI 0 ...)
          (extend:SI (match_operand:HI 1 ...)))

     (set (match_operand:SI 0 ...)
          (extend:SI (match_operand:QI 1 ...)))

整数定数はマシンモードを指定しないので、定数値を拡張する命令はどちらのパターンにもマッチします。実際にマッチするパターンは、ファイル中で最初に現れるパターンです。正しい結果を得るためには、それは広い方のモード(上記ではHImode) のパターンでなればなりません。パターンがQImodeの命令とマッチすると、定数値がそのモードに適合していない場合には不正な結果となります。

ジャンプ命令のパターン
===========

GNU CCは、マシンにはコンディションコードがあると仮定します。比較命令はコンディションコードをセットして、オペランドの有符号および無符号の比較結果を記録します。それとは別個にある分岐命令は、コンディションコードを調べて、その値に応じて分岐したりしなかったりします。分岐命令は、有符号系と無符号系とに分かれます。 Vaxや68000、32000など、よく使われている多くのマシンでは、このようになっています。

一部のマシンでは、比較命令は有符号と無符号に分かれますが、条件分岐命令はひとつの系統しかありません。これらのマシンの最も楽な扱い方は、アセンブリコードを出力する最終段階までは、他のマシンと同じように扱うことです。アセンブリコードを出力する時点で、比較命令のコードを出力するときに、 NEXT_INSN(insn)を使って、それに続く分岐命令を先読みします (変数insnは出力される命令を指し、命令パターン中の出力書き出しコードの中にあります)。無符号の分岐というRTLならば、無符号の比較を出力します。そうでなければ、有符号の比較を出力します。分岐命令そのものを出力するときは、有符号分岐も無符号分岐も同じ扱いです。

これができる理由は、 GNU CCは常に、コンディションコードをセットするRTL insnと、それを調べるRTL insnを2つくっつけて生成し、最後までその対(つい)を無傷で維持するからです。

このテクニックを使うには、マシン記述マクロNOTICE_UPDATE_CCを定義して、 CC_STATUS_INITをきず行なわければなりません。言い換えると、いかなる比較命令も余計ではありません。

一部のマシンには、比較＆分岐命令があって、コンディションコードがありません。それらに対しても同様のテクニックが有効です。比較命令を``出力する''時点になると、そのオペランドを2つのstatic変数に記録します。それに続く、コンディションコードに応じて分岐する命令を出力するとき、実際には記録されているオペランドを使用する比較＆分岐命令を出力します。

それは、比較＆分岐命令のパターンを定義するためにも使えます。最適化(オプティマイジング)を行なうコンパイル過程では、対を成している比較命令と分岐命令が、これらのパターンに基づいて結合されます。しかし、これは最適化がリクエストされていないときには起こりません。そこで、定義した特殊なパターンに加えて、上記の解のどれかを使わなければなりません。

マシン固有のピープホールオプティマイザの定義
======================

命令パターンに加え、 mdファイルにはマシン固有のピープホールオプティマイゼーションの定義を含めることができます。 [訳者注：peephole optimizations、覗き穴式最適化、コードシーケンスをある程度先読みすることによって、非効率な部分等の判断・書き換えを行なう最適化手法。]

コンバイナ(結合パスを実行する部分)は、プログラム中のデータの流れが、それを試行することを示唆していないときには、ある種のピープホールオプティマイゼーションに気付きません。たとえば、ひとつの目的を達成するための2つの命令を、 2つ目が最初の命令が計算したレジスタを使っていなくても、結合できる場合があります。マシン固有のピープホールオプティマイザは、そういう機会を検出できます。

その定義は次のような姿をしています。

     (define_peephole
       [INSN-PATTERN-1
        INSN-PATTERN-2
        ...]
       "CONDITION"
       "TEMPLATE")

上記のスケルトンの中で、 INSN-PATTERN-1等々は、連続している命令にマッチするパターンです。オプティマイゼーションは、一定の命令シーケンスがその冒頭から、 INSN-PATTERN-1、2、...とマッチしたときに適用されます。

INSN-PATTERN-1等々は、 define_insnの第二オペランドとほとんど同じ姿をしています。重要な違いがひとつあります。このパターンはRTXであり、ベクタではありません。 define_insnのパターンが一成分のベクタであるなら、 INSN-PATTERNは単なる成分であって、ベクタではないということになります。 define_insnのパターンに複数の成分があるなら、 INSN-PATTERNはそのベクタを、明示的なparallel RTXの中に入れなければなりません。

命令のオペランドは、普通にmatch_operandとmatch_dupとマッチさせられます。普通と違う点は、オペランド番号が定義内のすべての命令パターンに適用されることです。そこで、 2つの命令の中の同一オペランドを、一方の命令のmatch_operandと他方のmatch_dupを使って、チェックできます。

match_operandのパターンで使われているオペランドの制約は、オプティマイゼーションの適用の可否に直接の影響は及ぼしませんが、後から検証されるので、オプティマイゼーションが適用されるところへは常に、最も適切な制約を書いておきます。各オペランドに対して"g"を使うのが安全です。

命令シーケンスがパターンとマッチすると、 CONDITIONがチェックされます。これは、オプティマイゼーションを実際に行うかどうかを決めるための、 Cの式です (式が非ゼロならオプティマイゼーションを実行します)。 CONDITIONを省略すると (つまり、それが空文字列だと)、オプティマイゼーションは、パターンとマッチするどの命令シーケンスに対しても適用されます。

定義したピープホールオプティマイゼーションは、レジスタ割り当ての完了後に適用されます。したがって、オプティマイザはオペランドを見るだけで、どのオペランドがどのレジスタに収まるかをチェックできます。

CONDITIONの中のオペランドを参照する方法は、オペランド番号Iのオペランド ((match_operand I ...)にマッチするもの) に対してoperands[I]と書くことです。変数insnを使って、マッチさせられるinsnsの最後のものを参照します。 PREV_INSNを使って、それの前の命令を見つけます (ただし、途中にあるnote insnsはスキップするよう注意が必要です)。

中間的な結果による計算をオプティマイズするときは、 CONDITIONを使って、その中間的な結果が他の場所で使われていないときだけ、マッチを行なうようにできます。 Cの式、 dead_or_set_p (INSN, OP)を使います。ここでINSNは、その値(PREV_INSNをともに使って、insnの値から) を最後に使用する命令であってほしい命令です。そして、 OPが中間的な値 (operands[I]から) です。

オプティマイゼーションの適用とは、命令シーケンスをひとつの新たな命令で置換することです。 templateが、この結合された命令のアセンブラコードの、最終的な出力を制御します。その機能は、 define_insnのテンプレートとまったく同じです。このテンプレート内のオペランド番号は、元の命令シーケンスのマッチングで使われたものと同じです。

定義したピープホールオプティマイゼーションの結果は、何らかの命令パターンとマッチする必要はなく、またマッチする機会もありません。ピープホールオプティマイザの定義自体が命令パターンの役を果たし、命令の出力のされ方を制御します。

定義したピープホールオプティマイザは、最後の分岐最適化パスで実行されるのでそれらが作り出す命令が、自動的に結合されたり再編成されることはありません。

以下は、 68000のマシン記述から取った実例です。

     (define_peephole
       [(set (reg:SI 15) (plus:SI (reg:SI 15) (const_int 4)))
        (set (match_operand:DF 0 "register_operand" "f")
             (match_operand:DF 1 "register_operand" "ad"))]
       "FP_REG_P (operands[0]) && ! FP_REG_P (operands[1])"
       "*
     {
       rtx xoperands[2];
       xoperands[1] = gen_rtx (REG, SImode, REGNO (operands[1]) + 1);
     #ifdef MOTOROLA
       output_asm_insn (\"move.l %1, (sp)\", xoperands);
       output_asm_insn (\"move.l %1, -(sp)\", operands);
       return \"fmove.d (sp)+, %0\";
     #else
       output_asm_insn (\"move.l %1, sp\", xoperands);
       output_asm_insn (\"move.l %1, sp\", operands);
       return \"fmove.d sp+, %0\";
     #endif
     }
     ")

     上記のオプティマイゼーションの結果は、
     次のような変更が行なわれることです。

          jbsr _foobar
          addql #4,sp
          movel d1,sp@-
          movel d0,sp@-
          fmoved sp@+,fp0

     を、下のように変えます。

          jbsr _foobar
          movel d1,sp@
          movel d0,sp@-
          fmoved sp@+,fp0

コード生成用のRTLシーケンスの定義
==================

一部のターゲットマシン上では、 RTL生成用の標準パターン名の一部がひとつの命令では扱えず、 RTL insnsのシーケンスでならそれらを表現できます。こういうターゲットマシンに対しては、 define_expandを書いて、 RTLのシーケンスの生成の仕方を指定できます。

define_expandは、 define_insnとほとんど同じ形をしていますが、後者と違ってdefine_expandはRTLの生成用にのみ使用し、複数のRTL insnを作り出すことができます。

define_expand RTXには、 4つのオペランドがあります。

  * 名前。 define_expandは必ず名前で参照するので、名前がなければなりません。

  * RTXテンプレート。これは、式のそれぞれがひとつのinsnであるようなRTL式のベクタであるという意味で、 define_peephole用のRTLテンプレートと同様のものです。 [訳者注：原文のママ。]

  * Cの式の文字列による条件。この式は、 GNU CCの実行時にコマンド行で指定するターゲットマシンのサブクラスの種類によって、このパターンが使えるか使えないか表すために使います。これは標準パターン名を使うdefine_insnの条件と、同様のものです。

  * 準備文。ゼロ個以上のCの文から成る文字列で、 RTLテンプレートからRTLコードが生成される前に実行されるものです。

通常はこれらの文は、 RTLテンプレートの内部的なオペランドとして使うテンポラリレジスタを用意しますが、それらはまた、 emit_insnなどのルーチンをコールして、直接にRTL insnsを生成することもできます。このようなinsnsはどれも、 RTLテンプレートから生成されるものの前に来ます。

RTLテンプレートは、 RTL insnsの生成を制御することに加えて、このパターンが使用されるときに指定が必要なオペランドを記述することもします。特にそれは、各オペランドへの述語を与えます。

パターンからRTLを生成するために指定しなければならない真のオペランドは、 RTLテンプレートの中に最初に登場するとき、 match_operandで記述されなければなりません。これはオペランドの述語上の情報を使って、そうすることが有効なRTLコードのために必要ならば、オペランドをレジスタにプリロード(あらかじめロード)します。オペランドが複数回参照されるなら、以降の参照はmatch_dupを使います。

RTLテンプレートは、 define_expandが作るシーケンスの中でのみ使われるテンポラリレジスタやラベルなどの、内部的な``オペランド''も参照できます。内部的なオペランドは、 RTLテンプレートの中でmatch_dupで置き換えられるものであり、けしてmatch_operandではありません。内部的なオペランドの値は、コンパイラがこのパターンの使用をリクエストするときも、引数として渡されません。むしろそれらは、準備文の中で、パターン内部で計算されます。これらの文が値を計算して、それらをオペランドの適切な成分の中へストアし、 match_dupがそれらを見つけられるようにします。

準備文の中で使う特殊なマクロが2つ定義されています。 DONEとFAILです。これらにセミコロンを付けて文として使います。

DONE
     DONEを使って、そのパターン用のRTLの生成を終わらせます。このとき、そのパターンから生まれているRTL insnsは、準備文の中でemit_insnを明示的にコールして生じさせたものだけで、 RTLテンプレートは生成されません。

FAIL
     今回はそのパターンを失敗に終わらせます。パターンが失敗するとは、そのパターンが使えないという意味です。コンパイラの中のコール側のルーチンは、他のパターンを使う別のコード生成戦略を試みます。

     失敗は現在、バイナリ演算に対してのみサポートされています (加算、乗算、シフト、等々)。

     失敗する前にemit_insnで明示的にinsnsを生じさせてはいけません。

以下は、SPURチップの左シフトの定義の例です。

     (define_expand "ashlsi3"
       [(set (match_operand:SI 0 "register_operand" "")
             (ashift:SI
               (match_operand:SI 1 "register_operand" "")
               (match_operand:SI 2 "nonmemory_operand" "")))]
       ""
       "
     {
       if (GET_CODE (operands[2]) != CONST_INT
           || (unsigned) INTVAL (operands[2]) > 3)
             FAIL;
     }")

上記の例は、 define_expandを使って、シフトカウントが0から3までというサポートされている値域ならばシフト用のRTL insnを生成し、その他の、マシン命令が存在しないケースに対しては失敗します。失敗すると、コンパイラは別のパターンを使って (たとえばライブラリコール) 別の戦略を試みます。

コンパイラが、名前のあるパターン中の重要な条件文字列を取り扱えたならば、その場合はdefine_insnを使えたでしょう。以下は、 define_expandの力をさらに本格的にら 誤殖? 利用している例です (68000のゼロ拡張)。

     (define_expand "zero_extendhisi2"
       [(set (match_operand:SI 0 "general_operand" "")
             (const_int 0))
        (set (strict_low_part
               (subreg:HI
                 (match_operand:SI 0 "general_operand" "")
                 0))
             (match_operand:HI 1 "general_operand" ""))]
       ""
       "operands[1] = make_safe_from (operands[1], operands[0]);")

上記では、出力オペランド全体をクリアするものと、入力オペランドをその下半分でコピーするものの、計2つのRTL insnsが生成されます。このシーケンスは、入力オペランドが出力オペランド[の古い値]を参照すると不正なので、準備文でそうでないことを確認しています。関数make_safe_fromは、 operands[1]を、それがoperands[0]を参照しているならば、テンポラリレジスタにコピーします。それは、別のRTL insnを生じさせることによって行ないます。

最後は、内部オペランドの使い方を示す3つ目の例です。 SPURチップ上のゼロ拡張は、結果とハーフワードのマスクとのandをとることによって行なわれます。しかし、このマスクは、 const_intによっては表現できません。定数値はこのマシンにとっては大きすぎて、不適切だからです。そこで、それはforce_regによってレジスタにコピーし、そのレジスタをand演算に使います。

     (define_expand "zero_extendhisi2"
       [(set (match_operand:SI 0 "general_operand" "")
             (and:SI (subreg:SI
                        (match_operand:HI 1 "general_operand" "")
                        0)
                     (match_dup 2)))]
       ""
       "operands[2]
          = force_reg (SImode, gen_rtx (CONST_INT,
                                        VOIDmode, 65535));")

マシン記述マクロ
********

マシン記述のもう片方は、貫例的にtm-MACHINE.hと名付けられているCのヘッダファイルです。ファイルtm.hがこれにリンクされなければなりません。ヘッダファイルconfig.hにtm.hが含まれ、そしてコンパイラのソースファイルのほとんどが config.hをインクルードしています。

ランタイムターゲットの指定
=============

CPP_PREDEFINES
     これを-Dオプションを含む文字列定数へと定義して、マシンとシステムを識別するための定義済みマクロを定義します。これらのマクロは、 -ansiオプションが指定されなければ、定義済みマクロとして有効となります。

     たとえば、Sunの上では以下のような値を使えます。

          "-Dmc68000 -Dsun -Dunix"

CPP_SPEC
     Cの文字列定数で、CPPに渡すオプションをGNU CCのドライバプログラムに伝えます。これによって、GNU CCに与えたオプションを、 GNU CCがCPPに渡す際にどのように翻訳するかを、指定することもできます。

     何もする必要がないときには、このマクロを定義しないでください。

extern int target_flags;
     この宣言が存在しなければなりません。

TARGET_...
     この一連のマクロによって、ターゲットマシンの様々なオプション的機能のイネーブルやディスエーブルを、コンパイラのコマンド引数から制御できるようにします。たとえば、ひとつのマシン記述を68000と68020の両方に使えるようにして、コマンド引数でもって、 68020だけにしかない命令をコンパイラが生成してよいか否かを指定します。このコマンド引数は、 target_flags中のあるビットをテストする、マクロTARGET_68020により実効します。

     それぞれのオプションに対して、 TARGET_FEATURENAMEを定義します。その定義は、たとえば次のような、 target_flagsのビットをテストするものでなければなりません。

          #define TARGET_68020 (target_flags & 1)

     このマクロが使われる場所は、たとえば、命令パターンの条件式の中です。 TARGET_68020が、 68000のマシン記述ファイルであるm68k.mdの中に頻繁に登場していることに気付いてください。それらが使われるもうひとつの場所は、 tm-MACHINE.hファイルの中での他のマクロ定義の中です。

TARGET_SWITCHES
     このマクロは、 target_flagsの中のビットをセットしたりクリアしたりするための、コマンドオプションの名前を定義します。それの定義は、各コマンドオプションの小グループから成るイニシャライザ (初期化用リスト)です。

     ひとつひとつの小グループに文字列定数があり、それがオプション名と、 target_flagsでセットするビットを指定する数値を定義します。負数は、クリアするビットを指定します。その負数をネゲートした値が、クリアするべきビットを指定します。実際のオプション名は、指定時に-mをアペンドしたものになります。

     ひとつの小グループがヌル文字列でなければなりません。この小グループに与える数値が、 target_flagsのデフォルト値となります。ターゲットオプションはすべて、初期値がこの値になります。以下は、 -m68000と-m68020をそれぞれ逆の意味として定義し、後者をデフォルト値として指定する例です。

          #define TARGET_SWITCHES \
             {{ "68020", 1 }, \
              { "68000", -1 }, \
              { "", 1 }}

OVERRIDE_OPTIONS
     ターゲットマシンによっては、コマンドオプションのある種の組み合せが意味を成さない場合があります。このことに対処するために、 OVERRIDE_OPTIONSマクロを定義します。このマクロは、定義されていると、すべてのコマンドオプションがパースされた後に一度だけ実行されます。

メモリ配置
=====

このテーブル内の、ビット数で表したアラインメントのサイズであるマクロの定義は、定数である必要はないという点に注意してください。それらは、 target_flagsのようなstatic変数を参照する、 Cの式でもよいのです。See Run-time Target。

BITS_BIG_ENDIAN
     1バイト中の最上位ビットのビット番号が、最も低い番号ならば、このマクロを定義します。このことは、ビットフィールド命令が最上位ビットからカウントを始めることを意味します。マシンにビットフィールド命令がなければ、このマクロは不要です。

BYTE_BIG_ENDIAN
     1ワードの最上位バイトが、最も低いメモリ番地[68000流儀]ならば、このマクロを定義します。

WORDS_BIG_ENDIAN
     複数ワードのオブジェクトで、最上位ワードが最も若いワード番号ならば、このマクロを定義します。

BITS_PER_UNIT
     メモリのアドレシング単位(バイト)のビット数です。通常は8です。

BITS_PER_WORD
     1ワードのビット数です。通常は32です。

UNITS_PER_WORD
     1ワードのメモリ単位数です。通常は4です。

POINTER_SIZE
     ポインタの幅をビット数で表したものです。

POINTER_BOUNDARY
     ポインタをメモリにストアするために必要なアラインメントを、ビット数で表したものです。

PARM_BOUNDARY
     スタック上の関数パラメータに必要なアラインメントを、ビット数で表したものです。

STACK_BOUNDARY
     スタックポインタに対する何らかのアラインメントを、常時保持したければ、このマクロを定義します。定義は、必要とするアラインメント(ビット数)を表すCの式です。

FUNCTION_BOUNDARY
     関数のエントリポイントに必要とされるアラインメントを、ビット数で表したものです。

BIGGEST_ALIGNMENT
     このマシン上で何らかのデータ型が必要とする最大のアラインメントを、ビット数で表したものです。

EMPTY_FIELD_BOUNDARY
     構造体の中のビットフィールドで、空のビットフィールドとなるものに与えるアラインメントの、ビット数です。たとえば、次のようなビットフィールドです。

          int : 0;

STRUCTURE_SIZE_BOUNDARY
     構造体や共用体のサイズが、これの倍数でなければならないという最少単位を、ビット数で表します。構造体や共用体のサイズは、これの倍数へと丸められます。

     このマクロを定義しなければ、デフォルトはBITS_PER_UNITと同じです。

STRICT_ALIGNMENT
     データが公称のアラインメント上になければ、命令がエラーに終わるという場合は、このマクロを定義します。命令の速度が遅くなるだけなら、このマクロは定義しません。

PCC_BITFIELD_TYPE_MATTERS
     PCC (Portable C Compiler)の、一部の妙な動作パターンを真似したい場合はこれを定義します。それは、 intとして宣言されたビットフィールドが、構造体のサイズとアラインメントに対して、実際のintと同じ効果を及ぼすという動作です。

     GNU CCの中でそれがどういう効果であるかは、他のパラメータにも依りますが、ほとんどのマシン上で、構造体のサイズとアラインメントは、 32ビットまたはBIGGEST_ALIGNMENTのビットの倍数となります。

CHECK_FLOAT_VALUE (MODE, VALUE)
     値VALUE (またはタイプdouble)を、モードMODEに関して検証するCの文です。つまり、値VALUEが、このターゲットマシン上で、モードMODEの可能値域内にあるかどうかをチェックします。 MODEは常に、 SFmodeまたはDFmodeです。

     VALUEが有効でなければ、 errorをコールしてエラーメッセージをプリントし、それから何か有効な値をVALUEに代入しなければなりません。無効な値がコンパイラにかけられると、 UNIXのアセンブラでもクラッシュするような、不正なアセンブラコードを作り出すことがありえます。

     そういうチェックが必要ない場合は、このマクロを定義する必要はありません。

レジスタの使い方
========

FIRST_PSEUDO_REGISTER
     コンピュータが知っているべきハードウェアレジスタの数、レジスタ番号は0からFIRST_PSEUDO_REGISTER-1までなので、最初の疑似レジスタ番号がFIRST_PSEUDO_REGISTER番となります。

FIXED_REGISTERS
     コンパイルされたコード全体に渡って、どのレジスタが固定的な目的に使われ、したがって汎用レジスタとしては使えないことを指定するイニシャライザ。これらには、スタックポインタ、フレームポインタ (フレームポインタが不必要なら汎用レジスタにして使えるマシンを除く)、プログラムカウンタがアドレス可能なレジスタのひとつであるマシン上のプログラムカウンタ、および標準的な用途のその他の番号付きレジスタなどが含まれます。

     この情報はコンマで区切った数値の並びとして表現し、全体をブレースで囲みます。レジスタNが固定用途なら、 N番目の数値は1、固定用途でないなら0とします。

     このマクロによって初期化されるテーブルと、次のマクロによって初期化されるテーブルは、ランタイムに自動的にまたはマクロCONDITIONAL_REGISTER_USAGEのアクション、またはユーザによるコマンドオプション-ffixed-reg、 -fcall-used-reg および-fcall-saved-regによってオーバライドされえます。

CALL_USED_REGISTERS
     FIXED_REGISTERSと同様ですが、関数コールによって(一般的に)内容が破壊されるレジスタも、固定用途レジスタと同じく1にします。このマクロはしたがって、複数の関数コールにわたって生き残らなければならない値を、アロケートしてはいけないレジスタを識別します。

     あるレジスタがCALL_USED_REGISTERSで値が0なら、コンパイラは自動的に、関数のエントリ時点でそれをセーブし、そのレジスタが関数内で使われているなら、リターン時点でリストアします。

CONDITIONAL_REGISTER_USAGE
     2つの変数、 fixed_regsとcall_used_regs (いずれも型はchar []) を、それらが前の2つのマクロによって初期化された後で、条件によって変更するための、ゼロ個以上のCの文です。

     これは、固定用途や関数コールによって破壊されるレジスタが、ターゲットのフラグに依存する場合に必要です。

     そういう場合以外は、このマクロを定義する必要はありません。

     一定種類のレジスタ全部の使い方がターゲットのフラグに依存するなら、このマクロを使い、 GCCが使ってはいけない種類のレジスタのそれぞれに関し fixed_regsとcall_used_regsを1に変えて、 GCCにそのことを指示できます。また、使ってはならないレジスタの種類が文字で区別できるなら、マクロREG_CLASS_FROM_LETTERを、 NO_REGSを返すよう定義します。

     (ただし、この種類のレジスタがGENERAL_REGSに含まれていなくて、制約でこの種類のレジスタの使用が許されている命令パターンのすべてが、ターゲットのスイッチによって制御されるなら、 GCCはターゲットのスイッチがそれらの使用不可を指示しているきには、自動的にそれらのレジスタの使用を避けます。)

OVERLAPPING_REGNO_P (REGNO)
     定義されているならばこれは、値がREGNOであるCの式が、 REGNO番のハードレジスタが重複レジスタなら非ゼロとなります。これは、違う番号のハードレジスタと重複するハードレジスタを意味します (このような重複は望ましくないことですが、それを許さないとサポートできないマシンもあるのです)。このマクロは互いに重複するすべてのレジスタに関して、非ゼロを返さなければなりません。 GNU CCが重複レジスタを使うのは、ごく限られたやり方しかありません。基本ブロック内でのアロケーションと、際ロード用の一時的利用、以上がすべてです。

     このマクロが定義されていないと、どのハードレジスタも互いに重複しないことを意味します。これが普通の状況です。

INSN_CLOBBERS_REGNO_P (INSN, REGNO)
     定義されているならば、これは命令INSNがREGNO番のハードレジスタの内容を、なぜか破壊してしまうという場合に値が非ゼロとなるCの式です。 ``なぜか''というのは、命令のRTL式にはそういう効果が記述されていないという意味です。

     このマクロが定義されていないと、どの命令もレジスタを知らぬ間に破壊することはないということを意味します。これが通常の状況であり、特別の条件がないかぎりは、 RTL式で命令のアクションをすべて記述するのがベストです。

PRESERVE_DEATH_INFO_REGNO_P (REGNO)
     定義されているならばこれは、アセンブラコードの出力時に、 REGNO番のハードレジスタに関して、厳密なREG_DEAD notesが必要とされるならば非ゼロとなるCの式です。そういう場合は、レジスタアロケーションの後で行なわれてdeath notesを検証する若干のオプティマイゼーションが、このレジスタが関与している場合には行われません。

     マシン記述中の、アセンブラコードを書き出すために実行される一部のコードが、death notesを見るときに、レジスタのdeath infoを保存するよう編成することもできます。これは、 GNU CCがレジスタだと思うハードウェアの実際の機能が、普通の種類のレジスタではないときにのみ、必要です (たとえばハードウェアスタックである場合)。

     このマクロが定義されていないと、death notesの保存が必要ないことを意味します。これが普通の状況です。

HARD_REGNO_REGS (REGNO, MODE)
     モードがMODEの値を保存するために必要なハードレジスタで、レジスタ番号REGNOから始まる連続的なレジスタの番号を表すCの式。

     すべてのレジスタが正確に1ワードであるマシン上では、このマクロの適切な定義は、次のようになります。

          #define HARD_REGNO_NREGS (REGNO, MODE)             \
             (( GET_MODE_SIZE( MODE ) + UNITS_PER_WORD - 1 ) \
              / UNITS_PER_WORD ) )

     [訳者注：原文のママ。]

HARD_REGNO_MODE_OK (REGNO, MODE)
     モードがMODEの値をREGNO番の (またはREGNO番から始まる複数の) ハードレジスタにストアすることが許されるならば非ゼロとなるCの式です。すべてのレジスタが同等であるマシンでは、適切な定義は次のようになります。

          #define HARD_REGNO_MODE_OK (REGNO, MODE) 1

     このマクロは、固定レジスタの番号をチェックする必要はありません。アロケーションの機構は、それらに常に値が乗っていると見なすからです。

     多くのマシンに、浮動小数点用の特殊なレジスタがあります。浮動小数点のマシンモードは、浮動小数点レジスタにのみ許されると想定する人が多いようです。しかし、それは真ではありません。整数を保持できるレジスタならばどれでも、そのレジスタ上で浮動小数点演算が行われるか否かに関わらず、浮動小数点のマシンモードを安全に保持できます。

     浮動小数点専用のレジスタの真の重要性は、浮動小数点のマシンモードがそれらのレジスタに乗ってはいけないというだけのことではありません。これは、浮動小数点レジスタが、それらにストアされる値を何で正規化するならば真です。非浮動小数点数は、それによって不正な値になってしまうからです。しかし、浮動小数点レジスタが自動的な正規化を行なわず、どのようなビットパターンでもそれに、変更されることなく、トラップがかかることもなくストアしたり、取り出したりできるならば、浮動小数点レジスタにどのマシンモードが乗ってもよいのであり、このマクロがそのことを指定します。

     浮動小数点レジスタはアクセス速度が特に遅いので、浮動小数点演算を行なわないときには、値をそういうレジスタにではなくスタックフレームにストアした方が良い場合があります。浮動小数点レジスタの種類がGENERAL_REGSでないならば、命令の制約が特に求めていないかぎりは、使われることはありません。

     汎用レジスタをソースやデスティネーションとする浮動小数点のmove命令をサポートすることは必須です。なぜなら、共用体や構造体 (そのモードがSImodeまたはDImodeの) がそれらのレジスタ上にあることがあり、そのメンバが浮動小数点数の場合もあるからです。

MODES_TIEABLE_P (MODE1, MODE2)
     モードがMODE1の値と、 MODE2の値との間でのmove命令を避けるようなレジスタアロケーションを選ぶことが望ましいときには、非ゼロとなるCの式。

     どのレジスタRに関しても、 HARD_REGNO_MODE_OK (R, MODE1)と HARD_REGNO_MODE_OK (R, MODE2)が異なるならば、 MODES_TIEABLE_P (MODE1, MODE2)はゼロでなければなりません。 [訳者注：原文のママ。]

PC_REGNUM
     プログラムカウンタにレジスタ番号があるならば、これをそのレジスタ番号として定義します。そうでなければ定義しません。

STACK_POINTER_REGNUM
     スタックポインタレジスタのレジスタ番号。これはFIXED_REGISTERSで固定用途レジスタに指定されていなければなりません。多くのマシン上で、どのレジスタがスタックポインタレジスタかは、ハードウェアが決めています。

FRAME_POINTER_REGNUM
     フレームポインタレジスタのレジスタ番号。それは、スタックフレーム内のauto変数にアクセスするために使われるレジスタです。一部のマシンでは、それはハードウェアが決めています。その他のマシン上では、どのレジスタでもこの目的に指定できます。

FRAME_POINTER_REQUIRED
     関数がフレームポインタを使わなければならないなら非ゼロとなる、 Cの式。この式が再ロードパスで関数reloadの中で評価され、原則的現在の関数を調べて事実に従った決定をしますが、ほとんどのマシンでは、定数0または定数1で十分です。フレームポインタなしのコード生成を許すマシンなら0を使えば、時間とスペースが少し節約されます。フレームポインタの使用を避けることに、特に利点がないならば、 1を使います。

     フレームポインタを使えないと、コンパイラが途方に暮れる場合もあります。コンパイラはそういうケースを認識して、 FRAME_POINTER_REQUIREDの指定の如何に関わらず自動的に関数にフレームポインタを与えます。これは、ユーザが配慮する必要はありません。

     フレームポインタを必要としない関数の中では、フレームポインタレジスタを、それを固定用途レジスタに指定していなければ、普通の用途にアロケートできます。

ARG_POINTER_REGNUM
     引数ポインタレジスタのレジスタ番号。それは、関数の引数リストにアクセスするために使われるレジスタです。一部のマシンでは、それはフレームポインタレジスタと同じです。ハードウェアがそれを決めているマシンもあります。その他のマシンでは、それ用にどのレジスタでも使えます。それがフレームポインタレジスタと同じでないならば、それをFIXED_REGISTERSで固定用途レジスタに指定していなければなりません。

STATIC_CHAIN_REGNUM
     関数のスタティックチェーンポインタを渡すために使われるレジスタのレジスタ番号。これは、関数の中で定義された関数が、自分の外側の関数のローカル変数にアクセスできる、 PascalやAlgolのような言語に必要です。 Cにはこの機能がないので、現在は使われていませんが、マクロは定義しなければなりません。

     スタティックチェーンレジスタは、固定用途レジスタである必要はありません。

STRUCT_VALUE_REGNUM
     関数の値のモードがBLKmodeであるときは、値はFUNCTION_VALUEに基づいては返されません。代わりに、コール側がメモリブロックのアドレスを渡して、そこに値がストアされます。

     この値(アドレス値)がレジスタで渡されるなら、 STRUCT_VALUE_REGNUMをそのレジスタの番号とします。

STRUCT_VALUE
     構造体の値を収めるアドレスがレジスタで渡されないなら、 STRUCT_VALUEを、アドレスが渡される場所を表すRTXを返す式として定義します。これがmem RTXを返すなら、アドレスは``目に見えない''第一引数(隠れ引数)よして渡されます。

STRUCT_VALUE_INCOMING_REGNUM
     一部のアーキテクチャ上では、構造体の値のアドレスをコール側がストアする場合と、コールされた側がそれを見出す場所とが、同じでないことがあります。これはレジスタウィンドウのせいであったり、あるいは関数のプロローグがそれを別な場所に移すためです。コールされる側にとっての構造体の値のアドレスがレジスタにあるなら、そのマクロをそのレジスタ番号として定義します。

STRUCT_VALUE_INCOMING
     コールされる側にとっての構造体の値のアドレスが、レジスタにないなら、 STRUCT_VALUE_INCOMINGを、コールされる側がそこに値を見出すようなRTX用の式として定義します。値をスタック上に見出すべきならば、これをフレームポインタを参照するmem式を作るよう定義します。その値がmemなら、コンパイラはそれを不可視の第一引数用であると想定し、最初の実引数を見つけるときに、それ用のスペースを残します。

REG_ALLOC_ORDER
     定義されているとこれは、整数のベクタ用のイニシャライザであり、それにはハードレジスタの番号が、 GNU CCが優先的に使用すべき順番に入ります (使用が最も望ましいものから、そうでないものへという順序で)。

     このマクロが定義されていないと、レジスタは低い番号のものから使われていきます (ほかの条件が同等なら)。

     このマクロは、たとえば、 360のように最も高い番号のレジスタ(複数)を常にセーブしなければならず、複数レジスタをセーブする命令が連続したレジスタしかサポートしていない、という番号に使います。このマクロを、アロケート可能なレジスタの内、番号の高い方から使っていくように定義するのです。

レジスタクラス(レジスタの種類)
================

多くのマシン上で、番号付きレジスタのすべてが同等ではありません。たとえば、一部のレジスタはインデクス付きアドレシングには使えず、一部のレジスタは一部の命令の中では使えません。このようなマシンの制約を、レジスタクラス (レジスタの種類) を使ってコンパイラに対し記述します。

複数のレジスタクラスを定義し、そのそれぞれに名前を与え、どのレジスタがそれに属するかを指定します。そして特定の命令のオペランドとして許されるレジスタクラスを指定できます。

一般的には、ひとつのレジスタが複数のクラスに属します。事実、必ずALL_REGSという名のクラスがあって、それにすべてのレジスタが属さなければなりません。また、どのレジスタも属さないNO_REGSという名のクラスもなければなりません。 2つのクラスの接合が別のクラスになることがしばしばありますが、その場合特に新たなクラスを設ける必要はありません。

クラスの内のひとつはGENERAL_REGSでなければなりません。この名前には、特別なものは何もありませんが、オペランドの制約文字rとgがこのクラスを指定します。 GENERAL_REGSがALL_REGSと同じなら、それを単にALL_REGSに展開されるマクロとして定義します。

GENERAL_REGS以外のクラスがオペランドの制約の中で指定されるやり方は、マシン依存のオペランド制約文字を介するものです。各種のクラスに対応する制約文字を定義して、それらをオペランドの制約の中で使います。

一部の命令が2つのクラスの使用を許しているなら、その2つのクラスの接合をひとつのクラスとして定義すべきです。たとえば、ある命令が浮動小数点レジスタ (コプロセッサのレジスタ) または汎用レジスタのどちらを、あるオペランドとして使用を許しているなら、それらの両方を含むFLOAT_OR_GENERAL_REGSクラスを定義すべきです。そうしないと、最適化されていないコードが得られます。

レジスタクラスに関しては、各クラスに対して、それがどのクラスに属するか、各一対のクラスに対しては、両者の接合の中での大きい方のクラスはどちらか、などといったある種の冗長な情報も指定しなければなりません。

enum reg_class
     すべてのレジスタクラスの名前を列挙値として定義した、列挙型です。 NO_REGSが最初で、 ALL_REGSが最後、そしてその後に、ひとつ以上の列挙値LIM_REG_CLASSESというものを書きます。これはレジスタクラスではなくて、レジスタクラスが何種類あるかを示すものです。

     各レジスタクラスが番号を持ちますが、それはクラス名の値をintにキャストしたものです。この番号が、下に説明する多くのテーブルのインデクスの役を果たします。

N_REG_CLASSES
     レジスタクラスの実際の個数で、以下のように定義されます。

          #define N_REG_CLASSES (int)LIM_REG_CLASSES

REG_CLASS_NAMES
     レジスタクラスの名前がCの文字列として入っている、イニシャライザ。これらの名前は、一部のデバッグ用ダンプの出力時に使われます。

REG_CLASS_CONTENTS
     レジスタクラスの内容を収めたイニシャライザです。内容とは、ビットマスクとして機能する整数です。 N番目の整数が、 N番目のクラスの内容を指定します。この整数マスクMASKの解釈のされ方は、 R番目のレジスタは、 MASK & (1 << R)が1ならばそのクラスに属します。

     マシンのレジスタ数が32より多いと、整数(int)では間に合いません。そのときは整数の代わりにサブイニシャライザを使い、複数の整数をブレースで囲んだものを使います。それぞれのサブイニシャライザが、 hard-reg-set.hで定義されている HARD_REG_SET型用のイニシャライザとして、適切なものでなければなりません。

REGNO_REG_CLASS (REGNO)
     REGNO番のハードレジスタが属するレジスタクラスを値とするCの式。一般的には、そういうクラスは複数あるので、値が最少であるクラスを選びます。それより値が小さいクラスには、そのレジスタが属していないという意味になります。

BASE_REG_CLASS
     有効なベースレジスタが属するクラスの名前を定義するマクロ。ベースレジスタは、そのレジスタの値にディスプレースメントを足した値でアドレシングする、アドレシング方式で使われるレジスタです。

INDEX_REG_CLASS
     有効なインデクスレジスタが属するクラスの名前を定義するマクロ。インデクスレジスタは、そのレジスタの値にスケールファクタを掛けた値や、他のレジスタの値を足した値 (さらにディスプレースメントも足す) でアドレシングするアドレシング方式で使われるレジスタです。

REG_CLASS_FROM_LETTER (CHAR)
     レジスタクラスに関するマシン依存のオペランド制約文字を定義するCの式。 CHARがそういう目的の文字ならば、その式の値は、それに対応するレジスタクラスでなければなりません。そうでなければ、値はNO_REGSでなければなりません。

REGNO_OK_FOR_BASE_P (NUM)
     NUM番のレジスタが、オペランドのアドレスの中でベースレジスタとして使えるならば非ゼロとなるCの式。レジスタは、ハードレジスタまたは、そういうハードレジスタをアロケートされる疑似レジスタ。

REGNO_OK_FOR_INDEX_P (NUM)
     NUM番のレジスタが、オペランドのアドレスの中でインデクスレジスタとして使えるならば非ゼロとなるCの式。レジスタは、ハードレジスタまたは、そういうハードレジスタをアロケートされる疑似レジスタ。

     インデクスレジスタとベースレジスタの違いは、インデクスレジスタにはスケールファクタが掛けられることがある点です。ひとつのアドレスに2つのレジスタの和が含まれ、そのどちらもスケーリングされていないなら、どちらを``ベース''ないし``インデクス''とラベル付けしても構いません。しかし、どうラベル付けするにしても、それはレジスタの能力に関するマシンの制約にフィットするものでなければなりません。コンパイラは両方のラベル付けを試みて、有効な方を探し、どちらのラベル付けも有効でないときだけ、ひとつまたは両方のレジスタを再ロードします。

PREFERRED_RELOAD_CLASS (X, CLASS)
     値XをクラスがCLASSのレジスタにコピーすることが必要なときに使うレジスタクラスに、追加的な制約を課すためのCの式。式の値はレジスタクラスで、それはCLASSか、またはそれより小さいクラス。 CLASSは式の値として常に安全。事実、以下のような定義は常に安全です。

          #define PREFERRED_RELOAD_CLASS (X, CLASS) CLASS

     しかし、ときには、もっと制約のあるクラスを返した方がより良いコードを作る場合があります。たとえば、68000ではXがmoveq命令の範囲にある整数定数のとき、このマクロの値は、 CLASSがデータレジスタを含むかぎりは常にDATA_REGSです。データレジスタを要求することにより、 moveqが使われることが保証されます。

     Xがconst_doubleなら、 NO_REGSを返してXを強制的にメモリ定数にできます。これは、ある種のレジスタには浮動小数点数の即値をロードできないというタイプのマシンでは有用です。

CLASS_MAX_NREGS (CLASS, MODE)
     モードがMODEの値を保持するために必要な、クラスがCLASSの連続するレジスタの最大数を表すCの式。

     これは、マクロHARD_REGNO_NREGSと密接に関連しています。事実、マクロCLASS_MAX_NREGS (CLASS, MODE)の値は、クラスCLASSの中のすべてのREGNOの値に関して、 HARD_REGNO_NREGS (REGNO, MODE)の最大値でなければなりません。

     このマクロは、再ロードパスにおける、複数ワードの値の取り扱いの制御を支援します。

他に、2つの特殊なマクロが、定数と定数文字の関係を記述します。

CONST_OK_FOR_LETTER_P (VALUE, C)
     整数の値の一定の値域を指定する、マシン依存のオペランド制約文字を定義するCの式。 Cがそういう目的の文字ならば式は、整数であるVALUEが値域内にあることをチェックして、値域内であれば1を、そうでなければ0を返します。 Cがそういう文字でなければ、式の値はVALUEとは無関係に0でなければなりません。

CONST_DOUBLE_OK_FOR_LETTER_P (VALUE, C)
     浮動小数点数の値の一定の値域を指定する、マシン依存のオペランド制約文字を定義するCの式。 Cがそういう目的の文字ならば、式はコードconst_doubleのRTXであるVALUEが値域内にあることをチェックして、値域内であれば1を、そうでなければ0を返します。 Cがそういう文字でなければ、式の値はVALUEとは無関係に0でなければなりません。

スタック配置の記述
=========

STACK_GROWS_DOWNWARD
     スタックに1ワードプッシュすると、スタックポインタがより小さなアドレスへ移動するならば、このマクロを定義します。

     ``〜ならば、このマクロを定義します''と本書で言う場合それは、コンパイラがもっぱら#ifdef文でのみこのマクロをチェックするので、定義の実際の内容は無関係であるという意味です [定義されているか・いないかだけが有意]。

FRAME_GROWS_DOWNWARD
     ローカル変数のスロットのアドレスが、フレームポインタからの負のオフセットにあるならば、このマクロを定義します。

STARTING_FRAME_OFFSET
     最初にローカル変数用スロットとしてアロケートされる場所の、フレームポインタからのオフセット。

     FRAME_GROWS_DOWNWARDが定義されていれば、次のスロットのオフセットは、最初のスロットの長さをSTARTING_FRAME_OFFSETから引いたところである。そうでなければそれは、最初のスロットの長さをSTARTING_FRAME_OFFSETの値に足したところとなる。

PUSH_ROUNDING (NPUSHED)
     命令がNPUSHEDバイトをプッシュするとき、実際にスタック上にプッシュされるバイト数を表すCの式。

     ターゲットマシンにプッシュ命令がないときは、このマクロは定義しません。そうするとGNU CCに、別の戦略を採らせることになります。つまり、引数ブロックの全体をアロケートして、そこに引数をストアします。

     一部のマシンでは、次の定義で十分です。

          #define PUSH_ROUNDING (BYTES) BYTES

     しかし、その他のマシンでは、 1バイトをプッシュしている命令が、アラインメントを維持するために実際は2バイトをプッシュします。そのときは定義は次のようにします。

          #define PUSH_ROUNDING (BYTES) (((BYTES) + 1) & ~1)

FIRST_PARM_OFFSET
     引数ポインタレジスタから、最初の引数のアドレスまでのオフセットです。

RETURN_POP_ARGS (FUNTYPE)
     関数がリターン時に自分の引数をポップするならば1となり、関数は引数をまったくポップせず、関数のリターン時にコール側がすべてポップしなければならないならば0となるCの式。

     FUNTYPEは、当の関数を表している木のノードを値とするCの変数。通常それは、関数のデータ型を記述しているFUNCTION_TYPE型のノードです。これから、値のデータ型と、 (分かっているならば)引数を得ることができます。

     ライブラリ関数へのコールについて知るときは、 FUNTYPEにはライブラリ関数用のノードの識別子が入ります。したがって様々なライブラリ関数の中での区別は名前で行なえます。この文脈での``ライブラリ関数''とは、算術演算の実行に使われる関数であり、その名前はコンパイラだけが知り、コンパイルされるCのコード中には名前が出てこないものである点に注意してください。

     VAX上では、すべての関数が自分の引数をポップするので、このマクロの定義は1です。 68000の上で標準的なコール貫行を用いると、どの関数も自分の引数をポップしないので、マクロの値はこの場合常に0です。しかし、常に一定数の引数をとる関数は自分でポップし、そうでない関数 (printfのような) は何もポップしない (コール側がすべてポップする)、という別の貫行を使えます。この貫行が用いられているときは、関数が一定数の引数をとるかどうかを判断するためにFUNTYPEが調べられます。

FUNCTION_VALUE (VALTYPE, FUNC)
     VALTYPEというデータ型の値を関数が返す場所を表すRTXを、生成するCの式。 VALTYPEはデータ型を表す木のノードです。そのデータ型の表現に用いられるマシンモードは、 TYPE_MODE (VALTYPE)と書いて取得します。多くのマシン上で、そのモードだけが適切です (実際には、ほとんどのマシン上で、スカラー値はマシンモードが何であれ同じ場所に返されます)。

     コールされる関数が具体的に分かっているならば、 FUNCはそれを表す木のノード(FUNCTION_DECL)です。分かっていなければFUNCはヌルポインタです。これによって、コールのされ方がすべて分かっている関数については、帰値を返すときの方式を変えることが可能です。

FUNCTION_OUTGOING_VALUE (VALTYPE, FUNC)
     ターゲットマシンに``レジスタウィンドウ''があって、関数が値を返すレジスタが、コール側がその値を見い出すレジスタと同じでないならば、このマクロを定義します。このようなマシンに関しては、 FUNCTION_VALUEが、コール側が値を見出すレジスタを決めますが、 FUNCTION_OUTGOING_VALUEは関数(被コール側)に値を置く場所を教えるために、同じく定義されなければなりません。

     FUNCTION_OUTGOING_VALUEが定義されていないと、 FUNCTION_VALUEが両方の役目を果たします。

LIBCALL_VALUE (MODE)
     ライブラリ関数がモードMODEの値を返す場所を表すRTXを作るCの式。コールされる関数が具体的に分かっているならば、 FUNCはそれの木ノード(FUNCTION_DECL)。分かっていなければFUNCはヌルポインタです。これによって、コールのされ方がすべて分かっている関数については、帰値を返すときの方式を変えることが可能です。

     この場合の``ライブラリ関数''とは、算術演算の実行に使われる関数であり、その名前はコンパイラだけが知り、コンパイルされるCのコード中には名前が出て来ないものである点に注意してください。

FUNCTION_VALUE_REGNO_P (REGNO)
     REGNOが、関数の帰値が乗るハードレジスタの番号であるならば非ゼロとなるCの式。

     そのレジスタの、値を返すための用途が、 (たとえばdouble型の値の場合のように) ペアとなって使われるレジスタの後ろ半分用に限定されているならば、このマクロが認識される必要はありません。そこで、ほとんどのマシンに関して、以下の定義で十分です。

          #define FUNCTION_VALUE_REGNO_P (N) ((N) == 0)

     マシンにレジスタウィンドウがあって、コール側とコールされた側の関数が帰値に関して違うレジスタを用いるなら、このマクロはコール側のレジスタ番号だけを認識するものでなければなりません。

FUNCTION_ARG (CUM, MODE, TYPE, NAMED)
     関数の引数がレジスタで渡されるのか、それはどのレジスタかを制御するCの式。

     引数はCUMで、その引数の前までのすべての引数を要約します。 MODEは引数のマシンモードです。 TYPEは引数のデータ型を表す木のノード、分かっていなければ0 (それはCのサポートライブラリ関数の場合) です。 NAMEDは、普通の引数に関しては1、コールされる関数のプロトタイプで...と書き表されている無名引数用には0です。

     式の値は、引数がハードレジスタで渡されるならば、そのreg RTX、引数をスタックで渡すならばゼロです。

     VAXや68000では、通常は引数はすべてプッシュされるので、定義としてゼロで十分です。

FUNCTION_INCOMING_ARG (CUM, MODE, TYPE, NAMED)
     ターゲットマシンに``レジスタウィンドウ''があって、関数が引数を見い出すレジスタが、必ずしもコール側がその引数を渡したレジスタとは同一でないならば、このマクロを定義します。

     このようなマシンに関しては、 FUNCTION_ARGが、コール側が値を渡すレジスタを決め、そして同様にしてコールされる側の関数に引数がどこに到着するかを知らせるために、 FUNCTION_INCOMING_ARGが定義されなければなりません。

     FUNCTION_INCOMING_ARGが定義されていないと、 FUNCTION_ARGが両方の役目を果たします。

FUNCTION_ARG_PARTIAL_NREGS (CUM, MODE, TYPE, NAMED)
     引数の冒頭部分をレジスタで渡す場合の、その部分のワード数を表すCの式。引数の全体がレジスタまたはスタックで渡される場合は、式の値はゼロでなければなりせん。

     一部のマシンでは、ある種の引数が、一部はレジスタで、残り部分はメモリで渡されます。こういうマシン上では、引数の最初のNワードがレジスタで渡され、残りがスタックで渡されるのが普通です。一定のサイズを超えた複数ワードの引数(doubleや構造体)は、最初の数ワードがレジスタで渡され、残りはプッシュしなければなりません。このマクロはコンパイラに、どういうときにそれが起き、何ワードがレジスタへ行くのかを教えます。

     これらの引数に関するFUNCTION_ARGは、この引数用にコール側が使う最初のレジスタを返すものでなければなりません。同様にFUNCTION_INCOMING_ARGは、コールされる関数に関して、レジスタの情報を返します。

CUMULATIVE_ARGS
     FUNCTION_ARG、およびその他の関連マクロの最初の引数として使われる変数を宣言するCのデータ型。一部のターゲットマシンに関しては、 int型で十分であり、複数バイトの引数を当面これに収めることができます。

INIT_CUMULATIVE_ARGS (CUM, FNTYPE)
     引数CUMのステートを、引数リストの冒頭で初期化するためのCの文(セミコロンなし)。この変数のデータ型はCUMULATIVE_ARGSです。 FNTYPEは、その引数を受け取る関数のデータ型を表す木のノード、または、その引数がコンパイラのサポートライブラリの関数に渡されるのなら0です。

FUNCTION_ARG_ADVANCE (CUM, MODE, TYPE, NAMED)
     要約引数CUMを、変数リスト中のひとつの引数を越えて前進することによって更新します。 MODE、TYPE、NAMEDの値が、その引数を示します。これを行なうと、変数CUMは、その次の引数をFUNCTION_ARGなどで分析するのに適したものとなります。

FUNCTION_ARG_REGNO_P (REGNO)
     REGNO番のハードレジスタが、関数の引数渡しにときどき使われるならば、非ゼロとなるCの式。これは、 staticのチェーンや、構造体の値のアドレスなど、暗黙の引数はすべてスタックにプッシュされるので、レジスタをこの目的に使うことはありません。

FUNCTION_ARG_PADDING (MODE, SIZE)
     定義されているならばこれは、引数をスペースでパディング(詰め物)するのか、するとしたらどちら方向にするのかを決めるためのCの式。式の値のデータ型はenum directionで、値は引数の上へパディングするならupward、下へならdownward、パディングを禁ずるならnoneです。

     引数SIZEは、引数のサイズをバイト数で表すRTXです。これは、 MODEがBLKmodeのときだけ使います。その他の場合は、 sizeは0です。

     このマクロはパディングの量は制御しません。それはつねに、 PARM_BOUNDARYの至近の倍数になれば十分です。

     このマクロには、ほとんどのシステムにとって正しいデフォルトの定義があります。後退法(little-endian)のマシン用には、デフォルトは上(upward)にパディングです。前進法(big-endian)のマシン用には、 intよりも小さい一定サイズの引数は下(downward)にパディング、その他は上にです。

FUNCTION_PROLOGUE (FILE, SIZE)
     関数のエントリ部分のアセンブラコードを出力するCの複文。このプロローグ部分の役目は、スタックフレームのセットアップ、フレームポインタレジスタの初期化、セーブすべきレジスタのセーブ、および、ローカル変数用にSIZEバイトのアロケーションです。 SIZEは整数(integer)です。 FILEはアセンブラコードの出力先となるstdioのストリームです。

     関数の開始を示すラベルは、このマクロが出力する必要はありません。それは、このマクロの開始時にはすでに行なわれています。

     セーブすべきレジスタの判断は、マクロが配列regs_ever_liveを参照して行ないます。関数の中のどこかでハードレジスタRが使われるなら、配列の成分Rが非ゼロです。ということは、関数のプロローグはレジスタRをセーブすべきであるが、それがコールに使われるレジスタのひとつであるならセーブしなくてよいのです。

     関数がフレームポインタを使える・使えないというマシンの違いによって、関数のエントリのコードも違います。フレームポインタが求められているならセットアップし、そうでなければセットアップしません。その違いを判断するために、マクロは変数frame_pointer_neededを参照できます。この変数の値は、フレームポインタを必要とする関数の中ではランタイムに1です。

FUNCTION_PROFILER (FILE, LABELNO)
     プロファイリングサブルーチンmcountをコールするためのアセンブラコードを、 FILEにしゅつりょくするためのCの単文または複文。コールの前に、そのアセンブラコードは、カウンタ変数のアドレスを、 mcountがそこからアドレスを得るはずになっているレジスタにロードしなければなりません。この変数の名前は、 LPの後に番号LABELNOの付いたものなので、 fprintfでLP%dと書けば生成できます。

     mcountへのアドレスの渡し方の詳細は、 GNU CCによってではなく、オペレーティングシステムの環境で決まります。それを知るためには、プロファイリングをする小さなプログラムを、そのシステムにインストールされているCコンパイラを使ってコンパイルし、その結果のアセンブラコードを見てください。

EXIT_IGNORES_STACK
     このマクロは、リターン命令または関数のエピローグが、スタックポインタの値を無視するならば非ゼロとなるCの式として定義します。言い換えると、関数からのリターンの前にスタックポインタを調整する命令を削除しても安全ならばという意味です。

     このマクロの値は、フレームポインタを用いる関数の場合にのみ有意である点に注意してください。フレームポインタのない関数内では、最後のスタック調整を削除することは絶対に不可能であり、コンパイラにはそのことが、 EXIT_IGNORES_STACKの値が何であれ分かります。

FUNCTION_EPILOGUE (FILE, SIZE)
     関数から出口部分(関数の終了部分)のアセンブラコードを出力するCの複文。そのエピローグの役目は、セーブしたレジスタとスタックポインタをコール時の値にリストアして、制御をコール側へ返すことです。このマクロはマクロFUNCTION_PROLOGUEと同じ引数をとり、リストアするレジスタも同じように regs_ever_liveとCALL_USED_REGISTERSから判断されます。

     一部のマシンには、関数からのリターン時のすべての仕事を、一命令で行なえる命令があります。このようなマシンでは、その命令にreturnの名を与え、そしてマクロFUNCTION_EPILOGUEはまったく定義してはいけません。

     フレームポインタの有無によって、マシンの関数終了コードも違います。フレームポインタのあるなしで、コードが完全に違うこともあります。フレームポインタが求められているかどうかを知るために、マクロは変数frame_pointer_neededを参照できます。この変数の値は、フレームポインタを要する関数内ではランタイムに1です。

     一部のマシンでは、一部の関数が自分の引数を終了時にポップし、他の関数はコール側にポップさせます。たとえば、 -mrtdを指定されたときの68020は、引数の数が決まっている関数内では引数をポップします。

     RETURN_POPS_ARGSをどう定義したかによって、どの関数が自分の引数をポップするかが決まります。 FUNCTION_EPILOGUEは、その決定を知る必要があります。関数が自分の引数をポップすべきであるなら、変数current_function_pops_argsが非ゼロです。そのとき、変数current_function_args_sizeを、ポップするバイト数として使用します。

FIX_FRAME_POINTER_ADDRESS (ADDR, DEPTH)
     フレームポインタレジスタを使用するメモリアドレスを、スタックポインタレジスタを使用するように変えるためのCの複文。これは、パラメータの値をレジスタにロードする命令中で、再ロードパスがその機能のためにはフレームポインタは不要と判断したときに行なわれなければなりません。 ADDRはCの変数名であり、更新されたアドレスがその変数にストアされなければなりません。 DEPTHは、スタック上の一時変数の現在の深さ (現在プッシュされている引数のバイト数)です。フレームポインタ相対アドレスと、スタックポインタ相対アドレスとの間のオフセットには、 DEPTHが含まれていなければなりません。

     マシン記述の仕様では、フレームポインタレジスタには常にフレームポインタがあるとなっていても、やはりFIX_FRAME_POINTER_ADDRESSは定義しなければなりませんが、その定義はランタイムに一度も実行されないので空で構いません。

ライブラリサブルーチンの名前
==============

UDIVSI3_LIBCALL
     フルワードをフルワードで除算する場合にコールする関数の名前を表す、 Cの文字列定数。このマクロを定義しないと、 gnulibに定義されている関数である_udivsi3の名が使われます。

UMODSI3_LIBCALL
     フルワードをフルワードで除算した場合の余りを求めるためにコールする関数の名前を表す、 Cの文字列定数。このマクロを定義しないと、 gnulibに定義されている関数である_umodsi3の名が使われます。

TARGET_MEM_FUNCTION
     GNU CCが、 BSDのbcopyおよびbzero関数ではなく、 System V (とANSI C) のライブラリ関数memcpyとmemsetへのコールを生成すべきであるなら、このマクロを定義します。

アドレシングモード
=========

HAVE_POST_INCREMENT
     マシンがポストインクリメントのアドレシングをサポートしているなら、このマクロを定義します。

HAVE_PRE_INCREMENT
HAVE_POST_DECREMENT
HAVE_PRE_DECREMENT
     その他の(これら三種の)アドレシングに関して上と同様です。

CONSTANT_ADDRESS_P (X)
     XというRTXが整数を値とする定数ならば1となるCの式。これには、 symbol_refやlabel_ref式、 const算術演算式など、値がズバリ具体的ではない整数も含まれます。

     ほとんどのマシンで、これはCONSTANT_P (X)と定義できますが、一部のマシンでは、定数アドレスのサポートに関してさらに制約があります。

MAX_REGS_PER_ADDRESS
     有効なメモリアドレス中に現れてもよいレジスタの最大数。

GO_IF_LEGITIMATE_ADDRESS (MODE, X, LABEL)
     条件分岐によるgoto LABEL;という行のあるCの複文で、それはXというRTXがターゲットマシン上のモードがMODEのメモリオペランドの、合法なメモリアドレスならば実行されるもの。

     このためのサブルーチンとしては、複数の小さなマクロを定義する方がよいでしょう。そうしないと、複雑すぎて理解できないものとなります。

     このマクロは、厳密版と非厳密版という二種類が存在しなければなりません。厳密版は再ロードパスで使われます。それは、ハードレジスタをアロケートされなかった疑似レジスタはどれも、メモリ参照と見なすよう定義されなければなりません。何らかのレジスタが要請されるコンテキストにおいては、ハードレジスタをアロケートされていない疑似レジスタは、拒絶されなければなりません。

     非厳密版は、その他のパスで使われます。それは、レジスタが要請されるコンテキスト中でも、すべての疑似レジスタを受け入れるよう、定義されなければなりません。

     コンパイラのソースファイルの内、このマクロの厳密版を使いたいものは、マクロREG_OK_STRICTを定義します。厳密版と非厳密版の区別は、 #ifdef REG_OK_STRICTで指定します。

     GO_IF_LEGITIMATE_ADDRESSの定義を構成するサブルーチンの中には普通は、レジスタの用途合法性をチェックするサブルーチンがあります (ベースレジスタとして使えるか、インデクスレジスタとして使えるか、等々)。その場合、2つの版を要するのはこれらのサブルーチンマクロだけであり、それより高いレベルのマクロは、厳密版でも非厳密版でも同じです。

REG_OK_FOR_BASE_P (X)
     Xというreg RTXが、ベースレジスタとして用いてよいものならば非ゼロとなるCの式。ハードレジスタに関しては、それはハードウェアのレジスタに対する仕様でもって常に一義的に決まります (ベースレジスタとして使えるレジスタはハードウェアによって決まっている)。疑似レジスタに対する判断は、上記のREG_OK_STRICTで制御されます。このマクロは通常は二種類の版を要し、その使用をREG_OK_STRICTが制御します。

REG_OK_FOR_INDEX_P (X)
     Xというreg RTXが、インデクスレジスタとして用いてよいものならば非ゼロとなるCの式。

     インデクスレジスタとベースレジスタの違いは、インデクスレジスタはスケーリングできるという点です。アドレスに2つのレジスタの和が含まれていて、そのどちらもスケーリングされていなければ、どちらをベースないしインデクスと見なしても構いませんが、どちらのラベル付けをするにしてもそれは、レジスタ使用に関するマシンの制約に合致しているものでなければなりません。コンパイラは両方のラベリングを試み、片方または両方のレジスタを再ロードします。

LEGITIMATE_ADDRESS (X, OLDX, MODE, WIN)
     Xを、モードがMODEのオペランドの有効なメモリアドレスで置換することを試みるCの複文。 WINは、コード中のどこかにあるCの文ラベルで、このマクロ定義は以下を使って、

          GO_IF_LEGITIMATE_ADDRESS (mode, x, win)

     アドレスが合法となっていた場合のそれ以後の処理を避けます。

     Xは常に、 break_out_memory_refsのコールの結果であり、 oldxはその関数にXを作り出すために与えたオペランドです。

     このマクロから生成されるコードは、 Xの部分構造を変えてはいけません。それがXをより合法的な形式に変えるなら、それはX (つねにCの変数) に新たな値を代入しなければなりません。

     このマクロは、必ずしも合法的なアドレスを作り出せなくても構いません。コンパイラは、あらゆるケースに対応するための、標準的な方法(複数)を備えています。事実、このマクロが何もしなくても安全です。しかし、マシン依存の戦略がベターなコードを生むことが多いとは言えます。

GO_IF_MODE_DEPENDENT_ADDRESS (ADDR, LABEL)
     条件分岐によるgoto LABEL;のあるCの単文または複文で、メモリアドレスX (というRTX)が、それがメモリ参照に使われるときのマシンモードによっては、違う意味を持つこともあるならば実行されるもの。 [訳注：原文のママ]

     オートインクリメントとオートデクリメントのアドレスは、モード依存の効果を持つアドレスの典型です。なぜなら、インクリメントないしデクリメントの量が、アドレスされるオペランドのサイズだからです。その他の、モード依存のアドレスのあるマシンも一部にあります。 RISCマシンは、モード依存のアドレスのないものが多いようです。

     ADDRは、そのマシンの有効なアドレスです。

LEGITIMATE_CONSTANT (X)
     Xがターゲットマシン上の即値オペランドとして合法的な定数ならば非ゼロとなるCの式。 Xは、 const_doubleであるかまたは、 CONSTANT_Pを満足するものであり、それをチェックする必要はありません。実際には、 const_doubleが有効で、どのCONSTANT_Pも有効なマシン上では、このマクロの定義として1が適切です。

各種のパラメータ
========

CASE_VECTOR_MODE
     マシンモード名のエイリアス。これは、ジャンプテーブルの成分が持つべきマシンモードです。

CASE_VECTOR_PC_RELATIVE
     ジャンプテーブルの内容が相対アドレスであるべきなら、このマクロを定義します。

CASE_DROP_THROUGH
     インデクスの値が範囲外のときに、制御がひとつのcase insnを通り過ぎてしまうならば、これを定義します。これは、指定のdefaultラベルが、実際にはcase insn本体によって無視されることを意味します。

IMPLICIT_FIX_EXPR
     浮動小数点数の値を固定小数点に変換するために、デフォルトでつかわれるべきツリーコードのエイリアス。通常は、 FIX_ROUND_EXPRが使われます。

FIXUNS_TRUNC_LIKE_FIX_TRUNC
     浮動小数点数を有符号固定小数点数に変換する命令と同じ命令が、無符号の固定小数点数にも有効に変換するならば、このマクロを定義します。

EASY_DIV_EXPR
     一般的なケースのコードをコンパイルする際の、最も容易な種類の除算であるツリーコードのエイリアス。それは、 TRUNC_DIV_EXPR、 FLOOR_DIV_EXPR、 CEIL_DIV_EXPR、またはROUND_DIV_EXPRでしょう。これら4つの除算演算子は、結果を整数に丸めるときの方式が違います。これらのどれを使ってもよく、効率をベースに選べばよいときは、 EASY_DIV_EXPRが使われます。

DEFAULT_SIGNED_CHAR
     char型がデフォルトで有符号数なら値が1となり、無符号数なら0となる式。ユーザは-fsigned-charと-funsigned-charオプションを使って、いつでもこのデフォルトをオーバライドできます。

SCCS_DIRECTIVE
     プリプロセッサが#sccsディレクティブを無視して、エラーメッセージを表示しなくてよいなら、これを定義します。

IDENT_DIRECTIVE
     プリプロセッサが#identディレクティブを無視して、エラーメッセージを表示しなくてよいなら、これを定義します。

MOVE_MAX
     ひとつの命令でメモリ間の高速転送ができる最大のバイト数。

INT_TYPE_SIZE
     ターゲットマシンのint型のサイズをビット数で表すCの式。

SLOW_BYTE_ACCESS
     1ワード未満のメモリ(charまたはshort)へのアクセスが遅い (複数命令を要する) ならば、非ゼロとなるCの式としてこのマクロを定義します。

SLOW_ZERO_EXTEND
     ゼロ拡張(charまたはshortをintに拡張) がゼロであることが分かっているレジスタがデスティネーションならばより高速であるなら、このマクロを定義します。

     このマクロを定義するならば、以下のようなRTL構造を認識する命令パターンがなければなりません。

          (set (strict_low_part (subreg:QI (reg:SI ...) 0)) ... )

     HImodeに関しても同様です。

SHIFT_COUNT_TRUNCATED
     シフト命令が、シフトカンウトの下位数ビット以外は無視するならば、このマクロを定義します。それは、シフトカウントに対する符号拡張やゼロ拡張命令を省略できるという意味も含みます。

TRULY_LOOP_TRUNCATION (OUTPREC, INPREC)
     このマシン上で、ビット数がINPRECの整数(integer)を、それより小さいビット数OUTPRECの整数に、単にそれのOUTPREC分の長さの部分を取るだけで``変換''できるなら、非ゼロとなるCの式。

     多くのマシン上では、この式は1で構いません。

NO_FUNCTION_CSE
     レジスタ上のアドレスをコールするより、定数の関数アドレスをコールした方が良い、または両者の効率が同じならば、このマクロを定義します。

PROMOTE_PROTOTYPE
     プロトタイプでcharまたはshortと宣言されている引数が、実際はintとして渡されるならば、このマクロを定義します。これにより、データ型非合致によるエラーの一部を避けるほか、一部のマシンではより良いコードが生成されます。

STORE_FLAG_VALUE
     条件が真のときにstore-flag命令 (SCOND) によってストアされる値を表すCの式。これは奇数であることが必要で、通常は1または-1です。

     マシンにstore-flag命令がないときは、 STORE_FLAG_VALUEを定義してはいけません。

Pmode
     ポインタ用のマシンモードのエイリアス。通常その定義は次のようになります。

          #define Pmode SImode

FUNCTION_MODE
     callのRTL式の中で、コールされる関数へのメモリ参照に用いられるマシンモードのエイリアス。ほとんどのマシン上で、これはQImodeでなければなりません。

CONST_COSTS (X, CODE)
     Cのswitch文の一部で、定数RTL式の相対コストを記述するもの。それには式コードconst_int、 const、 symbol_refおよびlabel_refに対する caseラベルがなければなりません。それぞれのcaseは最後にreturn文に達して、その種類の定数値を式中で使った場合の相対コストを返すものでなければなりません。コストは定数の値によって違う場合もあるので、そのためにXを調べます。

     CODEは式コードですが、これはGET_CODE (X)で得られるので冗長です。

DOLLARS_IN_IDENTIFIERS
     識別子の名前の中で文字$を使うことが、デフォルトで許されているならば、このマクロを非ゼロに定義します。

コンディションコードの情報
=============

ファイルconditions.hに、コンディションコードの求め方を決めるための変数cc_statusが定義されています (コンディションコードの解釈が、それをセットする命令に依存する場合)。この変数の内容は、コンディションコードが現在それをベースにしているところのRTL式、および、いくつかの標準的フラグです。

マシン固有の特殊なフラグを、マシン記述のヘッダファイル中で定義しなければならない場合もあります。また、 CC_STATUS_MDEPを定義することによって、マシン固有の情報を追加することができます。

CC_STATUS_MDEP
     cc_statusのmdep部分の宣言に用いられるデータ型を表すCのコード。デフォルトではintです。

CC_STATUS_MDEP_INIT
     mdepフィールドの初期値を表すCの式。デフォルトでは0です。

NOTICE_UPDATE_CC (EXP)
     cc_statusの各部分を、 EXPを本体とする命令に会うように設定するためのCの複文。

     明示的に(cc0)をセットする命令とともに、他の活動の副産物としてコンディションコードをセットする命令も認識することが、このマクロの役目です。

     コンディションコードはセットせず、他のマシンレジスタを変える命令があるならば、ある式の結果の反映としてコンディションコードがセットされても、それらの命令によってその式が無効化されなかったかどうか、このマクロはチェックしなければなりません。たとえば、 68000ではアドレスレジスタにストアをする命令はコンディションコードをチェックせず、したがってそういう命令に関しては通常は、 NOTICE_UPDATE_CCはcc_statusを変えないと定義できます。しかし、直前の命令がa4@(102)番地の値に基づいてコンディションコードをセットし、現在の命令がa4に新たな値をストアしたらどうでしょうか。これによってコンディションコードは変わりませんが、それはもはや、 a4@(102)番地の内容を反映するものとしては真ではありません。この場合はNOTICE_UPDATE_CCは、コンディションコードの値が不定であることを表すように、 cc_statusを変えなければなりません。

アセンブラコードの出力
===========

ASM_SPEC
     アセンブラに渡す、 GNU CCのドライバプログラムへのオプションを表すCの文字列定数。それは、 GNU CCに与えたオプションを、 GNU CCがアセンブラに渡すオプションへと変換する方法を、指定することもできます。その例として、ファイルtm-sun3.hを見てください。

     何も必要のない場合は、このマクロを定義してはいけません。

LINK_SPEC
     リンカに渡す、 GNU CCのドライバプログラムへのオプションを表すCの文字列定数。それは、 GNU CCに与えたオプションを、 GNU CCがリンカに渡すオプションへと変換する方法を、指定することもできます。

     何も必要のない場合は、このマクロを定義してはいけません。

LIB_SPEC
     LINK_SPECとほとんど同じ目的で使われるCの文字列。両者の違いは、 LIBS_SPECはリンカに与えるコマンドの最後(末尾)で使われることです。

     このマクロが定義されていないと、デフォルトにより、標準的なCのライブラリが通常の場所からロードされます。 gcc.cを見てください。

STARTFILE_SPEC
     LINK_SPECとほとんど同じ目的で使われるCの文字列。両者の違いは、 STARTFILE_SPECはリンカに与えるコマンドの最初(頭)で使われることです。

     このマクロが定義されていないと、デフォルトにより、標準的なCのスタートアップファイルが通常の場所からロードされます。 gcc.cを見てください。

ASM_FILE_START (STREAM)
     アセンブラファイルの開始部に入るべきテキストを、 stdioのストリームSTREAMに出力するCの式。

     通常はこのマクロは、 #NO_APPを含む行を出力するよう定義されますが、これはほとんどのアセンブラにとっては無効果のコメントですが、 GNUアセンブラには、ある種のアセンブラ構文をチェックしないことによって時間の節約ができることを教えるものです。

     SDBを使用するシステムでは、何かのコマンドを出力することが必要です。 tm-attasm.hを見てください。

ASM_APP_ON
     個々のasm文、または連続するasm文のグループの前に出力されるべきテキストを表すCの文字列定数。通常これは#APPで、それはほとんどのアセンブラにとっては無効果ですが、 GNUアセンブラには、これ以降の行がすべて有効なアセンブラ行であるかチェックせよと指示するものです。

ASM_APP_OFF
     個々のasm文、または連続するasm文のグループの前に出力されるべきテキストを表すCの文字列定数。通常これは#NO_APPで、それはほとんどのアセンブラにとっては無効果ですが、 GNUアセンブラには、普通のコンパイラ出力にとって有効な時間節約のための前提を復活せよと指示するものです。

TEXT_SECTION_ASM_OP
     アセンブラの動作制御のために、命令とリードオンリーデータの前に置かれるべきCの文字列定数。通常は.textが正しい。

DATA_SECTION_ASM_OP
     アセンブラの動作制御のために、これ以降が書き込み可能な初期化済みデータであることを表すCの文字列定数。通常は.dataが正しい。

REGISTER_NAMES
     マシンレジスタの、アセンブラにとっての名前を収めたCのイニシャライザで、そのひとつひとつがCの文字列定数。コンパイラ中のレジスタ番号が、アセンブラ言語ではこの名前へと変換されます。

DBX_REGISTER_NUMBER(REGNO)
     コンパイラのレジスタ番号REGNOに対応する、 DBXのレジスタ番号を返すCの式。単純なケースでは、この式の値はREGNOそのもので構いません。しかし、ときには、コンパイラが知っているレジスタをDBXは知らない、という場合(またはその逆) があります。そういうとき、一部のレジスタの番号を、コンパイラ用とDBX用とで違える必要があります。

DBX_DEBUGGING_INFO
     GNU CCが、 -gオプションに応じてDBX用のデバッギング出力を作るべきなら、このマクロを定義します。

SDB_DEBUGGING_INFO
     GNU CCが、 -gオプションに応じてSDB用のデバッギング出力を作るべきなら、このマクロを定義します。

PUT_SDB_op
     SDBの特定のアセンブラディレクティブに関して、標準のシンタクスをオーバライドするならば、これらのマクロを定義します。これらのマクロとその引数のリストの例は、 sdbout.cを見てください。標準のシンタクスを用いるなら、とくにこれらを定義する必要はありません。

SDB_GENERATE_FAKE
     無名構造体や無名共用体のダミーの名前を作る、通常の方法をオーバライドするためにこのマクロを定義します。詳しくはsdbout.cを見てください。

DBX_NO_XREFS
     ユーザシステム上のDBXが、構文xstagnameをサポートしていないなら、このマクロを定義します。一部のシステムでは、 tagnameという名の構造体への前方参照を記述するために、この構文が使われます。その他のシステムでは、この構文はまったくサポートされていません。

DBX_CONTIN_LENGTH
     DBXフォーマットのデバッギング情報中のシンボル名は、一定の長さ(デフォルトでは80文字)を超えた場合に、 (それを2つの.stabsディレクティブを使って) 分割して続けることができます。

     一部のオペレーティングシステムでは、 DBXはこの分割を必要とし、他のシステムでは、分割をしてはいけません。このマクロの値をゼロと定義すると、分割を禁じます。このマクロを必要な値に定義することによって、デフォルトの分割長をオーバライドできます。

DBX_CONTIN_CHAR
     シンボル名が連続するときは通常、 .stabs文字列の終わりに \ (バックスラッシュ)の文字を加えることによって、連続を表します。 \以外の違う文字をその目的に使いたいとき、このマクロを定義します。システムにとってバックスラッシュが正しいときは、このマクロを定義してはいけません。

ASM_OUTPUT_LABEL (STREAM, NAME)
     NAMEという名前のラベルのアセンブラ定義を、 stdioのストリームSTREAMに出力するためのCの文(セミコロンなし)。式assemble_name (STREAM, NAME)を使って名前そのものを出力し、その名前を定義するために必要なアセンブラシンタクスと改行を出力します。

ASM_DECLARE_FUNCTION_NAME (STREAM, NAME, DECL)
     NAMEという名前を、今から定義する関数の名前として宣言するために必要な、何らかのテキストを、 stdioのストリームSTREAMに出力するためのCの文(セミコロンなし)。このマクロの役目は、ラベルの定義を(たぶんASM_OUTPUT_LABELを使って)出力することです。引数DECLはその関数を表現しているFUNCTION_DECLのツリーノードです。

     このマクロが定義されていないと、関数名はラベルとして通常のやり方で(ASM_OUTPUT_LABELにより)定義されます。

ASM_GLOBALIZE_LABEL (STREAM, NAME)
     ラベルSTREAMをグローバルにする、すなわち他のファイルからも参照できるようにするためのコマンドを、 stdioのストリームSTREAMに出力するためのCの文 (セミコロンなし)。式assemble_name (STREAM, NAME)を使って名前そのものを出力し、その前後に、その名前をグローバルにするために必要なアセンブラシンタクスと改行を出力します。

ASM_OUTPUT_EXTERNAL (STREAM, NAME, DECL)
     このコンパイル中で参照されているが定義はされていない、 NAMEという名前の外部シンボル名を宣言するために必要な、何らかのテキストを stdioのストリームSTREAMに出力するためのCの文 (セミコロンなし)。 DECLの値はその宣言のツリーノードです。

     このマクロは、出力するものが何もなければ定義する必要はありません。 GNUアセンブラと、ほとんどのUNIXアセンブラは、何も必要としません。

ASM_OUTPUT_LABELREF (STREAM, NAME)
     NAMEという名のラベルへの、アセンブラシンタクスによる参照を、 stdioのストリームSTREAMに出力するためのCの文。ほとんどのBerkeley UNIXシステムを初め、オペレーティングシステムの慣行によっては、名前の頭に文字_が付かなければなりません。

     このマクロは、 assemble_nameの中で使われます。

ASM_GENERATE_INTERNAL_LABEL (STRING, PREFIX, NUM)
     文字列PREFIXと数値NUMから作られる名前のラベルを、文字列STRINGにストアするCの文。

     この文字列が後でASM_OUTPUT_LABELREFによって出力されるときは、 ASM_OUTPUT_INTERNAL_LABELが同じPREFIXとNUMで作るものと、同じ出力を作るものでなければなりません。

ASM_OUTPUT_INTERNAL_LABEL (STREAM, PREFIX, NUM)
     文字列PREFIXと数値NUMから作られる名前のラベルを、 stdioのストリームSTREAMに出力するためのCの文。これらのラベルは内部的な目的に使われるものであり、オブジェクトファイルのシンボルテーブルに登場する必要はありません。多くのシステムで、文字Lで始まるラベルが、この目的に使われます。このマクロの通常の定義は以下のようなものです。

          fprintf (STREAM, "L%s%d:\n", PREFIX, NUM);

ASM_OUTPUT_CASE_LABEL (STREAM, PREFIX, NUM, TABLE)
     ジャンプテーブルの前に、ラベルを特別に出力する必要があるなら、これを定義します。最初の3つの引数は、 ASM_OUTPUT_INTERNAL_LABELのものと同じです。 4つ目の引数は、このラベルに続くジャンプテーブルです (addr_vecまたはaddr_vec_diffのあるjump insn)。

     この機能はSystem Vで、テーブルのためのswbeg文を出力するために使われます。

     このマクロが定義されていないと、これらのラベルはASM_OUTPUT_INTERNAL_LABELで出力されます。

ASM_OUTPUT_CASE_END (STREAM, NUM, TABLE)
     ジャンプテーブルの終わりに、何か特別なものを出力しなければならないなら、これを定義します。その定義は、テーブル用のコードが書かれた後に実行されるCの文です。それは、適切なコードをstdioのストリームSTREAMに書き出すものでなければなりません。引数TABLEは、そのジャンプテーブルのinsnで、 NUMは、その前に付くラベルのラベル番号です。このマクロが定義されていないと、ジャンプテーブルの終わりに特に何も出力されません。

ASM_FORMAT_PRIVATE_NAME (OUTVAR, name, NUBMER)
     文字列NAMEと、数値NUMBER、および何らかの適切な句読点から作って新たにアロケートした文字列を、 OUTVAR(これはchar *型の変数)に代入するCの式。文字列用のスペースはallocaを使って取得します。

     この文字列は、 ASM_OUTPUT_LABELREFへの引数として使われて、 NAMEという内部的static変数のためのアセンブララベルを作り出します。したがって、文字列は最終的に有効なアセンブラコードになるものでなければなりません。引数NUMBERは、このマクロが実行されるたびに違います。それにより、同じような名のスコープが異なる内部的codestatic 変数との衝突を防ぎます。

     ユーザが使用しているシンボルとの衝突を避けるために、この文字列はCの識別子としては有効でないことが理想的です。ほとんどのアセンブラで、アセンブラシンボルの中にピリオドやパーセント記号などを使えるので、名前と数値との間にこれらを少なくともひとつ入れれば十分です。

ASM_OUTPUT_ADDR_DIFF_ELT (STREAM, VALUE, REL)
     ディスパッチテーブル上のアドレスが、テーブルそのもののアドレスとの相対アドレスであるマシンでは、このマクロが必要です。

     その定義は、 2つのラベル間の差を生成するためのアセンブラ疑似命令を、 stdioのストリームSTREAMに出力するCの文です。 VALUEとRELは、 2つの内部的ラベルの番号です。これらのラベルの定義は、 ASM_OUTPUT_INTERNAL_LABELを使って出力され、ここでも同じようにプリントされるものでなければなりません。たとえば、次のようなものです。

          fprintf (STREAM, "\t.word L%d-L%d\n", VALUE, REL);

ASM_OUTPUT_ADDR_VEC_ELT (STREAM, VALUE)
     ディスパッチテーブル上のアドレスが絶対アドレスであるマシンでは、このマクロが必要です。

     その定義は、ラベルへの参照を生成するためのアセンブラ疑似命令を、 stdioのストリームSTREAMに出力するCの文です。 VALUEは内部的ラベルの番号であり、その定義はASM_OUTPUT_INTERNAL_LABELを使って出力されるものです。それはたとえば、次のようなものです。

          fprintf (STREAM, "\t.word L%d\n", VALUE);

ASM_OUTPUT_DOUBLE (STREAM, VALUE)
     値がVALUEであるdoubleの定数をアセンブルするアセンブラ命令を、 stdioのストリームSTREAMに出力するCの文。 VALUEは、 double型のCの式です。

ASM_OUTPUT_FLOAD (STREAM, VALUE)
     値がVALUEであるfloatの定数をアセンブルするアセンブラ命令を、 stdioのストリームSTREAMに出力するCの文。 VALUEは、 float型のCの式です。

ASM_OUTPUT_INT (STREAM, EXP)
ASM_OUTPUT_SHORT (STREAM, EXP)
ASM_OUTPUT_CHAR (STREAM, EXP)
     値がVALUEであるint、short、またはcharの定数をアセンブルするアセンブラ命令を、 stdioのストリームSTREAMに出力するCの文[訳注：原文のママ]。引数EXPは、定数値を表現するへRTL式です。この値をアセンブラの式として出力するためには、 output_addr_const (EXP)を使います。

ASM_OUTPUT_BYTE (STREAM, VALUE)
     数値VALUEが乗っている1バイトをアセンブルするアセンブラ命令を、 stdioのストリームSTREAMに出力するCの文。

ASM_OUTPUT_ASCII (STREAM, PTR, LEN)
     アドレスPTRにある長さLENの文字列定数をアセンブルするアセンブラ命令を、 stdioのストリームSTREAMに出力するCの文。 PTRはchar *型のCの式で、 LENはint型のCの式です。

     Berkley UNIXアセンブラのように、 .asciiという疑似オペランドを使えるアセンブラなら、マクロASM_OUTPUT_ASCIIを定義してはいけません。

ASM_OUTPUT_SKIP (STREAM, NBYTES)
     ロケーションカウンタをNBYTESバイト前進させるアセンブラ命令を、 stdioのストリームSTREAMに出力するCの文。 NBYTESはint型のCの式です。

ASM_OUTPUT_ALIGN (STREAM, POWER)
     ロケーションカウンタを2のPOWER乗バイト前進させるアセンブラ命令を、 stdioのストリームSTREAMに出力するCの文。 POWERはint型のCの式です。

ASM_OUTPUT_COMMON (STREAM, NAME, SIZE)
     サイズがSIZEバイトで、名前がNAMEのlocal-commonラベルのアセンブラ定義を、 stdioのストリームSTREAMに出力するためのCの文 (セミコロンなし)。式assemble_name (STREAM, NAME)を使って名前そのものを出力し、その前後に、名前を定義するためのアセンブラシンタクスと改行を出力します。

     このマクロは、非初期化static変数のアセンブラ定義のされ方を制御します。

ASM_OUTPUT_SOURCE_LINE (STREAM, LINE)
     現在のソースファイルの行番号LINEのコードの前に、 DBXまたはSDBのデバッグ用情報を、 stdioのストリームSTREAMに出力するCの文。

     デバッガの標準的なデバッグ情報を使う方が適切ならば、このマクロを定義する必要はありません。

TARGET_BELL
     エスケープシーケンス\aの整数値を表すCの定数式。

TARGET_BS
TARGET_TAB
TARGET_NEWLINE
     エスケープシーケンス\b、\t、および\nの整数値を表すCの定数式。

TARGET_VT
TARGET_FF
TARGET_CR
     エスケープシーケンス\v、\f、および\rの整数値を表すCの定数式。

ASM_OUTPUT_OPCODE (STREAM, PTR)
     マシン命令用に別の名前を必要とする、特殊なアセンブラを使っているときは、このマクロを定義してください。

     その定義は、 stdioのストリームSTREAMにアセンブラ命令のオペコードを出力するCの単文または複文です。引数PTRは、 ``内部的''な形式 …マシン記述に書かれている形式… のオペコード名をポイントするchar *型の変数です。その定義はオペコード名をSTREAMに出力し、必要な必要な変換を行ない、変数PTRをオペコードの終わりをポイントするようインクリメントして、それが二度出力されないようにします。

     実際はこのマクロ定義がオペラコード名の全体を処理しなくても、あるいは、オペコード以上のものを処理しても構いませんが、オペランドを置換するために%を含むテキストを処理したいならば、定義の中でその置換も明記しなければなりません。そのためには、テキストが正常に出力されるように、 PTRを正しくインクリメントするようにしてください。

     このマクロ定義が何もしないならば、命令は通常どおりに出力されます。

FINAL_PRESCAN_INSN (INSN, OPVEC, NOPERANDS)
     定義されていればこれは、 insnのアセンブラコードの出力の直前に実行されて、抽出されたオペランドの出力のされ方を変更するためのCの文。

     引数OPVECは、 insnから抽出されるオペランドが入っているベクタ、 NOPERANDSはinsnにとって有意なデータが入っているベクタの成分数です。このベクタの内容が、 insnのテンプレートをアセンブラコードに変換するために使われるものなので、ベクタの内容を変えることによって、アセンブラコードを変えることができるのです。

     このマクロは、様々なアセンブラシンタクスがひとつの命令パターンファイルを共有している場合に有用で、このマクロの定義を変えることにとって、大きな命令クラスの命令 (再編成されたオペランドを持つものなど) を、それぞれ異なって出力することができます。当然ながら、個々の命令パターンに影響するアセンブラシンタクスの各種変形は、これらのパターン中に条件付き出力ルーチンを書くことによって取り扱います。

     このパターンが定義されていないと、それは空文と等価です。

PRINT_OPERAND (STREAM, X, CODE)
     命令オペランドX用のアセンブラシンタクスを、 stdioのストリームSTREAMに出力するためのCの複文。 XはRTL式です。

     CODEは、オペランドのプリントのされ方を指定する値です。それは、同一のオペランドをコンテキストの違いによっては違ってプリントしなければならないというときに使います。 CODEは、オペランドのプリントをリクエストするために使われた %指定から得られるものです。その指定が単に%DIGITなら、 CODEは0です。指定が%LTR DIGITなら、 CODEはLTRのASCIIコードです。

     Xがレジスタなら、このマクロはレジスタの名前をプリントするものでなければなりません。その名前は、 char *[]型の配列reg_namesにあります。 reg_namesは、 REGISTER_NAMESによって初期化されています。

     マシン記述に%PUNCT (%の後に句読点文字)があるときは、このマクロはXがヌルポインタ、 CODEがその句読点文字でコールされます。

PRINT_OPERAND_ADDRESS (STREAM, X)
     アドレスXへのメモリ参照である命令オペランドの、アセンブラシンタクスを、 stdioのストリームSTREAMへ出力するCの複文。 XはRTL式です。

ASM_OPEN_PAREN
ASM_CLOSE_PAREN
     これらのマクロはCの文字列定数として定義し、算術演算式をグループ化するためのアセンブラシンタクスを記述します。以下の定義は、ほとんどのアセンブラにとって正しいものです。

          #define ASM_OPEN_PAREN "("
          #define ASM_CLOSE_PAREN ")"

コンフィギュレーションファイル
***************

コンフィギュレーションファイルconfig-MACHINE.hに、その上でコンパイラが実行されるマシンとシステムを記述するマクロ定義があります。その中の値のほとんどは、 GNU CCが走るすべてのマシン上で、実際には同じなので、したがって、コンフィギュレーションファイルのほとんどの部分は、どれも同じです。

しかし、変わるマクロもあります。

FAILURE_EXIT_CODE
     コンパイラが深刻なエラーによって終了するときに返される、ステータスコードを表すCの式。

SUCCESS_EXIT_CODE
     コンパイラが深刻なエラーなく終了するときに返される、ステータスコードを表すCの式。