view slide/index.md @ 133:4e800dcf936e

update
author anatofuz <anatofuz@cr.ie.u-ryukyu.ac.jp>
date Mon, 08 Feb 2021 08:51:56 +0900
parents 472e1c8babf4
children 6f2af8e66c35
line wrap: on
line source

---
marp: true
title: GearsOSのメタ計算
paginate: true
---


#  GearsOSのメタ計算

- 清水 隆博
    - 琉球大学理工学研究科
    - 198584B
    - 河野研

---
# 研究発表の構成
- 研究目的
- CbC、GearsOSの基礎概念
- GearsOSの新機能
- 本研究での新たなGearsOSのシステムの解説
    - GearsOSのInterfaceシステムの改善
    - Perlトランスパイラの改善


---

# 研究目的

- OSとアプリケーションの信頼性の保証したい
- OSそのものも巨大なプログラム
    - プログラムの信頼性の保証にはテストが使われる
- 並列並行処理などのバグや、そもそもOSを構成する処理が巨大
    - テストコードで信頼性を保証しきれない
- 形式手法を用いてテストに頼らず信頼性を保証したい
    - 既存のソースコードに形式手法を導入できるフレームワークを構築したい

---

# ノーマルレベルとメタレベルを用いた信頼性の向上

- プログラムの実行部分は以下の2つからなる
    - 入力と出力の関係を決める計算(ノーマルレベル)
    - プログラムを実行したり、 信頼性を保証するために必要な計算(メタレベル)
- メタレベルの例
    - メモリやCPUの資源管理
    - システムコールの動作(副作用)
    - 並行実行(他のプロセスとの干渉)
    - モデル検査(可能な実行を列挙する方式の実行)
    - 証明(入力時と出力時の論理的な条件)、(invariant)
- メタレベルの計算として信頼性を保証する

---
# ノーマル、メタレベルの計算とGearsOS
- ノーマル/メタレベルを一貫して記述できる言語にContinuation Based Cがある
    - CbCを用いてGearsOSを開発している
- GearsOSではメタ計算の生成にトランスパイラを使っている
    - CMakeとPerlでビルド時にノーマルレベルのコードをメタレベルに変換する
- トランスパイラで生成するメタレベルの計算をより拡張したい
---
# トランスパイラによるメタレベルの計算の改善
- 本研究ではトランスパイラの機能を改善し、様々な新機能をGearsOSに導入した
## 主なGearsOSに導入された新機能
- Interfaceシステムの強化
- 手書きからの解放
- MetaCodeGearの入れ替え機能の追加

#### まずGearsOSについて確認をしてから、新機能の解説を行う

---
# ノーマルレベルから見たGearsOSの構成

---
# CodeGear
- CbCで定義する軽量継続で表現する単位をCodeGearと呼ぶ
    - 呼び出すと元の関数に戻れない
- Cの関数とアセンブラの中間の様に使える
- CodeGearは返り値の型の代わりに`__code`で宣言する
- `goto CodeGear()`でCodeGearに継続する


---
# DataGear
- GearsOSで扱うデータの単位
- CodeGearの入出力はDataGearとして取り扱う
- InterfaceもDataGearとして扱われる
- Cの構造体の形でメタレベルでは表現される

---
# ノーマルレベル/メタレベルのCodeGear

- ノーマルレベルのCodeGearから見ると、直接別のCodeGearに継続している
- 実際はCodeGearの実行の前後にメタレベルの計算をするCodeGearが実行される
    - メタレベルの計算をするCodeGearをMetaCodeGearと呼ぶ

![h:220 w:800](./metacg.svg)




---
# ノーマルレベルでのGearsOSの実装
- 次に継続するCodeGearを引数で受け取れる
    - この場合は次のCodeGear(`next`)に値(`data`)を書き込む

```c
__code pop(struct SingleLinkedStack* stack, __code next(union Data* data, ...)) {
    if (stack->top) {
        data = stack->top->data;
        stack->top = stack->top->next;
    } else {
        data = NULL;
    }
    goto next(data, ...);
}
```
---
# ノーマルレベルでのGearsOSの実装
- 通常のプログラミング言語のように引数を受け取れる
- 引数を受け取り、次のCodeGearに継続する
    - `right_fork->checkAndSet`はInterfaceのAPI呼び出し

```c
__code pickup_rfork(struct PhilsImpl* phils, __code next(...)) {
    struct AtomicT_int* right_fork = phils->Rightfork;
    goto right_fork->checkAndSet(-1, phils->self, pickup_lfork, pickup_rfork);
}
```

---
# Interface
- GearsOSのモジュール化の仕組み
- APIとなるCodeGear とその操作に用いる DataGear の集合を表現する
- JavaのInteface、Haskellの型クラスに相当する
- `goto interfaceName->method()`のようにAPIを呼び出す
    - CbCのgoto文なので引数を渡すことも可能

---
# ノーマルレベルでのInterface(ノーマルレベル)
- 実装したいInterfaceのCodeGearの名前と引数を列挙する

```c
typedef struct Stack<>{
        __code whenEmpty(...);
        __code clear(Impl* stack,__code next(...));
        __code push(Impl* stack,Type* data, __code next(...));
        __code pop(Impl* stack, __code next(Type* data, ...));
        __code pop2(Impl* stack, __code next(Type* data, Type* data1, ...));
        __code isEmpty(Impl* stack, __code next(...), __code whenEmpty(...));
        __code get(Impl* stack, __code next(Type* data, ...));
        __code get2(Impl* stack, __code next(Type* data, Type* data1, ...));
        __code next(...);
} Stack;
```

---
# Interfaceの実装(ノーマルレベル)
- 実装したいInterfaceで定義されたAPIを実装する
    - 従来はすべて手書きしていた

```c
#include "../../../context.h"
#interface "Stack.h"

...

__code push(struct SingleLinkedStack* stack, union Data* data, __code next(...)) {
    Element* element = new Element();
    element->next = stack->top;
    element->data = data;
    stack->top = element;
    goto next(...);
}
```
---
# InterfaceのAPI呼び出し(ノーマルレベル)
- `goto interfaceName->method()`のようにAPIを呼び出す
- `StackTest` interfaceの`insertTest1`をAPI呼び出し
    - 引数で`stack`、`shutdown`を渡している
    - `StackTest`の実装は`createStackTestImpl3`で作っている

```c
Stack* stack = createSingleLinkedStack(context);
StackTest* stackTest = createStackTestImpl3(context);
goto stackTest->insertTest1(stack, shutdown);
```

---
# ノーマルレベルのまとめ
- `goto next`などで次の継続に飛ぶ
    - 継続に値を渡すことも可能
- ノーマルレベルのGearsOSのコードにはInterfaceがある
    - 実装を隠蔽、抽象化できる
    - 呼び出す際は`goto interface->method()`を使う

---
# GearsOSのメタレベルの計算とMetaDataGear
- GearsOSのメタレベルの計算は、GearsOSの基本構造と関わりが深い
- GearsOSの基本構造はメタなCodeGear/DataGearで表現される


---
# MetaDataGear
- ノーマルレベルのCodeGearの前後でMetaCodeGearが実行される
    - MetaCodeGear参照するDataGearのことをMetaDataGearと呼ぶ
        - Context
        - TaskManager
        - Worker
        - ...
![h:220 w:800](./metacg.svg)

---
# Context(1)
- 従来のOSのプロセスに相当するMetaDataGear
- GearsOSでのプログラム実行に必要な情報を持っている
    - DataGearの型定義
    - CodeGearの名前とStubCodeGearへの対応
    - goto時に引数を書き込むDataGearごとの場所
    - DataGearを管理するヒープ情報
---
# Context(2)
- GearsOSでcontextを触るのはメタ計算部分だけ
    - ノーマルレベルではcontextに触れない
- 従来は手書きでcontext.hの中で定義
- Context自体は構造体で定義されている
    - Contextの要素にDataGearの型がすべて揃った共用体がある
![w:532 h:10cm](./context.svg)




---
# CodeGear、DataGearの番号
- Perlで変換すると番号の形に抽象化される
    - 番号から具体的なCodeGear/DataGearを取り出すのはメタレベル




---
# メタレベルに変換されたGearsOSの実装
- ノーマルレベルのCodeGearの継続は、contextとCodeGearの番号のみ指定する
    - 次の継続を番号で指定する
    - 次の継続への引数も値をcontextに書き込むように変更される

- CodeGearの継続は直接行かず、MetaCodeGearを経由して継続する
    - 呼び出したいCodeGearはMetaCodeGearが番号から取り出す
    - アドレスを直接使うのはメタ計算のみ
    - 各CodeGearはContextのみ持ち歩き、入力はStubCodeGearから与えられる

---
# メタレベルに変換されたGearsOSの実装
- 継続先は`goto meta`に変更されている
    - 継続先は`next`に代入されている番号で決まる
- 出力の`data`は引数でもらったアドレスに書き込まれる
    - この引数はStubCodeGearで渡される


```c
__code popSingleLinkedStack(struct Context *context,struct SingleLinkedStack* stack,
 enum Code next,union Data **O_data) {
  Data* data  __attribute__((unused))  = *O_data;
    if (stack->top) {
        data = stack->top->data;
        stack->top = stack->top->next;
    } else {
        data = NULL;
    }
  *O_data = data;
    goto meta(context, next);
}
```

---
# メタに変換されたGearsOSの実装
- 直接checkAndSetに行かずにmetaに飛ぶ
- もとのコードは`goto right_fork->checkAndSet()`なので、Interfaceを使った継続
    - context内の継続先のInterfaceの引数格納用の配列に書き込まれる
    - `right_fork->checkAndSet`には数値が入ってる

```c
__code pickup_rforkPhilsImpl(struct Context *context,struct PhilsImpl* phils, enum Code next) {
    struct AtomicT_int* right_fork = phils->Rightfork;
    Gearef(context, AtomicT_int)->atomicT_int = (union Data*) right_fork;
    Gearef(context, AtomicT_int)->oldData = -1;
    Gearef(context, AtomicT_int)->newData = phils->self;
    Gearef(context, AtomicT_int)->next = C_pickup_lforkPhilsImpl;
    (cGearefontext, AtomicT_int)->fail = C_pickup_rforkPhilsImpl;
    goto meta(context, right_fork->checkAndSet);
}
```
---
# メタに変換されたGearsOSの実装
- 変換前
```c
__code pickup_rfork(struct PhilsImpl* phils, __code next(...)) {
    struct AtomicT_int* right_fork = phils->Rightfork;
    goto right_fork->checkAndSet(-1, phils->self, pickup_lfork, pickup_rfork);
}
```

- 変換後

```c
__code pickup_rforkPhilsImpl(struct Context *context,struct PhilsImpl* phils, enum Code next) {
    struct AtomicT_int* right_fork = phils->Rightfork;
    Gearef(context, AtomicT_int)->atomicT_int = (union Data*) right_fork;
    Gearef(context, AtomicT_int)->oldData = -1;
    Gearef(context, AtomicT_int)->newData = phils->self;
    Gearef(context, AtomicT_int)->next = C_pickup_lforkPhilsImpl;
    (cGearefontext, AtomicT_int)->fail = C_pickup_rforkPhilsImpl;
    goto meta(context, right_fork->checkAndSet);
}
```



---
# データを取り出すStubCodeGear

```c
__code pickup_rforkPhilsImpl_stub(struct Context* context) {
  PhilsImpl* phils = (PhilsImpl*)GearImpl(context, Phils, phils);
  enum Code next = Gearef(context, Phils)->next;
  goto pickup_rforkPhilsImpl(context, phils, next);
}
```


---


- メタレベルの処理を全部手で書くのは面倒
    - メタレベルの処理を自動生成してくれるフレームワークを導入したい
- フレームワークに乗って実装すると等価なCbCに変換される
    - CbCレベルではメタレベルの処理とノーマルレベルの処理が見れる
- メタ計算を自分で定義できるようにしたい
    - 実装のコードをほとんど変えずにモデル検査を導入できるようにしたい


---
# GearsOSのメタ計算フレームワークとInterface
- CbCには無いモジュール化の仕組みとしてInterfaceがある
    - Perlフレームワーク上に構築
    - コンパイル時に等価な純粋なCbCに変換する
- Interfaceの機能が他の言語のInterfaceの機能とギャップがあった
    - 実装していないAPIがあっても、変換前の時点でエラーが出ないなど
- Interfaceの定義ファイルもあるものと無いものがあった
    - トランスパイラで統一な処理が出来ない


---
# Perlを中心としたフレームワークによるメタ計算の生成
- GearsOSの例題を作製する場合も、コピペや手書きが多発していた
    - フレームワークに実装したAPIを使って自動生成に変更したい
- Interfaceの機能充実も、 メタ計算の生成もCbCで行うのは難しい
    - Perlを中心としたフレームワークを活用したい
- コード変換(トランスパイル)を行うために必要な環境の整備もする必要がある
- Perlを中心としたトランスパイルシステムの整備、拡張を行った
    - GearsOSのメタ計算の生成がより柔軟かつ高い信頼性で可能となった

---
# GearsOSの基礎概念
- CodeGear、DataGear
- Interface
- GearsOSのビルドシステム
    - cmake
    - Perlトランスパイラ







---
# InterfaceとDataGear
- Interface

---
# GearsOSのビルドシステム
- CMakeとPerlを使ってビルドする
    - CMakeはMakefileやbuild.ninjaを生成する
    - Perlは2種類のスクリプトが自動的に呼ばれる
- Perlによってコード変換・ファイル生成が行われた後にコンパイルが走る
    - Perlの実行前はノーマルレベルだけのコード、実行後はメタが入る
![w:632 h:10cm](./geasflow1.svg)


---
# GearsOSのビルドシステム
- Perlスクリプトはファイルの変換・生成を行う
    - メタ計算の変換・自動生成も行う
- メタ計算で必要なファイル、データ構造も生成する
    - CodeGear/DataGearの番号など

![w:632 h:10cm](./geasflow1.svg)

---
# Perlトランスパイラが生成するファイル

- context.hの中身をもとに生成するファイルが多い

|ファイル名|内容|元にするファイル|
|-|-|-|
|*.c | メタレベルに変換したGearsOSのコード | *.cbc|
|enumData.h| DataGearの番号を管理するenum | context.h|
|enumCode.h| CodeGearの番号を管理するenum | 変換された.cファイル|
|extern.h | CodeGearのextern一覧 | 変換された.cファイル|
|project-context.c | Contextの初期化ルーチンなどのCodeGear | context.h|
|typedefData.h | DataGearのtypef一覧| context.h|
|dataGearInit.c| DataGearのアロケート処理| context.h|

---
# 主なGearsOSに導入された新機能
- Interfaceシステムの強化
- 手書きからの解放
- MetaCodeGearの入れ替え機能の追加

---
# Interfaceシステムの強化
- Interface構文の簡素化
    - より簡潔に明確に記述できるように定義した。
- Interfaceの実装の型の導入
    - GearsOSでの型定義の方法に一貫性が生まれた
- Interfaceの引数の確認
- Interfaceで未定義のAPIの検知
- InterfaceにないAPIの呼び出しの検知
    - 他の言語のいわゆるInterfaceの機能が強化
    - コード変換前にPerlレベルでエラーを発生させた
---
# 手書きからの解放
- Interfaceの雛形ファイルの作製スクリプトの導入
- 別のInterfaceからの出力を取得するStubの自動生成
- 実装のCodeGear名からメタ情報の切り離し
    - メタな型情報はビルド時に付与される
- DataGearの型集合ファイルであるcontext.hの自動生成
- GearsOSの初期化ルーチンの自動生成

---
# GearsOSの新機能
- **自由なMetaCodeGearの作製、継続の入れ替え機能**
- Perlトランスパイラの変換ルーチンのデバッグ機能の追加
- ジェネリクスのサポート


---
# Interfaceの改良
- 従来のInterfaceにまつわるPerlのフレームワークを改良した
    - CbCの変換後のコードでなく、Perlレベルでエラー検知可能になった

---
# Interfaceの定義構文の改良
- 従来は引数とCodeGearの定義を別けて記述していた
    - Interfaceの宣言なので、書ける変数は引数/出力のもののみ
- Javaのクラス変数やインスタンス変数のようなものだと思われてしまった
    - GearsOSに慣れてない
    - シンタックスが問題
- シンタックスをgolangやJavaのInterfaceを参考に簡潔なものにした


---
# 従来のInterface
- 引数の組とAPI(CodeGear)は別けて記述する必要があった
```c
typedef struct Stack<Type, Impl>{
        union Data* stack;
        union Data* data;
        union Data* data1;


        __code whenEmpty(...);
        __code clear(Impl* stack,__code next(...));
        __code push(Impl* stack,Type* data, __code next(...));
        __code pop(Impl* stack, __code next(Type* data, ...));
        __code pop2(Impl* stack, __code next(Type* data, Type* data1, ...));
        __code isEmpty(Impl* stack, __code next(...), __code whenEmpty(...));
        __code get(Impl* stack, __code next(Type* data, ...));
        __code get2(Impl* stack, __code next(Type* data, Type* data1, ...));
        __code next(...);
} Stack;
```

---
# 定義し直したInterface構文
- APIのみを記述すれば良くなった
```c
```

---
# Implementの型定義ファイルの導入
- Interfaceは型定義ファイルがあったが、実装側はなかった
- context.h上にメタな構造体に直した実装の型を手で書いていた
    - これはメタ情報なので手で書きたくない
    - 型定義の一貫性がない
- 実装側にも型定義ファイルを導入して一貫性を持たせたい
    - Perlトランスパイラフレームワークで利用可能

---
# Implementの型定義
- 基本はInterfaceと同じシンタックス
    - どのInterfaceを実装しているかを`Impl`の後ろに書く
- Implの場合はフィールド変数を定義できる
- `実装名.h`の命名規則 (`PhilsImp.h` )
```c
typedef struct PhilsImpl <> impl Phils {
  int self;
  struct AtomicT_int* Leftfork;
  struct AtomicT_int* Rightfork;
  __code next(...);
} PhilsImpl;
```
---
# Interfaceの実装時の型名の省略
- 従来は型名を必ず書かなければならなかった
    - これはメタ情報なので変換時に書き換える
### 従来

```c
__code pickup_lforkPhilsImpl(struct PhilsImpl* phils, __code next(...)) {
    struct AtomicT_int* left_fork = phils->Leftfork;
    goto left_fork->checkAndSet(-1, phils->self, pickup_rforkPhilsImpl, eatingPhilsImpl);

}
```
### 現在

```c
__code pickup_lfork(struct PhilsImpl* phils, __code next(...)) {
    struct AtomicT_int* left_fork = phils->Leftfork;
    goto left_fork->checkAndSet(-1, phils->self, pickup_rfork, eating);

}
```

---
### トランスパイル前
```c
__code pickup_lfork(struct PhilsImpl* phils, __code next(...)) {
    struct AtomicT_int* left_fork = phils->Leftfork;
    goto left_fork->checkAndSet(-1, phils->self, pickup_rfork, eating);

}
```
### トランスパイル後
```c
__code pickup_lforkPhilsImpl(struct Context *context,struct PhilsImpl* phils, enum Code next) {
    struct AtomicT_int* left_fork = phils->Leftfork;
    Gearef(context, AtomicT_int)->atomicT_int = (union Data*) left_fork;
    Gearef(context, AtomicT_int)->oldData = -1;
    Gearef(context, AtomicT_int)->newData = phils->self;
    Gearef(context, AtomicT_int)->next = C_pickup_rforkPhilsImpl;
    Gearef(context, AtomicT_int)->fail = C_eatingPhilsImpl;
    goto meta(context, left_fork->checkAndSet);

}
```
---
# Interfaceのパーサーの導入
- PerlでのInterfaceの情報の取得は、CbC自体のファイルの解析処理と共通だった
    - Interfaceならではの情報が取れない
    - スクリプトに直接書かれているので他のツールが使えない
- モジュール化したInterfaceのパーサーを導入した
    - Perlフレームワークを使う一連のツールの作製が可能になった
        - InterfaceとImplを見た実装の雛形ファイルの作製ツール
        - コード変換時にInterfaceのAPIに関連するチェック機能

---
# 今回の修論での変更点/追加点の一覧

|項目|従来|今回の修論|
|-|-|-|
|実装の型ファイル|無し|新たに導入|
|union Dataの定義|手書き|自動生成|
|実装のCodeGearの型名|手書き|自動生成|
|実装のCbCファイル|手書き|雛形を自動生成|
|別のInterfaceの出力を取得するStub|手書き|自動生成|
|Interfaceの未実装の場合の警告|無し|新たに導入|
|Interfaceのparser|スクリプトに埋め込み|モジュール化|
|MetaCodeGearの差し替え処理|手書き|meta.pmによって自動化|


---
# Interfaceの実装の雛形生成コマンドの実装
- Interfaceと実装する型が決まると、最低限書かないといけないCodeGearが決まる
- 従来は手作業でCbCファイルにCodeGearの定義を書いて実装していた
    - コンストラクタも手書き
- JavaはIDEで、golangはコマンドとして雛形を生成するものを用意している
    - GearsOSでも導入した
- 実装の型ファイルを引数で渡すと雛形を生成する
```shell
$perl too/impl2cbc.pl SingleLinkedStack.h
```


---
# PerlトランスパイラでのInterfaceのエラー生成
- Interfaceの実装時に様々なミスをする可能性がある
    - APIを完全に実装していない
    - 呼び出しの引数を間違えている
    - 無いAPIを呼び出している
- 従来は変換した後CbCコンパイラがコンパイルする際や、実行時にしかエラーが出なかった
    - どの記述でエラーが出たのかの特定が困難
    - CbCコンパイラがコンパイルする前にトランスパイラで検知したい

---
# 実装をし忘れているAPIがあった場合のエラー終了
- PerlトランスパイラでのInterfaceのエラー生成
    - CbCコンパイラが動く前にエラーを検知

```
[ 33%] Generating c/examples/DPP2/PhilsImpl.c
[ERROR] Not define eating at examples/DPP2/PhilsImpl.cbc
make[3]: *** [CMakeFiles/DPP2.dir/build.make:101: c/examples/DPP2/PhilsImpl.c] Error 25
make[2]: *** [CMakeFiles/Makefile2:442: CMakeFiles/DPP2.dir/all] Error 2
make[1]: *** [CMakeFiles/Makefile2:450: CMakeFiles/DPP2.dir/rule] Error 2
make: *** [Makefile:293: DPP2] Error 2
```

---
# Context定義ファイルの自動生成
- ContextはすべてのDataGearの型情報をunion Dataとして持つ必要がある
    - すべてのDataGearの定義をunion Dataの定義内に書く
- これは今まで手書きで作製していた
    - Interfaceの型定義ファイルを導入したので、自動生成が可能になった
- ビルド時に使用しているDataGearを回収し、 context.hを作製する
```c
union Data {
    struct Stack {
        ...
    } Stack;
    struct SingleLinkedStack {
        ...
    } SingleLinkedStack;
}
```
---
# StubCodeGear
- 実行したいCodeGearの直前に実行されるMetaCodeGear
- contextからDataGearを取り出す操作をする
- すべてのノーマルレベルのCodeGearに付随する
    - Perlトランスパイラでビルド時に自動生成
![w:632 h:10cm](./stubCodeGear.svg)

---
# 出力があるIntefaceのAPI
## Stackから値を2つ取得するpop2 APIの定義の一部
- 値を2つとってくるのがGearsOSだと煩雑なので定義したAPI
    - 継続先に値を2つ書き込む
```c
__code pop2(struct SingleLinkedStack* stack, __code next(union Data* data, union Data* data1, ...)) {
    if (stack->top) {
        data = stack->top->data;
        stack->top = stack->top->next;
    } else {
        data = NULL;
    }
    if (stack->top) {
        data1 = stack->top->data;
        stack->top = stack->top->next;
    } else {
        data1 = NULL;
    }
    goto next(data, data1, ...);
}
```

---
## メタに変換されたpop2
- Contextの中のDataGearの保存場所にあるStack Interfaceにdataを書き込んでいる

```c
__code pop2SingleLinkedStack(struct Context *context,struct SingleLinkedStack* stack,
 enum Code next,union Data **O_data,union Data **O_data1) {
  Data* data  __attribute__((unused))  = *O_data;
  Data* data1  __attribute__((unused))  = *O_data1;
    if (stack->top) {
        data = stack->top->data;
...
*O_data = data;
  *O_data1 =  data1;
    goto meta(context, next);
}
```


```c
__code pop2SingleLinkedStack_stub(struct Context* context) {
  SingleLinkedStack* stack = (SingleLinkedStack*)GearImpl(context, Stack, stack);
  enum Code next = Gearef(context, Stack)->next;
  Data** O_data = &Gearef(context, Stack)->data;
  Data** O_data1 = &Gearef(context, Stack)->data1;
  goto pop2SingleLinkedStack(context, stack, next, O_data, O_data1);
}
```


---
## pop2 APIの呼び出し(ノーマルレベル)
- 継続で渡している `pop2Test1`で2つの値を受け取りたい

```c
__code pop2Test(struct StackTestImpl3* stackTest, struct Stack* stack, __code next(...)) {
    goto stack->pop2(pop2Test1);
}
```

- 受け取り側

```c
__code pop2Test1(struct StackTestImpl3* stackTest, union Data* data,
 union Data* data1, struct Stack* stack, __code next(...)) {
    String* str = (String*)data;
    String* str2 = (String*)data1;

    printf("%d\n", str->size);
    printf("%d\n", str2->size);
    goto next(...);
}
```


---
# Stackからの値を受け取るCodeGear
- 継続で渡されたCodeGearがContextから値をとってくるMetaCodeGearは手書きする必要があった
    - Perlトランスパイラで対応するCodeGearを自動生成するようになった




---
## 別のInterfaceの出力を受けるCodeGearのメタ計算部分の自動生成
- `goto interface->method()`している箇所を読み取る
    - `interface`がどのInterfaceなのかをPerlトランスパイラで特定させた
    - 特定したInterfaceをパーサーを呼び出して情報を取得
    - APIごとに出力があるかを調査
- 出力があったら、継続で渡しているCodeGearの入力を、呼び出しているInterfaceからとるように修正
    - データの取り出しはStubでしているので、新たなStubを作製した
---
## 元のStub
```c
__code pop2Test1StackTestImpl3_stub(struct Context* context) {
  StackTestImpl3* stackTest = (StackTestImpl3*)GearImpl(context, StackTest, stackTest);
  Data* data = Gearef(context, StackTest)->data;
  Data* data1 = Gearef(context, StackTest)->data1;
  Stack* stack = Gearef(context, StackTest)->stack;
  enum Code next = Gearef(context, StackTest)->next;
  goto pop2Test1StackTestImpl3(context, stackTest, data, data1, stack, next);
}
```


## 生成したStub

```c
__code pop2Test1StackTestImpl3_1_stub(struct Context* context) {
  StackTestImpl3* stackTest = (StackTestImpl3*)GearImpl(context, StackTest, stackTest);
  Data* data = Gearef(context, Stack)->data;
  Data* data1 = Gearef(context, Stack)->data1;
  Stack* stack = Gearef(context, StackTest)->stack;
  enum Code next = Gearef(context, StackTest)->next;
  goto pop2Test1StackTestImpl3(context, stackTest, data, data1, stack, next);
}
```

---
# メタ計算の切り替えAPI
- CodeGearが継続するMetaCodeGearを自由に選択できるPerlモジュールを導入した
    - 従来はデフォルトで設定されるMetaCodeGearにしか継続しなかった
    - Perlモジュールを書くことで特定のCodeGearの継続先を変更可能にした
- AspectJのポイントカットが類似機能
![w:932 h:10cm](./metapm.svg)

---
# meta.pm

- `replaceMeta`関数に置換対象を登録
    - `qr//`に正規表現リテラルで置換対象のCodeGearの名前を指定
    - `=>`の先に対応するgoto文の生成関数を指定する

```perl
package meta;

sub replaceMeta {
  return (
    [qr/PhilsImpl/ => \&generateMcMeta],
  );
}

sub generateMcMeta {
  my ($context, $next) = @_;
  return "goto mcMeta($context, $next);";
}
```


---
# Perlトランスパイルで生成されたメタ部分の比較
## meta.pm導入前
- 通常の`goto meta`

```c
__code thinkingPhilsImpl(struct Context *context,struct PhilsImpl* phils, struct Fork* fork, enum Code next) {
    printf("%d: thinking\n", phils->self);
    goto meta(context, C_pickup_lforkPhilsImpl);
}
```

## meta.pm導入後
- モデル検査用のMetaCodeGear`mcMeta`に`goto`する

```c
__code thinkingPhilsImpl(struct Context *context,struct PhilsImpl* phils, struct Fork* fork, enum Code next) {
    printf("%d: thinking\n", phils->self);
    goto mcMeta(context, C_pickup_lforkPhilsImpl);
}
```

---
# 今後の課題
- Perlトランスパイラのリファクタリング
    - 機能が充実したが複雑度が全体的に増してしまった
- PerlトランスパイラのGearsOS本体への組み込み
    - Perlトランスパイラが行っている処理はリンカ、ローダーに近い
    - OSが機能としてサポートしていてほしい
    - GearsOS自身で記述したい
- xv6への組み込み
    - GearsOSを既存のUNIXの置き換えとして実行したい
- ノーマルレベルの記述の純粋関数化
    - CbC/Cベースの記述ではノーマルレベルの記述に限界がある
---
# まとめ
- Perlトランスパイラのフレームワークの機能を充実させた
- Interfaceシステムを改良した
    - 型定義ファイルの導入を行った
        - 定義方法に一貫性が出た
    - Perlトランスパイラで警告を発生させるようになった
- 従来手書きしていたメタな定義をビルド時に自動的に生成するようにした
    - 煩雑な処理や手で実装することによるバグの混入を回避
- MetaCodeGearの制御をユーザー側で行えるようにした
    - モデル検査をメタ計算として自在に組み込むことが可能となった