C言語専用 x86 → RISC-V 移植統合テスティングフレームワーク「Akari」

# C言語専用 x86 → RISC-V 移植統合テスティングフレームワーク「Akari」
🚀 C言語プロジェクトに組み込みやすい！ x86 → RISC-V の移植を自動検証！ 🚀  

## 1. 目的と概要
✅ x86 の SSE / AVX / MMX / FMA / VNNI のイントリンシックを RISC-V に移植する際の品質を保証  
✅ 1対1で対応できる命令は最適化し、対応できない命令はエミュレーションコードで処理  
✅ C言語プロジェクトに簡単に統合できるように API 提供 (`akari.h`)  
✅ アセンブリ解析・数学的なベンチマーク比較・Intel公式リファレンス + ChatGPT API によるイントリンシック変換・CI/CD 自動テストを統合  


なぜ x86 → RISC-V 移植が難しいのか？

💡 移植で起こる主な問題点

1. x86 の AVX / SSE / MMX などのベクトル命令は RISC-V にそのまま移植できない

RISC-V には RISC-V Vector Extension (RVV) があり、AVX に似た機能を持つが、命令セットが異なるため単純な変換は不可。



2. x86 の一部命令（_mm_movemask_epi8 など）は RISC-V に直接対応する命令がない

これらは エミュレーションコード を使用して RISC-V 上で動作させる必要がある。



3. 最適化の方法が異なるため、x86 の移植後に性能が大きく異なる可能性がある

移植が正しく行われたかどうかの確認が難しい。



4. 移植後のコードが「正しく動作しているか」「最適化されているか」を検証する必要がある

そのために「アセンブリ解析」「ベンチマーク比較」「エミュレーションコード」「CI/CD の自動チェック」が必要。

---

## 2. イントリンシックの変換パターン
💡 x86 → RISC-V のイントリンシック変換には以下の3つのパターンがある  

| 変換タイプ | 対応状況 | 例 | 処理方法 |
|-----------|--------|----------|-----------------|
| 1対1対応 | ✅ 完全対応 | `_mm_add_ps → vadd.vv` | そのまま変換 |
| 非対応   | ❌ 変換不可 | `_mm_movemask_epi8` | エミュレーション |
| 部分対応 | ⚠️ 一部変換可 | `_mm_shuffle_ps` | 複数命令に展開 |

✅ RISC-V に 1対1 で対応する命令があれば、それを使用  
✅ 対応できない場合はエミュレーションコードを適用  
✅ 部分対応のケースでは、複数の RISC-V 命令で再構築  

---

## 3. イントリンシックのエミュレーションコード集

### 1. `_mm_shuffle_ps` (SSE)
### 説明: 4要素の128-bit ベクトルの順序を変更
```c
static inline void simd_shuffle(float* dst, float* src, int imm) {
    dst[0] = src[(imm >> 0) & 0x3];
    dst[1] = src[(imm >> 2) & 0x3];
    dst[2] = src[(imm >> 4) & 0x3];
    dst[3] = src[(imm >> 6) & 0x3];
}
```
 RISC-V の RVV には `_mm_shuffle_ps` に相当する命令がないため手動でエミュレーション

---

### 2. `_mm_hadd_ps` (SSE3)
### 説明: 4要素の128-bit ベクトルの隣接要素を加算
```c
static inline void simd_hadd(float* dst, float* src) {
    dst[0] = src[0] + src[1];
    dst[1] = src[2] + src[3];
    dst[2] = src[4] + src[5];
    dst[3] = src[6] + src[7];
}
```
 RVV には隣接要素を加算する命令がないため手動で対応

---

### 3. `_mm_movemask_epi8` (SSE2)
### 説明: 128-bit の 16要素の符号ビットを抽出し整数にまとめる
```c
static inline uint16_t simd_movemask_epi8(const int8_t* src) {
    uint16_t mask = 0;
    for (int i = 0; i < 16; i++) {
        if (src[i] & 0x80) {
            mask |= (1 << i);
        }
    }
    return mask;
}
```
 RISC-V には `PMOVMSKB` に相当する命令がないため手動でビットマスクを生成

---

### 4. `_mm256_blendv_ps` (AVX)
### 説明: 256-bit ベクトルの要素ごとにマスクを適用
```c
static inline void simd_blendv(float* dst, float* a, float* b, float* mask) {
    for (int i = 0; i < 8; i++) {
        dst[i] = mask[i] < 0 ? b[i] : a[i];
    }
}
```
 RVV には `_mm256_blendv_ps` に相当する命令がないため負の値かどうかで手動分岐

---

### 5. `_mm_min_epu8` (SSE2)
### 説明: 8ビット整数の最小値を計算
```c
static inline void simd_min_epu8(uint8_t* dst, uint8_t* a, uint8_t* b) {
    for (int i = 0; i < 16; i++) {
        dst[i] = (a[i] < b[i]) ? a[i] : b[i];
    }
}
```
 RVV には `_mm_min_epu8` に相当する命令がないためループで処理

---

### 6. `_mm_cvtsi128_si32` (SSE2)
### 説明: 128-bit ベクトルの最初の整数要素をスカラーに変換
```c
static inline int simd_cvtsi128_si32(const int* src) {
    return src[0];
}
```
 RISC-V には `_mm_cvtsi128_si32` に相当する命令がないため手動で取得

---

### 7. `_mm256_permutevar_ps` (AVX2)
### 説明: 256-bit ベクトルの要素の並び替え
```c
static inline void simd_permutevar(float* dst, float* src, int* indices) {
    for (int i = 0; i < 8; i++) {
        dst[i] = src[indices[i] & 0x7];
    }
}
```
 RVV には `_mm256_permutevar_ps` に相当する命令がないため手動でインデックス操作

---

### 8. `_mm_abs_epi16` (SSSE3)
### 説明: 16ビット整数の絶対値を計算
```c
static inline void simd_abs_epi16(int16_t* dst, int16_t* src) {
    for (int i = 0; i < 8; i++) {
        dst[i] = (src[i] < 0) ? -src[i] : src[i];
    }
}
```
 RVV には `_mm_abs_epi16` に相当する命令がないため手動で処理

---

### 9. `_mm256_max_epu32` (AVX2)
### 説明: 32ビット整数の最大値を計算
```c
static inline void simd_max_epu32(uint32_t* dst, uint32_t* a, uint32_t* b) {
    for (int i = 0; i < 8; i++) {
        dst[i] = (a[i] > b[i]) ? a[i] : b[i];
    }
}
```
 RVV には `_mm256_max_epu32` に相当する命令がないためループで処理


---

## 4. アセンブリ解析（最適化チェック）
💡 `objdump` の出力を JSON ベースで管理し、最適化が適用されているかをチェックする。  

```python
import json
import re
import sys

def load_patterns(json_file):
    with open(json_file, 'r') as f:
        return json.load(f)

def check_assembly(file_path, patterns, arch):
    with open(file_path, 'r') as f:
        asm = f.read()

    matched = False
    for label, pattern in patterns[arch].items():
        matches = re.findall(r"\b" + pattern + r"\b", asm)
        if matches:
            print(f"✅ {label}: {pattern} found {len(matches)} times in {file_path} ({arch})")
            matched = True
        else:
            print(f"❌ {label}: {pattern} NOT found in {file_path} ({arch})")

    return matched

if __name__ == "__main__":
    patterns = load_patterns("akari_asm_patterns.json")
    arch = sys.argv[2]  
    matched = check_assembly(sys.argv[1], patterns, arch)
    if not matched:
        sys.exit(1)
```
✅ JSON に定義された最適化パターンが適用されていなければ NG 判定  

---

## 5. 数学的に公平なベンチマーク解析
💡 移植が成功したかどうかを「理論値 vs. 実測値」で判定  

```python
import json
import numpy as np
import sys

def analyze_benchmarks(x86_json, riscv_json, epsilon=0.1):
    x86_results = load_json(x86_json)
    riscv_results = load_json(riscv_json)

    x86_times = np.array([run["real_time"] for run in x86_results["benchmarks"]])
    riscv_times = np.array([run["real_time"] for run in riscv_results["benchmarks"]])

    R_real = np.mean(riscv_times) / np.mean(x86_times)
    R_theory = expected_ratio_based_on_instruction_count(x86_results, riscv_results)

    if abs(R_real - R_theory) < epsilon:
        print(f"✅ OK")
    else:
        print(f"❌ NG")
        sys.exit(1)
```
✅ 理論値 (`R_theory`) と実測値 (`R_real`) の誤差が小さければ OK！  
✅ NG なら最適化ミス or 移植ミスの可能性があるので CI/CD でアラート！  

---

## 6. Intel公式リファレンス + ChatGPT API によるイントリンシック変換
💡 Intel の公式リファレンスをスクレイピングし、ChatGPT API を使って RISC-V への変換 or エミュレーションコードを自動生成  

```python
from selenium import webdriver
from selenium.webdriver.common.by import By
import json
import time

URL = "https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html"

driver = webdriver.Chrome()
driver.get(URL)
time.sleep(5)

intrinsics = []
elements = driver.find_elements(By.CSS_SELECTOR, ".intrinsic-name")

for elem in elements:
    name = elem.text.strip()
    if name:
        intrinsics.append(name)

driver.quit()

with open("intrinsics_list.json", "w") as f:
    json.dump(intrinsics, f, indent=2)
```
✅ Intel の公式リファレンスを Web スクレイピングし、すべてのイントリンシック命令をリスト化  

---

## 7. CI/CD での自動チェック
```yaml
- name: Analyze Benchmarks
  run: python3 akari_benchmark.py benchmark_x86.json benchmark_riscv.json
```
✅ アセンブリの最適化 & ベンチマークの白黒評価を CI/CD で自動化！  

---

## 8. まとめ
✅ x86 → RISC-V の移植を自動でチェックする Akari を作成！  
✅ C言語プロジェクト専用！ `#include "akari.h"` を追加するだけで組み込み可能！  
✅ 1対1変換可能なイントリンシックは最適化、対応不可のものはエミュレーション適用！  
✅ アセンブリ解析・数学的なベンチマーク比較を統合！  
✅ Intel 公式リファレンスをスクレイピングし、ChatGPT でイントリンシック変換！  
✅ CI/CD で統合し、自動テスト可能！  

🚀 **「Akari」は C 言語プロジェクトの x86 → RISC-V 移植を品質保証し、自動検証を可能にするフレームワーク！** 🚀