mir API 介紹

TODO

  • 整理入門到中階的範例傳到 github,不然這篇不好懂。

初始化

MIR 初始化跟結束分別為 MIR_init() / MIR_finish (MIR_context_t ctx)

當建立好 mir 的物件,可以將要實作的功能包裝成模組(module),mir 會用內建的雙向 linked list 將模組連接在一起,而模組包含了下面這些項目(item)

  • Function
  • Import
  • Export
  • Foward declaration
  • Prototype
  • Data
  • Reference data
  • Expression Data
  • Memory segment

模組的初始化跟結束分別為 MIR_new_module / MIR_finish_module

c2mir

將 C 程式碼當作輸入的話要透過 c2mir 的 API 來達成,其中 c2mir_compileget_func 會用來讀取程式碼,而 jit_ptr 會當作參數傳入 get_func,讀取完畢會回傳 EOF。

typedef struct jit_item {
    char *code;
    size_t code_size;
    size_t curr;
} jit_item_t;

int get_func(void *data)
{
    jit_item_t *item = data;
    return item->curr >= item->code_size ? EOF : item->code[item->curr++];
}

jit_item_t *jit_ptr = (jit_item_t *)malloc(sizeof(jit_item_t *));
jit_ptr->curr = 0;
jit_ptr->code = 
    "int add(int a,int b) {                  \
        int c = a + b;                       \
        printf(\"%d + %d = %d\n\", a, b, c); \
        return a + b;                        \
    }\n ";
jit_ptr->code_size = strlen(jit_ptr->code);

if (!c2mir_compile(ctx, options, get_func, jit_ptr, name, NULL)) {
    perror("Compile failure");
    exit(EXIT_FAILURE);
}

c2mir_compile 成功之後,會當作模組給 ctx 接上,所以如果今天是做直譯器的話,要呼叫編譯好的函數就要從 ctx 的模組的尾把取得編譯好的模組,再從模組裡面找編譯好的函數,透過 MIR-generator 產生機器碼就可以當作函式來呼叫了。

MIR_module_t module = DLIST_TAIL(MIR_module_t, *MIR_get_module_list(ctx));
MIR_item_t func = DLIST_HEAD(MIR_item_t, module->items);    

size_t func_len = DLIST_LENGTH(MIR_item_t, module->items);
int a = 10, b = 50;
for (int i = 0; i < func_len; i++, func = DLIST_NEXT(MIR_item_t, func)) {
    if (func->item_type == MIR_func_item) {
        int (*arich)(int, int) = MIR_gen(ctx, 0, func);
        arich(a, b);
        printf("%d + %d = %d\n", a, b, c);
        break;
    }
}

連結

執行 mir 之前需要將模組載入跟連結

MIR_load_module (MIR_context ctx, MIR_module_t m)
MIR_link (MIR_context ctx, void (*set_interface) (MIR_item_t item), void * (*import_resolver) (const char *))

MIR_link 的 set_interface 有3種

  • MIR_set_interp_interface: 呼叫函數的時候將 MIR 的直譯
  • MIR_set_gen_interface: MIR-generator 會將所有載入的函式產生機器碼,當呼叫函數就會執行機器碼
  • MIR_set_lazy_gen_interface: MIR-generator 只會當函式第一次被呼叫的時候產生機器碼

import_solver 則是當 mir 的 context 裡面有未使用 MIR 定義的函式(e.g. printf) 就會傳入函式名稱當作 key 來查詢,如果有找到函式就會回傳函式的地址。

未使用 MIR 定義的函式只會被呼叫不會被 MIR-generator 優化

typedef struct {
    char *name;
    void (*func)(void);
} func_obj_t;

func_obj_t func_list[4] = {
    {"printf", printf},
    {"puts", puts},
    {"show_result", show_result},
    {"show_item", show_item},
    {NULL, NULL}
};

void *import_resolver(const char *name)
{
    size_t len = sizeof(func_list) / sizeof(func_obj_t);
    for (int i = 0; len; i++) 
        if (!strcmp(func_list[i].name, name))
            return func_list[i].func;
    return NULL;
}

MIR_link(ctx, MIR_set_gen_interface, import_resolver);

執行

當模組都載入跟連結後就可以透過 MIR-generator 來產生機器碼了。

void MIR_gen_init (MIR_context_t ctx, int gens_num);
void MIR_gen_finish (MIR_context_t ctx);
void MIR_gen_set_optimize_level (MIR_context_t ctx, int gen_num, unsigned int level);
void *MIR_gen (MIR_context_t ctx, int gen_num, MIR_item_t func_item);

gens_num 表示要使用幾個 generator 來產生機器碼,每個 generator 都是不同的執行緒
MIR_gen 產生函式的機器碼,並回傳函式的地址,可以當作 C 函式來呼叫,呼叫後會執行產生的機器碼。
MIR_gen_set_optimize_level 設定優化的等級,一共有4級(0 ~ 3)可以選,等級愈高優化的愈激進,編譯時間也愈長,可以參考下圖的流程。

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →