(57)【要約】【課題】右辺値参照の機能が利用されないソースコードの実行速度を簡単に向上させることを目的とする。【解決手段】コンパイル装置は、ソースコードをコンパイルするコンパイル装置であって、前記ソースコードに基づいて生成される解析木データから、右辺値参照を宣言した第１関数が検出されず、且つ該第１関数とは異なる特定の第２関数が検出された場合、前記解析木データに前記右辺値参照が宣言された関数を示す構文木を追加して、前記解析木データを書き換える書き換え部、を含む。【選択図】図１
2017151903.tif 000002
【特許請求の範囲】
【請求項１】
ソースコードをコンパイルするコンパイル装置であって、
前記ソースコードに基づいて生成される解析木データから、右辺値参照を宣言した第１関数が検出されず、且つ該第１関数とは異なる特定の第２関数が検出された場合、前記解析木データに前記右辺値参照が宣言された第１関数を示す構文木を追加して、前記解析木データを書き換える書き換え部、
を備えることを特徴とするコンパイル装置。
【請求項２】
前記ソースコードに前記第２関数が記述されている場合、前記書き換え部は、該第２関数の記述の一部を変換して、前記右辺値参照が宣言された第１関数を示す構文木を生成し、生成された構文木を前記解析木データに追加する、
ことを特徴とする請求項１記載のコンパイル装置。
【請求項３】
前記第１関数は、ムーブコンストラクタまたはムーブ代入演算子であり、
前記第２関数は、デストラクタ、コピーコンストラクタまたはコピー代入演算子であること、
を特徴とする請求項１または２記載のコンパイル装置。
【請求項４】
前記第１関数の引数が複数ある場合、前記書き換え部は、該引数ごとに前記右辺値参照の宣言を付与した前記関数を示す解析木、および全ての引数に前記右辺値参照の宣言を付与した前記関数を示す解析木を前記解析木データに追加する、
ことを特徴とする請求項１乃至３のうち何れか１項に記載のコンパイル装置。
【請求項５】
前記書き換え部は、前記ムーブコンストラクタと前記ムーブ代入演算子とのうち何れか一方を示す解析木が検出され、且つ他方を示す解析木が検出されない場合、検出された解析木を前記解析木データに追加しない、
ことを特徴とする請求項３記載のコンパイル装置。
【請求項６】
前記ムーブ代入演算子を用いた演算式が１次元以上の配列の演算であり、且つ前記演算式の返り値添え字と複数の引数のそれぞれの添え字とが全て同じである場合にのみ、前記書き換え部は、前記変換された関数を示す解析木を前記解析木データに追加する、
ことを特徴とする請求項３乃至５のうち何れか１項に記載のコンパイル装置。
【請求項７】
前記書き換え部が書き換えた解析木データを復元したソースコードを出力する出力部、
を備えることを特徴とする請求項１乃至６のうち何れか１項に記載のコンパイル装置。
【請求項８】
ソースコードをコンパイルするコンパイル方法であって、
前記ソースコードに基づいて生成される解析木データから、右辺値参照を宣言した第１関数が検出されず、且つ該第１関数とは異なる特定の第２関数が検出された場合、前記解析木データに前記右辺値参照が宣言された第１関数を示す構文木を追加して、前記解析木データを書き換える、
処理をコンピュータが実行することを特徴とするコンパイル方法。
【請求項９】
ソースコードをコンパイルするコンパイルプログラムであって、
前記ソースコードに基づいて生成される解析木データから、右辺値参照を宣言した第１関数が検出されず、且つ該第１関数とは異なる特定の第２関数が検出された場合、前記解析木データに前記右辺値参照が宣言された第１関数を示す構文木を追加して、前記解析木データを書き換える、
処理をコンピュータに実行させることを特徴とするコンパイルプログラム。
【発明の詳細な説明】
【技術分野】
【０００１】
本発明は、コンパイル装置、コンパイル方法およびコンパイルプログラムに関する。
【背景技術】
【０００２】
プログラミングを行う際に使用される言語の１つとして、オブジェクト指向言語がある。近年のオブジェクト指向言語では、右辺値参照と称される機能をソースコードに定義し、右辺値を明示的に取り扱うコーディングを行うことが許容されている。
【０００３】
関連する技術として、オブジェクト指向プログラムの規約を記述する規約情報を用いて入力プログラムを解析した結果をチェックし、規約に違反している部分を指摘し、かつ修正例を出力する技術が提案されている（例えば、特許文献１を参照）。
【先行技術文献】
【特許文献】
【０００４】
【特許文献１】特開平１１−０８５４９６号公報
【発明の概要】
【発明が解決しようとする課題】
【０００５】
右辺値参照を利用しないソースコードの場合、オブジェクトが生成されるごとに、メモリの所定領域が確保される。例えば、右辺値が一時変数であっても、該一時変数のためにメモリの領域が確保される。そして、一時変数は参照された後に使用されないため、メモリの領域が解放される。
【０００６】
例えば、ソースコードに所定の演算が定義され、且つ該演算が繰り返し行われる場合、一時変数のために、メモリの領域確保および領域解放の処理が頻繁に行われる。その結果、実行速度が低下する。
【０００７】
一方で、右辺値参照の機能を利用する場合、該右辺値参照を利用することを宣言した関数の定義をソースコードに記述することが要求される。このため、ソースコードの記述が煩雑になる。
【０００８】
１つの側面として、本発明は、右辺値参照の機能が利用されないソースコードの実行速度を簡単に向上させることを目的とする。
【課題を解決するための手段】
【０００９】
１つの態様では、コンパイル装置は、ソースコードをコンパイルするコンパイル装置であって、前記ソースコードに基づいて生成される解析木データから、右辺値参照を宣言した第１関数が検出されず、且つ該第１関数とは異なる特定の第２関数が検出された場合、前記解析木データに前記右辺値参照が宣言された関数を示す構文木を追加して、前記解析木データを書き換える書き換え部、を含む。
【発明の効果】
【００１０】
１つの側面によれば、右辺値参照の機能が利用されないソースコードの実行速度を簡単に向上させることができる。
【図面の簡単な説明】
【００１１】
【図１】コンパイル装置の一例を示す機能ブロック図である。
【図２】コードの一例を示す図（その１）である。
【図３】コードの一例を示す図（その２）である。
【図４】コピーおよびムーブの一例を説明する図である。
【図５】コードの一例を示す図（その３）である。
【図６】コードの一例を示す図（その４）である。
【図７】コードの一例を示す図（その５）である。
【図８】コードの一例を示す図（その６）である。
【図９】コンパイラが行う関数ごとの処理を示す表である。
【図１０】コードの一例を示す図（その７）である。
【図１１】コードの一例を示す図（その８）である。
【図１２】コードの一例を示す図（その９）である。
【図１３】構文木の一例を示す図（その１）である。
【図１４】構文木の一例を示す図（その２）である。
【図１５】実施形態の処理の流れの一例を示すフローチャート（その１）である。
【図１６】実施形態の処理の流れの一例を示すフローチャート（その２）である。
【図１７】構文木の一例を示す図（その３）である。
【図１８】コードの一例を示す図（その１０）である。
【図１９】構文木の一例を示す図（その４）である。
【図２０】実施形態の処理の流れの一例を示すフローチャート（その３）である。
【図２１】構文木の一例を示す図（その５）である。
【図２２】構文木の一例を示す図（その６）である。
【図２３】コードの一例を示す図（その１１）である。
【図２４】コンパイル装置のハードウェア構成の一例を示す図である。
【発明を実施するための形態】
【００１２】
＜実施形態のコンパイル装置の一例＞
以下、図面を参照して、実施形態について、説明する。図１は、コンパイル装置１の一例である。例えば、コンパイル装置１は、ソースコードをコンパイルし、オブジェクトコードを生成する計算機（コンピュータ）である。
【００１３】
コンパイル装置１は、字句解析部２と構文解析部３と意味解析部４と中間言語出力部５と最適化部６とオブジェクトコード生成部７とソースコード復元部８と記憶部９と画面部１０とを含む。
【００１４】
実施形態では、ソースコードは記憶部９に記憶されているものとする。字句解析部２は、記憶部９から取得されたソースコードに記述された字句を解析し、例えば、ソースコードをトークンに分割する。ソースコードは、ソースプログラムとも称される。
【００１５】
構文解析部３は、字句解析部２が解析した字句の間の関係性を解析し、解析された字句を所定の構文規則に基づいて、木構造にした構文木データを生成する。構文木は、解析木の一例である。
【００１６】
意味解析部４は、構文解析部３が生成した構文木の意味を解析する。実施形態の構文解析部３は、書き換え部４Ａを含む。書き換え部４Ａは、上記の解析木データに、右辺値参照を宣言した関数を示す構文木を追加する。
【００１７】
中間言語出力部５は、意味解析部４により意味解析された構文木データに基づいて、中間言語（中間コード）を出力する。最適化部６は、出力された中間言語を最適化する。オブジェクトコード生成部７は、最適化された中間言語に基づいて、オブジェクトコードを生成する。Central Processing Unit(CPU)は、生成されたオブジェクトコードを実行する。
【００１８】
ソースコード復元部８は、書き換え部４Ａにより書き換えられた構文木データからソースコードを復元する。記憶部９は、各種の情報を記憶する。例えば、記憶部９は、ソースコードや構文木データ等を記憶する。ソースコード出力部１０は、復元されたソースコードを出力する。
【００１９】
＜右辺値および左辺値の一例＞
図２は、所定のオブジェクト指向言語の仕様に基づいて記述されたソースコードの一例を示す。図２以降のソースコードの例も同様である。
【００２０】
図２のソースコードの例のうち、実体のあるオブジェクトや名前が付されているオブジェクトは左辺値と称される。一方、一時的に使用されるオブジェクトや名前が付されていないオブジェクトは右辺値と称される。例えば、一時的に使用される変数（一時変数）は、右辺値である。
【００２１】
＜各種関数の一例＞
図３の例は、６種類の関数の例を示す。コンストラクタは、オブジェクトを生成し、初期化する関数である。デストラクタは、オブジェクトを削除する関数である。コピーコンストラクタは、自身と同じクラス型のオブジェクトをコピーして生成する関数である。コピー代入演算子は、クラス型のオブジェクトの代入に用いられる関数である。
【００２２】
ムーブコンストラクタは、自身と同じクラス型のオブジェクトをムーブする関数である。ムーブ代入演算子は、右辺値参照を仮引数にとる代入演算子である。ムーブコンストラクタおよびムーブ代入演算子では、右辺値参照の機能を利用することが宣言される。
【００２３】
実施形態では、デストラクタ、コピーコンストラクタよびコピー代入演算子は、右辺値参照を宣言しない関数であり、第２関数の一例である。また、ムーブコンストラクタおよびムーブ代入演算子は、右辺値参照を宣言した関数であり、第１関数の一例である。
【００２４】
ムーブコンストラクタおよびムーブ代入演算子には右辺値参照の機能が用いられる。右辺値参照の機能の利用は「&&」を記述することにより宣言される。例えば、図３の例において、ムーブコンストラクタおよびムーブ代入演算子の「vector &&」は、ベクトル型の右辺値参照を宣言していることを示す。
【００２５】
また、代入演算子には「operator」が用いられる。例えば、図３の例のコピー代入演算子の「vector& operator=(const vector& x)」には「operator」が用いられる。同様に、ムーブ代入演算子の「vector& operator=(vector&& x)」にも「operator」が用いられる。
【００２６】
ここで、一時変数（右辺値）は将来的に破棄される変数である。また、該一時変数のために、メモリ（例えば、Random Access Memory(RAM)）の所定の領域が使用される。右辺値参照を宣言するムーブコンストラクタおよびムーブ代入演算子は、一時変数のために使用されるメモリの領域が再利用される。
【００２７】
以下、コピーコンストラクタおよびコピー代入演算子をコピー関数と称し、ムーブコンストラクタおよびムーブ代入演算子をムーブ関数と称することがある。
【００２８】
例えば、図４の例において、コピー関数の場合、メンバをコピーする際、メンバおよびコピーされたメンバのために、それぞれメモリの領域が使用される。
【００２９】
一方、ムーブ関数の場合、メンバをムーブする際、単にオブジェクトの所有権を新たなメンバに移動するだけのため、１つのメンバのためにメモリの領域が使用される。このため、メモリの使用量が少なくなる。
【００３０】
実施形態では、右辺値参照を宣言した関数として、ムーブコンストラクタおよびムーブ代入演算子について説明するが、右辺値参照は、関数の引数や代入演算子等に使用されてもよい。
【００３１】
＜加算演算のサンプルコードの一例＞
図５は、加算演算のサンプルコードの一例を示す。図５の例のサンプルコードにおいて、「a」、「b」、「c」および「ret」は２次元の配列（行列）として定義される。該サンプルコードでは、「norm<stop」の条件が満たされるまで、「ret=a+b+c」の加算演算が繰り返される。
【００３２】
図５の「サンプルコード」の例は、代入演算子を用いて実現される。図５の「代入演算子を用いたコード」の例は、代入演算子（operator）を用いたコードである。図５の「代入演算子を用いたコード」の例で示されるように、２次元の配列の加算演算が繰り返し行われている。
【００３３】
図６は、右辺値参照の利用が宣言された「operator」を追加したサンプルコードの一例を示す。図６の例のサンプルコードのうち（１）の部分は、図５の例の「代入演算子を用いたコード」と同じである。右辺値参照を利用したサンプルコードには、（２）の部分が追加されている。図６の例のソースコードのうち「Matrix&&」は、右辺値参照の利用が宣言されていることを示す。
【００３４】
右辺値参照を利用しない場合、図６の例のサンプルコードのうち、（１）の部分の記述だけでよい。この場合、配列「a+b」の加算演算が行われ、演算結果が一時変数（tmpとする）に格納される。この一時変数のために、メモリの領域が確保される。
【００３５】
次に、配列「tmp+c」の加算演算が行われる。これにより、「ret=a+b+c」の演算結果が得られる。従って、「ret=a+b+c」の加算演算が１回行われると、一時変数「tmp」のためのメモリ領域の確保と、演算結果「ret」のためのメモリ領域の確保との合計２回のメモリ領域の確保が行われる。
【００３６】
「ret=a+b+c」の加算演算は繰り返し行われる。このため、加算演算が繰り返されるごとに、一時変数「tmp」のために確保されたメモリ領域の解放と、演算結果「ret」のために確保されたメモリ領域の解放との合計２回のメモリ領域の解放が行われる。
【００３７】
従って、右辺値参照を利用しない場合、図６のサンプルコードの（１）が実行されると、２回のメモリ確保と２回のメモリ解放とが行われる。
【００３８】
上述したように、図６のサンプルコードのうち、（２）の部分は右辺値参照を利用した代入演算子である。この場合、図６のサンプルコードのうち、（１）の部分で、配列「a+b」の加算演算が行われ、演算結果が一時変数「tmp」として返される。
【００３９】
「tmp」は一時変数であるため、図６のサンプルコードのうち（２）の部分で定義されている「operator」により、「tmp+c」の加算演算が行われる。（２）の部分の「operator」は右辺値参照を利用した代入演算子であるため、一時変数の所有権だけがムーブされ、メモリの領域確保は行われない。これに伴い、メモリの領域の解放も行われない。
【００４０】
従って、図６の例のサンプルコードに（１）の部分と（２）の部分とが記述されることにより、「ret=a+b+c」の加算演算が１回行われるごとに、１回のメモリの領域確保が行われ、メモリの領域解放は行われない。
【００４１】
上述したように、右辺値参照を利用しないソースコードの場合、「ret=a+b+c」の加算演算が１回行われるごとに、メモリの領域確保が２回行われ、且つメモリの領域解放が２回行われる。一方、右辺値参照を利用したソースコードの場合、「ret=a+b+c」の加算演算が１回行われるごとに、メモリの領域確保が１回のみ行われる。
【００４２】
メモリの領域確保および確保されたメモリの領域解放には所定の時間がかかる。従って、右辺値参照を利用しない場合と比較して、右辺値参照を利用した場合の方が、メモリの領域確保およびメモリの領域解放が少なくなるため、実行時間が短縮化される。
【００４３】
例えば、High Performance Computing(HPC)の分野におけるアプリケーションプログラムでは、同じ演算が多数回繰り返して行われる。このため、右辺値参照が利用されることで、メモリの領域確保およびメモリの領域解放の回数が少なくなり、実行速度がより向上する。
【００４４】
従って、右辺値参照を利用した演算が行われることが好ましい。ただし、図６の例に示されるように、右辺値参照を利用した演算を行うソースコードを記述する場合、該ソースコードに右辺値参照を宣言した関数が追加される。つまり、（２）の部分が追加される。
【００４５】
図７は、右辺値参照を利用する場合における、ソースコードに記述される右辺値参照を宣言した関数の一例を示す。図７の例のソースコードにおける「Matrix operator+」は右辺値参照が宣言され、且つ２つの引数「a」および「b」という２つの引数が定義されている。
【００４６】
この場合、変数「a」を右辺値参照として宣言した「operator+」と、変数「b」を右辺値参照として宣言した「operator+」と、変数「a」および「b」を右辺値参照として宣言した関数との３通りの「operator+」がソースコードに記述される。
【００４７】
従って、右辺値参照を利用する場合、１つの「operator」について、４通りの「operator」がソースコードに記述される。このため、コーディングが煩雑になる。また、第三者が作成したソースコードを利用したコーディングが行われる場合、上記の４通りの「operator+」をソースコードに正しく記述することは難しい。
【００４８】
また、上述した例では、２次元配列の加算演算だけを示しているが、例えば、減算や積算、除算等が行われる場合もあり、四則演算のそれぞれについて、４通りの「operator」をソースコードに定義することは、コーディングの困難性をさらに増加させる要因となる。
【００４９】
また、例えば、「a=a+b」のように、加算結果を加算対象の変数に代入する場合、代入演算子（operator）の記述が異なる。この場合、代入演算子は「Matrix operator+=」という記述になる。従って、この場合についても、４通りの関数がソースコードに定義される。
【００５０】
＜右辺値参照を宣言した関数の自動生成の一例＞
図８は、２次元の配列同士の積和演算を行う関数「operator+」のサンプルコードの一例を示す。図８のうち、（１）の部分がコーディングされた部分（記述された部分）である。（２）の部分は、（１）の部分に基づく右辺値参照が定義された関数の一例である。
【００５１】
（１）の積和演算を行うコーディングされた「operator+」の記述内容は、（２）の右辺値参照を宣言した「operator+」の記述内容と類似している。従って、コンパイル装置１は、コーディングされた（１）の部分の一部を変換して、（２）の部分を自動生成することができる。
【００５２】
ただし、ソースコードで使用されるオブジェクト指向言語の規約によっては、コンパイル装置１は、右辺値参照を宣言した「operator+」を生成しない場合もある。ソースコードの１つのクラスの中に、以下の５つの定義がされている場合、コンパイル装置１は、右辺値参照を宣言した「operator+」を生成しない。
１：デストラクタが定義されている場合
２：コピーコンストラクタが定義されている場合
３：コピー代入演算子が定義されている場合
４：ムーブコンストラクタのみが定義されている場合
５：ムーブ代入演算子のみが定義されている場合
【００５３】
図９の例における網掛けが施された「なし」が上記の５つの場合である。「default」は、コンパイル装置１がコーディングされた部分に基づいて、特殊メンバ関数を自動的に生成する場合（暗黙的な宣言）である。この機能は、「defaulted function」とも称される。
【００５４】
実施形態における特殊メンバ関数は、コンストラクタ、デストラクタ、コピーコンストラクタ、コピー代入演算子、ムーブコンストラクタおよびムーブ代入演算子の６種類であるものとする。
【００５５】
図９の例における「ユーザが定義」は、ソースコードにコーディングされている関数を示す。「deleted」は削除される関数を示す。
【００５６】
例えば、ソースコードの１つのクラスにデストラクタが定義されている場合、コンパイル装置１は、ムーブコンストラクタおよびムーブ代入演算子を自動的に生成しない。これは、上述した言語の規約に基づく。
【００５７】
また、代入演算子が示す演算式の計算順序の依存関係によっては、演算結果に誤りを生じる場合がある。図８の例の演算式は、配列の加算演算を示す。この場合、配列の演算式の返り値である配列の添え字と配列の各引数の添え字との間に計算順序の依存関係があるかによって、演算結果に誤りを生じることがある。
【００５８】
例えば、書き換え部４Ａは、図８の「Vector operator*(Matrix& mat, Vector& vec)」の部分の一部を変換して、右辺値参照を宣言した関数を生成したとする。この場合、上記の部分は「Vector&& operator*(Matrix& mat, Vector&& vec)」に変換される。このうち、「&&」が右辺値参照を宣言していることを示す。
【００５９】
コンパイル装置１が、図８の（１）の部分の演算式に基づいて、（２）の部分の演算式を生成したとする。この場合、演算式の返り値の添え字と各引数の添え字とが全て一致していなければ、演算結果に誤りを生じる。加算演算以外の演算も同様である。
【００６０】
実施形態では、変換対象の変数の添え字と変換元の変数の添え字とが全て同じである場合、計算順序に依存関係がないとする。一方、変換対象の変数の添え字と変換元の変数の添え字とのうち一部でも異なる場合、計算順序に依存関係があるとする。
【００６１】
演算式の返り値（変換対象の値）の添え字と各引数（変換元の値）の添え字が全て一致する場合、計算順序の依存関係がないため、上述した変換が行われたとしても、演算結果に誤りを生じることはない。
【００６２】
一方、演算式の返り値の添え字と各引数の添え字とが１つでも異なっている場合、計算順序の依存関係があるため、上述した変換が行われると、演算結果に誤りを生じる。
【００６３】
図１０は、計算順序の依存関係がある演算式と計算順序の依存関係がない演算式との一例を示す。図１０の例において、「ベクトルの加算演算」の例では、演算式の返り値と各引数の値とは全て添え字が「i」で一致している。
【００６４】
このため、該演算式には計算順序の依存関係がなく、単純に変換がされたとしても、演算結果に誤りは生じない。この場合、コンパイル装置１は、単純に変換がされた関数を生成してもよい。
【００６５】
「行列ベクトル積」の例では、演算式の返り値の添え字は「i」であるが、引数のうち１つ「matrix」の添え字は「i」および「j」である。よって、この場合、返り値と引数とで添え字が異なる。
【００６６】
従って、該演算式には計算順序の依存関係があり、単純に変換がされると、演算結果に誤りを生じる。この場合、コンパイル装置１は、単純に変換がされた関数を生成しない。
【００６７】
「行列同士の加算演算」の例では、演算式の返り値の添え字は「i」および「j」であり、各引数の添え字も「i」および「j」である。よって、添え字は全て一致するため、計算順序の依存関係がない。この場合、単純に変換がされたとしても、演算結果に誤りが生じないため、コンパイル装置１は、単純に変換がされた関数を生成してもよい。
【００６８】
「行列と行例の積和演算」の例では、演算式の返り値の添え字は「i」および「j」であり、各引数の添え字は、「i」および「k」、または「k」および「j」である。返り値と引数とで添え字が異なる。
【００６９】
よって、この例では、計算順序の依存関係があるため、単純に変換がされると、演算結果に誤りが生じる。従って、コンパイル装置１は、単純に変換がされた関数を生成しない。
【００７０】
＜ムーブ関数の自動生成の一例＞
実施形態では、上述した言語の規約によって、右辺値参照を宣言した関数を生成することが制限される場合、「default」なムーブコンストラクタまたはムーブ代入演算子が生成されるようにする。
【００７１】
上述したように、構文解析部３が解析した構文木に基づいて、意味解析部４は、構文木の意味解析を行う。そこで、実施形態では、上述した制約がある場合、書き換え部４Ａは、「default」なムーブコンストラクタまたはムーブ代入演算子が生成されるように、構文木データを書き換える。
【００７２】
図１１のソースコードの例において、「~Matrix(){ }」が定義されている。このうち、「~」はデストラクタを宣言していることを示す。従って、上述した規約に基づいて、コンパイル装置１は、「default」なムーブコンストラクタまたはムーブ代入演算子を生成しない。
【００７３】
この場合、書き換え部４Ａは、「default」なムーブコンストラクタまたはムーブ代入演算子を示す構文木を構文木データに追加して、該構文木データを書き換える。これにより、「default」なムーブコンストラクタまたはムーブ代入演算子が生成される。
【００７４】
＜ムーブ自動生成の処理の一例＞
図１２は、２つのサンプルコード例（サンプルコード例１およびサンプルコード例）を示す。このうち、サンプルコード例１が、書き換え部４Ａの処理の対象となる。サンプルコード例２は、「default」なムーブコンストラクタとムーブ代入演算子とが定義されたソースコードの一例である。
【００７５】
サンプルコード例１には、「struct Matrix」というクラスと「Matrix operator+」というルーチンとが記述されている。コンパイル装置１の字句解析部２は、サンプルコード例１で示されるソースコードを字句解析する。
【００７６】
そして、構文解析部３が、字句解析の結果に基づいて、意味解析を行い、構文木データを生成する。図１３は、サンプルコード例１のうち、「struct Matrix」というクラスの構文木データである。
【００７７】
この構文木データには、「Member function」に、デストラクタを示す「~Matrix」が含まれている。従って、コンパイル装置１は、上述した規約に基づき、「default」なムーブ関数を生成しない。図１４は、サンプルコード例２（「default」なムーブコンストラクタとムーブ代入演算子とが定義されたソースコード）の構文木データの一例を示す。
【００７８】
次に、図１５のフローチャートを参照して、書き換え部４Ａの処理の流れの一例について説明する。書き換え部４Ａは、構文木データのチェックが終了したかを判定する（ステップＳ１）。チェックが終了した場合（ステップＳ１でＹＥＳ）、処理は終了する。
【００７９】
チェックが終了していない場合（ステップＳ１でＮＯ）、書き換え部４Ａは、サンプルコード例１を順番に解析し、構文木データがクラスを定義しているデータであるかを判定する（ステップＳ２）。
【００８０】
書き換え部４Ａは、構文木データの最初の型「Type」に基づいて、ステップＳ２の判定を行う。ステップＳ２の判定がＮＯの場合、処理はステップＳ１に戻る。
【００８１】
ステップＳ２でＹＥＳの場合、書き換え部４Ａは、特殊メンバ関数とムーブコンストラクタとムーブ代入演算子との値に「ＦＡＬＳＥ」を設定する（ステップＳ３）。各値は、特殊メンバ関数とムーブコンストラクタとムーブ代入演算子とのそれぞれについて、定義されたクラスに含まれているかを示す。
【００８２】
書き換え部４Ａは、クラスの中の構文木データをチェックしていく（ステップＳ４）。書き換え部４Ａは、クラス中にデストラクタ、コピーコンストラクタまたはコピー代入演算子が検出されたかを判定する（ステップＳ５）。
【００８３】
ステップＳ５でＹＥＳの場合、クラス中からデストラクタ、コピーコンストラクタまたはコピー代入演算子の何れかが検出される。この場合、書き換え部４Ａは特殊メンバ関数の値を「ＴＲＵＥ」に設定する（ステップＳ６）。そして、処理は、ステップＳ４に戻る。
【００８４】
ステップＳ５でＮＯの場合、書き換え部４Ａは、クラス中にムーブコンストラクタがあるかを判定する（ステップＳ７）。ステップＳ７でＹＥＳの場合、書き換え部４Ａは、ムーブコンストラクタの値を「ＴＲＵＥ」に設定する（ステップＳ８）。そして、処理は、ステップＳ４に戻る。
【００８５】
ステップＳ７でＮＯの場合、書き換え部４Ａは、クラス中にムーブ代入演算子があるかを判定する（ステップＳ９）。ステップＳ８でＹＥＳの場合、書き換え部４Ａは、ムーブ代入演算子の値を「ＴＲＵＥ」に設定する（ステップＳ１０）。そして、処理は、ステップＳ４に戻る。
【００８６】
書き換え部４Ａは、クラスの定義が終了したかを判定する（ステップＳ１１）。ステップＳ１１でＮＯの場合、処理はステップＳ４に戻る。ステップＳ１１でＹＥＳの場合、処理は「Ａ」に進む。
【００８７】
ここで、図１３に例示した構文木データに基づいて、書き換え部４Ａが行う処理について説明する。構文木データの最初の「TYPE」は「Matrix」を示す。つまり、構文木データは、「Matrix」型のクラス定義がされていることを示す。従って、ステップＳ２はＹＥＳになる。
【００８８】
ステップＳ４において、書き換え部４Ａは、構文木データの「Member function」および「Variable」をチェックしていく。最初の「Member function」はコンストラクタを示す。従って、ステップＳ５、ステップＳ７およびステップＳ９の判定は全てＮＯになる。
【００８９】
構文木データが示すクラスにおいて、「ptr_」の「Variable」がクラスの定義が終了することを示す。このため、書き換え部４Ａが、「Member function」が「Matrix」をチェックしている時点では、ステップＳ１１の判定はＮＯになる。
【００９０】
次に、書き換え部４Ａは、構文木データのうち「~Matrix」が、ステップＳ５、ステップＳ７またはステップＳ９でＹＥＳになるかを判定する。「~Matrix」はデストラクタを宣言しているため、ステップＳ５の判定はＹＥＳになる。よって、特殊メンバ関数の値は「ＴＲＵＥ」に変更される。
【００９１】
次に、書き換え部４Ａは、構文木データのうち「size」が、ステップＳ５、ステップＳ７またはステップＳ９でＹＥＳになるかを判定する。「size」は変数を示すため、ステップＳ５、ステップＳ７およびステップＳ９の判定は全てＮＯになる。
【００９２】
次に、書き換え部４Ａは、構文木データのうち「ptr_」が、ステップＳ５、ステップＳ７またはステップＳ９でＹＥＳになるかを判定する。「ptr_」は変数を示すため、ステップＳ５、ステップＳ７およびステップＳ９の判定は全てＮＯになる。
【００９３】
「ptr_」でクラスの定義は終了するため、ステップＳ１１の判定はＹＥＳになる。従って、上述したように、特殊メンバ関数の値は「ＴＲＵＥ」に変更され、ムーブコンストラクタおよびムーブ代入演算子の値は「ＦＡＬＳＥ」から変更されない。
【００９４】
次に、「Ａ」以降の処理について、図１６を参照して説明する。書き換え部４Ａは、特殊メンバ関数とムーブコンストラクタとムーブ代入演算子とのうち何れかの値が「ＴＲＵＥ」であるかを判定する（ステップＳ１２）。
【００９５】
ステップＳ１２でＹＥＳの場合、書き換え部４Ａは、ムーブコンストラクタの値が「ＦＡＬＳＥ」であるかを判定する（ステップＳ１３）。
【００９６】
ステップＳ１３でＹＥＳの場合、書き換え部４Ａは、「default」なムーブコンストラクタの定義を示す構文木を構文木データに追加する（ステップＳ１４）。ステップＳ１３でＮＯの場合、ステップＳ１４の処理は行われない。
【００９７】
書き換え部４Ａは、ムーブ代入演算子の値が「ＦＡＬＳＥ」であるかを判定する（ステップＳ１５）。ステップＳ１５でＹＥＳの場合、書き換え部４Ａは、「default」なムーブ代入演算子の定義を示す構文木を構文木データに追加する（ステップＳ１６）。ステップＳ１５でＮＯの場合、ステップＳ１６の処理は行われない。
【００９８】
図１３の構文木データの場合、特殊メンバ関数の値は「ＴＲＵＥ」であり、ムーブコンストラクタおよびムーブ代入演算子の値は「ＦＡＬＳＥ」である。よって、ステップＳ１２の判定はＹＥＳになる。
【００９９】
ムーブコンストラクタの値は「ＦＡＬＳＥ」であるため、ステップＳ１４の処理が行われる。ムーブ代入演算子の値は「ＦＡＬＳＥ」であるため、ステップＳ１６の処理が行われる。
【０１００】
ムーブコンストラクタの値が「ＦＡＬＳＥ」の場合、ムーブコンストラクタの定義がクラスの中に記述されていないことになる。この場合、書き換え部４Ａは、ムーブコンストラクタがクラスの中で定義されるように、構文木データに「default」なムーブコンストラクタの定義を追加して、該構文木データを書き換える。
【０１０１】
一方、ムーブコンストラクタの値が「ＴＲＵＥ」の場合、ムーブコンストラクタの定義がクラスの中に記述されていることになる。この場合、書き換え部４Ａは、構文木データに「default」なムーブコンストラクタの定義を追加しない。
【０１０２】
図１６に示されるように、ムーブ代入演算子についての処理もムーブコンストラクタの処理と同様である。ステップＳ１２の判定でＮＯの場合、ステップＳ１５の判定でＹＥＳの場合、またはステップＳ１６の処理が行われた後、処理は「Ｂ」から図１５のステップＳ１に戻る。
【０１０３】
図１７は、書き換え部４Ａが図１３の構文木データを書き換えた後の構文木データの一例を示す。図１７の構文木データには、ムーブコンストラクタおよびムーブ代入演算子が追加されている。
【０１０４】
図１７の構文木データのうち点線で囲まれた部分は、書き換え部４Ａが構文木データに追加した構文木（「default」なムーブコンストラクタを示す構文木および「default」なムーブ代入演算子を示す構文木）を示す。追加された構文木は、図１４で示した構文木と同じである。
【０１０５】
図１８は、書き換え部４Ａにより書き換えられた構文木データに基づくサンプルコード例１を示す。実施形態では、ソースコード復元部８が、書き換えられた構文木データに基づいて、サンプルコード例１を復元する。
【０１０６】
サンプルコード例１のうち、点線で囲まれた部分が、書き換え部４Ａにより追加されたムーブコンストラクタおよびムーブ代入演算子を示す。
【０１０７】
＜operator自動生成の処理の一例＞
図１９は、サンプルコード例１のうち、「operator+」の構文木を示す。書き換え部４Ａは、「operator+」の構文木に基づいて、該構文木データを、右辺値参照を利用した構文木データに書き換える。
【０１０８】
図２０は、書き換え部４Ａの処理の流れの一例を示すフローチャートである。書き換え部４Ａは、構文木データから、「operator」の構文木を抽出する（ステップＳ２０）。
【０１０９】
書き換え部４Ａは、構文木データのチェックが全て終了したかを判定する（ステップＳ２１）。ステップＳ２でＹＥＳの場合、処理は終了する。ステップＳ２でＮＯの場合、「operator」の構文木は左辺値を戻す「operator」であるかを判定する（ステップＳ２２）。
【０１１０】
ステップＳ２２でＮＯの場合、「operator」の構文木は左辺値を戻す「operator」ではない。この場合、書き換え部４Ａは構文木データを書き換えない。そして、処理はステップＳ２０に戻る。
【０１１１】
ステップＳ２２でＹＥＳの場合、書き換え部４Ａは、「operator」の引数の何れかの型と返り値の型とが一致するかを判定する（ステップＳ２３）。ステップＳ２３でＮＯの場合、「operator」の引数の何れの型も返り値の型と一致しない。
【０１１２】
この場合、戻り値と型が一致する引数がないため、書き換え部４Ａが右辺値参照を利用した構文木データに書き換える処理は煩雑になる。よって、ステップＳ２３でＮＯの場合、処理はステップＳ２０に戻る。
【０１１３】
書き換え部４Ａは、「operator」の構文木を探索し、演算式の返り値の変数の添え字と各引数の変数の添え字とが全て同じであるかを判定する（ステップＳ２４）。ステップＳ２４でＮＯの場合、計算順序の依存関係があることになる。
【０１１４】
このため、「operator+」の構文木に基づいて、該構文木データを、右辺値参照を利用した構文木データに書き換えると、書き換えられた構文木に基づく演算式の演算結果に誤りを生じることになる。
【０１１５】
よって、ステップＳ２４でＮＯの場合、書き換え部Ａは、構文木データを、右辺値参照を利用した構文木データに書き換えない。このため、処理はステップＳ２０に戻る。
【０１１６】
一方、ステップＳ２４でＹＥＳの場合、演算式の返り値の変数の添え字と各引数の変数の添え字とが全て同じである。この場合、「operator+」の構文木に基づいて、該構文木データが、上述した右辺値参照を利用した構文木データに書き換えされたとしても、「operator+」で規定される演算式の演算結果に誤りは生じない。
【０１１７】
この場合、書き換え部４Ａは、構文木データを探索し、右辺値参照を宣言した「operator」の構文木があるかを判定する（ステップＳ２５）。
【０１１８】
ステップＳ２５でＹＥＳの場合、構文木データにある右辺値参照を宣言した「operator」の構文木を使用すればよいため、上述した右辺値参照を利用した構文木データへの書き換えは行われない。このため、処理はステップＳ２０に戻る。
【０１１９】
ステップＳ２５でＮＯの場合、書き換え部４Ａは、「operator」の構文木に基づいて、変数を変換して、右辺値参照を利用した「operator」を示す構文木を構文木データに追加して、該構文木データを書き換える（ステップＳ２６）。
【０１２０】
また、書き換え部４Ａは、追加された「operator」の２つの引数を入れ替えた場合の「operator」を示す構文木、および２つの引数の両者に右辺値参照を定義した「operator」を示す構文木を構文木データに追加する。これにより、構文木データは書き換えられる（ステップＳ２７）。
【０１２１】
図１９の構文木の例の場合、「operator+」の左辺値は「Matrix」型であり、返り値「ret」も「Matrix」型である。よって、ステップＳ２２の判定はＹＥＳになる。
【０１２２】
「operator+」の構文木は、返り値が「Matrix」型であり、引数「a」および「b」の両者の変数は「Matrix」型であることを示している。よって、ステップＳ２３の判定はＹＥＳになる。
【０１２３】
「operator+」の構文木は、配列「ret」、「a」および「ｂ」の全ての変数の添え字が「i」および「j」であることを示している。よって、ステップＳ２４の判定はＹＥＳになる。
【０１２４】
構文木データには、右辺値参照を宣言した「operator」は定義されていない。よって、ステップＳ２５の判定はＮＯになる。従って、ステップＳ２６およびステップＳ２７の処理が行われる。
【０１２５】
図２１は、図１９で示した「operator+」の構文木、およびステップＳ２６の処理で追加される構文木の一例を示す。書き換え部４Ａは、「operator+」の構文木の一部を変換して、ステップＳ２６の処理で追加される構文木を生成する。
【０１２６】
図２１の例では、書き換え部４Ａは、「operator+」に対応する「Type」を「Matrix&&」に変換している。また、書き換え部４Ａは、「parameter」の変数が「a」に対応する「Matrix&」を「Matrix&&」に変換している。これにより、「operator+」の変数「a」は右辺値参照を利用することが宣言される。
【０１２７】
また、書き換え部４Ａは、「statement」の「operator」を「std::move(a)」に変換する。これにより、ムーブ代入演算子によるムーブが実現される。書き換え部４Ａは、以上の変換を行うことで、ステップＳ２６の処理で追加される構文木を生成する。
【０１２８】
図２２は、図１９で示した「operator+」の構文木、およびステップＳ２７の処理で追加される構文木の例を示す。書き換え部４Ａは、「operator+」の構文木の一部を変換して、ステップＳ２７の処理で追加される構文木を生成する。
【０１２９】
書き換え部４Ａは、「operator+」に対応する「Type」を「Matrix&&」に変換する。また、書き換え部４Ａは、「parameter」の変数が「b」に対応する「Matrix&」を「Matrix&&」に変換している。これにより、「operator+」の変数「b」は右辺値参照を利用することが宣言される。
【０１３０】
図２２の例の場合、「statement」が「for」の構文は削除されている。書き換え部４Ａは、「statement」が「return」に対応する「operator」を「operator+(b,a)」に変換する。以上により、「operator」の２つの引数のうち右辺値参照の利用を宣言する引数が入れ替えられる。
【０１３１】
また、図２２の例のうち、もう１つの追加される構文木は、「operator」の引数である「a」および「b」の両者が右辺値参照を利用することを示す。このため、書き換え部４Ａは、「operator+」に対応する「Type」を「Matrix&&」に変換する。
【０１３２】
また、書き換え部４Ａは、「parameter」の変数が「a」および「b」に対応する「Matrix&」を「Matrix&&」に変換している。これにより、「operator+」の変数「a」および「b」は右辺値参照を利用することが宣言される。
【０１３３】
そして、書き換え部４Ａは、「statement」が「return」に対応する「operator」を「operator+(a,b)」に変換する。以上により、「operator」の２つの引数の両者が右辺値参照を利用することが宣言される。
【０１３４】
書き換え部４Ａは、以上の構文木を構文木データに追加し、該構文木データを書き換える。図２３は、書き換え部４Ａにより、書き換えられた構文木データを復元したソースコードの一例を示す。ソースコードの復元は、ソースコード復元部８が行う。
【０１３５】
図２３のサンプルコード例１のうち、点線で囲まれた部分は、ムーブ自動生成の処理により追加された部分を示す。一点鎖線で囲まれた部分は、operator自動生成の処理により追加された部分を示す。
【０１３６】
中間言語出力部５は、書き換えられた構文木データに基づいて、中間言語を生成および出力する。最適化部６は、出力された中間言語を最適化する。オブジェクトコード生成部７は、最適化された中間言語からオブジェクトコードを生成する。
【０１３７】
従って、実施形態では、書き換え部４Ａが、構文木データに右辺値参照を宣言した関数を示す構文木を追加し、該構文木データを書き換えるため、メモリの領域確保および領域解放の回数を抑制することができる。これにより、実行速度が向上する。
【０１３８】
構文木データに上述した各種の構文木を追加する処理は、書き換え部４Ａが行う。よって、構文木データに右辺値参照を宣言した関数を示す構文木が自動的に追加される。従って、右辺値参照の機能が利用されないソースコードの実行速度を簡単に向上させることができる。
【０１３９】
上述した言語の規約により、右辺値参照を宣言した「default」なムーブ関数が生成されない場合がある。この場合でも、書き換え部４Ａが構文木データを書き換えることで、該構文木データに上述した「default」なムーブ関数が追加される。これにより、オブジェクトコードの実行速度が向上する。
【０１４０】
また、「default」なムーブ関数が追加されたとしても、配列の演算式の返り値の添え字と各引数の添え字とが異なる場合、計算順序に依存関係が生じることから、演算結果に誤りを生じる。
【０１４１】
実施形態では、書き換え部４Ａは、配列の演算式の返り値の添え字と各引数の添え字とが全て同じ場合に、operator自動生成処理を行う。このため、計算順序の依存関係に起因する演算結果の誤りが生じることを回避できる。
【０１４２】
また、書き換え部４Ａが書き換えた構文木データは、コンパイラを実行するアーキテクチャに依存しない。また、ソースコード復元部８は、書き換え部４Ａが書き換えを行ったソースコードを復元する。従って、該ソースコードは他のコンパイラでも利用することができる。
【０１４３】
ここで、ソースコード出力部１０は、ソースコード復元部８が、書き換えられた構文木データから復元したソースコードを、例えば、ディスプレイ等に出力してもよい。例えば、復元したソースコードがディスプレイに表示されることで、元のソースコードがどのように書き換えられたかが提示される。これにより、ソースコードレベルでの性能向上の方策を提示することができる。
【０１４４】
＜コンパイル装置のハードウェア構成の一例＞
次に、図２４の例を参照して、コンパイル装置１のハードウェア構成の一例を説明する。図２４の例に示すように、バス１００に対して、プロセッサ１１１とＲＡＭ１１２とＲＯＭ１１３と補助記憶装置１１４と媒体接続部１１５とディスプレイ１１６とが接続されている。
【０１４５】
プロセッサ１１１は任意の処理回路であり、上述したＣＰＵである。プロセッサ１１１はＲＡＭ１１２に展開されたプログラムを実行する。実行されるプログラムとしては、実施形態の処理を行うプログラムを適用してもよい。ＲＯＭ１１３はＲＡＭ１１２に展開されるプログラムを記憶する不揮発性の記憶装置である。
【０１４６】
補助記憶装置１１４は、種々の情報を記憶する記憶装置であり、例えばハードディスクドライブや半導体メモリ等を補助記憶装置１１４に適用してもよい。媒体接続部１１５は、可搬型記録媒体１１９と接続可能に設けられている。
【０１４７】
可搬型記録媒体１１９としては、可搬型のメモリや光学式ディスク（例えば、Compact Disc(CD)やDigital Versatile Disc(DVD)等）、半導体メモリ等を適用してもよい。この可搬型記録媒体１１９に実施形態の処理を行うプログラムが記録されていてもよい。
【０１４８】
コンパイル装置１のうち、記憶部９は、ＲＡＭ１１２や補助記憶装置１１４等により実現されてもよい。コンパイル装置１のうち、記憶部９以外の各部は、与えられたコンパイルプログラムをプロセッサ１１１が実行することにより実現されてもよい。
【０１４９】
ＲＡＭ１１２、ＲＯＭ１１３、補助記憶装置１１４および可搬型記録媒体１１９は、何れもコンピュータ読み取り可能な有形の記憶媒体の一例である。これらの有形な記憶媒体は、信号搬送波のような一時的な媒体ではない。
【０１５０】
＜その他＞
本実施形態は、以上に述べた実施の形態に限定されるものではなく、本実施形態の要旨を逸脱しない範囲内で種々の構成または実施形態を取ることができる。以上の実施形態に関して、さらに以下の付記を開示する。
（付記１）
ソースコードをコンパイルするコンパイル装置であって、
前記ソースコードに基づいて生成される解析木データから、右辺値参照を宣言した第１関数が検出されず、且つ該第１関数とは異なる特定の第２関数が検出された場合、前記解析木データに前記右辺値参照が宣言された第１関数を示す構文木を追加して、前記解析木データを書き換える書き換え部、
を備えることを特徴とするコンパイル装置。
（付記２）
前記ソースコードに前記第２関数が記述されている場合、前記書き換え部は、該第２関数の記述の一部を変換して、前記右辺値参照が宣言された第１関数を示す構文木を生成し、生成された構文木を前記解析木データに追加する、
ことを特徴とする付記１記載のコンパイル装置。
（付記３）
前記第１関数は、ムーブコンストラクタまたはムーブ代入演算子であり、
前記第２関数は、デストラクタ、コピーコンストラクタまたはコピー代入演算子であること、
を特徴とする付記１記載のコンパイル装置。
（付記４）
前記書き換え部は、デフォルト化された前記ムーブコンストラクタまたは前記代入演算子を示す構文木を前記解析木データに追加する、
ことを特徴とする付記３記載のコンパイル装置。
（付記５）
前記第１関数の引数が複数ある場合、前記書き換え部は、該引数ごとに前記右辺値参照の宣言を付与した前記関数を示す解析木、および全ての引数に前記右辺値参照の宣言を付与した前記関数を示す解析木を前記解析木データに追加する、
ことを特徴とする付記１記載のコンパイル装置。
（付記６）
前記書き換え部は、前記ムーブコンストラクタと前記ムーブ代入演算子とのうち何れか一方を示す解析木が検出され、且つ他方を示す解析木が検出されない場合、検出された解析木を前記解析木データに追加しない、
ことを特徴とする付記３記載のコンパイル装置。
（付記７）
前記ムーブ代入演算子を用いた演算式が１次元以上の配列の演算であり、且つ前記演算式の返り値添え字と複数の引数のそれぞれの添え字とが全て同じである場合にのみ、前記書き換え部は、前記変換された関数を示す解析木を前記解析木データに追加する、
ことを特徴とする付記３記載のコンパイル装置。
（付記８）
前記書き換え部が書き換えた解析木データを復元したソースコードを出力する出力部、
を備えることを特徴とする付記１記載のコンパイル装置。
（付記９）
ソースコードをコンパイルするコンパイル方法であって、
前記ソースコードに基づいて生成される解析木データから、右辺値参照を宣言した第１関数が検出されず、且つ該第１関数とは異なる特定の第２関数が検出された場合、前記解析木データに前記右辺値参照が宣言された第１関数を示す構文木を追加して、前記解析木データを書き換える、
処理をコンピュータが実行することを特徴とするコンパイル方法。
（付記１０）
ソースコードをコンパイルするコンパイルプログラムであって、
前記ソースコードに基づいて生成される解析木データから、右辺値参照を宣言した第１関数が検出されず、且つ該第１関数とは異なる特定の第２関数が検出された場合、前記解析木データに前記右辺値参照が宣言された第１関数を示す構文木を追加して、前記解析木データを書き換える、
処理をコンピュータに実行させることを特徴とするコンパイルプログラム。
【符号の説明】
【０１５１】
１    コンパイル装置
２    字句解析部
３    構文解析部
４    意味解析部
４Ａ    書き換え部
５    中間言語出力部
６    最適化部
７    オブジェクトコード生成部
８    ソースコード復元部
９    記憶部
１０    ソースコード出力部
１１１    プロセッサ
１１２    ＲＡＭ
１１３    ＲＯＭ
１１６    ディスプレイ
【図１】
2017151903.tif 000003
【図２】
2017151903.tif 000004
【図３】
2017151903.tif 000005
【図４】
2017151903.tif 000006
【図５】
2017151903.tif 000007
【図６】
2017151903.tif 000008
【図７】
2017151903.tif 000009
【図８】
2017151903.tif 000010
【図９】
2017151903.tif 000011
【図１０】
2017151903.tif 000012
【図１１】
2017151903.tif 000013
【図１２】
2017151903.tif 000014
【図１３】
2017151903.tif 000015
【図１４】
2017151903.tif 000016
【図１５】
2017151903.tif 000017
【図１６】
2017151903.tif 000018
【図１７】
2017151903.tif 000019
【図１８】
2017151903.tif 000020
【図１９】
2017151903.tif 000021
【図２０】
2017151903.tif 000022
【図２１】
2017151903.tif 000023
【図２２】
2017151903.tif 000024
【図２３】
2017151903.tif 000025
【図２４】
2017151903.tif 000026