(57)【要約】【課題】コードに含まれるループの実行時間を短縮する。【解決手段】コンパイル装置１０は、記憶部１１と変換部１２を有する。記憶部１１は、ループ内に、Ｋ回転前の演算の結果を参照する第１の変数を含む演算式１５をもつコード１３を記憶する。変換部１２は、演算式１５を、Ｋ＋１回転以上前の演算の結果を参照する第２の変数を用いて、第１の変数を含まない演算式１６に展開する。変換部１２は、演算式１５に基づいてループを実行する場合の実行時間Ｔ１と、演算式１６に基づいてループ内のＪ回転目の演算とＪ＋Ｋ回転目の演算とを並列化する場合の実行時間Ｔ２とを比較する。変換部１２は、比較の結果に応じて、コード１３を、Ｊ回転目の演算とＪ＋Ｋ回転目の演算とを並列に実行させる並列処理命令を含むコード１４に変換するか決定する。【選択図】図１
2015191346.tif 000002
【特許請求の範囲】
【請求項１】
コンピュータに、
第１のコードからループを検出し、
前記ループに含まれる演算式であってＫ回転前（Ｋは１以上の整数）の演算の結果を参照する第１の変数を含む第１の演算式を、Ｋ＋１回転以上前の演算の結果を参照する第２の変数を用いて、前記第１の変数を含まない第２の演算式に展開し、
前記第１の演算式に基づいて前記ループを実行する場合の第１の実行時間と、前記第２の演算式に基づいて前記ループ内のＪ回転目（Ｊは１以上の整数）の演算とＪ＋Ｋ回転目の演算とを並列化する場合の第２の実行時間とを比較し、
前記比較の結果に応じて、前記第１のコードを、前記Ｊ回転目の演算と前記Ｊ＋Ｋ回転目の演算とを並列に実行させる並列処理命令を含む第２のコードに変換するか決定する、
処理を実行させるコンパイルプログラム。
【請求項２】
前記第２の演算式が複数の演算子を含んでおり、前記複数の演算子の実行順序を入れ替えることで前記第２の実行時間が短くなる場合、前記実行順序を入れ替えた第２の演算式に基づいて前記第２のコードを生成する、
請求項１記載のコンパイルプログラム。
【請求項３】
前記第２の実行時間は、プロセッサが前記並列処理命令の実行に要するサイクル数を示すプロセッサ情報を用いて算出する、
請求項１または２記載のコンパイルプログラム。
【請求項４】
コンピュータが実行するコンパイル方法であって、
第１のコードからループを検出し、
前記ループに含まれる演算式であってＫ回転前（Ｋは１以上の整数）の演算の結果を参照する第１の変数を含む第１の演算式を、Ｋ＋１回転以上前の演算の結果を参照する第２の変数を用いて、前記第１の変数を含まない第２の演算式に展開し、
前記第１の演算式に基づいて前記ループを実行する場合の第１の実行時間と、前記第２の演算式に基づいて前記ループ内のＪ回転目（Ｊは１以上の整数）の演算とＪ＋Ｋ回転目の演算とを並列化する場合の第２の実行時間とを比較し、
前記比較の結果に応じて、前記第１のコードを、前記Ｊ回転目の演算と前記Ｊ＋Ｋ回転目の演算とを並列に実行させる並列処理命令を含む第２のコードに変換するか決定する、
コンパイル方法。
【請求項５】
ループ内に、Ｋ回転前（Ｋは１以上の整数）の演算の結果を参照する第１の変数を含む第１の演算式をもつ第１のコードを記憶する記憶部と、
前記第１の演算式を、Ｋ＋１回転以上前の演算の結果を参照する第２の変数を用いて、前記第１の変数を含まない第２の演算式に展開し、
前記第１の演算式に基づいて前記ループを実行する場合の第１の実行時間と、前記第２の演算式に基づいて前記ループ内のＪ回転目（Ｊは１以上の整数）の演算とＪ＋Ｋ回転目の演算とを並列化する場合の第２の実行時間とを比較し、
前記比較の結果に応じて、前記第１のコードを、前記Ｊ回転目の演算と前記Ｊ＋Ｋ回転目の演算とを並列に実行させる並列処理命令を含む第２のコードに変換するか決定する、変換部と、
を有するコンパイル装置。
【発明の詳細な説明】
【技術分野】
【０００１】
本発明はコンパイルプログラム、コンパイル方法およびコンパイル装置に関する。
【背景技術】
【０００２】
ＣＰＵ（Central Processing Unit）などのプロセッサの中には、ＳＩＭＤ（Single Instruction Multiple Data）命令と呼ばれる１つの命令によって、異なるデータに対して同じ種類の演算を並列実行できるものがある。ＳＩＭＤ命令を実行するプロセッサは、並列に処理すべき異なるデータを組み合わせて格納するＳＩＭＤレジスタと呼ばれるレジスタを備える。例えば、ＳＩＭＤレジスタｔｓ１にデータＡ（１），Ａ（２）が格納され、ＳＩＭＤレジスタｔｓ２にデータＢ（１），Ｂ（２）が格納された状態で、ｔｓ１＋ｔｓ２というＳＩＭＤ命令が入力されたとする。すると、プロセッサは、Ａ（１）＋Ｂ（１），Ａ（２）＋Ｂ（２）という２つの加算を並列に実行することになる。
【０００３】
ＳＩＭＤ命令を含むコードを生成する方法としては、ＳＩＭＤ命令でない複数の命令の中から、演算の種類が同じであり並列に実行できる２以上の命令を抽出し、抽出した命令を組み合わせてＳＩＭＤ命令を生成する方法が考えられる。例えば、高級言語で記述されたソースコードを機械可読なオブジェクトコードに変換するコンパイル装置の中には、最適化処理として、２以上の命令を組み合わせてＳＩＭＤ命令に変換するものがある。組み合わせ可能な命令の数（ＳＩＭＤ幅）は、プロセッサのアーキテクチャによって異なる。
【０００４】
なお、ＳＩＭＤ命令への変換（ＳＩＭＤ化）にあたり、次のような処理を行うコンパイル装置が提案されている。このコンパイル装置は、命令の組み合わせの候補として第１の組み合わせと第２の組み合わせが抽出された場合、それぞれの組み合わせを採用したときの予測実行時間を算出する。そして、コンパイル装置は、第１の組み合わせと第２の組み合わせのうち、予測実行時間が短い方を選択してＳＩＭＤ化を行う。
【先行技術文献】
【特許文献】
【０００５】
【特許文献１】特開２０１３−８０４０７号公報
【発明の概要】
【発明が解決しようとする課題】
【０００６】
ところで、コンパイル装置が扱うコードには、ループが記載されていることがある。ループ内では、ループ変数の値を変えながら（例えば、ループ変数の値を１ずつ大きくしながら）、ループ変数を含む同じ演算式が繰り返し実行される。
【０００７】
このとき、ループのｍ回転目（ｍ回目のイテレーション）の演算と、当該ループのｎ回転目（ｎ回目のイテレーション）の演算とを、ＳＩＭＤ命令を用いて並列実行できる場合がある。例えば、配列Ａ，Ｂとループ変数Ｊを用いて、Ａ（Ｊ）＝Ａ（Ｊ）＋Ｂ（Ｊ）という演算式がループ内に記載されているとする。ここで、Ｊ回転目の演算Ａ（Ｊ）＝Ａ（Ｊ）＋Ｂ（Ｊ）とＪ＋１回転目の演算Ａ（Ｊ＋１）＝Ａ（Ｊ＋１）＋Ｂ（Ｊ＋１）とは、依存関係がなく独立している。よって、コンパイル装置は、Ａ（Ｊ）とＡ（Ｊ＋１）とが並列に計算されるようにＳＩＭＤ化することが考えられる。この場合、ループ内で実行される命令の数を半分程度に削減することができる。
【０００８】
一方で、ループのｍ回転目の演算と当該ループのｎ回転目の演算とを、そのままでは並列実行すべきでない場合もある。例えば、Ａ（Ｊ）＝Ａ（Ｊ−１）＋Ｂ（Ｊ）という演算式がループ内に記載されているとする。ここで、Ｊ＋１回転目の演算Ａ（Ｊ＋１）＝Ａ（Ｊ）＋Ｂ（Ｊ＋１）は、Ｊ回転目の演算Ａ（Ｊ）＝Ａ（Ｊ−１）＋Ｂ（Ｊ）の結果を参照している。よって、このままＡ（Ｊ）とＡ（Ｊ＋１）とを並列に計算しようとすると、逐次実行する場合と結果が変わってしまうおそれがあり好ましくない。このため、従来のコンパイル装置では、ループのｍ回転目の演算と当該ループのｎ回転目の演算との間に依存関係があると、ＳＩＭＤ命令を用いた最適化は活用されていなかった。
【０００９】
１つの側面では、本発明は、コードに含まれるループの実行時間を短縮するコンパイルプログラム、コンパイル方法およびコンパイル装置を提供することを目的とする。
【課題を解決するための手段】
【００１０】
１つの態様では、コンピュータに以下の処理を実行させるコンパイルプログラムが提供される。第１のコードからループを検出する。ループに含まれる演算式であってＫ回転前（Ｋは１以上の整数）の演算の結果を参照する第１の変数を含む第１の演算式を、Ｋ＋１回転以上前の演算の結果を参照する第２の変数を用いて、第１の変数を含まない第２の演算式に展開する。第１の演算式に基づいてループを実行する場合の第１の実行時間と、第２の演算式に基づいてループ内のＪ回転目（Ｊは１以上の整数）の演算とＪ＋Ｋ回転目の演算とを並列化する場合の第２の実行時間とを比較する。比較の結果に応じて、第１のコードを、Ｊ回転目の演算とＪ＋Ｋ回転目の演算とを並列に実行させる並列処理命令を含む第２のコードに変換するか決定する。
【００１１】
また、１つの態様では、コンピュータが実行するコンパイル方法が提供される。
また、１つの態様では、記憶部と変換部とを有するコンパイル装置が提供される。記憶部は、ループ内に、Ｋ回転前（Ｋは１以上の整数）の演算の結果を参照する第１の変数を含む第１の演算式をもつ第１のコードを記憶する。変換部は、第１の演算式を、Ｋ＋１回転以上前の演算の結果を参照する第２の変数を用いて、第１の変数を含まない第２の演算式に展開する。変換部は、第１の演算式に基づいてループを実行する場合の第１の実行時間と、第２の演算式に基づいてループ内のＪ回転目（Ｊは１以上の整数）の演算とＪ＋Ｋ回転目の演算とを並列化する場合の第２の実行時間とを比較する。変換部は、比較の結果に応じて、第１のコードを、Ｊ回転目の演算とＪ＋Ｋ回転目の演算とを並列に実行させる並列処理命令を含む第２のコードに変換するか決定する。
【発明の効果】
【００１２】
１つの側面では、コードに含まれるループの実行時間を短縮できる。
【図面の簡単な説明】
【００１３】
【図１】第１の実施の形態のコンパイル装置の例を示す図である。
【図２】第２の実施の形態のコンパイル装置のハードウェア例を示す図である。
【図３】第２の実施の形態のコンパイル装置の機能例を示す図である。
【図４】ＳＩＭＤ命令とＳＩＭＤレジスタの例を示す図である。
【図５】ＳＩＭＤ最適化による第１のコード変換例を示す図である。
【図６】ＳＩＭＤ化前後の演算の第１の対応例を示す図である。
【図７】ＳＩＭＤ最適化による第２のコード変換例を示す図である。
【図８】ＳＩＭＤ化前後の演算の第２の対応例を示す図である。
【図９】ＳＩＭＤ最適化による第３のコード変換例を示す図である。
【図１０】ＳＩＭＤ化前後の演算の第３の対応例を示す図である。
【図１１】ＳＩＭＤ最適化による第４のコード変換例を示す図である。
【図１２】ＳＩＭＤ化の阻害要因の例を示す図である。
【図１３】ＳＩＭＤ化前後の演算の第４の対応例を示す図である。
【図１４】ＳＩＭＤ最適化による第５のコード変換例を示す図である。
【図１５】ＳＩＭＤ最適化による第６のコード変換例を示す図である。
【図１６】プロセッサアーキテクチャの例を示す図である。
【図１７】ループ内のサイクル数の第１の計算例を示す図である。
【図１８】ループ内のサイクル数の第２の計算例を示す図である。
【図１９】ループ内のサイクル数の第３の計算例を示す図である。
【図２０】プロセッサアーキテクチャの他の例を示す図である。
【図２１】ループ内のサイクル数の第４の計算例を示す図である。
【図２２】ループ内のサイクル数の第５の計算例を示す図である。
【図２３】ループ内のサイクル数の第６の計算例を示す図である。
【図２４】ＳＩＭＤ最適化の手順例を示すフローチャートである。
【図２５】並列化可能と判断されないコード例を示す図である。
【発明を実施するための形態】
【００１４】
以下、本実施の形態を図面を参照して説明する。
［第１の実施の形態］
図１は、第１の実施の形態のコンパイル装置の例を示す図である。
【００１５】
第１の実施の形態のコンパイル装置１０は、最適化として、コード１３から並列処理命令を含むコード１４に変換することがある。並列処理命令は、異なるデータに対する演算をプロセッサに並列に実行させる単一の命令であり、例えば、ＳＩＭＤ命令である。コード１３，１４は、プロセッサに実行させたい命令の内容を記述していると言うこともできる。コード１３は、高級言語で記述されたソースコードでもよいし、ソースコードから変換された中間コードでもよい。コード１４は、最適化された中間コードでもよいし、アセンブリコードや機械可読なオブジェクトコードでもよい。また、コンパイル装置１０は、ユーザが操作する端末装置でもよいし、端末装置からアクセスされるサーバ装置でもよい。コンパイル装置１０は、コンピュータまたは情報処理装置を用いて実装してもよい。
【００１６】
コンパイル装置１０は、記憶部１１および変換部１２を有する。記憶部１１は、コード１３を記憶する。記憶部１１は、ＲＡＭ（Random Access Memory）などの揮発性の記憶装置でもよいし、ＨＤＤ（Hard Disk Drive）などの不揮発性の記憶装置でもよい。変換部１２は、記憶部１１に記憶されたコード１３をコード１４に変換することがある。変換部１２は、ＣＰＵやＤＳＰ（Digital Signal Processor）などのプロセッサでもよいし、ＡＳＩＣ（Application Specific Integrated Circuit）やＦＰＧＡ（Field Programmable Gate Array）などの特定用途の電子回路を含んでもよい。プロセッサは、例えば、記憶部１１または他の記憶装置に記憶されたプログラムを実行する。なお、複数のプロセッサの集合（マルチプロセッサ）を「プロセッサ」と呼んでもよい。
【００１７】
変換部１２は、記憶部１１に記憶されたコード１３から、ある条件を満たす演算式１５を含むループを検出する。演算式１５は、Ｋ回転前（Ｋは１以上の整数）の演算の結果を参照する変数（第１の変数）を含むものである。例えば、演算式Ａ（Ｊ）＝Ａ（Ｊ−Ｋ）＋Ｂ（Ｊ）に含まれる変数Ａ（Ｊ−Ｋ）は、Ｋ回転前の演算で値が定義されるものであり「第１の変数」に該当する。Ｋ＝１と置くと、上記の演算式はＡ（Ｊ）＝Ａ（Ｊ−１）＋Ｂ（Ｊ）となり、１回転前の演算の結果を参照していることになる。
【００１８】
このような演算式１５がループに含まれている場合、ループのＪ＋Ｋ回転目（Ｊは１以上の整数）の演算は、Ｊ回転目の演算に依存するため、このままではＪ回転目の演算と並列に実行しないことが好ましい。そこで、変換部１２は、演算式１５が「第１の変数」を含まないように変形することで、Ｊ回転目の演算とＪ＋Ｋ回転目の演算とを並列に実行できるようにする。具体的には、変換部１２は、Ｋ＋１回転以上前の演算の結果を参照する変数（第２の変数）を用いて、演算式１５を演算式１６に展開する。
【００１９】
例えば、変換部１２は、演算式Ａ（Ｊ）＝Ａ（Ｊ−Ｋ）＋Ｂ（Ｊ）に含まれる変数Ａ（Ｊ−Ｋ）をその演算式自身を用いて展開することで、演算式Ａ（Ｊ）＝Ａ（Ｊ−２Ｋ）＋Ｂ（Ｊ−Ｋ）＋Ｂ（Ｊ）を得る。展開後の演算式に含まれる変数Ａ（Ｊ−２Ｋ）は、２Ｋ回転前の演算の結果を参照するものであり「第２の変数」に該当する。２Ｋ回転前の演算の結果を参照することで、Ｋ回転前の演算との依存関係が切断される。Ｋ＝１と置くと、２回転前の演算の結果を利用して、１回転前の演算との依存関係を切断していることになる。このような変数の展開を連続的に行うことで、依存関係を過去に遡り、並列実行できる演算の範囲を広げることもできる。例えば、変数の展開を３回行うと、１〜３回転前の演算との依存関係が切断され、４回転分の演算が並列実行可能になる。
【００２０】
ただし、展開された演算式１６が示す演算量やメモリアクセス量は、展開前の演算式１５が示す演算量やメモリアクセス量よりも大きくなることが多い。このため、演算式１６に基づいて、少なくともＪ回転目の演算とＪ＋Ｋ回転目の演算とを並列化しても、展開および並列化を行わない場合よりコード１４が効率化しているとは限らない。演算式１６を利用することでコード１４が効率化するか否かは、並列度や並列処理命令の実行に要するサイクル数など、ループを実行するプロセッサのアーキテクチャに依存する。また、コード１４が効率化するか否かは、展開前の演算式１５の内容にも依存し得る。
【００２１】
そこで、変換部１２は、演算式１５に基づいてループを実行する場合の実行時間Ｔ１と、演算式１６に基づいて少なくともＪ回転目の演算とＪ＋Ｋ回転目の演算とを並列化する場合の実行時間Ｔ２とを比較する。変換部１２は、プロセッサのアーキテクチャを示すプロセッサ情報を参照して、命令スケジューリングを仮実行し、実行時間Ｔ１，Ｔ２を予測してもよい。実行時間Ｔ１，Ｔ２の単位には、サイクル数やクロック数を用いてもよい。
【００２２】
例えば、変換部１２は、演算Ａ（Ｊ）＝Ａ（Ｊ−Ｋ）＋Ｂ（Ｊ）と演算Ａ（Ｊ＋Ｋ）＝Ａ（Ｊ）＋Ｂ（Ｊ＋Ｋ）を、ＳＩＭＤ命令などの並列処理命令を利用せずに実行する場合について実行時間Ｔ１を算出する。また、変換部１２は、演算Ａ（Ｊ）＝Ａ（Ｊ−２Ｋ）＋Ｂ（Ｊ−Ｋ）＋Ｂ（Ｊ）と演算Ａ（Ｊ＋Ｋ）＝Ａ（Ｊ−Ｋ）＋Ｂ（Ｊ）＋Ｂ（Ｊ＋Ｋ）を、並列処理命令を利用して実行する場合について実行時間Ｔ２を算出する。
【００２３】
そして、変換部１２は、比較結果に応じて、コード１３からＳＩＭＤ命令などの並列処理命令を含むコード１４に変換するか決定する。例えば、実行時間Ｔ２が実行時間Ｔ１より小さい場合（実行時間が短縮する場合）、変換部１２は、コード１３をコード１４に変換すると決定する。また、例えば、実行時間Ｔ２が実行時間Ｔ１以上の場合（実行時間が短縮しない場合）、変換部１２は、コード１３をコード１４に変換しないと決定する。
【００２４】
第１の実施の形態のコンパイル装置１０によれば、Ｋ回転前の演算の結果を参照する変数を含む演算式１５が、その変数を含まない演算式１６に展開される。そして、演算式１５に基づいてループを実行する場合の実行時間Ｔ１と、演算式１６に基づいて並列化してループを実行する場合の実行時間Ｔ２とが比較され、比較の結果に応じて、コード１３から並列処理命令を含むコード１４に変換するか否かが決定される。これにより、演算式の展開による演算量やメモリアクセス量の増大と、並列処理命令を利用することによる命令数の減少が総合的に考慮され、ループの実行時間を短縮することができる。
【００２５】
［第２の実施の形態］
第２の実施の形態のコンパイル装置１００は、高級言語で記述されたソースコードをコンパイルし、機械可読なオブジェクトコードを生成する。コンパイル装置１００は、ユーザが操作する端末装置でもよいし、端末装置からアクセスされるサーバ装置でもよい。コンパイル装置１００は、例えば、コンピュータを用いて実装される。その場合、コンパイル装置１００は、ソフトウェアとしてのコンパイラを実行する。
【００２６】
図２は、第２の実施の形態のコンパイル装置のハードウェア例を示す図である。
コンパイル装置１００は、ＣＰＵ１０１、ＲＡＭ１０２、ＨＤＤ１０３、画像信号処理部１０４、入力信号処理部１０５、媒体リーダ１０６および通信インタフェース１０７を有する。これらのユニットはバスに接続されている。ＣＰＵ１０１は第１の実施の形態の変換部１２の一例であり、ＲＡＭ１０２は第１の実施の形態の記憶部１１の一例である。
【００２７】
ＣＰＵ１０１は、プログラムの命令を実行する演算回路を含むプロセッサである。ＣＰＵ１０１は、ＨＤＤ１０３に記憶されているプログラムやデータの少なくとも一部をＲＡＭ１０２にロードし、プログラムを実行する。なお、ＣＰＵ１０１は複数のプロセッサコアを備えてもよく、コンパイル装置１００は複数のプロセッサを備えてもよく、以下で説明する処理を複数のプロセッサまたはプロセッサコアを用いて並列に実行してもよい。また、複数のプロセッサの集合（マルチプロセッサ）を「プロセッサ」と呼んでもよい。
【００２８】
ＲＡＭ１０２は、ＣＰＵ１０１が実行するプログラムやＣＰＵ１０１が演算に用いるデータを一時的に記憶する揮発性の半導体メモリである。なお、コンパイル装置１００は、ＲＡＭ以外の種類のメモリを備えてもよく、複数個のメモリを備えてもよい。
【００２９】
ＨＤＤ１０３は、ＯＳ（Operating System）やミドルウェアやアプリケーションソフトウェアなどのソフトウェアのプログラム、および、データを記憶する不揮発性の記憶装置である。ＨＤＤ１０３に記憶されるプログラムには、コンパイルプログラムが含まれる。なお、コンパイル装置１００は、フラッシュメモリやＳＳＤ（Solid State Drive）などの他の種類の記憶装置を備えてもよく、複数の不揮発性の記憶装置を備えてもよい。
【００３０】
画像信号処理部１０４は、ＣＰＵ１０１からの命令に従って、コンパイル装置１００に接続されたディスプレイ１１１に画像を出力する。ディスプレイ１１１としては、ＣＲＴ（Cathode Ray Tube）ディスプレイ、液晶ディスプレイ（ＬＣＤ：Liquid Crystal Display）、プラズマディスプレイ（ＰＤＰ：Plasma Display Panel）、有機ＥＬ（ＯＥＬ：Organic Electro-Luminescence）ディスプレイなどを用いることができる。
【００３１】
入力信号処理部１０５は、コンパイル装置１００に接続された入力デバイス１１２から入力信号を取得し、ＣＰＵ１０１に出力する。入力デバイス１１２としては、マウスやタッチパネルやタッチパッドやトラックボールなどのポインティングデバイス、キーボード、リモートコントローラ、ボタンスイッチなどを用いることができる。また、コンパイル装置１００に、複数の種類の入力デバイスが接続されていてもよい。
【００３２】
媒体リーダ１０６は、記録媒体１１３に記録されたプログラムやデータを読み取る読み取り装置である。記録媒体１１３として、例えば、フレキシブルディスク（ＦＤ：Flexible Disk）やＨＤＤなどの磁気ディスク、ＣＤ（Compact Disc）やＤＶＤ（Digital Versatile Disc）などの光ディスク、光磁気ディスク（ＭＯ：Magneto-Optical disk）、半導体メモリなどを使用できる。媒体リーダ１０６は、例えば、記録媒体１１３から読み取ったプログラムやデータをＲＡＭ１０２またはＨＤＤ１０３に格納する。
【００３３】
通信インタフェース１０７は、ネットワーク１１４に接続され、ネットワーク１１４を介して他のコンピュータと通信を行うインタフェースである。通信インタフェース１０７は、スイッチなどの通信装置とケーブルで接続される有線通信インタフェースでもよいし、基地局と無線リンクで接続される無線通信インタフェースでもよい。
【００３４】
なお、コンパイル装置１００は、媒体リーダ１０６を備えていなくてもよく、ユーザが操作する端末装置から制御可能である場合には画像信号処理部１０４や入力信号処理部１０５を備えていなくてもよい。また、ディスプレイ１１１や入力デバイス１１２が、コンパイル装置１００の筐体と一体に形成されていてもよい。
【００３５】
図３は、第２の実施の形態のコンパイル装置の機能例を示す図である。
コンパイル装置１００は、ソースコード記憶部１２１、中間コード記憶部１２２、オブジェクトコード記憶部１２３、ソースコード解析部１３１、最適化部１３２、アセンブリコード生成部１３６およびファイル生成部１３９を有する。ソースコード記憶部１２１、中間コード記憶部１２２およびオブジェクトコード記憶部１２３は、例えば、ＲＡＭ１０２またはＨＤＤ１０３に確保した記憶領域として実現される。ソースコード解析部１３１、最適化部１３２、アセンブリコード生成部１３６およびファイル生成部１３９は、例えば、ＣＰＵ１０１が実行するプログラムのモジュールとして実現される。
【００３６】
ソースコード記憶部１２１は、ユーザにより作成されたコードであって、高級言語で記述されたソースコードを記憶する。高級言語の例としては、ＦＯＲＴＲＡＮやＣ言語などの手続き型言語が挙げられる。ソースコードは、コンパイル装置１００で作成されてもよいし、他の装置で作成されてコンパイル装置１００に送信されてもよい。
【００３７】
中間コード記憶部１２２は、ソースコードから変換されたコードであって、オブジェクトコードに変換される前の中間コードを記憶する。中間コードは、コンパイルの途中で内部的に用いられるコードであり、コンパイル装置１００がその表現形式を任意に決定してもよい。後述するように、最適化は中間コードに対して行われる。
【００３８】
オブジェクトコード記憶部１２３は、ソースコードから中間コードを経て生成されたコードであって、機械語で記述されたオブジェクトコードを記憶する。機械語では、命令やオペランドがビット列（数値）で表現される。オブジェクトコードは、ある種類のＣＰＵをターゲットとして生成される。オブジェクトコードを実行するＣＰＵは、コンパイル装置１００が有するＣＰＵ１０１でもよいし、他の装置が有するＣＰＵでもよい。
【００３９】
コンパイル装置１００は、複数のＣＰＵアーキテクチャのうちユーザが指定したものをターゲットとして、オブジェクトコードを生成してもよい。その場合、例えば、コンパイル装置１００は、複数のＣＰＵアーキテクチャそれぞれについて、使用可能な命令、各命令の実行に要するサイクル数、使用可能なレジスタなどを示すＣＰＵ情報を保持する。そして、コンパイル装置１００は、ユーザが指定したＣＰＵアーキテクチャのＣＰＵ情報を参照して、そのＣＰＵアーキテクチャに対応したオブジェクトコードを生成する。
【００４０】
ソースコード解析部１３１は、ソースファイル名などを含むコンパイルコマンドを受け付ける。すると、ソースコード解析部１３１は、ソースコード記憶部１２１から、指定されたソースファイルに含まれるソースコードを読み出し、字句解析、構文解析、意味解析などのフロントエンド処理を実行する。そして、ソースコード解析部１３１は、ソースコードに対応する中間コードを生成し中間コード記憶部１２２に格納する。また、コンパイルコマンドにコンパイルオプションが含まれている場合、ソースコード解析部１３１は、コンパイルオプションを最適化部１３２に通知することがある。コンパイルオプションには、後述するように、演算子の実行順序の入れ替えを許容するオプションが含まれ得る。
【００４１】
最適化部１３２は、中間コード記憶部１２２に記憶された中間コードを読み出し、処理結果を変えずに処理を効率化する余地のある中間コードを検索する。最適化部１３２は、検索された中間コードを中間コード記憶部１２２上で書き換えることで、コンパイル装置１００で生成されるオブジェクトコードの最適化を実現する。最適化部１３２は、並列化部１３３、ＳＩＭＤ化部１３４および汎用最適化部１３５を有する。
【００４２】
並列化部１３３は、ターゲットのＣＰＵが、複数のハードウェアスレッドや複数のＣＰＵコアなど、並列に命令を実行可能な複数のハードウェア単位を備えているか確認する。ターゲットのＣＰＵが複数のハードウェア単位を備えている場合、並列化部１３３は、中間コードに含まれる命令の間の依存関係を分析し、並列に実行可能な命令の組み合わせを判定する。並列化部１３３による最適化は、ＳＩＭＤ化部１３４による最適化の後に行ってもよい。並列実行される命令は、オペランド毎に１つのデータ単位を扱うスカラ命令でもよいし、オペランド毎に２以上のデータ単位を扱うＳＩＭＤ命令でもよい。
【００４３】
ＳＩＭＤ化部１３４は、ターゲットのＣＰＵがＳＩＭＤ命令を実行可能であるか確認する。ターゲットのＣＰＵがＳＩＭＤ命令を実行可能である場合、ＳＩＭＤ化部１３４は、ＳＩＭＤ命令に変換するスカラ命令の組み合わせを決定し、中間コード記憶部１２２に格納された中間コードを書き換える。１つのＳＩＭＤ命令に変換できるスカラ命令の上限数（ＳＩＭＤ幅）は、ターゲットのＣＰＵのアーキテクチャによって異なり得る。ＳＩＭＤ化部１３４は、ＳＩＭＤ幅以下の数のスカラ命令を選択してＳＩＭＤ命令に変換する。
【００４４】
このとき、ＳＩＭＤ化部１３４は、実行効率の高いＳＩＭＤ命令を生成できるよう、ソースコードが示す演算手順を処理結果が変わらない範囲で変更することがある。後述するように、第２の実施の形態では主に、ＳＩＭＤ化部１３４がループ内の演算をＳＩＭＤ化することを考える。例えば、ループ内のｉ回転目（ｉ回目のイテレーション）の演算とｉ＋１回転目（ｉ＋１回目のイテレーション）の演算との間に依存関係がない場合、この２つの演算をＳＩＭＤ命令を用いて実現することで、ループの総回転数を削減できる。
【００４５】
汎用最適化部１３５は、中間コードに対して、ターゲットのＣＰＵに依存しない汎用的な最適化を行う。汎用的な最適化には、ある演算結果を後で参照することがわかっている場合、その演算結果をレジスタから追い出さずに保持しておくことで、メモリアクセスを削減することが含まれ得る。また、汎用的な最適化には、後の演算に影響を与えない不要な演算や変数（例えば、参照されない値を算出する演算やその値を格納する変数）を削除すること、分岐命令が少なくなるように制御構造を変更することなどが含まれ得る。
【００４６】
アセンブリコード生成部１３６は、最適化部１３２による最適化が行われた後、中間コード記憶部１２２に記憶された中間コードを読み出し、最適化された中間コードをアセンブリ言語で記述されたアセンブリコードに変換する。アセンブリコード生成部１３６は、スケジューリング部１３７およびレジスタ割付部１３８を有する。
【００４７】
スケジューリング部１３７は、命令の実行効率が向上するように、処理結果が変わらない範囲で命令の順序を入れ替える。例えば、スケジューリング部１３７は、パイプラインハザードが少なくなるように、パイプラインに投入する命令の順序を決定する。また、ターゲットのＣＰＵが複数のハードウェア単位を備えている場合、スケジューリング部１３７は、予め命令をそれら複数のハードウェア単位に振り分けておくことがある。
【００４８】
レジスタ割付部１３８は、中間コードに含まれる変数に対して、ターゲットのＣＰＵがもつレジスタを割り当てる。ＳＩＭＤ命令のオペランドとしての変数に対しては、レジスタ割付部１３８は、２以上のデータ単位を格納できるＳＩＭＤレジスタを割り当てる。レジスタ割付部１３８は、使用するレジスタが最小になるように割当を行ってもよい。
【００４９】
ファイル生成部１３９は、オブジェクトコード記憶部１２３にオブジェクトファイルを生成する。ファイル生成部１３９は、アセンブリコード生成部１３６が生成したアセンブリコードを機械可読なオブジェクトコードに変換し、生成したオブジェクトファイルに対して書き込む。これにより、ソースコードのコンパイルが完了する。
【００５０】
図４は、ＳＩＭＤ命令とＳＩＭＤレジスタの例を示す図である。
ここでは、コンパイル装置１００が生成したオブジェクトコードを、ＣＰＵ２０が実行するものとする。ＣＰＵ２０は、コンパイル装置１００が備えるＣＰＵ１０１と同一でもよいし異なってもよい。ＣＰＵ２０は、ＳＩＭＤレジスタ２１〜２３（ｔｓ１，ｔｓ２，ｔｓ３）を有する。ＣＰＵ２０が実行可能なＳＩＭＤ命令のＳＩＭＤ幅が４であるとすると、ＳＩＭＤレジスタ２１〜２３それぞれには４つのデータ単位を格納できる。
【００５１】
例えば、ソースコードまたは中間コードに、長さ４以上の配列変数ａ，ｂを用いて、ａ（１）＋ｂ（１），ａ（２）＋ｂ（２），ａ（３）＋ｂ（３），ａ（４）＋ｂ（４）という４つの演算が定義されているとする。ＳＩＭＤ化部１３４がこの４つの演算をＳＩＭＤ化した場合、例えば、ＳＩＭＤレジスタ２１に、変数ａ（１），ａ（２），ａ（３），ａ（４）の値がこの順に並んでロードされる。また、ＳＩＭＤレジスタ２２に、変数ｂ（１），ｂ（２），ｂ（３），ｂ（４）の値がこの順に並んでロードされる。
【００５２】
そして、ｔｓ１＋ｔｓ２＝ｔｓ３というＳＩＭＤ命令が投入されると、ＣＰＵ２０は、ＳＩＭＤレジスタ２１，２２の対応する位置にあるデータ単位を組み合わせて、以下の４つの演算を並列に実行する。ＳＩＭＤレジスタ２１のａ（１）の値とＳＩＭＤレジスタ２２のｂ（１）の値を加算して、ＳＩＭＤレジスタ２３の１番目の位置に格納する。ＳＩＭＤレジスタ２１のａ（２）の値とＳＩＭＤレジスタ２２のｂ（２）の値を加算して、ＳＩＭＤレジスタ２３の２番目の位置に格納する。ＳＩＭＤレジスタ２１のａ（３）の値とＳＩＭＤレジスタ２２のｂ（３）の値を加算して、ＳＩＭＤレジスタ２３の３番目の位置に格納する。ＳＩＭＤレジスタ２１のａ（４）の値とＳＩＭＤレジスタ２２のｂ（４）の値を加算して、ＳＩＭＤレジスタ２３の４番目の位置に格納する。
【００５３】
次に、ＳＩＭＤ化部１３４がループ内の演算をＳＩＭＤ化する例を説明する。以下では、ターゲットのＣＰＵのＳＩＭＤ幅が４であり、ＳＩＭＤ命令を用いてループ内の４回転分の演算（ｉ回転目〜ｉ＋３回転目の演算）を並列化する場合を考える。これにより、ループの回転数が約４分の１に削減され、実行命令数が削減される。なお、以下に挙げるコード例は、理解が容易になるように高級言語ＦＯＲＴＲＡＮで記述している。ＳＩＭＤ化部１３４は、実際には、中間コードに対してＳＩＭＤ最適化を実行する。
【００５４】
図５は、ＳＩＭＤ最適化による第１のコード変換例を示す図である。
コード２０１には、倍精度浮動小数点型の長さ１００００の配列変数ａ，ｂと、ループ変数ｉと、ｉを１から１００００まで１ずつ増加させるループと、ループ内の演算式ａ（ｉ）＝ａ（ｉ）＋ｂ（ｉ）とが定義されている。配列変数ａ，ｂの各要素の初期値は、サブルーチンｆｏｏを呼び出すコードよって設定されている。このとき、ＳＩＭＤ化部１３４は、例えば、コード２０１を以下のようなコード２０２に変換する。
【００５５】
コード２０２には、ｉを１から１００００まで４ずつ増加させるループと、ループ内の演算式ａ（ｉ：ｉ＋３）＝ａ（ｉ：ｉ＋３）＋ｂ（ｉ：ｉ＋３）とが定義されている。ａ（ｉ：ｉ＋３）は、配列変数ａの中のｉ〜ｉ＋３の範囲を示す長さ４の部分配列である。同様に、ｂ（ｉ：ｉ＋３）は、配列変数ｂの中のｉ〜ｉ＋３の範囲を示す長さ４の部分配列である。コード２０２で行われるロード、加算、ストアは、ＳＩＭＤ命令（ＳＩＭＤ−ＬＯＡＤ，ＳＩＭＤ−ＡＤＤ，ＳＩＭＤ−ＳＴＯＲＥなど）を用いて実現される。これにより、ループの回転数が１００００回転から２５００回転に減少する。２５００回転それぞれでは、元のループの４回転分の演算が並列に実行される。
【００５６】
図６は、ＳＩＭＤ化前後の演算の第１の対応例を示す図である。
コード２０１のループを実行すると、まずｉ＝１に対応する演算３０１、すなわち、ａ（１）＝ａ（１）＋ｂ（１）が実行される。次に、ｉ＝２に対応する演算３０２、すなわち、ａ（２）＝ａ（２）＋ｂ（２）が実行される。以下、ｉ＝３に対応する演算３０３、ｉ＝４に対応する演算３０４、ｉ＝５に対応する演算３０５、ｉ＝６に対応する演算３０６、ｉ＝７に対応する演算３０７、ｉ＝８に対応する演算３０８が順に実行される。
【００５７】
これに対し、コード２０２のループを実行すると、まずｉ＝１に対応する演算３０、すなわち、ａ（１：４）＝ａ（１：４）＋ｂ（１：４）が実行される。演算３０は、コード２０１の演算３０１〜３０４に対応する。ａ（ｉ）を定義する演算以外にａ（ｉ）を参照する演算がないため、演算３０１〜３０４の間には依存関係がなく、ＳＩＭＤ命令を用いて演算３０１〜３０４を並列化することができる。次に、ｉ＝５に対応する演算３１、すなわち、ａ（５：８）＝ａ（５：８）＋ｂ（５：８）が実行される。演算３１は、コード２０１の演算３０５〜３０８に対応する。演算３０５〜３０８の間には依存関係がなく、ＳＩＭＤ命令を用いて演算３０５〜３０８を並列化することができる。
【００５８】
図７は、ＳＩＭＤ最適化による第２のコード変換例を示す図である。
コード２１１には、ループ変数ｉを１から１００００まで１ずつ増加させるループと、ループ内の演算式ａ（ｉ）＝ａ（ｉ＋１）＋ｂ（ｉ）とが定義されている。このとき、ＳＩＭＤ化部１３４は、例えば、コード２１１をコード２１２に変換する。コード２１２には、ｉを１から１００００まで４ずつ増加させるループと、ループ内の演算式ａ（ｉ：ｉ＋３）＝ａ（ｉ＋１：ｉ＋４）＋ｂ（ｉ：ｉ＋３）とが定義されている。
【００５９】
図８は、ＳＩＭＤ化前後の演算の第２の対応例を示す図である。
コード２１１のループを実行すると、まずｉ＝１に対応する演算３１１、すなわち、ａ（１）＝ａ（２）＋ｂ（１）が実行される。次に、ｉ＝２に対応する演算３１２、すなわち、ａ（２）＝ａ（３）＋ｂ（２）が実行される。以下、ｉ＝３に対応する演算３１３、ｉ＝４に対応する演算３１４、ｉ＝５に対応する演算３１５、ｉ＝６に対応する演算３１６、ｉ＝７に対応する演算３１７、ｉ＝８に対応する演算３１８が実行される。これに対し、コード２１２のループを実行すると、まずｉ＝１に対応する演算３２、すなわち、ａ（１：４）＝ａ（２：５）＋ｂ（１：４）が実行される。次に、ｉ＝５に対応する演算３３、すなわち、ａ（５：８）＝ａ（６：９）＋ｂ（５：８）が実行される。
【００６０】
ここで、ａ（ｉ）を定義する演算ではａ（ｉ＋１）を参照しているため、演算３１１〜３１４の間には依存関係がある。例えば、ａ（２）は、演算３１１で参照され演算３１２で更新される。ａ（３）は、演算３１２で参照され演算３１３で更新される。しかし、演算３１１〜３１４の依存関係は、ａ（ｉ）を先に参照して後に更新するという参照・定義の順序関係になっており、演算３１１〜３１４を並列化してもその順序関係は崩れない。例えば、ａ（２），ａ（３）の値がメモリからＳＩＭＤレジスタにロードされ（参照）、加算が行われた後、ａ（２），ａ（３）の値がメモリにストアされる（定義）。
【００６１】
よって、演算３１１〜３１４は、依存関係があるものの、演算３２のように並列化することが可能である。同様に、演算３１５〜３１８は、依存関係があるものの、参照・定義の順序関係が崩れないため演算３３のように並列化することが可能である。
【００６２】
図９は、ＳＩＭＤ最適化による第３のコード変換例を示す図である。
コード２２１には、倍精度浮動小数点型の長さ１０００４の配列変数ａ，ｂと、ループ変数ｉを５から１０００４まで１ずつ増加させるループと、ループ内の演算式ａ（ｉ）＝ａ（ｉ−４）＋ｂ（ｉ）とが定義されている。このとき、ＳＩＭＤ化部１３４は、例えば、コード２２１をコード２２２に変換する。コード２２２には、ｉを５から１０００４まで４ずつ増加させるループと、ループ内の演算式ａ（ｉ：ｉ＋３）＝ａ（ｉ−４：ｉ−１）＋ｂ（ｉ：ｉ＋３）とが定義されている。
【００６３】
図１０は、ＳＩＭＤ化前後の演算の第３の対応例を示す図である。
コード２２１のループを実行すると、まずｉ＝５に対応する演算３２１、すなわち、ａ（５）＝ａ（１）＋ｂ（５）が実行される。次に、ｉ＝６に対応する演算３２２、すなわち、ａ（６）＝ａ（２）＋ｂ（６）が実行される。以下、ｉ＝７に対応する演算３２３、ｉ＝８に対応する演算３２４、ｉ＝９に対応する演算３２５、ｉ＝１０に対応する演算３２６、ｉ＝１１に対応する演算３２７、ｉ＝１２に対応する演算３２８が実行される。これに対し、コード２２２のループを実行すると、まずｉ＝５に対応する演算３４、すなわち、ａ（５：８）＝ａ（１：４）＋ｂ（５：８）が実行される。次に、ｉ＝９に対応する演算３５、すなわち、ａ（９：１２）＝ａ（５：８）＋ｂ（９：１２）が実行される。
【００６４】
ここで、演算３２１〜３２８の中には、ａ（ｉ）を定義した後にａ（ｉ）を参照するという定義・参照の依存関係がある。例えば、ａ（５）は、演算３２１で定義され演算３２５で参照される。ａ（６）は、演算３２２で定義され演算３２６で参照される。ａ（ｉ）を定義する演算とａ（ｉ）を参照する演算とを並列化してしまうと、定義・参照の順序関係が崩れるため、元の演算式とは計算方法が異なる不正な変形となる。
【００６５】
しかし、演算３２１〜３２８では、ａ（ｉ）を定義する演算とａ（ｉ）を参照する演算とは、時系列上でＳＩＭＤ幅以上離れている。例えば、ａ（５）を定義する演算３２１とａ（５）を参照する演算３２５とは、ループ４回転分離れている。ａ（６）を定義する演算３２２とａ（６）を参照する演算３２６とは、ループ４回転分離れている。このため、ＳＩＭＤ命令を用いても、ａ（ｉ）を定義する演算とａ（ｉ）を参照する演算とは並列化されず、実質的に定義・参照の順序関係は崩れない。例えば、ａ（５），ａ（６）を定義する演算はコード２２２の演算３４に対応し、ａ（５），ａ（６）を参照する演算はコード２２２の演算３５に対応する。演算３４の後に演算３５が実行される限り、ａ（５），ａ（６）の定義・参照の順序関係は崩れない。よって、ＳＩＭＤ化が可能である。
【００６６】
図１１は、ＳＩＭＤ最適化による第４のコード変換例を示す図である。
コード２３１には、倍精度浮動小数点型の長さ１００００の配列変数ａ，ｂと、ループ変数ｉを２から１００００まで１ずつ増加させるループと、ループ内の演算式ａ（ｉ）＝ａ（ｉ−１）＋ｂ（ｉ）とが定義されている。このとき、ＳＩＭＤ化部１３４は、例えば、コード２３１を、コード２３２，２３３を経由してコード２３４に変換する。
【００６７】
コード２３２には、ｉを５から１００００まで１ずつ増加させるループが定義されている。また、ループの前には、ループから外したｉ＝２〜４に対応する以下の３つの演算が定義されている：ａ（２）＝ａ（１）＋ｂ（２），ａ（３）＝ａ（２）＋ｂ（３），ａ（４）＝ａ（３）＋ｂ（４）。最初の数回転の演算をループの前に出すことは、「ループピーリング」と言うことがある。コード２３２のループピーリングは、コード２３３のようにループ内の演算式を変形するための準備として行われる。
【００６８】
コード２３３には、ループ内の演算式ａ（ｉ）＝（（（ａ（ｉ−４）＋ｂ（ｉ−３））＋ｂ（ｉ−２））＋ｂ（ｉ−１））＋ｂ（ｉ）が定義されている。コード２３３の演算式は、配列変数ａについてコード２３１，２３２の演算式を連続的に展開したものである。すなわち、ａ（ｉ−１）がａ（ｉ−２）＋ｂ（ｉ−１）に展開され、ａ（ｉ−２）がａ（ｉ−３）＋ｂ（ｉ−２）に展開され、ａ（ｉ−３）がａ（ｉ−４）＋ｂ（ｉ−３）に展開される。この展開により、ａ（ｉ）を定義する演算式から、直前の３回転で定義される変数ａ（ｉ−１），ａ（ｉ−２），ａ（ｉ−３）を除去することができる。
【００６９】
配列変数ａについての演算式の展開は、定義する変数ａ（ｉ）と参照する変数ａ（ｉ−ｎ）の間の時系列上の差（回転数）が、ＳＩＭＤ幅またはそれ以上になるまで行われる。ここでは、ＳＩＭＤ幅が４であるため、参照する変数がａ（ｉ−４）になるまで演算式が展開されている。また、ループ変数ｉの初期値は、参照する変数の添字ｉ−ｎが配列変数ａの添字の最小値になるように決定される。ここでは、ｉ−４＝１より、ループ変数ｉの初期値は５と決定される。また、ループピーリングでは、初期値より小さいループ変数ｉの値に対応する演算が、ループの前に定義される。ここでは、ループ変数ｉの初期値が５であるため、ｉ＝２〜４に対応する演算がループの前に定義されている。
【００７０】
コード２３４には、ｉを５から１００００まで４ずつ増加させるループと、ループ内の演算式ａ（ｉ：ｉ＋３）＝（（（ａ（ｉ−４：ｉ−１）＋ｂ（ｉ−３：ｉ））＋ｂ（ｉ−２：ｉ＋１））＋ｂ（ｉ−１：ｉ＋２））＋ｂ（ｉ：ｉ＋３）とが定義されている。コード２３４の演算式は、コード２３３の演算式をＳＩＭＤ化したものである。
【００７１】
図１２は、ＳＩＭＤ化の阻害要因の例を示す図である。
ここでは、コード２３２からコード２３３のような演算式の展開を行わず、仮に図５，７，９と同様の方法でコード２３１をＳＩＭＤ化した場合を考える。
【００７２】
コード２３１のループを実行すると、まずｉ＝２に対応する演算３３１、すなわち、ａ（２）＝ａ（１）＋ｂ（２）が実行される。次に、ｉ＝３に対応する演算３３２、すなわち、ａ（３）＝ａ（２）＋ｂ（３）が実行される。以下、ｉ＝４に対応する演算３３３、ｉ＝５に対応する演算３３４、ｉ＝６に対応する演算３３５、ｉ＝７に対応する演算３３６、ｉ＝８に対応する演算３３７、ｉ＝９に対応する演算３３８が実行される。
【００７３】
ここで、ａ（ｉ＋１）を定義する演算ではａ（ｉ）を参照しているため、演算３３１〜３３４の間には依存関係がある。例えば、ａ（２）は、演算３３１で定義され演算３３２で参照される。ａ（３）は、演算３３２で定義され演算３３３で参照される。この依存関係は、ａ（ｉ）を先に定義して後に参照するという定義・参照の順序関係になっており、演算３３１〜３３４をそのまま並列化するとその順序関係が崩れる。例えば、演算３３１〜３３４をａ（２：５）＝ａ（１：４）＋ｂ（２：５）という演算３６に置き換えると、ａ（２），ａ（３）が先に参照されて、その後にａ（２），ａ（３）が更新される。
【００７４】
よって、演算３３１〜３３４を演算３６のようにそのまま並列化すると、元の演算式とは計算方法が異なる不正な変形となる。同様に、演算３３５〜３３８の間には定義・参照の順序の依存関係があり、演算３３５〜３３８をａ（６：９）＝ａ（５：８）＋ｂ（６：９）という演算３７に置き換えるとその順序関係が崩れる。よって、演算３３５〜３３８を演算３７のようにそのまま並列化すると、元の演算式とは計算方法が異なる不正な変形となる。
【００７５】
図１３は、ＳＩＭＤ化前後の演算の第４の対応例を示す図である。
上記問題を解決するため、ＳＩＭＤ化部１３４は、図１１のように演算式を展開して命令をＳＩＭＤ化する。コード２３３のループを実行すると、まずｉ＝５に対応する演算３４１、すなわち、ａ（５）＝ａ（１）＋ｂ（２）＋ｂ（３）＋ｂ（４）＋ｂ（５）が実行される。次に、ｉ＝６に対応する演算３４２、すなわち、ａ（６）＝ａ（２）＋ｂ（３）＋ｂ（４）＋ｂ（５）＋ｂ（６）が実行される。以下、ｉ＝７に対応する演算３４３、ｉ＝８に対応する演算３４４、ｉ＝９に対応する演算３４５、ｉ＝１０に対応する演算３４６、ｉ＝１１に対応する演算３４７、ｉ＝１２に対応する演算３４８が実行される。
【００７６】
これに対し、コード２３４のループを実行すると、まずｉ＝５に対応する演算３８、すなわち、ａ（５：８）＝ａ（１：４）＋ｂ（２：５）＋ｂ（３：６）＋ｂ（４：７）＋ｂ（５：８）が実行される。次に、ｉ＝９に対応する演算３９、すなわち、ａ（９：１２）＝ａ（５：８）＋ｂ（６：９）＋ｂ（７：１０）＋ｂ（８：１１）＋ｂ（９：１２）が実行される。演算３９は、ａ（５：８）を参照しているため演算３８に依存する。
【００７７】
ここで、演算３４１〜３４８の中には、ａ（ｉ）を定義した後にａ（ｉ）を参照するという定義・参照の依存関係がある。例えば、ａ（５）は、演算３４１で定義され演算３４５で参照される。しかし、ａ（ｉ）を定義する演算とａ（ｉ）を参照する演算とは、時系列上でＳＩＭＤ幅以上離れている。例えば、ａ（５）を定義する演算３４１とａ（５）を参照する演算３４５とは、ループ４回転分離れている。演算３４２〜３４４の間では、ａ（５）は参照されない。このため、ＳＩＭＤ命令を用いて、演算３４１〜３４４を演算３８のように並列化することが可能である。同様に、ＳＩＭＤ命令を用いて、演算３４５〜３４８を演算３９のように並列化することが可能である。
【００７８】
図１４は、ＳＩＭＤ最適化による第５のコード変換例を示す図である。
上記のコード２３４の演算式には、４つの加算の演算子が含まれている。コード２３４では、これら４つの加算が前方から後方に向かって逐次的に実行される。よって、ＣＰＵ２０が複数のＳＩＭＤ命令を並列に実行可能な複数の演算器を備えていても、コード２３４の４つの加算は並列化されない。一方、実数の加算には結合則が成立するため、４つの加算の実行順序を入れ替えることも可能である。そこで、ＳＩＭＤ化部１３４は、１つの演算式内での並列性が高くなるように、演算子の実行順序を入れ替えてもよい。
【００７９】
ＳＩＭＤ化部１３４は、例えば、コード２３３をコード２３５に変換する。コード２３５には、ループ内の演算式ａ（ｉ）＝（ａ（ｉ−４）＋（ｂ（ｉ−３）＋ｂ（ｉ−２）））＋（ｂ（ｉ−１）＋ｂ（ｉ））が定義されている。これは、４つの加算の実行順序をコード２３３から変更したものである。コード２３５では、４つの加算のうち１番目の加算が２番目の加算の後に実行され、３番目の加算が１番目および４番目の加算の後に実行される。一方、１番目または２番目の加算と４番目の加算とは、並列に実行することが可能である。よって、コード２３５の演算式内の並列性は、コード２３３よりも高い。
【００８０】
コード２３６には、ｉを５から１００００まで４ずつ増加させるループと、ループ内の演算式ａ（ｉ：ｉ＋３）＝（ａ（ｉ−４：ｉ−１）＋（ｂ（ｉ−３：ｉ）＋ｂ（ｉ−２：ｉ＋１）））＋（ｂ（ｉ−１：ｉ＋２）＋ｂ（ｉ：ｉ＋３））とが定義されている。コード２３６の演算式は、コード２３５の演算式をＳＩＭＤ化したものである。ＣＰＵ２０が複数のＳＩＭＤ命令を並列に実行できる複数の演算器を備えている場合、コード２３６の１番目または２番目の加算と４番目の加算とを並列に実行することが可能である。
【００８１】
ただし、プロセッサによる浮動小数点演算ではレジスタ長が有限であり丸め誤差が発生し得ることから、結合則が成立する場合であっても、複数の演算子の実行順序を入れ替えることで演算結果が変わってしまうことがある。そこで、第２の実施の形態では、コンパイルオプションによって演算子の実行順序の入れ替えが明示的に許可された場合のみ、ＳＩＭＤ化部１３４が、図１４に示したような演算式の変換を行うこととする。
【００８２】
図１５は、ＳＩＭＤ最適化による第６のコード変換例を示す図である。
上記では、配列変数ａ，ｂの長さやループ変数ｉの上限値・下限値が固定値であるコードをＳＩＭＤ最適化する例を示した。ただし、配列変数ａ，ｂの長さやループ変数ｉの上限値・下限値がパラメータになっているコードについてもＳＩＭＤ最適化を行い得る。
【００８３】
コード２４１には、倍精度浮動小数点型の長さｎの配列変数ａ，ｂと、ループ変数ｉと、ｉを２からｍまで１ずつ増加させるループと、ループ内の演算式ａ（ｉ）＝ａ（ｉ−１）＋ｂ（ｉ）とが定義されている。ｎ，ｍは、コンパイル時にはその値が未定でありループの実行時までにその値が決定されるパラメータである。このとき、ＳＩＭＤ化部１３４は、例えば、コード２４１を以下のようなコード２４２に変換する。
【００８４】
コード２４２には、ｉを５から（ｍ÷４）×４まで４ずつ増加させる１番目のループと、１番目のループ内のＳＩＭＤ化した演算式と、ループピーリングされたｉ＝２〜４に対応する３回転分の演算とが定義されている。また、コード２４２には、１番目のループの後からｉ＝ｍまでに対応する２番目のループと、２番目のループ内のＳＩＭＤ化されない演算式とが定義されている。また、コード２４２には、ｍが７未満であるか判定しｍが７未満の場合に１番目のループをスキップする条件分岐が定義されている。このように、配列変数ａ，ｂの長さやループ変数ｉの上限値・下限値が可変であっても、ＳＩＭＤ化部１３４は、条件分岐を用いてＳＩＭＤ最適化を行うことが可能である。
【００８５】
以上説明したように、ＳＩＭＤ化部１３４は、ループ内の演算式が前回転（前イテレーション）の演算結果を参照する変数を含んでいても、演算式を展開することで、ループ内の複数回転分の演算をＳＩＭＤ化することが可能である。しかし、演算式の展開は、命令数やメモリアクセス回数を増加させ得る。このため、展開されＳＩＭＤ化された演算式の実行効率が、展開前のＳＩＭＤ化しない演算式の実行効率よりも高くなるとは限らない。
【００８６】
例えば、単純なレジスタ割付方法によれば、図１１のコード２３１の演算式は、２つのロード命令と１つの加算命令と１つのストア命令とで実現され得る。よって、コード２３１のループ４回転の演算は、１６命令で実現される。一方、図１１のコード２３４や図１４のコード２３６の演算式は、５つのロード命令と４つの加算命令と１つのストア命令とで実現され得る。よって、元のループ４回転に相当する演算は、１０命令で実現される。このように、多くの場合、上記のＳＩＭＤ最適化によって命令の実行回数が削減される。しかし、以下のような理由から、実行効率が高くなるとは限らない。
【００８７】
（１）ＣＰＵのアーキテクチャの中には、ＳＩＭＤ命令の実行時間（サイクル数）がスカラ命令よりも遅いものがある。ＳＩＭＤ命令の実行時間がどの程度遅いかは、ＣＰＵのアーキテクチャに依存する。（２）ループ内に並列に実行可能な演算式が多数あるなど、ＳＩＭＤ化しなくても演算器の空き時間が少なくなるように命令スケジューリングが可能である場合、ＳＩＭＤ化による改善の効果が小さい可能性がある。（３）メモリアクセス回数の増加によって、実効効率が低下するおそれがある。実行効率への影響の程度は、ＣＰＵのアーキテクチャやコードに依存する。ＳＩＭＤ幅が大きいほど、メモリアクセス回数の増加の影響が大きくなる可能性がある。（４）ＣＰＵが備える演算器が少ない場合やＳＩＭＤ幅が小さい場合には、ＳＩＭＤ化による改善の効果が小さい可能性がある。
【００８８】
そこで、ＳＩＭＤ化部１３４は、ループ内の演算について、ＳＩＭＤ化前のコード（例えば、コード２３１）とＳＩＭＤ化後のコード（例えば、コード２３４やコード２３６）の実行時間を示すサイクル数を試算する。そして、ＳＩＭＤ化部１３４は、後者のサイクル数が前者のサイクル数よりも小さい、すなわち、ＳＩＭＤ化により実行効率が高くなっている場合のみ、中間コード記憶部１２２の中間コードを書き換えるようにする。
【００８９】
図１６は、プロセッサアーキテクチャの例を示す図である。
ループ実行のサイクル数を算出するにあたり、ＳＩＭＤ化部１３４は、ターゲットのＣＰＵ２０のアーキテクチャを示すプロセッサ情報を参照する。プロセッサ情報には、ＣＰＵ２０が備える演算器の数や各種類の命令の実行に要するサイクル数などが記載される。このプロセッサ情報は、例えば、ＲＡＭ１０２やＨＤＤ１０３に予め記憶されている。
【００９０】
ＣＰＵ２０は、演算器２４〜２７を有する。演算器２４，２５はぞれぞれ、スカラ型のロード命令、ＳＩＭＤ幅４のＳＩＭＤ型のロード命令、スカラ型のストア命令、および、ＳＩＭＤ幅４のＳＩＭＤ型のストア命令を実行できる。演算器２６，２７はそれぞれ、スカラ型の加算と、ＳＩＭＤ幅４のＳＩＭＤ型の加算命令を実行できる。演算器２４〜２７は、パイプライン処理化されている。よって、演算器２４〜２７それぞれには、１サイクル毎に１命令（１スカラ命令または１ＳＩＭＤ命令）実行させることができる。
【００９１】
命令が投入されてからその命令の実行が完了するまでのパイプライン上でのサイクル数は、命令の種類によって異なる。演算器２４，２５において、スカラ型のロード命令は３サイクル、ＳＩＭＤ型のロード命令は３サイクル、スカラ型のストア命令は１サイクル、ＳＩＭＤ型のストア命令は１サイクルを要する。演算器２６，２７において、スカラ型の加算命令は３サイクル、ＳＩＭＤ型の加算命令は３サイクルを要する。
【００９２】
演算器２４〜２７は、並列に命令を実行できる。よって、１サイクル毎に最大で４つのスカラ型またはＳＩＭＤ型の命令を投入できる。ただし、メモリからの読み出しの最大同時実行数は２である一方、メモリへの書き込みの最大同時実行数は１である。このため、演算器２４，２５の何れか一方がストア命令に従ってメモリにデータを書き込んでいるときは、他方はロード命令によるデータの読み出しおよびストア命令によるデータの書き込みを行わない。すなわち、ＣＰＵ２０には、１つまたは２つの加算命令と１つまたは２つのロード命令を同時に投入することができる。また、ＣＰＵ２０には、１つまたは２つの加算命令と１つのストア命令を同時に投入することができる。
【００９３】
図１７は、ループ内のサイクル数の第１の計算例を示す図である。
ここでは、図１６のＣＰＵ２０にオブジェクトコードを実行させる場合を考える。コード２３１のループ４回転の演算は、図１７のようにスケジューリングできる。
【００９４】
サイクル＃１で、ｂ（ｉ）を読み出すロード命令を投入する。図１７において、ｔ２０などは、ＳＩＭＤレジスタでないスカラ型のレジスタを示す。サイクル＃４で、ａ（ｉ−１）とｂ（ｉ）を足す加算命令と、ｂ（ｉ＋１）を読み出すロード命令を投入する。サイクル＃６で、ｂ（ｉ＋２）を読み出すロード命令を投入する。サイクル＃７で、ａ（ｉ）を書き込むストア命令と、ａ（ｉ）とｂ（ｉ＋１）を足す加算命令を投入する。この加算命令で参照するａ（ｉ）については、前回転で算出したものをレジスタ（レジスタｔ３０）に残しておくことで、メモリから読み出すロード命令を省略している。
【００９５】
サイクル＃９で、ｂ（ｉ＋３）を読み出すロード命令を投入する。サイクル＃１０で、ａ（ｉ＋１）を書き込むストア命令と、ａ（ｉ＋１）とｂ（ｉ＋２）を足す加算命令を投入する。サイクル＃１３で、ａ（ｉ＋２）を書き込むストア命令と、ａ（ｉ＋２）とｂ（ｉ＋３）を足す加算命令を投入する。サイクル＃１６で、ａ（ｉ＋３）を書き込むストア命令を投入する。サイクル＃１７で、ｉに４を足す加算命令を投入する。この試算によって、ループ４回転の演算が１８サイクルで終了すると予測される。
【００９６】
図１８は、ループ内のサイクル数の第２の計算例を示す図である。
コード２３４のループ１回転の演算は、図１８のようにスケジューリングできる。
サイクル＃１で、ａ（ｉ−４：ｉ−１）を読み出すロード命令と、ｂ（ｉ−３：ｉ）を読み出すロード命令を投入する。図１８において、ｔｓ１，ｔｓ２などは、ＳＩＭＤレジスタを示す。サイクル＃２で、ｂ（ｉ−２：ｉ＋１）を読み出すロード命令と、ｂ（ｉ−１：ｉ＋２）を読み出すロード命令を投入する。サイクル＃３で、ｂ（ｉ：ｉ＋３）を読み出すロード命令を投入する。ここで、ｂ（ｉ−２），ｂ（ｉ−１），ｂ（ｉ），ｂ（ｉ＋１），ｂ（ｉ＋２）は、異なるＳＩＭＤレジスタに重複して格納されるため、２以上のＳＩＭＤ命令によって２回以上メモリから読み出されることになる。
【００９７】
サイクル＃４で、ａ（ｉ−４：ｉ−１）とｂ（ｉ−３：ｉ）を足す加算命令を投入する。この加算命令はＳＩＭＤ型であり、スカラ型の加算命令４つに相当する。サイクル＃７で、サイクル＃４の演算結果とｂ（ｉ−２：ｉ＋１）を足す加算命令を投入する。サイクル＃１０で、サイクル＃７の演算結果とｂ（ｉ−１：ｉ＋２）を足す加算命令を投入する。サイクル＃１３で、サイクル＃１０の演算結果とｂ（ｉ：ｉ＋３）を足す加算命令を投入する。サイクル＃１６で、ａ（ｉ：ｉ＋３）を書き込むストア命令を投入する。サイクル＃１７で、ｉに４を足す加算命令を投入する。この試算によって、変換後のループ１回転の演算が１８サイクルで終了すると予測される。
【００９８】
コード２３４の演算式では、４つの加算が逐次的に実行される。このため、図１８に示すように、サイクル＃４〜＃１５において、ＣＰＵ２０の並列処理能力が活用されず演算器２４〜２６の空き時間が多くなっている。その結果、コード２３４の予測サイクル数はコード２３１と同じになっており、実行効率が改善していない。
【００９９】
図１９は、ループ内のサイクル数の第３の計算例を示す図である。
コード２３６のループ１回転の演算は、図１９のようにスケジューリングできる。
サイクル＃１で、ｂ（ｉ−３：ｉ）を読み出すロード命令と、ｂ（ｉ−２：ｉ＋１）を読み出すロード命令を投入する。サイクル＃２で、ｂ（ｉ−１：ｉ＋２）を読み出すロード命令と、ｂ（ｉ：ｉ＋３）を読み出すロード命令を投入する。サイクル＃３で、ａ（ｉ−４：ｉ−１）を読み出すロード命令を投入する。サイクル＃４で、ｂ（ｉ−３：ｉ）とｂ（ｉ−２：ｉ＋１）を足す加算命令を投入する。サイクル＃５で、ｂ（ｉ−１：ｉ＋２）とｂ（ｉ：ｉ＋３）を足す加算命令を投入する。演算式の２番目と４番目の加算は依存関係がないため、サイクル＃４の結果を待たずにサイクル＃５の加算命令を投入できる。
【０１００】
サイクル＃７で、ａ（ｉ−４：ｉ−１）とサイクル＃４の演算結果を足す加算命令を投入する。サイクル＃１０で、サイクル＃７の演算結果とサイクル＃５の演算結果を足す加算命令を投入する。サイクル＃１３で、ａ（ｉ：ｉ＋３）を書き込むストア命令を投入する。サイクル＃１４で、ｉに４を足す加算命令を投入する。この試算によって、変換後のループ１回転の演算が１５サイクルで終了すると予測される。
【０１０１】
コード２３６の演算式は、コード２３４の演算式よりも並列性が高い。このため、ＳＩＭＤ命令でも、ＣＰＵ２０の並列処理能力を活用することができる。その結果、コード２３６の予測サイクル数はコード２３１よりも少なくなり、実行効率が改善する。この場合、ＳＩＭＤ化部１３４は、コード２３１をコード２３６に置き換えると決定する。
【０１０２】
なお、図１８，１９では、ＣＰＵ２０のＳＩＭＤ幅４の全てを利用して、１つのＳＩＭＤ命令で４つのスカラ命令相当の演算を実行することとした。これに対し、ＳＩＭＤ幅の一部のみを利用するようにしてもよい。例えば、ＣＰＵ２０のＳＩＭＤ幅が４であっても、１つのＳＩＭＤ命令で並列実行する演算を２つに制限してもよい。メモリアクセスの負荷が大きい場合に、１つのＳＩＭＤ命令で並列実行する演算の数（多重度）を制限することが考えられる。その場合、例えば、ＳＩＭＤ化部１３４は、異なる多重度について予測サイクル数を算出し、予測サイクル数が最小になる多重度を選択してもよい。
【０１０３】
次に、ＣＰＵのアーキテクチャの違いがサイクル数に与える影響について説明する。
図２０は、プロセッサアーキテクチャの他の例を示す図である。
ＣＰＵ２０ａは、演算器２４ａ〜２７ａを有する。演算器２４ａ，２５ａはぞれぞれ、スカラ型のロード命令、ＳＩＭＤ幅４のＳＩＭＤ型のロード命令、スカラ型のストア命令、および、ＳＩＭＤ幅４のＳＩＭＤ型のストア命令を実行できる。演算器２６ａ，２７ａはそれぞれ、スカラ型の加算と、ＳＩＭＤ幅４のＳＩＭＤ型の加算命令を実行できる。
【０１０４】
命令が投入されてからその命令の実行が完了するまでのサイクル数は、図１６に示したＣＰＵ２０と異なる。演算器２４ａ，２５ａにおいて、スカラ型のロード命令は３サイクル、ＳＩＭＤ型のロード命令は５サイクル、スカラ型のストア命令は１サイクル、ＳＩＭＤ型のストア命令は３サイクルを要する。演算器２６ａ，２７ａにおいて、スカラ型の加算命令は３サイクル、ＳＩＭＤ型の加算命令は５サイクルを要する。すなわち、ＣＰＵ２０と異なり、ＳＩＭＤ命令に要するサイクル数がスカラ命令より大きくなっている。
【０１０５】
図２１は、ループ内のサイクル数の第４の計算例を示す図である。
ここでは、図２０のＣＰＵ２０ａにオブジェクトコードを実行させる場合を考える。コード２３６のループ１回転の演算は、図２１のようにスケジューリングできる。
【０１０６】
サイクル＃１で、ｂ（ｉ−３：ｉ）を読み出すロード命令と、ｂ（ｉ−２：ｉ＋１）を読み出すロード命令を投入する。サイクル＃２で、ｂ（ｉ−１：ｉ＋２）を読み出すロード命令と、ｂ（ｉ：ｉ＋３）を読み出すロード命令を投入する。サイクル＃３で、ａ（ｉ−４：ｉ−１）を読み出すロード命令を投入する。サイクル＃６で、ｂ（ｉ−３：ｉ）とｂ（ｉ−２：ｉ＋１）を足す加算命令を投入する。サイクル＃７で、ｂ（ｉ−１：ｉ＋２）とｂ（ｉ：ｉ＋３）を足す加算命令を投入する。
【０１０７】
サイクル＃１１で、ａ（ｉ−４：ｉ−１）とサイクル＃６の演算結果を足す加算命令を投入する。サイクル＃１６で、サイクル＃１１の演算結果とサイクル＃７の演算結果を足す加算命令を投入する。サイクル＃２１で、ａ（ｉ：ｉ＋３）を書き込むストア命令を投入する。サイクル＃２２で、ｉに４を足す加算命令を投入する。この試算によって、変換後のループ１回転の演算が２３サイクルで終了すると予測される。
【０１０８】
ターゲットのＣＰＵ２０ａではＳＩＭＤ命令の実行時間が長い。このため、図２１に示すように、サイクル＃８〜＃２０において、ＣＰＵ２０ａの並列処理能力が活用されず演算器２４ａ〜２７ａの空き時間が多くなっている。その結果、コード２３６の予測サイクル数はコード２３１より大きくなっており、実行効率が悪化している。
【０１０９】
次に、元のコードの違いがＳＩＭＤ化の改善効果に与える影響について説明する。
図２２は、ループ内のサイクル数の第５の計算例を示す図である。
ＳＩＭＤ化前のコードとして、コード２５１を考える。コード２５１には、ループ変数ｉを２から１００００まで１ずつ増加させるループが定義されている。ループ内には、次の３つの演算式が定義されている：ａ１（ｉ）＝ａ１（ｉ−１）＋ｂ１（ｉ），ａ２（ｉ）＝ａ２（ｉ−１）＋ｂ２（ｉ），ａ３（ｉ）＝ａ３（ｉ−１）＋ｂ３（ｉ）。
【０１１０】
ここでは、図１６のＣＰＵ２０にオブジェクトコードを実行させる場合を考える。コード２５１のループ４回転の演算は、図２２のようにスケジューリングできる。
サイクル＃１で、ｂ１（ｉ）を読み出すロード命令と、ｂ１（ｉ＋２）を読み出すロード命令を投入する。サイクル＃２で、ｂ２（ｉ）を読み出すロード命令と、ｂ２（ｉ＋２）を読み出すロード命令を投入する。サイクル＃３で、ｂ３（ｉ）を読み出すロード命令と、ｂ３（ｉ＋２）を読み出すロード命令を投入する。サイクル＃４で、ｂ１（ｉ＋１）を読み出すロード命令と、ｂ１（ｉ＋３）を読み出すロード命令と、ａ１（ｉ−１）＋ｂ１（ｉ）の加算命令を投入する。サイクル＃５で、ｂ２（ｉ＋１）を読み出すロード命令と、ｂ２（ｉ＋３）を読み出すロード命令と、ａ２（ｉ−１）＋ｂ２（ｉ）の加算命令を投入する。サイクル＃６で、ｂ３（ｉ＋１）を読み出すロード命令と、ｂ３（ｉ＋３）を読み出すロード命令と、ａ３（ｉ−１）＋ｂ３（ｉ）の加算命令を投入する。
【０１１１】
サイクル＃７で、ａ１（ｉ）を書き込むストア命令と、ａ１（ｉ）＋ｂ１（ｉ＋１）の加算命令を投入する。サイクル＃８で、ａ２（ｉ）を書き込むストア命令と、ａ２（ｉ）＋ｂ２（ｉ＋１）の加算命令を投入する。サイクル＃９で、ａ３（ｉ）を書き込むストア命令と、ａ３（ｉ）＋ｂ３（ｉ＋１）の加算命令を投入する。サイクル＃１０で、ａ１（ｉ＋１）を書き込むストア命令と、ａ１（ｉ＋１）＋ｂ１（ｉ＋２）の加算命令を投入する。サイクル＃１１で、ａ２（ｉ＋１）を書き込むストア命令と、ａ２（ｉ＋１）＋ｂ２（ｉ＋２）の加算命令を投入する。サイクル＃１２で、ａ３（ｉ＋１）を書き込むストア命令と、ａ３（ｉ＋１）＋ｂ３（ｉ＋２）の加算命令を投入する。
【０１１２】
サイクル＃１３で、ａ１（ｉ＋２）を書き込むストア命令と、ａ１（ｉ＋２）＋ｂ１（ｉ＋３）の加算命令を投入する。サイクル＃１４で、ａ２（ｉ＋２）を書き込むストア命令と、ａ２（ｉ＋２）＋ｂ２（ｉ＋３）の加算命令を投入する。サイクル＃１５で、ａ３（ｉ＋２）を書き込むストア命令と、ａ３（ｉ＋２）＋ｂ３（ｉ＋３）の加算命令を投入する。サイクル＃１６で、ａ１（ｉ＋３）を書き込むストア命令を投入する。サイクル＃１７で、ａ２（ｉ＋３）を書き込むストア命令を投入する。サイクル＃１８で、ａ３（ｉ＋３）を書き込むストア命令を投入する。サイクル＃１９で、ｉ＋４の加算命令を投入する。この試算によって、ループ４回転の演算が２０サイクルで終了すると予測される。
【０１１３】
コード２５１のループには、依存関係のない３つの演算式が含まれている。このため、スカラ命令を密にスケジューリングでき、演算器２４〜２６の空き時間を少なくできる。
図２３は、ループ内のサイクル数の第６の計算例を示す図である。
【０１１４】
コード２５２は、コード２５１をＳＩＭＤ化したものである。コード２５２には、ループ変数ｉを５から１００００まで４ずつ増加させるループが定義されている。ループ内には、次の３つの演算式が定義されている。ａ１（ｉ：ｉ＋３）＝（ａ１（ｉ−４：ｉ−１）＋（ｂ１（ｉ−３：ｉ）＋ｂ１（ｉ−２：ｉ＋１）））＋（ｂ１（ｉ−１：ｉ＋２）＋ｂ１（ｉ：ｉ＋３））。ａ２（ｉ：ｉ＋３）＝（ａ２（ｉ−４：ｉ−１）＋（ｂ２（ｉ−３：ｉ）＋ｂ２（ｉ−２：ｉ＋１）））＋（ｂ２（ｉ−１：ｉ＋２）＋ｂ２（ｉ：ｉ＋３））。ａ３（ｉ：ｉ＋３）＝（ａ３（ｉ−４：ｉ−１）＋（ｂ３（ｉ−３：ｉ）＋ｂ３（ｉ−２：ｉ＋１）））＋（ｂ３（ｉ−１：ｉ＋２）＋ｂ３（ｉ：ｉ＋３））。
【０１１５】
ここでは、図１６のＣＰＵ２０にオブジェクトコードを実行させる場合を考える。コード２５２のループ１回転の演算は、図２３のようにスケジューリングできる。
サイクル＃１で、ｂ１（ｉ−３：ｉ）を読み出すロード命令と、ｂ１（ｉ−２：ｉ＋１）を読み出すロード命令を投入する。サイクル＃２で、ｂ２（ｉ−３：ｉ）を読み出すロード命令と、ｂ２（ｉ−２：ｉ＋１）を読み出すロード命令を投入する。サイクル＃３で、ｂ３（ｉ−３：ｉ）を読み出すロード命令と、ｂ３（ｉ−２：ｉ＋１）を読み出すロード命令を投入する。サイクル＃４で、ｂ１（ｉ−１：ｉ＋２）を読み出すロード命令と、ｂ１（ｉ：ｉ＋３）を読み出すロード命令を投入する。サイクル＃５で、ｂ２（ｉ−１：ｉ＋２）を読み出すロード命令と、ｂ２（ｉ：ｉ＋３）を読み出すロード命令を投入する。サイクル＃６で、ｂ３（ｉ−１：ｉ＋２）を読み出すロード命令と、ｂ３（ｉ：ｉ＋３）を読み出すロード命令を投入する。
【０１１６】
サイクル＃７で、ａ１（ｉ−４：ｉ−１）を読み出すロード命令と、ｂ１（ｉ−３：ｉ）＋ｂ１（ｉ−２：ｉ＋１）の加算命令と、ｂ１（ｉ−１：ｉ＋２）＋ｂ１（ｉ：ｉ＋３）の加算命令を投入する。サイクル＃８で、ａ２（ｉ−４：ｉ−１）を読み出すロード命令と、ｂ２（ｉ−３：ｉ）＋ｂ２（ｉ−２：ｉ＋１）の加算命令と、ｂ２（ｉ−１：ｉ＋２）＋ｂ２（ｉ：ｉ＋３）の加算命令を投入する。サイクル＃９で、ａ３（ｉ−４：ｉ−１）を読み出すロード命令と、ｂ３（ｉ−３：ｉ）＋ｂ３（ｉ−２：ｉ＋１）の加算命令と、ｂ３（ｉ−１：ｉ＋２）＋ｂ３（ｉ：ｉ＋３）の加算命令を投入する。
【０１１７】
サイクル＃１０で、ａ１（ｉ−４：ｉ−１）とサイクル＃７の演算結果を足す加算命令を投入する。サイクル＃１１で、ａ２（ｉ−４：ｉ−１）とサイクル＃８の演算結果を足す加算命令を投入する。サイクル＃１２で、ａ３（ｉ−４：ｉ−１）とサイクル＃９の演算結果を足す加算命令を投入する。サイクル＃１３で、サイクル＃１０の演算結果とサイクル＃７の演算結果を足す加算命令を投入する。サイクル＃１４で、サイクル＃１１の演算結果とサイクル＃８の演算結果を足す加算命令を投入する。サイクル＃１５で、サイクル＃１２の演算結果とサイクル＃９の演算結果を足す加算命令を投入する。
【０１１８】
サイクル＃１６で、ａ１（ｉ：ｉ＋３）を書き込むストア命令を投入する。サイクル＃１７で、ａ２（ｉ：ｉ＋３）を書き込むストア命令を投入する。サイクル＃１８で、ａ３（ｉ：ｉ＋３）を書き込むストア命令を投入する。サイクル＃１９で、ｉ＋４の加算命令を投入する。この試算によって、変換後のループ１回転の演算が２０サイクルで終了すると予測される。図２３に示すように、コード２５２のＳＩＭＤ命令は密にスケジューリングできるものの、変換前のコード２５１のスケジューリング効率が高い。このため、コード２５２の予測サイクル数はコード２５１と同じであり、実行効率が改善しない。
【０１１９】
次に、ＳＩＭＤ化部１３４が行うＳＩＭＤ最適化の手順例を説明する。
図２４は、ＳＩＭＤ最適化の手順例を示すフローチャートである。
（Ｓ１）ＳＩＭＤ化部１３４は、中間コード記憶部１２２から中間コードを読み出し、中間コードの中からループを検出する。ループ内には、１以上の演算式が含まれる。
【０１２０】
（Ｓ２）ＳＩＭＤ化部１３４は、ターゲットのＣＰＵのアーキテクチャにおけるＳＩＭＤ幅Ｎを確認する。例えば、ＳＩＭＤ化部１３４は、ＲＡＭ１０２またはＨＤＤ１０３に予め記憶されたプロセッサ情報を参照して、ＳＩＭＤ幅Ｎを確認する。ターゲットのＣＰＵの種類は、コンパイル時にコンパイルオプションとして指定されてもよい。
【０１２１】
ＳＩＭＤ化部１３４は、ループのＮ回転分の演算を、演算式を変形せずに並列化可能であるか判断する。Ｎ回転分の演算を同時実行しても変数の定義・参照の順序が変わらない場合、並列化可能であると判断される。一方、同時実行すると変数の定義・参照の順序が変わってしまう場合または変わる可能性がある場合、並列化可能でないと判断される。
【０１２２】
例えば、１〜Ｎ−１回転前の演算結果を参照する変数が演算式に含まれている場合、並列化可能でないと判断される。また、何回転前の演算結果を参照するのか静的に決定できない変数が演算式に含まれている場合（インダイレクトアクセス）、並列化可能でないと判断される。また、ループがユーザ関数の呼び出しを含む場合や、ループの途中で制御がループ外に抜ける可能性のある場合も、並列化可能でないと判断される。並列化可能な場合はステップＳ１０に処理が進み、並列化可能でない場合はステップＳ３に処理が進む。
【０１２３】
（Ｓ３）ＳＩＭＤ化部１３４は、ＳＩＭＤ化の阻害要因（ループ内の演算が並列化可能でない原因）が、変数の定義・参照の順序であるか判断する。すなわち、１〜Ｎ−１回転前の演算結果を参照する変数のみが阻害要因であるか判断する。この条件を満たす場合はステップＳ４に処理が進み、条件を満たさない場合はＳＩＭＤ最適化が終了する。
【０１２４】
（Ｓ４）ＳＩＭＤ化部１３４は、ループピーリングを行う。すなわち、ＳＩＭＤ化部１３４は、ループ内の最初の数回転の演算がループ外で実行されるように、ループ直前に演算式を挿入する。例えば、ＳＩＭＤ化部１３４は、定義される配列変数の添字と参照される配列変数の添字とが幾つ離れていれば並列化可能になるかを、ＳＩＭＤ幅Ｎから算出する。そして、ＳＩＭＤ化部１３４は、変形後の演算式における参照される配列変数の添字とループ変数ｉの初期値から、何回転分の演算をループ外に出すか決定する。
【０１２５】
（Ｓ５）ＳＩＭＤ化部１３４は、演算式に含まれる１〜Ｎ−１回転前の演算結果を参照する変数を、その演算式自体を用いて展開する。変数の展開は、演算式に１〜Ｎ−１回転前の演算結果を参照する変数が含まれなくなるまで連続的に行われ得る。例えば、元の演算式が１回転前の演算結果を参照する変数を含む場合、変数の展開を連続的にＮ−１回行うことで、１〜Ｎ−１回転前の演算結果を参照する変数を含まない演算式を得る。
【０１２６】
（Ｓ６）ＳＩＭＤ化部１３４は、コンパイルコマンドに、演算順序の変更を許容するコンパイルオプションが付加されていたか確認する。当該コンパイルオプションが付加されていた場合、ＳＩＭＤ化部１３４は、展開後の演算式に含まれる演算子の実行順序を最適化する。例えば、ＳＩＭＤ化部１３４は、プロセッサ情報を参照して、ターゲットのＣＰＵが備える演算器の種類および数を確認する。ターゲットのＣＰＵが２以上のＳＩＭＤ命令を並列実行可能な場合、並列化が容易になるように演算子の実行順序を入れ替える。
【０１２７】
（Ｓ７）ＳＩＭＤ化部１３４は、元のループの記述に基づいて命令スケジューリングを仮実行し、ＳＩＭＤ命令を用いずにＮ回転分の演算を実行した場合の所要サイクル数を予測する。また、ＳＩＭＤ化部１３４は、ステップＳ５，Ｓ６の変形後のループの記述に基づいて命令スケジューリングを仮実行し、ＳＩＭＤ命令を用いて元のループＮ回転相当の演算を実行した場合の所要サイクル数を予測する。
【０１２８】
（Ｓ８）ＳＩＭＤ化部１３４は、ＳＩＭＤ化後の所要サイクル数がＳＩＭＤ化前の所要サイクル数より減少するか、すなわち、ＳＩＭＤ化によってループの実行効率が向上するか判断する。所要サイクル数が減少すると予測される場合はステップＳ９に処理が進み、所要サイクル数が減少しないと予測される場合はＳＩＭＤ最適化が終了する。
【０１２９】
（Ｓ９）ＳＩＭＤ化部１３４は、ステップＳ５，Ｓ６で変形した演算式を採用する。
（Ｓ１０）ＳＩＭＤ化部１３４は、ステップＳ２で並列化可能と判断された演算式またはステップＳ９で採用した変形後の演算式から、ループＮ回転分の演算を１回転で実行するＳＩＭＤ命令を生成する。そして、ＳＩＭＤ化部１３４は、中間コード記憶部１２２に記憶された中間コードを、ＳＩＭＤ命令を含むものに書き換える。すなわち、中間コードに含まれる幾つかのスカラ命令がＳＩＭＤ命令に変換される。このＳＩＭＤ最適化によって、ＳＩＭＤ命令を含むオブジェクトコードが生成されることになる。
【０１３０】
図２５は、並列化可能と判断されないコード例を示す図である。
上記のステップＳ２において並列化可能でないと判断される演算式の一例として、インダイレクトアクセスされる配列変数を含む演算式が挙げられる。コード２６１には、倍精度浮動小数点型の長さ１００００の配列変数ａ，ｂ，ｉｄｘ１，ｉｄｘ２と、ループ変数ｉを１から１００００まで１ずつ増加させるループが定義されている。ループ内には、演算式ａ（ｉｄｘ１（ｉ））＝ａ（ｉｄｘ２（ｉ））＋ｂ（ｉ）が定義されている。
【０１３１】
配列変数ｉｄｘ１は、ループｉ回転目の演算において配列変数ａの何番目の要素が定義されるべきかを示している。配列変数ｉｄｘ２は、ループｉ回転目の演算において配列変数ａの何番目の要素が参照されるべきかを示している。コード２６１では、ループｉ回転目の演算において、配列変数ａの何番目の要素が参照され配列変数ａの何番目の要素が定義されるかは、静的に決定されずコード２６１の実行時に決定される。このため、１〜Ｎ−１回転前の演算結果が参照されるか否かを、コンパイル時に判断することができない。そこで、コード２６１のループは並列化可能でないと判断される。なお、コード２６１については、上記のステップＳ３の判断もＮＯになりＳＩＭＤ最適化は行われない。
【０１３２】
第２の実施の形態のコンパイル装置１００によれば、ＳＩＭＤ幅をＮとすると、１〜Ｎ−１回転前の演算結果が参照されないようにループ内の演算式が展開される。これにより、そのままではＳＩＭＤ化できないループ内の演算をＳＩＭＤ化することが可能となる。また、ＳＩＭＤ化しない場合の予測サイクル数と演算式を展開してＳＩＭＤ化する場合の予測サイクル数とが比較され、ＳＩＭＤ化するか否かが決定される。これにより、演算式を展開することによる命令数やメモリアクセス回数の増加と、ＳＩＭＤ化することによる命令数の削減とが総合的に考慮され、ループの実行効率を向上させることができる。
【０１３３】
また、ターゲットのＣＰＵのアーキテクチャを示すプロセッサ情報を参照して予測サイクル数を算出することで、ターゲットのＣＰＵのアーキテクチャも考慮してＳＩＭＤ最適化を行うか否か適切に判断できる。また、ユーザが許可する場合には、展開した演算式内での演算子の実行順序を最適化することで、ループの実行サイクル数を短縮できる。
【０１３４】
なお、前述のように、第１の実施の形態の情報処理は、コンパイル装置１０にプログラムを実行させることで実現することができる。第２の実施の形態の情報処理は、コンパイル装置１００にプログラムを実行させることで実現することができる。
【０１３５】
プログラムは、コンピュータ読み取り可能な記録媒体（例えば、記録媒体１１３）に記録しておくことができる。記録媒体としては、例えば、磁気ディスク、光ディスク、光磁気ディスク、半導体メモリなどを使用できる。磁気ディスクには、ＦＤおよびＨＤＤが含まれる。光ディスクには、ＣＤ、ＣＤ−Ｒ（Recordable）／ＲＷ（Rewritable）、ＤＶＤおよびＤＶＤ−Ｒ／ＲＷが含まれる。プログラムは、可搬型の記録媒体に記録されて配布されることがある。その場合、可搬型の記録媒体からＨＤＤなどの他の記録媒体（例えば、ＨＤＤ１０３）にプログラムを複製して（インストールして）実行してもよい。
【符号の説明】
【０１３６】
１０  コンパイル装置
１１  記憶部
１２  変換部
１３，１４  コード
１５，１６  演算式
Ｔ１，Ｔ２  実行時間
【図１】
2015191346.tif 000003
【図２】
2015191346.tif 000004
【図３】
2015191346.tif 000005
【図４】
2015191346.tif 000006
【図５】
2015191346.tif 000007
【図６】
2015191346.tif 000008
【図７】
2015191346.tif 000009
【図８】
2015191346.tif 000010
【図９】
2015191346.tif 000011
【図１０】
2015191346.tif 000012
【図１１】
2015191346.tif 000013
【図１２】
2015191346.tif 000014
【図１３】
2015191346.tif 000015
【図１４】
2015191346.tif 000016
【図１５】
2015191346.tif 000017
【図１６】
2015191346.tif 000018
【図１７】
2015191346.tif 000019
【図１８】
2015191346.tif 000020
【図１９】
2015191346.tif 000021
【図２０】
2015191346.tif 000022
【図２１】
2015191346.tif 000023
【図２２】
2015191346.tif 000024
【図２３】
2015191346.tif 000025
【図２４】
2015191346.tif 000026
【図２５】
2015191346.tif 000027