RISC-V環境でのベクトル化されたSIMD命令の実装と確認

## RISC-V環境でのベクトル化されたSIMD命令の実装と確認

この記事では、x86アーキテクチャのSIMD命令をRISC-V環境で動作させる方法を説明します。以下に示すコードは、x86のSIMD命令である`_mm_add_pd`をRISC-Vのベクトル命令に置き換えたものです。

### x86のSIMD命令

まず、x86のSIMD命令`_mm_add_pd`の実装を示します。

```c
extern __inline __m128d __attribute__((__gnu_inline__, __always_inline__, __artificial__))
_mm_add_pd(__m128d __A, __m128d __B) {
   return (__m128d)((__v2df)__A + (__v2df)__B);
}
```

これをRISC-V環境で動作させるために、以下のようにコードを変更します。

### RISC-V環境での実装

```c
#include <stdio.h>
#include <stdlib.h>
#include <riscv_vector.h>

// __v2df
typedef double __v2df __attribute__ ((__vector_size__ (16)));

// __m128d
typedef __v2df __m128d;

// _mm_add_pd
__m128d _mm_add_pd(__m128d A, __m128d B) {
    return (__m128d)((__v2df)A + (__v2df)B);
}

int main() {
    __m128d a = {1.0, 2.0};
    __m128d b = {3.0, 4.0};
    __m128d c = _mm_add_pd(a, b);
    printf("%f %f\n", c[0], c[1]);
    return 0;
}
```

### RISC-Vツールチェインのインストール

RISC-V環境での開発を始めるには、以下のコマンドでツールチェインをインストールします。

```sh
brew tap riscv-software-src/riscv
brew install riscv-tools
brew install riscv-isa-sim riscv-pk
brew install riscv-gnu-toolchain
echo 'export PATH="/opt/homebrew/opt/riscv-gnu-toolchain/bin:$PATH"' >> ~/.zshrc
source ~/.zshrc
```

### LLVM/Clangのビルドとインストール

次に、LLVM/Clangをビルドしてインストールします。

```sh
git clone --depth=1 https://github.com/llvm/llvm-project.git
cd llvm-project
mkdir build 
cd build
cmake -G "Ninja" \
  -DLLVM_ENABLE_PROJECTS="clang;compiler-rt" \
  -DLLVM_TARGETS_TO_BUILD="RISCV" \
  -DCMAKE_BUILD_TYPE=Release \
  ../llvm
ninja
cd bin
echo "export PATH=`pwd`:\$PATH" >> ~/.zshrc 
source ~/.zshrc                            
```

### Clangのバージョン確認とコンパイル

Clangが正しくインストールされたか確認し、コードをコンパイルします。

```sh
clang -v                                   
clang version 19.0.0git (https://github.com/llvm/llvm-project.git b77e734e4e6c8f5e016ba3ac49526862e6039482)
Target: riscv64-unknown-unknown-elf
Thread model: posix

export RISCV_SYSROOT=$(riscv64-unknown-elf-gcc --print-sysroot)

clang -target riscv64-unknown-elf -march=rv64gcv -mabi=lp64d -O3 test.c -c -s \
--sysroot=$RISCV_SYSROOT \
-I$RISCV_SYSROOT/include \
-L$RISCV_SYSROOT/lib \
-lc -lm -lclang_rt.builtins -Wimplicit-function-declaration -fvectorize -fslp-vectorize
```

### アセンブリコードの確認

生成されたオブジェクトファイルを逆アセンブルし、ベクトル化された命令を確認します。

```sh
llvm-objdump -S test.o
```

出力されたアセンブリコードは以下の通りです：

```assembly
test.o:       file format elf64-littleriscv

Disassembly of section .text:

0000000000000000 <_mm_add_pd>:
       0: cd817057      vsetivli        zero, 0x2, e64, m1, ta, ma
       4: 5e054457      vmv.v.x v8, a0
       8: 3e85e457      vslide1down.vx  v8, v8, a1
       c: 5e0644d7      vmv.v.x v9, a2
      10: 3e96e4d7      vslide1down.vx  v9, v9, a3
      14: 02849457      vfadd.vv        v8, v8, v9
      18: 42802557      vmv.x.s a0, v8
      1c: 3e80b457      vslidedown.vi   v8, v8, 0x1
      20: 428025d7      vmv.x.s a1, v8
      24: 8082          ret

0000000000000026 <main>:
      26: 1141          addi    sp, sp, -0x10
      28: e406          sd      ra, 0x8(sp)
      2a: 00000537      lui     a0, 0x0
      2e: 00050513      mv      a0, a0
      32: 40100593      li      a1, 0x401
      36: 15d2          slli    a1, a1, 0x34
      38: 00803637      lui     a2, 0x803
      3c: 161e          slli    a2, a2, 0x27
      3e: 00000097      auipc   ra, 0x0
      42: 000080e7      jalr    ra <main+0x18>
      46: 4501          li      a0, 0x0
      48: 60a2          ld      ra, 0x8(sp)
      4a: 0141          addi    sp, sp, 0x10
      4c: 8082          ret
```

### ベクトル命令の解説

- `vsetivli` は、ベクトルレジスタの初期設定を行う命令です。
- `vmv.v.x` は、スカラー値をベクトルレジスタに移動する命令です。
- `vslide1down.vx` は、ベクトル要素をシフトする命令です。
- `vfadd.vv` は、ベクトル要素同士の浮動小数点加算を行う命令です。
- `vmv.x.s` は、ベクトルレジスタのスカラー要素をスカラー値に移動する命令です。
- `vslidedown.vi` は、ベクトル要素をシフトする命令です。

これらの命令が使用されていることから、ベクトル化されていることがわかります。

### 結論

`_mm_add_pd`関数のアセンブリコードを見ると、ベクトル化が適用されていることが確認できます。`vfadd.vv`命令などのベクトル命令が含まれているため、コードはベクトル化されています。これにより、RISC-V環境で効率的にSIMD命令を使用することができます。

以上が、x86のSIMD命令をRISC-V環境でベクトル化して動作させる方法の説明です。