C GNU Lightning-类似Lisp的;套用;功能

C GNU Lightning-类似Lisp的;套用;功能,c,compiler-construction,gnu,jit,jitter,C,Compiler Construction,Gnu,Jit,Jitter,我试图用GNU Lightning创建一个类似Lisp的“Apply”函数:一个函数F,它接收一个指向函数的指针、一个参数计数和一个整数数组,并使用正确数量的参数调用G 我的代码工作不正常。发生了什么?我该怎么办 代码如下: #include <stdio.h> #include <stdlib.h> #include <lightning.h> int f0() { printf("f0();\n"); } int f1(int p1) {

我试图用GNU Lightning创建一个类似Lisp的“Apply”函数:一个函数F,它接收一个指向函数的指针、一个参数计数和一个整数数组,并使用正确数量的参数调用G

我的代码工作不正常。发生了什么?我该怎么办

代码如下:

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

int f0() {
    printf("f0();\n");
}

int f1(int p1) {
    printf("f1(%d);\n", p1);
}

int f2(int p1, int p2) {
    printf("f2(%d, %d);\n", p1, p2);
}

int f3(int p1, int p2, int p3) {
    printf("f3(%d, %d %d)\n", p1, p2, p3);
}  


int main (int argc, char **argv) {
    init_jit(argv[0]);

    jit_state_t *_jit = jit_new_state();

    int (*f)(int *g, int argc, int *argv);

    jit_prolog();
        jit_node_t *p_g = jit_arg();
        jit_node_t *p_argc = jit_arg();
        jit_node_t *p_argv = jit_arg();
        jit_getarg(JIT_R0, p_g);
        jit_getarg(JIT_R1, p_argc);
        jit_getarg(JIT_R2, p_argv);
        jit_prepare();

        /* for (  ;  argc;  argc--, argv++) */
        jit_node_t *label = jit_label();
        jit_node_t *zero  = jit_beqi(JIT_R1, 0);
            // *argv
            jit_ldr_i(JIT_V0, JIT_R2);
            jit_pushargr(JIT_V0);

            // Go next
            // argv++
            jit_addi(JIT_R2, JIT_R2, sizeof(int));

            // argc--
            jit_subi(JIT_R1, JIT_R1, 1);

            // 
            jit_patch_at(jit_jmpi(), label);
        jit_patch(zero);
        jit_finishr(JIT_R0);
        jit_reti(0);
    jit_epilog();

    f = jit_emit();

    jit_clear_state();

    f((void *)f0, 0, NULL);

    int a1[] = {10};
    f((void *)f1, 1, a1);

    int a2[] = {100, 200};
    f((void *)f2, 2, a2);

    int a3[] = {1000, 2000, 3000};
    f((void *)f3, 3, a3);

    finish_jit();

    return 0;
}
实际产出:

f0();
f1(10);
f2(200, 2);
f3(3000, 3 -13360)

如果我们在代码中添加对
jit\u反汇编()
的调用,我们会看到为
f
函数生成的代码如下所示:

0x7f2511280000      sub    $0x30,%rsp
0x7f2511280004      mov    %rbx,0x28(%rsp)
0x7f2511280009      mov    %rbp,(%rsp)
0x7f251128000d      mov    %rsp,%rbp
0x7f2511280010      sub    $0x18,%rsp
0x7f2511280014      mov    %rdi,%rax
0x7f2511280017      mov    %rsi,%r10
0x7f251128001a      mov    %rdx,%r11
0x7f251128001d      nopl   (%rax)
0x7f2511280020      test   %r10,%r10
0x7f2511280023      je     0x7f2511280040
0x7f2511280029      movslq (%r11),%rbx
0x7f251128002c      mov    %rbx,%rdi
0x7f251128002f      add    $0x4,%r11
0x7f2511280033      sub    $0x1,%r10
0x7f2511280037      jmpq   0x7f2511280020
0x7f251128003c      nopl   0x0(%rax)
0x7f2511280040      callq  *%rax
0x7f2511280042      xor    %rax,%rax
0x7f2511280045      mov    %rbp,%rsp
0x7f2511280048      mov    0x28(%rsp),%rbx
0x7f251128004d      mov    (%rsp),%rbp
0x7f2511280051      add    $0x30,%rsp
0x7f2511280055      retq
如果我们查看由jit\U pushargr生成的代码,问题就会变得很明显:

0x7f251128002c      mov    %rbx,%rdi
因此,这将
*argv
的值设置为被调用函数的第一个参数(
rdi
是保存x64调用约定中第一个参数的寄存器)。它多次这样做是因为它在一个循环中,但它总是被设置的第一个参数。因此,当在循环后调用函数时,
rdi
将保存循环中最后写入它的值(即,
argv
中的最后一个值),而其他参数寄存器/内存位置根本不会被写入

jit_pushargr
的工作方式是,当您第一次调用它时,它将写入第一个参数,当您第二次调用它时,它将写入第二个参数,依此类推。但是在你的代码中你只调用它一次,所以它只写第一个参数

因此,要使用lightning创建应用函数,需要实际调用
jit\u pushargr
argc
次。这意味着,您不需要生成一个通用的apply函数,而是需要在普通的C语言中定义apply函数本身,并生成一个helper函数来推送给定数量的参数


或者,您可以完全不用lightning,而是使用libffi来实现这一点,这将是用于此类用途的更传统的工具,并且不会在每次调用
apply
时产生生成新代码的开销。当然,这不会阻止您使用lightning生成的代码调用
apply
函数。

非常感谢。目的是为内嵌APPLY代码的解释器创建JIT。这必须接受N个单参数,最后一个作为其他参数的列表…类似于此:int g(p1,p2,p3,p4,p5,p6,p7,p8){…}int数组[]={4,5,6,7};f(g,0,1,2,3,数组);参数的数量必须是任意的。@Tommy很抱歉,我不完全清楚您所说的“内联应用程序代码”是什么意思,或者您通常想要得到什么。我从您的第一句话中得到的是,您正在为您的语言编写一个JIT编译器,并且希望您的
apply
函数能够从该语言中使用。然后,第二句话似乎是对
apply
所做的事情的解释(在这一点上似乎是多余的)。我不明白的是你在问我什么(如果有的话)。@汤米,你让它工作了吗?如果你仍然被困住,我很乐意解释更多。很抱歉,我没有再回答你,但我放弃了这个想法。我还没有准备好改变解释器的架构
0x7f251128002c      mov    %rbx,%rdi