(57)【要約】【課題】関数が返すポインタを代入先として指定したソースコードから生成されるオブジェクトコードの実行効率を向上させる。【解決手段】記憶部１１は、ポインタを戻り値として返す関数１５と、関数１５を呼び出す関数呼び出し１７を代入先として記載した代入文１６とを含むソースコード１３を記憶する。処理部１２は、関数１５に基づいて、関数１５に含まれていない追加の引数を含んでおり、ポインタを返す代わりに追加の引数の値をポインタが指す記憶領域に格納する関数１８を生成し、代入文１６を、代入文１６で代入される値を追加の引数として渡して関数１８を呼び出す関数呼び出し１９に置換する。処理部１２は、関数１８と関数呼び出し１９とを含むオブジェクトコード１４を生成する。【選択図】図１
2019144857.tif 000002
【特許請求の範囲】
【請求項１】
ポインタを戻り値として返す第１の関数と、前記第１の関数を呼び出す第１の関数呼び出しを代入先として記載した代入文とを含むソースコードを記憶する記憶部と、
前記第１の関数に基づいて、前記第１の関数に含まれていない追加の引数を含んでおり、前記ポインタを返す代わりに前記追加の引数の値を前記ポインタが指す記憶領域に格納する第２の関数を生成し、前記代入文を、前記代入文で代入される値を前記追加の引数として渡して前記第２の関数を呼び出す第２の関数呼び出しに置換し、前記第２の関数と前記第２の関数呼び出しとを含むオブジェクトコードを生成する処理部と、
を有する情報処理装置。
【請求項２】
前記代入文は、等号の左辺に前記第１の関数呼び出しを含む、
請求項１記載の情報処理装置。
【請求項３】
前記第１の関数が返す前記ポインタは、配列変数の記憶領域を指す、
請求項１記載の情報処理装置。
【請求項４】
前記第２の関数呼び出しは、前記追加の引数としてスカラ値を渡し、
前記第２の関数は、前記配列変数の複数の要素それぞれに前記スカラ値を代入する、
請求項３記載の情報処理装置。
【請求項５】
前記第２の関数呼び出しは、前記追加の引数として配列を渡し、
前記第２の関数は、前記配列変数の異なる要素に前記配列の異なる値を代入する、
請求項３記載の情報処理装置。
【請求項６】
前記処理部は、前記第２の関数において前記配列変数の複数の要素への値の代入が並列に実行されるように前記オブジェクトコードを生成する、
請求項３記載の情報処理装置。
【請求項７】
コンピュータが実行するコンパイル方法であって、
ポインタを戻り値として返す第１の関数と、前記第１の関数を呼び出す第１の関数呼び出しを代入先として記載した代入文とを含むソースコードを取得し、
前記第１の関数に基づいて、前記第１の関数に含まれていない追加の引数を含んでおり、前記ポインタを返す代わりに前記追加の引数の値を前記ポインタが指す記憶領域に格納する第２の関数を生成し、前記代入文を、前記代入文で代入される値を前記追加の引数として渡して前記第２の関数を呼び出す第２の関数呼び出しに置換し、
前記第２の関数と前記第２の関数呼び出しとを含むオブジェクトコードを生成する、
コンパイル方法。
【請求項８】
コンピュータに、
ポインタを戻り値として返す第１の関数と、前記第１の関数を呼び出す第１の関数呼び出しを代入先として記載した代入文とを含むソースコードを取得し、
前記第１の関数に基づいて、前記第１の関数に含まれていない追加の引数を含んでおり、前記ポインタを返す代わりに前記追加の引数の値を前記ポインタが指す記憶領域に格納する第２の関数を生成し、前記代入文を、前記代入文で代入される値を前記追加の引数として渡して前記第２の関数を呼び出す第２の関数呼び出しに置換し、
前記第２の関数と前記第２の関数呼び出しとを含むオブジェクトコードを生成する、
処理を実行させるコンパイルプログラム。
【発明の詳細な説明】
【技術分野】
【０００１】
本発明は情報処理装置、コンパイル方法およびコンパイルプログラムに関する。
【背景技術】
【０００２】
ソフトウェア開発においては、高水準言語で記述されたソースコードを機械語などの低水準言語で記述されたオブジェクトコードに変換するコンパイラを使用することが多い。コンパイラは、演算結果に影響がない範囲で実行時間の短縮やメモリ使用量の削減などを目的として、ソースコードをオブジェクトコードに変換する過程で各種の最適化を行うことがある。ソースコードに関数が記述されている場合、コンパイラが実行する最適化には、関数呼び出しを効率化する最適化が含まれることがある。
【０００３】
例えば、関数呼び出しが代入文の右辺に１回だけ出現している場合に、代入文の左辺のデータを関数呼び出しの引数に組み込むことで、関数の戻り値の転送を削減する最適化方法が提案されている。また、複数のレコードを含むブロック型戻り値を返す関数を検出し、当該関数を呼び出す呼び出し命令が右辺に出現する代入文を検出し、左辺の変数のアドレスを関数に渡すことでブロック型戻り値の転送を削減するプログラム翻訳装置が提案されている。また、関数において参照されるポインタ変数を追跡してポインタグラフを生成し、冗長なポインタを削減するコンパイル最適化方法が提案されている。
【先行技術文献】
【特許文献】
【０００４】
【特許文献１】特開平２−８５９３１号公報
【特許文献２】特開平６−３０１５４９号公報
【特許文献３】特開平１０−２６９０８８号公報
【発明の概要】
【発明が解決しようとする課題】
【０００５】
高水準言語であるプログラミング言語の中には、ポインタを戻り値として返す関数を呼び出す関数呼び出しを、代入文の左辺に記述することができるものがある。この代入文は、戻り値であるポインタが指す記憶領域に、代入文の右辺の値を書き込むことを意味する。このようなプログラミング言語の文法は、データ構造の定義や管理を関数に行わせ、関数から提供されるデータ構造を利用してデータ処理を行う場合に使用され得る。
【０００６】
しかし、関数がポインタを返す際には、呼び出し側モジュールがポインタの指す記憶領域への書き込みを適切に行えるように、配列長などデータ構造に関する情報を呼び出し側モジュールに伝達することになる。そのため、上記のソースコードを単純にコンパイルすると、スタック領域を用いてデータ構造に関する情報を受け渡すオブジェクトコードが生成され、スタック操作が多い非効率なオブジェクトコードになってしまうことがあるという問題がある。また、呼び出し側モジュールではデータ構造が静的には不明であるため、ＳＩＭＤ（Single Instruction Multiple Data）並列化やループ最適化などの既存のコンパイル最適化を代入文に適用することが難しいという問題がある。
【０００７】
１つの側面では、本発明は、関数が返すポインタを代入先として指定したソースコードから生成されるオブジェクトコードの実行効率を向上させる情報処理装置、コンパイル方法およびコンパイルプログラムを提供することを目的とする。
【課題を解決するための手段】
【０００８】
１つの態様では、記憶部と処理部とを有する情報処理装置が提供される。記憶部は、ポインタを戻り値として返す第１の関数と、第１の関数を呼び出す第１の関数呼び出しを代入先として記載した代入文とを含むソースコードを記憶する。処理部は、第１の関数に基づいて、第１の関数に含まれていない追加の引数を含んでおり、ポインタを返す代わりに追加の引数の値をポインタが指す記憶領域に格納する第２の関数を生成し、代入文を、代入文で代入される値を追加の引数として渡して第２の関数を呼び出す第２の関数呼び出しに置換し、第２の関数と第２の関数呼び出しとを含むオブジェクトコードを生成する。また、１つの態様では、コンピュータが実行するコンパイル方法が提供される。また、１つの態様では、コンピュータに実行させるコンパイルプログラムが提供される。
【発明の効果】
【０００９】
１つの側面では、関数が返すポインタを代入先として指定したソースコードから生成されるオブジェクトコードの実行効率を向上できる。
【図面の簡単な説明】
【００１０】
【図１】第１の実施の形態の情報処理装置を説明する図である。
【図２】情報処理装置のハードウェア例を示すブロック図である。
【図３】情報処理装置の機能例を示すブロック図である。
【図４】最適化前コードの例を示す図である。
【図５】最適化前の第１の命令実行例を示す図である。
【図６】最適化前の第２の命令実行例を示す図である。
【図７】最適化後コードの例を示す図である。
【図８】最適化後の第１の命令実行例を示す図である。
【図９】最適化後の第２の命令実行例を示す図である。
【図１０】最適化処理の手順例を示すフローチャートである。
【図１１】最適化処理の手順例を示すフローチャート（続き）である。
【発明を実施するための形態】
【００１１】
以下、本実施の形態を図面を参照して説明する。
［第１の実施の形態］
第１の実施の形態を説明する。
【００１２】
図１は、第１の実施の形態の情報処理装置を説明する図である。
第１の実施の形態の情報処理装置１０は、高水準言語（高級言語）を用いて記述されたソースコード（ソースプログラム）を、機械語など低水準言語（低級言語）で記述されたオブジェクトコード（オブジェクトプログラム）に変換する。オブジェクトコードは、情報処理装置１０によって実行される実行可能プログラムでもよいし、他の情報処理装置によって実行される実行可能プログラムでもよい。情報処理装置１０は、コンパイル装置やコンパイラなどと言うこともできる。情報処理装置１０は、ソフトウェアとしてのコンパイラ（コンパイルプログラム）を実行するコンピュータでもよい。情報処理装置１０は、端末装置などのクライアント装置でもよいしサーバ装置でもよい。
【００１３】
情報処理装置１０は、記憶部１１および処理部１２を有する。記憶部１１は、ＲＡＭ（Random Access Memory）などの揮発性の半導体メモリでもよいし、ＨＤＤ（Hard Disk Drive）やフラッシュメモリなどの不揮発性ストレージでもよい。処理部１２は、例えば、ＣＰＵ（Central Processing Unit）やＤＳＰ（Digital Signal Processor）などのプロセッサである。ただし、処理部１２は、ＡＳＩＣ（Application Specific Integrated Circuit）やＦＰＧＡ（Field Programmable Gate Array）などの特定用途の電子回路を含んでもよい。プロセッサは、ＲＡＭなどのメモリ（記憶部１１でもよい）に記憶されたプログラムを実行する。プログラムにはコンパイルプログラムが含まれる。複数のプロセッサの集合を「マルチプロセッサ」または単に「プロセッサ」と言うことがある。
【００１４】
記憶部１１は、ソースコード１３を記憶する。また、記憶部１１は、後述するように処理部１２によってソースコード１３から変換されたオブジェクトコード１４を記憶する。ソースコード１３は、ＦＯＲＴＲＡＮなどの高水準言語によって記述されたプログラムである。オブジェクトコード１４は、機械語やアセンブリ言語や中間言語など、ソースコードと比べて低水準言語によって記述されたプログラムである。
【００１５】
ソースコード１３は、関数１５（第１の関数）および代入文１６を含む。
関数１５は、関数１５の呼び出しに応じてポインタを戻り値として返す関数である。ポインタは、例えば、ある変数のメモリ領域を示すアドレスである。関数１５が返すポインタは、例えば、複数の配列要素を含む配列変数のメモリ領域を指す。図１では関数１５は、インデックスが１〜４の４個の配列要素を含む、配列長４かつインデックス幅１の配列ａｒｒａｙを示すポインタを戻り値として返している。
【００１６】
代入文１６は、関数１５を呼び出す関数呼び出し１７（第１の関数呼び出し）を代入先として記載した文である。代入文１６は、例えば、代入演算を示す等号の左辺に関数呼び出し１７を含んでおり、代入される値（代入値）のリテラルまたは代入値が既に格納された変数を等号の右辺に含んでいる。代入文１６は、関数１５から返されるポインタが指すメモリ領域に代入値を代入することを示している。図１では代入文１６は、ポインタが指す配列ａｒｒａｙの４個の配列要素それぞれに整数「１０」を代入している。
【００１７】
処理部１２は、ソースコード１３をコンパイルしてオブジェクトコード１４を生成する。もしソースコード１３を単純にコンパイルした場合、次のような問題が生じる。
代入文１６を実行すると、まず関数呼び出し１７が実行されて関数１５が呼び出され、関数１５から戻り値としてポインタが返される。このとき、代入文１６は、ポインタを受け取っただけではポインタが指す変数のデータ構造が不明である。このため、関数１５が戻り値を返す際には、配列長やインデックス幅などデータ構造に関する情報を、スタック領域を用いて関数１５から代入文１６に伝達することになる。代入文１６は、ポインタおよびデータ構造に関する情報に基づいて代入演算を行う。しかし、スタック領域への情報挿入（ｐｕｓｈ）やスタック領域からの情報抽出（ｐｏｐ）などのスタック操作が多いと、関数呼び出し１７の実行が非効率になってしまう。
【００１８】
また、データ構造の定義や管理を行う関数１５と、当該データ構造に従って代入演算を行う代入文１６が、異なる翻訳単位（コンパイル単位）に属している。よって、代入文１６から見ると静的にデータ構造が確定せず、ＳＩＭＤ並列化やループ最適化などの既存の最適化手法を代入演算に適用することが難しい。例えば、関数１５から返されるポインタが４個の配列要素を含む配列ａｒｒａｙを指していることが事前にわかれば、これら４個の配列要素への値の代入を、ＳＩＭＤ命令を用いて並列化する余地がある。しかし、代入文１６からはデータ構造が事前にわからないため、このような並列化は難しい。
【００１９】
そこで、処理部１２は、関数呼び出し１７に関して以下のような最適化を行う。最適化は、ソースコード１３をオブジェクトコード１４に変換する途中の任意の時点で行うことが可能である。例えば、処理部１２は、ソースコード１３を中間言語で記述された内部処理用の中間コードに変換し、中間コードをオブジェクトコード１４に変換する。その場合、処理部１２は、中間コードレベルで最適化を行うことができる。また、処理部１２は、ソースコードレベルやオブジェクトコードレベルで最適化を行うことも可能である。なお、図１では理解を容易にするため、最適化されたオブジェクトコード１４の処理手順をソースコード形式で記述しているが、実際には低水準言語で処理手順が記述される。また、この説明はソースコードレベルで最適化を行うことを意図したものではない。
【００２０】
処理部１２は、関数１５に基づいて関数１８（第２の関数）を生成する。関数１８は、関数１５に含まれている引数を含み、更に関数１５に含まれていない追加の引数を含む。関数１８は、原則として関数１５と同様の演算を行う。ただし、関数１８は、ポインタを返す代わりに、追加の引数の値をポインタが指すメモリ領域に格納する。すなわち、代入文１６に代わって関数１８が代入演算を行う。図１では関数１８は、追加の引数ａｒｇを含み、配列ａｒｒａｙの４個の配列要素それぞれに引数ａｒｇの値を代入している。
【００２１】
また、処理部１２は、代入文１６を関数呼び出し１９（第２の関数呼び出し）に置換する。関数呼び出し１９は、代入文１６の代入値を追加の引数として渡して関数１８を呼び出す。引数の受け渡しは、スタック領域を用いて行われる。図１では関数呼び出し１９は、整数「１０」を引数ａｒｇとして渡して関数１８を呼び出している。よって、関数１８により配列ａｒｒａｙの４個の配列要素それぞれに整数「１０」が代入される。
【００２２】
なお、関数１５，１８は、他のプログラムモジュールからの呼び出しに応じて演算を行う一定単位のプログラムモジュールであればよい。関数１８は戻り値を返さないプログラムモジュールであってもよい。関数１５，１８は、プログラミング言語に応じて、サブルーチン、手続き、メソッド、セクションなど様々な名称で呼ばれることがある。
【００２３】
そして、処理部１２は、関数１８および関数呼び出し１９に相当する命令を含むオブジェクトコード１４を生成する。代入文１６は最適化によって関数呼び出し１９に置換されるため、オブジェクトコード１４は代入文１６に相当する命令を含まない。一方、処理部１２は、関数１５を関数１８に置換してもよいし、関数１５を残して関数１８を追加してもよい。よって、オブジェクトコード１４は、関数１８に加えて関数１５に相当する命令を含んでもよいし、関数１５に相当する命令を含まなくてもよい。
【００２４】
最適化されたオブジェクトコード１４では、関数呼び出し１９が実行されると、スタック領域を用いて関数呼び出し１９から関数１８に代入値が渡される。代入先の変数のデータ構造は関数１８自身が知っているため、関数１８は代入演算を容易に実行することができる。また、関数１８は、関数呼び出し１９に戻り値としてポインタを返さなくてよく、スタック領域を用いてデータ構造に関する情報を関数呼び出し１９に伝達しなくてよい。よって、スタック操作を削減でき関数呼び出し１９の実行を効率化できる。
【００２５】
また、データ構造の定義や管理を行うプログラムモジュールと、当該データ構造に従って代入演算を行うプログラムモジュールが同一である。よって、ＳＩＭＤ並列化やループ最適化などの既存の最適化手法を代入演算に適用することが容易となる。例えば、関数１８では配列ａｒｒａｙのデータ構造が静的にわかるため、配列ａｒｒａｙの４個の配列要素それぞれに引数の値を代入する代入演算を、ＳＩＭＤ命令を用いて並列化できる。
【００２６】
このように、関数１５が返すポインタを代入先として指定したソースコード１３から生成されるオブジェクトコード１４の実行効率を向上させることができる。
［第２の実施の形態］
次に、第２の実施の形態を説明する。
【００２７】
第２の実施の形態の情報処理装置１００は、高水準言語で記述されたソースコードを機械語で記述された実行プログラムに変換するコンパイルを行う。
図２は、情報処理装置のハードウェア例を示すブロック図である。
【００２８】
情報処理装置１００は、ＣＰＵ１０１、ＲＡＭ１０２、ＨＤＤ１０３、画像信号処理部１０４、入力信号処理部１０５、媒体リーダ１０６および通信インタフェース１０７を有する。ＣＰＵ１０１、ＲＡＭ１０２、ＨＤＤ１０３、画像信号処理部１０４、入力信号処理部１０５、媒体リーダ１０６および通信インタフェース１０７は、バス１０８に接続されている。なお、情報処理装置１００は、第１の実施の形態の情報処理装置１０に対応する。ＣＰＵ１０１は、第１の実施の形態の処理部１２に対応する。ＲＡＭ１０２またはＨＤＤ１０３は、第１の実施の形態の記憶部１１に対応する。
【００２９】
ＣＰＵ１０１は、プログラムの命令を実行する演算回路を含むプロセッサである。ＣＰＵ１０１は、ＨＤＤ１０３に記憶されたプログラムやデータの少なくとも一部をＲＡＭ１０２にロードし、プログラムを実行する。なお、ＣＰＵ１０１は複数のプロセッサコアを備えてもよく、情報処理装置１００は複数のプロセッサを備えてもよく、以下で説明する処理を複数のプロセッサまたはプロセッサコアを用いて並列に実行してもよい。また、複数のプロセッサの集合（マルチプロセッサ）を「プロセッサ」と言うことがある。
【００３０】
ＲＡＭ１０２は、ＣＰＵ１０１が実行するプログラムやＣＰＵ１０１が演算に用いるデータを一時的に記憶する揮発性の半導体メモリである。なお、情報処理装置１００は、ＲＡＭ以外の種類のメモリを備えてもよく、複数個のメモリを備えてもよい。
【００３１】
ＨＤＤ１０３は、ＯＳ（Operating System）やミドルウェアやアプリケーションソフトウェアなどのソフトウェアのプログラム、および、データを記憶する不揮発性の記憶装置である。なお、情報処理装置１００は、フラッシュメモリやＳＳＤ（Solid State Drive）など他の種類の記憶装置を備えてもよく、複数の不揮発性の記憶装置を備えてもよい。
【００３２】
画像信号処理部１０４は、ＣＰＵ１０１からの命令に従って、情報処理装置１００に接続されたディスプレイ１１１に画像を出力する。ディスプレイ１１１としては、ＣＲＴ（Cathode Ray Tube）ディスプレイ、液晶ディスプレイ（ＬＣＤ：Liquid Crystal Display）、プラズマディスプレイ、有機ＥＬ（ＯＥＬ：Organic Electro-Luminescence）ディスプレイなど、任意の種類のディスプレイを用いることができる。
【００３３】
入力信号処理部１０５は、情報処理装置１００に接続された入力デバイス１１２から入力信号を取得し、ＣＰＵ１０１に出力する。入力デバイス１１２としては、マウスやタッチパネルやタッチパッドやトラックボールなどのポインティングデバイス、キーボード、リモートコントローラ、ボタンスイッチなどを用いることができる。また、情報処理装置１００に、複数の種類の入力デバイスが接続されていてもよい。
【００３４】
媒体リーダ１０６は、記録媒体１１３に記録されたプログラムやデータを読み取る読み取り装置である。記録媒体１１３として、例えば、フレキシブルディスク（ＦＤ：Flexible Disk）やＨＤＤなどの磁気ディスク、ＣＤ（Compact Disc）やＤＶＤ（Digital Versatile Disc）などの光ディスク、光磁気ディスク（ＭＯ：Magneto-Optical disk）、半導体メモリなどを使用できる。媒体リーダ１０６は、例えば、記録媒体１１３から読み取ったプログラムやデータをＲＡＭ１０２またはＨＤＤ１０３に格納する。
【００３５】
通信インタフェース１０７は、ネットワーク１１４に接続され、ネットワーク１１４を介して他の装置と通信を行うインタフェースである。通信インタフェース１０７は、スイッチなどの通信装置とケーブルで接続される有線通信インタフェースでもよいし、基地局と無線リンクで接続される無線通信インタフェースでもよい。
【００３６】
図３は、情報処理装置の機能例を示すブロック図である。
情報処理装置１００は、ソースコード記憶部１２１、中間コード記憶部１２２、実行プログラム記憶部１２３、中間コード生成部１２４、最適化部１２５およびバックエンド処理部１２６を有する。ソースコード記憶部１２１、中間コード記憶部１２２および実行プログラム記憶部１２３は、例えば、ＲＡＭ１０２またはＨＤＤ１０３を用いて実装される。中間コード生成部１２４、最適化部１２５およびバックエンド処理部１２６は、例えば、ＣＰＵ１０１が実行するコンパイルプログラムを用いて実装される。
【００３７】
ソースコード記憶部１２１は、ユーザが作成したソースコードを記憶する。ソースコードは、ＦＯＲＴＲＡＮ２００８などの高水準言語で記述されている。ソースコードは、ユーザが入力デバイス１１２を用いて情報処理装置１００に入力してもよいし、情報処理装置１００がネットワーク１１４を介して他の情報処理装置から受信してもよい。
【００３８】
中間コード記憶部１２２は、情報処理装置１００が生成した中間コードを記憶する。中間コードは、コンパイルの内部処理に適した中間言語で記述されている。
実行プログラム記憶部１２３は、情報処理装置１００が生成した実行プログラムを記憶する。実行プログラムは、機械語で記述されたオブジェクトコードを含む。実行プログラムには、ライブラリなどソースコードに対応するオブジェクトコード以外のコードが組み込まれていてもよい。実行プログラムは、ＣＰＵ１０１、情報処理装置１００が有する他のプロセッサまたは他の情報処理装置のプロセッサによって実行され得る。
【００３９】
中間コード生成部１２４は、ソースコード記憶部１２１からソースコードを読み出し、字句解析、構文解析、意味解析などのフロントエンド処理を行う。中間コード生成部１２４は、ソースコードから変換された中間コードを中間コード記憶部１２２に格納する。
【００４０】
最適化部１２５は、中間コード記憶部１２２に記憶された中間コードを解析し、実行効率の高い実行プログラムが生成されるように中間コードを変換する最適化を行う。最適化の目的には、実行時間の短縮とメモリ使用量の削減が含まれ得る。最適化部１２５が行う最適化には、異なる値に対する同種の演算を逐次実行する代わりに、ＳＩＭＤ命令を用いて並列実行するように変換するＳＩＭＤ並列化が含まれる。また、最適化部１２５が行う最適化には、ループ内の命令を複製してループ回転数を減らすループ展開が含まれる。また、最適化部１２５が行う最適化には、後述する関数呼び出しの効率化が含まれる。
【００４１】
最適化部１２５は、関数生成部１２７および関数呼び出し変換部１２８を有する。
関数生成部１２７は、配列を指すポインタを戻り値として返す関数を中間コードの中から検索する。該当する関数がある場合、関数生成部１２７は、当該関数に類似する２つの関数を中間コードに追加する。追加する第１の関数は、呼び出し時に引数としてスカラ値を受け取り、配列のポインタを返す代わりに、当該配列の各配列要素に対して引数のスカラ値を代入するものである。スカラ値は、単一の整数や単一の実数や単一の文字などの具体的な値である。追加する第２の関数は、呼び出し時に引数として複数の値の集合（配列のデータ）を受け取り、配列のポインタを返す代わりに、当該配列の異なる配列要素に対して引数の異なる値を代入するものである。
【００４２】
関数呼び出し変換部１２８は、関数生成部１２７が検索した関数を呼び出す関数呼び出しを中間コードの中から検索し、該当する関数呼び出しを代入先に指定している代入命令を中間コードの中から検索する。このような代入命令は、ソースコード上では当該関数呼び出しを左辺に含む代入文に対応する。この代入命令は、関数から戻り値として返されたポインタが指す配列の各配列要素に対して値を代入することを意味する。
【００４３】
関数呼び出し変換部１２８は、該当する代入命令を、代入命令が指定する代入値の種類に応じて、関数生成部１２７が追加した２つの関数の何れか一方を呼び出す関数呼び出しに置換する。代入命令が指定する代入値は、ソースコード上では代入文の右辺に記載されている。代入値がスカラ値である場合、関数呼び出し変換部１２８は、代入値であるスカラ値を引数として渡して上記の第１の関数を呼び出す関数呼び出しを生成する。代入値が配列である場合、関数呼び出し変換部１２８は、代入値である配列を引数として渡して上記の第２の関数を呼び出す関数呼び出しを生成する。
【００４４】
バックエンド処理部１２６は、最適化部１２５が最適化を行った後の中間コードを読み出し、中間コードをオブジェクトコードに変換するバックエンド処理を行う。バックエンド処理部１２６は、オブジェクトコードを含む実行プログラムを実行プログラム記憶部１２３に格納する。バックエンド処理部１２６は、中間コードをアセンブリ言語で記述されたアセンブリコードに変換し、アセンブリコードをオブジェクトコードに変換してもよい。実行プログラムでは、ライブラリプログラムなど、ソースコードに対応するオブジェクトコードから参照される他のオブジェクトコードがリンクされてもよい。リンク処理はコンパイラが行ってもよいし、コンパイラとは異なるリンカが行ってもよい。
【００４５】
なお、中間コード生成部１２４、最適化部１２５およびバックエンド処理部１２６はコンパイラに属する。バックエンド処理部１２６がリンク処理を行ってもよい。また、情報処理装置１００が、図示しないリンカを有していてもよい。
【００４６】
次に、ソースコードの例を示し、関数呼び出しの最適化を行わない場合の命令実行例と関数呼び出しの最適化を行った場合の命令実行例を説明する。
図４は、最適化前コードの例を示す図である。
【００４７】
ソースコード１３１は、ソースコード記憶部１２１に記憶される。ソースコード１３１には、モジュール１３１ａ，１３１ｂ，１３１ｃが記載されている。
モジュール１３１ａは、配列要素が整数型であり要素数（配列長）が１００の配列ａを宣言している。また、モジュール１３１ａは、関数ｆを定義している。関数ｆは、整数型の引数ｓｔａｒｔを含む。関数ｆは、配列ａのｓｔａｒｔ番目の配列要素からｓｔａｒｔ＋３番目の配列要素までの範囲を切り出し、切り出した部分配列ａ（ｓｔａｒｔ：ｓｔａｒｔ＋３）のアドレスを示すポインタを戻り値として返すものである。
【００４８】
モジュール１３１ｂは、引数ｓｔａｒｔとして整数「１」を渡して関数ｆを呼び出す関数呼び出しｆ（１）を左辺にもち、代入値である整数「１０」を右辺にもつ代入文を含む。この代入文は、関数ｆが返す部分配列ａ（ｓｔａｒｔ：ｓｔａｒｔ＋３）の４個の配列要素それぞれに対して整数「１０」を代入することを意味する。よって、配列要素ａ（１），ａ（２），ａ（３），ａ（４）それぞれに整数「１０」が代入される。
【００４９】
モジュール１３１ｃは、配列要素が整数型であり要素数が４であり各配列要素の値が整数「１０」である配列ｘを定義している。また、モジュール１３１ｃは、引数ｓｔａｒｔとして整数「１」を渡して関数ｆを呼び出す関数呼び出しｆ（１）を左辺にもち、代入値である配列ｘを右辺にもつ代入文を含む。この代入文は、関数ｆが返す部分配列ａ（ｓｔａｒｔ：ｓｔａｒｔ＋３）の４個の配列要素に対応付けて配列ｘの４個の整数を代入することを意味する。すなわち、ａ（ｓｔａｒｔ）＝ｘ（１），ａ（ｓｔａｒｔ＋１）＝ｘ（２），ａ（ｓｔａｒｔ＋２）＝ｘ（３），ａ（ｓｔａｒｔ＋３）＝ｘ（４）である。よって、ａ（１）＝ａ（２）＝ａ（３）＝ａ（４）＝１０となる。
【００５０】
ソースコード１３１から、関数生成部１２７および関数呼び出し変換部１２８の最適化を行わずにオブジェクトコードを生成した場合、命令の実行手順は次のようになる。
図５は、最適化前の第１の命令実行例を示す図である。
【００５１】
ここでは、モジュール１３１ｂの代入文に着目する。オブジェクトコードを起動したプロセッサは、以下の命令１４１ａ〜１４１ｋを順に実行することになる。
プロセッサは、引数ｓｔａｒｔとして整数「１」をスタックにｐｕｓｈする（１４１ａ）。スタックは、プロセッサが使用するＲＡＭの中の記憶領域であって、後入れ先出し（ＬＩＦＯ：Last In First Out）方式で値を記憶する記憶領域である。関数呼び出しに関する情報を記憶するスタックをコールスタックと言うことがある。スタックに値を格納するスタック操作をｐｕｓｈと言い、スタックから値を抽出するスタック操作をｐｏｐと言う。プロセッサは、関数ｆの先頭の命令にジャンプする（１４１ｂ）。
【００５２】
プロセッサは、引数ｓｔａｒｔとして整数「１」をスタックからｐｏｐする（１４１ｃ）。プロセッサは、ｓｔａｒｔ＝１から配列ａのサブセットである部分配列ａ（１：４）を特定する。プロセッサは、部分配列ａ（１：４）の先頭アドレス、開始インデックスを示す整数「１」、要素数を示す整数「４」、インデックス幅（刻み幅）を示す整数「１」をスタックにｐｕｓｈする（１４１ｄ）。インデックス幅は、配列ａから切り出された複数の配列要素のインデックスが幾つずつ離れているかを示す。インデックス幅が１であることは、配列ａから隣接する連続した配列要素が切り出されたことを意味する。これら４つの情報をｐｕｓｈする順序は、呼び出し側と合意しておけば任意の順序でよい。プロセッサは、関数ｆから呼び出し側にリターンする（１４１ｅ）。
【００５３】
プロセッサは、部分配列ａ（１：４）の先頭アドレス、開始インデックスを示す整数「１」、要素数を示す整数「４」、インデックス幅を示す整数「１」をスタックからｐｏｐする（１４１ｆ）。これら４つの情報の順序は、関数ｆとの間で予め合意しておく。プロセッサは、ループ変数ｉの値を１に初期化する（１４１ｇ）。この位置でラベルＬ１が定義される（１４１ｈ）。プロセッサは、部分配列ａ（１：４）の配列要素ａ（ｉ）にスカラ値である整数「１０」を代入する（１４１ｉ）。プロセッサは、ループ変数ｉの値を１増加させる（１４１ｊ）。プロセッサは、ループ変数ｉの値と整数「４」を比較し、ループ変数ｉの値が４以下であればラベルＬ１にジャンプする（１４１ｋ）。ループ変数ｉの値が４より大きければ、ループを抜けて代入処理を終了する。
【００５４】
図６は、最適化前の第２の命令実行例を示す図である。
ここでは、モジュール１３１ｃの代入文に着目する。オブジェクトコードを起動したプロセッサは、命令１４２ａ〜１４２ｋを順に実行することになる。命令１４２ａ〜１４２ｈは、図５の命令１４１ａ〜１４１ｈと同様である。命令１４２ｈの後、プロセッサは、部分配列ａ（１：４）の配列要素ａ（ｉ）に、配列ｘの配列要素ｘ（ｉ）の値を代入する（１４２ｉ）。ｘ（ｉ）＝１０であるため、配列要素ａ（ｉ）に整数「１０」が代入される。その後の命令１４２ｊ，１４２ｋは、図５の命令１４１ｊ，１４１ｋと同様である。
【００５５】
このように、配列のポインタを返す関数を呼び出す関数呼び出しが代入文の左辺に記載されている場合、その代入文が示す代入処理を実行する毎に、配列のデータ構造に関する情報を受け渡すスタック操作が発生する。関数の呼び出し側では、ポインタが指す配列のデータ構造を静的に確定できないためである。よって、スタック操作が多くなりオブジェクトコードの実行効率が低下することがある。また、関数の呼び出し側では配列のデータ構造が事前にわからないため、命令１４１ｉ，１４２ｉをＳＩＭＤ命令に変換して４個の配列要素の代入演算を並列化することは難しい。
【００５６】
そこで、関数生成部１２７および関数呼び出し変換部１２８が以下の最適化を行う。
図７は、最適化後コードの例を示す図である。
ここでは理解を容易にするため、ソースコード１３１を変換したソースコード１３２を説明する。実際には関数生成部１２７および関数呼び出し変換部１２８は、ソースコード１３１に対応する中間コードを変換して、ソースコード１３２に対応する中間コードを生成する。ソースコード１３２には、モジュール１３２ａ，１３２ｂ，１３２ｃが記載されている。モジュール１３１ａがモジュール１３２ａに変換され、モジュール１３１ｂがモジュール１３２ｂに変換され、モジュール１３１ｃがモジュール１３２ｃに変換される。
【００５７】
モジュール１３２ａは、モジュール１３１ａと同様に、配列ａを宣言していると共に関数ｆを定義している。更に、モジュール１３２ａは、関数ｆに基づいて生成された関数ｆ＿ｓｃａｌａｒと関数ｆ＿ａｒｒａｙを定義している。なお、図７で使用しているプログラミング言語では、戻り値のない関数はサブルーチンと呼ばれる。
【００５８】
関数ｆ＿ｓｃａｌａｒは、関数ｆと同様の引数ｓｔａｒｔに加えて、整数型の引数ｎｅｗ＿ａｒｇを含む。関数ｆ＿ｓｃａｌａｒは、配列ａのｓｔａｒｔ番目の配列要素からｓｔａｒｔ＋３番目の配列要素までの範囲を切り出す。関数ｆ＿ｓｃａｌａｒは、ポインタを戻り値として返す代わりに、部分配列ａ（ｓｔａｒｔ：ｓｔａｒｔ＋３）の４個の配列要素それぞれに対して引数ｎｅｗ＿ａｒｇの値を代入する。
【００５９】
関数ｆ＿ａｒｒａｙは、関数ｆと同様の引数ｓｔａｒｔに加えて、配列型の引数ｎｅｗ＿ａｒｇを含む。関数ｆ＿ａｒｒａｙは、配列ａのｓｔａｒｔ番目の配列要素からｓｔａｒｔ＋３番目の配列要素までの範囲を切り出す。関数ｆ＿ａｒｒａｙは、ポインタを戻り値として返す代わりに、部分配列ａ（ｓｔａｒｔ：ｓｔａｒｔ＋３）の４個の配列要素に対して引数ｎｅｗ＿ａｒｇの配列に含まれる４個の値を代入する。ａ（ｓｔａｒｔ）＝ｎｅｗ＿ａｒｇ（１），ａ（ｓｔａｒｔ＋１）＝ｎｅｗ＿ａｒｇ（２），ａ（ｓｔａｒｔ＋２）＝ｎｅｗ＿ａｒｇ（３），ａ（ｓｔａｒｔ＋３）＝ｎｅｗ＿ａｒｇ（４）である。
【００６０】
モジュール１３２ｂは、関数ｆの代わりに関数ｆ＿ｓｃａｌａｒを呼び出す。モジュール１３２ｂは、引数ｓｔａｒｔとして整数「１」を渡し、引数ｎｅｗ＿ａｒｇとして整数「１０」を渡して、関数ｆ＿ｓｃａｌａｒを呼び出している。よって、配列要素ａ（１），ａ（２），ａ（３），ａ（４）それぞれに整数「１０」が代入される。
【００６１】
モジュール１３２ｃは、モジュール１３１ｃと同様に配列ｘを定義している。一方、モジュール１３２ｃは、関数ｆの代わりに関数ｆ＿ａｒｒａｙを呼び出す。モジュール１３２ｃは、引数ｓｔａｒｔとして整数「１」を渡し、引数ｎｅｗ＿ａｒｇとして配列ｘのアドレスを渡して、関数ｆ＿ａｒｒａｙを呼び出している。よって、ａ（１）＝ｘ（１），ａ（２）＝ｘ（２），ａ（３）＝ｘ（３），ａ（４）＝ｘ（４）となる。すなわち、配列要素ａ（１），ａ（２），ａ（３），ａ（４）それぞれに整数「１０」が代入される。
【００６２】
ソースコード１３２に相当する最適化済みの中間コードからオブジェクトコードを生成した場合、命令の実行手順は次のようになる。
図８は、最適化後の第１の命令実行例を示す図である。
【００６３】
ここでは、モジュール１３２ｂの代入文に着目する。オブジェクトコードを起動したプロセッサは、以下の命令１４３ａ〜１４３ｅを順に実行することになる。
プロセッサは、引数ｓｔａｒｔを示す整数「１」と引数ｎｅｗ＿ａｒｇを示す整数「１０」をスタックにｐｕｓｈする（１４３ａ）。これら２つの情報をｐｕｓｈする順序は、関数ｆ＿ｓｃａｌａｒと合意しておけば任意の順序でよい。プロセッサは、関数ｆ＿ｓｃａｌａｒの先頭の命令にジャンプする（１４３ｂ）。
【００６４】
プロセッサは、引数ｓｔａｒｔを示す整数「１」と引数ｎｅｗ＿ａｒｇを示す整数「１０」をスタックからｐｏｐする（１４３ｃ）。これら２つの情報の順序は、呼び出し側との間で予め合意しておく。プロセッサは、ｓｔａｒｔ＝１から配列ａのサブセットである部分配列ａ（１：４）を特定する。プロセッサは、部分配列ａ（１：４）に含まれる配列要素ａ（１），ａ（２），ａ（３），ａ（４）それぞれに対して、引数ｎｅｗ＿ａｒｇとして取得したスカラ値である整数「１０」を代入する（１４３ｄ）。命令１４３ｄはＳＩＭＤ命令として実装することができる。すなわち、４個の配列要素への整数の代入を、１つのＳＩＭＤ命令を用いて並列に実行することが可能となる。プロセッサは、関数ｆ＿ｓｃａｌａｒから呼び出し側にリターンする（１４３ｅ）。
【００６５】
図９は、最適化後の第２の命令実行例を示す図である。
ここでは、モジュール１３２ｃの代入文に着目する。オブジェクトコードを起動したプロセッサは、以下の命令１４４ａ〜１４４ｆを順に実行することになる。
【００６６】
プロセッサは、引数ｓｔａｒｔを示す整数「１」と引数ｎｅｗ＿ａｒｇを示す配列ｘの先頭アドレスをスタックにｐｕｓｈする（１４４ａ）。これら２つの情報をｐｕｓｈする順序は、関数ｆ＿ａｒｒａｙと合意しておけば任意の順序でよい。プロセッサは、関数ｆ＿ａｒｒａｙの先頭の命令にジャンプする（１４４ｂ）。
【００６７】
プロセッサは、引数ｓｔａｒｔを示す整数「１」と引数ｎｅｗ＿ａｒｇを示す配列ｘの先頭アドレスをスタックからｐｏｐする（１４４ｃ）。これら２つの情報の順序は、呼び出し側との間で予め合意しておく。プロセッサは、ｓｔａｒｔ＝１から配列ａのサブセットである部分配列ａ（１：４）を特定する。プロセッサは、引数ｎｅｗ＿ａｒｇが示す先頭アドレスに基づいて、配列ｘの先頭から４個の配列要素である配列要素ｘ（１），ｘ（２），ｘ（３），ｘ（４）をＲＡＭからロードする（１４４ｄ）。命令１４４ｄはＳＩＭＤ命令として実装することができる。すなわち、４個の配列要素のロードを、１つのＳＩＭＤ命令を用いて並列に実行することが可能となる。
【００６８】
プロセッサは、部分配列ａ（１：４）に含まれる４個の配列要素である配列要素ａ（１），ａ（２），ａ（３），ａ（４）に対して、命令１４４ｄでロードした配列要素ｘ（１），ｘ（２），ｘ（３），ｘ（４）を代入する（１４４ｅ）。すなわち、ａ（１）＝ｘ（１），ａ（２）＝ｘ（２），ａ（３）＝ｘ（３），ａ（４）＝ｘ（４）となる。命令１４４ｅはＳＩＭＤ命令として実装することができる。すなわち、４個の配列要素に対する代入を、１つのＳＩＭＤ命令を用いて並列に実行することが可能となる。プロセッサは、関数ｆ＿ａｒｒａｙから呼び出し側にリターンする（１４４ｆ）。
【００６９】
このように、関数が配列のポインタを返して呼び出し側が配列への代入を行う代わりに、呼び出し側が関数に代入値を渡して関数が配列への代入を行うように最適化される。よって、配列のデータ構造に関する情報を関数から呼び出し側に提供しなくてよく、スタック操作が削減される。また、関数自身は配列のデータ構造を事前に知ることができるため、配列への代入をＳＩＭＤ命令を用いて並列化することが容易となる。
【００７０】
次に、情報処理装置１００による最適化処理の手順を説明する。
図１０は、最適化処理の手順例を示すフローチャートである。
（Ｓ１０）関数生成部１２７は、中間コードの中から、配列（配列ａ）を指すポインタを戻り値として返す関数（関数ｆ）を検索する。
【００７１】
（Ｓ１１）関数生成部１２７は、ステップＳ１０で検索された関数ｆのうちステップＳ１２〜Ｓ１９が未処理のものがあるか判断する。未処理の関数ｆがある場合は１つの関数ｆを選択してステップＳ１２に進み、ない場合はステップＳ２０に進む。
【００７２】
（Ｓ１２）関数生成部１２７は、関数ｆをコピーした関数ｆ＿ｓｃａｌａｒを中間コードに追加する。追加する関数ｆ＿ｓｃａｌａｒは、関数ｆの関数名の末尾に所定の文字列を追加するなど関数ｆとは異なる関数名をもつようにする。
【００７３】
（Ｓ１３）関数生成部１２７は、ステップＳ１２で生成した関数ｆ＿ｓｃａｌａｒの引数に、スカラ値をとる引数ｎｅｗ＿ａｒｇを追加する。関数ｆ＿ｓｃａｌａｒは、関数ｆがもつ引数についてはそのまま引き継ぐようにする。
【００７４】
（Ｓ１４）関数生成部１２７は、ステップＳ１２で生成した関数ｆ＿ｓｃａｌａｒに、配列ａの各配列要素に引数ｎｅｗ＿ａｒｇのスカラ値を代入する命令を追加する。
（Ｓ１５）関数生成部１２７は、ステップＳ１２で生成した関数ｆ＿ｓｃａｌａｒから、戻り値として配列ａのポインタを返す命令を削除する。
【００７５】
（Ｓ１６）関数生成部１２７は、関数ｆをコピーした関数ｆ＿ａｒｒａｙを中間コードに追加する。追加する関数ｆ＿ａｒｒａｙは、関数ｆの関数名の末尾に所定の文字列を追加するなど関数ｆや関数ｆ＿ｓｃａｌａｒとは異なる関数名をもつようにする。
【００７６】
（Ｓ１７）関数生成部１２７は、ステップＳ１６で生成した関数ｆ＿ａｒｒａｙの引数に、配列をとる引数ｎｅｗ＿ａｒｇを追加する。関数ｆ＿ａｒｒａｙは、関数ｆがもつ引数についてはそのまま引き継ぐようにする。
【００７７】
（Ｓ１８）関数生成部１２７は、ステップＳ１６で生成した関数ｆ＿ａｒｒａｙに、配列ａに引数ｎｅｗ＿ａｒｇの配列を代入する命令を追加する。
（Ｓ１９）関数生成部１２７は、ステップＳ１６で生成した関数ｆ＿ａｒｒａｙから、戻り値として配列ａのポインタを返す命令を削除する。そして、ステップＳ１１に進む。
【００７８】
図１１は、最適化処理の手順例を示すフローチャート（続き）である。
（Ｓ２０）関数呼び出し変換部１２８は、中間コードの中から、ステップＳ１０で検索された何れかの関数ｆにジャンプする関数呼び出し（関数呼び出しｃ）を検索する。
【００７９】
（Ｓ２１）関数呼び出し変換部１２８は、ステップＳ２０で検索された関数呼び出しｃのうちステップＳ２２〜Ｓ２８が未処理のものがあるか判断する。未処理の関数呼び出しｃがある場合は１つの関数呼び出しｃを選択してステップＳ２２に進み、ない場合は関数呼び出しに関する最適化処理が終了する。
【００８０】
（Ｓ２２）関数呼び出し変換部１２８は、関数呼び出しｃの位置は代入文の左辺に相当するか、すなわち、関数呼び出しｃが返す配列ａのポインタが代入命令において代入先として使用されているか判断する。関数呼び出しｃの位置が代入文の左辺に相当する場合はステップＳ２３に進み、それ以外の場合はステップＳ２１に進む。
【００８１】
（Ｓ２３）関数呼び出し変換部１２８は、ステップＳ２２の代入文の右辺はスカラ値であるか、すなわち、代入命令における代入値がスカラ値か判断する。代入文の右辺がスカラ値である場合はステップＳ２４に進み、それ以外の場合はステップＳ２５に進む。
【００８２】
（Ｓ２４）関数呼び出し変換部１２８は、関数呼び出しｃのジャンプ先をステップＳ１２で生成された関数ｆ＿ｓｃａｌａｒに変更する。そして、ステップＳ２７に進む。
（Ｓ２５）関数呼び出し変換部１２８は、ステップＳ２２の代入文の右辺はインデックス幅（刻み幅）１の配列であるか、すなわち、代入命令における代入値がインデックス幅１の配列を示しているか判断する。インデックス幅１の配列は、複数の配列要素がｘ（１），ｘ（２），ｘ（３），…のように連続するインデックス番号によって識別される配列である。代入文の右辺がインデックス幅１の配列である場合はステップＳ２６に進み、それ以外の場合はステップＳ２１に進む。
【００８３】
（Ｓ２６）関数呼び出し変換部１２８は、関数呼び出しｃのジャンプ先をステップＳ１６で生成された関数ｆ＿ａｒｒａｙに変更する。
（Ｓ２７）関数呼び出し変換部１２８は、代入文の右辺に相当するスカラ値またはインデックス幅１の配列のアドレスを、関数呼び出しｃの引数に追加する。関数呼び出しｃに既存の引数が存在する場合、新たな引数は末尾に追加する。
【００８４】
（Ｓ２８）関数呼び出し変換部１２８は、関数呼び出しｃを左辺に含む代入文に相当する代入命令を、中間コードから削除する。そして、ステップＳ２１に進む。
第２の実施の形態の情報処理装置１００によれば、戻り値として配列のポインタを返す関数を呼び出す関数呼び出しが代入文の左辺に記載されたソースコードをコンパイルする際に、新たな関数がオブジェクトコードに追加される。新たな関数は、引数として代入値を受け取り、ポインタを返す代わりに関数の中で配列への代入演算を行う。また、オブジェクトコードにおいて上記の代入文に相当する代入命令が、代入値を引数として渡して新たな関数を呼び出す関数呼び出し命令に置換される。
【００８５】
これにより、ポインタが指す配列のデータ構造に関する情報を関数から呼び出し側に伝達しなくてもよく、当該情報を受け渡すためのスタック操作を削減することができる。よって、スタック操作が削減された効率的なオブジェクトコードを生成することができる。また、配列への代入演算を行う新たな関数では配列のデータ構造が静的にわかっているため、代入演算に対してＳＩＭＤ並列化やループ最適化などのコンパイル最適化を適用することが容易となる。よって、オブジェクトコードの実行効率が向上する。
【符号の説明】
【００８６】
１０  情報処理装置
１１  記憶部
１２  処理部
１３  ソースコード
１４  オブジェクトコード
１５，１８  関数
１６  代入文
１７，１９  関数呼び出し
【図１】
2019144857.tif 000003
【図２】
2019144857.tif 000004
【図３】
2019144857.tif 000005
【図４】
2019144857.tif 000006
【図５】
2019144857.tif 000007
【図６】
2019144857.tif 000008
【図７】
2019144857.tif 000009
【図８】
2019144857.tif 000010
【図９】
2019144857.tif 000011
【図１０】
2019144857.tif 000012
【図１１】
2019144857.tif 000013