本明細書の一部に組み込まれ、本明細書の一部を構成する添付図面は、現時点において好適と思われる実施形態を示す。
以下に説明する種々の新規の特徴を実現する例示としての装置を図1に示す。図1は、目的レジスタ(target register )15を含む目的プロセッサ13を、多くのソフトウェア・コンポーネント19,20,21を含むメモリ18とともに示しており、更に基本ブロック・キャッシュ23、グローバル・レジスタ・ストア27を含むワーキング・ストレージ16及び変換の対象コード(subject code)17を示している。ソフトウェア・コンポーネントは、オペレーティング・システム20、トランスレータ・コード19、及び変換済みコード21を含む。トランスレータ・コード19は、例えば一つのISAの対象コードを別のISAの変換済みコードに変換するエミュレータとして、あるいは対象コードを変換済みコード、すなわち同一のISAの各々に変換するアクセラレータとして機能する。
トランスレータ19、すなわちトランスレータを実行するソース・コードのコンパイル版、及び変換済みコード21、すなわちトランスレータ19により生成される対象コード17の変換版は、例えば通常マイクロ・プロセッサ又は他の適切なコンピュータである目的プロセッサ13上で動作するUNIX(登録商標)のようなオペレーティング・システム20と連動して動作する。ここで、図1に示す構造は単なる例示に過ぎず、例えば、本発明によるソフトウェア、方法、及びプロセスはオペレーティング・システム内部に含まれるか、あるいはオペレーティング・システムの下位レベルに位置するコードとして用いることができることを理解されたい。対象コード、トランスレータ・コード、オペレーティング・システム、及びストレージ機構は、当該技術分野の当業者には公知の非常に多くのタイプのうちのいずれかのタイプとすることができる。
図1による装置では、プログラム・コード変換は、変換済みコード21が動作している間の実行時間に動的に行なわれることが好ましい。トランスレータ19は変換済みプログラム21に従って動作する。変換プロセスの実行パスは制御ループであり、この制御ループでは、トランスレータ・コード19を実行して対象コード17のブロックを変換済みコード21に変換するステップと、次に変換済みコードの当該ブロックを実行するステップと、を含み、変換済みコードの各ブロックの終わりは、制御をトランスレータ・コード19に返す命令を含む。別の表現をすると、変換するステップと、次いで、その対象コードを実行するステップとは交互に行われるため、対象プログラム17の一部のみが一度に変換され、第1基本ブロックの変換済みコードを後続基本ブロック群の変換の前に実行する。トランスレータの基本変換ユニットは基本ブロックであり、これは、トランスレータ19が対象コード17を一度に一つの基本ブロックの割合で変換することを意味する。基本ブロックは、厳密に一つのエントリー・ポイント(entry point )及び厳密に一つのエグジット・ポイント(exit point)が設定されたコード・セクションとして公式に定義され、このコード・セクションによって、ブロック・コードを単一の制御パスに制限する。この理由により、基本ブロック群は制御フローの基本ユニットである。
変換済みコード21を生成するプロセスでは、中間表現(IR:intermediate representation )ツリーは対象命令シーケンス(subject instruction sequence)に基づいて生成される。IRツリーは、計算表現の抽象表現であり、対象プログラムが実行するオペレーションである。後の時点で、変換済みコード21はIRツリーに基づいて生成される。
本明細書に記載するIRノード群の集まりを、口語表現を用いて「ツリー(trees )」と呼ぶ。我々は、正式には、このような構造は実際には有向非巡回グラフ(DAG:directed acyclic graph)であって、ツリーではないことを注記しておく。ツリーの正式な定義には、各ノードが最大でも一つの親ノードしか持たないことが必要である。記載する実施形態では、共通部分式削除(common subexpression elimination)をIR生成中に使用するため、ノード群は多くの場合、複数の親ノードを有する。例えば、フラグが影響する命令結果のIRは、2つの抽象レジスタが参照することができ、これらのレジスタは、宛先対象レジスタ(destination subject register)及びフラグ結果パラメータ(flag result parameter )に対応する。
例えば、対象命令(subject instruction )“add %r1, %r2, %r3”によって、対象レジスタのコンテンツ%r2及び%r3に加算を行ない、その結果を対象レジスタ%r1に格納する。従って、この命令は抽象表現“%r1=%r2+%r3”に対応する。この例では、抽象レジスタ%r1を、命令オペランド%r2及び%r3を表わす2つの部分式を含むadd表現で定義する。対象プログラム17に関連して、これらの部分式は他の前の対象命令に対応することができる、又は直接定数値(immediate constant values )のような現行命令の詳細を表わすことができる。
“add”命令を解析すると、新規の“+”IRノードが加算の抽象演算子に対応して生成される。“+”IRノードはオペランド(IRにおいて部分式ツリーとして表示され、多くの場合対象レジスタに保持される)を表わす他のIRノードへの参照を格納する。“+”ノードはそれ自体が、ノードが定義する値を有する対象レジスタ(%r1の抽象レジスタ、命令の宛先レジスタ)によって参照される。例えば、図2の中央から右側の部分は、X86命令“add %ecx, %edx”に対応するIRツリーを示している。
当該技術分野の当業者であれば理解するように、一つの実施形態では、トランスレータ19はオブジェクト指向の、C++のようなプログラミング言語を使用して具体化される。例えば、IRノードはC++オブジェクトとして具体化され、他のノードへの参照は、これらの他のノードに対応するC++オブジェクトへのC++参照として具体化される。従って、IRツリーは、互いに対する種々の参照を含むIRノード・オブジェクト群の集まりとして具体化される。
更に、説明中の実施形態において、IR生成には、一連の抽象レジスタが使用される。これらの抽象レジスタは対象アーキテクチャ(subject architecture)の特定機能に対応する。例えば、対象アーキテクチャの各物理レジスタに関して固有の抽象レジスタ(「対象レジスタ」)が存在する。同様に、対象アーキテクチャに設けられる各条件コード・フラグに対応する固有の抽象レジスタが存在する。抽象レジスタは、IR生成中のIRツリーのプレース・ホルダーとして機能する。例えば、対象命令シーケンスの所与のポイントでの対象レジスタ%r2の値は、対象レジスタ%r2の抽象レジスタに関連する特定のIR表現ツリーによって表わされる。一つの実施形態では、抽象レジスタはC++オブジェクトとして具体化され、C++オブジェクトは特定のIRツリーに、当該ツリーのルート・ノード・オブジェクト(root node object)へのC++参照を行なうことによって関連付けられる。
上に記載した例示としての命令シーケンスでは、トランスレータは、%r2及び%r3の値に対応するIRツリーを既に生成し、同時に“add ”命令に先行する対象命令を解析している。別の表現をすると、%r2及び%r3の値を計算する部分式は既にIRツリーとして表わされている。“add %r1,%r2,%r3”命令のIRツリーを生成する場合、新規の“+”ノードは、%r2及び%r3のIR部分ツリーへの参照を含む。
抽象レジスタの実施形態はトランスレータ・コード19及び変換済みコード21の両方におけるコンポーネントに分割される。トランスレータ19の内部では、「抽象レジスタ」はIR生成過程において使用されるプレース・ホルダーであるため、抽象レジスタはIRツリーに関連付けられ、このIRツリーで、特定の抽象レジスタが対応する対象レジスタの値が計算される。従って、トランスレータの抽象レジスタは、IRノード・オブジェクト(すなわちIRツリー)への参照を含むC++オブジェクトとして具体化される。抽象レジスタ・セットが参照する全てのIRツリーの集合は、ワーキングIRフォレストと呼ぶ(「フォレスト(forest)」と呼ばれるのは、フォレストが複数の抽象レジスタ・ルートを含み、各ルートを経由してIRツリーへの参照が行なわれるからである)。ワーキングIRフォレストは、対象コードの特定ポイントにおける対象プログラムの抽象操作のスナップ・ショットを表わす。
変換済みコード21内では、「抽象レジスタ」はグローバル・レジスタ・ストア内部の特定のロケーションであり、このグローバル・レジスタ・ストアに向かう対象レジスタ値、及びグローバル・レジスタ・ストアからの対象レジスタ値が実際の目的レジスタと同期する。別の構成として、一つの値がグローバル・レジスタ・ストアからロードされてしまうと、変換済みコード21の抽象レジスタが目的レジスタであると見なすことができ、この目的レジスタが対象レジスタ値を、変換済みコード21の実行の間であり、かつレジスタ・ストアにセーブ・バックされる前に、一時的に保持する。
上に記載するプログラム変換の一例を図2に示す。図2は、x86命令の2つの基本ブロックの変換、及び変換プロセスにおいて生成される該当するIRツリーを示している。図2の左側は、変換の間のトランスレータ19の実行パスを示している。ステップ151において、トランスレータ19が対象コードの第1基本ブロック153を目的コード21に変換し、次にステップ155において、当該目的コード21を実行する。目的コード21の実行が終了すると、ステップ157において、制御をトランスレータ19に返し、このステップでは、トランスレータが対象コード17の次の基本ブロック159を目的コード21に変換し、次に当該目的コード21をステップ161において実行し、これらの操作が繰り返される。
対象コードの第1基本ブロック153を目的コードに変換する過程において、トランスレータ19はIRツリー163を当該基本ブロック153に基づいて生成する。この場合、IRツリー163は、フラグが影響する命令であるソース命令“add %ecx, %edx”から生成される。IRツリー163を生成する過程において、4つの抽象レジスタがこの命令により定義される。4つの抽象レジスタとは、宛先抽象レジスタ%ecx167、フラグが影響する第1の命令パラメータ169、フラグが影響する第2の命令パラメータ171、及びフラグが影響する命令結果173である。“add”命令に対応するIRツリーは“+”演算子175(すなわち加算)であり、この演算子のオペランドは対象レジスタ%ecx177及び%edx179である。
従って、第1基本ブロック153をエミュレートすると、フラグが影響する命令のパラメータ及び結果を格納することによりフラグが保留状態に設定される。フラグが影響する命令は“add %ecx, %edx”である。命令のパラメータは、エミュレートされる対象レジスタ%ecx177及び%edx179の現行値である。対象レジスタ使用177,179の前に位置する“@ ”記号は、対象レジスタの値がグローバル・レジスタ・ストアから、%ecx及び%edxにそれぞれ対応するロケーションから取り出されることを意味するが、これは、これらの特定の対象レジスタへのロードが現行基本ブロックによって予め行われていなかったからである。次にこれらのパラメータ値は第1及び第2フラグ・パラメータ抽象レジスタ169,171に格納される。加算演算子175の結果はフラグ結果抽象レジスタ173に格納される。
IRツリーが生成された後、該当する目的コード21がIRに基づいて生成される。目的コード21を汎用IRから生成するプロセスは当該技術分野ではよく知られている。目的コードを変換済みブロックの最後に挿入して、フラグ結果173及びフラグ・パラメータ169,171の抽象レジスタを含む抽象レジスタをグローバル・レジスタ・ストア27にセーブする。目的コードを生成した後、このコードをステップ155において実行する。
図2は変換及び実行を交互に繰り返す例を示している。トランスレータ19はまず、変換済みコード21を第1基本ブロック153の対象命令17に基づいて生成し、次に基本ブロック153の変換済みコードを実行する。第1基本ブロック153の最後では、変換済みコード21は制御をトランスレータ19に返し、次にトランスレータが第2基本ブロック159を変換する。次に、第2基本ブロック159の変換済みコード21を実行する。第2基本ブロック159の実行の最後では、変換済みコードは制御をトランスレータ19に返し、次にトランスレータが次の基本ブロック159を変換し、このような操作が繰り返される。
従って、トランスレータ19で動作する対象プログラムは、交互に繰り返す形で実行される2つの異なるタイプのコード、すなわちトランスレータ・コード19及び変換済みコード21を有する。トランスレータ・コード19は実行時間の前にコンパイラによって、トランスレータ19の高レベルのソース・コードの内容に基づいて生成される。変換済みコード21は、実行時間全体を通じてトランスレータ・コード19により、プログラムの変換対象コード17に基づいて生成される。
対象プロセッサ・ステートの表示は同じようにして、トランスレータ19要素及び変換済みコード21要素に分割される。トランスレータ19は対象プロセッサ・ステートを、変数及び/又はオブジェクトのような種々の明示的なプログラミング言語デバイスに格納し、トランスレータをコンパイルするために使用するコンパイラは、ステート及びオペレーションをどのようにして目的コードの中で用いるかについて決定する。これに対して、変換済みコード21は対象プロセッサ・ステートを暗示的に、目的レジスタ及びメモリ・ロケーションに格納し、これらの目的レジスタ及びメモリ・ロケーションは、変換済みコード21の目的命令によって直接操作される。
例えば、グローバル・レジスタ・ストア27の低レベル表示は単なる割当てメモリ領域である。これは、変換済みコード21から抽象レジスタがどのように見え、変換済みコード21が抽象レジスタとどのように相互作用するのかについて、定義メモリ領域と種々の目的レジスタとの間のセーブ及び復元により示す。しかしながら、トランスレータ19のソース・コードにおいては、グローバル・レジスタ・ストア27は、高いレベルでアクセスし、操作することができるデータ・アレイ又はオブジェクトである。変換済みコード21に関しては、簡単のために高レベル表示は設けない。
幾つかの場合においては、トランスレータ19において静的であるか、あるいは統計的に判断することができる対象プロセッサ・ステートは、動的に計算されるのではなく、変換済みコード21に直接エンコードされる。例えば、トランスレータ19は、フラグが影響する最後の命令の命令タイプに特化される変換済みコード21を生成することができるが、これは、フラグが影響する最後の命令の命令タイプが変化した場合に、トランスレータが同じ基本ブロックの異なる目的コードを生成することを意味する。
トランスレータ19は、以下に記載するように、拡張基本ブロック、アイソブロック(isoblock)、グループ・ブロック、及びキャッシュ変換ステートの最適化を特に容易にする各基本ブロック変換に対応するデータ構造を含む。図3はこのような基本ブロック・データ構造30を示しており、この構造は、対象アドレス31、目的コード・ポインタ33(すなわち、変換済みコードの目的アドレス)、変換ヒント34、エントリー及びエグジット条件35、プロファイリング・メトリック37、先行基本ブロック38、及び後続基本ブロック39のデータ構造への参照、及びエントリー・レジスタ・マップ40を含む。図3は更に、基本ブロック・キャッシュ23を示しており、この基本ブロック・キャッシュは、対象アドレス毎にインデックスの付された基本ブロック・データ構造、例えば30,41,42,43,44...の集まりである。一つの実施形態では、特定の変換済み基本ブロックに対応するデータは、C++オブジェクトに格納することができる。トランスレータは新規の基本ブロック・オブジェクトを基本ブロックの変換時に生成する。
基本ブロックの対象アドレス31は、対象プログラム17のメモリ空間における当該基本ブロックの開始アドレスであり、対象プログラム17が対象アーキテクチャで動作していた場合に基本ブロックが位置したと考えられるメモリ・ロケーションを指す。このアドレスは対象開始アドレス(subject starting address)とも呼ばれる。各基本ブロックは特定の範囲の対象アドレスに対応する(一つのアドレスが各対象命令に対応する)が、対象開始アドレスは基本ブロックの第1命令の対象アドレスである。
基本ブロックの目的アドレス33は、目的プログラムにおける変換済みコード21のメモリ・ロケーション(開始アドレス)である。目的アドレス33は、目的コード・ポインタ又は目的開始アドレスとも呼ばれる。変換済みブロックを実行するために、トランスレータ19は目的アドレスを関数ポインタとして処理し、この関数ポインタを修飾参照して変換済みコードを呼び出す(変換済みコードに制御を渡す)。
基本ブロック・データ構造30,41,42,43,...は基本ブロック・キャッシュ23に格納され、このキャッシュは対象アドレスによって体系化される基本ブロック・オブジェクトの格納場所である。基本ブロックの変換済みコードの実行が終了すると、このコードは制御をトランスレータ19に返し、更に、基本ブロックの宛先(後続)対象アドレス31の値をトランスレータに返す。後続基本ブロックが既に変換されているか否かを判断するために、トランスレータ19は宛先対象アドレス31を基本ブロック・キャッシュ23における基本ブロック群(すなわち、既に変換されているブロック群)の対象アドレス31と比較する。未だ変換されていない基本ブロック群を変換して、実行する。既に変換されている(そして以下に議論するように、同程度のエントリー条件を有する)基本ブロック群を単純に実行する。時間が経つと、現われる基本ブロック群のうちの多くの基本ブロックが既に変換されてしまっており、これによって変換コストの増分が小さくなる。従って、変換を必要とするブロックが段々少なくなるため、トランスレータ19は時間が経過するとともに高速になる。
拡張基本ブロック
例示としての実施形態に従って適用される一つの最適化では、コード生成の範囲を、「拡張基本ブロック」と呼ばれる方法により広げる。基本ブロックAが後続ブロックを一つ(例えば基本ブロックB)しか持たない場合においては、トランスレータは静的にBの対象アドレスを求めることができる(Aがデコードされている場合)。このような場合においては、基本ブロックA及びBを結合して単一ブロック(A’)とし、このブロックA’を拡張基本ブロックと呼ぶ。別の表現をすれば、拡張基本ブロック機構は、静的に求めることができる宛先を有する無条件ジャンプに適用することができる。ジャンプが条件付である場合、あるいは宛先を静的に求めることができない場合、別個の基本ブロックを形成する必要がある。拡張基本ブロックは依然として形式的には基本ブロックである。何故なら、AからBへの中間ジャンプを取り除いた後、ブロックA’のコードは単一フローの制御しか持たないため、同期はABの境界では必要ではないからである。
AがBを含む複数の後続ブロックを有することができる場合でも、拡張基本ブロック群を使用して特定の実行を行なうためにAをBに拡張することができ、この場合、Bは実際の後続ブロックであり、Bのアドレスは静的に求めることができる。
静的に求めることができるアドレスは、トランスレータがデコード時に求めることができるアドレスである。ブロックのIRフォレストを作成している間、IRツリーを、宛先アドレス抽象レジスタに関連付けられる宛先対象アドレスに関して作成する。宛先アドレスIRツリーの値を静的に求めることができる(すなわち、動的に変化するか、あるいは実行時の対象レジスタ値によって変化することがない)場合、後続ブロックは静的に求めることができる。例えば、無条件ジャンプ命令の場合では、宛先アドレス(すなわち、後続ブロックの対象開始アドレス)はジャンプ命令自体において暗示的であり、ジャンプ命令の対象アドレスに、ジャンプ命令の中でエンコードされるオフセットを加えた値は、宛先アドレスに等しい。同じように、定数畳み込み(constant folding)(例えば、X+(2+3)⇒X+5)及び式畳み込み(expression folding)(例えば、(X*5)*10⇒X*50)の最適化処理により、このような手法を使用しなかった場合の「動的」宛先アドレスを静的に求めることができるようになる。従って、宛先アドレスの計算では、定数値を宛先アドレスIRから抽出する。
拡張基本ブロックA’が生成されると、トランスレータは次にこのブロックを、IR生成、最適化、及びコード生成を行なうときの他の全ての基本ブロックと同じように処理する。コード生成アルゴリズムはより広い範囲(すなわち、結合された基本ブロックA及びBのコード)で動作するため、トランスレータ19は更に最適なコードを生成する。
当該技術分野の当業者であれば理解できることであるが、デコーディングは個々の対象命令を対象コードから抽出するプロセスである。対象コードはフォーマットされていないバイト・ストリーム(すなわち、メモリにおけるバイトの集まり)として格納される。可変長の命令を含む対象アーキテクチャ(例えばx86)の場合では、デコーディングにはまず、命令境界を識別する必要があり、固定長の命令アーキテクチャの場合では、命令境界の識別は普通に行われる操作である(例えば、MIPS単位で、各4バイトが一命令となる)。次に、対象命令フォーマットを、所与の命令を構成するバイト群に適用して命令データ(すなわち、命令タイプ、オペランド・レジスタ番号、即値フィールド、及び命令にエンコードされる他の全ての情報)を抽出する。既知アーキテクチャのマシン命令をフォーマットされていないバイト・ストリームから当該アーキテクチャの命令フォーマットを使用してデコードするプロセスは、当該技術分野では良く知られている。
図4は拡張基本ブロックの生成の様子を示している。拡張基本ブロックを形成する候補である一連の基本ブロックの構成要素は、最先行の候補基本ブロック(A)がデコードされると検出される。トランスレータ19がAの後続ブロック(B)を静的に求めることができることを検出すると(51)、トランスレータはBの開始アドレスを求め(53)、次にデコード・プロセスをBの開始アドレスから再開する。Bの後続ブロック(C)を静的に求めることができると判断すると(55)、デコード・プロセスはCの開始アドレスに進み、このようなプロセスが繰り返される。勿論、後続ブロックを静的に求めることができない場合は、通常の変換及び実行を再開する(61,63,65)。
全ての基本ブロックのデコードの間、ワーキングIRフォレストは、現行ブロックの後続ブロックの対象アドレス31(すなわち、宛先対象アドレスであり、トランスレータは宛先アドレスの専用抽象レジスタを有する)を計算するためのIRツリーを含む。拡張基本ブロックの場合、新規の基本ブロックの構成要素のそれぞれがデコード・プロセスによって取り込まれると中間ジャンプが取り除かれているという現象を補償するために、当該ブロックの対象アドレスの計算に関するIRツリーを除去する(54(図4))。別の表現をすれば、トランスレータ19が静的にBのアドレスを計算し、デコードがBの開始アドレスから再開されると、Bの対象アドレス31(このアドレスはAのデコードの過程において作成されている)の動的計算に対応するIRツリーを除去し、デコードがCの開始アドレスに進むと、Cの対象アドレスに対応するIRツリーを除去し(59)、同様な操作を繰り返す。IRツリーの「除去(pruning )」は、宛先アドレス抽象レジスタは依存するが、他の抽象レジスタは依存しない全てのIRノードを除去することを意味する。別の表現をすると、除去によってIRツリーと宛先抽象レジスタとの間のリンクが解除され、同じIRツリーへの他の全てのリンクは影響を受けることがない。特定の場合においては、除去されるIRツリーには、別の抽象レジスタが依存する可能性もあり、この場合、IRツリーは対象プログラムの実行動作を依然として維持している。
コードの爆発的な増加を防止するために(従来から、このようなコード特化に対する軽減要素である)、トランスレータは拡張基本ブロックを所定の最大数の対象命令に制限する。一つの実施形態では、拡張基本ブロックは最大200の対象命令に制限される。
アイソブロック(Isoblock)
図示の実施形態において用いる別の最適化は所謂“アイソブロッキング”である。この方法によれば、基本ブロック群の変換は互換性リストにおいてパラメータ化又は特化され、この互換性リストは対象プロセッサ・ステート及びトランスレータ・ステートを記述する一連の可変条件である。互換性リストは対象アーキテクチャ毎に異なり、異なるアーキテクチャ機能を考慮に入れている。特定の基本ブロック変換のエントリー及びエグジットにおける互換性条件の実際の値を、それぞれエントリー条件及びエグジット条件と呼ぶ。
実行が既に変換されている基本ブロックに達し、前の変換のエントリー条件が現行のワーキング条件(すなわち、前のブロックのエグジット条件)とは異なる場合、基本ブロックを再度変換して、今回は現行のワーキング条件に基づく必要がある。結果として、同じ対象コード基本ブロックがこの時点で複数の目的コード変換によって表示される。同じ基本ブロックのこれらの異なる変換をアイソブロックと呼ぶ。
アイソブロックをサポートするために、各基本ブロック変換に関連するデータは1セットのエントリー条件35及び1セットのエグジット条件36を含む(図3)。一つの実施形態では、基本ブロック・キャッシュ23は、まず対象アドレス31により、次にエントリー条件35,36により体系化される(図3)。別の実施形態では、トランスレータが基本ブロック・キャッシュ23に対象アドレス31についてクエリーを出すと、クエリーは複数の変換済み基本ブロック(アイソブロック)を返すことができる。
図5はアイソブロックの使用方法を示している。第1の変換済みブロックの実行の最後では、変換済みコード21は次のブロック(すなわち後続ブロック)の対象アドレスを計算して返す(71)。次に制御が、破線73で境界を定めているように、トランスレータ19に返される。トランスレータ19では、ステップ75において、基本ブロック・キャッシュ23に、返された対象アドレス31を使用してクエリーを出す。基本ブロック・キャッシュは、同じ対象アドレス31を有する0、1、又は2以上の基本ブロック・データ構造を返すことができる。基本ブロック・キャッシュ23が基本ブロック・データ構造を返さない場合(この基本ブロックが未だ変換されていないことを意味する)、基本ブロックをステップ77においてトランスレータ19が変換する必要がある。基本ブロック・キャッシュ23が返す各データ構造は、対象コードの同じ基本ブロックの異なる変換(アイソブロック)に対応する。判定ひし形79に示すように、(第1の変換済みブロックの)現行のエグジット条件が、基本ブロック・キャッシュ23が返すデータ構造のいずれのエントリー条件とも一致しない場合、基本ブロックを再度ステップ81において変換し、今回はこれらのエグジット条件でパラメータ化する必要がある。現行のエグジット条件が、基本ブロック・キャッシュ23が返すデータ構造のうちの一つのデータ構造のエントリー条件に一致する場合、当該変換は互換性があり、当該変換はステップ83において、再変換を行うことなく実行することができる。例示としての実施形態では、トランスレータ19は互換性のある変換済みブロックを、目的アドレスを関数ポインタとして修飾参照することにより実行する。
上に述べたように、基本ブロック変換は互換性リストでパラメータ化することが好ましい。次に、例示としての互換性リストについてX86及びPowerPCアーキテクチャの両方に関して記載する。
X86アーキテクチャに関する例示としての互換性リストでは、(1)対象レジスタ間の遅延伝播、(2)重複抽象レジスタ、(3)保留中の条件コード・フラグが影響する命令のタイプ、(4)条件コード・フラグが影響する命令パラメータ間の遅延伝播、(5)ストリング・コピー操作の方向、(6)対象プロセッサの浮動小数点ユニット(FPU:floating point unit )モード、及び(7)セグメント・レジスタの修飾の表示を行なう。
X86アーキテクチャに関する互換性リストでは、レジスタ・エイリアス(register aliasing )とも呼ばれる、トランスレータによる対象レジスタ間の全ての遅延伝播の表示を行なう。レジスタ・エイリアスは、トランスレータが、2つの対象レジスタが同じ値を基本ブロック境界に含むことを認識する場合に生じる。対象レジスタ値が依然として同じである限り、該当する抽象レジスタ群のうちの一つの抽象レジスタのみが、このレジスタをグローバル・レジスタ・ストアにセーブすることによって同期する。セーブされた対象レジスタが上書きされるまで、セーブされていないレジスタへの参照は、単にセーブされたレジスタを使用するか、あるいはコピーする(move命令により)。これにより、変換済みコードにおける2回のメモリ・アクセス(save+restore)を回避する。
X86アーキテクチャに関する互換性リストでは、重複抽象レジスタのいずれが現在定義されているかに関する表示を行なう。特定の場合においては、対象アーキテクチャは複数の重複対象レジスタを含み、これらの重複対象レジスタは、トランスレータが複数の重複抽象レジスタを使用して表示する。例えば、可変幅の対象レジスタは、複数の重複抽象レジスタを使用して、各アクセスサイズに一つのレジスタが対応する形で表示される。例えば、x86“EAX”レジスタには、次の対象レジスタ群のうちのいずれかを使用してアクセスすることができ、この場合、対象レジスタ群の各々は該当する抽象レジスタを有する。対象レジスタ群とは、EAX(bits 31...0),AX(bits 15...0),AH(bits 15...8),及びAL(bits 7...0)”である。
X86アーキテクチャに関する互換性リストでは、各整数及び浮動小数点条件コード・フラグに関して、フラグ値が正規化されているか、あるいは保留になっているか否かについての表示、及び保留になっている場合は保留中のフラグが影響する命令のタイプの表示を行なう。
X86アーキテクチャに関する互換性リストでは、条件コード・フラグが影響する命令パラメータに関するレジスタ・エイリアスの表示を行なう(特定の対象レジスタが、フラグが影響する命令パラメータの値を保持し続ける場合、又は第2パラメータの値が第1パラメータと同じである場合)。互換性リストではまた、第2パラメータが小さな定数(すなわち直前の命令候補)であるか否かについての表示、及び第2パラメータが小さな定数である場合のその値の表示を行なう。
X86アーキテクチャに関する互換性リストでは、対象プログラムにおけるストリング・コピー操作の現在の方向の表示を行なう。この条件フィールドは、ストリング・コピー操作がメモリにおいて上に、又は下に向いているか否かを示す。これは、“strcpy()”関数コールのコード特化を、関数の方向を表わすアーギュメントで変換をパラメータ化することによりサポートする。
X86アーキテクチャに関する互換性リストでは、対象プロセッサのFPUモードの表示を行なう。FPUモードは、対象浮動小数点命令が32ビット又は64ビット・モードで動作するか否かを示す。
X86アーキテクチャに関する互換性リストでは、セグメント・レジスタの修飾の表示を行なう。全てのX86命令メモリ参照は、6つのメモリ・セグメント・レジスタ、すなわちコード・セグメント(CS:code segment),データ・セグメント(DS:data segment),スタック・セグメント(SS:stack segment ),エキストラ・データ・セグメント(ES:extra data segment),汎用セグメント(FS:general purpose segment ),汎用セグメント(GS:general purpose segment )のうちの一つに基づく。通常の環境では、アプリケーションはセグメント・レジスタ群を修飾することはない。従って、コード生成は、デフォルト設定により、セグメント・レジスタ値が依然として一定であるという仮定に基づいて特化される。しかしながら、プログラムによりプログラムのセグメント・レジスタ群を修飾することができ、この場合、該当するセグメント・レジスタ互換ビットを設定して、トランスレータに汎用化メモリ・アクセスに関するコードを適切なセグメント・レジスタのダイナミック値を使用して生成させる。
PowerPCアーキテクチャに関する互換性リストの例示としての実施形態では、(1)エンコードされた(mangled )レジスタ、(2)リンク値伝播(link value propagation)、(3)保留中の条件コード・フラグが影響する命令のタイプ、(4)条件コード・フラグが影響する命令パラメータ間の遅延伝播、(5)条件コード・フラグ値のエイリアス、及び(6)サマリー・オーバーフローのフラグ同期ステートの表示を行なう。
PowerPCアーキテクチャに関する互換性リストでは、エンコードされたレジスタの表示を行なう。対象コードが、ベース・アドレスの対象レジスタを使用する複数の連続するメモリ・アクセスを含む場合、トランスレータはこれらのメモリ・アクセスを、エンコードされたレジスタを使用して変換することができる。対象プログラム・データがターゲット・メモリにおいて、このデータが対象メモリに含まれていたときのアドレスと同じアドレスには位置しない場合、トランスレータは、対象コードが計算する全てのメモリ・アドレスに目的オフセットを含ませる必要がある。対象レジスタは対象ベース・アドレスを含むが、エンコードされた目的レジスタは当該対象ベース・アドレスに対応する目的アドレスを含む(すなわち、対象ベース・アドレス+目的オフセット)。レジスタをエンコードする場合、メモリ・アクセスは、対象コード・オフセットを、エンコードされたレジスタに格納された目的ベース・アドレスに直接加えることにより更に効率的に変換することができる。これに比べると、エンコードされたレジスタ機構が無い場合、このシナリオでは、各メモリ・アクセスの目的コードを、空間及び実行時間の両方を使って更に操作する必要がある。互換性リストは、抽象レジスタがあるとすれば、いずれの抽象レジスタがエンコードされるかを示す。
PowerPCアーキテクチャに関する互換性リストでは、リンク値伝播の表示を行なう。リーフ関数(すなわち、他の関数をコールしない関数)の場合、関数本体をコール/リターン・サイトに拡張する(上に議論した拡張基本ブロック機構の場合と同じように)ことができる。従って、関数本体、及び関数のリターンに続くコードがともに変換される。これは関数リターン特化(function return specialization)とも呼ぶ。何故なら、このような変換は関数のリターン・サイトからのコードを含むため、関数のリターン・サイトに特化されるからである。特定のブロック変換にリンク値伝播を使用したか否かがエグジット条件に反映される。従って、トランスレータが、リンク値伝播を使用した変換が行われるブロックを処理する場合、トランスレータは現行のリターン・サイトが前のリターン・サイトと同じになるか否かについて見積もる必要がある。関数群は、これらの関数がコールされる同じロケーションに戻るため、コール・サイト及びリターン・サイトは事実上同じである(1つ、又は2つの命令によるオフセットを含む)。従って、トランスレータは、リターン・サイト群が同じであるか否かを、それぞれのコール・サイトを比較することにより判断するが、この操作は、それぞれの先行ブロック(関数ブロックの前の実行及び現在の実行の)の対象アドレスを比較する操作と等価である。従って、リンク値伝播をサポートする実施形態では、各基本ブロック変換に関連するデータは先行ブロック変換(又は、先行ブロックの対象アドレスの他の特定の表示)への参照を含む。
PowerPCアーキテクチャに関する互換性リストでは、各整数及び浮動小数点条件コード・フラグに関して、フラグ値が正規化されているか、あるいは保留になっているか否かについての表示、及び保留になっている場合は保留中のフラグが影響する命令のタイプの表示を行なう。
PowerPCアーキテクチャに関する互換性リストでは、フラグが影響する命令パラメータに関するレジスタ・エイリアスの表示を行なう(フラグが影響する命令パラメータ値が偶然、対象レジスタにおいて生きている場合、又は第2パラメータの値が第1パラメータと同じである場合)。互換性リストではまた、第2パラメータが小さな定数(すなわち直前の命令候補)であるか否かについての表示、及び第2パラメータが小さな定数である場合のその値の表示を行なう。
PowerPCアーキテクチャに関する互換性リストでは、PowerPC条件コード・フラグ値に関するレジスタ・エイリアスの表示を行なう。PowerPCアーキテクチャは、明示的に全セットのPowerPCフラグを汎用(対象)レジスタにロードする命令を含む。対象レジスタにおける対象フラグ値のこの明示的な表示は、トランスレータが行なう条件コード・フラグ・エミュレーション最適化の邪魔になる。互換性リストでは、フラグ値が対象レジスタにおいて生きているか否かについての表示、及び含まれている場合にはどのレジスタにおいて生きているのかについての表示を行なう。IR生成の間、このような対象レジスタがフラグ値を保持している間のこの対象レジスタへの参照は、該当する抽象レジスタへの参照に変換される。この機構によって、対象フラグ値を目的レジスタにおいて明示的に計算し、目的レジスタに格納する必要がなくなり、これによって今度は、トランスレータによる標準条件コード・フラグ最適化の適用が可能になる。
PowerPCアーキテクチャに関する互換性リストでは、サマリー・オーバーフロー同期の表示を行なう。このフィールドは、8個のサマリー・オーバーフロー条件ビットのうちのいずれのビットがグローバル・サマリー・オーバーフロー・ビットに追随しているかを示す。グローバル・サマリー・オーバーフローが設定される場合に、PowerPCの8個の条件フィールドのうちの一つのフィールドが更新されると、このフィールドが、特定の条件コード・フィールドの該当するサマリー・オーバーフロー・ビットにコピーされる。
変換ヒント(Translation Hints )
例示としての実施形態において実行する別の最適化では、図3の基本ブロック・データ構造の変換ヒント34を用いる。この最適化は、特定の基本ブロックに固有であるが、当該ブロックの全ての変換に関して同じである静的基本ブロック・データが存在するという認識に基づいて進行する。計算コストが高く付くような特定のタイプの静的データの場合、トランスレータが、該当するブロックの第1変換の間にデータを一旦計算し、次に結果を格納してこの結果を同じブロックに対する将来時点での変換に用いると一層効率的である。このデータは同じブロックの全ての変換に関して同じであるため、このデータによって変換がパラメータ化されることはなく、従って、このデータが正式にブロックの(上に説明した)互換性リストの一部となることはない。しかしながら、コストが高く付く静的データはそれでも、各基本ブロック変換に関連するデータに格納される。何故なら、データをセーブする方が再計算するよりも安価であるからである。同じブロックの後の時点の変換において、トランスレータ19が前の変換を再使用することができない場合でも、トランスレータ19はこれらの「変換ヒント」(すなわち、キャッシュされた静的データ)の利点を生かして第2の後の時点の変換に要する変換コストを下げることができる。
一つの実施形態では、各基本ブロック変換に関連するデータは変換ヒントを含み、これらの変換ヒントは、当該ブロックの第1変換の間に一旦計算され、次に各後続の変換に際してコピーされる(あるいは参照される)。
例えば、C++により具体化されるトランスレータ19においては、変換ヒントはC++オブジェクトとして具体化され、この場合、同じブロックの異なる変換に対応する基本ブロック・オブジェクトはそれぞれ、同じ変換ヒント・オブジェクトへの参照を格納する。別の構成として、C++により具体化されるトランスレータにおいては、基本ブロック・キャッシュ23は、(変換当たりではなく)対象基本ブロック当たり一つの基本ブロック・オブジェクトを含むことができ、この場合、このような各オブジェクトは、該当する変換ヒント・オブジェクトへの参照を含むか、あるいは保持する。このような基本ブロック・オブジェクトはまた、エントリー条件によって体系化される変換オブジェクトであって、当該ブロックの異なる変換に対応する変換オブジェクトへの複数の参照を含む。
X86アーキテクチャに関する例示としての変換ヒントでは、(1)初期命令プレフィックス、及び(2)初期リピート・プレフィックスの表示を行なう。X86アーキテクチャに関するこのような変換ヒントでは特に、どの位多くのプレフィックスをブロックの第1命令が有するのかについての表示を行なう。幾つかのX86命令は、命令の動作を修飾するプレフィックスを有する。このアーキテクチャ機能により、X86命令ストリームをデコードするのが困難になる(すなわちコストが高く付く)。一旦、初期プレフィックスの数がブロックの第1デコード操作の間に求まると、当該値をトランスレータ19が変換ヒントとして格納するため、同じブロックに関する後続の変換では、改めてこの数を求める必要がない。
X86アーキテクチャに関する変換ヒントでは更に、ブロックの第1命令がリピート・プレフィックスを有するか否かについての表示を行なう。ストリング操作のような特定のX86命令は、プロセッサに指示して当該命令を複数回実行させるリピート・プレフィックスを有する。変換ヒントは、このようなプレフィックスが含まれているか否かを示し、含まれている場合にはその値を示す。
一つの実施形態では、各基本ブロックに関連する変換ヒントは更に、当該基本ブロックに対応するIRフォレスト全体を含む。これにより、フロントエンドが実行するデコーディング及びIR生成の全てを効果的にキャッシュすることができる。別の実施形態では、変換ヒントはIRフォレストを、IRフォレストが最適化される前に存在するときに含む。別の実施形態では、IRフォレストは変換ヒントとしてキャッシュされないため、変換済みプログラムのメモリ・リソースを使わずに済む。
グループ・ブロック
例示としてのトランスレータの実施形態において実行される別の最適化は、全ての抽象レジスタを各変換済み基本ブロックの実行の最後に同期させる必要から生じるプログラム・オーバーヘッドを無くすことを目的とする。この最適化はグループ・ブロック最適化と呼ばれる。
上に議論したように、基本ブロック・モードでは(例えば図2)、ステートを一つの基本ブロックから次の基本ブロックに、全ての変換済みコード・シーケンスにアクセスすることができるメモリ領域、すなわちグローバル・レジスタ・ストア27を使用して渡す。グローバル・レジスタ・ストア27は抽象レジスタ群のレポジトリであり、これらの抽象レジスタの各々は、特定の対象レジスタの値、又は他の対象アーキテクチャ機能に対応し、かつ特定の対象レジスタの値、又は他の対象アーキテクチャ機能をエミュレートする。変換済みコード21を実行している間、抽象レジスタ群は目的レジスタ群に保持されるため、抽象レジスタ群は命令を構成することができる。トランスレータ・コード21を実行している間、抽象レジスタ値はグローバル・レジスタ・ストア27又は目的レジスタ群15に格納される。
従って、図2に示すような基本ブロック・モードでは、全ての抽象レジスタは各基本ブロックの最後で2つの理由により同期する必要がある。すなわち、(1)制御がトランスレータ・コード19に戻り、これによって全ての目的レジスタを上書きすることができ、(2)コード生成では、1度に一つの基本ブロックしか処理しないため、トランスレータ19は、全ての抽象レジスタ値が生きているという前提を必要とし(すなわち、後続の基本ブロックにおいて使用される)、従ってこれらの値はセーブされる必要がある。グループ・ブロック最適化機構の目標は、通過が頻繁に行われる基本ブロック境界を横切る際の同期を、複数の基本ブロックを全体として連続するブロック群に変換することにより減らすことである。複数の基本ブロックを変換してともに纏めることにより、ブロック境界での同期を無くすことはできないが、最小化することができる。
グループ・ブロック・コンストラクションは、現行ブロックのプロファイリング・メトリックがトリガーしきい値に達するとトリガーされる。このブロックはトリガー・ブロックと呼ばれる。コンストラクションは、次のステップに分割される(図6)。(1)メンバー・ブロック群の選択(71)、(2)メンバー・ブロック群の順番の決定(73)、(3)グローバル・デッド・コードの除去(75)、(4)グローバル・レジスタの割り当て(77)、及び(5)コードの生成(79)。第1ステップ71では、プログラムの制御フロー・グラフの深さ優先探索(DFS:depth-first search)巡回をトリガー・ブロックから開始し、かつインクルージョンしきい値(inclusion threshold )及び最大数制限で調整する形で実行することによりグループ・ブロックに含まれるべきブロックの組を識別する。第2ステップ73では、ブロックの組の順番を決め、グループ・ブロックを通るクリティカル・パスを識別して、同期コードを最小化し、かつ分岐を減らす効率的なコード・レイアウトを可能にする。第3ステップ75及び第4ステップ77では最適化を行なう。最終ステップ79では今度は、全てのメンバー・ブロックの目的コードを生成して、効率的なコード・レイアウトを効率的なレジスタ割り当てにより生成する。
グループ・ブロックのコンストラクション及びグループ・ブロックからの目的コードの生成において、トランスレータ・コード19は図6に示すステップを実行する。トランスレータ19が、既に変換されている基本ブロックを処理する段になると、当該ブロックを実行する前に、トランスレータ19はブロックのプロファイリング・メトリック37(図3)をトリガーしきい値と照合する。トランスレータ19は、基本ブロックのプロファイリング・メトリック37がトリガーしきい値を超えるとグループ・ブロック生成を開始する。トランスレータ19は、グループ・ブロックのメンバーを、トリガー・ブロックから開始し、かつインクルージョンしきい値及び最大数制限で調整する形で制御フロー・グラフを巡回することにより識別する。次に、トランスレータ19はメンバー・ブロック群の順番を生成し、これによりグループ・ブロックを通過するクリティカル・パスを識別する。次に、トランスレータ19はグローバル・デッド・コードの除去を実行し、トランスレータ19は各メンバー・ブロックに関するレジスタの生死情報(register liveness information )を、各ブロックに対応するIRを使用して収集する。次に、トランスレータ19はグローバル・レジスタ割り当てを、一様なレジスタ・マッピング(uniform register mapping)の部分セットを全てのメンバー・ブロックに関して定義するアーキテクチャ固有ポリシーに従って実行する。最後に、トランスレータ19は各メンバー・ブロックの目的コードを、グローバル・レジスタ割り当て制約に従い、かつレジスタの生死解析を使用して順番に生成する。
上に述べたように、各基本ブロックに関連するデータはプロファイリング・メトリック37を含む。一つの実施形態では、プロファイリング・メトリック37は実行回数であり、これはトランスレータ19が、特定の基本ブロックが実行された回数をカウントすることを意味する。本実施形態では、プロファイリング・メトリック37は整数カウント・フィールド(カウンタ)として表示される。別の実施形態では、プロファイリング・メトリック37は実行時間であり、これはトランスレータ19が、特定の基本ブロックの全ての実行に関する実行時間の動作合計を維持することを意味し、この操作は、例えば基本ブロックの最初及び最後にコードを埋め込んで、ハードウェア又はソフトウェア・タイマーをそれぞれ開始し、終了することにより行なう。本実施形態では、プロファイリング・メトリック37は合計実行時間の特定の表示(タイマー)を使用する。別の実施形態では、トランスレータ19は、各先行基本ブロック及び/又は各後続基本ブロックに対応するように、各基本ブロックの複数セットのプロファイリング・メトリック37を格納するため、異なるプロファイリング・データが異なる制御パスに関して維持される。各トランスレータ・サイクル(すなわち、変換済みコード21の複数の実行の間のトランスレータ・コード19の実行)において、適切な基本ブロックのプロファイリング・メトリック37が更新される。
グループ・ブロック群をサポートする実施形態では、各基本ブロックに関連するデータは更に、既知の先行ブロック群及び後続ブロック群の基本ブロック・オブジェクトへの参照38,39を含む。これらの参照はともに、全ての既に実行されている基本ブロックの制御フロー・グラフを構成する。グループ・ブロック形成の間、トランスレータ19は、この制御フロー・グラフを巡回して、どの基本ブロックを形成中のグループ・ブロックに含めるかを決定する。
図示の実施形態におけるグループ・ブロック形成は3つのしきい値、すなわちトリガーしきい値、インクルージョンしきい値、及び最大数制限に基づく。トリガーしきい値及びインクルージョンしきい値は、各基本ブロックのプロファイリング・メトリック37を参照する。各トランスレータ・サイクルでは、次の基本ブロックのプロファイリング・メトリック37がトリガーしきい値と比較される。メトリック37がトリガーしきい値に一致する場合、グループ・ブロック形成が開始される。次に、インクルージョンしきい値を使用してグループ・ブロックの範囲を、どの後続基本ブロック群をグループ・ブロックに含めるかを識別することにより決定する。最大数制限は、一つのグループ・ブロックに含まれる基本ブロックの数に対する上限を、全てのグループ・ブロックに関して定義する。
トリガーしきい値が基本ブロックAに関して一致すると、新規のグループ・ブロックがAをトリガー・ブロックとして形成される。次に、トランスレータ19は定義巡回(definition traversal)を開始する、すなわちAの後続ブロックの巡回を制御フロー・グラフにおいて行って、含められる他のメンバー・ブロック群を識別する。巡回が所与の基本ブロックに達すると、このブロックのプロファイリング・メトリック37をインクルージョンしきい値と比較する。メトリック37がインクルージョンしきい値に一致する場合、当該基本ブロックを含めるためにマークし、このブロックの後続ブロックに対する巡回を継続する。ブロックのメトリック37がインクルージョンしきい値を下回る場合、当該ブロックを除去し、このブロックの後続ブロックは巡回しない。巡回が終了すると(すなわち、全てのパスが除去されたブロック(excluded block)に達するか、あるいは含められたブロック(included block)に戻るサイクルに達するか、あるいは最大数制限が満たされる)、トランスレータ19は新規のグループ・ブロックを含められた基本ブロック群(included basic blocks )の全てに基づいて構築する。
アイソブロック群及びグループ・ブロック群を使用する実施形態では、制御フロー・グラフはアイソブロック群のグラフであり、これは、グループ・ブロック生成のために、同じ対象ブロックの異なるアイソブロックが異なるブロックとして処理されることを意味する。従って、同じ対象ブロックの異なるアイソブロックのプロファイリング・メトリック37は合計されない。
別の実施形態では、アイソブロック群を基本ブロック変換に使用せずに、グループ・ブロック変換に使用するが、これは、グループではない基本ブロックの変換は一般化されない(エントリー条件に特化されない)ことを意味する。本実施形態では、基本ブロックのプロファイリング・メトリックを各実行のエントリー条件によって分離するため、異なるプロファイリング情報が各理論アイソブロック(すなわち、異なる各セットのエントリー条件)に関して維持される。本実施形態では、各基本ブロックに関連するデータはプロファイリング・リストを含み、このリストの各メンバーは3項目セットであり、(1)エントリー条件から成るセット、(2)該当するプロファイリング・メトリック、及び(3)該当する後続ブロック群から成るリストを含む。このデータは、基本ブロックへの各セットのエントリー条件に関するプロファイリング及び制御パス情報を維持するが、実際の基本ブロック変換はこれらのエントリー条件に特化されない。本実施形態では、トリガーしきい値を基本ブロックのプロファイリング・メトリック・リスト内部の各プロファイリング・メトリックと比較する。制御フロー・グラフを巡回する場合、所与の基本ブロックのプロファイリング・リストの各要素は制御フロー・グラフの個別ノードとして処理される。従って、インクルージョンしきい値は、ブロックのプロファイリング・リストの各プロファイリング・メトリックと比較される。本実施形態では、グループ・ブロック群は、ホットな対象ブロックの特定のホットなアイソブロック(特定のエントリー条件に特化された)に関して生成されるが、これらの同じ対象ブロック群の他のアイソブロック群は、これらのブロックの汎用(アイソブロックではない)変換を使用して実行する。
定義巡回の後、トランスレータ19は図6のステップ73で順番付け巡回(ordering traversal)を実施して、メンバー・ブロック群が変換される順番を決定する。メンバー・ブロック群の順番は変換済みコード21の命令キャッシュ動作(ホット・パスは連続する必要がある)及びメンバー・ブロック境界において必要な同期(同期はホット・パスに沿って最小化する必要がある)の両方に影響を及ぼす。一つの実施形態では、トランスレータ19は、実行回数により順番付けされた深さ優先探索(DFS)アルゴリズムを使用して順番付け巡回を実施する。巡回は最大実行回数を有するメンバー・ブロックから開始する。巡回対象メンバー・ブロックが複数の後続ブロックを有する場合、相対的に大きな実行回数を有する後続ブロックに対してまず巡回が行なわれる。
当該技術分野の当業者であれば、グループ・ブロック群は内部制御分岐、複数のエントリー・ポイント、及び/又は複数のエグジット・ポイントを有することができるため、正式な基本ブロックではないことが分かるであろう。
一旦、グループ・ブロックが形成されてしまうと、更に別の最適化をこのブロックに適用することができるが、この操作は本明細書では「グローバル・デッド・コードの除去(global dead code elimination)」と呼ばれる。このようなグローバル・デッド・コードの除去では、生死解析(liveness analysis )に関する技術を用いる。グローバル・デッド・コードの除去は、一つのグループの基本ブロックに対応する中間表現(IR)から余計な作業を取り除くプロセスである。
一般的に、対象プロセッサ・ステートは変換範囲境界に同期する必要がある。対象レジスタのような一つの値は、値の定義から始まり、再定義(上書き)される前の値の最後の使用で終了するコード範囲に渡って「生きている(live)」と考えられる。従って、値(例えば、IR生成に関連する一時値、コード生成に関連する目的レジスタ、又は変換に関連する対象レジスタ)の使用及び定義に関する解析は、当該技術分野では生死解析として公知である。トランスレータがデータ及びステートの使用(読み出し)及び定義(書き込み)に関して有する認識(すなわち生死解析)は全て、トランスレータの変換範囲に制限される。プログラムの残りの部分は未知である。更に詳細には、トランスレータは、どの対象レジスタが変換範囲の外で(例えば後続基本ブロックにおいて)使用されるのかについては認識しないため、全てのレジスタが使用されると仮定する必要がある。従って、所与の基本ブロックの内部で修飾された全ての対象レジスタの値(定義)は、これらの値が将来時点で使用されることを見込んで、当該基本ブロックの最後でセーブする(グローバル・レジスタ・ストア27に格納する)必要がある。同様に、所与の基本ブロックにおいて使用されることになる値を有する全ての対象レジスタは、当該基本ブロックの最初に復元させる(グローバル・レジスタ・ストア27からロードする)必要がある、すなわち基本ブロックの変換済みコードは、当該基本ブロック内部でこのコードを最初に使用する前に、所与の対象レジスタを復元させるように作用する必要がある。
IR生成の一般的な機構は、暗示的形態の「ローカル」デッド・コードの除去を含み、除去の範囲は一度に小グループのIRノードにのみローカルに限定される。例えば、対象コードの共通部分式Aは、式ツリーA自体の複数のインスタンスではなく、複数の親ノードを有する、Aの単一のIRツリーにより表示される。「除去(elimination )」は、一つのIRノードが複数の親ノードへのリンクを有することができるという点で暗示的である。同様に、IRプレース・ホルダーとしての抽象レジスタの使用は、デッド・コードの除去の暗示的形態である。所与の基本ブロックの対象コードが特定の対象レジスタを定義するように作用することが絶対にない場合、当該ブロックに関するIR生成の最後では、当該対象レジスタに対応する抽象レジスタは空のIRツリーを参照することになる。コード生成フェーズでは、このシナリオにおいて、適切な抽象レジスタはグローバル・レジスタ・ストアと同期する必要がないことが認識される。従って、ローカル・デッド・コードの除去は、IR生成フェーズにおいて暗示的であり、IRノード群が生成されるにつれて徐々に行われる。
ローカル・デッド・コードの除去とは異なり、「グローバル」デッド・コードの除去アルゴリズムは基本ブロックのIR式フォレスト全体に適用される。例示としての実施形態によるグローバル・デッド・コードの除去は、ライブ領域(live region )及びデッド領域(dead region )を識別するために、グループ・ブロックの各基本ブロックの範囲内の対象レジスタ使用(読み出し)及び対象レジスタ定義(書き込み)の解析を意味する生死解析を必要とする。IRを変換してデッド領域を取り除くため、目的コードが実行する必要のある作業の量が減る。例えば、対象コードの所与のポイントにおいて、トランスレータ19が、特定の対象レジスタがレジスタの次の使用の前に定義される(上書きされる)ことを認識するか、あるいは検出する場合、対象レジスタは当該先行定義次第で変わるコードの全ポイントで死んでいると考えられる。IRの観点からすると、定義されるが再定義の前には決して使用されない対象レジスタは、目的コードを決して発生させることなくIRフェーズにおいて除去することができるデッド・コードである。目的コード生成の観点からすると、死んでいる目的レジスタは、スピル(spill (溢れ))を生じさせることなく、他の一時値又は対象レジスタ値に使用することができる。
グループ・ブロック・グローバル・デッド・コードの除去(group block global dead code elimination)では、生死解析を全てのメンバー・ブロックに対して実行する。生死解析により、各メンバー・ブロックのIRフォレストが生成され、次に、このIRフォレストを使用して当該ブロックに関する対象レジスタ生死情報を生成する。各メンバー・ブロックのIRフォレストはまた、グループ・ブロック生成のコード生成フェーズにおいて必要とされる。一旦、各メンバー・ブロックのIRが生死解析において生成されると、IRをセーブして、コード生成における後続の使用に供することができる、あるいはIRをコード生成の間に消去して、再生成することができる。
グループ・ブロック・グローバル・デッド・コードの除去によって、IRを効果的に2つの方法で「変形(transform )する」ことができる。まず、生死解析の間に各メンバー・ブロックに関して生成されるIRフォレストを変形することができ、次に当該IRフォレストの全体をコード生成フェーズに進める(すなわち、当該IRフォレストの全体をセーブしてコード生成フェーズの間に再使用する)ことができる。このシナリオでは、IR変形(IR transformations)をコード生成フェーズを通過して進めるが、この操作は、これらのIR変形をIRフォレストに直接適用し、次に変形済みIRフォレストをセーブすることにより行なう。このシナリオでは、各メンバー・ブロックに関連するデータは生死情報(グローバル・レジスタ割り当てにおいて更に使用することになる)、及び当該ブロックに関する変形済みIRフォレストを含む。
別の構成として、かつ好適には、メンバー・ブロックに関するIRを変形するグローバル・デッド・コードの除去ステップは、グループ・ブロック生成の最終コード生成フェーズの間に、先に生成される生死情報を使用して実行する。本実施形態では、グローバル・デッド・コード変換は、「死んだ(dead)」対象レジスタ群のリストとして記録することができ、次にこのリストが各メンバー・ブロックに関連する生死情報にエンコードされる。従って、IRフォレストの実際の変形は次のコード生成フェーズにより行われ、このフェーズでは、デッド・レジスタ・リストを使用してIRフォレストを削除する。このシナリオによって、トランスレータは、生死解析の間にIRを一旦生成し、次にIRを削除し、更に同じIRをコード生成の間に再生成することができ、このポイントで、IRは生死解析を使用して変形される(すなわち、グローバル・デッド・コードの除去がIR自体に適用される)。このシナリオでは、各メンバー・ブロックに関連するデータは生死情報を含み、この生死情報は死んだ対象レジスタのリストを含む。IRフォレストはセーブされない。詳細には、IRフォレストがコード生成フェーズにおいて(再)生成された後、死んだ対象レジスタ(これらのレジスタは生死情報内の死んだ対象レジスタのリストに列挙される)に関するIRツリーは削除される。
一つの実施形態では、生死解析の間に生成されるIRは、生死情報が抽出された後に削除してメモリ・リソースを節約する。IRフォレスト(メンバー・ブロック当たり一つのIRフォレスト)は、コード生成の間に1回に一つのメンバー・ブロックの割合で再生成される。本実施形態では、全てのメンバー・ブロックに関するIRフォレストは変換のいかなるポイントにおいても共存することはない。しかしながら、生死解析及びコード生成の間にそれぞれ生成されるIRフォレストの2つのバージョンは同じである。何故なら、これらのバージョンは、対象コードから同じIR生成プロセスを使用して生成されるからである。
別の実施形態では、トランスレータは各メンバー・ブロックに関するIRフォレストを生死解析の間に生成し、次にIRフォレストを各メンバー・ブロックに関連するデータにセーブしてコード生成の間に再使用する。本実施形態では、全てのメンバー・ブロックに関するIRフォレストは、生死解析の最後から(グローバル・デッド・コードの除去ステップにおける)コード生成まで共存する。本実施形態の別の構成では、IRに対する変形又は最適化は、IRの初期生成と(生死解析の間の)IRの最後の使用と(コード生成)の間の期間には実行されない。
別の実施形態では、全てのメンバー・ブロックに関するIRフォレストは、生死解析ステップとコード生成ステップとの間にセーブし、ブロック間最適化をIRフォレストに対してコード生成の前に行なう。本実施形態では、トランスレータは、全てのメンバー・ブロックのIRフォレストが変換の同じポイントで共存するという利点を生かし、最適化は、これらのIRフォレストを変形する異なるメンバー・ブロックのIRフォレストに跨って行われる。この場合、コード生成において使用されるIRフォレストは、(上述した2つの実施形態におけるように)生死解析において使用されるIRフォレストと同じにはならない。何故なら、IRフォレストはブロック間最適化により次の段階で変形されてしまうからである。別の表現をすれば、コード生成において使用されるIRフォレストは、1回に一つのメンバー・ブロックの割合でこれらのIRフォレストを新規に生成することから生じるIRフォレストとは異ならせることができる。
グループ・ブロック・グローバル・デッド・コードの除去においては、デッド・コード検出の範囲は、生死解析が複数のブロックに同時に適用されるということから広くなる。従って、対象レジスタが第1メンバー・ブロックにおいて定義され、続いて第3メンバー・ブロックにおいて再定義される場合(途中で使用されることなく、あるいはエグジット・ポイント無しで)、第1定義に関するIRツリーは第1メンバー・ブロックから削除することができる。これに比べると、基本ブロック・コード生成では、トランスレータ19はこの対象レジスタが死んだことを検出することができない。
上に記載したように、グループ・ブロック最適化の一つの目標は、基本ブロック境界でのレジスタ同期の必要を低減するか、あるいは無くすことにある。従って、どのようにしてレジスタ割り当て及びレジスタ同期をトランスレータ19がグループ・ブロック化の間に行なうことができるかについての説明を次に行なう。
レジスタ割り当ては、抽象(対象)レジスタを目的レジスタに関連付けるプロセスである。レジスタ割り当ては、コード生成に必要な要素である。何故なら、抽象レジスタ値は目的レジスタに含まれて目的命令を構成する必要があるからである。目的レジスタと抽象レジスタとの間のこれらの割り当て(すなわちマッピング)の表示はレジスタ・マップ(register map)と呼ばれる。コード生成の間、トランスレータ19はワーキング・レジスタ・マップ(working register map)を維持し、このマップはレジスタ割り当ての現ステート(すなわち、目的コードの所与のポイントで実際に存在する目的レジスタと抽象レジスタとの間のマッピング)を表わす。今後、メンバー・ブロックの出口でのワーキング・レジスタ・マップを抽象的に表わすスナップ・ショットであるエグジット・レジスタ・マップ(exit register map )への参照が行われる。しかしながら、エグジット・レジスタ・マップは同期には必要がないため、このマップは記録されず、従って純粋に抽象的である。エントリー・レジスタ・マップ40(図3)は、メンバー・ブロックの入口でのワーキング・レジスタ・マップを表わすスナップ・ショットであり、このマップは同期を取るために記録する必要がある。
また、上に説明したように、グループ・ブロックは複数のメンバー・ブロックを含み、コード生成は各メンバー・ブロックに対して別々に行われる。従って、各メンバー・ブロックはブロック固有のエントリー・レジスタ・マップ40及びエグジット・レジスタ・マップを有し、これらのマップは、特定の目的レジスタ群の特定の対象レジスタ群への、当該ブロックの変換済みコードの最初及び最後のそれぞれにおける割り当てを表わす。
グループ・メンバー・ブロックに関するコード生成は、このブロックのエントリー・レジスタ・マップ40(入口でのワーキング・レジスタ・マップ)によってパラメータ化されるが、コード生成によってワーキング・レジスタ・マップの修飾も行なわれる。メンバー・ブロックに関するエグジット・レジスタ・マップは、コード生成プロセスによって修飾される、当該ブロックの最後におけるワーキング・レジスタ・マップを表わす。第1メンバー・ブロックが変換されると、ワーキング・レジスタ・マップは空になる(以下に説明するグローバル・レジスタ割り当てに従う)。第1メンバー・ブロックに関する変換の最後では、ワーキング・レジスタ・マップは、コード生成プロセスにより生成されるレジスタ・マッピングを含む。次に、ワーキング・レジスタ・マップを、全ての後続メンバー・ブロックのエントリー・レジスタ・マップ40にコピーする。
メンバー・ブロックに関するコード生成の最後では、幾つかの抽象レジスタは同期する必要がない。レジスタ・マップによって、トランスレータ19はメンバー・ブロック境界での同期を、どのレジスタが実際に同期する必要があるのかを識別することにより最小化することができる。これに比べて、(グループではない)基本ブロックのシナリオでは、全ての抽象レジスタは全ての基本ブロックの最後で同期する必要がある。
メンバー・ブロックの最後では、3つの同期シナリオを後続ブロックに基づいて用いることができる。第1に、後続ブロックが、未だ変換されていないメンバー・ブロックである場合、このブロックのエントリー・レジスタ・マップ40は、ワーキング・レジスタ・マップと同じであると定義され、同期は必要がないと結論付けられる。第2に、後続ブロックがグループの外にある場合、全ての抽象レジスタは同期する必要がある(すなわち、完全同期)。何故なら、制御が、後続ブロックの実行の前にトランスレータ・コード19に戻るからである。第3に、後続ブロックが、既に固定されたレジスタ・マップを有するメンバー・ブロックである場合、同期コードを挿入してワーキング・マップを後続ブロックのエントリー・マップと一致させる必要がある。
レジスタ・マップ同期のコストの一部は、レジスタ同期を最小化するか、あるいはレジスタ同期をホット・パスに沿って完全に無くすように作用するグループ・ブロックの順番付け巡回により、低減される。メンバー・ブロック群は順番付け巡回により生成される順番に変換される。各メンバー・ブロックが変換されると、このブロックのエグジット・レジスタ・マップは、未だ固定されていないエントリー・レジスタ・マップを有する全ての後続メンバー・ブロックのエントリー・レジスタ・マップ40に移行する。実際、グループ・ブロックの最も多い頻度で実行されるパス(hottest path)がまず変換され、当該パスに沿った、全てではないにしても、ほとんどのメンバー・ブロック境界では同期は必要ではない。何故なら、該当するレジスタ・マップが全て一致するからである。
例えば、第1メンバー・ブロックと第2メンバー・ブロックとの間の境界では常に同期が必要ではない。何故なら、第2メンバー・ブロックは、第1メンバー・ブロックのエグジット・レジスタ・マップ41と同じになるように固定されたエントリー・レジスタ・マップ40を常に有するからである。メンバー・ブロック群の間でのある程度の同期は回避することができない。何故なら、グループ・ブロック群は内部制御分岐及び複数のエントリー・ポイントを含むことができるからである。これは、実行が、異なる時間において異なるワーキング・レジスタ・マップを有する異なる先行ブロックから同じメンバー・ブロックに達することができることを意味する。これらの場合では、トランスレータ19がワーキング・レジスタ・マップを適切なメンバー・ブロックのエントリー・レジスタ・マップと同期させる必要がある。
必要に応じて、レジスタ・マップ同期がメンバー・ブロック境界で生じる。トランスレータ19はコードをメンバー・ブロックの最後に挿入してワーキング・レジスタ・マップを後続ブロックのエントリー・レジスタ・マップ40と同期させる。レジスタ・マップ同期においては、各抽象レジスタは10個の同期条件のうちの一つの同期条件に従う。テーブル1は、10個のレジスタ同期状態をトランスレータのワーキング・レジスタ・マップ及び後続ブロックのエントリー・レジスタ・マップ40の関数として示している。テーブル2は、10個の正式な同期状態を、これらの状態のテキスト記述及び該当する同期動作の擬似コード記述(擬似コードについては以下に記載する)として列挙することによりレジスタ同期アルゴリズムを記述している。従って、全てのメンバー・ブロック境界において、全ての抽象レジスタが10個の状態に対応するアルゴリズムを使用して同期する。同期条件及び同期動作に関するこの詳細かつ明確な表現によって、トランスレータ19は、効率的な同期コードを生成することができ、これにより各抽象レジスタに関する同期コストが最小化される。
次に、テーブル2に列挙される同期動作機能について記載する。“Spill(E(a))”は、抽象レジスタaを目的レジスタE(a)から対象レジスタ・バンク(グローバル・レジスタ・ストアのコンポーネント)にセーブするように作用する。“Fill(t,a)”は、抽象レジスタaを対象レジスタ・バンクから目的レジスタtにロードするように作用する。“Reallocate()”は、抽象レジスタを、利用可能であれば新規の目的レジスタに移動させるか、あるいは再割り当てするか、あるいは目的レジスタが利用できない場合には抽象レジスタをスピル(溢れ)させるように作用する。“FreeNoSpill(t)”は、関連する抽象対象レジスタをスピルさせることなく、目的レジスタが空き(free)であるとして目的レジスタをマークするように作用する。FreeNoSpill()関数は、同じ同期ポイントにおいて、アルゴリズムの複数回の適用に跨る大きなスピルが生じないようにするために必要である。ここで、“Nil”同期動作が生じる状態に関して、同期コードは該当する抽象レジスタ群には必要ではないことに留意されたい。
トランスレータ19は2つのレベルのレジスタ割り当て、すなわちグローバル・レジスタ割り当て、及びローカル・レジスタ割り当て(又は一時レジスタ割り当て)をグループ・ブロック内で実行する。グローバル・レジスタ割り当ては、コード生成前の定義であって、グループ・ブロック全体に渡って(すなわち、全てのメンバー・ブロックに渡って)有効な特定のレジスタ・マッピングに関する定義である。ローカル・レジスタ割り当ては、コード生成プロセスにおいて生成されるレジスタ・マッピングである。グローバル・レジスタ割り当てによって特定のレジスタ割り当て制限が定義され、これらの制限は、メンバー・ブロック群のコード生成機能のパラメータ化が、ローカル・レジスタ割り当てを制限することにより行なわれるように作用する。
グローバルに割り当てられる抽象レジスタ群は、メンバー・ブロック境界で同期する必要がない。何故なら、これらのレジスタは全てのメンバー・ブロックの同じそれぞれの目的レジスタに割り当てられることが保証されるからである。このアプローチは、同期コード(これによってブロック間のレジスタ・マッピングの差が補償される)は全く、メンバー・ブロック境界でグローバルに割り当てられる抽象レジスタ群には必要ではないという利点を有する。グループ・ブロック・レジスタ・マッピングの不具合は、グローバルに割り当てられる目的レジスタ群は新規マッピングには直ちには利用することができないため、このグループ・ブロック・レジスタ・マッピングによってローカル・レジスタ割り当てが妨害されるということである。この不具合を補償するために、グローバル・レジスタ・マッピングの数を特定のグループ・ブロックに関して制限することができる。
実際のグローバル・レジスタ割り当ての数及び選択は、グローバル・レジスタ割り当てポリシーにより定義される。グローバル・レジスタ割り当てポリシーは、対象アーキテクチャ、ターゲット・アーキテクチャ、及び変換対象アプリケーションに基づいて構成することができる。グローバルに割り当てられるレジスタの最適数は実験的に求められ、かつ目的レジスタの数、対象レジスタの数、変換対象アプリケーションのタイプ、及びアプリケーションの使用パターンの関数である。この最適数は通常、目的レジスタの合計数から所定の小さな数を差し引いた数の数分の一であるため、確実に十分な数の目的レジスタが一時値に関して確保される。
MIPS−X86トランスレータ及びPowerPC−X86トランスレータのように、多くの対象レジスタが存在するが、目的レジスタがほとんど無い場合においては、グローバルに割り当てられるレジスタの数はゼロである。これは、X86アーキテクチャは、非常に少ない数の目的レジスタしか含まないため、固定されたレジスタ割り当てを使用すると必ず、割り当てを全く使用しない場合よりも悪い目的コードが生成される現象が観察されているからである。
X86−MIPSトランスレータのように、多くの対象レジスタ及び多くの目的レジスタが存在する場合では、グローバルに割り当てられるレジスタの数(n)は目的レジスタの数(T)の4分の3である。従って次式が成り立つ。
X86−MIPS: n=3/4*T
X86アーキテクチャは汎用レジスタをほとんど含まないが、このアーキテクチャは多くの対象レジスタを有するものとして扱われる。何故なら、多くの抽象レジスタが複雑なX86プロセッサ・ステート(例えば条件コード・フラグを含む)をエミュレートするために必要であるからである。
対象レジスタ及び目的レジスタの数が、MIPS−MIPSアクセラレータのように、ほぼ同じである場合では、ほとんどの目的レジスタがグローバルに割り当てられて、一時値を保存するためにほんの幾つかだけが確保される。従って次式が成り立つ。
MIPS−MIPS: n=T−3
グループ・ブロック(群)全体に渡って使用される対象レジスタの合計数が、目的レジスタの数(T)以下である場合では、全ての対象レジスタがグローバルにマッピングされる。これは、レジスタ・マップ全体が全てのメンバー・ブロックに渡って一定であることを意味する。目的レジスタ及びアクティブ対象レジスタの数が等しいことを意味する、(s=T)が成り立つ特殊な場合、これは、一時計算のためには目的レジスタが残されないことを意味し、この場合、一時値は、同じ表現ツリーの内部ではもはや使用されない(このような情報は生死解析から得られる)対象レジスタにグローバルに割り当てられる目的レジスタにローカルに割り当てられる。
グループ・ブロック生成の最後では、コード生成は各メンバー・ブロックに関してたどり順(traversal order )に行われる。コード生成の間、各メンバー・ブロックのIRフォレストは(再)生成され、(当該ブロックの生死情報に含まれる)死んだ対象レジスタのリストを使用して、IRフォレストを目的コードの生成前に削除する。各メンバー・ブロックを変換すると、このブロックのエグジット・レジスタ・マップは、全ての後続メンバー・ブロック(既に固定されているブロックを除く)のエントリー・レジスタ・マップ40に移行する。ブロック群はたどり順に変換されるため、ホット・パスに沿ったレジスタ・マップ同期を最小化するだけでなく、ホット・パス変換がターゲット・メモリ空間で連続になるという効果がある。基本ブロック変換の場合と同じように、グループ・メンバー・ブロック変換は一連のエントリー条件、すなわちグループ・ブロックが生成されたときの現行のワーキング条件に特化される。
図7は、トランスレータ・コード19が例示としての実施形態に従って行なうグループ・ブロック生成の例を示している。この例示としてのグループ・ブロックは、5個のメンバー(“A”〜“E”)と、最初に、一つのエントリー・ポイント(「エントリー1」;エントリー2は以下に議論するように、アグリゲーションにより後で生成される)及び3つのエグジット・ポイント(「エグジット1」、「エグジット2」、及び「エグジット3」)と、を有する。この例では、グループ・ブロック生成のトリガーしきい値は45000の実行回数であり、メンバー・ブロック群のインクルージョンしきい値は1000の実行回数である。このグループ・ブロックのコンストラクションは、ブロックAの実行回数(この時点では45074)が45000のトリガーしきい値に達したときにトリガーされていて、この時点で、制御フロー・グラフの探索を実行してグループ・ブロック・メンバーを識別している。この例では、5個のブロックが、1000のインクルージョンしきい値を超えていることが判明している。一旦、メンバー・ブロック群が識別されると、順番の決まった深さ優先探索(プロファイリング・メトリックによって順番が決定される)を実行して、相対的にホットなブロック群及びこれらのブロックの後続ブロックが最初に処理されるようにし、これによって、クリティカル・パスを考慮した順番(critical path ordering)に従った一連のブロックが生成される。
この段階で、グローバル・デッド・コードの除去を実行する。各メンバー・ブロックをレジスタ使用及びレジスタ定義のために解析する(すなわち生死解析)。これにより、コード生成が2つの方法で一層効率的に行われる。まず、ローカル・レジスタ割り当てでは、どの対象レジスタがグループ・ブロックの中で生きているか(すなわち、どの対象レジスタを現行の、又は後続のメンバー・ブロックに使用するか)を考慮に入れることができ、これによって、スピル(spill )によるコストを最小化し易くなる。死んだレジスタ群が最初に溢れる。何故なら、これらのレジスタは復元される必要がないからである。更に、生死解析によって、特定の対象レジスタが定義され、使用され、次に再定義される(上書きされる)ことが判明すると、値を最後に使用した後はいつでも廃棄することができる(すなわち、この値の目的レジスタを空きにすることができる)。生死解析によって、特定の対象レジスタ値が定義され、次にそれまでの間に全く使用されることなく再定義されることが判明すると(起こりそうにはないが、これは、対象コンパイラがデッド・コードを生成したことを意味する)、当該値に関する該当するIRツリーを削除して、目的コードが絶対にこのツリーに関して生成されないようにすることができる。
グローバル・レジスタ割り当てが次に行われる。トランスレータ19は頻繁にアクセスされる対象レジスタ群に、全てのメンバー・ブロックに渡って一定の固定目的レジスタ・マッピング(fixed target register mapping )を割り当てる。グローバルに割り当てられるレジスタ群は溢れることができず、これらの目的レジスタはローカル・レジスタ割り当てには利用することができないことを意味する。目的レジスタ群が占める割合は、対象レジスタ群が目的レジスタ群よりも多い場合、一時的な対象レジスタ・マッピングに関して維持する必要がある。グループ・ブロック内部の全セットの対象レジスタを目的レジスタに丁度収めることができる特殊な場合では、スピル(spill )及びフィル(fill)が完全に回避される。図7に示すように、トランスレータはコード(“Pr1”)を埋め込んで、グループ・ブロック(“A”)の先頭に入る前にこれらのレジスタをグローバル・レジスタ・ストア27からロードする。このようなコードはプロローグ・ロード(prologue load )と呼ばれる。
この時点で、グループ・ブロックは目的コード生成の準備が整う。コード生成の間、トランスレータ19はワーキング・レジスタ・マップ(抽象レジスタ群と目的レジスタ群との間のマッピング)を使用してレジスタ割り当てを追跡し続ける。各メンバー・ブロックの始まりでのワーキング・レジスタ・マップの値は、当該ブロックの関連エントリー・レジスタ・マップ40に記録される。
まず、グローバルに割り当てられる抽象レジスタ群をロードするプロローグ・ブロックPr1が生成される。このポイントで、Pr1の最後におけるワーキング・レジスタ・マップをブロックAのエントリー・レジスタ・マップ40にコピーする。
次にブロックAを変換して、目的コードを、Pr1の目的コードの直後に埋め込む。制御フロー・コードを埋め込んでエグジット1に関するエグジット条件を処理するが、このエグジット条件はエピローグ・ブロックEp1(後で埋め込むことになる)へのダミー分岐(dummy branch)(後でパッチを当てることになる)から成る。ブロックAの最後では、ワーキング・レジスタ・マップをブロックBのエントリー・レジスタ・マップ40にコピーする。Bのエントリー・レジスタ・マップ40をこのように固定することにより2つの結論に至る。まず、AからBに至るパスでは同期は必要がない。次に、他のブロック(すなわち、このグループ・ブロックのメンバー・ブロック又はアグリゲーションを使用する別のグループ・ブロックのメンバー・ブロック)からBへのエントリーには必ず、当該ブロックのエグジット・レジスタ・マップがBのエントリー・レジスタ・マップと同期する必要がある。
次に、ブロックBがクリティカル・パスに位置する。このブロックの目的コードを、ブロックAの直後に埋め込み、2つの後続ブロックC及びAを処理するためのコードを次に埋め込む。第1の後続ブロックCはそのエントリー・レジスタ・マップ40を未だ固定していないため、ワーキング・レジスタ・マップを単純にCのエントリー・レジスタ・マップにコピーする。しかしながら、第2の後続ブロックAは既にそのエントリー・レジスタ・マップ40を固定しているため、ブロックBの最後でのワーキング・レジスタ・マップ及びブロックAのエントリー・レジスタ・マップは異なり得る。レジスタ・マップに差があると必ず、ブロックBからブロックAに至るパスに沿った特定の同期(“B−A”)が必要となり、この同期によってワーキング・レジスタ・マップをエントリー・レジスタ・マップ40に一致させる。この同期はレジスタのスピル(spill )、フィル(fill)、及びスワップ(swap)の形態で行われ、そして上に記載した10個のレジスタ・マップ同期シナリオに詳述される。
次に、ブロックCを変換し、目的コードをブロックCの直後に埋め込む。ブロックD及びEを同様に変換し、連続して埋め込む。EからAに至るパスにおいても、Eのエグジット・レジスタ・マップ(すなわち、Eの変換の最後でのワーキング・レジスタ・マップ)から、ブロック“E−A”に埋め込まれるAのエントリー・レジスタ・マップ40へのレジスタ・マップ同期が必要である。
グループ・ブロックから出て、制御をトランスレータ19に返す前に、グローバルに割り当てられるレジスタ群はグローバル・レジスタ・ストアに同期する必要がある。このコードはエピローグ・セーブ(epilogue save )と呼ばれる。メンバー・ブロック群が変換された後、コード生成によってエピローグ・ブロック群が全てのエグジット・ポイント(Ep1,Ep2,及びEp3)に対応して埋め込まれ、分岐先がメンバー・ブロック群全体を通じて固定される。アイソブロック群及びグループ・ブロック群の両方を使用する実施形態では、制御フロー・グラフ巡回を、当該ブロックのアイソブロック群ではなく、固有の対象ブロック群(すなわち、対象コードの特定の基本ブロック)に対して実行する。従って、アイソブロック群はグループ・ブロック生成に対して透過的である。特殊な差別化は、1回の変換又は複数回の変換が行われる対象ブロック群に関しては行われない。
例示の実施形態では、グループ・ブロック最適化及びアイソブロック最適化の両方を有利な形で用いることができる。しかしながら、アイソブロック機構(isoblock mechanism)が異なる基本ブロック変換を同じ対象コード・シーケンスに関して生成することができるということが、どのブロックをグループ・ブロックに含めるかについての決定プロセスを複雑にする。何故なら、含める対象のブロック群はグループ・ブロックが形成されるまで存在してはならないからである。最適化の前に存在した特化されないブロック群(unspecialized block )を使用して収集される情報は、選択及びレイアウト・プロセスにおいて使用される前に適合させる必要がある。
例示の実施形態は更に、ネストしたループ構造をグループ・ブロック生成に取り入れる方法を用いる。グループ・ブロック群は最初、一つのみのエントリー・ポイントで、すなわちトリガー・ブロックの始点で生成される。プログラム内のネストしたループによって、内部ループがまず起動し、内部ループを表わすグループ・ブロックが生成される。その後、外部ループが起動し、内部ループだけでなく外部ループの全てのブロックを含む新規のグループ・ブロックが生成される。グループ・ブロック生成アルゴリズムが、内部ループに対して行われるワークを考慮に入れず、その代わり、当該ワークの全てを再度実行する場合、深くネストしたループを含むプログラムは、次第に大きくなるグループ・ブロック群を徐々に生成し、より大きなストレージ及びより多くのワークが各グループ・ブロック生成において必要となる。更に、古い(内部)グループ・ブロック群には到達できなくなるため、これらのグループ・ブロックはほとんど、あるいは全く利点をもたらさない。
例示の実施形態によれば、グループ・ブロック集約(group block aggregation )を使用して既に構築されているグループ・ブロックを最適化された追加ブロック群と結合させることができる。ブロック群を選択して新規のグループ・ブロックに含めるフェーズの間、前のグループ・ブロックに既に含められている候補ブロック群が識別される。これらのブロックに関する目的コードを埋め込むのではなく、アグリゲーションを行なうことにより、トランスレータ19が適切なロケーションへのリンクを既存のグループ・ブロックに生成する。これらのリンクは既存のグループ・ブロックの中央にジャンプすることができるため、当該ロケーションに対応するワーキング・レジスタ・マップを強化する必要がある。従って、リンクに埋め込まれるコードはレジスタ・マップ同期コードを必要に応じて含む。
基本ブロック・データ構造30に格納されるエントリー・レジスタ・マップ40はグループ・ブロック集約をサポートする。アグリゲーションにより、他の変換済みコードはグループ・ブロックの中央に、メンバー・ブロックの始点をエントリー・ポイントとして使用してジャンプすることができる。このようなエントリー・ポイントは、ワーキング・レジスタ・マップがメンバー・ブロックのエントリー・レジスタ・マップ40と同期することを必要とし、この操作は、トランスレータ19が、同期コード(すなわち、スピル及びフィル)を先行ブロックのエグジット・ポイントとメンバー・ブロックのエントリー・ポイントとの間に埋め込むことにより行なう。
一つの実施形態では、幾つかのメンバー・ブロックのレジスタ・マップを選択的に消去してリソースを節約する。最初に、グループの全メンバー・ブロックのエントリー・レジスタ・マップを無制限に格納して、全てのメンバー・ブロックの始点におけるグループ・ブロックへの(集約グループ・ブロックからの)エントリーを容易にする。グループ・ブロック群が大きくなると、幾つかのレジスタ・マップを消去してメモリを節約することができる。この消去が行われると、アグリゲーションによってグループ・ブロックが複数の領域に効果的に分割され、これらの領域のうちの幾つか(すなわち、消去されてしまったレジスタ・マップを有するメンバー・ブロック群)は集約エントリー(aggregate entry )にアクセスすることができない。異なるポリシーを使用して、どのレジスタ・マップを格納すべきかを判断する。一つのポリシーでは、全てのメンバー・ブロックの全てのレジスタ・マップを格納する(すなわち消去が行われない)。別のポリシーでは、最も実行回数の多いメンバー・ブロック群に関するレジスタ・マップのみを格納する。別のポリシーでは、後方分岐の分岐先(すなわちループの始点)であるメンバー・ブロック群に関するレジスタ・マップのみを格納する。
別の実施形態では、各グループ・メンバー・ブロックに関連するデータは、全ての対象命令ロケーションに関して記録されるレジスタ・マップを含む。これにより、他の変換済みコードはグループ・ブロックの中央の、メンバー・ブロックの始点だけでなくどのポイントにもジャンプすることができる。何故なら、特定の場合においては、グループ・メンバー・ブロックは、グループ・ブロックが形成されるときに未検出のエントリー・ポイントを含むことができるからである。この方法では大量のメモリが消費されるため、この方法はメモリ節約に関心がない場合にのみ適切となる。
グループ・ブロック化によって、頻繁に実行するブロック群又はブロック集合群を識別するとともに、これらのブロックに対して更に別の最適化を実行する機構が実現される。計算コストが高く付く最適化をグループ・ブロック群に適用するため、これらのグループ・ブロックの形成は、頻繁に実行されることが分かっている基本ブロック群に限定して行なうことが好ましい。グループ・ブロックの場合、余分の計算は、頻繁に実行されるという意味で正当化される。頻繁に実行される連続ブロックは「ホット・パス(hot path)」と呼ばれる。
複数の実施形態を構成することができるが、これらの実施形態では、複数レベルの頻度及び最適化を使用して、トランスレータ19が、複数レベルの頻繁に実行される基本ブロックを検出し、益々複雑になる最適化が適用される。別の構成として、かつ上に記載したように、2つのレベルの最適化のみを使用する。基本最適化を全ての基本ブロックに適用し、単一セットの更なる最適化をグループ・ブロック群に、上述のグループ・ブロック生成機構を使用して適用する。
概要
図8は、トランスレータが実行時に、変換済みコードの実行の間に実行するステップを示している。第1基本ブロック(BBN−1)の実行が完了すると(1201)、このブロックは制御をトランスレータに返す(1202)。トランスレータは第1基本ブロックのプロファイリング・メトリックをインクリメントする(1203)。次に、トランスレータは、基本ブロック・キャッシュに第1基本ブロックの実行により返される対象アドレスを使用して、現行の基本ブロック(BBNであり、このブロックはBBN−1の後続ブロックである)の既に変換されたアイソブロックに関するクエリーを出す。後続ブロックが既に変換されている場合、基本ブロック・キャッシュは一つ以上の基本ブロック・データ構造を返す。次に、トランスレータは後続ブロックのプロファイリング・メトリックをグループ・ブロック・トリガーしきい値と比較する(1207)(この操作では、複数のアイソブロックのプロファイリング・メトリックを集約することができる)。しきい値が満たされない場合、トランスレータは、基本ブロック・キャッシュが返すいずれのアイソブロックがワーキング条件に合致するか(すなわち、BBN−1のエグジット条件と同じエントリー条件を有するアイソブロック)をチェックする。合致するアイソブロックが検出されると、当該変換を実行する(1211)。
後続ブロックのプロファイリング・メトリックがグループ・ブロック・トリガーしきい値を超えると(1207)、合致するアイソブロックが存在する場合でも、上に議論したように、新規のグループ・ブロックを生成して(1213)、実行する(1211)。
基本ブロックがアイソブロックを全く返さないか、あるいは返されるアイソブロック群のいずれもが合致しない場合、上に議論したように、現行ブロックを、現行のワーキング条件で特化されたアイソブロックに変換する(1217)。BBNのデコードの最後で、BBNの後続ブロック(BBN+1)を静的に求めることができる場合(1219)、拡張基本ブロックが生成される(1215)。拡張基本ブロックが生成されると、BBN+1が変換され(1217)、この後これと同様な操作が繰り返される。変換が完了すると、新規のアイソブロックが基本ブロック・キャッシュに格納されて(1221)、実行される(1211)。
パーシャル・デッド・コードの除去(Partial Dead Code Elimination )
トランスレータの別の実施形態では、レジスタ定義の全てを巡回アレイに加えた後であり、かつストアをアレイに加えた後に、更に後続ブロックを、基本的にIRを全て巡回した後において処理した後に、更なる最適化をグループ・ブロックに適用することができ、この操作を本明細書では、「パーシャル・デッド・コードの除去」と呼び、図9のステップ76に示す。このようなパーシャル・デッド・コードの除去には別のタイプの生死解析を用いる。パーシャル・デッド・コードの除去は、計算によらない分岐(non-computed branch )又は計算によるジャンプ(computed jump )で終了するブロック群に対して、グループ・ブロック・モードで適用されるコード・モーション(code motion )の形をとる最適化である。
図9に示す実施形態では、パーシャル・デッド・コードの除去ステップ76を、図6に関連して説明したグループ・ブロック・コンストラクション・ステップに加え、この場合、パーシャル・デッド・コードの除去は、グローバル・デッド・コードの除去ステップ75の後であり、かつグローバル・レジスタ割り当てステップ77の前に行なわれる。
前に記載したように、対象レジスタのような値は、コードの定義から始まり、再定義(上書き)される前のコードの最後の使用で終了するコード範囲に渡って「生きている」と考えられ、この場合、値の使用及び定義の解析は当該技術分野では生死解析として知られる。パーシャル・デッド・コードの除去を、計算によらない分岐及び計算によるジャンプの両方で終了するブロック群に適用する。
計算によらない2つの分岐先で終了するブロックに関して、当該ブロックにおける全てのレジスタ定義を解析して、これらのレジスタ定義のうちのいずれが複数の分岐先のうちの一つで死んでいて(使用される前に再定義される)、他の分岐先で生きているのかを識別する。次にコードを、これらの定義の各々に関して、ブロックのメイン・コードの内部ではなく、コードのライブ・パス(live path )の始点でコード・モーション最適化手法として生成することができる。図10Aを参照すると、2つの分岐先のライブ・パス及びデッド・パス(dead path )を示す例を提示して、実行するレジスタ定義解析を理解し易くする。ブロックAでは、レジスタR1はR1=5と定義される。次にブロックAは条件分岐で終了し、ブロックB及びCに分岐する。ブロックBでは、ブロックAにおいてR1に関して定義された値(R1=5)を使用する前に、レジスタR1はR1=4に再定義される。従って、ブロックBは、レジスタR1のデッド・パスとして識別される。ブロックCでは、レジスタR1の再定義の前に、ブロックAにおけるレジスタ定義R1=5をレジスタR2の定義に使用するため、ブロックCに至るパスはレジスタR1のライブ・パスになる。レジスタR1は、このレジスタの分岐先のうちの一方において死んでいるが、分岐先の他方では生きているとして示されるため、レジスタR1はパーシャル・デッド・レジスタ定義として識別される。
計算によらない分岐に使用するパーシャル・デッド・コードの除去アプローチはまた、2よりも多い数の異なる分岐先にジャンプすることができるブロックに適用することができる。図10Bを参照すると、複数のジャンプ先に至るデッド・パス、及び考えられるライブ・パスを識別するために行なわれるレジスタ定義解析を示す例が提示されている。上に記載したように、レジスタR1はブロックAにおいてR1=5と定義される。次にブロックAは、ブロックB,C,Dなどのいずれかにジャンプすることができる。ブロックBでは、ブロックAにおいてR1に関して定義された値(R1=5)を使用する前に、レジスタR1がR1=4に再定義される。従って、ブロックBは、レジスタR1のデッド・パスとして識別される。ブロックCでは、レジスタR1の再定義の前に、ブロックAにおけるレジスタ定義R1=5をレジスタR2の定義に使用するため、ブロックCに至るパスはレジスタR1のライブ・パスになる。この解析を種々のジャンプに対応するパスの各々に関して継続して行なって、パスがデッド・パス又は考えられるライブ・パスであるかを判断する。
レジスタ定義が最もホットな(最も実行回数の多い)ジャンプ先に関して死んでいる場合、他のパスのみに対応するコードを代わりに生成することができる。考えられる他のライブ・パスのうちの一部のライブ・パスは、これも死んでいると判明することがあり得るが、このパーシャル・デッド・コードの除去アプローチは、最もホットなパスに関して効率的である。何故なら、他の全てのジャンプ先は調査する必要がないからである。図9のステップ76のパーシャル・デッド・コードの除去アプローチについての残りの説明のほとんどは、条件分岐を参照しながら記載することとするが、これは、計算によるジャンプに関するパーシャル・デッド・コードの除去は単に、条件分岐に関する解決方法を拡張したものとすることができるからである。
次に図11を参照すると、パーシャル・デッド・コードの除去手法を用いる好適な方法についての更に詳細な説明が示される。記載のように、パーシャル・デッド・コードの除去には生死解析が必要であり、この場合、計算によらない分岐又は計算によるジャンプで終了するブロックに関する全てのパーシャル・デッド・レジスタ定義を最初にステップ401で識別する。レジスタ定義が部分的に死んでいるか否かを識別するために、分岐又はジャンプの後続ブロック(現行ブロックを含むこともできる)を解析して、このブロックの後続ブロックの各々における当該レジスタに関する生死状態を判断する。レジスタが一つの後続ブロックで死んでいるが、別の後続ブロックでは死んでいない場合、レジスタはパーシャル・デッド・レジスタ定義として識別される。パーシャル・デッド・レジスタの識別は、グローバル・デッド・コードの除去ステップ75において実行するフル・デッド・コード(fully dead code )(この場合、レジスタ定義は両方の後続ブロックにおいて死んでいる)の識別の後に行なわれる。一旦、パーシャル・デッド・レジスタとして識別されると、レジスタをパーシャル・デッド・レジスタ定義のリストに加えて後続のマーキング・フェーズ(marking phase )において使用する。
一旦、一連のパーシャル・デッド・レジスタ定義が識別されると、再帰的マーキング・アルゴリズム(recursive marking algorithm )403を適用して、パーシャル・デッド・レジスタの各々のチャイルド・ノード(表現式)を再帰的にマークして、一連のパーシャル・デッド・ノードを実現する(すなわち、一連のレジスタ定義及び部分的に死んでいるこれらの定義のチャイルド・ノード)。ここで、パーシャル・デッド・レジスタ定義の各チャイルドは部分的に死んでいると考えられるだけであることに留意されたい。チャイルドは単に、このチャイルドがライブ・レジスタ定義(又はいずれかのタイプのライブ・ノード)によって共有されない場合に部分的に死んでいるとして分類することができるだけである。一つのノードが部分的に死んでいると判明すると、そのチャイルド群が部分的に死んでいるか否かを判断し、同様な操作を繰り返す。これにより、ノードを部分的に死んでいると識別する前に、一つのノードへの全ての参照が部分的に死んでいることを証明する再帰的マーキング・アルゴリズムが提供される。
従って、再帰的マーキング・アルゴリズム403を行なうために、個々の参照が部分的に死んでいるか否かについて保存するのではなく、一つのノードへの全ての参照が部分的に死んでいるか否かを判断する。従って、各ノードはdeadCount(すなわち、部分的に死んでいるペアレントノードからのこのノードへの参照の数)及びrefCount(このノードへの参照の合計数)を有する。deadCountは、参照が部分的に死んでいる可能性があるとしてマークされる度にインクリメントする。一のノードのdeadCountを、そのrefCountと比較し、これらの2つが等しくなる場合は、当該ノードへの全ての参照が部分的に死んでおり、そしてノードをパーシャル・デッド・ノードのリストに加える。次に、全てのパーシャル・デッド・ノードが識別されるまで、再帰的マーキング・アルゴリズムを、パーシャル・デッド・ノードのリストに丁度今加えたノードのチャイルド群に適用する。
ステップ403において適用する再帰的マーキング・アルゴリズムは、全てのレジスタ定義を巡回アレイ(traversal array )に加えた後であり、かつストアをアレイに加える前に、buildTraversalArray()関数において実行することができること好ましい。パーシャル・デッド・レジスタ定義のリストの各レジスタに関して、recurseMarkPartialDeadNode()関数を2つのパラメータで呼び出す。すなわち、レジスタ定義ノード及びこのノードが生きているパスである。死んでいる(すなわちデッド・パスにおける)レジスタ定義に関するノード群は最終的に廃棄し、パーシャル・ライブパス(部分的に生きているパス)に関するレジスタ定義は、分岐又はジャンプの複数のパスのうちの一つのパスに移動させて、パーシャル・ライブ・ノード(部分的に生きているノード)の個別リストを生成する。2つのリストは、条件分岐の場合に生成され、一方のリストは条件が真であると判定される場合に従う「トゥルー・パス(true path )」に対応し、他方のリストは条件が「偽(false )」であると判定される場合に従う「フォールス・パス(false path)」に対応する。これらのパス及びノードは、パーシャル・デッド(partially dead)ではなくパーシャル・ライブ(partially live)と呼ばれるが、これは、これらのパス及びノードが死んでいるパスに対応するノード群が廃棄され、ノード群が生きているパスに対応するノード群のみが維持されるからである。この機能を提供するために、各ノードは、ノードが生きているのはどのパスであるかについて識別する変数を含むことができる。次の擬似コードは、recurseMarkPartialDeadNode()関数の実行中に実行される。
一旦、recurseMarkPartialDeadNode()関数を、一連のパーシャル・デッド・レジスタ定義に含まれるパーシャル・デッド・レジスタ定義の各々に関して呼び出すと、3セットのノードが存在することになる。第1セットのノードは全てのフル・ライブ・ノード群(すなわち、これらのノードのdeadCountよりも大きいrefCountを有するノード群)を含み、他の2セットは条件分岐の各パスに対応するパーシャル・ライブ・ノード群(すなわち、これらのノードのdeadCountに一致するrefCountを有するノード群)を含む。これらの3セットのうちのいずれかを空にすることが可能である。最適化の一形態として、コード・モーションは、パーシャル・ライブ・ノード群に関するコードの埋め込みを、フル・ライブ・ノード群に関するコードを埋め込んだ後にまで遅らせる場合に適用される。
順番制限(ordering restriction)に起因して、コード・モーションを、ステップ403で検出されるパーシャル・ライブ・ノード群の全てに対して実行することが常に可能である訳ではない。例えば、ロードの移動は、このロードの後にストアが続く場合には行なうことが許されない。何故なら、ロードによって読み出される値が、ストアによって上書きされる可能性があるからである。同様に、レジスタ参照に対しては、当該レジスタに対するレジスタ定義が全て生きている場合にコード移動を行なうことができない。何故なら、レジスタ定義によって、レジスタ参照の生成に使用される対象レジスタ・バンクに含まれる値が上書きされるからである。従って、ステップ405において、ストアが後に続く全てのロードに対して再帰的にマークを外し、ステップ407において、該当するフル・ライブ・レジスタ定義を有する全てのレジスタ参照に対するマークを外す。
ステップ405においてマークが外されるロード及びストアに関して、中間表現が最初であり、かつパーシャル・デッド・ノードの収集の前に生成されると、この中間表現には、ロード及びストアを実行する必要のある順番が含まれることに注目されたい。この初期の中間表現をrecurseMarkPartialDeadNode()関数の中に使用してロードとストアとの間に依存関係を構築して、メモリ・アクセス及びメモリ変更が正しい順番で確実に行なわれるようにする。この機能を、ロードの後にストアが続く、簡単な例で示すために、ストアをロードに依存させてロードを最初に実行する必要があることを示す。パーシャル・デッド・コードの除去方法を用いる場合、ロード及びそのチャイルド・ノードに対するマークを外して、ロードがストアの生成前に確実に生成されるようにする必要がある。recurseUnmarkPartialDeadNode()関数を使用してこのマーク外し操作を実行する。
パーシャル・デッド・コードの除去方法のステップ405では別の構成として更に、ロード−ストア・エイリアス(load-store aliasing )情報の最適化を行なう。ロード−ストア・エイリアスによって、連続するロード関数及びストア関数が同じアドレスにアクセスする状況の全てが取り除かれる。2回のメモリ・アクセス(例えば、1回のロード及び1回のストア、2回のロード、2回のストア)において、これらのアクセスにおいて使用されるメモリ・アドレスが同じか、あるいは重複する場合にエイリアスを行なう(alias )。連続するロード及びストアを、traverseLoadStoreOrder()関数の実行中に実行する段階になると、これらのロード及びストアは絶対にエイリアスすることができないか、あるいはエイリアスすることができる、のいずれかである。これらのロード及びストアを絶対にエイリアスすることができない場合、ロードとストアとの間に依存関係を付加する必要がないため、ロードに対してマークを外す必要もない。ロード−ストア・エイリアス最適化によって、2回のアクセスにより冗長な表現式が確実にエイリアスされ、従って冗長な表現式が無くなる状況を識別することができる。例えば、同じアドレスに対する2つのストア命令は、これらの命令の間にロード命令がない場合には必要ではない。何故なら、2番目のストアによって1番目のストアが上書きされるからである。
ステップ407においてマークが外されるレジスタ参照に関して、この局面は、コード生成方針によって、レジスタ参照を当該同じレジスタのレジスタ定義の前に生成する必要がある場合に重要となる。これは、レジスタ参照が、レジスタがブロックの始点で採る値を表わす結果として生じ、レジスタ定義を最初に行なうことにより当該値が、この値が読み出される前に上書きされ、レジスタ参照が誤った値に対して行われることになる。従って、レジスタ参照に対しては、該当するフル・ライブ・レジスタ定義がある場合にコード移動を行なうことができない。この状況を解決するために、このような場合が生じるか否かをtraverseRegDefs()関数を使用して判断して、このカテゴリーに属する全てのレジスタ参照に対するマークをステップ407で外す。
複数セットのライブ・ノード及びパーシャル・ライブ・ノードが生成されて、必要に応じてこれらのノードに対するマークが外された後、目的コードをこれらのノードに関して生成する必要がある。パーシャル・デッド・コードの除去方法を利用しない場合、中間表現における各ノードに関するコードは、traverseGenerate()関数内部のループの中で生成され、この場合、後続ノード以外の全てのノードは、これらのノードの準備が整っていると考えられる場合に、すなわちこれらのノードの依存関係が、後続ノードが最後に処理される形で満たされる場合に生成されている。この操作は、パーシャル・デッド・コードの除去を用いると一層複雑になる。何故なら、この時点では、コード生成が行われる3セット(フル・ライブ・セット及び2つのパーシャル・ライブ・セット)のノードが存在するからである。条件ジャンプの場合、ノード群から成るセットの数は、計算によるジャンプの数とともにそれぞれ大きくなる。後続ノードは生きていることが保証されるため、コード生成は全てのフル・ライブ・ノードから始まり、その後に後続ノードが続き、この場合、コード・モーションが適用されて後でパーシャル・ライブ・ノードが生成される。
パーシャル・ライブ・ノード群に関するコード生成の順番は、計算によらない分岐における特定の分岐の後続分岐のロケーションによって変わり、分岐が生じるグループ・ブロックには後続分岐群が全く含まれない、グループ・ブロックにも後続分岐群のうちの一つ、又は両方が含まれるかによって変わる。従って、計算によらない分岐に関しては、パーシャル・デッド・コードを生成するためにコードを必要とする3つの異なる関数が存在する。
計算によらない分岐で終了するブロックに関して埋め込まれるコードは、どの後続分岐も同じグループ・ブロックに含まれない場合、次のテーブル3に示す順番に従って生成される。
セクションAに埋め込まれる命令は、フル・ライブ・ノードに必要な命令の全てを含む。パーシャル・デッド・コードの除去が行われなくなるか、あるいはパーシャル・デッド・ノードが検出されない場合、セクションAのフル・ライブ・ノード群はブロック(後続ブロックを除く)に関するIRノード群の全てを表わす。セクションBに埋め込まれる命令は、後続ノードの機能を実行する。次にコード生成パスがCに達する(分岐条件が「偽」である場合)か、あるいはEにジャンプする(分岐条件が「真」である場合)。パーシャル・デッド・コードの除去を行なわない場合、セクションDに埋め込まれる命令は後続コードの直後に続く。しかしながら、パーシャル・デッド・コードの除去を行なう場合は、偽パスに対応するパーシャル・ライブ・ノード群は、偽の時の分岐先へのジャンプが行なわれる前に実行する必要がある。同様に、パーシャル・デッド・コードの除去を行なわない場合、セクションFにおいて生成される第1命令のアドレスは普通、条件が真であった場合の後続コードの宛先アドレスであったが、パーシャル・デッド・コードの除去を行なう場合は、セクションEにおける真パスに対応するパーシャル・ライブ・ノード群は、最初に実行する必要がある。
両方の後続分岐が同じグループ・ブロックに存在する場合、同期コードを生成する必要がある。両方の後続分岐が同じグループ・ブロックに存在する場合、多くの要素がコードを埋め込む順番に影響を及ぼし、これらの要素としては、各後続分岐が変換されてしまっているか、あるいはどの後続分岐の実行回数が多いかのような項目が挙げられる。両方の後続分岐が同じグループ・ブロックに存在する場合に埋め込まれるコードは通常、パーシャル・ライブ・ノード群をこの時点で同期コード(このコードがある場合)の生成前に生成する必要があることを除いて、いずれの後続分岐もグループ・ブロックには無い場合に関して上に記載したものと同じである。計算によらない分岐で終了するブロックに関して埋め込まれるコードは、両方の後続分岐が同じグループ・ブロックに含まれる場合、次のテーブル4に示す順番に従って生成される。
計算によらない分岐の後続分岐のうちの一つの分岐が同じグループ・ブロックに存在し、かつ他の後続分岐がグループ・ブロックの外部に存在する場合、同じグループ・ブロック内部のノード群に関するパーシャル・ライブ・コードは、両方の後続分岐が同じグループ・ブロックに存在する場合に関連して上に記載したように処理される。
外部後続分岐の場合、外部後続分岐に関するパーシャル・ライブ・コードは、GroupBlockExitの前に、インライン展開の形で埋め込まれる場合があり、かつグループ・ブロックのエピローグ・セクションに埋め込まれる場合がある。エピローグに位置するように意図されたパーシャル・ライブ・コードはインライン展開によって生成され、次にエピローグ・オブジェクトのテンポラリー・エリアにコピーされる。命令ポインタをリセットし、その後、ステートを復元して、インライン展開する必要のあるコードをパーシャル・ライブ・コードに上書きすることができる。エピローグを生成する時になると、コードをテンポラリー・エリアから適切な場所のエピローグにコピーする。
パーシャル・デッド・ノード群に関するコード生成を実行するために、traverseGenerate()におけるループと同じ機能を有するnodeGenerate()関数を利用して3セットのノードの各々を生成する。正しいセットが毎回確実に生成されるようにするために、nodeGenerate()関数は、refCountに一致するdeadCountを有するノード群を無視する。従って、nodeGenerate()が(traverseGenerate()から)呼び出される最初の時には、フル・ライブ・ノード群のみが生成される。一旦、後続コードが生成されてしまうと、2セットのパーシャル・ライブ・ノードは、これらのノードのdeadCountを、nodeGenerate()が再度呼び出される直前にゼロに設定することにより生成することができる。
遅延バイトスワッピング最適化(Lazy Byteswapping Optimization)
トランスレータ19の好適な実施形態において用いる別の最適化は「遅延(lazy)」バイトスワッピングである。この方法によれば、最適化は、基本ブロックの中間表現(IR)の内部の連続するバイトスワップ操作の実行を防止して、連続するバイトスワップ操作が最適化されるようにすることにより行われる。この最適化手法は、グループ・ブロック内の基本ブロック群の全てに適用して、バイトスワップ操作を遅延させ、かつバイトスワップ値を使用する必要がある時点においてのみ適用するようにする。
バイトスワッピングとは、ワード内のバイト群の位置を入れ替えてワード内のバイト群の順番を反転させる操作を指す。このようにして、最初のバイト及び最後のバイトの位置が入れ替わり、2番目のバイト及び最後から2番目のバイトの位置が入れ替わる。バイトスワッピングは、ワード群を、リトルエンディアン(little-endian )演算環境に関して生成されたビッグエンディアン(big-endian)演算環境において使用するか、あるいはその逆の関係の演算環境において使用する場合に必要となる。ビッグエンディアン演算環境では、ワード群をメモリにMSB順に保存するため、これはワードの最上位バイトが第1アドレスを有することを意味する。リトルエンディアン演算環境では、ワード群をメモリにLSB順に保存するため、これはワードの最下位バイトが第1アドレスを有することを意味する。
いずれの所与のアーキテクチャも、リトルエンディアン又はビッグエンディアンのいずれかである。従って、トランスレータに関するいずれの所与の対象/目的プロセッサ・アーキテクチャペアに関しても、特定のトランスレータ・アプリケーションをコンパイルする場合に、対象プロセッサ・アーキテクチャ及び目的プロセッサ・アーキテクチャが同じエンディアン設定を有するか否かについて判断する必要がある。データは、対象プロセッサ・アーキテクチャを理解することができるように、メモリに対象エンディアン形式(subject-endian format )で配列される。従って、目的プロセッサ・アーキテクチャによるデータの理解を実現するために、目的プロセッサ・アーキテクチャは、対象プロセッサ・アーキテクチャと同じエンディアン設定を有する必要があるか、あるいは異なる場合は、メモリからロードするか、あるいはメモリに保存するデータは全てバイトスワップして目的エンディアン形式(target-endian format)にする必要がある、のいずれかである。対象プロセッサ・アーキテクチャ及び目的プロセッサ・アーキテクチャのエンディアン設定が異なる場合、トランスレータはバイトスワッピングを起動する必要がある。例えば、対象プロセッサ・アーキテクチャ及び目的プロセッサ・アーキテクチャが異なる状況においては、データの特定ワードをメモリから読み出す場合に、バイト群の順番は、操作の実行の前には必ず入れ替えて、バイト群が目的プロセッサ・アーキテクチャにおいて期待される順番になるようにする必要がある。同様に、データの特定ワードが計算されており、特定ワードをメモリに書き出す必要がある場合、バイト群は、これらのバイトがメモリにおいて期待される順番になるように再度スワップする必要がある。
遅延バイトスワッピングとは、本トランスレータ19によって行われる方法を指し、この方法では、値が実際に使用されるまで、バイトスワップ操作をワードに対して実行する時点を遅らせる。ワードに対して実行するバイトスワップ操作をワードの値が実際に利用されるまで遅らせることにより、連続するバイトスワップ操作がブロックのIRに含まれるか否かを判断することができ、従ってこれらの操作を生成される目的コードから取り除くことができる。バイトスワップをデータの同じワードに対して2回実行する操作は、正味の効果をもたらすことはなく、単にワードのバイト群の順番を2回反転させるだけであるため、ワードのバイト群の順番がこれらのバイトの最初の順番に戻る。遅延バイトスワッピングによる最適化を行って、連続するバイトスワップ操作をIRから取り除くことができるため、目的コードをこれらの連続するバイトスワップ操作に関して生成する必要がなくなる。
トランスレータ19によるIRツリーの生成に関連して前に記載したように、ブロックのIRを生成する場合、各レジスタ定義はIRノード群から成るツリーである。各ノードは一つの表現式として認識される。各表現式は多くのチャイルド・ノードを有することができる。これらの項目に関する簡単な例を提示するために、レジスタが「3+4」として定義される場合、定義のトップ・レベル表現は2つのチャイルド、すなわち「3」及び「4」を有する「+」である。「3」及び「4」も表現であるがこれらはチャイルドを持たない。バイトスワップは一つのチャイルド、すなわちバイトスワップ対象の値を有するタイプの表現である。
図12を参照すると、遅延バイトスワッピング最適化手法を用いる好適な方法が示される。グループ・ブロック・モードにおいて、ブロックのIRをステップ100で分析して各対象レジスタ定義の位置を特定し、この位置では、各対象レジスタ定義に関して、定義のトップ・レベル表現がバイトスワップであるか否かをステップ102において判断する。遅延バイトスワッピング最適化は、バイトスワップ操作をそのトップ・レベル表現として含まない対象レジスタ定義には適用しない(ステップ104)。トップ・レベル表現がバイトスワップである場合、バイトスワップ表現はIRからステップ106において取り除かれ、このレジスタの遅延バイトスワップ・フラグを設定する。バイトスワップが取り除かれるという表示は基本的に、廃棄されるバイトスワップ表現を有するバイトスワップのチャイルドとして再定義されるレジスタを参照する。これによって、このレジスタに定義される値が、期待とは反対のバイト順番になる。このようになるのは、レジスタの値を正しく使用することができる前にバイトスワップを実行する必要があるからであることを念頭に置く必要がある。
バイトスワップ表現が取り除かれ、かつこのレジスタに定義される値が、期待とは反対のバイト順番になっていることを表示するために、遅延バイトスワップ・フラグを当該レジスタに対して設定する。各レジスタに関連するフラグ、すなわちBoolean値があり、このBoolean値は、当該レジスタの値が正しいバイト順番になっているか、あるいは反対のバイト順番になっているかを表わす。レジスタの値を使用することが望ましく、かつ当該レジスタの遅延バイトスワップ・フラグが設定されている(すなわち、フラグのBoolean値が「真」に切り換わっている)場合、レジスタの値はまず、この値を使用することができる前にバイトスワップする必要がある。図12に示すこの最適化を適用することにより、バイトスワップ表現をIRから取り除くが、この操作は、バイトスワップ操作を、レジスタの値が実際に使用されるまで遅らせることができるように行なわれる。この最適化の作用によりバイトスワップを、値が実際に使用されるポイントまで、かつこれらのバイトスワップがメモリからロードされるポイントに遅らせることができる。値が使用されるポイントがたまたま、メモリに戻る形のストアとなる場合には、2つの連続するバイトスワップを取り除くことができるという結果から得られる最適化による省力化が実現する。
一旦、「真」として設定された遅延バイトスワップ・フラグを有するレジスタが参照されると、IRを変更してバイトスワップ表現を、ブロックのIRの参照表現(referenced expression )の上に挿入する必要がある。別のバイトスワップ表現がIRの中の挿入バイトスワップ表現に隣接する場合、最適化を適用して、いずれのバイトスワップ操作も目的コードの中で生成されないようにする。
新規の値がレジスタに格納されると必ず、当該レジスタの遅延バイトスワップ・ステートをリセットするが、これは、当該レジスタの遅延バイトスワップ・フラグに関するBoolean値が「偽」に設定されることを意味する。遅延バイトスワップ・フラグが「偽」に設定される場合、バイトスワップは、レジスタの値が使用される前に実行する必要はない。何故なら、レジスタの値は既に、目的プロセッサ・アーキテクチャによって期待される正しいバイト順番になっているからである。「偽」の遅延バイトスワップ・ステートは全てのレジスタ定義に関するデフォルト・ステートであるため、レジスタを定義するときには必ず、フラグを設定してこのデフォルト・ステートを表わすようにする必要がある。
遅延バイトスワップ・ステートは、IRにおけるレジスタ群の各々に関する全ての遅延バイトスワップ・フラグから成る集合である。どの時点においても、レジスタ群は「セットされる」(レジスタのBoolean値が「真」である)又は「リセットされる」(レジスタのBoolean値が「偽」である)ことによりレジスタ群の各々の現行ステートを示す。グループ・ブロックの内部の所与のブロックのエグジット・ステート(すなわち、遅延バイトスワップ・フラグ群から成る集合)は、グループ・ブロックを通過するホット・パスの内部の次のブロックに関するエントリー・ステートとしてコピーされる。上に詳細を記載したように、グループ・ブロックは、特定の方法により互いに接続される基本ブロック群の集まりから成る。グループ・ブロックを実行すると、異なる基本ブロックを通過するパスに従って各基本ブロックが今度はグループ・ブロックを出るまで実行される。所与のグループ・ブロックの場合、グループ・ブロックの種々の基本ブロックを通過する多くの使用可能な実行パスが存在し得、この場合、所謂「ホット・パス」は最も多い頻度でグループ・ブロックを通過するパスである。「ホット・パス」は、最適化がパスの頻繁な使用に起因して行われる場合に、グループ・ブロックを通過する他のパスよりも優先されることが好ましい。このためには、グループ・ブロックが生成されると、「ホット・パス」に沿ったブロック群が「最初に」生成され、これによって、ホット・パスに存在する各ブロックのエントリー・バイトスワップ・ステートが設定されて、ホット・パスに存在する前のブロックのエグジット・ステートに等しくなる。
有効パス群のうちの一つのパスが、既に生成されている当該ブロックに関するコードを有する基本ブロックにループ・バックする状況においては、レジスタ群の現行の遅延バイトスワップ・ステートが、このコードがこの生成コードが単純に実行される前に取ると期待されるステートとなることを保証する必要がある。この前提条件は、同期コードを相対的にコールドなパスに位置するブロック群の間に埋め込むことにより、当該ブロックのエントリー遅延バイトスワップ・ステートでエンコードされる。同期は、現行基本ブロックのエグジット・ステートから次のブロックのエントリー・ステートに移行する動作である。各レジスタに関して、遅延バイトスワップ・フラグ群をブロック群の間で分析して、これらのフラグが同じであるか否かを判断する必要がある。遅延バイトスワップ・フラグ群が同じである場合は何も行なう必要はないが、これらのフラグが異なる場合は、当該レジスタの中に現在格納されている値をバイトスワップする必要がある。
グループ・ブロック・モードから基本ブロック・モードに戻ると、遅延バイトスワップ・ステートが修正される。修正は、現行ステートからnullステートへの同期であり、この場合、全ての遅延バイトスワップ・フラグがグループ・ブロック・モードから出るときにリセットされる。
遅延バイトスワッピング最適化は、浮遊小数点レジスタにおけるロード及びストアに利用することもでき、これによって、浮遊小数点バイトスワップ(floating point byteswap )に要するコストに起因する一層大きな省力化が最適化によってもたらされる。単精度浮動小数点数を、コードがロードする必要がある状況においては、単精度浮動小数点ロードをバイトスワップし、次に直ちに倍精度数に変換する必要がある。同様に、逆変換は、コードが単精度数を後で保存する必要がある場合には必ず実行する必要がある。浮動小数点ストア及びロードに関するこれらの状況を打開するために、各浮動小数点レジスタに関する互換性タグに余分なフラグを設けて、バイトスワップ及び変換の両方を遅れて実行することができるようにする(すなわち、値が要求されるまで遅らせる)。
遅れてバイトスワップされるレジスタが参照されて、バイトスワップ操作が上に記載したように参照レジスタの上に埋め込まれる場合、更なる最適化では、バイトスワップされた値をレジスタに書き戻し、遅延バイトスワップ・フラグをリセットする。書き戻し機構(writeback mechanism )と呼ばれるこのタイプの最適化は、レジスタのコンテンツが繰り返し使用される場合に有効である。遅延バイトスワッピング最適化を用いる目的は、値を使用することが必要になるまで、実際のバイトスワッピング操作を遅らせることであり、この場合、この遅れは、レジスタの値が決して利用されない場合においてか、あるいは連続するバイトスワップ操作を最適化することができる場合において、目的コードを減らすために有効である。しかしながら、一旦、レジスタのコンテンツを実際に使用すると、遅らせたバイトスワップ操作を実行する必要があり、従って遅延バイトスワッピングによりもたらされる省力化はもはや得られない。更に、遅延バイトスワッピング最適化を既に実行してしまい、かつレジスタの値を複数の後続ブロックにおいて繰り返し使用する場合、レジスタの値は間違ったエンディアン値を有することになり、バイトスワップ操作を各使用の前に埋め込む必要があるため、複数のバイトスワップ操作が必要になる。これによって、非効率な目的コードが遅延バイトスワッピング操作を用いなかった場合よりも悪い形で実行されることになる。
同じレジスタ値に対して実行する複数のバイトスワップ操作からもたらされるこの非効率的な目的コード生成を回避するために、遅延バイトスワッピング最適化は更に、第1バイトスワップ操作をレジスタの値に対して実行する必要が生じると直ちに、レジスタをその目的エンディアン値(target-endian value )に再定義して、バイトスワップした値をレジスタに書き戻す書き戻し機構を含む。このレジスタに関する遅延バイトスワップ・フラグもこの時点でリセットして、レジスタがその期待される目的エンディアン値を含むことを表示する。これにより、レジスタは、後続ブロック群の各々に対応する、レジスタの修正済み目的エンディアン・ステートになり、全体の目的コード効率は、遅延バイトスワッピング最適化を全く適用しなかった場合の効率と同じになる。このようにして、遅延バイトスワッピング最適化によって必ず、目的コードは、遅延バイトスワッピング最適化を実行しない場合に生成される目的コードよりも効率が少なくとも高い、ことによるとそれよりも遥かに高い形で生成される。
図13A〜13Cは、上に記載した遅延バイトスワッピング最適化の例を示している。対象コード200は例を簡単にするために、図13Aに、いずれかの特定アーキテクチャのマシンコードではなく擬似コードとして例示する。対象コード200は、多数回のループ周回、レジスタr3への値のロード、及び元の状態に戻した当該値の保存を記述する。グループ・ブロック202を生成して、図13Aに示す2つの基本ブロック、ブロック1及びブロック2を含める。遅延バイトスワッピング機構を実行しない場合、2つの基本ブロックに関して生成される中間表現(IR)は図13Bに示すようなものとなる。説明を簡単にするために、条件レジスタをレジスタr1に基づいて設定するIRはこの図には示さない。
一旦、ブロック1及び2に関するIRが生成されると、レジスタ定義リストを分析して、定義の最上位レベル・ノードとしてのバイトスワップを探し出す。この操作を行なうと、レジスタr3に関する最上位レベル・ノード204がバイトスワップ(BSWAP)として定義されていることが検出される。レジスタr3の定義を変更してバイトスワップ・ノード204のチャイルド・ノード、すなわちロード・ノード206の定義とし、この場合、遅延バイトスワッピングが呼び出されていることを念頭に置く必要がある。ブロック2のIRでは、レジスタr3がノード208によって参照されることが分かる。遅延バイトスワッピングがレジスタr3の定義において呼び出されているため、バイトスワップを、図13Cの挿入バイトスワップ(BSWAP)ノード214で示すように、バイトスワップを使用することができる前にこの参照の上に埋め込む必要がある。この状況においては、2つの連続するバイトスワップ、BSWAPノード210、及びBSWAPノード214がブロック2のIRに現われる。次に、遅延バイトスワッピング最適化によって、これらのbswap210及びbswap214の両方を畳み込んで、図13Cに示すように、バイトスワップ表現がブロック1及びブロック2の両方に関するIRから取り除かれるようにする。この遅延バイトスワッピング最適化の結果として、ロード・ノード206(このノードはループに位置し、そして複数回実行される)の上のバイトスワップ204、及びブロック2のストア・ノード212に接続されるバイトスワップ210がIRから取り除かれるため、これらのバイトスワップ操作が目的コードの中に生成される現象を無くすことにより大きな省力化が達成される。
インタープリタ
種々の新規なインタープリタ機能をトランスレータ機能と連携させて実行する例示としての別の装置を図14に示す。図14は、目的レジスタ15を含む目的プロセッサ13を、多くのソフトウェア・コンポーネント19,20,21,及び22を格納するメモリ18ととも示す。これらのソフトウェア・コンポーネントは、トランスレータ・コード19、オペレーティング・システム20、変換済みコード21、及びインタープリタ・コード22を含む。ここで、図14に示す装置は図1に示すトランスレータ装置にほぼ類似するが、追加の新規なインタープリタ機能が図14の装置におけるインタープリタ・コード22によって付加される点が異なることに注目されたい。図14のコンポーネントは、図1に関して説明した、同じ番号が付与されたコンポーネントと同様に機能するため、同じ番号が付与されたこれらのコンポーネントに関する説明は繰り返す必要がないものとして、図14に関する説明から省く。図14に関する以下の説明では、追加として設けられたインタープリタ機能に焦点を当てる。
上に詳細に記載したように、対象コード17を目的プロセッサ13上で実行しようとする場合、トランスレータ19は対象コード17のブロック群を変換済みコード21に変換して、これを目的プロセッサ13が実行する。特定の状況においては、最初に対象コード17を変換済みコード21に変換して実行するのではなく、対象コード17の一部を解釈してこれらの部分を直接実行すると更に大きな利点が得られる。対象コード17を解釈すると、変換済みコード21を格納する必要がなくなることによってメモリを節約することができ、更に対象コード17の変換を待つことから生じる遅延を回避することにより遅延特性を改善することができる。対象コード17を解釈する操作は通常、単に変換済みコード21を実行するよりも長い時間を要する。何故なら、インタープリタ22は対象プログラムの各ステートメントを、ステートメントが実行される度に解析して、次に所望の操作を実行する必要があるが、変換済みコード21は単に操作を実行するのみであるからである。この実行時間解析は、「解釈オーバーヘッド(interpretive overhead )」として知られる。非常に多くの回数に渡って実行される対象コードの部分に関しては、コードを解釈する操作は、コードを変換する操作よりも特に長い時間を要するため、変換済みコードを、その都度変換を必要とせずに再使用するとよい。しかしながら、少ない回数しか実行されない対象コード17の部分に関しては、対象コード17を解釈する操作は、対象コード17を変換済みコード21に変換する操作と、それに続いて変換済みコード21を実行する操作との両方を合わせた時間よりも短い時間で実行できる。
対象コード17を目的プロセッサ13上で実行する操作の効率を最適化するために、図14において具体化される装置は、インタープリタ22及びトランスレータ19の組合せを利用して対象コード17のそれぞれの部分を実行する。代表的なマシン・インタープリタは、当該マシンの全命令セットを入力/出力機能と併せてサポートする。しかしながら、このような代表的なマシン・インタープリタは極めて複雑であり、かつ複数のマシンの全命令セットをサポートする必要がある場合には更に複雑になる。対象コード中で具体化される代表的なアプリケーション・プログラムにおいて、非常に多くの数の対象コード・ブロック(すなわち基本ブロック群)は、対象コードを実行するように設計されたマシンの命令セットのほんの一部しか利用しない。
従って、本実施形態において記載するインタープリタ22は、対象コード17に関して使用することができる命令セットのほんの一部をサポートする、すなわち対象コード17の非常に多くの基本ブロックに渡って利用される命令のうちのほんの一部をサポートする簡易構成のインタープリタであることが好ましい。インタープリタ22を利用する理想的な状況は、インタープリタ22が処理することができる対象コード17の基本ブロック群のほとんどが、非常に少ない回数しか実行されない場合である。インタープリタ22はこれらの状況において特に有利である。何故なら、対象コード17の非常に多くのブロックは、トランスレータ19によって変換済みコード21に変換される必要が全くないからである。
図15は例示としての方法を表わし、この方法によって、図14の装置は、対象コード17のそれぞれの部分を解釈するのか、あるいは変換するのかを判断する。まず、対象コード17を解析する場合には、ステップ300において、インタープリタ22が実行されるべき対象コード17をサポートするか否かが判断される。インタープリタ22は、これらには限定されないがPPCインタープリタ及びX86インタープリタを含む、どのような数の利用可能なプロセッサ・アーキテクチャに関する対象コードもサポートするように設計することができる。インタープリタ22が対象コード17をサポートしない場合、本発明の他の実施形態に関連して上に記載したように、対象コード17はステップ302において、トランスレータ19によって変換される。インタープリタ22が全てのタイプの対象コード17に対して均等に機能することができるようにするために、ヌル・インタープリタ(何も実行しないインタープリタ)をサポートされない対象コードに対して使用して、サポートされない対象コードが特別な形で処理される必要がないようにすることができる。インタープリタ22がサポートする対象コード17に関して、インタープリタ22により処理されるべき対象コード命令セットのサブセットがステップ304において決定される。この命令サブセットによって、インタープリタ22は対象コード17のほとんどを解釈することができる。ここでは命令のインタープリタ・サブセットと呼ばれる、インタープリタ22がサポートする命令のサブセットを決定する方法について、以下に更に詳細に記載する。命令のインタープリタ・サブセットは、単一のアーキテクチャ・タイプに向けられる命令を含むことができるか、あるいは複数の利用可能なアーキテクチャに渡って拡張された命令を含むことができる。命令のインタープリタ・サブセットは、図15の解釈アルゴリズム(interpreting algorithm)を実際に実行する前に決定され、格納されることが好ましい。この場合、命令のうちの格納されるインタープリタ・サブセットはステップ304において取り出される可能性が高くなる。
対象コードのブロック群は、ステップ306において、1回に一ブロックの割合で解析される。ステップ308では、対象コード17の特定ブロックが、インタープリタ22がサポートする命令サブセットに含まれる命令のみを含むか否かが判断される。対象コード17の基本ブロックに含まれる命令が、命令のインタープリタ・サブセットに含まれる場合、インタープリタ22はステップ310において、このブロックの実行回数が所定の変換しきい値に達したか否かを判断する。変換しきい値は、インタープリタ22が基本ブロックを、ブロックを解釈する方が基本ブロックを変換するよりも効率が悪くなる前に実行することができる回数として選択される。一旦、実行回数が変換しきい値に達すると、ステップ302において、対象コード17のブロックはトランスレータ19により変換される。実行回数が変換しきい値を下回る場合、インタープリタ22は、ステップ312において、当該ブロックの対象コード17を命令ごとに解釈する。次に、制御がステップ306に戻って、対象コードの次の基本ブロックの解析が行われる。解析されるブロックが命令のインタープリタ22サブセットに含まれない命令を含む場合、対象コード17のそのブロックには解釈不能のマークが付けられ、ステップ302において、このブロックはトランスレータ19により変換される。このようにして、対象コード17のそれぞれの部分は最適性能を実現するために必要に応じて、解釈されるか、あるいは変換される。
このアプローチを使用して、インタープリタ22は、基本ブロックに解釈不能のマークが付けられているか、あるいは基本ブロックの実行回数が既に変換しきい値に達していることがない限り(これらの場合は、基本ブロックはそれらのインスタンスに変換される)、対象コード17の基本ブロックを解釈する。特定の状況においては、インタープリタ22がコードの実行中であり、解釈不能のマークが付けられたか、あるいは変換しきい値に達した実行回数(通常、分岐において格納されている)を有する対象コードの対象アドレスに遭遇する。これらの場合、トランスレータ19は次の基本ブロックを変換する。
ここで、インタープリタ22は、基本ブロック・オブジェクトを生成せずにメモリを節約し、実行回数は基本ブロック・オブジェクトにではなく、キャッシュに格納されることに注目されたい。インタープリタ22が、サポートされる分岐命令を通過する度に、インタープリタ22は、分岐先のアドレスに関連するカウンタをインクリメントする。
命令セットのインタープリタ・サブセットは、種々の利用可能な方法によって決定され、コードを解釈する操作とコードを変換する操作との間で生じる性能上のトレードオフに基づいて種々の形で選択される。好適には、命令のインタープリタ・サブセットは、命令が一連の選択プログラム・アプリケーションに渡って検出される頻度を測定することにより、対象コード17の解析前に定量的に得られる。どのようなプログラム・アプリケーションを選択してもよいが、これらのアプリケーションは、明らかに異なるタイプを含んで広い命令スペクトル(spectrum of instructions)をカバーするように注意深く選択されることが好ましい。例えば、これらのアプリケーションは、オブジェクティブCアプリケーション(例えば、TextEdit,Safari)、カーボン・アプリケーション(例えば、Office Suite)、広く使用されているアプリケーション(例えば、Adobe,Macromedia)、又はいずれかの他のタイプのプログラム・アプリケーションを含むことができる。次に、選択アプリケーションに渡る最大の基本ブロック範囲が提供されるように命令サブセットが選択される、これは、この命令サブセットが、この命令サブセットを使用して解釈可能な最大数の完全基本ブロック(complete basic block)を提供することを意味する。最大数の基本ブロックの全てを対象とする命令は必ずしも、最も頻繁に実行又は変換される命令と同じではないが、結果として得られる命令サブセットは、最も頻繁に実行又は変換される命令にほぼ対応する。この命令のインタープリタ・サブセットはメモリに格納されて、インタープリタ22により呼び出されることが好ましい。
特定の選択プログラム・アプリケーションに対して、モデルも使用して実験を行なうことにより、本発明の発明者らは、(特別にテストされるアプリケーションに関する合計115個の命令の中から)最も頻繁に変換される命令と、最も頻繁に変換される命令を使用して解釈可能な基本ブロックの数との間の相関が、次のテーブルにより表わすことができることを見出した。
これらの結果から、対象コード17の基本ブロック群の約80〜90%をインタープリタ22が、最も頻繁に変換される30個の命令のみを使用して解釈可能であることが分かる。更に、相対的に少ない実行回数を有するブロック群は、解釈に関して相対的に高い優先度が付与される。何故なら、インタープリタ22を使用して得られる利点のうちの一つは、メモリを節約することであるからである。最も頻繁に変換される30個の命令を選択することによって、解釈可能なブロック群の25%が1回のみ実行され、解釈可能なブロック群の75%が50回以下の回数だけ実行されることが判明した。
単なる例として、約50μsに10個の対象命令の「平均」基本ブロックを変換し、1個の対象命令をこのような基本ブロックにおいて15nsで実行する仮定コストを使用して、最も頻繁に変換される命令を解釈することにより可能になる省力化を推定するために、次のテーブルに含まれる推定は、どのくらい良好にインタープリタ22が動作して、最も頻繁に変換される30個の命令の使用に基づく大きな利点をインタープリタ22にもたらす必要があるのかについて示している。
最大変換しきい値は、コストがブロック変換コストを上回る前にインタープリタ22がブロックを実行することができる回数に等しくなるように設定されている。
命令のうち、対象コード命令セットから選択される特定のインタープリタ・サブセットは、解釈機能及び変換機能の所望の動作に従って種々に調整可能である。更に、対象コード17の特化された部分を、変換するのではなく、解釈する必要があるインタープリタ22命令サブセットに含めることも重要である。特に解釈する必要のある、対象コードのこのような一つの特化された部分はトランポリン(trampoline)と呼ばれ、OSXアプリケーションにおいて多用される。トランポリンは、実行時に動的に生成されるコードの小さな部分である。トランポリンは、高位言語(HLL:high-level language )、及び実行可能な小さなコード・オブジェクトをその場で(on-the-fly)生成してコード・セクション間の間接参照(indirection )を行なう(例えば、Macintosh(登録商標)上の)プログラム・オーバーレイ構造に時々見ることができる。BSDでは、かつ恐らくは他のユニックスにおいては、信号(この信号にはハンドラーが設定されている)がプロセスに送信される場合、トランポリン・コードを使用して制御をカーネルからユーザ・モードに戻す。トランポリンが解釈されない場合、パーテションを各トランポリンに関して生成する必要があり、これによってメモリ使用率が極めて高くなる。
最も頻繁に変換される命令(例えば上位30個の命令)のうちの特定の割合を処理する機能を備えるインタープリタ22を使用することにより、インタープリタ22は、テスト・プログラムにおける対象コードの全ての基本ブロックの約80%を解釈することが判明した。変換しきい値を50回の実行回数と100回の実行回数との間に設定し、同時にインタープリタが、対象命令のブロックについて変換ブロックよりも20倍以上遅くなることがないようにすることにより、全ての基本ブロックの60〜70%が変換されないようにする。これによって、絶対に生成されることがない目的コード21が減る結果として、メモリを30〜40%という大きな割合で節約することができる。レイテンシーも、不要と考えられるワークを遅らせることにより改善することができる。
ここで、インタープリタ22によって実現される上述の省力化は、インタープリタ22を特定の形で使用することにより得られる実験結果に基づくものであったことに留意されたい。命令のうち、対象コード命令から選択される特定のインタープリタ・サブセット、及び選択された特定の変換しきい値のようなインタープリタ22の種々の特徴は、インタープリタ22の特定の実施形態、及び解釈機能と変換機能との間で実現される所望のバランスに基づいて種々に選択される。更に、命令の特定のインタープリタ・サブセットは、特定の目的アプリケーション・プログラムを解釈する機能として選択される。
幾つかの好適な実施形態について示し、説明してきたが、当該技術分野の当業者であれば、種々の変更及び変形を、添付の請求項に定義する本発明の技術範囲から逸脱しない範囲において加え得ることが分かるであろう。
本出願に関連する本明細書と同時に、あるいは本明細書よりも先に出願され、かつ本明細書を用いて公に調査することができる全ての論文及び文書に関心が向けられることになるので、このような論文及び文書の全ての内容は、本明細書において参照することにより本発明の開示に含まれる。
本明細書に開示する特徴の全て(全ての添付の請求項、要約、及び図を含む)、及び/又はこのようにして開示される全ての方法又はプロセスのステップの全ては、このような特徴及び/又はステップの少なくとも特定の部分が相容れない組合せを除き、どのような組合せでも組み合わせることができる。
本明細書に開示する各特徴(全ての添付の請求項、要約、及び図を含む)は、特に断らない限り、同じ、等価な、又は同様の目的を達成する別の特徴により置き換えることができる。従って、特に断らない限り、開示する各特徴は一般的な一連の等価な、又は同様な特徴の一例に過ぎない。
本発明はこれまでの実施形態(複数の実施形態)の詳細に限定されない。本発明は、本明細書に開示する複数の特徴(全ての添付の請求項、要約、及び図を含む)のいずれか一つの新規の特徴、又はこれらの特徴のいずれかの新規の組合せ、あるいはこれまでに開示したいずれかの方法又はプロセスの複数のステップのいずれか一つの新規のステップ、又はこれらのステップの新規の組合せに拡張することができる。