Files
hakorune/docs/private/roadmap/phases/phase-15.77/RUST_MINIMIZATION_PLAN.md

13 KiB
Raw Blame History

Rust最小化計画Phase 15.77

🎯 目標

99,406行 → 100-200行99.8%削減)

Rust層を極限まで薄くし、VM実行エンジン+FFI基盤のみ残す。


📊 削減計画の全体像

Phase 15.75開始時2025-10-05

総行数: 99,406行

内訳:
├── Parser層        : ~30,000行
├── AST層           : ~10,000行
├── MIR Builder層   : ~40,000行
├── VM層            : ~5,000行
├── FFI/Runtime層   : ~2,000行
└── その他          : ~12,406行

Phase 15.77完了時(目標)

総行数: 100-200行

内訳:
├── main.rs         : ~50行エントリーポイント
├── VM実行エンジン  : ~100行最小ループ
├── FFI基盤         : ~50行extern_c実行
└── その他削除不可  : ~0行

📅 週次削減計画

Week 32025-11-23 - 11-29Parser削除

削除対象(~40,000行

src/front/parser_layer/
├── parser.rs               (~5,000行) ❌
├── lexer.rs                (~3,000行) ❌
├── token.rs                (~1,000行) ❌
└── grammar/                (~8,000行) ❌

src/front/ast/
├── ast_nodes.rs            (~4,000行) ❌
├── ast_visitor.rs          (~2,000行) ❌
├── ast_printer.rs          (~1,000行) ❌
└── ast_validator.rs        (~3,000行) ❌

その他Parser関連:
├── src/front/syntax/       (~8,000行) ❌
└── src/front/source_map/   (~5,000行) ❌

置き換え方法

// BeforeRust Parser
fn parse_source(source: &str) -> Result<Ast> {
    let tokens = lexer::tokenize(source)?;
    let ast = parser::parse(tokens)?;
    Ok(ast)
}

// After凍結EXE経由
fn parse_source_frozen(source: &str) -> Result<Mir> {
    // hako-frozen-v1を呼び出し
    let mir_json = Command::new("hako-frozen-v1")
        .arg("--emit-mir-json")
        .arg("-")
        .stdin(Stdio::piped())
        .stdout(Stdio::piped())
        .spawn()?
        .wait_with_output()?;

    let mir = serde_json::from_slice(&mir_json.stdout)?;
    Ok(mir)
}

スモークテスト修正

# Before
./hako program.hako  # Rust Parser使用

# After
./hako program.hako  # 凍結EXE経由でParser実行

期待削減

  • 削減: ~40,000行
  • 残り: ~59,000行59.4%削減)

Week 42025-11-30 - 12-06MIR Builder削除準備

検証対象(~40,000行

src/backend/mir_builder/
├── builder.rs              (~8,000行) 検証
├── expression.rs           (~10,000行) 検証
├── statement.rs            (~8,000行) 検証
├── control_flow.rs         (~6,000行) 検証
└── type_inference.rs       (~8,000行) 検証

並行動作確認

// Rust MIR Builder
fn build_mir_rust(ast: &Ast) -> Result<Mir> {
    let mut builder = MirBuilder::new();
    builder.visit_ast(ast)?;
    Ok(builder.finish())
}

// Hakorune MIR Builder
fn build_mir_hakorune(ast_json: &str) -> Result<Mir> {
    let mir_json = Command::new("hako")
        .arg("apps/selfhost-compiler/mir_builder.hako")
        .arg("--input")
        .arg(ast_json)
        .output()?;

    let mir = serde_json::from_slice(&mir_json.stdout)?;
    Ok(mir)
}

// 両方動作確認
#[test]
fn test_mir_builder_parity() {
    let source = "local x = 42; return x;";
    let ast = parse_source(source).unwrap();

    let mir_rust = build_mir_rust(&ast).unwrap();
    let mir_hako = build_mir_hakorune(&ast_to_json(&ast)).unwrap();

    assert_eq!(mir_rust, mir_hako);  // パリティ確認
}

Week 4では削除しない

  • 理由: パリティ確認・安全性検証
  • 削除: Week 5で実行

Week 52025-12-07 - 12-13MIR Builder削除実行

削除実行(~40,000行

# MIR Builder完全削除
rm -rf src/backend/mir_builder/

# Hakorune実装をデフォルトへ
# src/main.rs
fn build_mir(source: &str) -> Result<Mir> {
    // Hakorune MIR Builder使用
    build_mir_hakorune(source)
}

スモークテスト確認

# 全テスト実行Hakorune MIR Builder使用
bash tools/smokes/v2/run.sh --profile quick-selfhost

# 期待結果
# Total: 185
# Passed: 170
# Failed: 15

期待削減

  • 削減: ~40,000行
  • 残り: ~19,000行80.9%削減)

Week 62025-12-14 - 12-20雑多なコード削除

削除対象(~18,800行

未使用依存削除:
├── serde_derive        ❌AST/MIR生成不要
├── syn                 ❌Parser不要
├── quote               ❌(コード生成不要)
└── proc-macro2         ❌(マクロ不要)

デッドコード削除:
├── src/utils/          (~2,000行) ❌
├── src/diagnostics/    (~3,000行) ❌凍結EXE側
├── src/optimizer/      (~5,000行) ❌LLVM側
└── src/codegen/        (~8,000行) ❌LLVM側

テスト削除:
├── tests/parser/       (~500行) ❌
├── tests/mir_builder/  (~300行) ❌
└── 残すテスト: tests/vm/ tests/ffi/ のみ

Cargo.toml整理

# Before多数の依存
[dependencies]
serde = "1.0"
serde_derive = "1.0"
serde_json = "1.0"
syn = "2.0"
quote = "1.0"
proc-macro2 = "1.0"
clap = "4.0"
# ... 30個以上

# After最小限
[dependencies]
serde = "1.0"          # MIR JSONデシリアライズ
serde_json = "1.0"     # MIR JSON読み込み
libloading = "0.8"     # FFI動的ロード
# 合計3個

期待削減

  • 削減: ~18,800行
  • 残り: ~200行99.8%削減)

🎯 最終構成目標100-200行

ファイル構成

src/
├── main.rs                 (~50行) ✅
├── vm/
│   └── executor.rs        (~100行) ✅
├── ffi/
│   └── extern_c.rs        (~50行) ✅
└── lib.rs                 (~10行) ✅

Cargo.toml                 (~30行) ✅
build.rs                   (~20行) ✅ (FFIビルド用)

合計: ~260行少しオーバー、調整必要

src/main.rs~50行

use std::process::Command;
use serde_json;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let args: Vec<String> = std::env::args().collect();

    // Step 1: 凍結EXE経由でMIR取得
    let mir_json = Command::new("hako-frozen-v1")
        .arg("--emit-mir-json")
        .arg(&args[1])
        .output()?
        .stdout;

    // Step 2: MIRデシリアライズ
    let mir: Mir = serde_json::from_slice(&mir_json)?;

    // Step 3: VM実行
    let result = vm::execute(mir)?;

    println!("{}", result);
    Ok(())
}

// MIR構造体最小限
#[derive(serde::Deserialize)]
struct Mir {
    functions: Vec<Function>,
    entry: String,
}

#[derive(serde::Deserialize)]
struct Function {
    name: String,
    blocks: Vec<BasicBlock>,
}

#[derive(serde::Deserialize)]
struct BasicBlock {
    instructions: Vec<Instruction>,
}

#[derive(serde::Deserialize)]
enum Instruction {
    Const { dst: u32, value: Value },
    BinOp { dst: u32, lhs: u32, rhs: u32, op: BinOp },
    Call { dst: u32, callee: String, args: Vec<u32> },
    ExternC { dst: u32, symbol: String, args: Vec<u32> },
    Ret { value: u32 },
    // ... 16命令のみ
}

src/vm/executor.rs~100行

use crate::Mir;
use std::collections::HashMap;

pub fn execute(mir: Mir) -> Result<Value, String> {
    let mut vm = VM::new(mir);
    vm.run()
}

struct VM {
    mir: Mir,
    registers: Vec<Value>,
    current_block: usize,
    current_instruction: usize,
}

impl VM {
    fn new(mir: Mir) -> Self {
        Self {
            mir,
            registers: vec![Value::Null; 256],
            current_block: 0,
            current_instruction: 0,
        }
    }

    fn run(&mut self) -> Result<Value, String> {
        loop {
            let func = &self.mir.functions[0];  // エントリー関数
            let block = &func.blocks[self.current_block];
            let inst = &block.instructions[self.current_instruction];

            match inst {
                Instruction::Const { dst, value } => {
                    self.registers[*dst as usize] = value.clone();
                }
                Instruction::BinOp { dst, lhs, rhs, op } => {
                    let lhs = &self.registers[*lhs as usize];
                    let rhs = &self.registers[*rhs as usize];
                    self.registers[*dst as usize] = binop(lhs, rhs, *op);
                }
                Instruction::ExternC { dst, symbol, args } => {
                    let result = crate::ffi::call_extern(symbol, args, &self.registers)?;
                    self.registers[*dst as usize] = result;
                }
                Instruction::Ret { value } => {
                    return Ok(self.registers[*value as usize].clone());
                }
                // ... 他の命令
            }

            self.current_instruction += 1;
        }
    }
}

fn binop(lhs: &Value, rhs: &Value, op: BinOp) -> Value {
    match (lhs, rhs, op) {
        (Value::Int(a), Value::Int(b), BinOp::Add) => Value::Int(a + b),
        (Value::Int(a), Value::Int(b), BinOp::Sub) => Value::Int(a - b),
        // ... 基本演算のみ
        _ => panic!("unsupported binop"),
    }
}

src/ffi/extern_c.rs~50行

use libloading::{Library, Symbol};
use std::collections::HashMap;

static mut ALLOWLIST: Vec<String> = Vec::new();

pub fn call_extern(
    symbol: &str,
    args: &[u32],
    registers: &[Value],
) -> Result<Value, String> {
    // Step 1: 許可チェック
    unsafe {
        if !ALLOWLIST.contains(&symbol.to_string()) {
            return Err(format!("ExternCDenied: {}", symbol));
        }
    }

    // Step 2: ライブラリロード
    let lib = Library::new("libc.so.6")
        .map_err(|e| format!("Failed to load library: {}", e))?;

    // Step 3: シンボル取得
    unsafe {
        let func: Symbol<extern "C" fn() -> i64> = lib.get(symbol.as_bytes())
            .map_err(|e| format!("Symbol not found: {}", e))?;

        // Step 4: 実行引数0個のみ簡略化
        let result = func();
        Ok(Value::Int(result))
    }
}

pub fn init_allowlist() {
    unsafe {
        ALLOWLIST = vec![
            "getpid".to_string(),
            "strlen".to_string(),
            "system".to_string(),
        ];

        // ENV変数から追加
        if let Ok(list) = std::env::var("HAKO_FFI_ALLOW_LIST") {
            for symbol in list.split(',') {
                ALLOWLIST.push(symbol.to_string());
            }
        }
    }
}

各週の検証ポイント

Week 3: Parser削除後

# 動作確認
./hako local_tests/hello.nyash
# Expected: Hello, World!

# スモークテスト
bash tools/smokes/v2/run.sh --profile quick-selfhost
# Expected: 170 PASS / 15 FAIL

# 行数確認
tokei src/
# Expected: ~59,000行

Week 4: MIR Builder削除準備

# パリティテスト
cargo test test_mir_builder_parity
# Expected: ok

# 並行動作確認
HAKO_USE_RUST_MIR_BUILDER=1 ./hako test.hako  # Rust
HAKO_USE_RUST_MIR_BUILDER=0 ./hako test.hako  # Hakorune
# Expected: 両方OK

Week 5: MIR Builder削除後

# スモークテストHakoruneのみ
bash tools/smokes/v2/run.sh --profile quick-selfhost
# Expected: 170 PASS / 15 FAIL

# 行数確認
tokei src/
# Expected: ~19,000行

Week 6: 最終調整後

# 最終動作確認
./hako local_tests/hello.nyash
./hako --backend llvm local_tests/arithmetic.nyash

# 最終スモークテスト
bash tools/smokes/v2/run.sh --profile quick-selfhost
# Expected: 170 PASS / 15 FAIL

# 最終行数確認
tokei src/
# Expected: ~200行

⚠️ ロールバック手順

各週でロールバック可能

# Week 3で問題発生 → Week 2に戻る
git revert HEAD~10  # Week 3のコミット取り消し
cargo build --release
bash tools/smokes/v2/run.sh --profile quick-selfhost

# Week 5で問題発生 → Week 4に戻る
git revert HEAD~20  # Week 5のコミット取り消し
# ... 同様

凍結EXEへの完全ロールバック

# 最悪の場合: 凍結EXE使用に戻る
cp hako-frozen-v1 ./hako
./hako program.hako  # 凍結EXE経由で安定動作

📊 削減行数の追跡

Git commit messageテンプレート

Phase 15.77: Week X - [作業内容]

削減行数: ~XX,XXX行
残り行数: ~XX,XXX行
削減率: XX.X%

動作確認:
- スモークテスト: 170 PASS / 15 FAIL
- AOT導線: OK

関連: #issue-number

週次レポート

## Week X削減レポート

### 削減内容
- [削除したディレクトリ/ファイル]

### 削減行数
- 削減: ~XX,XXX行
- 残り: ~XX,XXX行
- 削減率: XX.X%

### 動作確認
- [ ] スモークテスト: 170 PASS維持
- [ ] AOT導線動作
- [ ] 凍結EXEロールバック確認

### 問題点
- [なし/あれば記載]

作成日: 2025-10-14 Phase: 15.77Week 3-6 目標: 99,406行 → 100-200行99.8%削減)