# Tensor Language (TL) メモリ管理システム

このドキュメントでは、現在の TL ランタイムにおけるメモリ管理アーキテクチャについて詳細に解説します。

## 1. 概要

TL のメモリ管理は、**スコープベースの自動管理 (RAII に近い)** と **参照カウント (Reference Counting)** を組み合わせたハイブリッド方式を採用しています。
Python のようなガベージコレクション (GC) は持たず、C++ の `std::shared_ptr` や Rust の Drop/Ownership に近い挙動をランタイム (`MemoryManager`) がエミュレートしています。

主な特徴:
*   **スコープ管理**: ブロック (`{ ... }`) に入るとスコープが作成され、出るとそのスコープ内で確保された変数は自動的に解放されます。
*   **参照カウント**: テンソルや構造体は参照カウントを持ち、複数の変数が同じデータを共有する場合（浅いコピーなど）、カウントが0になるまでメモリは解放されません。
*   **テンソルプール**: 解放されたテンソルのメモリ領域を再利用し、`malloc/free` のオーバーヘッドを削減します。

---

## 2. アーキテクチャ

メモリ管理の中核は `tl_runtime` クレート内の `MemoryManager` シングルトンです。

### 構成要素

```rust
pub struct MemoryManager {
    // スコープスタック: 各スコープは確保されたポインタのリストを持つ
    scopes: Vec<Vec<AllocationRecord>>,

    // 参照カウント: ポインタアドレス -> カウント数
    refcounts: HashMap<*mut c_void, usize>,

    // 型情報: ポインタが Tensor なのか Struct なのかを区別
    ptr_types: HashMap<*mut c_void, AllocationType>,

    // アリーナアロケータの状態保存用
    arena_offsets: Vec<usize>,
}
```

### AllocationType
管理対象のメモリは以下の3種類に分類されます：
1.  **Tensor**: `OpaqueTensor` (C++互換のテンソル構造体)。最も複雑で、プール管理やバックエンド固有の解放ロジックを持ちます。
2.  **Struct**: ユーザー定義構造体。`malloc` で確保され、`free` で解放されます。
3.  **VecPtr**: `Vec<T>` コンテナ。要素の再帰的な解放が必要です。

---

## 3. メモリのライフサイクル

### 3.1. 確保と登録 (Allocation & Registration)

新しいオブジェクト（テンソルや構造体）が生成されると、即座に `MemoryManager` に登録されます。

1.  **メモリ確保**: `malloc` (構造体) または `tensor_new` (テンソル) が呼ばれる。
2.  **登録 (`register_ptr`)**: 生成されたポインタを現在のスコープリストに追加します。
3.  **初期カウント**: 参照カウント (RefCount) を `1` に設定します（現在のスコープが所有）。

```rust
// 例: register_struct
mgr.scopes.last_mut().push(ptr); // スコープに追加
mgr.refcounts.insert(ptr, 1);    // RefCount = 1
```

### 3.2. スコープ共有 (Acquire)

変数の単純なコピーや、関数への引数渡しにおいて、実データ（ポインタ）はコピーされず共有されます。この時、参照カウントをインクリメントします。

*   **操作**: `tl_ptr_acquire(ptr)`
*   **効果**: RefCount `+1`

### 3.3. 解放プロセス (Release)

スコープを抜ける際、または変数が不要になった際に `release` が呼ばれます。

*   **操作**: `tl_ptr_release(ptr)`
*   **ロジック**:
    1.  RefCount を `-1`。
    2.  RefCount が **0** になった場合のみ、実際のメモリ解放処理 (`free`) を実行。
        *   **Tensor**: メタデータを削除し、`TensorPool` に返却するか、物理的に解放。
        *   **Struct**: `libc::free` を呼び出し。
        *   **Vec**: 要素を再帰的に `release` し、コンテナ自体を解放。
    3.  RefCount が **1以上** の場合、何もしない（他の誰かがまだ使っている）。

### 3.4. スコープ脱出 (Scope Exit)

関数やブロックの終了時に `tl_mem_exit_scope` が呼ばれます。

1.  現在のスコープに登録されている全てのポインタをリストアップ。
2.  **逆順 (LIFO)** に `tl_ptr_release` を呼び出し。
    *   これにより、そのスコープで確保された変数の RefCount がデクリメントされます。
    *   誰も共有していなければ RefCount=0 となり、即座に解放されます。
3.  アリーナアロケータのオフセットを復元（一時メモリのリセット）。

---

## 4. 特殊なメカニズム

### 4.1. Unregister (所有権の移動)

関数から値を返す（Return）場合、その値は「呼び出し元のスコープ」に所有権が移動する必要があります。しかし、通常通りスコープを抜けると解放されてしまいます。
これを防ぐため、`unregister` (登録抹消) が使われます。

*   **操作**: `tl_mem_unregister(ptr)`
*   **効果**: 現在のスコープリストからそのポインタを**削除**しますが、**参照カウントは減らしません**。
*   **結果**: スコープを抜けても `release` されないため、RefCount=1 のまま生き残り、呼び出し元に引き継がれます。

### 4.2. Tensor Pool (テンソルプール)

頻繁なメモリ確保/解放によるパフォーマンス低下を防ぐため、解放されたテンソルは直ちに `free` されず、プール (`free_list`) に保存されます。

*   キー: `(要素数, データ型, デバイスID)`
*   再利用: 同じサイズのテンソル確保要求が来ると、プールから再利用されます。
*   自動調整: メモリ不足時はプールサイズが縮小されます。

---

## 5. 現在の課題と制約

### 5.1. Struct の二重解放リスク
`Vec<Struct>` のようなコンテナにおいて、要素である `Struct` がスタック変数（自動スコープ管理対象）のコピーである場合、以下の問題が発生するリスクがあります（現在調査中）：
1.  スタック変数がスコープ終了で解放される。
2.  `Vec` が破棄される際に、保持しているポインタ（既に解放済み）を再度解放しようとする。
*対策*: これを防ぐため、コンパイラ側で `Unregister` を適切に挿入するか、浅いコピー時に RefCount を正しく管理する必要があります。

### 5.2. 循環参照
現在の単純な参照カウント方式では、循環参照（例: AがBを持ち、BがAを持つ）が発生すると、RefCount が0にならずメモリリークします。現状の TL 言語仕様では循環参照を作りにくいため、大きな問題にはなっていません。
