前述のように、C言語等の高級言語と比較して、アセンブリ言語でのコーディングには記述ミスが発生し易い。コーディング時の様々な場面で記述ミスは発生し得るが、特に命令のオペランドとしてレジスタを指定するときにミスを犯し易い。そこで、最初にプロセッサに設けられたレジスタについて説明する。
図1(a)は、プロセッサに設けられた汎用レジスタxn(n=0,1,2,…, 31)の模式図である。
汎用レジスタxn(n=0,1,2,…, 31)は、特定の用途に限定されないレジスタであって、例えばデータ、アドレス、及び即値等を格納するレジスタである。ここでは、インデックスn(=0,1,2,…, 31)で複数の汎用レジスタxnの各々を識別する。
一方、図1(b)は、プロセッサに設けられたベクトルレジスタvn(n=0,1,2,…, 31)の模式図である。
ベクトルレジスタvn(n=0,1,2,…, 31)は、SIMD(Single Instruction Multiple Data)レジスタであって、ベクトル演算の対象となるベクトルデータを格納するためのレジスタである。汎用レジスタxn(n=0,1,2,…, 31)と同様に、本実施形態ではインデックスn(=0,1,2,…, 31)で複数のベクトルレジスタvn(n=0,1,2,…, 31)の各々を識別する。また、各々のベクトルレジスタvn(n=0,1,2,…, 31)のサイズは、例えば128ビットである。
図2は、ベクトルレジスタを指定可能な命令セットであるARM社のARMv8-Aアーキテクチャに基づくプロセッサにおいて、128ビット長のベクトルレジスタvn(n=0,1,2,…, 31)を指定するアセンブリ言語の書式を示す模式図である。
図2に示すように、アセンブリ言語においてベクトルレジスタvn(n=0,1,2,…31)を指定するには、「vn.x」、「vn.d」、「vn.s」、「vn.h」、「vn.b」という書式が採用される。
この書式において、「vn」は、インデックスが「n」のベクトルレジスタvnを指定する書式である。そして、ドット「.」の後に続く「x」、「d」、「s」、「h」、「b」は、一つのベクトルレジスタvnに格納されたベクトルデータの要素のサイズを示す書式である。例えば、「x」は要素のサイズが128ビットであることを示し、「d」は要素のサイズがダブルワード(64ビット)であることを示す。
同様に、「s」、「h」、「b」は、要素のサイズがそれぞれシングルワード(32ビット)、ハーフワード(16ビット)、バイト(8ビット)であることを示す。
このように、この書式では、「vn」と「d」とをドット「.」で連結した「vn.d」等の文字列により、ベクトルレジスタvnとその要素のサイズとを指定する。
次に、ベクトルレジスタを用いたアセンブリ言語の文法について説明する。
図3~図5は、ベクトルレジスタを用いたアセンブリ言語の文法について示す模式図である。
このうち、図3(a)は、vadd命令の文法について示す模式図である。図3(a)のvadd命令は、二つのベクトルレジスタv0、v1の各々の対応する要素同士を加算し、その結果をベクトルレジスタv2の対応する要素に格納する符号あり整数の加算命令である。このように、vadd命令は、各ベクトルレジスタの要素ごとの加算命令であるため、各ベクトルレジスタv0、v1、v2の各々の要素のサイズが同一であることを前提とする。
そのため、開発者は、vadd命令のオペランドにおける全てのベクトルレジスタの要素のサイズを例えば「b」で同一にする。この場合、vadd命令は、各ベクトルレジスタの要素に格納されている8ビットの符号あり整数を加算する命令となる。
図3(b)は、vadd命令の文法の他の例について示す模式図である。
図3(b)の例では、vadd命令のオペランドに指定された各ベクトルレジスタの要素のサイズを「h」としている。この場合、vadd命令は、各ベクトルレジスタの要素に格納されている16ビットの符号あり整数を加算する命令となる。
図4(a)は、vfadd命令の文法について示す模式図である。
図4(a)のvfadd命令は、二つのベクトルレジスタv0、v1の各々の対応する要素同士を加算し、その結果をベクトルレジスタv2の対応する要素に格納する浮動小数の加算命令である。前述のvadd命令と同様に、vfadd命令も各ベクトルレジスタの要素ごとの加算命令であるため、各ベクトルレジスタv0、v1、v2の各々の要素のサイズが同一であることを前提とした命令である。
よって、開発者は、vfadd命令のオペランドにおける全てのベクトルレジスタの要素のサイズを例えば「s」で同一にする。この場合は、vfadd命令は、各ベクトルレジスタの要素に格納されている32ビットの浮動小数を加算する命令となる。
図4(b)は、vfadd命令の文法の他の例について示す模式図である。
図4(b)の例では、vfadd命令のオペランドに指定された各ベクトルレジスタの要素のサイズを「d」としている。この場合、vfadd命令は、各ベクトルレジスタの要素に格納されている64ビットの浮動小数を加算する命令となる。
図5は、multiply命令の文法について示す模式図である。
図5のmultiply命令は、二つのベクトルレジスタv0、v1の各々の対応する要素同士を乗算し、その結果をベクトルレジスタv2の各々の要素に格納する整数の乗算命令である。multiply命令は、このように要素ごとの乗算命令であるため、各ベクトルレジスタv0、v1、v2の各々の要素のサイズが同一であることを前提とした命令である。この例では、各ベクトルレジスタv0、v1、v2の各々の要素のサイズを「h」で同一にしている。
更に、multiply命令は、整数と浮動小数点のようにデータタイプが異なるデータ同士の積をサポートしていない。そのため、multiply命令は、ソースレジスタのベクトルレジスタv0、v1の各々に同一のデータタイプのデータが書き込まれていることを前提とする命令である。
次に、汎用レジスタを用いたアセンブリ言語の文法について説明する。
図6(a)、(b)は、汎用レジスタを用いたアセンブリ言語の文法について示す模式図である。
このうち、図6(a)は、add命令の文法について示す模式図である。add命令は、汎用レジスタを対象とした符号あり64ビット整数の加算命令である。図6(a)の例では、二つの汎用レジスタx0、x1の各々に格納されているデータが加算され、その結果が汎用レジスタx2に書き込まれる。
ベクトルレジスタとは異なり、汎用レジスタにおいては要素同士の演算という考え方はない。そのため、add命令のオペランドにおいては、ドット「.」で要素のサイズを指定する必要はない。
また、図6(b)は、fadd命令の文法について示す模式図である。
fadd命令は、汎用レジスタを対象とした64ビットの浮動小数の加算命令である。図6(b)の例では、二つの汎用レジスタx0、x1の各々に格納されているデータが加算され、その結果が汎用レジスタx2に書き込まれる。
add命令と同様に、fadd命令のオペランドにおいても、ドット「.」で要素のサイズを指定する必要はない。
次に、アセンブリ言語でのコーディング例について説明する。
図7は、開発者が記述したアセンブリ言語のソースコードの正しいコーディング例を示す模式図である。
アセンブリ言語のソースコードにおいては、命令のオペランドとして汎用レジスタxn(n=0,1,2,…, 31)、ベクトルレジスタvn(n=0,1,2,…, 31)、及びアドレス等の即値が指定される。
例えば、コードT1においては、load命令のオペランドとして「x0」と「アドレス1」が指定される。このコードT1は、即値として指定された「アドレス1」のメモリから64ビットのデータを読み出し、それを汎用レジスタx0に書き込むコードである。
また、コードT2においては、vadd命令のオペランドとして「v2.b」、「v0.b」、及び「v1.b」が指定される。このコードT2は、ベクトルレジスタv0とベクトルレジスタv1の各々の要素に格納されているデータを読み出し、それらを8ビットの符号あり整数として加算して結果をベクトルレジスタv2に書き込む命令である。
以下では、コードT2におけるベクトルレジスタv0、v1のようにデータの読み出し元のレジスタをソース(src)レジスタとも呼ぶ。また、コードT1の汎用レジスタx0やコードT2のベクトルレジスタv2のようにデータの書き込み先のレジスタをデスティネーション(dst)レジスタとも呼ぶ。
次に、開発者が記述したアセンブリ言語のソースコードの記述ミスの例について説明する。その記述ミスのタイプとしては、以下の第1~第5例がある。
・第1例
図8は、第1例に係る記述ミスについて説明するためのアセンブリ言語のソースコードを示す模式図である。
図8の例では、コードT3において、符号あり64ビット整数の加算結果がデスティネーションレジスタである汎用レジスタx2に書き込まれている。そして、コードT4においては、その汎用レジスタx2がfadd命令のソースレジスタに指定されている。
fadd命令が演算の対象とするデータタイプは浮動小数であるから、fadd命令のソースレジスタである汎用レジスタx2には浮動小数が書き込まれていなければならない。しかし、この例では、コードT3において汎用レジスタx2に符号あり整数が書き込まれてしまっているため、このコーディングは誤りである。
コードT3、T4ではオペランドに汎用レジスタが指定されているが、ベクトルレジスタをオペランドに指定した場合にも同様の誤りは生じ得る。
図9は、ベクトルレジスタをオペランドに指定したときの第1例に係る記述ミスの例について説明するためのアセンブリ言語のソースコードを示す模式図である。
図9の例では、コードT5において、符号あり32ビット整数の加算結果がデスティネーションレジスタであるベクトルレジスタv2に書き込まれている。そして、コードT6においては、そのベクトルレジスタv2がvfadd命令のソースレジスタに指定されている。
fadd命令と同様に、vfadd命令も浮動小数同士を加算する命令であるから、そのソースレジスタであるベクトルレジスタv2には浮動小数が書き込まれていなければならない。しかし、この例では、コードT5においてベクトルレジスタv2に整数が書き込まれてしまっているため、このコーディングは誤りである。
図8や図9のように、命令が演算の対象とするデータタイプと、実際にソースレジスタに書き込まれているデータのデータタイプとが一致しないコーディングは誤りである。
・第2例
図10は、第2例に係るミスについて説明するためのアセンブリ言語のソースコードを示す模式図である。
図10の例では、コードT7のvadd命令において、デスティネーションレジスタのデータサイズとしてシングルワード「s」が指定されている。よって、コードT7のvadd命令を実行すると、データサイズがシングルワード「s」の要素を有するベクトルデータがベクトルレジスタv2に書き込まれる。
一方、次のコードT8のvadd命令においては、ソースレジスタとデスティネーションレジスタの各々のデータサイズとしてダブルワード「d」が指定されている。これによれば、開発者は、コードT8においてデータサイズがダブルワード「d」のデータ同士の演算を意図していることになる。
しかし、ソースレジスタのベクトルレジスタv2には、前述のコードT7によってデータサイズがシングルワード「s」の要素が書き込まれているため、コードT8を実行すると開発者の意図とは異なる結果が得られてしまう。
このように、先行命令のデスティネーションレジスタのデータサイズと、そのデスティネーションレジスタをソースレジスタとする後続命令の当該ソースレジスタのデータサイズとが異なる記述は誤りである。
・第3例
図11は、第3例に係るミスについて説明するためのアセンブリ言語のソースコードを示す模式図である。
図11の例では、コードT9のadd命令において、ソースレジスタである汎用レジスタx0、x1のデータを加算して、その結果を汎用レジスタx2に書き込んでいる。但し、コードT9よりも以前に汎用レジスタx1をデスティネーションレジスタとして使用しているコードはない。よって、汎用レジスタx1にどのようなデータが書き込まれているかが不明であり、当該データは不定となる。このような状態でコードT9を実行しても、汎用レジスタx2に書き込まれるデータも不定となってしまう。
そのため、このようにデスティネーションレジスタとして使われたことがないレジスタをソースレジスタに指定するのは誤りである。
・第4例
図12は、第4例に係る記述ミスについて説明するためのアセンブリ言語のソースコードを示す模式図である。
図12の例では、コードT10のadd命令において汎用レジスタx2をデスティネーションレジスタとして使用することにより、汎用レジスタx0、x1の各々のデータを加算した値を汎用レジスタx2に書き込んでいる。
但し、コードT10以降のコードでは、その汎用レジスタx2をソースレジスタとして使用することなく、コードT11のadd命令で汎用レジスタx2の内容を上書きしている。これでは、汎用レジスタx2に書き込まれているコードT10の実行結果を一度も使用していないことになるため、コードT10の存在意義が不明となり、コードT10又はコードT11におけるレジスタの指定ミスが疑われる。
そのため、このようにデスティネーションレジスタとして使ったレジスタを、その後にソースレジスタとして使うことなく再びデスティネーションレジスタに指定するのは誤りである。
・第5例
図13は、第5例に係る記述ミスについて説明するためのアセンブリ言語のソースコードを示す模式図である。
図13の例では、コードT12において、vfadd命令の実行結果である浮動小数がベクトルレジスタv0に書き込まれる。また、コードT13においては、vadd命令の実行結果である整数がベクトルレジスタv1に書き込まれる。
そして、コードT14のmultiply命令において、ソースレジスタのベクトルレジスタv0、v1の各々の要素のデータ同士が乗算され、その結果がデスティネーションレジスタのベクトルレジスタv2に書き込まれる。
但し、multiply命令は、前述のように二つのソースレジスタに書き込まれているデータのタイプが同一であることを前提とする命令である。この例では、ベクトルレジスタv0とベクトルレジスタv1の各々に書き込まれているデータタイプが浮動小数点型と整数型とで異なるため、このmultiply命令を実行すると開発者が意図したのとは異なる実行結果が得られてしまう。
よって、このように二つのソースレジスタに書き込まれているデータのデータタイプが異なる場合もコーディングの誤りとなる。
図14は、前述の第1~第5例に係る記述ミスをまとめた図である。
以下に、図14の各々の記述ミスやこれに等価なミスを検出できる各実施形態について説明する。
(第1実施形態)
本実施形態では、図14の第1~第5例に係る記述ミスに起因した例外を検出することが可能なプロセッサについて説明する。
図15は、本実施形態に係るプロセッサの構成図である。
図15に示すように、このプロセッサ20は、命令デコード回路21、データフェッチ回路22、命令実行回路23、書き戻し回路24、例外処理回路25、ステータスレジスタファイル26、及び演算用レジスタファイル27を備える。
更に、プロセッサ20の外部には、命令メモリ28aとデータメモリ28bとを備えたメモリ28が設けられる。このうち、命令メモリ28aは、プロセッサ20で実行する機械語の命令列を記憶するメモリである。そして、データメモリ28bは、命令の実行時に使用するデータを記憶するメモリである。
また、演算用レジスタファイル27は、図1と図2に示した汎用レジスタxn(n=0,1,2,…, 31)とベクトルレジスタvn(n=0,1,2,…, 31)とを備えたハードウェアである。
また、ステータスレジスタファイル26は、汎用レジスタxn(n=0,1,2,…, 31)とベクトルレジスタvn(n=0,1,2,…, 31)の各々のステータスを示すステータス情報を格納した複数のレジスタを備えたハードウェアである。
一方、命令実行回路23は、命令メモリ28aに格納されている命令を実行する回路であって、実行回路30、ステータス更新回路31、第1の例外検出部32a、及び第2の例外検出部32bを有する。
このプロセッサ20では以下のように命令が実行される。
まず、命令メモリ28aのアドレスのうち、不図示のプログラムカウンタが指すアドレスにある機械語の命令を命令デコード回路21が読み出す。
そして、命令デコード回路21がその命令をデコードし、デコード内容をデータフェッチ回路22、命令実行回路23、及び書き戻し回路24の各々に出力する。デコード内容としては、命令の種類、ソースレジスタとデスティネーションレジスタの各々のインデックス、ソースレジスタの要素のデータサイズ、及びソースレジスタのデータタイプがある。
例えば、アセンブリ言語の「vfadd v2.s, v0.s, v1.s」に相当する命令をデコードする場合を考える。その場合、命令デコード回路21は、命令メモリ28aから読み出した機械語をデコードすることにより命令の種類が「vfadd」であることを特定する。また、命令デコード回路21は、命令メモリ28aから読み出した機械語をデコードすることにより、vfadd命令のソースレジスタが「v0」と「v1」であり、デスティネーションレジスタが「v2」であることを特定する。
更に、読み出した機械語のビット列に基づいて、命令デコード回路21は、1番目のソースレジスタ「v0」の要素のサイズが「s」であることを特定し、かつ2番目のソースレジスタ「v1」の要素のサイズが「s」であることを特定する。
そして、前述のように命令の種類「vfadd」が浮動小数を対象とした演算であることから、命令デコード回路21は、命令の種類「vfadd」に基づいてソースレジスタのデータタイプが浮動小数であることを特定する。なお、命令の種類が「add」の場合には、命令デコード回路21は、ソースレジスタのデータタイプが符号あり整数であることを特定する。
次に、データフェッチ回路22が、デコード内容に基づいて、演算用レジスタファイル27とデータメモリ28bのいずれか一方からデータを読み出し、それを命令実行回路23に出力する。
例えば、前述の「vfadd v2.s, v0.s, v1.s」の例では、第1オペランドと第2オペランドの各々にレジスタが指定されている。よって、この場合は、データフェッチ回路22は、演算用レジスタファイル27にあるベクトルレジスタv0とベクトルレジスタv1の各々のデータを読み出し、それらのデータを命令実行回路23に出力する。
なお、命令デコード回路21がデコードした機械語が「load x0, アドレス1」の場合には、データフェッチ回路22は、データメモリ28bのアドレスのうち「アドレス1」にあるデータを読み出す。そして、データフェッチ回路22は、読み出したデータを命令実行回路23に出力する。
続いて、命令実行回路23の第1の例外検出部32aと第2の例外検出部32bの各々が、命令の実行によって例外が発生するかを検出する。
このうち、第2の例外検出部32bは、アセンブリ言語の記述ミスに起因した例外を検出する回路である。本実施形態では、後述のようにステータスレジスタファイル26に格納されているステータス情報と、命令デコード回路21が出力したデコード内容とに基づいて第2の例外検出部32bが例外を検出する。その検出方法やステータス情報の詳細については後述する。
一方、第1の例外検出部32aは、アセンブリ言語の記述ミスとは無関係の例外を検出する回路である。そのような例外としては、未実装命令を実行しようとしたときの例外や、0除算を実行したときの例外がある。
ここで、第1の例外検出部32a又は第2の例外検出部32bが例外を検出した場合は、これらの例外検出回路は例外信号を生成してそれを例外処理回路25に出力する。例外処理回路25は、その例外信号に応じた処理を行う回路である。
一方、第1の例外検出部32aと第2の例外検出部32bのいずれもが例外を検出しなかった場合には、ステータス更新回路31が、ステータスレジスタファイル26にあるステータス情報を更新する。
これと共に、実行回路30が命令を実行し、その実行結果を書き戻し回路24に出力する。このとき、実行回路30は、命令デコード回路21が出力したデコード内容に含まれる命令の種類を特定し、その命令の種類に応じた演算を行う。
例えば、前述の「vfadd v2.s, v0.s, v1.s」の例では、実行回路30は、命令の種類が「vfadd」であることを特定する。そして、実行回路30は、ベクトルレジスタv0とベクトルレジスタv1の各々のデータを加算し、それにより得られた値を書き戻し回路24に出力する。
続いて、書き戻し回路24が、命令の実行結果を演算用レジスタファイル27とデータメモリ28bのいずれか一方に書き戻す。これらのうちのどちらに実行結果を書き戻すかは、命令デコード回路21が出力したデコード内容に含まれる命令の種類に応じて書き戻し回路24が判断する。
例えば、前述の「vfadd v2.s, v0.s, v1.s」の例を考える。この例における「vfadd」は、デスティネーションレジスタに実行結果を書き戻す命令である。よって、書き戻し回路24は、ベクトルレジスタv0とベクトルレジスタv1の各々のデータを加算した値を演算用レジスタファイル27のベクトルレジスタv2に書き戻す。
なお、命令デコード回路21がデコードしたオペコードがstore命令を示している場合には、書き戻し回路24は、実行結果をデータメモリ28bに書き戻す。
以上により、一つの命令の実行を終える。
図16は、ステータスレジスタファイル26と演算用レジスタファイル27の各々の模式図である。
図16に示すように、演算用レジスタファイル27は、汎用レジスタxn(n=0,1,2,…, 31)とベクトルレジスタvn(n=0,1,2,…, 31)とを備えたレジスタファイルである。これらのレジスタのサイズは特に限定されない。本実施形態では汎用レジスタxn(n=0,1,2,…, 31)のサイズを64ビットとし、ベクトルレジスタvn(n=0,1,2,…, 31)のサイズを128ビットとする。
一方、ステータスレジスタファイル26は、汎用レジスタxn(n=0,1,2,…, 31)の各々と対応付けられたステータスレジスタsxn(n=0,1,2,…, 31)を備えたハードウェアである。更に、そのステータスレジスタファイル26は、ベクトルレジスタvn(n=0,1,2,…, 31)に対応付けられたステータスレジスタsvn(n=0,1,2,…, 31)も備える。
このうち、汎用レジスタxn(n=0,1,2,…, 31)に対応したステータスレジスタsxn(n=0,1,2,…, 31)は、汎用レジスタxn(n=0,1,2,…, 31)のステータス情報Qを格納するためのレジスタである。ステータス情報Qは、汎用レジスタxn(n=0,1,2,…, 31)のステータスを示す情報であって、第1のフラグW、第2のフラグR、及び型情報DTを備える。
このうち、第1のフラグWは、実行回路30が命令を実行する前に汎用レジスタxn(n=0,1,2,…, 31)がデスティネーションレジスタとして使用済みかどうかを示す1ビットのデータである。例えば、汎用レジスタx0がデスティネーションレジスタとして使用済みである場合を考える。この場合は、ステータスレジスタsx0に格納されているステータス情報Qの第1のフラグWが「1」となる。また、その汎用レジスタx0がデスティネーションレジスタとして使用済ではない場合には、その第1のフラグWは「0」となる。
また、第2のフラグRは、命令実行回路23が命令を実行する前に汎用レジスタxn(n=0,1,2,…, 31)がソースレジスタとして使用済みかどうかを示す1ビットのデータである。例えば、汎用レジスタx0がソースレジスタとして使用済の場合には、ステータスレジスタsx0における第2のフラグRが「1」となり、使用済ではない場合には第2のフラグRが「0」となる。
型情報DTは、汎用レジスタxnに格納されているデータのデータタイプを示すデータである。そのデータタイプとしては、符号あり整数、符号無し整数、浮動小数、及び不定の4種類がある。型情報DTは、これらの4種類のデータタイプの各々を一意に識別する2ビットの情報である。
一方、ベクトルレジスタvnに対応したステータスレジスタsvn(n=0,1,2,…, 31)は、ベクトルレジスタvn(n=0,1,2,…, 31)のステータス情報Qを格納するためのレジスタである。このステータス情報Qは、ベクトルレジスタvn(n=0,1,2,…, 31)のステータスを示す情報であって、前述の第1のフラグW、第2のフラグR、及び型情報DTの他にサイズ情報DSを有する。
サイズ情報DSは、ベクトルレジスタvn(n=0,1,2,…, 31)に格納されているベクトルデータの要素のサイズを示す情報である。図2を参照して説明したように、要素のサイズとしては、「x(128ビット)」、「d(ダブルワード)」、「s(シングルワード)」、「h(ハーフワード)」、「b(バイト)」、及び不定の6種類がある。サイズ情報DSは、これらの5種類のサイズを一意に識別するための3ビットの情報である。
上記のステータス情報Qは、各例外検出部32a、32bがいずれも例外を検出せずに実行回路30が正常に命令を実行したときに、ステータス更新回路31(図15参照)によって更新される。ステータス情報Qの更新は、デコード内容に含まれる各レジスタのインデックスと命令の種類に基づいて以下のように行われる。
例えば、オペランドにソースレジスタが指定された命令を実行回路30が実行したときに、ステータス更新回路31は、そのソースレジスタのインデックスをデコード内容から特定する。そして、ステータス更新回路31は、ステータスレジスタsxn、svn(n=0,1,2,…, 31)のうちで特定したインデックスを有するレジスタの第2のフラグRに「1」をセットする。
また、オペランドにデスティネーションレジスタが指定された命令を実行回路30が実行したときには、ステータス更新回路31は、そのデスティネーションレジスタのインデックスをデコード内容から特定する。そして、ステータス更新回路31は、ステータスレジスタsxn、svn(n=0,1,2,…, 31)のうちで特定したインデックスを有するレジスタの第1のフラグWに「1」をセットする。更に、この場合は、ステータス更新回路31は、当該ステータスレジスタの第2のフラグRを「0」にする。これにより、そのデスティネーションレジスタが、データが書き込まれてからまだソースレジスタとして使用されていない状態に設定されることになる。
また、オペランドにデスティネーションレジスタが指定された命令を実行回路30が実行したときには、ステータス更新回路31は、そのデスティネーションレジスタの型情報DTを更新する。例えば、実行回路30が「add x2, x0, x1」という命令を実行した場合を考える。この場合は、ステータス更新回路31は、デコード内容に基づいて、命令の種類が「add」であることと、デスティネーションレジスタのインデックスが「2」であることを特定する。このadd命令は、符号あり64ビット整数の加算命令であり、命令の実行によってデスティネーションレジスタの汎用レジスタx2に符号あり整数が格納される。よって、この場合は、ステータス更新回路31は、インデックスが「2」のステータスレジスタsx2に格納されている型情報DTが符号あり整数を示すようにステータス情報Qを更新する。なお、デスティネーションレジスタにベクトルレジスタvn(n=0,1,2,…, 31)が指定されている場合も、これと同様にしてステータス更新回路31がステータス情報Qを更新する。
このように、ステータス更新回路31は、レジスタにデータを書き込む命令を実行回路30が実行したときに、その命令が演算の対象とするデータのデータタイプを型情報DTが示すように、そのレジスタに対応した型情報DTを更新する。
更に、ステータス更新回路31は、オペランドにデスティネーションレジスタとしてベクトルレジスタ指定された命令を実行回路30が実行したときには、そのデスティネーションレジスタのサイズ情報DSを更新する。例えば、実行回路30が「vadd v3.s, v0.s, v1.s」という命令を実行した場合を考える。この場合は、ステータス更新回路31は、デコード内容に基づいて、デスティネーションレジスタのインデックスとデータサイズがそれぞれ「3」とシングルワード「s」であることを特定する。そして、ステータス更新回路31は、インデックスが「3」のステータスレジスタsv3に格納されているサイズ情報DSがシングルワードを示すようにステータス情報Qを更新する。
このように、ステータス更新回路31は、ベクトルレジスタにデータを書き込む命令を実行回路30が実行したときに、書き込まれたデータのデータサイズを示すようにそのレジスタに対応したサイズ情報DSを更新する。
次に、第2の例外検出部32bが検出する例外について説明する。
図17は、第2の例外検出部32bが例外を検出するときの検出ルールを模式的に示す図である。
第2の例外検出部32bが検出する例外の種類としては、「W例外」、「R例外」、「データタイプ例外」、「データサイズ例外」、及び「srcデータタイプ例外」がある。
このうち、W例外は、命令のソースレジスタが過去にデスティネーションレジスタとして使用されていない場合に発生する例外であり、図14の第3例の記述ミスがあった場合に発生する。
W例外は、前述の第1のフラグWを利用して検出することができる。例えば、ソースレジスタの第1のフラグWが「0」の場合は、そのソースレジスタは過去にデスティネーションレジスタとして使用されていないことになる。よって、第2の例外検出部32bは、第1のフラグWが「0」の場合にW例外が発生したことを検出する。
また、R例外は、先行命令がデータを書き込んだレジスタを後続命令がデスティネーションレジスタとして使用する場合に、先行命令と後続命令の間の全ての命令がそのレジスタをソースレジスタとして使用しない場合の例外である。このR命令は、図14の第4例の記述ミスがあった場合に発生する。
R例外は、前述の第1のフラグWと第2のフラグRとを利用して検出することができる。例えば、命令のデスティネーションレジスタが、過去に別の命令でデスティネーションレジスタとして使用済みの場合には第1のフラグWは「1」となる。また、そのデスティネーションレジスタが、その後の命令でソースレジスタとして使用されていない場合には第2のフラグRは「0」となる。よって、第2の例外検出部32bは、第1のフラグWが「1」であり、かつ第2のフラグRが「0」の場合にR例外が発生したことを検出する。
そして、データタイプ例外は、命令が演算の対象とするデータタイプと、実際にソースレジスタに書き込まれているデータのデータタイプとが一致しない場合に発生する例外であり、図14の第1例の記述ミスがあった場合に発生する。
データタイプ例外は、前述の型情報DTを利用して検出することができる。例えば、その型情報DTが示すデータタイプと、命令が演算の対象とするデータタイプとが一致しない場合に、第2の例外検出部32bはデータタイプ例外が発生したことを検出する。
一方、データサイズ例外は、図14の第2例の記述ミスに起因して発生する例外である。そのデータサイズ例外は、先行命令がレジスタに書き込んだデータのデータサイズと、そのレジスタをソースレジスタとする後続命令において指定されたソースレジスタのデータサイズとが異なる場合に発生する。
このデータサイズ例外は、前述のサイズ情報DSを利用して検出することができる。例えば、ある命令のソースレジスタに指定されたデータサイズと、そのソースレジスタに対応したサイズ情報DSが示すデータサイズとが一致しない場合に、第2の例外検出部32bはデータサイズ例外が発生したことを検出する。
そして、srcデータタイプ例外は、図14の第5例の記述ミスがあった場合に発生する例外である。そのsrcデータタイプ例外は、二つのソースレジスタのデータタイプが同一であることを前提とする命令において、各ソースレジスタの各々のデータタイプが一致しない場合に発生する。
srcデータタイプ例外も、前述の型情報DTを利用して検出することができる。例えば、第2の例外検出部32bは、二つのソースレジスタのデータタイプが同一であることを前提とする命令において、各ソースレジスタの型情報DTのデータタイプ同士が一致しない場合にsrcデータタイプ例外を検出する。
図18は、第2の例外検出部32bのブロック図である。
図18に示すように、第2の例外検出部32bは、選択回路40、例外検出回路41、及び例外信号生成回路42を有する。
このうち、選択回路40は、命令デコード回路21が出力したデコード内容からソースレジスタとデスティネーションレジスタのそれぞれのインデックスを取得する。そして、選択回路40は、汎用レジスタxn(n=0,1,2,…, 31)とベクトルレジスタvn(n=0,1,2,…, 31)の各々のうち、取得したインデックスに対応するレジスタを選択する。
一方、例外検出回路41は種々の例外を検出する回路である。この例では、例外検出回路41は、データタイプ例外検出回路43、データサイズ例外検出回路44、W例外検出回路45、srcデータタイプ例外検出回路46、及びR例外検出回路47を有する。
このうち、データタイプ例外検出回路43は、データタイプ例外を検出する回路である。データタイプ例外の検出を行うために、データタイプ例外検出回路43は、命令デコード回路21が出力したデコード内容からソースレジスタに格納されていることが期待されるデータタイプを取得する。更に、データタイプ例外検出回路43は、そのソースレジスタに対応したステータス情報Qを選択回路40を介して取得する。
そして、データタイプ例外検出回路43は、ステータス情報Qの型情報DTが示すデータタイプと、命令デコード回路21から取得したソースレジスタのデータタイプとが一致するかを判定する。そして、一致しないと判定した場合に、データタイプ例外検出回路43は、データタイプ例外を検出したという検出結果を例外信号生成回路42に出力する。
また、データサイズ例外検出回路44は、データサイズ例外を検出する回路である。データサイズ例外の検出を行うために、データサイズ例外検出回路44は、命令デコード回路21が出力したデコード内容に基づいて、ソースレジスタに格納されていることが期待されるベクトルデータの要素のデータサイズを取得する。更に、データサイズ例外検出回路44は、そのソースレジスタに対応したステータス情報Qを選択回路40を介して取得する。
そして、データサイズ例外検出回路44は、ステータス情報Qのサイズ情報DSが示すサイズと、命令デコード回路21から取得したソースレジスタのサイズとが一致するかを判定する。そして、一致しないと判定した場合に、データサイズ例外検出回路44は、データサイズ例外を検出したという検出結果を例外信号生成回路42に出力する。
また、W例外検出回路45は、W例外を検出する回路である。W例外の検出を行うために、W例外検出回路45は、命令のソースレジスタに対応したステータス情報Qを選択回路40を介して取得する。
そして、W例外検出回路45は、そのステータス情報Qにおける第1のフラグWが「0」の場合に、W例外を検出したという検出結果を例外信号生成回路42に出力する。
更に、srcデータタイプ例外検出回路46は、命令デコード回路21からデコード内容に含まれる命令の種類を取得する。そして、その命令の種類に基づいて、srcデータタイプ例外検出回路46は、その命令が二つのソースレジスタに格納されているデータタイプが同一であることを前提としているかを判断する。
このとき、データタイプが同一であることを前提としていると判断したときは、srcデータタイプ例外検出回路46は、命令の二つのソースレジスタの各々に対応したステータス情報Qを選択回路40を介して取得する。
更に、srcデータタイプ例外検出回路46は、そのステータス情報Qにおける型情報DTが、二つのソースレジスタの各々で一致しているかを判定する。そして、両者が一致しない場合に、srcデータタイプ例外検出回路46は、srcデータタイプ例外を検出したという検出結果を例外信号生成回路42に出力する。
そして、R例外検出回路47は、R例外を検出する回路である。R例外の検出を行うために、R例外検出回路47は、命令のソースレジスタに対応したステータス情報Qと、命令のデスティネーションレジスタに対応したステータス情報Qとを選択回路40を介して取得する。そして、R例外検出回路47は、デスティネーションレジスタに対応するステータス情報Qの第1のフラグWと、ソースレジスタに対応するステータス情報Qの第2のフラグRとを特定する。更に、R例外検出回路47は、第1のフラグWが「1」であり、かつ第2のフラグRが「0」である場合に、R例外を検出したという検出結果を例外信号生成回路42に出力する。
なお、例外検出回路41は、実行回路30から後述の無効信号を受信した場合には例外の検出を行わない。また、実行回路30から後述の有効信号を受信した場合には、例外検出回路41は、上記のようにして例外の検出を行う。
例外信号生成回路42は、例外検出回路41の検出結果に応じた例外信号を出力する。その例外信号には、例外の種類、命令のアドレス、及びレジスタのインデックスが含まれる。
このうち、例外の種類としては、「データタイプ例外」、「データサイズ例外」、「W例外」、「srcデータタイプ例外」、及び「R例外」がある。例えば、データタイプ例外検出回路43が例外を検出したという検出結果を出した場合には、例外の種類として「データタイプ例外」が例外信号に含まれることになる。
また、命令のアドレスは、例外が発生した命令のアドレスである。例えば、命令デコード回路21が保持しているアドレスを例外信号生成回路42が取得することにより、例外信号生成回路42が例外信号に命令のアドレスを含ませることができる。
そして、レジスタのインデックスは、例外が発生した命令のオペランドに指定されているレジスタのインデックスである。例えば、例外信号生成回路42は、命令デコード回路21が出力したデコード内容からソースレジスタとデスティネーションレジスタのそれぞれのインデックスを取得することにより、それを例外信号に含ませることができる。
次に、この例外信号の出力先である例外処理回路25の機能について説明する。
図19は、例外処理回路25の機能について説明するための模式図である。
例外信号を受信すると、例外処理回路25は、命令デコード回路21、データフェッチ回路22、命令実行回路23、及び書き戻し回路24の各々に対し、現在実行中の命令の実行を停止するように指示する。
そして、例外処理回路25は、メモリ28に格納されている例外ベクタテーブル50を参照し、受信した例外信号に含まれる例外の種類に対応したジャンプ先のアドレスを特定する。図19の例では、W例外のジャンプ先の命令メモリ28aのアドレスを「0xAAAAAAAA」とし、R例外のジャンプ先のアドレスを「0xBBBBBBBB」等としている。これらのアドレスにおける命令メモリ28aには、例外の種類に応じて実行する例外処理プログラムが予め格納されている。
その後、例外処理回路25は、特定したジャンプ先のアドレスを命令デコード回路21に通知する。そして、命令デコード回路21が、通知されたアドレスにある命令をフェッチする。これにより、命令実行回路23において、例外の種類に応じた例外処理プログラムが実行されることになる。
以上説明したプロセッサ20によれば、ステータスレジスタsxn、svn(n=0,1,2,…, 31)に格納されているステータス情報Qを利用して、例外検出回路41が、図17の検出ルールに従って例外を検出する。これにより、アセンブリに記述ミスがあった場合に例外検出回路41が例外を検出するようになり、その例外に基づいて開発者がアセンブリプログラムに記述ミスがあったことに気付くことができる。その結果、開発者がアセンブリプログラムを容易にデバッグすることができるようになり、プログラム開発の効率化を図ることができる。
しかも、このように記述ミスに起因した例外をプロセッサ20が検出することで、記述ミスのあるプログラムをプロセッサ20で無駄に実行する時間が減り、プロセッサ20やメモリ28等のハードウェア資源の無駄な消費を改善できる。
更に、ステータス情報Qには、第1のフラグW、第2のフラグR、型情報DT、及びサイズ情報DSがある。これらの情報を図17の検出ルールに適用することにより、例外検出回路41は、「R例外」、「W例外」、「データタイプ例外」、「データサイズ例外」、及び「srcデータタイプ例外」を検出することができる。そのため、これらのどの例外が検出されたかに応じ、開発者が、アセンブリにおける具体的な記述ミスの種類を特定することができる。
ところで、プログラムの実行時にはサブルーチン呼び出しやOS(Operating System)によるコンテキストスイッチが発生することがある。この場合は、呼び出されたサブルーチンや別のコンテキストによってレジスタのデータが上書きされるのを防ぐために、汎用レジスタxnとベクトルレジスタvnの各々にあるデータをデータメモリ28bに退避させる必要がある。そして、元のルーチンの処理に戻ったときには、処理を中断したところからプログラムを再開できるようにするために、データメモリ28bに退避させたデータを汎用レジスタxnとベクトルレジスタvnの各々に復元させる。
本実施形態では、汎用レジスタxnに対応付けて用いられているステータスレジスタsxnに格納されているステータス情報Qについても退避と復元を行うのが好ましい。同様の理由により、ステータスレジスタsvnについてもステータス情報Qの退避と復元を行うのが好ましい。
そこで、次に、ステータスレジスタsxn(n=0,1,2,…, 31)、svn(n=0,1,2,…, 31)の各々に格納されているステータス情報Qの退避と復元とを行うことが可能な命令について説明する。
図20は、本実施形態に係るstoreStatus命令とloadStatus命令について模式的に示す図である。
storeStatus命令は、第1のストア命令の一例であって、ステータスレジスタsxn(n=0,1,2,…, 31)に格納されているステータス情報Qを汎用レジスタxn(n=0,1,2,…, 31)に保存する命令である。ここでは、storeStatus命令の第1オペランドにデスティネーションレジスタとして汎用レジスタxn(n=0,1,2,…, 31)の一つを指定する。そして、storeStatus命令の第2オペランドにソースレジスタとしてステータスレジスタsxn(n=0,1,2,…, 31)の一つを指定する。
実行回路30(図15参照)は、このstoreStatus命令を実行することにより、ステータスレジスタsxn(n=0,1,2,…, 31)にあるステータス情報Qを汎用レジスタxn(n=0,1,2,…, 31)に保存する。
なお、ソースレジスタとデスティネーションレジスタの各々のインデックスは同一である必要はなく、インデックスが異なるレジスタ間でステータス情報Qを移動させてもよい。
また、storeStatus命令で汎用レジスタxn(n=0,1,2,…, 31)の一つに保存したステータス情報Qをデータメモリ28bに保存するには、汎用レジスタxn(n=0,1,2,…, 31)のデータをデータメモリに格納するstore命令を使用すればよい。
このように実行回路30がstoreStatus命令とstore命令とを実行することにより、ステータスレジスタsxn(n=0,1,2,…, 31)のステータス情報Qをデータメモリ28bに退避させることができる。
一方、loadStatus命令は、第1のロード命令の一例であって、汎用レジスタxn(n=0,1,2,…, 31)に保存されているステータス情報Qをステータスレジスタsxn(n=0,1,2,…, 31)に書き込む命令である。
ここでは、loadStatus命令の第1オペランドにデスティネーションレジスタとしてステータスレジスタsxn(n=0,1,2,…, 31)の一つを指定する。そして、loadStatus命令の第2オペランドにソースレジスタとして汎用レジスタxn(n=0,1,2,…, 31)の一つを指定する。
実行回路30(図15参照)は、このloadStatus命令を実行することにより、汎用レジスタxn(n=0,1,2,…, 31)に保存されているステータス情報Qをステータスレジスタsxn(n=0,1,2,…, 31)に書き込む。
storeStatus命令と同様に、loadStatus命令においてもソースレジスタとデスティネーションレジスタの各々のインデックスは同一である必要はなく、インデックスが異なるレジスタ間でステータス情報Qを移動させてもよい。
なお、データメモリ28bに退避しておいたステータス情報Qを汎用レジスタxn(n=0,1,2,…, 31)の一つに保存するにはデータメモリ28bのデータを汎用レジスタに書き込むload命令を使用すればよい。
上記のようなstoreStatus命令やloadStatus命令を実行回路30(図15参照)が実行することにより、ステータスレジスタsxn(n=0,1,2,…, 31)とデータメモリ28bとの間でステータス情報Qの退避や復元を行うことができる。
なお、この例ではstoreStatus命令とloadStatus命令の各々のオペランドにステータスレジスタsxn(n=0,1,2,…, 31)を指定したが、これに代えてステータスレジスタsvn(n=0,1,2,…, 31)を指定してもよい。同様に、storeStatus命令とloadStatus命令の各々のオペランドに指定された汎用レジスタxn(n=0,1,2,…, 31)に代えてベクトルレジスタvn(n=0,1,2,…, 31)を使用してもよい。
ところで、図20の例では、データメモリ28bとステータスレジスタsxn(n=0,1,2,…, 31)との間でのステータス情報Qの退避と復元とを汎用レジスタxn(n=0,1,2,…, 31)を介して行った。
このように汎用レジスタxn(n=0,1,2,…, 31)を介してステータス情報Qの退避と復元とを行うのではなく、次のような命令を利用することにより、ステータスレジスタsxn(n=0,1,2,…, 31)とデータメモリ28bとの間でステータス情報Qの退避と復元とを直接行ってもよい。
図21は、本実施形態に係るsaveStatus命令とloadStatus命令について模式的に示す図(その1)である。
saveSatus命令は、第2のストア命令の一例であって、ステータスレジスタsxn(n=0,1,2,…, 31)に格納されているステータス情報Qをデータメモリ28bに保存する命令である。
ここでは、saveStatus命令の第1オペランドにアドレスを指定し、第2オペランドにステータスレジスタsxn(n=0,1,2,…, 31)を指定する。実行回路30は、このsaveStatus命令を実行することにより、第2オペランドのステータスレジスタsxn(n=0,1,2,…, 31)にあるステータス情報Qを、第1オペランドのアドレスのデータメモリ28bに保存する。
なお、データメモリ28bに保存されたステータス情報Qをステータスレジスタsxn(n=0,1,2,…, 31)に保存するにはloadStatus命令を利用すればよい。
但し、図20のようにレジスタ間でのデータの転送を行うloadStatus命令と区別するために、ここでは図20とは異なる書式でloadStatus命令を記述する。図21の例では、loadStatus命令の第1オペランドにステータスレジスタsxn(n=0,1,2,…, 31)を指定し、第2オペランドにアドレスを指定する。実行回路30は、この書式のloadStatus命令を実行することにより、第2オペランドのアドレスにあるデータメモリ28bのステータス情報Qを、第1オペランドのステータスレジスタsxn(n=0,1,2,…, 31)に書き込む。なお、この書式を採用したloadStatus命令は、第2のロード命令の一例である。
このように実行回路30がsaveStatus命令とloadStatus命令を実行すると、汎用レジスタxn(n=0,1,2,…, 31)を介さずにステータス情報Qの退避と復元とを行うことができるため、プログラムの実行速度を高速化できる。
なお、図20と図21の例では、ステータス情報Qの退避先や復元先が一つのレジスタのみであるが、次のように全てのレジスタで一括して退避と復元をすると便利な場合もある。
図22は、本実施形態に係るsaveStatus命令とloadStatus命令について模式的に示す図(その2)である。
図21の二つのオペランドをとるsaveStatus命令の書式とは異なり、この例では、開発者が一つのオペランドのみをとるようにsaveStatus命令を記述する。実行回路30は、このsaveStatus命令を実行することにより、オペランドのアドレスのデータメモリ28bに、全てのステータスレジスタsxn、svn(n=0, 1, …31)に格納されている全てのステータス情報Qを一括して保存する。なお、このsaveStatus命令は第3のストア命令の一例である。
一方、loadStatus命令についても、図21のように二つのオペランドをとる書式とは異なり、アドレスを指定する一つのオペランドのみをとるように記述する。実行回路30は、このloadStatus命令を実行することにより、指定されたアドレスのデータメモリ28bにある全てのステータス情報Qの各々を、対応するステータスレジスタsxn、svn(n=0, 1, …31)の各々に一括して書き込む。なお、このloadStatus命令は第3のロード命令の一例である。
図20~図22のうちのどの命令を使用してステータス情報Qの退避と復元とを行うかは特に限定されない。但し、コンテキストスイッチの場合には、どの命令を使用するのかをABI(Application Binary Interface)に基づいて開発者が決定するのが好ましい。ABIは、サブルーチンを呼び出す側(caller)の責任で退避と復元とを行うレジスタと、呼び出された側(callee)の責任で退避と復元とを行うレジスタとをプロセッサごとに定めた規約である。
例えば、プロセッサ20がARMv8-Aアーキテクチャに準拠している場合、callee側が汎用レジスタx19~x28とベクトルレジスタv8~v15の各々におけるデータの退避と復元とを行うことが定められている。この規約に従うと、データの退避と復元とをするのに合わせて、これらのレジスタに対応したステータスレジスタsx19~sx28、sv8~sv15に格納されているステータス情報Qについても退避と復元を行うことになる。この場合に図22のように全てのステータスレジスタとデータメモリ28bとの間で退避と復元とを行う命令を使用すると、退避や復元をする必要がないレジスタのデータまで処理する必要が生じ、プログラムの実行速度が低下する。よって、サブルーチンコールの場合には、図20や図21のようにステータスレジスタsxn、svn(n=0, 1, …31)ごとに退避や復元を行う命令を使用し、プログラムの実行速度を向上させるのが好ましい。
一方、OSのコンテキストスイッチの場合には、全ての汎用レジスタx0~x31と全てのベクトルレジスタv0~v31のデータに対して退避と復元とが行われる。よって、コンテキストスイッチの場合には、図22のように全てのステータスレジスタとデータメモリ28bとの間で退避と復元とを行う命令を使用し、プログラムのコードサイズを小さくするのが好ましい。
ところで、プロセッサ20においては、上記のように開発者が自ら記述したアセンブリ言語のソースコードの記述ミスに対して例外信号生成回路42が例外信号を出力するが、場合によっては例外信号が不要な場合もある。
例えば、開発者が手作業で記述したアセンブリ言語のソースコードではなく、コンパイラが出力したアセンブリ言語のソースコードには、コンパイラの最適化が不十分なことに起因して無意味な命令が含まれることがある。その命令に対して例外検出回路41が例外を検出すると、手作業に由来したミスを検出するという目的から外れた場合にも例外が検出されることになり、例外の過検知となって煩わしい。
そこで、本実施形態では、以下のようにして例外検出回路41の機能を抑制する。
図23(a)は、例外検出回路41の機能を抑制するときのアセンブリ言語のソースコードの模式図である。
このソースコード53においては、開発者が、例外を検出したくない命令列53aの前にdisableExeptGen命令を記述する。そして、開発者が、その命令列53aの後にenableExeptGen命令を記述する。なお、disableExeptGen命令は無効命令の一例であり、enableExeptGen命令は有効命令の一例である。
実行回路30がdisableExeptGen命令を実行すると、実行回路30が例外検出回路41(図18参照)に無効信号を通知する。無効信号は、例外の検出を行うという例外検出回路41の機能を無効にする信号である。よって、その無効信号を受けた例外検出回路41は、命令列53aにおける例外の検出を行わない。その結果、仮に命令列53aに例外が発生するような記述ミスが含まれていても、例外信号生成回路42は例外信号を生成しないことになる。
また、命令列53aの実行が終わって実行回路30がenableExeptGen命令を実行すると、実行回路30が例外検出回路41に有効信号を通知する。有効信号は、例外を検出するという例外検出回路41の機能を有効にする信号である。よって、その有効通知を受けた例外検出回路41は、enableExeptGen命令の後続命令において例外の検出を行う。これにより、enableExeptGen命令の後続の命令列53bで例外が発生した場合には、例外信号生成回路42が例外信号を生成することになる。
このようにdisableExeptGen命令とenableExeptGen命令とを利用することにより、命令列53aの実行時に例外信号が発生するのを抑制したり、例外信号の発生を再開させたりすることができる。
なお、以下のようにdisableExeptGen命令とenableExeptGen命令の各々にオペランドを記述してもよい。
図23(b)は、disableExeptGen命令にオペランドを記述した例の模式図である。
この例では、disableExeptGen命令の第1オペランドに、開発者が複数のステータスレジスタsxn、svn(n=0,1,2,…, 31)のうちの一つを記述する。そして、第2オペランドには、複数の例外の種類の各々を識別する識別子を開発者が記述する。ここでは、W例外の識別子を「W」、R例外の識別子を「R」、データタイプ例外の識別子を「DataType」、データサイズ例外の識別子を「DataSize」、srcデータタイプ例外の識別子を「SrcDataType」とする。
この書式のdisableExeptGen命令を実行回路30が実行すると、第1オペランドのステータスレジスタsxn、svnにあるステータス情報Qを用いて第2オペランドの例外を検出するという例外検出回路41の機能が無効となる。例えば、「disableExeptGen sx0, W|R」と記述した場合には、ステータスレジスタsx0に格納されているステータス情報Qを用いてW例外とR例外とを検出する機能が無効となる。
一方、図23(c)は、enableExeptGen命令にオペランドを記述した例の模式図である。
この例では、enableExeptGen命令の第1オペランドに、開発者が複数のステータスレジスタsxn、svn(n=0, 1, …31)のうちの一つを記述する。そして、第2オペランドには、複数の例外の種類の各々を識別する識別子を開発者が記述する。
この書式のenableExeptGen命令を実行回路30が実行すると、第1オペランドのステータスレジスタsxn、svn(n=0, 1, …31)にあるステータス情報Qを用いて第2オペランドの例外を検出するという例外検出回路41の機能が有効となる。例えば、「enableExeptGen sx0, W」と記述した場合には、ステータスレジスタsx0に格納されているステータス情報Qを用いてW例外を生成するかを判定する機能が有効となる。
このように、disableExeptGen命令とenableExeptGen命令の各々にオペランドを記述することにより、検出対象の例外の種類を選択できる。更に、その例外を検出するのに使用するステータス情報Qの格納先であるステータスレジスタを指定することができ、開発者の利便性が向上する。
(第2実施形態)
本実施形態では、第1実施形態で説明したプロセッサ20の動作を模擬するシミュレーションプログラムについて説明する。
図24は、シミュレーションプログラムを実行する情報処理装置のハードウェア構成図である。
この情報処理装置60は、PC(Personal Computer)等の計算機であって、記憶装置60a、メモリ60b、プロセッサ60c、通信インターフェース60d、表示装置60e、及び入力装置60fを有する。これらの各部は、バス60gにより相互に接続される。
このうち、記憶装置60aは、HDD(Hard Disk Drive)やSSD(Solid State Drive)等の不揮発性のストレージデバイスであり、本実施形態に係るシミュレーションプログラム61を記憶する。
なお、シミュレーションプログラム61をコンピュータが読み取り可能な記録媒体60hに記録させておき、プロセッサ60cに記録媒体60hのシミュレーションプログラム61を読み取らせるようにしてもよい。
そのような記録媒体60hとしては、例えばCD-ROM(Compact Disc - Read Only Memory)、DVD(Digital Versatile Disc)、及びUSB(Universal Serial Bus)メモリ等の物理的な可搬型記録媒体がある。また、フラッシュメモリ等の半導体メモリやハードディスクドライブを記録媒体60hとして使用してもよい。これらの記録媒体60hは、物理的な形態を持たない搬送波のような一時的な媒体ではない。
更に、公衆回線、インターネット、及びLAN(Local Area Network)等に接続された装置にシミュレーションプログラム61を記憶させておき、プロセッサ60cがそのプログラム61を読み出して実行するようにしてもよい。
一方、メモリ60bは、DRAM等のようにデータを一時的に記憶するハードウェアであって、その上に前述のシミュレーションプログラム61が展開される。
プロセッサ60cは、情報処理装置60の各部を制御したり、メモリ60bと協働してシミュレーションプログラム61を実行したりするCPU(Central Processing Unit)やGPU(Graphical Processing Unit)等のハードウェアである。
更に、通信インターフェース60dは、情報処理装置60をLAN等のネットワークに接続するためのインターフェースである。
そして、表示装置60eは、液晶表示装置等のハードウェアであって、開発者に種々の情報を表示する。また、入力装置60fは、キーボードやマウス等のハードウェアである。例えば、開発者は、入力装置60fを操作することにより、情報処理装置60に対して種々の指示を出すことになる。
図25は、情報処理装置60がシミュレーションプログラム61を実行したときの情報処理装置60の機能構成図である。
図25に示すように、情報処理装置60は、制御部63と記憶部64とを備える。
このうち、制御部63は、プロセッサ60cとメモリ60bがシミュレーションプログラム61を実行することにより実現される処理部であり、環境構築部65とシミュレーション実行部66とを有する。
環境構築部65は、プロセッサ20を模擬した疑似プロセッサを生成する処理部である。また、シミュレーション実行部66は、開発者からの指示を受け付けて、環境構築部65が生成した疑似プロセッサでシミュレーションを実行する処理部である。
一方、記憶部64は、記憶装置60aとメモリ60bとにより実現され、シミュレーション対象のプロセッサ20(図15参照)が実行する機械語の実行可能プログラム67を記憶する。その実行可能プログラム67は、開発者が手作業で記述したアセンブリ言語のソースコードをアセンブルして得られた機械語のプログラムである。
なお、情報処理装置60におけるプロセッサ60cは、シミュレーション対象のプロセッサ20と同一である必要はなく、プロセッサ20と異なる命令セットを実行するプロセッサでもよい。これにより、シミュレーション対象のプロセッサ20が手元にない状況でもその動作を模擬でき、プロセッサ20が実行する実行可能プログラムの開発効率が上がる。
図26は、環境構築部65が生成した疑似プロセッサ70の機能ブロック図である。
疑似プロセッサ70は、命令デコード部71、データフェッチ部72、命令実行部73、書き戻し部74、例外処理部75、ステータスレジスタファイル部76、及び演算用レジスタファイル部77を有する。更に、疑似プロセッサ70は、命令メモリ部78aとデータメモリ部78bとを備えたメモリ部78を有する。
これらの各部は、第1実施形態に係るプロセッサ20の各部の機能をソフトウェア的に実現したものであり、メモリ60bとプロセッサ60cが協働してシミュレーションプログラム61を実行することにより実現される。
図27は、第1実施形態に係るプロセッサ20の各部と、本実施形態に係る疑似プロセッサ70の各部との対応関係を示す図である。
図27においては、同一の機能を有する要素同士を矢印で示している。例えば、命令デコード部71は、命令デコード回路21をソフトウェア的に実現した機能ブロックであり、命令デコード回路21と同一の機能を有する。
また、命令デコード部71以外の各部も、プロセッサ20において対応する要素と同一の機能を有する。その機能については第1実施形態で説明したため、以下ではその説明を省略する。
なお、疑似プロセッサ70の各部をソフトウェアで実現する具体的な方法は特に限定されない。例えば、演算用レジスタファイル部77は、メモリ60bに確保された第1の記憶領域77aで実現される。そして、演算用レジスタファイル部77の汎用レジスタxn(n=0,1,2,…, 31)とベクトルレジスタvn(n=0,1,2,…, 31)は、第1の記憶領域77aに格納される配列要素で実現され、配列要素のインデックスが各レジスタのインデックスとなる。
同様に、ステータスレジスタファイル部76は、メモリ60bに確保された第2の記憶領域76aで実現される。そして、ステータスレジスタファイル部76のステータスレジスタsxn、svn(n=0,1,2,…, 31)は、第2の記憶領域76aに格納される配列要素で実現され、配列要素のインデックスが各ステータスレジスタのインデックスとなる。
また、メモリ28は、メモリ60bに確保された第3の記憶領域78cで模擬することができる。
次に、本実施形態に係るシミュレーション方法について説明する。
図28は、本実施形態に係るシミュレーション方法について説明するためのフローチャートである。
まず、環境構築部65が、疑似プロセッサ70の各部を生成する(ステップS11)。例えば、環境構築部65は、汎用レジスタxn(n=0,1,2,…, 31)とベクトルレジスタvn(n=0,1,2,…, 31)とを模擬する第1の記憶領域77aと、ステータスレジスタsxn、svn(n=0,1,2,…, 31)を模擬する第2の記憶領域76aとをメモリ60bに確保する。また、メモリ28を模擬するために、環境構築部65は、メモリ60bに第3の記憶領域78cを確保する。
更に、環境構築部65は、実行可能プログラム67を読み込んでそれを命令メモリ部78aに格納する(ステップS12)。
次に、環境構築部65が、ステータスレジスタファイル部76を初期化する(ステップS13)。例えば、環境構築部65は、ステータスレジスタファイル部76におけるステータスレジスタsxn、svn(n=0,1,2,…, 31)を模擬する配列を初期化する。例えば、WフラグとRフラグの各々0に設定され、データタイプDTとデータサイズDSの各々は不定を表すビット列に設定される。
次に、命令デコード部71が、命令メモリ部78aに格納されている機械語の命令を読み出す(ステップS14)。
そして、命令デコード部71がその命令をデコードし、デコード内容をデータフェッチ部72、命令実行部73、及び書き戻し部74の各々に出力する(ステップS15)。デコード内容としては、命令の種類、ソースレジスタとデスティネーションレジスタの各々のインデックス、ソースレジスタの要素のデータサイズ、ソースレジスタのデータタイプ等がある。
次に、データフェッチ部72が、デコード内容に基づいて、演算用レジスタファイル部77とデータメモリ部78bのいずれか一方からデータを読み出し、それを命令実行部73に出力する(ステップS16)。
次いで、実行部83が命令を実行する(ステップS17)。
そして、命令実行部73の第1の例外検出部82aが、アセンブリ言語の記述ミスとは無関係の例外を検出する(ステップS18)。そのような例外としては、未実装命令を実行しようとしたときの例外や、0除算を使用としたときの例外がある。
更に、ステップS18では、命令実行部73の第2の例外検出部82bが、アセンブリ言語の記述ミスに起因した例外を検出する。例えば、第2の例外検出部82bは、ステータスレジスタファイル部76にあるステータス情報Qと、命令デコード部71が出力したデコード内容とに基づいて例外を検出する。なお、第2の例外検出部82bが行う例外検出処理の詳細については後述する。
次いで、第1の例外検出部82aと第2の例外検出部82bの各々が、例外検出処理の結果に基づいて、例外を検出したかどうかを判定する(ステップS19)。
ここで、第1の例外検出部82aと第2の例外検出部82bのいずれもが例外を検出しないと判断した場合(ステップS19:否定)には、実行部83が、命令の実行結果を書き戻し部74に出力する(ステップS20)。
一方、第1の例外検出部82aが例外を検出したと判断した場合(ステップS19:肯定)は、第1の例外検出部82aは、例外信号を生成してそれを例外処理部75に出力する(ステップS20)。なお、第2の例外検出部82bが例外を検出した判断した場合(ステップS19:肯定)も、第2の例外検出部82bは、例外信号を生成してそれを例外処理部75に出力する(ステップS20)。
そして、その例外信号を受けた例外処理部75は、ジャンプ先のアドレスを例外ベクタテーブル50(図19参照)から特定し、特定したジャンプ先のアドレスを命令デコード部71に通知する(ステップS21)。
この後は、実行可能プログラム67の各命令に対してステップS14~S22を繰り返して行う。そして、実行可能プログラム67の全ての命令に対してS14~S22を終了すると、本実施形態に係るシミュレーション方法を終える。
次に、ステップS18の例外検出処理について説明する。
図29は、ステップS18の例外検出処理のフローチャートである。
まず、第2の例外検出部82bが、データタイプ例外があるかどうかをチェックする(ステップS31)。例えば、第2の例外検出部82bは、ステップS17で実行した命令のソースレジスタのステータス情報Qをステータスレジスタファイル部76から読み出す。そして、第2の例外検出部82bは、そのステータス情報Qの型情報DTが示すデータタイプと、命令が演算の対象とするデータタイプとが一致しない場合にデータタイプ例外を検出する。
次に、第2の例外検出部82bが、ステップS31で読み出したステータス情報Qを用いて、データサイズ例外があるかどうかをチェックする(ステップS32)。例えば、第2の例外検出部82bは、そのステータス情報Qのサイズ情報DSが示すデータサイズと、ステップS17で実行した命令においてソースレジスタに期待されるデータサイズとが一致しない場合にデータサイズ例外を検出する。
次いで、第2の例外検出部82bが、W例外があるかどうかをチェックする(ステップS33)。そのチェックは、ステップS31で読み出したステータス情報Qを用いて行われる。例えば、第2の例外検出部82bは、ステータス情報Qにおける第1のフラグWが「0」の場合にW例外を検出する。
続いて、第2の例外検出部82bが、そのステータス情報Qの第1のフラグWと第2のフラグRとに基づいてR例外があるかどうかをチェックする(ステップS34)。一例として、第2の例外検出部82bは、第1のフラグWが「1」であり、かつ第2のフラグRが「0」の場合にR例外を検出する。
次に、第2の例外検出部82bが、srcデータタイプ例外があるかどうかをチェックする(ステップS35)。例えば、第2の例外検出部82bは、ステップS17で実行した命令が、二つのソースレジスタのデータタイプが同一であることを前提とする命令かどうかを判断する。ここで、二つのソースレジスタのデータタイプが同一であることを前提とする命令であると判断したときは、第2の例外検出部82bは、当該命令の各ソースレジスタのステータス情報Qをステータスレジスタファイル部76から読み出す。そして、例外検出部82は、これらのステータス情報Qの各々の型情報DTが示すデータサイズ同士が一致しない場合にsrcデータタイプ例外を検出する。
以上により、ステップS18の例外検出処理を終える。
以上説明した本実施形態によれば、第1実施形態に係るプロセッサ20において実行可能プログラム67の命令を実際に実行しなくても、その実行可能プログラム67に記述ミスがあるかどうかを情報処理装置60でチェックできる。
更に、このように情報処理装置60で記述ミスをチェックできるため、記述ミスのあるプログラムをプロセッサ20で無駄に実行する時間が減り、プロセッサ20やメモリ28等のハードウェア資源の無駄な消費を改善できる。
しかも、ステップS18の例外検出処理においては、第1実施形態と同様にステータス情報Qを利用して例外を検出する。そのため、開発者が手作業でアセンブリを記述したときのミスに由来するデータタイプ例外、データサイズ例外、W例外、R例外、及びsrcデータタイプ例外の各々を検出することができる。そして、この例外に基づいて開発者がデバッグすることができ、アセンブリ言語のプログラムの開発効率を高めることができる。
なお、本実施形態は上記に限定されない。
例えば、第1実施形態の図20~図22で説明したloadStatus命令、storeStatus命令、及びsaveStatus命令を実行部83が実行してもよい。これにより、第2の記憶領域76a(図27参照)で模擬されるステータスレジスタsxn、svn(n=0,1,2,…, 31)のデータの退避や復元をすることができ、サブルーチンコールやOSのコンテキストスイッチを模擬することができる。
更に、第1実施形態の図23(a)~(c)のdisableExceptGen命令やenableExceptGen命令を開発者がアセンブリ言語のソースコードに記述し、例外検出部82が例外を検出する機能を無効にしたり有効にしたりしてもよい。
(第3実施形態)
第1実施形態では、プロセッサ20が実行可能プログラムを実行するときに、アセンブリ言語の記述ミスに起因した例外をプロセッサ20が検出した。
これに対し、本実施形態では、アセンブリ言語の記述ミスがあった場合に、アセンブリ言語から機械語の実行可能プログラムを生成するアセンブラプログラムがエラーを出力する。
図30は、本実施形態において実行可能プログラムを実行するターゲットマシンが備えるプロセッサ90の構成図である。
なお、図30において、第1実施形態の図15で説明したのと同じ要素には図15におけるのと同じ符号を付し、以下ではその説明を省略する。
本実施形態では、第1実施形態とは異なり、ステータスレジスタファイル26(図15参照)に格納されたステータス情報Qを利用してプロセッサ90が例外を検出しない。そのため、このプロセッサ90は、第1実施形態に係るプロセッサ20からステータスレジスタファイル26、ステータス更新回路31、及び第2の例外検出部32bを省いたハードウェア構成を有する。これらの回路以外の各部の動作は第1実施形態と同様である。
このプロセッサ90で実行される機械語の実行可能プログラムは、以下のように本実施形態に係るアセンブラプログラムで生成される。
図31は、本実施形態に係るアセンブラプログラムを実行する情報処理装置のハードウェア構成図である。
この情報処理装置100は、PC等の計算機であって、記憶装置100a、メモリ100b、プロセッサ100c、通信インターフェース100d、表示装置100e、及び入力装置100fを有する。これらの各部は、バス100gにより相互に接続される。
記憶装置100aは、HDDやSSD等の不揮発性のストレージデバイスであり、本実施形態に係るアセンブラプログラム112を記憶する。
なお、アセンブラプログラム112をコンピュータが読み取り可能な記録媒体100hに記録させておき、プロセッサ100cに記録媒体100hのアセンブラプログラム112を読み取らせるようにしてもよい。
そのような記録媒体100hとしては、例えばCD-ROM、DVD、及びUSBメモリ等の物理的な可搬型記録媒体がある。また、フラッシュメモリ等の半導体メモリやハードディスクドライブを記録媒体100hとして使用してもよい。これらの記録媒体100hは、物理的な形態を持たない搬送波のような一時的な媒体ではない。
更に、公衆回線、インターネット、及びLAN等に接続された装置にアセンブラプログラム112を記憶させておき、プロセッサ100cがそのアセンブラプログラム112を読み出して実行するようにしてもよい。
一方、メモリ100bは、DRAM等のようにデータを一時的に記憶するハードウェアであって、その上に前述のアセンブラプログラム112が展開される。
プロセッサ100cは、情報処理装置100の各部を制御したり、メモリ100bと協働してアセンブラプログラム112を実行したりするCPUやGPU等のハードウェアである。
更に、通信インターフェース100dは、情報処理装置100をLAN等のネットワークに接続するためのインターフェースである。
そして、表示装置100eは、液晶表示装置等のハードウェアであって、開発者に種々の情報を表示する。また、入力装置100fは、キーボードやマウス等のハードウェアである。例えば、開発者は、入力装置100fを操作することにより、情報処理装置100に対して種々の指示を出すことになる。
図32は、情報処理装置100がアセンブラプログラム112を実行したときの情報処理装置100の機能構成図である。
図32に示すように、情報処理装置100は、制御部101と記憶部102とを備える。
このうち、記憶部102は、記憶装置100aとメモリ100bとにより実現され、アセンブルの対象となるアセンブリ言語のソースプログラム109を記憶する。また、記憶部102は、そのアセンブリ言語のソースプログラム109に記述ミスがあるかを判定するのに使用されるステータス情報110も記憶する。このステータス情報110の詳細については後述する。
更に、記憶部102は、アセンブリ言語のソースプログラム109をアセンブルして得られた機械語の実行可能プログラム111も記憶する。
一方、制御部101は、初期化部103、取得部104、例外検出部105、エラー出力部106、ステータス更新部107、及び機械語生成部108を有する。
このうち、初期化部103は、アセンブルに先立ってステータス情報110を初期化する処理部である。また、取得部104は、アセンブル対象のアセンブリ言語のソースプログラム109を記憶部102から取得する処理部である。
例外検出部105は、ステータス情報110に基づいて、ソースプログラム109に記述された命令における例外を検出する処理部である。例えば、オペランドに汎用レジスタxn(n=0,1,2,…, 31)やベクトルレジスタvn(n=0,1,2,…, 31)が指定された命令を含むアセンブリ言語のコードを取得部104が取得すると、例外検出部105は、その命令における例外を検出する。
そして、エラー出力部106は、例外検出部105が例外を検出したときにエラーを出力する処理部である。
一方、ステータス更新部107は、例外検出部105が例外を検出しなかったときにステータス情報110の内容を更新する処理部である。そして、機械語生成部108は、例外検出部105が例外を検出しなかったときに、アセンブリ言語のコードをアセンブルして機械語を生成し、その機械語を含む実行可能プログラム111を記憶部102に書き出す処理部である。
次に、ステータス情報110について説明する。
ステータス情報110は、演算用レジスタファイル27(図30参照)に設けられた汎用レジスタxn(n=0,1,2,…, 31)とベクトルレジスタvn(n=0,1,2,…, 31)の各々のステータスを示す情報である。ステータス情報110の実現方法は特に限定されないが、本実施形態では配列でステータス情報110を実現する。
図33は、本実施形態に係るステータス情報を表すC++のソースコードの模式図である。
このステータス情報110において、コードT20は、汎用レジスタxn(n=0,1,2,…, 31)とベクトルレジスタvn(n=0,1,2,…, 31)の各々のデータサイズを示す列挙型「dataSize_t」のメンバ変数を宣言するコードである。そのメンバ変数としては、バイトを示す「sizeB」、ハーフワードを示す「sizeH」、シングルワードを示す「sizeS」、ダブルワードを示す「sizeD」、及び128ビットを示す「sizeX」がある。
また、コードT20においては、汎用レジスタxn(n=0,1,2,…, 31)やベクトルレジスタvn(n=0,1,2,…, 31)にデータが格納されておらずデータサイズが不定であることを示す「CLEAN」も列挙型「dataSize_t」のメンバ変数として宣言される。
コードT21は、ベクトルレジスタvn(n=0, 1, …31)に格納されたベクトルデータの要素のデータサイズを格納するための配列を宣言するコードである。ここでは、ベクトルレジスタvn(n=0,1,2,…, 31)用の配列として配列「dataSizeVReg」を宣言する。
なお、この配列「dataSizeVReg」の各々の要素は、対応するベクトルレジスタに格納されているベクトルデータの要素のデータサイズを示す型情報の一例である。
また、本実施形態では、配列要素のインデックスがレジスタのインデックスを表すものとする。例えば、dataSizeVReg[0], dataSizeVReg [1],…dataSizeVReg [31]がベクトルレジスタv0、v1、…v31の各々のデータサイズ示す。
なお、この例ではコードT21において各配列「dataSizeVReg」の全ての要素を「CLEAN」に初期化しておく。
一方、コードT22は、汎用レジスタxn(n=0, 1, …31)とベクトルレジスタvn(n=0, 1, …31)の各々のデータタイプを示す列挙型「dataType」のメンバ変数を宣言するコードである。そのメンバ変数としては、符号なし整数を示す「typeUnsigned」、符号あり整数を示す「typeSigned」、及び浮動小数を示す「typeFloat」がある。
また、コードT22においては、汎用レジスタxn(n=0, 1, …31)やベクトルレジスタvn(n=0, 1, …31)にデータが格納されておらずデータタイプが不定であることを示す「CLEAN」も列挙型「dataType_t」のメンバ変数として宣言される。
コードT23は、汎用レジスタxn(n=0, 1, …31)とベクトルレジスタvn(n=0, 1, …31)の各々のデータタイプを格納するための配列を宣言するコードである。本実施形態では、汎用レジスタxn用の配列として配列「dataTypeGReg」を宣言し、ベクトルレジスタ用の配列として配列「dataTypeVReg」を宣言する。
なお、これらの配列「dataTypeGReg」、「dataTypeVReg」の各々の要素は、対応するレジスタに格納されているデータのデータタイプを示す型情報の一例である。
また、データサイズの例と同様に、コードT23においても配列要素のインデックスがレジスタのインデックスを表すものとする。例えば、dataTypeGReg[0], dataTypeGReg [1],…dataTypeGReg [31]が汎用レジスタx0、x1、…x31の各々のデータサイズ示し、dataTypeVReg[0], dataTypeVReg [1],…dataTypeVReg [31]がベクトルレジスタv0、v1、…v31の各々のデータサイズ示す。
なお、この例では、コードT23において配列「dataTypeGReg」、「dataTypeVReg」の全ての要素を「CLEAN」に初期化しておく。
そして、コードT24は、汎用レジスタxn(n=0, 1, …31)とベクトルレジスタvn(n=0, 1, …31)の各々がソースレジスタとして使用済みかどうかを示す列挙型「readAccess_t」のメンバ変数を宣言するコードである。そのメンバ変数としては、ソースレジスタとして使用済みではないことを示す「FALSE」と、ソースレジスタとして使用済みであることを示す「TRUE」とがある。
コードT25は、列挙型「readAccess_t」のメンバ変数を格納するための配列を宣言するコードである。ここでは、汎用レジスタxn(n=0, 1, …31)用の配列として配列「readAccessGReg」を宣言し、ベクトルレジスタvn(n=0, 1, …31)用の配列として配列「readAccessVReg」を宣言する。
なお、配列「readAccessGReg」、「readAccessVReg」の各々の要素は、対応するレジスタがソースレジスタとして使用済かどうかを示す第2のフラグの一例である。
また、これらの配列「readAccessGReg」、「readAccessVReg」の各々の要素のインデックスは、レジスタのインデックスに等しいものとする。例えば、readAccessGReg[0], readAccessGReg[1],…readAccessGReg[31]が汎用レジスタx0、x1、…x31の各々に対応し、readAccessVReg[0], readAccessVReg[1],…readAccessVReg[31]がベクトルレジスタv0、v1、…v31に対応する。
一方、コードT26は、汎用レジスタxn(n=0, 1, …31)とベクトルレジスタvn(n=0, 1, …31)の各々がデスティネーションレジスタとして使用済みかどうかを示す列挙型「writeAccess_t」のメンバ変数を宣言するコードである。そのメンバ変数としては、デスティネーションレジスタとして使用済みではないことを示す「FALSE」と、デスティネーションレジスタとして使用済みであることを示す「TRUE」とがある。
コードT27は、列挙型「writeAccess_t」のメンバ変数を格納するための配列を宣言するコードである。ここでは、汎用レジスタxn(n=0, 1, …31)用の配列として配列「writeAccessGReg」を宣言し、ベクトルレジスタvn(n=0, 1, …31)用の配列として配列「writeAccessVReg」を宣言する。
なお、配列「writeAccessGReg」、「writeAccessVReg」の各々の要素は、対応するレジスタがデスティネーションレジスタとして使用済かどうかを示す第1のフラグの一例である。
また、これらの配列「writeAccessGReg」、「writeAccessVReg」の各々の要素のインデックスは、レジスタのインデックスに等しいものとする。例えば、writeAccessGReg[0], writeAccessGReg[1],…writeAccessGReg[31]が汎用レジスタx0、x1、…x31の各々に対応し、writeAccessVReg[0], writeAccessVReg[1],…writeAccessVReg[31]がベクトルレジスタv0、v1、…v31に対応する。
このように、ここでは配列「dataSizeVReg」、「dataTypeGReg」、「dataTypeVReg」、「readAccessGReg」、「readAccessVReg」、「writeAccessVReg」、及び「writeAccessVReg」によってステータス情報110を実現する。これらの配列の要素は、例外検出部105が例外を検出しなかったときに、ステータス更新部107によって更新される。
例えば、オペランドにソースレジスタが指定された命令について例外がなかったときは、ステータス更新部107は、そのソースレジスタに対応した「readAccessGReg」又は「readAccessVReg」の要素に「TRUE」を格納する。
そして、オペランドにデスティネーションレジスタが指定された命令について例外がなかったときもステータス更新部107は各配列の要素を更新する。この場合は、ステータス更新部107は、デスティネーションレジスタに対応した配列「writeAccessGReg」又は「writeAccessVReg」の要素に「TRUE」を格納する。更に、ステータス更新部107は、そのデスティネーションレジスタに対応した配列「readAccessGReg」又は「readAccessVReg」の要素に「FALSE」を格納する。これにより、そのデスティネーションレジスタが、データが書き込まれてからまだソースレジスタとして使用されていない状態に設定されることになる。
また、ステータス更新部107は、レジスタにデータを書き込む命令がアセンブリ言語のソースプログラム109に存在するときに、そのレジスタに対応した配列「dataTypeGReg」又は「dataTypeVReg」を更新する。例えば、ソースプログラム109にコード「add x2, x0, x1」が記述されている場合を考える。add命令は、符号あり64ビット整数の加算命令である。よって、この場合は、ステータス更新部107は、汎用レジスタx2に対応する「dataTypeGReg[2]」に「typeSigned」を格納する。このように、ステータス更新部107は、配列「dataTypeGReg」又は「dataTypeVReg」の要素に、「typeUnsigned」、「typeSigned」、及び「typeFloat」のちで命令が演算の対象とするデータタイプを格納する。
同様に、ステータス更新部107は、ベクトルレジスタにデータを書き込む命令がアセンブリ言語のソースプログラム109に存在するときに、そのベクトルレジスタに対応した配列「dataSizeVReg」を更新する。例えば、ソースプログラム109に「vadd v3.s, v0.s, v1.s」が記述されている場合を考える。この場合は、デスティネーションレジスタであるベクトルレジスタv3に書き込まれるデータのサイズとしてシングルワード「s」が指定されている。そこで、この場合は、ステータス更新部107は、ベクトルレジスタv3に対応する「dataSizeVReg[3]」に「sizeS」を格納する。このように、ステータス更新部107は、配列「dataSizeVReg」の要素に、「sizeB」、「sizeH」、「sizeS」、「sizeD」、及び「sizeX」のうちでデスティネーションレジスタに指定されたデータサイズを格納する。
次に、例外検出部105が例外を検出するときの検出ルールについて説明する。
図34(a)は、オペランドに汎用レジスタxn(n=0,1,2…31)が指定された場合の検出ルールについて模式的に示す図である。
図34(a)に示すように、オペランドに汎用レジスタが指定された場合に例外検出部105が検出対象とする例外には、第1実施形態の図17で説明した「W例外」、「R例外」、「データタイプ例外」、及び「srcデータタイプ例外」がある。
このうち、W例外は、ある命令のソースレジスタがその命令の先行命令においてデスティネーションレジスタとして使用されていない場合に発生する例外であって、配列「writeAccessGReg」を利用して検出することができる。
例えば、ある命令のソースレジスタが汎用レジスタx0である場合を考える。その汎用レジスタx0に対応した「writeAccessGReg[0]」が「FALSE」の場合には、その汎用レジスタx0は過去にデスティネーションレジスタとして使用されていないことになる。よって、例外検出部105は、「writeAccessGReg[0]」が「FALSE」の場合にW例外を検出する。
また、R例外は、先行命令がデータを書き込んだレジスタを後続命令がデスティネーションレジスタとして使用する場合に、先行命令と後続命令の間の全ての命令がそのレジスタをソースレジスタとして使用しない場合の例外である。そのR例外は、配列「writeAccessGReg」と配列「readAccessGReg」とを利用して検出することができる。
例えば、ある命令のデスティネーションレジスタが汎用レジスタx0である場合を考える。その汎用レジスタx0が、過去に別の命令でデスティネーションレジスタとして使用済みの場合には「writeAccessGReg[0]」が「TRUE」となる。また、その汎用レジスタx0が、その後の命令でソースレジスタとして使用されていない場合には「readAccessGReg[0]」が「FALSE」となる。よって、例外検出部105は、「writeAccessGReg[0]」が「TRUE」であり、かつ「readAccessGReg[0]」が「FALSE」の場合にR例外を検出する。
また、データタイプ例外は、命令が演算の対象とするデータタイプとソースレジスタに書き込まれている実際のデータのデータタイプとが一致しない場合に発生する例外である。
データタイプ例外は配列「dataTypeGReg」を利用して検出することができる。例えば、取得部104が取得したアセンブリ言語のコードが「fadd x2, x0, x1」である場合を考える。この場合、例外検出部105は、アセンブリ言語のコードにおける「fadd」との記述から命令の種類がfaddであることを特定し、この命令が演算の対象とするデータのデータタイプが「typeFloat」であることを特定する。更に、例外検出部105は、上記のコードの第2オペランドの「x0」との記述に基づいて、この命令のソースレジスタが汎用レジスタx0であることを特定する。
一方、汎用レジスタx0に対応した配列「dataTypeGReg」の要素である「dataTypeGReg[0]」には「typeUnsigned」が格納されているとする。この場合は、「dataTypeGReg[0]」と「typeFloat」とが一致しない。例外検出部105は、このように「dataTypeGReg[0]」が「typeFloat」に一致しない場合にデータタイプエラーを検出する。
なお、この例では例外検出部105が第2オペランド「x0」についてのデータタイプ例外を検出したが、同様の方法で第3オペランド「x1」についてのデータタイプ例外も例外検出部105が検出することができる。
そして、srcデータタイプ例外は、命令の二つのソースレジスタの各々のデータタイプが一致しない場合に発生する例外である。
srcデータタイプ例外は配列「dataTypeGReg」を利用して検出することができる。
例えば、取得部104が取得したアセンブリ言語のコードが「multiply x2, x0, x1」である場合を考える。この場合、例外検出部105は、アセンブリ言語のコードにおけるにおける「multiply」との記述から命令の種類が「multiply」であることを特定する。そして、例外検出部105は、特定したmultiply命令が二つのソースレジスタのデータタイプが同一であることを前提とする命令であることに基づき、汎用レジスタx0、x1に格納されているデータのデータタイプを比較する。
ここで、汎用レジスタx0に格納されているデータタイプが符号無し整数であり、「dataTypeGReg[0]」が「typeUnsigned」であるとする。一方、汎用レジスタx1に格納されているデータタイプが浮動小数であり、「dataTypeGReg[1]」が「typeFloat」であるとする。この場合は、「dataTypeGReg[0]」と「dataTypeGReg[1]」とが一致しない。例外検出部105は、このように「dataTypeGReg[0]」と「dataTypeGReg[1]」とが一致しない場合にsrcデータタイプ例外を検出する。
一方、図34(b)は、オペランドにベクトルレジスタvn(n=0,1,2…31)が指定された場合の検出ルールについて模式的に示す図である。
ベクトルレジスタの場合も、図34(a)の汎用レジスタの場合と同様の検出ルールで「W例外」、「R例外」、「データタイプ例外」、及び「srcデータタイプ例外」を検出できる。
更に、ベクトルレジスタの場合には「データサイズ例外」を検出するための検出ルールもある。
データサイズ例外は、先行命令がレジスタに書き込んだデータのデータサイズと、そのレジスタをソースレジスタとする後続命令において指定されたソースレジスタのデータサイズとが異なる場合に発生する例外である。そのデータサイズ例外は、配列「dataSizeVReg」を利用して検出することができる。
例えば、取得部104が取得したアセンブリ言語のコードが「vadd v2.s, v0.s, v1.s」である場合を考える。この場合、例外検出部105は、第2オペランドの「v0.s」との記述に基づいて、このvadd命令のソースレジスタに指定されたデータサイズが「sizeS」であることを特定する。更に、例外検出部105は、第2オペランドの「v0.s」との記述に基づいて、このvadd命令のソースレジスタがベクトルレジスタv0であることも特定する。
一方、ベクトルレジスタv0に対応した配列「dataSizeVReg」の要素である「dataSizeVReg [0]」には「sizeB」が格納されているとする。この場合は、「dataSizeVReg[0]」は「sizeS」に一致しない。例外検出部105は、このように「dataSizeVReg[0]」が「sizeS」に一致しない場合にデータサイズ例外を検出する。
なお、この例では例外検出部105が「vadd v2.s, v0.s, v1.s」の第2オペランド「v0.s」についてのデータサイズ例外を検出したが、同様の方法で第3オペランド「v1.s」についてのデータサイズ例外も検出することができる。
以上説明した情報処理装置100によれば、ステータス情報110を利用して、例外検出部105が、図34(a)、(b)の検出ルールに従ってアセンブル時に例外を検出する。これにより、アセンブリ言語のソースプログラム109に記述ミスがあった場合に例外検出部105が例外を検出するようになり、その例外に基づいて開発者がソースプログラム109に記述ミスがあったことに気付くことができる。その結果、開発者がソースプログラム109を容易にデバッグすることができるようになり、プログラム開発の効率化を図ることができる。
更に、ステータス情報110には、「W例外」、「データタイプ例外」、「データサイズ例外」、及び「srcデータタイプ例外」を検出するための配列が定義されている。そのため、そのステータス情報110に基づいて、例外検出部105が、上記の各例外のうちのどの例外が発生したのかを特定できる。そして、これらのどの例外が検出されたかに応じ、開発者が、ソースプログラム109における具体的な記述ミスを特定することができる。
ところで、このように例外検出部105が例外を検出するとアセンブリ言語のソースプログラム109における記述ミスを発見できるが、場合によっては例外検出部105が例外を検出する機能を無効にした方が便利なこともある。
例えば、第1実施形態で説明したように、コンパイラの最適化が不十分であることに起因して無意味なアセンブリ言語のコードがソースプログラム109に含まれている場合がある。そのようなコードに対して例外検出部105が例外を検出すると、手作業に由来したミスを検出するという目的から外れた場合にも例外が検出されて煩わしい。
そこで、本実施形態では、以下のようにして例外検出部105の機能を抑制する。
図35は、例外検出部105の機能を抑制するときのアセンブリ言語のソースプログラム109の模式図である。
このソースプログラム109においては、開発者が、例外を検出したくない命令列109aの前に、アセンブリ言語のディレクティブである「.disable_check」を記述する。「.disable_check」は、例外検出部105が例外を検出する処理を無効にするディレクティブである。
そして、開発者が、その命令列109aの後にディレクティブ「.enable_check」を記述する。「.enable_check」は、例外検出部105が例外を検出する処理を有効にするディレクティブである。
例外検出部105は、取得部104がディレクティブ「.disable_check」を取得した場合には、そのディレクティブの後続命令に対して例外を検出する処理を行わない。よって、命令列109aに例外が発生するような記述ミスが含まれていても、機械語生成部108がこの命令列109aをコンパイルして機械語を生成することになる。
また、命令列109aのコンパイルが終わって取得部104がディレクティブ「.enable_check」を取得すると、例外検出部105は例外を検出する処理を再開する。よって、図35の例では、命令列109bにおける例外が例外検出部105によって検出されることになる。
このようにディレクティブ「.disable_check」、「.enable_check」を利用することにより、例外検出部105が例外を検出する処理を無効にしたり、例外検出部105が例外を検出する処理を有効にしたりすることができる。
なお、このようにアセンブリ言語のディレクティブを使用にするのに代えて、開発者が情報処理装置100にアセンブルの指示を与えるときのコマンドライン引数を利用して例外を検出する機能を抑制してもよい。
図36は、コマンドライン引数の一例を示す模式図である。
図36においては、アセンブリ言語のソースプログラム109から機械語の実行可能プログラム111(図32参照)を生成することを情報処理装置100に指示するコマンド「gas」の引数に「-no_check」を与えている。コマンドライン引数の「-no_check」は、情報処理装置100がソースプログラム109をアセンブルするときに、例外検出部105が例外を検出する機能を無効にする引数である。これにより、アセンブル時に不要な例外が検出されるのを防止でき、プログラム開発の利便性を高めることができる。
次に、本実施形態に係るアセンブラプログラム112が実行する処理について説明する。
図37は、本実施形態に係るアセンブラプログラム112が実行する処理のフローチャートである。
まず、初期化部103がステータス情報110を初期化する(ステップS41)。例えば、初期化部103は、配列「dataSizeVReg」、「dataTypeGReg」、及び「dataTypeVReg」の全ての要素に「CLEAN」を格納する。また、初期化部103は、配列「readAccessGReg」、「readAccessVReg」、「writeAccessGReg」、及び「writeAccessVReg」の全ての要素に「FALSE」を格納する。
次に、取得部104が、記憶部102からアセンブリ言語のソースプログラム109を取得する(ステップS42)。
次いで、例外検出部105が、例外を検出する機能が有効かどうかを判断する(ステップS43)。一例として、例外検出部105は、取得部104がディレクティブ「.disable_check」を読み込んだ場合に、例外を検出する機能が有効ではないと判定する。また、例外検出部105は、コマンドライン引数に「-no_check」が含まれている場合にも、例外を検出する機能が有効ではない判定する。
一方、例外検出部105は、取得部104がディレクティブ「.enable_check」を読み込んだ場合や、コマンドライン引数に「-no_check」が含まれていない場合に、例外を検出する機能が有効である判定する。
ここで、例外を検出する機能が有効ではない(ステップS43:否定)と判定された場合にはステップS48に移る。
ステップS48においては、機械語生成部108が、取得部104が取得したコードを機械語に変換する。そして、機械語生成部108は、その機械語を含む実行可能プログラム111を生成し、それを記憶部102に書き出す。
一方、例外を検出する機能が有効である(ステップS43:肯定)と判定された場合にはステップS44に移る。
ステップS44においては、例外検出部105が例外検出処理を行う。その例外検出処理については後述する。
そして、例外検出部105が、例外を検出したかどうかを判定する(ステップS45)。
ここで、例外を検出したと判定された場合(ステップS45:肯定)にはステップS46に移る。
ステップS46においては、エラー出力部106がエラーを出力し、処理を終える。エラーの出力方法は特に限定されない。例えば、エラー出力部106は、エラーの原因である例外の種類を標準出力に出力する。
一方、例外を検出しなかったと判定された場合(ステップS45:否定)にはステップS47に移り、ステータス更新部107がステータス情報110を更新する。
次いで、前述のステップS48に移り、機械語生成部108が機械語の実行可能プログラム111を生成し、それを記憶部102に書き出す。
この後は、アセンブリ言語のソースプログラム109に記述されているコードの行数だけステップS43~S48を繰り返して行い、処理を終える。
次に、ステップS44の例外検出処理について説明する。
図38は、ステップS44の例外検出処理のフローチャートである。
まず、例外検出部105が、データタイプ例外があるかどうかをチェックする(ステップS51)。例えば、例外検出部105は、取得部104が取得したアセンブリ言語のコードから命令の種類を特定することにより、この命令が演算の対象とするデータタイプを特定する。更に、例外検出部105は、アセンブリ言語のコードに基づいて、この命令のソースレジスタを特定する。
そして、例外検出部105は、配列「dataTypeGReg」、「dataTypeVReg」の各要素のうちでこのソースレジスタに対応した要素を特定する。そして、例外検出部105は、特定した要素が示すデータタイプと、命令が演算の対象とするデータタイプとが一致するかを判定し、一致しない場合にデータタイプ例外を検出する。
次に、例外検出部105が、データサイズ例外があるかどうかをチェックする(ステップS52)。例えば、例外検出部105は、取得部104が取得したアセンブリ言語のコードに基づいて、そのコードに含まれる命令のソースレジスタを特定する。更に、例外検出部105は、配列「dataSizeVReg」の各要素のうちでこのソースレジスタに対応した要素が示すデータサイズと、命令のソースレジスタに指定されたデータサイズとが一致するかを判定する。そして、両者が一致しない場合に例外検出部105はデータサイズ例外を検出する。
次いで、例外検出部105が、W例外があるかどうかをチェックする(ステップS53)。一例として、例外検出部105は、取得部104が取得したアセンブリ言語のコードに基づいて、そのコードに含まれる命令のソースレジスタを特定する。そして、例外検出部105は、配列「writeAccessGReg」、「writeAccessVReg」の各要素のうちで特定したソースレジスタに対応する要素が「FALSE」の場合にW例外を検出する。
続いて、例外検出部105が、R例外があるかどうかをチェックする(ステップS54)。例えば、例外検出部105は、取得部104が取得したアセンブリ言語のコードに基づいて、そのコードに含まれる命令のデスティネーションレジスタを特定する。
特定したデスティネーションレジスタが汎用レジスタの場合には、例外検出部105は、そのデスティネーションレジスタに対応する配列「writeAccessGReg」と配列「readAccessGReg」のそれぞれの要素を特定する。更に、例外検出部105は、特定した配列「writeAccessGReg」の要素が「TRUE」であり、かつ特定した配列「readAccessGReg」の要素が「FALSE」の場合にR例外を検出する。
なお、デスティネーションレジスタがベクトルレジスタの場合にも、例外検出部105は、配列「writeAccessVReg」と配列「readAccessVReg」のそれぞれの要素を利用してR例外を検出する。
次に、例外検出部105が、srcデータタイプ例外があるかどうかをチェックする(ステップS55)。
例えば、例外検出部105は、取得部104が取得したアセンブリ言語のコードから命令の種類を特定し、その命令が二つのソースレジスタのデータタイプが同一であることを前提とする命令かどうかを判断する。そして、データタイプが同一であることを前提とした命令であると判断した場合には、例外検出部105は、取得したコードに基づいて二つのソースレジスタを特定する。
特定した二つのソースレジスタが汎用レジスタである場合には、例外検出部105は、これらのソースレジスタの各々に対応する配列「dataTypeGReg」の要素が同一かどうかを判断する。そして、同一でないと判断した場合は、例外検出部105は、srcデータタイプ例外を検出する。なお、ソースがベクトルレジスタの場合にも、例外検出部105は、「dataSizeVReg」を利用してsrcデータタイプ例外を検出する。
以上により、例外検出処理を終える。
上記した本実施形態によれば、アセンブリ言語のソースプログラム109をアセンブルするときに、ステータス情報110(図33)に基づいて例外検出部105が例外を検出する。そのため、アセンブルにより得られた実行可能プログラム111をプロセッサ90(図30参照)で実行する前に早期にソースプログラム109の記述ミスを検出でき、プログラム開発の効率化を図ることができる。
更に、実行可能プログラム111の実行前に記述ミスを検出できることで、記述ミスのある実行可能プログラムをプロセッサ90で無駄に実行する時間が減り、プロセッサ90やメモリ28等のハードウェア資源の無駄な消費を改善できる。
しかも、ステップS44の例外検出処理で使用するステータス情報111には、「R例外」、「W例外」、「データタイプ例外」、「データサイズ例外」、及び「srcデータタイプ例外」を検出するための種々の配列が含まれる。その配列の要素を図34(a)、(b)の検出ルールに適用することにより、例外検出部105が上記の種々の例外を検出できる。そのため、どの種類の例外が検出されたかに応じ、開発者が、アセンブリ言語のソースプログラム109における具体的な記述ミスを特定することができる。
(第4実施形態)
第1~第3実施形態で説明した「W例外」、「R例外」、「データタイプ例外」、「データサイズ例外」、及び「srcデータタイプ例外」の各々は、JIT(Just In Time)コンパイラ技術を使用したプログラムでも生じ得る。
JITコンパイラ技術は、実行時に決定されるパラメータ、処理内容、及びプロセッサの状況に応じて、好適な機械語の命令列を生成する技術である。JITコンパイラ技術を用いて生成した機械語の命令列は、AOT(Ahead Of Time)型のコンパイラが生成する汎用的に処理可能な機械語の命令列からなる実行可能プログラムよりも処理が高速である。
そこで、まずこのJITコンパイラ技術について、AOTコンパイラ技術と比較しながら説明する。
図39は、AOTコンパイラ技術やJITコンパイラ技術により生成された実行可能プログラムを実行する情報処理装置のハードウェア構成図である。
この情報処理装置117は、HPC用途の計算機やPC(Personal Computer)等の計算機であって、図30に示したのと同一の構造を有するプロセッサ90とメモリ28とを有する。
このうち、プロセッサ90は、図30に示した各種回路21~25と演算用レジスタファイル27とを備えたハードウェアである。また、メモリ28は、実行可能プログラムが展開されるDRAM等の揮発性メモリである。その実行可能プログラムは、以下のようにAOTコンパイラ技術を用いてソースコードをコンパイルすることにより生成することができる。また、JITコンパイラ技術を用いる場合には、実行可能プログラムの実行中に機械語の命令列が動的に生成される。
図40(a)は、AOTコンパイラ技術でコンパイルすることを前提としたC++の疑似ソースコード120の一例を示す模式図である。
AOTコンパイラ技術では、開発者がC言語やC++の文法に即してソースコードを記述し、そのソースコードをGCC(GNU Compiler Collection)等のコンパイラが機械語の命令列にコンパイルする。
図40(a)の例では、処理120aにおいて配列「Tbl」の各要素をパラメータ「q」で除する。そして、処理120bにおいて、配列「in」の要素を配列「Tbl」の要素で除し、それを配列「out」に格納する。
図40(b)は、パラメータ「q」と配列「in」、「out」を宣言したC++の疑似ソースコード121の一例を示す模式図である。
パラメータ「q」は、前述の処理120aにおける除数であり、以下では入力パラメータとも呼ぶ。また、配列「in」と配列「out」は、それぞれ処理120bにおける入力データと出力データである。これらの配列「in」、「out」に格納するデータは特に限定されない。ここでは16個の画素データからなる画像を1000000枚格納する二次元配列として配列「in」と配列「out」を宣言する。
図40(c)は、配列「Tbl」の初期値を宣言したC++の疑似ソースコード122の一例を示す模式図である。
配列「Tbl」は、画素データを量子化する量子化テーブルの値を格納する配列である。ここでは、各配列「in」、「out」に対応した16個の要素を持つ配列として配列「Tbl」を宣言する。そして、配列「Tbl」の各要素の初期値は2のべき乗であると仮定する。
図40(a)~図40(c)のソースコード120~122は全てC言語やC++の文法に即して開発者が記述し、コンパイラによってアセンブリプログラムに変換される。
図41は、AOTコンパイラ技術で前述のソースコード120をコンパイルして得られたアセンブリプログラム124の疑似コードの模式図である。
そのアセンブリプログラム124には、プロセッサ90の命令セットに含まれる複数の命令が各処理120a、120bに対応して生成されている。
例えば、処理120aはmov命令からjmplt命令に至る6個の命令で実現され、処理120bはmov命令からjmplt命令に至る10個の命令で実現される。なお、ここでは最初に汎用レジスタx2に入力パラメータ「q」が保存されているものとする。
ここで、処理120bにおける命令「div x2, x2, x1」について考える。この命令は、ソースコード120の処理10bにおける「in[i]/Tbl[i]」に相当する命令である。除数の「Tbl[i]」は、ソースコード120の処理120aにおいて入力パラメータ「q」で除されている。上記の命令「div x2, x2, x1」は入力パラメータ「q」の値の如何を問わずに正しい除算の結果を与える命令である。したがって、アセンブリプログラム124は、どのような入力パラメータ「q」に対しても正しい結果を与える汎用的なコードとなっている。
しかしながら、div命令のような除算を行う命令は、他の命令と比較して実行サイクル数が多い命令である。したがって、div命令は、実行開始してからその結果が得られるまでのスループットが大きく、処理性能の低下を招いてしまう命令である。プロセッサの種類にもよるが、div命令以外の数値演算命令の実行サイクル数は1~5であるのに対し、div命令の実行サイクル数は80程度もあることがある。更に、深層学習や画像処理等ではforループのループ回数が膨大となるため、そのforループの内側にあるdiv命令によってスループットの低下が更に顕著となる。
このようなアセンブリプログラム124をアセンブラが機械語の命令列に翻訳することにより機械語からなる実行可能プログラムが生成されることになる。LLVMのように、コンパイラの種類によっては、プロセッサの種類によらず、仮想的な命令セットを持つプロセッサ向けのアセンブリプログラムを生成することがある。この場合、このアセンブリプログラムを個別のプロセッサ向けの機械語の命令列に変換することもあるが、div命令のような除算命令があるとスループットが低下する点は同じである。
図42は、AOTコンパイラ技術で得られた実行可能プログラムの動作について示す模式図である。
図42に示すように、実行可能プログラム125は、入力データである配列「in」の各要素と入力パラメータ「q」の入力を受け付ける。そして、前述のように入力パラメータ「q」や配列「in」の値の如何を問わずに、実行可能プログラム125は、同一のアセンブリプログラム124から得られた機械語の命令列により処理を行い、その処理の結果を配列「out」の各要素に格納する。
次に、スループットの低下を抑制し得るJITコンパイラ技術を前提としたプログラムについて説明する。
図43は、JITコンパイラ技術を使用したC++の疑似ソースコード126の一例を示す模式図である。
このソースコード126は、その実行結果が図40(a)のソースコード120の実行結果と同一になるように開発者によって記述されたコードであって、処理126aと処理126bとを有する。このうち、処理126aは、ソースコード120の処理120aと同様に、配列「Tbl」の各要素をパラメータ「q」で除する処理である。また、処理126bは、配列「in」の要素を配列「Tbl」の要素で除してそれを配列「out」に格納する処理を行う機械語の命令列を生成する処理である。
その処理126bには、命令の名前であるニーモニックと同じ関数名を有する「mov(x0, i)」等の関数が開発者によって記述される。関数「mov(x0, i)」は、言わばアセンブリ言語の「mov x0, #i」に対応した関数であって、「mov x0, #i」が行う処理を表す機械語をメモリ28に書き込む関数である。なお、アセンブリ言語では変数は記述することができず、「mov x0, #5」や「mov x0, #-128」等のようにアセンブリ言語では固定の値しか指定することができない。JITコンパイラ技術を用いた場合、即値に変数iが使用できる。このことはJITコンパイラ技術の利点の1つである。このように関数名が命令のニーモニックと同一であり、かつその命令が行う処理を表す機械語をメモリに書き込む関数のことを以下ではニーモニック関数と呼ぶ。
処理126bは、i=0~15に対してin[i]/Tbl[i]を実行する機械語の命令列をメモリ28に書き込む処理である。この例では、開発者がswitch文を記述したことにより、除数である配列要素「Tbl[i]」の値に応じて異なるニーモニック関数を使って機械語の命令列が生成される。
例えば、「Tbl[i]」の値が「1」の場合には、「in[i]」に対する除数が「1」となるため、「in[i]」に対して何も行う必要がない。よって、この場合は、「case 1」において「in[i]」の値が格納されている汎用レジスタx1の値に対して演算する機械語の生成は行わず、そのままout[i]へ値を格納する機械語をメモリ28に書き込むのみである。
一方、「Tbl[i]」の値が「2」の場合には、「case 2」においてshiftR命令の機械語の生成に対応した「shiftR(x1, x1, #1)」を実行する。このニーモニック関数は、汎用レジスタx1の内容を1ビットだけ右にシフトし、その結果をレジスタx1に書き込む処理を表す機械語をメモリ28に書き込む関数である。よって、「shiftR(x1, x1, #1)」を実行することにより、汎用レジスタx1に格納されている「in[i]」を2で除したのと等価な処理を行う機械語をメモリ28に書き込むことができる。
また、「Tbl[i]」の値が「4」の場合には、「case 4」において「shiftR(x1, x1, #2)」を実行する。これにより、汎用レジスタx1の内容が右に2ビットだけシフトし、汎用レジスタx1に格納されている「in[i]」を4で除したのと等価な処理を行う機械語をメモリ28に書き込むことができる。
このように、除数の「Tbl[i]」が2のべき乗の値の場合には、shiftR命令に対応するニーモニック関数が実行される。
そして、「Tbl[i]」の値が「1」、「2」、「4」のような2のべき乗ではない場合には、「default」において「div(x1, x1, x2)」を実行する。このニーモニック関数は、div命令に対応した関数であって、汎用レジスタx1の内容を汎用レジスタx2の内容で除した値を汎用レジスタx1に書き込む機械語をメモリ28に書き込む関数である。
このソースコード126によれば、「Tbl[i]」の値が「1」、「2」、「4」のような2のべき乗である場合には、div命令よりも実行サイクル数が少ないshiftR命令に等価な機械語や何もしない機械語がメモリ28に書き込まれる。そして、「Tbl[i]」の値が「1」、「2」、「4」のような2のべき乗でない場合にのみdiv命令に等価な機械語がメモリに書き込まれる。
JITコンパイラ技術では、このように「Tbl[i]」等のパラメータの値に応じて実行サイクル数を低減するのに最適な機械語を書き込むことにより、AOTコンパイラ技術と比較してプログラムの実行速度を高速化することができる。
図44は、ソースコード126をコンパイルして得られた実行可能プログラムの実行中に、処理126bがメモリ28にどのような機械語の命令列を書き込んだかを示す模式図である。なお、その実行可能プログラムを実行する際、入力パラメータ「q」に「8」を与えている。また、図44では、この機械語の命令列を逆アセンブルしたアセンブリプログラム127の疑似コードも併記している。
図44に示すように、q=8の場合には、配列「Tbl」の各要素が先頭から順に「1」、「2」、「4」となる。よって、処理126bのforループ実行に際して、i=0(case 1)、i=1(case 2)、i=2(case 4)の各場合に対応したshiftR関数とstore関数の各々が生成する機械語128がメモリ28内に配置されることになる。そして、その機械語128を逆アセンブルしたコードは、アセンブリプログラム127におけるコード127a、127b、127cとなる。
図45は、実行時に呼び出す関数を、JITコンパイラ技術で実行時に生成する実行可能プログラムの動作について示す模式図である。ここでは、JITコンパイラ技術を用いたソースコード126をコンパイルして得られた実行可能プログラム130の動作について説明する。
図45に示すように、実行可能プログラム130は、まず入力パラメータ「q」の入力を受け付ける(ステップP10)。次いで、実行可能プログラム130は、その入力パラメータ「q」の値に応じて、処理が高速になる機械語128を生成する(ステップP11)。前述の図44の例では、「Tbl[i]」の値に適した機械語128が生成される。
続いて、実行可能プログラム130は、入力データである配列「in」の各要素の入力を受け付けて(ステップP12)、処理の結果を配列「out」の各要素に格納する(ステップP13)。
このとき、機械語128の中にはスループットの遅いdiv命令が含まれていないため、アセンブリプログラム124に対応する実行可能プログラムよりも高速な処理を行うことができる。しかも、このように入力パラメータ「q」の値に応じて適切な機械語128を生成することにより、JITコンパイラ技術ではAOTコンパイラ技術よりもプログラムの実行速度を高速化できる。
ところで、このようなJITコンパイラ技術を使用する場合は、開発者は、図43に示したようなソースコード126を自ら記述することになる。そのソースコード126においてニーモニック関数movやニーモニック関数load等を呼び出すコードはアセンブリ言語の文法に類似している。そのため、ソースコード126等のアプリケーションプログラム用のソースコードを記述するときに、図8~図13に示した第1~第5例に類似の記述ミスが発生することがある。そのような記述ミスについて以下に説明する。
・第1例
図46は、第1例に係る記述ミスについて説明するためのアプリケーションプログラム用のC++のソースコードの模式図である。
このソースコード140では、コードT40の文「vmov(v15.s, 3);」によってニーモニック関数vmovが呼び出されている。そのニーモニック関数vmovの第1引数の「v15.s」は、アセンブリ言語のv15.sに対応した書式である。このニーモニック関数vmovを実行すると、アセンブリ言語のコード「vmov v15.s, 3」と同じ処理を実行するための機械語の命令列をメモリ28に書き込むコードが実行される。また、このコード「vmov v15.s, 3」は、データサイズがシングルワード「s」の4個の要素の各々に整数の即値「3」を格納したベクトルデータをベクトルレジスタv15に書き込むコードである。
一方、コードT41の文「float_multiply(vi.s, vi.s, v15.s);」は、ニーモニック関数float_multiplyを呼び出すための文である。このニーモニック関数float_multiplyを実行すると、アセンブリ言語のコード「float_multiply(vi.s, vi.s, v15.s);」と同じ処理を実行するための機械語の命令列をメモリ28に書き込むコードが実行される。なお、このようにカウンタ変数「i」を用いたforループの内側に「vi.s」等を記述した場合、「vi」の「i」の部分にはカウンタ変数「i」の値「0」、「1」、「2」、…が代入されるものとする。また、このfloat_multiply命令は、第2オペランドと第3オペランドの各々に指定されているベクトルレジスタに格納されている浮動小数同士を乗算し、その結果を第1オペランドのベクトルレジスタに書き込む命令である。
このようにfloat_multiply命令が演算の対象とするデータタイプは浮動小数であるから、float_multiply命令のソースレジスタであるベクトルレジスタv15には浮動小数が書き込まれていなければならない。しかし、この例では、コードT40においてベクトルレジスタv15に整数が書き込まれてしまっているため、このコーディングは誤りである。
・第2例
図47は、第2例に係る記述ミスについて説明するためのアプリケーションプログラム用のC++のソースコードの模式図である。
このソースコード141では、コードT42の文「vmov(v15.b, 3);」によってニーモニック関数vmovが呼び出されている。このニーモニック関数vmovを実行すると、アセンブリ言語のコード「vmov v15.b, 3」と同じ処理を実行するための機械語の命令列をメモリ28に書き込むコードが実行される。また、このコード「vmov v15.b, 3」は、データサイズがバイト「b」の16個の要素の各々に整数の即値「3」を格納したベクトルデータをベクトルレジスタv15に書き込むコードである。
一方、コードT43においては、ニーモニック関数multpilyを呼び出すための文「multiply(vi.s, vi.s, v15.s);」が実行される。このニーモニック関数multiplyを実行すると、アセンブリ言語のコード「multiply vi.s, vi.s, v15.s」と同じ処理を実行するための機械語の命令列をメモリ28に書き込むコードが実行される。このコード「multiply vi.s, vi.s, v15.s」は、ベクトルレジスタvi、v15の各々に格納されているデータサイズがシングルワード「s」の要素同士を乗算し、その結果をベクトルレジスタviの対応する要素に書き込むコードである。これによれば、開発者は、コードT43においてデータサイズがシングルワード「s」のデータ同士の演算を意図していることになる。
しかし、ベクトルレジスタv15には、前述のコードT42によってデータサイズがバイト「b」の要素が書き込まれているため、コードT43を実行すると開発者の意図とは異なる結果が得られてしまう。
このように、先行命令のデスティネーションレジスタのデータサイズと、そのデスティネーションレジスタをソースレジスタとする後続命令の当該ソースレジスタのデータサイズとが異なる記述は誤りである。
・第3例
図48は、第3例と第4例に係る記述ミスについて説明するためのアプリケーションプログラム用のC++のソースコードの模式図である。
このソースコード142では、コードT44において、「i」の値が0~7の各々に対して文「vload(vi.s, inAddr);」が実行される。この文は、vload命令に対応したニーモニック関数vloadを呼び出すための文である。このニーモニック関数vloadを実行すると、「vload vi.s, inAddr」という命令と同じ処理を実行するための機械語の命令列をメモリに書き込むコードが実行される。なお、命令「vload vi.s, inAddr」は、アドレスが「inAddr」のメモリのデータを、ベクトルレジスタviの4個の要素の各々に書き込む命令である。
一方、コードT45においては、「i」の値が0~9の各々に対して文「multiply(vi.s, vi.s, v15.s);」が実行される。但し、i=8やi=9のときのコードT45の第2引数であるベクトルレジスタv8、v9は、前述のコードT44でデータが書き込まれておらず、コードT45の以前にデスティネーションレジスタとして使用されていない。よって、ベクトルレジスタv8、v9にどのようなデータが書き込まれているかが不明であり、ベクトルレジスタv8、v9のデータは不定となる。
このような状態でコードT45を実行しても、デスティネーションレジスタのベクトルレジスタv8、v9に書き込まれるデータも不定となってしまう。よって、このようにデスティネーションレジスタとして使われたことがないレジスタをソースレジスタに指定するのは誤りである。
・第4例
第4例に係る記述ミスについて、図48のソースコード142を引き続き参照しながら説明する。
そのソースコード142のコードT46においては、「multiply v0.s, v1.s, v15.s」という命令と等価な機械語を生成する処理を行う文「multiply(v0.s, v1.s, v15.s);」が実行される。この命令は、ベクトルレジスタv1、v15の各々の要素同士を乗算してその結果をベクトルレジスタv0に書き込む命令である。
ベクトルレジスタv0にはコードT45の結果が書き込まれているが、その結果を一度も使用することなく、コードT46においてベクトルレジスタv0の内容が上書きされてしまっている。これでは、ベクトルレジスタv0に演算結果を書き込んだコードT45の存在意義が不明となり、コードT45又はコードT46におけるレジスタの指定ミスが疑われる。
そのため、このようにデスティネーションレジスタとして使ったレジスタを、その後にソースレジスタとして使うことなく再びデスティネーションレジスタに指定するのは誤りである。
・第5例
図49は、第5例に係る記述ミスについて説明するためのアプリケーションプログラム用のC++のソースコードの模式図である。
このソースコード143では、コードT47においてニーモニック関数vmovを呼び出すための文「vmov(v15.s, 7);」が実行される。このように第2引数に整数が指定された場合、ニーモニック関数vmovは、ベクトルレジスタv15の4個の要素の各々に整数の即値「7」を書き込む命令「vmov v15.s, 7」と等価な処理を行う機械語を生成する関数となる。
また、コードT48においては、第2引数に「3.14」が指定されたニーモニック関数vmovが実行される。この場合は、コードT48におけるニーモニック関数vmovは、ベクトルレジスタv14の4個の要素の各々に浮動小数で表される「3.14」を書き込む命令「vmov v14.s, 3.14」と等価な処理を行う機械語を生成する関数となる。
一方、コードT49においては、整数同士の乗算を行うニーモニック関数multiplyが呼び出されている。このニーモニック関数multiplyに対応したmultiply命令は、二つのソースオペランドの各々のデータタイプが同じであることを前提とする命令である。
しかし、この例では、ベクトルレジスタv15に書き込まれているデータのタイプが整数であるのに対し、ベクトルレジスタv14に書き込まれているデータのタイプは浮動小数となっており、両者は同じではない。
よって、このように二つのソースレジスタに書き込まれているデータのデータタイプが異なる場合もコーディングの誤りとなる。
図50は、前述の第1例~第5例の記述ミスをまとめた図である。
図50に示すように、第1~第3実施形態と同様に、本実施形態においても第1~第5例の各々に対応した例外を定義する。これらのうち、「データタイプ例外」と「データサイズ例外」は、それぞれ第1例と第2例の記述ミスに対応する。また、「W例外」と「R例外」は、それぞれ第3例と第4例の記述ミスに対応する。そして「srcデータタイプ例外」は第5例の記述ミスに対応する。
次に、これらの例外を検出できる情報処理プログラムについて説明する。
本実施形態においても、第1~第3実施形態と同様に、汎用レジスタxn(n=0, 1, …31)とベクトルレジスタvn(n=0, 1, …31)の各々のステータスを記憶したステータス情報を使用する。
図51は、本実施形態に係るステータス情報を表すC++の疑似ソースコードを示す模式図である。
このステータス情報145におけるコードT60~T67の各々は、図33におけるステータス情報110におけるコードT20~T27と同一であるため、ここではその概略を説明するのみとする。
図33を参照して説明したように、コードT61は、ベクトルレジスタvn(n=0, 1, …31)のデータサイズを格納する配列「dataSizeVReg」を宣言するコードである。
また、コードT63は、汎用レジスタxn(n=0, 1, …31)とベクトルレジスタvn(n=0, 1, …31)の各々のデータタイプを格納する配列「dataTypeGReg」、「dataTypeVReg」を宣言するコードである。
そして、コードT65は、汎用レジスタxn(n=0, 1, …31)とベクトルレジスタvn(n=0, 1, …31)の各々がソースレジスタとして使用済みかどうかを示す配列「readAccessGReg」、「readAccessVReg」を宣言するコードである。
更に、コードT67は、汎用レジスタxn(n=0, 1, …31)とベクトルレジスタvn(n=0, 1, …31)の各々がデスティネーションレジスタとして使用済みかどうかを示す配列「writeAccessGReg」、「writeAccessVReg」を宣言するコードである。
図52(a)、(b)は、図51のステータス情報を用いて例外を検出するときの検出ルールについて説明するための模式図である。
このうち、図52(a)は、ニーモニック関数の引数に汎用レジスタxn(n=0,1,2…31)が指定された場合の検出ルールについて模式的に示す図である。また、図52(b)は、ニーモニック関数の引数にベクトルレジスタvn(n=0,1,2…31)が指定された場合の検出ルールについて模式的に示す図である。
図52(a)、(b)の検出ルールは、図34(a)、(b)におけるのと同一であるため、ここではその概略を説明するのみとする。
例えば、図52(a)に示すように、W例外は、ソースレジスタに対応する配列「writeAccessGReg」の要素が「FALSE」であるときに発生する。
また、R例外は、デスティネーションレジスタに対応する配列「writeAccessGReg」の要素が「TRUE」であり、かつデスティネーションレジスタに対応する配列「readAccessGReg」の要素が「FALSE」の場合に検出される。
更に、データタイプ例外は、ソースレジスタに対応する配列「dataTypeGReg」の要素が、命令が演算の対象とするデータタイプと異なる場合に検出される。
そして、srcデータタイプ例外は、1番目と2番目の各々のソースレジスタに対応する配列「dataTypeGReg」の要素同士が異なる場合に検出される。
また、図52(b)に示すように、ニーモニック関数の引数にベクトルレジスタが指定された場合も、汎用レジスタの場合と同様に「W例外」、「R例外」、「データタイプ例外」、及び「srcデータタイプ例外」が検出される。
更に、ベクトルレジスタの場合には、これらの例外の他に「データサイズ例外」も検出される。その「データサイズ例外」は、ニーモニック関数の引数に指定されたソースレジスタに対応する配列「dataSizeVReg」の要素が、当該引数に指定されたデータサイズと異なる場合に検出される。
次に、本実施形態に係るニーモニック関数について説明する。
本実施形態に係るニーモニック関数は、開発者が記述するアプリケーション用のプログラムとは別の情報処理プログラムにおいて定義される。その定義では、以下のような種々の型が用いられる。
図53(a)~(d)と図54(a)~(d)は、ニーモニック関数で使用される種々の型を定義するC++の疑似ソースコードの模式図である。
このうち、図53(a)は、Operand型を定義するソースコードの例である。
Operand型は、メンバ変数として「type」と「value」とを有するクラスである。このうち、「type」には、レジスタや即値等のオペランドの種類が格納される。そして、「value」には、即値やレジスタのインデックス等の数値が格納される。
図53(b)は、AddrReg型を定義するソースコードの例である。
AddrReg型は、アドレスレジスタを示すクラスである。そのクラスのメンバ変数は、アドレスのベース値を保持するレジスタのインデックスを格納する「regIndex」と、アドレスオフセット値である即値を格納する「imm_value」である。なお、「imm_value」の初期値は0とする。
図53(c)は、Imm型を定義するソースコードの例である。
Imm型は、符号付き整数即値を示すクラスである。そのクラスのメンバ変数は、符号付き整数即値を格納する「imm_value」である。
図53(d)は、UnsignedImm型を定義するソースコードの例である。
UnsignedImm型は、符号無し整数即値を示すクラスである。そのクラスのメンバ変数は、符号無し整数即値を格納する「imm_value」である。
また、図54(a)~(d)は、それぞれVRegB型、VRegH型、VRegS型、VRegD型を定義するソースコードの例である。これらの型は、ベクトルレジスタに格納されるベクトルデータの要素のデータサイズを示すクラスである。例えば、VRegB型とVRegH型は、それぞれバイトとハーフワードに対応する。そして、VRegS型とVRegD型は、それぞれシングルワードとダブルワードに対応する。なお、これらのクラスのメンバ変数は、いずれもデータサイズを示す符号無し整数の「regIndex」である。
次に、本実施形態に係るニーモニック関数の定義について説明する。
ニーモニック関数は、命令セットに含まれる全ての命令に対応して定義される。例えば、命令セットにmultiply命令、add命令、load命令、及びstore命令等があれば、これらに対応してニーモニック関数multiply、ニーモニック関数add、ニーモニック関数load、及びニーモニック関数storeが定義される。以下では、これらのニーモニック関数の一部について説明する。
図55及び図56は、ニーモニック関数multiplyを定義するC++のソースコードが記述されたソースファイル150の模式図である。
図55及び図56に示すように、このソースファイル150には開発者によってコードT70~T83が記述される。
このうち、コードT70は、ニーモニック関数multiplyが受け取る引数「dst」、「src0」、及び「src1」を宣言するコードである。これらの引数のうち、「dst」は、multiply命令の第1オペランドであるデスティネーションレジスタを示す。また、「src0」と「src1」は、それぞれmultiply命令の第2オペランドと第3オペランドのソースレジスタを示す。
一方、コードT71の文「nm = “multiply_VRegB”;」は、命令とそのオペランドのデータサイズとを識別する文字列「multiply_VRegB」を変数nmに代入する文である。ここでは、文字列「multiply_VRegB」における「multiply」により、ニーモニック関数multiplyがmultiply命令に対応することが一意に識別される。また、文字列「multiply_VRegB」における「VRegB」により、ソースレジスタとデスティネーションレジスタのデータサイズがバイト「b」であることが一意に識別される。
また、コードT72は、Operand型の変数「op0」、「op1」、及び「op2」を宣言して、その各々のメンバ変数に所定の値を代入するコードである。ここでは、変数「op0」、「op1」、及び「op2」は、それぞれ「dst」、「src0」、及び「src1」に対応するものとする。
この場合、変数「op0」は、デスティネーションレジスタに対応することになる。そのため、変数「op0」のメンバ変数「type」には「REGISTER」が代入され、メンバ変数「value」にはデスティネーションレジスタのインデックスを表す「dst.regIndex」が代入される。
同様に、変数「op1」は1番目のソースレジスタに対応するため、そのメンバ変数「type」には「REGISTER」が代入され、「value」には1番目のソースレジスタのインデックスを表す「src0.regIndex」が代入される。
更に、変数「op2」は2番目のソースレジスタに対応するため、そのメンバ変数「type」には「REGISTER」が代入され、「value」には2番目のソースレジスタのインデックスを表す「src1.regIndex」が代入される。
そして、コードT73は、配列「oplist」に上記の変数「op0」、「op1」、及び「op2」を代入する文である。
一方、コードT74~T78は、前述の図52(a)、(b)の検出ルールに従って例外を検出するコードである。例えば、コードT74とコードT75は、それぞれ「R例外」と「W例外」を検出するコードである。また、コードT76とコードT77は、それぞれ「データサイズ例外」と「データタイプ例外」を検出するコードである。そして、コードT78は「srcデータタイプ例外」を検出するコードである。
なお、これらのコードT74~T78においては、例外が検出された場合にエラーを出力する文も含まれる。例えば、コードT74においてR例外が検出された場合には、エラーとして“Invalid register use. Data on destination register is not used.”という文字列が標準出力に出力される。
そして、上記のコードT74~T78において例外が検出されなかった場合にはコードT79~T83が実行される。
このうち、コードT79は、MachineCodeEmitter関数を呼び出し、そのMachineCodeEmitter関数の返り値を関数writeでメモリ28に書き込むコードである。MachineCodeEmitter関数は、変数「nm」と変数「oplist」とを引数として受け取り、変数「oplist」で表されるオペランドに対して変数「nm」で表される命令が行う処理を表す機械語を生成する関数である。
なお、MachineCodeEmitter関数は、プロセッサ90のアセンブラプログラムと共に作成された動作が検証済の関数である。
そして、コードT80~T83は、ステータス情報145を更新するコードである。
このうち、コードT80は、配列「writeAccessVReg」の要素を更新するコードである。この例では、multiply命令を実行することによりデスティネーションレジスタが使用済みとなるため、そのデスティネーションレジスタに対応した配列「writeAccessVReg」の要素に「TRUE」が格納される。
また、コードT81は、配列「dataSizeVReg」の要素を更新するコードである。この例では、コードT70において、ニーモニック関数multiplyの第1引数の型が「VRegB」となっており、multiply命令のデスティネーションレジスタのデータサイズがバイト「b」であることが特定されている。よって、コードT81においては、バイト「b」を示す「sizeB」が、そのデスティネーションレジスタに対応した配列「dataSizeVReg」の要素に格納される。
そして、コードT82は、配列「dataTypeVReg」の要素を更新するコードである。multiply命令においては、二つのソースレジスタと同じデータタイプのデータがデスティネーションレジスタに書き込まれる。よって、この例では、デスティネーションレジスタに対応した配列「dataTypeVReg」の要素に、1番目のソースレジスタのデータタイプを格納している。
また、コードT83は、配列「readAccessVReg」の要素を更新するコードである。ニーモニック関数multiplyを実行すると、コードT70で指定された二つの引数「src0」、「src1」が示す二つのソースレジスタが使用済みとなる。そのため、コードT83においては、これら二つのソースレジスタに対応した配列「readAccessVReg」の各々の要素に、ソースレジスタとして使用済みであることを示す「TRUE」が格納される。
以上のように、この例では、コードT74~T78により「R例外」、「W例外」、「データサイズ例外」、「データタイプ例外」、及び「srcデータタイプ例外」を検出できる。
なお、前述のように、ニーモニック関数は、命令セットに含まれる全ての命令に対応して定義される。次に、ニーモニック関数multiply以外のニーモニック関数のソースコードの例について説明する。
図57~図69は、前述のソースファイル150に記述された種々のニーモニック関数を定義するC++の疑似ソースコードを模式的に示す図である。
例えば、図57及び図58は、ニーモニック関数float_multiplyを定義するC++のソースコードが記述されたソースファイル150の模式図である。また、図59~図69は、vload命令、vadd命令、vstore命令、cvtssBtoH命令、vmov命令、及びcvtFloatSigned命令の各々に対応したニーモニック関数を定義するソースコードが記述されたソースファイル150の模式図である。
なお、図65のニーモニック関数cvtssBtoHはcvtssBtoH命令に対応したニーモニック関数である。そのcvtssBtoH命令は、ソースレジスタに格納されているMSB(Most Significant Bit)側の8個の符号付き8ビットデータを、符号付き16ビットデータに変換してデスティネーションレジスタに格納する命令である。
そして、図69のニーモニック関数cvtFloatSignedは、cvtFloatSigned命令に対応したニーモニック関数である。そのcvtFloatSigned命令は、ソースレジスタに格納されている32ビットの浮動小数を、32ビットの符号付き整数に変換してデスティネーションレジスタに格納する命令である。
また、図57~図69の各々において、図55と図56で説明したのと同種のコードにはこれらの図におけるのと同じ符号を付し、その説明は省略する。
命令によって発生する例外に相違はあるものの、図57~図69に示す種々のニーモニック関数においてもコードT74~T78により例外を検出することができる。
なお、ニーモニック関数の種類によっては、このように例外を検出する機能を無効にできると便利な場合がある。例えば、実行可能プログラムの実行に先立って汎用レジスタx0を0クリアするxor(x0,x0,x0)というニーモニック関数がある。この関数は、ソースレジスタにデータが書き込まれているかどうかによらず汎用レジスタx0を0クリアする関数であるため、W例外が検出されて実行されないと汎用レジスタx0を0クリアできなくなってしまう。
その場合には、以下のようにニーモニック関数が例外を検出する機能を抑制すればよい。
図70(a)、(b)は、例外を検出する機能を抑制する方法について模式的に示す図である。
この例では、図70(a)のように例外を検出する機能を備えたニーモニック関数xorと、図70(b)のように当該機能を備えていないニーモニック関数xor_without_checkの各々を開発者がソースファイル150に記述する。
このうち、図70(a)のニーモニック関数xorでは、前述のコードT74~T78によって例外のチェックが行われ、コードT80~T83によってステータス情報145の更新が行われる。一方、図70(b)のニーモニック関数xor_without_checkではコードT74~T78、T80~T83がなく、例外のチェックとステータス情報145の更新が行われない。
なお、ニーモニック関数xorとニーモニック関数xor_without_checkのいずれにおいても、xor命令の機械語をメモリ28に書き込むコードT79がソースファイル150に記述される。
そして、例外を検出する機能を使用したい場合には、開発者が、図60(a)のニーモニック関数xorを呼び出すコードをアプリケーションプログラム用のソースファイルに記述する。
一方、例外を検出する機能を使用したくない場合には、開発者が、図60(b)のニーモニック関数xor_without_checkを呼び出すコードをアプリケーションプログラムのソースファイルに記述すればよい。
なお、このように二種類のニーモニック関数を用意するのではなく、一つのニーモニック関数で例外を検出する機能を抑制できるようにしてもよい。
図71は、このように例外を検出する機能を抑制することができるニーモニック関数のC++の疑似ソースコードの模式図である。
図71に示すように、この例では、ニーモニック関数xorの引数に、例外を検出する機能を有効にするかどうかを指定する引数「no_check」を追加する。
そして、引数「no_check」の値が「0」の場合には、前述のコードT74~T78によって例外のチェックが行われ、コードT80~T83によってステータス情報145の更新が行われる。一方、引数「no_check」の値が「0」以外の場合には、例外のチェックとステータス情報の更新とが行われない。
これにより、アプリケーションプログラム用のソースファイルでニーモニック関数xorを呼び出すときに開発者が引数「no_check」の値を指定することにより、簡単に例外を検出する機能を有効にしたり無効にしたりできる。
また、次のようにグローバル変数で例外を検出する機能を抑制するようにしてもよい。
図72(a)は、例外を検出する機能を抑制するためのグローバル変数「g_check_on」をニーモニック関数xorの内部に記述したときのソースファイル150の模式図である。
この例では、グローバル変数「g_check_on」の値が「1」かどうかを判定するif文を開発者がソースファイル150に記述する。そして、開発者は、そのif文の内部に、例外のチェックを行うコードT74~T78や、ステータス情報145の更新を行うコードT80~T83を記述する。
図72(b)は、このニーモニック関数xorを利用したアプリケーションプログラム用のソースファイル152のC++の疑似ソースコードを模式的に示す図である。
ここでは、開発者が、複数のニーモニック関数xorを呼び出すコード152aをソースファイル152に記述した場合を想定する。そして、開発者が、これらのニーモニック関数xorが例外を検出する機能を無効にしたいと考えているものとする。この場合は、開発者は、コード152aの前の位置にdisable_check関数を記述する。disable_check関数は、グローバル変数「g_check_on」の値を「0」にセットする関数である。
これにより、コード152aのニーモニック関数xor(x0,x0,x0)とニーモニック関数xor(x1,x1,x1)の各々のコードT74~T78が実行されず、これらのニーモニック関数が例外を検出する機能を無効にすることができる。
また、このコード152aの後のコード152bで例外を検出する機能を有効にしたい場合には、開発者は、コード152aの後の位置にenable_check関数を記述すればよい。enable_check関数は、グローバル変数「g_check_on」の値を「1」にセットする関数である。
これにより、コード152aのニーモニック関数xor(x0,x0,x0)とニーモニック関数xor(x1,x1,x1)の各々のコードT74~T78が実行され、これらのニーモニック関数が例外を検出する機能を有効にすることができる。
このようにdisable_check関数とenable_check関数とを利用することにより、ニーモニック関数が例外を検出する処理を無効にしたり、ニーモニック関数が例外を検出する処理を有効にしたりすることができる。なお、disable_check関数とenable_check関数の各々の定義は、例えば開発者がソースファイル150に記述しておけばよい。
次に、図55~図69のコードT79におけるMachineCodeEmitter関数について説明する。
図73は、MachineCodeEmitter関数のC++の疑似ソースコードが記述されたソースファイル151の一例を示す模式図である。そのソースファイル151は、アセンブラプログラム自身のソースファイルの一部でもよい。
この例では、コードT90~T93によりMachineCodeEmitter関数の機能が実現される。このうち、コードT90は、変数「mnemonic」と変数「op0」、「op1」、「op2」の各々を32ビットの符号無し整数として宣言する文である。
また、コードT91は、MachineCodeEmitter関数が引数として受け取った変数「nm」の内容に対応したオペコードを変数「mnemonic」に代入するコードである。例えば、変数「nm」で特定されるニーモニックが「mov」の場合には、mov命令のオペコード「0x01000000」が変数「mnemonic」に代入される。
そして、コードT92は、変数「nm」の内容に応じて変数「op0」、「op1」、「op2」の各々に対してビット操作を行うことにより、命令の仕様で定められたビット位置にこれらの変数を位置させるコードである。例えば、mov命令の場合には、32ビットの内の17~24ビットに第1オペランドが位置し、8~16ビットに第2オペランドが位置する。そこで、mov命令の場合には、文「op0=oplist[0]<<16;」を実行することにより、32ビットの内の17~24ビットに変数「op0」のビット列を位置させる。一方、変数「op1」については、文「op1=oplist[1]<<8;」を実行することにより、32ビットの8~16ビット目に「op1」のビット列を位置させる。なお、mov命令は第3オペランドをとらないため、文「op2=0;」により変数「op2」を「0」にする。
更に、コードT93は、各変数「mnemonic」、「op0」、「op1」、「op2」の各々を上位ビットから順に連結したビット列を生成し、それを返り値として返す文である。そのビット列は、変数「oplist」で特定されるオペランドに対して変数「nm」で特定される命令が行う処理を表す機械語である。
このように、MachineCodeEmitter関数は、引数「nm」で表される命令が、引数「oplist」で表されるオペランドに対して行う処理を表す機械語を生成する関数である。
プロセッサ90を開発する際には、そのプロセッサ90で動作する機械語の実行可能プログラムを生成するためのツール群も開発される。そのツール群には、C言語やC++言語で記述されたソースファイルをアセンブリ言語に変換するためのコンパイラや、アセンブリ言語を機械語に変換するためのアセンブラプログラムが含まれる。そのようなツール群としては、例えばLLVMがある。MachineCodeEmitter関数は、LLVMのアセンブラプログラムに内蔵されている関数であり、アセンブラプログラムを開発した際にその動作が検証されて提供されている。そのため、本実施形態では、ニーモニック関数が正しい機械語を生成しているかという動作検証を行う必要がなく、開発者の負担減を実現できる。
開発者は、上記のようにニーモニック関数が定義されたソースファイル150を利用することにより、プロセッサで実行する様々なアプリケーションプログラムを開発することができる。そこで、そのアプリケーションプログラムの開発環境について以下に説明する。
図74は、ニーモニック関数が定義されたソースファイル150を利用した開発環境について示す模式図である。
この例では、開発者が、例えばC++を用いてアプリケーションプログラム用のソースファイル152を作成する。そのソースファイル152は、JITコンパイラ技術の機能を使用することを前提としたファイルであって、C++のライブラリ関数に加えて、ソースファイル150にあるニーモニック関数を呼び出す記述を含む。
そして、開発者の指示の下で、コンパイラ、アセンブラプログラム、及びリンカのプログラム群153がビルドを行う。そのビルドの際、プログラム群153に含まれるコンパイラがソースファイル152をコンパイルする。
このとき、コンパイラは、各ソースファイル150、151、152を読み込んでアセンブリ言語の中間言語ファイルを出力する。これらのソースファイルのうち、ソースファイル151は、前述のMachineCodeEmitter関数が記述されたソースファイルである。そして、アセンブラプログラムがその中間言語ファイルを機械語の命令列に変換してオブジェクトファイルを生成する。
その後、リンカは、オブジェクトファイルと種々のライブラリとをリンクすることにより、プロセッサ90で実行可能なバイナリ形式の実行可能プログラム154を生成する。
なお、機械語の生成ルールが秘匿されているようなプロセッサでは、アセンブラプログラムのソースファイルが公開されていないものの、実行ライブラリファイルが入手可能なこともある。その場合には、ソースファイル151に代えて機械語生成関数の機能を予め機械語の命令列に変換済みの実行ライブラリファイル151aを入力として用い、これをリンクすることにより実行ライブラリファイル151aを生成すればよい。
以上により、アプリケーションプログラム用のソースファイル152から実行可能プログラム154を生成することができる。
本実施形態では、図55~図69に示したように、各ニーモニック関数を定義するソースファイル150に、図52(a)、(b)の検出ルールに従って例外を検出するコードT74~T78が記述されている。そのため、図52(a)、(b)の検出ルールに当てはまるような記述ミスがアプリケーションプログラム用のソースファイル152にあると、実行可能プログラム154の実行時にエラーが出力される。これにより、ソースファイル152に記述ミスがあることに開発者が気付くことができ、開発者がソースファイル152のデバッグをするのが容易となる。
次に、この実行可能プログラム154を実行するときの情報処理装置117の動作について説明する。
図75は、実行可能プログラム154を得るためのアプリケーションプログラム用のソースファイル152に記述されているC++の疑似ソースコードの模式図である。
図75に示すように、この例では、mov命令、load命令、add命令、及びstore命令の各々に対応したニーモニック関数がソースファイル152に記述されている場合を想定する。
図76は、このソースファイル152をコンパイルして得られた実行可能プログラム154を実行するときの情報処理装置117の動作について示すフローチャートである。
まず、情報処理装置117は、ステータス情報145(図51参照)を初期化する(ステップS61)。
例えば、情報処理装置117は、配列「dataSizeVReg」、「dataTypeGReg」、及び「dataTypeVReg」の全ての要素に「CLEAN」を格納する。更に、情報処理装置117は、配列「readAccessGReg」、「readAccessVReg」、「writeAccessGReg」、及び「writeAccessVReg」の全ての要素に「FALSE」を格納する。
次に、情報処理装置117は、実行可能プログラム154に記述されているニーモニック関数の実行処理を行う(ステップS62)。この処理は、図75の実行可能プログラム154に記述されている複数のニーモニック関数ごとに行われる。
例えば、図75のコードT85に記述されているニーモニック関数load(v2.b, x0)の実行処理を行うと、そのニーモニック関数load(v2.b, x0)の内部でMachineCodeEmitter関数が呼び出される。そして、そのMachineCodeEmitter関数が、アセンブリ言語のコード「load v2.b, x0」に相当する機械語128(図45参照)を生成し、それをメモリ28に書き込む。
次に、情報処理装置117は、ステップS62で生成した機械語128を呼び出し(ステップS63)、それを実行する(ステップS64)。
以上により、実行可能プログラム154を実行するときの情報処理装置117の処理を終える。
次に、上記したステップS62のニーモニック関数の実行処理について説明する。
図77は、ニーモニック関数の実行処理をするときの情報処理装置117の機能構成図である。
図77に示すように、情報処理装置117は、制御部171と記憶部172とを備える。
このうち、記憶部172は、メモリ28により実現される機能ブロックであり、前述のステータス情報145を記憶する。
一方、制御部171は、例外検出部175、エラー出力部176、ステータス更新部177、機械語生成部178、及び書き込み部179を有する。
このうち、例外検出部175は、図55~図69のコードT74~T78によって実現される処理部であって、ニーモニック関数を実行したときに発生する例外をステータス情報145に基づいて検出する。
例えば、図75のコードT85に記述されているニーモニック関数vload(v2.b, x0)の実行処理を行う場合を考える。この場合は、図59のコードT74において、ステータス情報145の一部である配列writeAccessVReg[dst.regIndex]と配列readAccessVReg[dst.regIndex]に基づいてR例外が検出される。なお、変数「dst」には、ニーモニック関数vload(v2.b, x0)の第1引数の「v2.b」が格納されている。
更に、この場合は、図59のコードT75において、ステータス情報145の一部である配列writeAccessVReg[addr.regIndex]に基づいてW例外が検出される。なお、変数「addr」には、ニーモニック関数vload(v2.b, x0)の第2引数の「x0」が格納されている。
一方、エラー出力部176は、例外検出部175が例外を検出したときにエラーを出力する処理部である。本実施形態では、図55~図69のコードT74~T78の各々に記述されているthrow文によりエラー出力部176が実現される。図75のコードT85に記述されているニーモニック関数vload(v2.b, x0)の実行処理を行う例では、図59のコードT74におけるthrow文によってエラーが出力される。図59に示すように、そのエラーの内容は、「“Invalid register use. Data on destination register is not used.”」という文字列である。また、図59のコードT75を実行した場合には、「“Invalid register use. No data written on source register.”」という文字列がエラーとして出力される。
そして、ステータス更新部177は、例外検出部175が例外を検出しなかったときにステータス情報145の内容を更新する処理部である。そのステータス更新部177の機能は、図55~図69のコードT80~T83によって実現される。
更に、機械語生成部178は、図55~図69のコードT79におけるMachineCodeEmitter関数によって実現される処理部である。前述のように、MachineCodeEmitter関数は、ニーモニック関数に対応する命令がオペランドに対して行う処理を表す機械語128を生成する関数である。
そして、書き込み部179は、図55~図69のコードT79におけるwrite関数によって実現される処理部であって、機械語生成部178が生成した機械語の命令列をメモリ28に書き込む処理部である。
次に、図77に示した情報処理装置117の各部が実行する情報処理方法について説明する。
図78は、本実施形態に係る情報処理方法のフローチャートである。
まず、例外検出部175が、例外を検出する機能が有効かどうかを判断する(ステップS71)。例えば、例外検出部175は、図71の引数「no_check」の値が「0」の場合には当該機能が有効であると判断し、引数「no_check」の値が「0」以外の場合には当該機能が有効ではないと判断する。
また、開発者が図72(b)の関数disable_check()をソースファイル150に記述している場合は、例外検出部175は、グローバル変数「g_check_on」の値に応じて例外を検出する機能が有効かどうかを判断する。例えば、例外検出部175は、グローバル変数「g_check_on」の値が「1」の場合には当該機能が有効であると判断し、グローバル変数「g_check_on」の値が「1」以外の場合には当該機能が有効ではないと判断する。
ここで、例外を検出する機能が有効ではない(ステップS71:否定)と判定された場合にはステップS76に移る。
ステップS76においては、機械語生成部178が機械語128を生成する。例えば、図75のコードT85に記述されているニーモニック関数vload(v2.b, x0)を実行する場合を考える。この場合は、機械語生成部178は、ニーモニック関数vload(v2.b, x0)に対応したアセンブリ言語の命令「vload v2.b, x0」がオペランドの「v2.b」、「x0」に対して行う処理の機械語128を生成する。
そして、書き込み部179が機械語128をメモリ28に書き込む(ステップS77)。
一方、例外を検出する機能が有効である(ステップS71:肯定)と判定された場合にはステップS72に移る。
ステップS72においては、例外検出部175が例外検出処理を行う。その例外検出処理については後述する。
そして、例外検出部175が、例外を検出したかどうかを判定する(ステップS73)。
ここで、例外を検出したと判定された場合(ステップS73:肯定)にはステップS74に移る。
ステップS74においては、エラー出力部176がエラーを出力し、処理を終える。
一方、例外を検出しなかったと判定された場合(ステップS73:否定)にはステップS75に移り、ステータス更新部177がステータス情報145を更新する。
この後は、前述のステップS76とステップS77を実行し、処理を終える。
次に、ステップS72の例外検出処理について説明する。
図79は、ステップS72の例外検出処理のフローチャートである。
この例外検出処理は、図52(a)、(b)の検出ルールに基づいて、以下のように例外検出部175が実行する。
まず、例外検出部175が、データタイプ例外があるかどうかをチェックする(ステップS81)。このチェックは、図55~図69のコードT77を実行することにより行われる。コードT77に記述されているように、例外検出部175は、ニーモニック関数に対応した命令が演算の対象とするデータタイプと、ニーモニック関数の引数のソースレジスタに実際に書き込まれているデータタイプと異なるかを判断する。
例えば、図58のコードT77では、1番目のソースレジスタに実際に書き込まれているデータタイプ「dataTypeVReg[src0.regIndex]」が、float_multiply命令が演算の対象とするデータタイプ「typeFloat」と異なるかが判断される。また、このコードT77では、2番目のソースレジスタに書き込まれているデータのデータタイプ「dataTypeVReg[src1.regIndex]」が、float_multiply命令が演算の対象とするデータタイプ「typeFloat」と異なるかも判断される。
そして、命令が演算の対象とするデータタイプとソースレジスタに実際に書き込まれているデータのデータタイプとが異なる場合に、例外検出部175は、データタイプ例外を検出する。
次に、例外検出部175が、データサイズ例外があるかどうかをチェックする(ステップS82)。このチェックは、図55~図69のコードT76を実行することにより行われる。コードT76に記述されているように、例外検出部175は、ニーモニック関数の引数のソースレジスタに実際に書き込まれているデータのデータサイズと、当該引数に指定されたデータサイズとが異なるかを判断する。
例えば、図57のコードT76では、1番目のソースレジスタに書き込まれているデータのデータサイズ「dataSizeVReg[src0.regIndex]」が、引数に指定されたデータサイズ「sizeS」と異なるかが判断される。また、このコードT76では、2番目のソースレジスタに書き込まれているデータのデータサイズ「dataSizeVReg[src1.regIndex]」が、引数に指定されたデータサイズ「sizeS」と異なるかも判断される。
そして、ソースレジスタに実際に書き込まれているデータのデータサイズと引数に指定されたデータサイズとが異なる場合に、例外検出部175は、データサイズ例外を検出する。
次いで、例外検出部175が、W例外があるかどうかをチェックする(ステップS83)。このチェックは、図55~図69のコードT75を実行することにより行われる。コードT75に記述されるように、例外検出部105は、配列「writeAccessGReg」、「writeAccessVReg」の各要素のうち、ニーモニック関数の引数に指定されたソースレジスタに対応する要素が「FALSE」の場合にW例外を検出する。
例えば、図57のコードT75では、1番目のソースレジスタに対応した「writeAccessVReg[src0.regIndex]」が「TRUE」でないかどうかが判断される。同様に、このコードT75では、2番目のソースレジスタに対応した「writeAccessVReg[src1.regIndex]」が「TRUE」でないかどうかも判断される。
そして、「writeAccessVReg[src0.regIndex]」が「TRUE」でない場合や、「writeAccessVReg[src1.regIndex]」が「TRUE」でない場合に、例外検出部105がW例外を検出する。
続いて、例外検出部175が、R例外があるかどうかをチェックする(ステップS84)。このチェックは、図55~図69のコードT74を実行することにより行われる。コードT74に記述されるように、ニーモニック関数の引数に指定されたレジスタがベクトルレジスタの場合は、例外検出部175は、配列「writeAccessVReg」と配列「readAccessVReg」を使用してチェックを行う。例えば、例外検出部175は、これらの配列「writeAccessVReg」、「readAccessVReg」の各要素のうちで、ニーモニック関数の引数に指定されたデスティネーションレジスタに対応する要素を使用する。そして、例外検出部175は、その配列「writeAccessVReg」の要素が「TRUE」であり、かつ配列「readAccessVReg」の要素が「FALSE」の場合にR例外を検出する。
例えば、図57のコードT74においては、「writeAccessVReg[dst.regIndex]」が「TRUE」であり、かつ「readAccessVReg[dst.regIndex]」が「TRUE」でない場合に、例外検出部175がR例外を検出する。
なお、デスティネーションレジスタが汎用レジスタの場合にも、例外検出部175は、配列「writeAccessGReg」と配列「readAccessGReg」のそれぞれの要素を利用してR例外を検出する。
次に、例外検出部175が、srcデータタイプ例外があるかどうかをチェックする(ステップS85)。このチェックは、図55~図69のコードT78を実行することにより行われる。コードT78に記述されるように、ニーモニック関数の引数に指定されたレジスタがベクトルレジスタの場合は、例外検出部175は、配列「dataTypeVReg」を使用してチェックを行う。例えば、例外検出部175は、ニーモニック関数の引数に指定された二つのソースレジスタの各々に対応する配列「dataTypeVReg」の要素が同一かどうかを判断し、同一でない場合にsrcデータタイプ例外を検出する。
図56のコードT78では、1番目のソースレジスタに対応した「dataTypeVReg[src0.regIndex]」と、2番目のソースレジスタに対応した「dataTypeVReg[src1.regIndex]」とが等しくない場合に、例外検出部175がsrcデータタイプ例外を検出する。
なお、ソースレジスタが汎用レジスタの場合にも、例外検出部175は、「dataTypeGReg」を利用してsrcデータタイプ例外を検出する。
以上により、例外検出処理を終える。
上記した本実施形態によれば、ニーモニック関数を定義するソースファイル150に、開発者が、図52(a)、(b)の検出ルールに従って例外を検出するコードT74~T78を記述する。そのため、アプリケーションプログラム用のソースファイル152に記述ミスがあった場合に、その記述ミスに起因した例外を例外検出部175が検出し、更にエラー出力部176がエラーを出力する。そのエラーに基づいて、開発者がソースファイル152を容易にデバッグすることができるようになり、プログラム開発の効率化を図ることができる。
更に、このように記述ミスに起因した例外を例外検出部175が検出することで、記述ミスのある実行可能プログラム154をプロセッサ90で無駄に実行する時間が減る。その結果、プロセッサ90やメモリ28等のハードウェア資源の無駄な消費を改善できる。
しかも、上記のコードT74~T78は、ニーモニック関数の引数が示すレジスタのステータス情報145(図51参照)に基づいて例外を検出するコードである。そのステータス情報145における各配列を使用することにより、例外検出部175が、「R例外」、「W例外」、「データタイプ例外」、「データサイズ例外」、及び「srcデータタイプ例外」を検出することができる。そして、これらのどの例外が検出されたかに応じ、開発者が、アプリケーションプログラム用のソースファイル152における具体的な記述ミスを特定することができる。