RISC-Vの命令セット

RISC-Vの命令セットの概要

前回は、RISC-Vの発展の背景や、RISC-Vの特徴、様々な情報源について紹介しました。今回は、RISC-Vを学ぶにあたって最も基本となる命令セットについて紹介したいと思います。

RISC-Vの命令セットの特徴「シンプルかつ、独立性が高い」

RISC-Vの命令セットの特徴について聞かれるとき、まず真っ先に「シンプルである」と答えると思います。

実際、x86やArmなどのアーキテクチャに比べて命令セットは綺麗にまとめられており、あまり使うことのない無駄な命令も定義されていません。その引き合いに出される例として、RISC-V FoundationのChairman of the BoardであるKrste Asanovic教授がよく言及している命令があります。

x86命令には"AAA"命令という、誰が使うのか分からない、非常にレアケースの命令があります。RISC-Vには、このような非常に複雑で、ほぼ使われることのない命令は定義されていません。

このようにRISC-VはRISC(Reduced Instruction Set Computer)の名が示すとおり、極めてシンプルで分かりやすい命令セットであると言えます。

図図:x86のAAA命令。RISC-Vはシンプルな命令群のみで構成されており、このような複雑な命令は存在しない(RISC-V Tutorial Introductionより抜粋)

シンプルが故に命令数は大きくなりがち、それをマイクロアーキテクチャでカバーする

しかし一方で、複雑な命令が存在しない分、同じプログラムをコンパイルするとトータルで使用する命令数は大きくなりがちです。

下のグラフを見てください。これはRISC-Vの命令セットの効率性を示す、よく登場してくるグラフです。x軸はベンチマークの名前、y軸はベンチマークプログラムを実行するのに必要な命令のバイト数です。

コードサイズの比較を見てください。RV32やRV64は、比較対象となるx86/Armv8と比べると大きいですね?つまり、シンプルであるがゆえに、アプリケーションをコンパイルした際の命令コードサイズは大きくなりがちであると言う特徴が見えてきます。

これに対して、RISC-Vは2つの方法で命令コードサイズを小さくしています。

図図. ベンチマークプログラムを各種ISAでコンパイルした場合、実際に実行される命令バイト数の比較。x86-64を1.0として比較(RISC-V ISA Shootoutより抜粋)>

1. Compresed Instructionの利用

RISC-Vのよりコンパクトな命令セット群であるCompressed Instructionを使用することでトータル命令長を抑えることで対策しています。上記のグラフにおける、左端の項目(RV32C、RV64C)の項目がそれにあたります。

Compressed Instructions仕様は16ビット長の命令セット(デフォルトはRISC-Vの命令セットは32ビット長)を使用することで、コードサイズを削減しようとしています。

2. マイクロアーキテクチャ(実装)による解決

たとえ命令長が長くなったとしても、実際にプロセッサ上で実行する場合にはMicro Fusionという手法を用いて実行命令数を削減することを想定しています。

例えば、add命令と、次にadd命令の結果を、メモリアドレスとして使用するload命令が並んでいる場合、これを1つの命令としてデコードすることで命令発行のバンド幅を稼ぐことができ、性能向上につなげることができます。

例えば、以下のプログラムは、デコード時に1つの命令として解釈され、LSUで実行されるとします。すると、add命令のために確保されたALUは解放されることになり、後続の命令を発行することで性能を稼ぐことができます。

					add rd, rs1, rs2
					ld rd, 0(rd)
					

RISC-Vの命令セットの解説

RISC-Vのアーキテクチャレベルの定義方法

ここからは、RISC-Vの命令セットについて簡単に解説していきます。分かっている人や、「ISAなんて興味なくて、とにかくアプリケーションを開発したいんだ!」という方には退屈かもしれません。少々お付き合いください。

RISC-Vの命令セットは、"基本命令"とそれに付随する"拡張命令"によって構成されています。基本命令はその名の通り最も基本的な命令セットで、これに対して拡張命令は基本命令の上に拡張形式で載せていく命令セットです。

基本命令は、以下の4つが定義されています。

  • RV32I(32bitアドレッシング・整数命令)
  • RV32E(32bitアドレッシング・組み込み向け命令)
  • RV64I(64bitアドレッシング・整数命令)
  • RV128I(128bitアドレッシング・整数命令)

これに対して、拡張命令はM、A、F、D、Q、L、C、B、J、T、P、V、Nが定義されています。詳細は割愛しますが、例えば"M"は乗除算命令、"A"はアトミック命令など、カテゴリ毎に命令群が定義されています。

図図:RISC-VのISAは基本命令と拡張命令に分類される

アーキテクチャは32bitアドレッシングモードから128bitアドレッシングモードまで定義

RISC-Vが先進的である一つの理由として、デフォルトでアドレッシングモードが32bit、64bit、128bitの3種類が用意されているということです。32bit、64bitは最近では一般的になってきましたが、128bitのアドレッシングモードが定義されているのは珍しいのではないでしょうか。

それぞれのアドレッシングモードに応じて汎用レジスタの大きさも決まっています。32bit、64bit、128bitのアドレッシングモードで、それぞれレジスタ幅は32bit、64bit、128bitです。このアドレッシングモードの長さは、ISAマニュアルではXLENと呼ばれ、ISAの重要なパラメータの一つです。整数演算に使用するレジスタの本数は32本(ただしRV32Eは16本)で、そのうち最初の1本(x0)は常に0で固定です。

図図:RISC-Vのレジスタセット(整数演算系)

RISC-Vはその名の冠する通りRISC命令セットですので、データの操作は基本的にレジスタを介して実行されます。メモリアクセス命令はレジスタに対して実行され、演算命令、分岐命令などもレジスタの値を元に実行されます。

浮動小数点演算命令

RISC-Vは浮動小数点演算レジスタと、それに対応する浮動小数点演算命令が定義されています。浮動小数点演算は大きく分けて32bit(単精度)浮動小数点と、64bit(倍精度)浮動小数点、さらに128bit(4倍精度)浮動小数点がありますが、RISC-Vの拡張仕様では、

  • F: 単精度浮動小数点命令
  • D: 倍精度浮動小数点命令
  • Q: 4倍精度浮動小数点命令

が定義されています。それぞれ、Fのみをサポートしているときは32bitの浮動小数点レジスタが用意され、Dの場合は64bit、Qの場合は128bitに拡張されます。このビット長は汎用レジスタのビット長のXLENと区別するためにFLENとして表現されます。

図図:RISC-Vのレジスタセット(浮動小数点系)

組み込み向けレジスタ縮小命令セットRV32E

ところで、RISC-Vってどのようなアプリケーション領域をターゲットにしているのでしょうか?

答えは「どのようなアプリケーション領域もターゲットにしている」です。つまり、RISC-VはHPCから組み込み向けまで、あらゆる領域で適用できるように命令セットを定義しているのです。この章で紹介するRV32Eは、その中で組み込み向けに策定された命令セットです。次に紹介するC拡張も似ているのですが、似て非なるものなので注意が必要です(筆者も時々間違えます)。

RV32Eは32ビット命令長のままで、レジスタ数を32本から16本に減らした命令セットです。RV32Eの場合は32本のレジスタのうち、x0からx15までのレジスタを使うことができ、それ以降のレジスタアドレスを指定した場合には無効命令例外が発行されるようになっています。

このような仕様からわかるように、どちらかというとハードウェアは単純にレジスタ数を減らすだけで、あとはコンパイラによるレジスタアサインを工夫して、x0からx15までを上手く使いまわすアセンブリコードを生成することを意図しています。

図図:RV32E命令セットでは使用できるレジスタの本数が16本に限定される

16ビット圧縮命令

RV32Eはレジスタの本数を圧縮する命令セット定義でした。もう一つ、命令セット自体を圧縮する方式として、RVCという命令セットが定義されています。使用するレジスタの長さで、32ビット長の時はRV32C、64ビットの時はRV64Cなどと呼ばれます。

RISC-Vの命令セットは基本的に32ビット命令長ですが、RVCは16ビット命令長です。ArmアーキテクチャにおけるThumb命令のようなものです。RVC命令セットの詳細はriscv.orgのUser Level ISA Specificationに記載されていますが、ここではざっくりとした仕様を紹介します。

この圧縮命令は、基本モードと同様に32本のレジスタを利用できますが、利用できるのはいくつかの命令に限定されます。RVC命令のうちいくつかは32本のレジスタにアクセスできますが、それ以外の命令ではアクセスレジスタが限定されています。これによりレジスタアドレスフィールドを削減して、RVCは命令長を16bitに削減しています。

ロードストアに関しては、レジスタベースでアドレス計算を行うロードストア命令と、スタックポインタベースでアドレス計算を行う命令が存在します。レジスタベースのロードストア命令では、32本のレジスタセットのうち8種類のレジスタのみが指定できるということになります。一方でスタックポインタベースのロードストア命令は、ベースアドレスレジスタはx2に限定される代わりに、データレジスタに任意のレジスタを指定することができ、また任意のレジスタにメモリから値をロードすることができます。

7-th Workshopで提案された新命令セット "Vector Extension"

正式な仕様ではないのですが、2017年11月に開催された7-th RISC-V WorkshopではVector Extension(所謂SIMD命令)のProposalが発表されました。

SIMD命令はHPCなどをはじめとする高性能アプリケーションにおいて非常に重要な命令セットです。x86ではSSEそしてAVX、ArmではNEONが定義されており、RISC-Vでも同様の命令セットが入ると思われています。

詳細についてはRISC-V 7th Workshopでの発表資料にて、詳しく解説されています。ここでは詳細は説明しませんが、HPC分野など、性能面を重視するアプリケーションを開発している方は、チェックしておいて損なことはないでしょう。

図図:7th RISC-V Workshopで発表されたVector Extensionの資料

オリジナルの命令を作ろう

Custom命令とRoCC(Rocket Custom Coprocessor)

RISC-Vは既定の命令セットだけでなく、オリジナルの命令を追加できる余地が残っています。ここでRISC-VのISAマップを簡単に見てみましょう。

図図:RISC-Vの命令フィールドには、カスタム命令として利用することのできるフィールドが4つ定義されている(RISC-V Instruction Set Manual. Volume I: User-Level ISA Document Version 2.2 より抜粋)

Custom-0からCustom-3(Custom2,3はRV128と共用)が定義されていることが分かると思います。こちらはユーザが定義することができる命令マップです。

このカスタム命令を活用する手段の一つとして、RoCC(Rocket Custom Coprocessor)があります。RoCCは、RISC-Vの実装であるRocket CoreやBOOMで利用されているカスタムアクセラレータ向けのインタフェースです。Rocket-Coreの場合は、カスタム命令を実行すると、このRoCCを通じて外部のアクセラレータを呼び出すことができます。アクセラレータでの実行結果を、カスタム命令の戻り値として返すことができます。

この機構を使うことで、ユーザはRISC-Vの仕様を拡張し、アプリケーションのクリティカルな部分をオフロードし、処理を高速化することができるようになります。

図図:RoCCインタフェースを使えば、RISC-Vのカスタム命令を使ってオリジナルアクセラレータを開発できる。

さいごに

RISC-Vの命令セットについて、簡単にですが紹介してみました。次回は、RISC-Vの命令セット仕様について、より深い部分について解説していきます。

前の記事を読む