view paper/generate_code.tex @ 62:dcfd2feeb0fd default tip

update
author mir3636
date Sun, 17 Feb 2019 01:53:26 +0900
parents 1e1804a9269a
children
line wrap: on
line source

\chapter{メタレベルのコードの自動生成}

stub Code Gear などの Meta Code Gear は通常ユーザーレベルからは見ることのできない Meta Data Gear である Context を扱うため、ユーザー自身が記述することは望ましくない。
stub Code Gear は Code Gear 毎に記述する必要があるためユーザーの記述量が多くなる。
また、stub Code Gear でユーザーが Context から Data Gear を参照するためのコードが非常に煩雑である。
このため Meta Code Gear は自動生成されるのが望ましい。
そこで Meta Gear を自動生成するためのスクリプトを導入した。
また、このスクリプトによって Context の参照をユーザーレベルから隠すことができ、
ユーザーレベルの Code Gear もシンプルになった。

これらの Meta Code Gear は本来は OS が動的に生成するべきであるが、
現在はコンパイル時に perl スクリプトによって静的に生成を行うことで実現している。

\section{Meta Code Gear の生成}

stub Code Gear は Code Gear 間の継続に挟まれる Meta Code Gear である。
必要な Data Gear を Context から参照し、継続する Code Gear へと渡すための Meta Code Gear である。
Code Gear 毎に記述する必要があり、継続する Code Gear の引数を見て取り出す Data Gear を選択する。
stub Code Gear はユーザーが任意に記述することも出来るが、Meta を扱うため自動生成を行いたい。
そのため、stub Code Gear を 自動生成する generate stub を Perl スクリプトで作成した。
これにより Code Gear の記述量を約半分にすることができる。


ソースコード \ref{StackPop} は ユーザーレベルの Code Gear である。
この Code Gear を generate stub によって変換、
stub Code Gear の生成を行なったコードがソースコード \ref{MetaCodeGear} である。

\begin{lstlisting}[frame=lrbt,label=StackPop,caption={\footnotesize 変換前の Stack pop}]
__code popSingleLinkedStack(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, ...);
}

\end{lstlisting}

\begin{lstlisting}[frame=lrbt,label=MetaCodeGear,caption={\footnotesize 変換後の Stack pop}]

__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);
}

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


生成された stub  Code Gear は、
継続先の Code Gear が引数で指定した Input Data Gear、Output Data Gear を Context から参照している。
Gearef は Context から Data Gear を参照するためのマクロである。
ソースコード\ref{macro} 1行目が Gearef マクロの定義である。
ソースコード\ref{noref} はマクロを用いなかった場合の pop stack の stub Code Gear である。
Context には Allocation 等で生成した Data Gear へのポインタが格納されている。
Code Gear が Context にアクセスする際、
ポインタを使用してデータを取り出すため煩雑なコードとなる。
そこで Code Gear がデータを参照するための Gearef というマクロを定義した。
Gearef は Context から Interface の引数格納用の Data Gear を取り出す。
Gearef に Context と Interface の型を渡すことでデータの参照が行える。

GearImpl マクロは Interface の型に包まれた Data Gear から
実装の Data Gear を取り出すためのマクロである。
ソースコード\ref{macro} 2行目が GearImpl マクロの定義である。
実装の Data Gear を取り出す際も、ポインタでの記述が複雑になってしまうため 同様に GearImpl を定義した。
GearImpl は Context と Interface の型、実装の Data Gear 名を指定することで参照する。

\begin{lstlisting}[frame=lrbt,label=macro,caption={Gearef、GearImpl}]
#define Gearef(context, t) (&(context)->data[D_##t]->t)
#define GearImpl(context, intf, name) (Gearef(context, intf)->name->intf.name)
\end{lstlisting}

\begin{lstlisting}[frame=lrbt,label=noref,caption={マクロを用いない stub Code Gear}]
__code popSingleLinkedStack_stub(struct Context* context) {
    SingleLinkedStack* stack = (SingleLinkedStack*)context->data[D_Stack]->Stack.stack->Stack.stack;
    enum Code next = context->data[D_Stack]->Stack.next;
    Data** O_data = &context->data[D_Stack]->Stack.data;
    goto popSingleLinkedStack(context, stack, next, O_data);
}
\end{lstlisting}

また、Code Gear は継続の際 meta へと goto する。
Context はすべての Code Gear のリストを持っており、継続先の Code Gear へは
enum で対応付けられた Code Gear のアドレスのリストを参照して継続を行う。
この meta もスクリプトにより自動生成される。

stub Code Gear を生成するために generate\_stub は、
ソースコード上の Code Gear を全て取得し、引数からその Code Gear に必要な Data Gear を選択する。

generate\_stub は stub Code Gear を生成する際、Code Gear の引数と Interface を照らし合わせ、
Code Gear が要求する引数の Data Gear を Context から取り出すための Gearef または GearImpl を決定する。

この時既に stub Code Gear が記述されている Code Gear は stub Code Gear が生成されずに無視される。

generate\_stub は Code Gear の変換も行う。
ソースコード \ref{MetaCodeGear} では、popSingleLinkedStack の引数の Output Data Gear を見て、
Output Data Gear の格納を行うコードが挿入されている。
また、継続のコードが goto meta へと変換されている。

\section{Context の生成}
generate\_context は Context を生成する Perl スクリプトである。
Context は生成する際に Code Gear のリストとアドレスの対応、
引数格納用の Data Gear の生成を行う。

このため Gears OS で新たに Code Gear、Data Gear を定義した際には、
Code Gear、Data Gear の enum のリストの更新、引数格納用の Data Gear の Allocation を行うコードの記述を行わなければならなかった。

generate\_context を用いることでこれらの煩雑な記述を自動生成で行えるようにした。

\begin{figure}[ht]
 \begin{center}
  \includegraphics[width=120mm]{./fig/generate_context3.pdf}
 \end{center}
 \caption{generate\_context による Context の生成}
 \label{fig:gc}
\end{figure}

generate\_context は Context の定義が記述されている context.h を読み、Data Gear を取得する。
Code Gear の取得は generate\_stub で生成されたコードを読み、ソースコード内の \_\_code 型を見て行う。
取得した Code Gear、Data Gear の enum の定義は enumCode.h、enumData.h に生成される。

Code Gear、Data Gear の名前とポインタの対応は generate\_context によって生成される enum Code、enum Data を指定することで接続を行う。
generate context は、この Code Gear、Data Gear の初期化のコードの initContext の生成も行う。
(ソースコード \ref{initContext})

また、Gears OS のメタ計算に用いる \_\_code meta や main関数から継続する start\_code、
終了処理を行う exit\_code も生成される。

これらはまとめて target-context.c として生成される。

\begin{lstlisting}[frame=lrbt,label=init_context,caption={\footnotesize 生成された target-context.c}]

#include <stdlib.h>

#include "../context.h"

void initContext(struct Context* context) {
    context->heapLimit = sizeof(union Data)*ALLOCATE_SIZE;
    context->code = (__code(**) (struct Context*)) NEWN(ALLOCATE_SIZE, void*);
    context->data = NEWN(ALLOCATE_SIZE, union Data*);
    context->heapStart = NEWN(context->heapLimit, char);
    context->heap = context->heapStart;

    context->code[C_clearSingleLinkedStack]    = clearSingleLinkedStack_stub;
    context->code[C_exit_code]    = exit_code_stub;
    context->code[C_getSingleLinkedStack]    = getSingleLinkedStack_stub;
    context->code[C_isEmptySingleLinkedStack]    = isEmptySingleLinkedStack_stub;
    context->code[C_popSingleLinkedStack]    = popSingleLinkedStack_stub;
    context->code[C_pushSingleLinkedStack]    = pushSingleLinkedStack_stub;
    context->code[C_stack_test1]    = stack_test1_stub;
    context->code[C_stack_test2]    = stack_test2_stub;
    context->code[C_stack_test3]    = stack_test3_stub;
    context->code[C_stack_test4]    = stack_test4_stub;
    context->code[C_start_code]    = start_code_stub;

#include "dataGearInit.c"

}

__code meta(struct Context* context, enum Code next) {
    // printf("meta %d\n",next);
    goto (context->code[next])(context);
}

__code start_code(struct Context* context) {
    goto meta(context, context->next);
}

__code start_code_stub(struct Context* context) {
    goto start_code(context);
}

__code exit_code(struct Context* context) {
    free(context->code);
    free(context->data);
    free(context->heapStart);
    goto exit(0);
}

__code exit_code_stub(struct Context* context) {
    goto exit_code(context);
}    

// end context_c
\end{lstlisting}

Context には Allocation 等で生成した Data Gear へのポインタが格納されている。
Code Gear は Context を通して Data Gear へアクセスする。
Data Gear の Allocation を行うコードは dataGearInit.cに生成される。