view slide/index.md @ 136:fe84812351e0

update
author anatofuz <anatofuz@cr.ie.u-ryukyu.ac.jp>
date Mon, 08 Feb 2021 11:01:31 +0900
parents 58a8b3df7e9b
children c6247127e7f4
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のビルドシステム
- 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)

---
# ノーマル、メタレベルの計算とGearsOS
- トランスパイラで生成するメタレベルの計算をより拡張したい
    - 手書きで実装しなければならなかったものを自動化したい
- 本研究ではトランスパイラでメタ計算を生成する機能を改善した
    - 他にもメタ計算にまつわる様々な新機能を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);
```

---
# par goto
- GearsOSの並行呼び出し構文
- 通常のgoto文の様に`par goto`と書くと、並列実行される

```c
    Phils* phils0 = createPhilsImpl(context,0,fork0,fork1);
    Phils* phils1 = createPhilsImpl(context,1,fork1,fork2); 
    Phils* phils2 = createPhilsImpl(context,2,fork2,fork3); 
    Phils* phils3 = createPhilsImpl(context,3,fork3,fork4); 
    Phils* phils4 = createPhilsImpl(context,4,fork4,fork0); 

    par goto phils0->thinking(exit_code);
    par goto phils1->thinking(exit_code);
    par goto phils2->thinking(exit_code);
    par goto phils3->thinking(exit_code);
    par goto phils4->thinking(exit_code);
```

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

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


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

---
# GearsOSのシステム構成
- MetaDataGearであるContext, TaskManager, Workerによって構成

![](./gears_structure.svg)

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


---
# メタレベルでのDataGearの管理
- メタレベルではDataGearを纏めて管理する必要がある
- すべてのDataGearはCの共用体を使って定義される
    - `union Data`型
- `union Data`はすべてのDataGearにキャストすることが可能
    - Contextには`union Data`としてDataGearを保存する
- メタレベルの計算では必ずこの`union Data`が利用される

```c
union Data {
    struct Stack {
        ...
    } Stack;
    struct SingleLinkedStack {
        ...
    } SingleLinkedStack;
    struct Context Context;
    ...
}
```

---
# union Dataの定義

- union Data型はcontext.hに定義されている
- 計算で使うすべてのDataGearの構造体の定義を列挙する
    - 従来は手書きですべて記述
    - Intefaceの定義を書き、CbCを書き、context.hに追記...
- 手書きで生成していたので、いくつかの問題があった
    - 整理しないと計算に使わないDataGearの定義が残る
    - Interfaceの定義ファイルと食い違うケースがある
    - DataGearの数が増えると手書きするのが煩雑
- すべてのメタレベルの計算の元となる重要なデータ
    - 手書きの場合ミスが発生する可能性が高い




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

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

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

---
# CodeGearの番号
- ビルドするCbCファイルをPerlで解析し、`__code`を数え上げる
    - 連番でenumを生成する
- Contextの中にCodeGearの配列がある
    - StubCodeGearへの参照が含まれている

```c
enum Code {
    C_checkAndSetAtomicReference,
    C_clearSingleLinkedStack,
    C_clearSynchronizedQueue,
    C_createTask,
    C_decrementTaskCountTaskManagerImpl,
    C_exit_code,
    C_get2SingleLinkedStack,
    ...
};
```
---
# DataGearの番号
- union Dataで定義されている構造体一覧からPerlで生成される
    - context.hの内容に依存する

```c
enum DataType {
    D_Code,
    D_Atomic,
    D_AtomicReference,
    D_CPUWorker,
    D_Context,
    D_Element,
    ...
};
```

---
# StubCodeGear
- DataGearにアクセスするにはContextから番号を指定して行う
- メタレベルのCodeGearは引数はContextのみになっている
    - CodeGearの引数に指定していたものは、ContextからMetaCodeGearで取り出す必要がある
- StubCodeGearはCodeGearの直前に実行されて、DataGearをContextから取得する


![w:540 h:400](./contextContinuation.svg)


---
# ノーマルレベルのpopの確認

```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の実装
- 継続先は`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);
}
```


---
# popのStubCodeGear

- CodeGearで必要なデータをContextから取り出す操作
    - 実行したいCodeGearの直前に実行される
- この場合はContext中のStackの引数格納場所から値を取り出す

```c
__code popSingleLinkedStack_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;
  goto popSingleLinkedStack(context, stack, next, O_data);
}
```

---
# メタに変換された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);
}
```




---
# 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のメタレベルについてのまとめ
- ノーマルレベルで書いたCodeGearにはContextの操作が付け加わる
- 継続への書き込み、CodeGearの値の受け取りはContext経由で行われる
    - 値の受け取りはStubCodeGearが行う
    - PerlトランスパイラでMetaCodeGearが生成される
- メタレベルへの変換はビルド時にPerlトランスパイラによって行われる
- メタレベルの情報はPerlトランスパイラが生成するファイがル重要
    - DataGearの扱いはcontext.hに定義したunion Dataに依存する


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

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

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

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


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

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

---
# 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の実装時の型名の省略
- APIのCodeGearには、トランスパイル時に具体的な型が名前につけられる
### トランスパイル前
```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)->next = C_pickup_rforkPhilsImpl;
    Gearef(context, AtomicT_int)->fail = C_eatingPhilsImpl;
    goto meta(context, left_fork->checkAndSet);
}
```



---
# PerlトランスパイラでのInterfaceのエラー生成
- 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
```

---
# 手書きからの解放
- 従来はメタレベルの計算に関係する処理・ファイルを手書きするケースが多々あった
    - 手書きではなく型定義ファイルからのファイル生成
    - トランスパイル時にコードの解析を行いメタ計算を自動生成


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

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




---
# Context定義ファイルの自動生成
- ContextはすべてのDataGearの型情報をunion Dataとして持つ必要がある
    - すべてのDataGearの定義をunion Dataの定義内に書く
- これは今まで手書きで作製していた
    - Interfaceの型定義ファイルを導入したので、自動生成が可能になった
- ビルド時に使用しているDataGearを回収し、 context.hを作製する

---
# 出力がある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 APIの呼び出し
- pop2を呼び出して、Stackの中身を2つ`pop2Test1`で受け取る

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

## pop2 APIからデータの受け取り(ノーマルレベル)
- Stackからデータを2つ受け取る継続

```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(...);
}
```


---
# 出力があるIntefaceのAPIの問題
- 従来は別のInterfaceからの出力を受け取るCodeGearのStubは手で書かないといけなかった
    - Contextにあるデータの場所がずれているので、StubCodeGearがデータをとってきてくれない
        - Interfaceが値をとるContextの場所
        - Stackがdataを書き込むContextの場所
- Perlトランスパイラで呼び出しているAPIのInterfaceを解析し、Contextの場所を明確に指定するStubCodeGearを自動で作製するようにした



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

---