published on in 编译

JIT・Lightning 示例「printf」

在了解lightning的基本使用方式后,我们再来了解如何在lightning中调用c函数。

我们以调用c程序中的printf函数为例,这里先列出完整代码:

#include <stdio.h>
#include <lightning.h>

static jit_state_t *_jit;

typedef void (*pvfi)(int);      /* Pointer to Void Function of Int */

int main(int argc, char *argv[]) {
    pvfi          myFunction;             /* ptr to generated code */
    jit_node_t    *start, *end;           /* a couple of labels */
    jit_node_t    *in;                    /* to get the argument */

    init_jit(argv[0]);
    _jit = jit_new_state();

    start = jit_note(__FILE__, __LINE__);
    jit_prolog();
    in = jit_arg();
    jit_getarg(JIT_R1, in);
    jit_prepare();
    jit_pushargi((jit_word_t)"generated %d bytes\n");
    jit_ellipsis();
    jit_pushargr(JIT_R1);
    jit_finishi(printf);
    jit_ret();
    jit_epilog();
    end = jit_note(__FILE__, __LINE__);

    myFunction = jit_emit();

    /* call the generated code, passing its size as argument */
    myFunction((char*)jit_address(end) - (char*)jit_address(start));
    jit_clear_state();

    jit_disassemble();

    jit_destroy_state();
    finish_jit();
    return 0;
}

标记注释

start = jit_note(__FILE__, __LINE__);
/* ... ... */
end = jit_note(__FILE__, __LINE__);

这两条指令调用了jit_note宏,它在jit代码中创建了一个注释;jit_note的参数通常是文件名字符串和行号整数,但如果只需要在代码中创建一个简单的标记,则使用NULL作为字符串参数是完全有效的。

处理可变参量

jit_ellipsis();

由于函数printf是个可变参函数,所以需要调用jit_ellipsis()

字符串参数处理

jit_pushargi((jit_word_t)"generated %d bytes\n");

使用jit_word_t做字符串参数的类型转换。

引入并调用c函数

jit_prepare();
/* ... ...*/
jit_finishi(printf);

在jit中调用printf函数,需要在装填参数前先执行jit_prepare()函数,在完成参数装填后需要执行jit_finishi(printf)

显式的写入结语

jit_epilog();

lightning可以自己处理代码段截止内容,但是有的时候为了能准确的标记代码段的地址需要我们调用jit_epilog()来显式的处理。

通过调用函数执行jit代码

myFunction((char*)jit_address(end) - (char*)jit_address(start));
jit_clear_state();

调用jit_address获取字节地址需要使用到jit state中的信息,因此应当放在jit_clear_state()前执行。

标准输出

jit_disassemble();

disassemble会将生成的代码转储到标准输出,除非lightning是在禁用反汇编程序的情况下构建的,在这种情况下不会显示任何输出。