2021/01/12

2021/01/12

# やったこと

  • gmain実装
  • generate_stub.plで内部で.cbcを書き換えられるようになった
    • ファイルにしなくてもPerl内部で変換前のコードを生成出来る
  • genrate_stub.plに簡易デバッグモード追加
  • genrics関連の実装
  • 主査/副査の先生が発表されました

# generate_stub.pl

  • *.cbc(Gearsのsyntaxで書かれたCbC)から *.c (純粋なCbC)に変換するスクリプト
  • cmakeで呼び出す
    • 各.cbcファイルごと処理される
    • cmake上で実行した場合、c/ディレクトリの中に.cファイルが生成される
  • CbCのトランスコンパイラっぽいやつ

# 処理の修正

  • data gear, このファイルが何なのかを調べるgetDataGear

  • 実際にファイルを生成するgenerateDataGearの大きく2つで行われる

  • 以前

    • cbcファイルを読み込んで処理(getDataGear)
    • もう一度読みながら*.cを生成(generateDataGear)
  • 現在

    • 最初にCbCファイルを配列として読み込む
    • 配列を読んで処理(getDataGear)
    • すでに配列に保存されているcbcを読みながら*.cを生成(generateDataGear)
  • 配列にしたので、ファイルを経由せずににPerlでCbCの操作が出来るようになった

    • 何かを適応してstubを生成するのがわりと書きやすくなった

# gmain

  • main関数を作るのが大変
    • initCodeなどを書かないといけない
    • ほとんどコピペで済ましている
  • コピペで済ましているので、gnerate_stub.plを使うことで変換出来るようにした
    • 終了する場合はshutdownに継続する

# 変換前

1
2
3
4
5
6
7
8
9
#interface "Stack.h"
#interface "StackTest.h"
#interface "TaskManager.h"

__code gmain(){
    Stack* stack = createSingleLinkedStack(context);
    StackTest* stackTest = createStackTestImpl3(context);
    goto stackTest->insertTest1(stack, shutdown);
}

# 変換後

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>

#include "../../../context.h"

int cpu_num = 1;
int length = 102400;
int split = 8;
int* array_ptr;
int gpu_num = 0;
int CPU_ANY = -1;
int CPU_CUDA = -1;

__code initDataGears(struct Context *context,struct LoopCounter* loopCounter, struct TaskManager* taskManager) {
    // loopCounter->tree = createRedBlackTree(context);
    loopCounter->i = 0;
    taskManager->taskManager = (union Data*)createTaskManagerImpl(context, cpu_num, gpu_num, 0);
    goto meta(context, C_prevTask);
}

__code initDataGears_stub(struct Context* context) {
	LoopCounter* loopCounter = Gearef(context, LoopCounter);
	TaskManager* taskManager = Gearef(context, TaskManager);
	goto initDataGears(context, loopCounter, taskManager);
}

__code prevTask(struct Context *context,struct LoopCounter* loopCounter) {
    printf("cpus:\t\t%d\n", cpu_num);
    printf("gpus:\t\t%d\n", gpu_num);
    printf("length:\t\t%d\n", length);
    printf("length/task:\t%d\n", length/split);
    /* puts("queue"); */
    /* print_queue(context->data[ActiveQueue]->queue.first); */
    /* puts("tree"); */
    /* print_tree(context->data[Tree]->tree.root); */
    /* puts("result"); */
    goto meta(context, C_createTask);
}


__code prevTask_stub(struct Context* context) {
	LoopCounter* loopCounter = Gearef(context, LoopCounter);
	goto prevTask(context, loopCounter);
}

__code createTask(struct Context *context,struct LoopCounter* loopCounter, struct TaskManager* taskManager) {
    Stack* stack = createSingleLinkedStack(context);
    StackTest* stackTest = createStackTestImpl3(context);
    Gearef(context, StackTest)->stackTest = (union Data*) stackTest;
    Gearef(context, StackTest)->stack = stack;
    Gearef(context, StackTest)->next = C_shutdown;
    goto meta(context, stackTest->insertTest1);
}

__code createTask_stub(struct Context* context) {
	LoopCounter* loopCounter = Gearef(context, LoopCounter);
	TaskManager* taskManager = Gearef(context, TaskManager);
	goto createTask(context, loopCounter, taskManager);
}

__code shutdown(struct Context *context,struct TaskManager* taskManager) {
    Gearef(context, TaskManager)->taskManager = (union Data*) taskManager;
    Gearef(context, TaskManager)->next = C_exit_code;
    goto meta(context, taskManager->shutdown);
}

__code shutdown_stub(struct Context* context) {
    goto shutdown(context, &Gearef(context, TaskManager)->taskManager->TaskManager);
}



void init(int argc, char** argv) {
    for (int i = 1; argv[i]; ++i) {
        if (strcmp(argv[i], "-cpu") == 0)
            cpu_num = (int)atoi(argv[i+1]);
        else if (strcmp(argv[i], "-l") == 0)
            length = (int)atoi(argv[i+1]);
        else if (strcmp(argv[i], "-s") == 0)
            split = (int)atoi(argv[i+1]);
        else if (strcmp(argv[i], "-cuda") == 0) {
            gpu_num = 1;
            CPU_CUDA = 0;
        }
    }
}

int main(int argc, char** argv) {
    init(argc, argv);
    struct Context* main_context = NEW(struct Context);
    initContext(main_context);
    main_context->next = C_initDataGears;
    goto start_code(main_context);
}

# コンパイル時チェック機能

  • Interfaceの引数があってないのがありがち
    • generate_stub.plで生成された.cと.cbcをにらめっこしないといけない
    • 気づくまで難しい, 気づかないケースがある(今までの実装)
  • generate_stub.plの中でinterface gotoをしている箇所で引数検査を行う
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
[
    [0] {
        argc   1,
        args   "Impl* phils, __code next(...)",
        name   "thinking"
    }
]
[
    [0] "fork0",
    [1] " __exit"
]
1
[EROR] invalid arg     par goto phils0->thinking(fork0, __exit);
  you shoud impl Impl* phils, __code next(...)
  • そもそもそんなメソッドが無いケースも判定
1
2
[ERROR] not found phils0 definition at     par goto phils0->think(fork0, __exit);
 in c/examples/DPP/main.c

# debug

  • 実行時に--debugをつけるとデバッグオプションが起動
  • Perlで正規表現マッチしまくっているが、どこでマッチしたかデバッグするのが難しい
    • 具体的にPerlの何行目にマッチしたのかを表示してくれる
1
2
            } elsif (/^(.*)goto (\w+)\((.*)\);/) {
                debug_print("generateDataGear",__LINE__, $_) if $opt_debug;
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
$ perl generate_stub.pl --debug examples/pop_and_push/StackTest2Impl.cbc
[getDataGear] match 175 : #interface "StackTest2.h"
[getDataGear] match 137 : typedef struct StackTest2 <> {
[getDataGear] match 327 :   __code insertTest1(Impl* stackTest2, struct Stack* stack, union Data* data1, __code next(...));
[getDataGear] match 327 :   __code gotoOtherInterface(Impl* stackTest2, struct Stack* stack, union Data* data1, struct StackTest* stackTest, __code next(...));
[getDataGear] match 327 :   __code next(...);
[getDataGear] match 309 : } StackTest2;
[getDataGear] match 336 : } StackTest2;
[getCodeGear] match 381 : typedef struct StackTest2 <> {
[getCodeGear] match 386 :   __code insertTest1(Impl* stackTest2, struct Stack* stack, union Data* data1, __code next(...));
[getCodeGear] match 386 :   __code gotoOtherInterface(Impl* stackTest2, struct Stack* stack, union Data* data1, struct StackTest* stackTest, __code next(...));
[getCodeGear] match 386 :   __code next(...);
[getDataGear] match 153 : StackTest2* createStackTest2Impl(struct Context* context) {
[getDataGear] match 206 : __code insertTest1_StackTest2Impl(struct StackTest2Impl* stackTest2, struct Stack* stack, union Data* data1, __code next(...)) {
[getDataGear] match 244 :     goto stack->push(data1, next);
[AUTOINCLUDE] Forget #interface 'Stack'  declaration in examples/pop_and_push/StackTest2Impl.cbc at generate_stub.pl line 265.
[getDataGear] match 137 : typedef struct Stack<>{
[getDataGear] match 327 :         __code clear(Impl* stack,__code next(...));
[getDataGear] match 327 :         __code push(Impl* stack,union Data* data, __code next(...));
[getDataGear] match 327 :         __code pop(Impl* stack, __code next(union Data* data, ...));
[getDataGear] match 327 :         __code pop2(Impl* stack, __code next(union Data* data, union Data* data1, ...));
[getDataGear] match 327 :         __code isEmpty(Impl* stack, __code next(...), __code whenEmpty(...));
[getDataGear] match 327 :         __code get(Impl* stack, __code next(union Data* data, ...));
[getDataGear] match 327 :         __code get2(Impl* stack, __code next(union Data* data, union Data* data1, ...));
[getDataGear] match 327 :         __code next(...);
[getDataGear] match 327 :         __code whenEmpty(...);
[getDataGear] match 309 : } Stack;
[getDataGear] match 336 : } Stack;
[getCodeGear] match 381 : typedef struct Stack<>{
[getCodeGear] match 386 :         __code clear(Impl* stack,__code next(...));
[getCodeGear] match 386 :         __code push(Impl* stack,union Data* data, __code next(...));
[getCodeGear] match 386 :         __code pop(Impl* stack, __code next(union Data* data, ...));
[getCodeGear] match 386 :         __code pop2(Impl* stack, __code next(union Data* data, union Data* data1, ...));
[getCodeGear] match 386 :         __code isEmpty(Impl* stack, __code next(...), __code whenEmpty(...));
[getCodeGear] match 386 :         __code get(Impl* stack, __code next(union Data* data, ...));
[getCodeGear] match 386 :         __code get2(Impl* stack, __code next(union Data* data, union Data* data1, ...));
[getCodeGear] match 386 :         __code next(...);
[getCodeGear] match 386 :         __code whenEmpty(...);
[getDataGear] match 206 : __code gotoOtherInterface(struct StackTest2Impl* stackTest2, struct Stack* stack, union Data* data1, struct StackTest* stackTest, __code next(...)) {
[getDataGear] match 244 :   goto stack->push(data1, stackTest->pop2Test);
[generateDataGear] match 655 : #include "../../../context.h"
[generateDataGear] match 650 : #interface "StackTest2.h"
[generateDataGear] match 783 :
[generateDataGear] match 783 : // ----
[generateDataGear] match 783 : // typedef struct StackTest2Impl <Self, Isa> impl StackTest2 {
[generateDataGear] match 783 : //   __code next(...);
[generateDataGear] match 783 : // } StackTest2Impl;
[generateDataGear] match 783 : // ----
[generateDataGear] match 783 :
[generateDataGear] match 783 : StackTest2* createStackTest2Impl(struct Context* context) {
[generateDataGear] match 783 :     struct StackTest2* stackTest2  = new StackTest2();
[generateDataGear] match 783 :     struct StackTest2Impl* stack_test2impl = new StackTest2Impl();
[generateDataGear] match 783 :     stackTest2->stackTest2 = (union Data*)stack_test2impl;
[generateDataGear] match 783 :     stackTest2->insertTest1 = C_insertTest1_StackTest2Impl;
[generateDataGear] match 783 :     return stackTest2;
[generateDataGear] match 783 : }
[generateDataGear] match 783 :
[generateDataGear] match 674 : __code insertTest1_StackTest2Impl(struct StackTest2Impl* stackTest2, struct Stack* stack, union Data* data1, __code next(...)) {
[generateDataGear] match 788 :     goto stack->push(data1, next);
[generateDataGear] match 1030 : }
[generateDataGear] match 1054 : }
[generateDataGear] match 783 :
[generateDataGear] match 674 : __code gotoOtherInterface(struct StackTest2Impl* stackTest2, struct Stack* stack, union Data* data1, struct StackTest* stackTest, __code next(...)) {
[generateDataGear] match 788 :   goto stack->push(data1, stackTest->pop2Test);
[generateDataGear] match 1030 : }
[generateDataGear] match 1054 : }
[generateDataGear] match 783 :
[generateDataGear] match 783 :

# generics関連

  • 今までのヘッダーファイルのtypedef struct SingleLinkedStack <Type, Impl> {の<Type, Impl>がこれと言った意味がなかった
    • 一種の特殊変数(型エイリアス)っぽく使っていたけれど、特に意味がなかった
    • 型エイリアスならグローバルで定義(== Gearsの構文としてサポートしているべき)な気がする
  • いらんそうなので全部削除した
  • 今まで使っていた型はデフォルトで定義していることにしたい
    • Self
    • Impl
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
typedef struct Stack<>{
        __code clear(Impl* stack,__code next(...));
        __code push(Impl* stack,union Data* data, __code next(...));
        __code pop(Impl* stack, __code next(union Data* data, ...));
        __code pop2(Impl* stack, __code next(union Data* data, union Data* data1, ...));
        __code isEmpty(Impl* stack, __code next(...), __code whenEmpty(...));
        __code get(Impl* stack, __code next(union Data* data, ...));
        __code get2(Impl* stack, __code next(union Data* data, union Data* data1, ...));
        __code next(...);
        __code whenEmpty(...);
} Stack;

# genericsの使用例

いくつかのパターンが存在する

# 型宣言時にgenericsを使用する

  • <>の中に型変数を導入する
    • genericsになる
  • 複数存在しても可能
  • genericsが満たしていてほしいInterfaceを:のあとに続けてかける
    • この場合はSayは保証される
    • 保証はコンパイルタイム (Perl level)
    • 実行時タイム (cbc level)
1
2
3
4
5
typedef struct GenericsTest <T:Say> {
  __code print(Impl* generics_test, T* pointer, __code next(...));
  __code print2(Impl* generics_test, D* integer, __code next(...));
  __code next(...);
} GenericsTest;
  • Tの境界が指定されていない場合
    • 置き換えられるかの確認
    • 置き換えられそうならコードを生成する
      • 型(header)
      • CodeGear
  • Tの境界が指定されている場合
    • 型チェック
      • data gearの同一性
      • primitive typeはwrapeperをいれる?
    • 置き換えられそうなら置換する
  • Genericsを使っている対象がInterfaceの場合はめんどう
    • いずれかのImpl側で実装している必要がある
    • どのImplがどのInterfaceの実装なのかはPerlレベルで判定済み
      • generate_contextあたりで判定したい
  • c/以下に型名でファイルを生成して、それをコンパイル時に使う
    • generate_stubからc/以下をアクセスする必要がある

# 現状

  • とりあえずgenerics使っている箇所の検知まで到達
    • Interface_Genericsの命名規則
    • AtomicT_Intみたいに変換される
1
2
3
4
5
6
typedef struct PhilsImpl <> impl Phils {
  int self;
  AtomicT<int> Leftfork;
  Atomic<int> Rightfork;
  __code next(...);
} PhilsImpl;
1
2
3
4
5
6
7
8
9
$ perl tools/check_convert_context_struct.pl examples/DPP/PhilsImpl.h
[INFO] use generics AtomicT int at AtomicT<int> Leftfork;
[INFO] use generics Atomic int at Atomic<int> Rightfork;
    struct PhilsImpl {
        int self;
        AtomicT_Int Leftfork;
        Atomic_Int Rightfork;
        enum Code next;
    } PhilsImpl;

# codeGearの内部で別のgenericsを使うケース

1
2
3
4
5
6
typedef struct GenericsTest <T:Say, D> {
  __code print(Impl* generics_test, T* pointer, __code next(...));
  __code print2(Impl* generics_test, D* integer, __code next(...));
  __code print3(Impl* generics_test, AtomicT<int>* atomic_t, __code next(...));
  __code next(...);
} GenericsTest;
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
$ perl tools/check_convert_context_struct.pl examples/sandbox/GenericsTest.h
    struct GenericsTest {
        union Data* generics_test;
        T* pointer;
        D* integer;
        AtomicT_int* atomic_t;
        enum Code print;
        enum Code print2;
        enum Code print3;
        enum Code next;
    } GenericsTest;
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
$ perl tools/check_convert_context_struct.pl examples/sandbox/GenericsTest.h
    struct GenericsTest {
        union Data generics_test*;
        GENERICS pointer*;
        GENERICS integer*;
        AtomicT_int atomic_t*;
        enum Code print;
        enum Code print2;
        enum Code print3;
        enum Code next;
    } GenericsTest;

# context.hのぬけ

  • さっき直した
    • context.hの中身を回収する正規表現がちょっとバグってた
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
#endif
    ///home/anatofuz/src/firefly/hg/Gears/Gears/src/parallel_execution/../parallel_execution/AtomicT.h
#ifndef ATOMICT_STRUCT
    struct AtomicT {
        union Data* atomicTImpl;
        union Data newData;
        union Data init;
        enum Code checkAndSet;
        enum Code set;
        enum Code next;
        enum Code fail;
    } AtomicT;
#define ATOMICT_STRUCT
#else
    struct AtomicT;
#endif
Licensed under CC BY-NC-SA 4.0
comments powered by Disqus
Built with Hugo
Theme Stack designed by Jimmy