この文書は「ECMA-335 Patirion III - CIL Instruction Set」の日本語訳です。原文はECMA-335 Patirion III - CIL Instruction Setにあります。この文書は完璧なもの(ただし、原文に忠実にする完璧ではなく、より理解しやすい方向での完璧さ)を目指していますが、内容の保証は出来ません。また、細部に独自解釈が混じっている場合もあるので、不安な場合は原文の方を参照してください。
連絡事項
目次
まだ無し
【この前文は適当に訳されています。気を付けて】
この明細は「共通中間言語(CIL)命令セット」、共通言語インフラストラクチャの明細の一部の詳細な記述です。Partition I【訳していないため、英語のページに飛びます】は、CLIのアーキテクチャについて記述し、CIL命令セットに関係の多くの問題の概観を提供します。その概観は、ここで記述されるような命令セットについての理解にとって必要です。
各命令記述は1セットの関連するCLIマシン命令について記述します。各命令定義は5部から成ります。
「ExecutionEngineException」は、実行エンジンの内部状態が悪くなり、実行が継続できないことを示します(注意:単に検証可能なコードを実行するシステムでは、この例外はスローされない)。
「StackOverflowException」は、ハードウェアスタックサイズを超過したことを示します。この例外の正確なタイミング、およびそれが生じる条件は実装依存です(注意:この例外は「Must_Provide_Maxstack【まだ訳していません】」に記載されている最大スタックサイズと無関係です。サイズは、この例外が物理的なハードウェア上のその方法状態の実装と関係する間に、Partition I【英語のドキュメントに飛びます】に記述された方法状態の一部である評価スタックの深さに関係があります)。【どなたか、文章まとめてください】
「OutOfMemoryException」は、命令がメモリを割り当てる(newobj、newarr)のに利用可能なメモリスペースが使い尽くされたことを示します。また、あるいは実装に特有の理由(例えば、ネイティブコードへのJITコンパイルに基づいた実装は、与えられたメソッドへの一回目の呼び出しあるいはcallvirtを実行する間に翻訳されたメソッドを格納するためにスペースを使い果たすかもしれません)のために起こる場合もあります。
さらに、数値のオペランドを持っている命令は、それらがオペランドの型に基づいて、どのように作動するか説明するオペランド型表を指定します。詳細は「1.5 オペランド型表( Operand Type Table) 」を参照してください。
すべての命令がすべてのCLIのプロフィールに含まれるとは限らないことに注意してください。詳細は、PartitionIV【英語のドキュメント】 を参照してください。
Common Type System(CTS)は豊かなタイプシステムを定義し、Common Language Specification(CLS)は言語相互運用性を特徴づけます。しかし、CLI自身ははるかに簡単な型に対処します。
これらの型はユーザによって定義された値の型と内蔵型のサブセットを含めます。 サブセットは「基本的なCLI型」として一括して知られています。
オブジェクト参照とポインタ型には「null」が割り当てられる可能性があるので注意してください。「null」はCLIの中で「0(全てのビットが0)」になるように定義されています。
注意: アーキテクチャにかかわらず、1~2バイト整数も4バイト整数としてロードされ、その後は4バイト整数として扱われます。8バイト整数は種類の異なるものとして別扱いとします。このことは、通常の整数演算がonv 命令や conv.ovf 命令を実行しなくても、すべての実装において同一の結果になることを保証し、コードのポータビリティに役立ちます。
1~2バイトの整数型をもたらす変換命令が実際にスタックの値をint32(32ビット)にしますが、値は低ビットにだけ入ります(つまり、上のビットは符号付き拡張又は符号無し拡張されます)。
1~2バイトの整数の操作を正しくシミュレートするために、1~2バイト整数の形への変換がdiv、rem、shr、比較および条件付き分岐命令の前に必要です。
明白な変換指示に加えて、次の4つのケースでCLIが特別な方法で1~2バイトの整数を扱います。
最後の2つの手順では、実際の値の切り捨てがネイティブの呼び出し規約に到達しています。ですが、CILではこれの影響を受けずにまるで適切な「conv」命令を含んでいるかのように動きます。
注意: 上の仕様は、対応する実装が、中間計算型の精度に切り捨てするのを避けるのを許容します。従って、より広い精度ハードウェアレジスタの使用を許可します。また、同じであるか、より大きい精度をもたらす最適化変換アプリケーションも許可します。正確に複製可能な振る舞いが言語かアプリケーションによって要求される場合、明示的な変換が使用されてもよい。
浮動小数点式の内部表現がその名目上の型より大きな範囲あるいは精度を持っている時に記憶位置に入れられる場合、自動的に記憶位置の型になります。これは、正確の損失あるいは範囲外値(NaN、+無限、または-無限)の生成を含んでいるかもしれません。しかしながら、もし値が変更されずにそれが記憶位置から再びロードされる場合、値は将来の使用時に内部表現で保持されているかもしれません。エイリアシング(訳注:様々な型でのアクセス)することおよび他のスレッド実行の影響(メモリ・モデル・セクションを参照)を考慮に入れ、メモリ位置が後のロードの時にまだ有効であることを保証することはコンパイラの責任です。 しかしながら、余分な正確さを持つこの自由は、明示的な変換(conv.r4またはconv.r8)の実行に続いて許されません(その時に内部表現は関連する型において正確に表現可能であるに違いない)。
注意: 特定の記憶型に変換することができない値を検知するために、変換指示(conv.r4、またはconv.r8)そして次に「ckfinite」を使用して範囲外値をチェックします。特定の記憶型に切り替える場合にアンダーフローを検知するため、変換の前後に0との比較が要求されます。
注意:これらは、デノーマライズされた浮動小数点数上の算術演算の振る舞いを指定しません。また、はそのような明細が作成されても、それを指定しません。これはIEC 60559:1989と一致します。さらに、この標準は、作成されるNaNsの正確なビット・パターンにアクセスする方法、および32ビットおよび64ビットの表現間のNaNを変換する場合振る舞いを指定しません。この振る舞いはすべて「deliberately left implementation-specific【訳不明】」です。
CLIのブール型はメモリの中で1バイトを占めます。すべてが0のビットパターンは、偽(false)の値を表します。任意のビットセット(0でない整数)を備えたビットパターンは、真(true)の値を表します。
オブジェクト参照(「O」型)は完全に不透明です。これらには算術演算命令はありません。可能なたただ一つの比較オペレーションが2つのオブジェクト参照間で等しい(また等しくない)を判定できます。オブジェクト参照で定義された変換命令はありません。オブジェクト参照は、あるCILオブジェクト命令(特にnewobjおよびnewarr)によって作成されます。オブジェクト参照は、引数として渡すこと、返り値として使うこと、ローカル変数として格納することができ、配列よびオブジェクトのフィールドに格納出来ます。
ポインタには2種類あります。1つは管理されていないポインタ。もう一つは管理されたポインタ。同じ配列あるいはオブジェクト(Partition I_alink_partitionIを参照)の中へのポインタについては、次の算術演算が定義されます。
これらのオペレーションのどれも「安全なコード」の中で許可されません。
異なる種類のポインタ間での算術を使用することによるガベージコレクタへの影響を理解することは重要です。管理されていないポインタは、ガベージコレクタによってコントロールされるメモリに参照を付けてはなりません。それらの上で算術を行なうことは、システムのメモリ安全性を危険にさらすかもしれません(従って、それは安全ではありません)。しかし、それらはガベージコレクタに報告されないので、その演算への影響はありません。
しかしながら、管理されたポインタは、ガベージコレクタに報告されます。ガーベジコレクションの一部として、それらが指す位置の内容およびポインターそれ自体の両方は修正することができます。それらが、その管理下(評価スタック、呼び出しスタック、スタティック・メモリあるいは別のアロケーターの管理下のメモリ)にないメモリを指す場合、ガベージコレクタは管理されたポインタを無視するでしょう。しかしながら、管理されたポインタがガベージコレクタによってコントロールされたメモリを参照する場合、それはオブジェクトのフィールド、配列の要素あるいはちょうど配列の終了を過ぎた要素のアドレスを指示するに違いありません。アドレス算術が他の位置(割り付けられたメモリ中のオブジェクトヘッダーあるいはギャップ)を参照する、管理されたポインタを作成するために使用される場合、ガベージコレクタのオペレーションは無指定です。
管理されていないポインタはC/C++のような言語の中で使用される従来のポインタです。それらの使用上の制限はありません。しかし、それらの大部分は証明することが出来ないコードになります。アンマネージドポインタは、あたかもそれらが符号なし整数(CLIではそのように扱われます)かのように、アンマネージドポインタを含んでいるマーク位置へ完全に有効ですが、特定のタイプのデータにアンマネージドポインタとしてそれらを目立たさせるほうが多くの場合よい。これは、返り値、ローカル変数あるいは引数のためにシグネチャの中で「ELEMENT_TYPE_PTR」を使用する、あるいはフィールドまたは配列要素のためにポインタ型を使用することにより行われます。
管理されていないポインタはガベージコレクタに報告されず、整数が使用することができる方法の中で使用することができます。
マネージドポインタ(&)は、ローカル変数、メソッドの引数、オブジェクトのフィールド、値型のフィールド、配列の要素、あるいは配列の終了を過ぎた要素が格納されるアドレス(管理された配列の中へのポインタインデックスのために)を指し示します。マネージドポインタは「null」にはなりません。(マネージドメモリを指さなくても、それらはガベージコレクタに報告されるに違いありません)
マネージドポインタは、返り値、ローカル変数、引数のためにシグネチャの中で「ELEMENT_TYPE_BYREF」を使用する、あるいはフィールドか配列要素のために「by-ref型」を使用することにより指定されます。
3章では、各命令が同系統命令表にまとめられて示されます。それは、命令の各変形について説明しています。「フォーマット」テーブルのは、すべての引数に加えて、命令のオペコードをリストします。
形式 (Format) |
命令形式 (Assembly Format) |
説明 (Description) |
---|---|---|
FE 0A <unsigned int16> | Ldarga argNum | fetch the address of argument argNum. |
0F <unsigned int8> | Ldarga.s argNum | fetch the address of argument argNum, short form |
「フォーマット」の最初の1つあるいは2つの番号は、この指示がどのようにコード化されるか(その「オペコード」)を示します。したがって、ldarga命令は、別の命令の終わりに「FE」続けて「0A」を保持するバイトとしてコード化されます。タイプ名は、1つの命令列の中で続くべき数を表わします。2バイトこの例において、符号なしの整数として直接扱われることになっている量は「FE 0A」オペコードに続きます。
内蔵型(int8、符号無しint8、int16、符号無しint16、int32、符号無しint32、int64、符号無しin64、float32およびfloat64)が、現われることができる固定サイズのうちのどれでも、記述をフォーマットします。これらの型は、引数用のバイトの数およびどのようにそれを解釈しなければならないか(符号付き、符号無しあるいは浮動小数点)を定義します。さらに、メタデータ・トークンが<T>として示されて現われることができます。印は4バイトの整数としてコード化されます。引数はすべて、リトル・エンディアンです。命令オペコードおよび引数のためのバイトは、できるだけ(整列パディングは終っていません)しっかりとパックされます。
「Format」には命令それぞれについて、記憶を助ける「Assembly Format」を定義します。命令列が引数を伴っているものについては、命令への引数の各々に名前を付けます。各命令引数については、「Assembly Format」に名前があります。これらの名前は、命令記述の中でその後使用されます。
CIL オペコードは長さ1バイト以上です(更にオペランドが続くかもしれません)。第1のバイトが「0x00~0xEF」または「0xFC~0xFF」の範囲ものもは標準化のために予約済です。第1のバイトが「0xF0~0xFB」の範囲は空いていて、実験の目的に利用可能です。任意の方法の実験オペコードの使用は無効の、そして結果証明できない方法を与えます。
現在定義された命令コードは、表1の中で指定されます。
【表1:長いので略】
スタック推移図は命令が実行される前、および後の評価スタックの状態を示します。以下は、典型的なスタック推移図です。
...,value1,value2 -> ...,result
この図は、スタックがその上に少なくとも2つの要素を持っているに違いないことを示します。また、定義では、一番上の値(「スタックのトップ」あるいは「最も最近Pushされた」)はValue2と呼ばれ、その下が(value2に先立ってPushされた)value1と呼ばれます。(このような図では、スタックはページに沿って右に成長します)。その命令はスタックからこれらの値を取り除き、それらをresultと呼ばれる別の値に取り替えます。
英語の記述は、フォーマットおよびスタック推移だけでは明白でない命令に関するどんな詳細も記述します。
多くのCIL処理がスタック上で数値のオペランドをとります。これらの処理はそれらがどのようにオペランドの型に対処するかに依存して、いくつかのカテゴリーに分類されます。次の表は有効な型のオペランドおよび結果の型を要約します。ここで引用されたタイプが、CIL立証のようなツールによって使用されるもっと詳細な型ではなくCLIによってトレースされるような型であることに注目してください。CLIによってトレースされた型は次のとおりです。「int32」「int64」「native int」「F」「O」および「&」。
「A op B (オペコードは add, div, mul, rem, sub)」。下記の表はオペランド型の個々の可能な組み合わせのために、結果の型を示します。マークされないマスは無効なCIL命令を示します。暗くなったマスは、検証可能でないCIL命令を示します。命令のリストを備えたマスはそれらの命令にのみ有効です。
A's Type | B's Type | |||||
---|---|---|---|---|---|---|
int32 | int64 | native int | F | & | O | |
int32 | int32 | native int | &(add) | |||
int64 | int64 | |||||
native int | native int | native int | &(add) | |||
F | F | |||||
& | &(add, sub) | &(add, sub) | native int(sub) | |||
O |
次の表は「neg命令」のものです。マークされない箱は無効なCIL命令を示します。この命令の有効な用途はすべて検証可能です。
ペランドの型 | int32 | int64 | native int | F | & | O |
---|---|---|---|---|---|---|
戻り値の型 | int32 | int64 | native int | F |
下の表は、スタック上のトップ2つの値に基づいて、ブールの返り値または分岐を行う命令についてです。beq、beq.s、bge、bge.s、bge.un、bge.un.s、bgt、bgt.s、bgt.un、bgt.un.s、ble、ble.s、ble.un、ble.un.s、blt、blt.s、blt.un、blt.un.s、bne.un、bne.un.s、ceq、cgt、cgt.un、clt、clt.un、に使用されます。「OK」そのオペランドタイプが全ての処理について有効であることを示しています。何も書かれていないマスは無効なCIL命令を示しています。暗くなったマスは、検証可能でないCIL命令を示します。命令のリストを備えたマスはそれらの命令にのみ有効です。
int32 | int64 | native int | F | & | O | |
---|---|---|---|---|---|---|
int32 | OK | OK | ||||
int64 | OK | |||||
native int | OK | OK |
Beq[.s], bne.un[.s], ceq |
|||
F | OK | |||||
& |
beq[.s], bne.un[.s], ceq |
OK1 | ||||
O |
beq[.s], bne.un[.s], ceq2 |
注意 : 2つのオペランドが同じ配列へポインタでない場合、結果は、ガベージコレクトの中の2つの関係ないデータ項目の距離です。この距離は、次のガーベジコレクションで変わるでしょう。本質的に、結果は有用なものではなく計算するために使用することができません。
and、div.un、not、or、rem.un、xorを使用した場合、これらの命令は整数型でのみ作動します。また、div.unとrem.unの命令は符号なし整数としてそれらの引数を扱い、符号無しの結果に対応するビットパターンを出力します。しかしながら、CLIの明細に記述されるように、CLIはスタック上の符号有り・符号なし整数を区別しません。そのnot命令は単項で、入力と同じタイプを返します。shlおよびshr命令はそれらの最初のオペランドと同じタイプを返します。また、それらの2番目のオペランドは符号無しネイティブ整数型であるに違いありません。空白のマスは無効なCIL、命令です。他のすべてのマスは、オペランドの検証可能な組み合わせを表します。
int32 | int64 | native int | F | & | O | |
---|---|---|---|---|---|---|
int32 | int32 | native int | ||||
int64 | int64 | |||||
native int | native int | native int | ||||
F | ||||||
& | ||||||
O |
下の表はシフト演算についてのものです。シフト演算のためのオペランドおよび結果の正しい組み合わせを表しています(shl、shr、shr.un)。空白のマスは無効なCIL命令を示します。他のすべてのマスは、オペランドの検証可能な組み合わせを表示します。もしも、「シフトする量」オペランドが「シフトされる」オペランドより大きい幅の場合、結果は実装に依存します。(例えばint32整数が37ビットシフトした場合)
シフトする量 | |||||||
---|---|---|---|---|---|---|---|
int32 | int64 | native int | F | & | O | ||
シフトされる側 | int32 | int32 | int32 | ||||
int64 | int64 | int64 | |||||
native int | native int | native int | |||||
F | |||||||
& | |||||||
O |
下の表はオーバーフロー付きの演算に関する命令についての表です。目標データ型の中で結果を表わすことができない場合、これらの命令は例外を生成します。add.ovf, add.ovf.un, mul.ovf, mul.ovf.un, sub.ovf, sub.ovf.un命令です。暗くなったマスは検証可能ではありません。また、空白のマスは無効なCIL命令を示しています。
|
int32 |
int64 |
native int |
F |
& |
O |
---|---|---|---|---|---|---|
int32 | int32 | native int |
&
add.ovf.un |
|||
int64 | int64 | |||||
native int | native int | native int |
&
add.ovf.un |
|||
F | ||||||
& |
& add.ovf.un, sub.ovf.un |
& add.ovf.un, sub.ovf.un |
native int sub.ovf.un |
|||
O |
下の表は変換命令についてのものです。これらの命令は、評価スタック上のトップのアイテムをある数値の型から別の型に変換します。結果型は、命令の一部として指定されたデータ型として表現可能なことを保証されます(つまり、conv.u2命令は、符号無しint16に格納することができる値を返します)。しかしながら、スタックには、最小で4バイトの値しか格納できません。conv.<型>,conv.ovf.<型>,conv.ovf.<型>.un命令が使えます。暗くなったマスは検証可能でない命令を示します。また、空白のマスは無効なCIL命令を示しています。
Convert-To | Input (from evaluation stack) | |||||
---|---|---|---|---|---|---|
int32 | int64 | native int | F | & | O | |
int8 unsigned int8 int16 unsigned int16 |
Truncate1 | Truncate1 | Truncate1 | Truncate to zero2 | ||
int32 unsigned int32 |
Nop | Truncate1 | Truncate1 | Truncate to zero2 | ||
int64 | Sign extend | Nop | Sign extend | Truncate to zero2 | Stop GC tracking | Stop GC tracking |
unsigned int64 | Zero extend | Nop | Zero extend | Truncate to zero2 | Stop GC tracking | Stop GC tracking |
native int | Sign extend | Truncate1 | Nop | Truncate to zero2 | Stop GC tracking | Stop GC tracking |
native unsigned int | Zero extend | Truncate1 | Nop | Truncate to zero2 | Stop GC tracking | Stop GC tracking |
All Float Types | To Float | To Float | To Float | Change precision3 |
CLIは6つの型(int32, native int, int64, F, O, &)のみに働いていますが、メタデータははるかに豊富なモデルをメソッドの引数に提供します。メソッドの呼び出しの時、CLIは次の表の中に詳述されている暗黙の型変換を行ないます(概念的に、それは適切なconvを挿入します。それらの結果は切り捨てや、丸め誤差があるかもしれないです)。「OK」と書かれているマスは、暗黙の変換が行われます。暗くなっている箱は検証可能でない変換です。また、無効な変換は空白のマスになっています(コンパイラーは目的の効果を得るために明示的なconv.*やconv.*. ovf命令を使うことも出来ます)。
Type In Signature | Stack Parameter | |||||
---|---|---|---|---|---|---|
int32 | native int | int64 | F | & | O | |
int8 | OK | OK | ||||
unsigned int8, bool | OK | OK | ||||
int16 | OK | OK | ||||
unsigned int16, char | OK | OK | ||||
int32 | OK | OK | ||||
unsigned int32 | OK | OK | ||||
int64 | OK | |||||
unsigned int64 | OK | |||||
native int |
OK Sign extend |
OK | ||||
native unsigned int |
OK Zero extend |
OK Zero extend |
||||
float32 | Note4 | |||||
float64 | Note4 | |||||
Class | OK | |||||
Value Type (Note2) | Note1 | Note1 | Note1 | Note1 | ||
By-Ref( & ) |
OK Start GC tracking |
OK | ||||
Ref Any(Note3) |
この表に関するさらなる注:
確実さのために、CILコードには詳細な制限があります。
これらはCILからネイティブコードへの単純なコンパイラを構築することをより容易にするために課されて、いくつかのさらなる制限もあります。このセクションは適用される一般的な制限と、個々の命令のためにあります。
メソッドの実装は下に指定されるようにコード化されて、CIL命令から連続するブロックによって提供されます。メソッドのための命令ブロックのアドレスと長さは、ファイル・フォーマットで指定されます( Partition II_alink_partitionII, Common Intermediate Language Physical Layout【英語のページ飛びます】を参照)。第1の命令は、命令ブロックの第1のバイト(最低のアドレス)にあります。
命令は可変サイズです。各命令のサイズは、命令バイト自体の内容(を解読することによって)から決定することができます。サイズ、また、命令内のバイトの注文は各命令定義によって指定されます。命令は、バイトストリームの中で詰め物をせずに続きます。アラインメントおよびバイトオーダー両方に関係しません。
各命令は、バイトの正確な数を占めます。また、命令ブロックの終了まで、次の命令は次のバイトに直ちに始まります。命令ブロックが(指定されたブロックの長さまでに)完全な最後の指示を形成せずに終了することは無効です。
命令プリフィックスは、新しい命令を導入せずに命令の長さを拡張します。1つ以上のプリフィックスがある命令は、第1の命令プリフィックスの第1のバイトにからのみ始まります。
【ここの囲みは間違っている可能性大です。】
注意:命令ブロックの終了まで、それが分岐の目標でなくても、どんなコントロールを移す命令の後に続く命令はデコードされます。コントロールが到達不可能な場合でも命令が命令ストリームへと現れるかもしれません。相対アドレスなデータアドレッシングモードはありません。また、生のデータは、直接命令ストリームの内に埋め込むことができません。ある命令は命令の一部として即値を埋め込むことを可能にします。しかしながら、それは命令ストリームに直接埋め込まれていた生のデータの許可と異なります。到達不能コードがマシンに生成されたコードの結果現われるかもしれないし許可されます。しかし、それは、常に適切に決まった命令シーケンスの形をしているに違いありません。
命令ストリームは、翻訳の実行に先立って翻訳することができます。また、関連する命令ブロックが廃棄されます。したがって、コード・アドレスを捕らえて操作するcall、retなどのような命令さえ、CIL命令ストリームのアドレスの代わりに翻訳されたアドレス上で作動するために仮想になることができます。