2

I'm trying to make a sort of Lisp like "Apply" function with GNU Lightning: a function F that receive a pointer to a function, an argument count and an array of integers and call G with the right number of parameters.

My code is not working properly. What's wrong? How can I do?

Here the code:

#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;
}

Expected output:

f0();
f1(10);
f2(100,200);
f3(1000,2000,3000);

Real output:

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

1 Answers1

1

If we add a call to jit_disassemble() to your code, we see that the generated code for your f function looks like this:

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

If we look at the code that's generated by jit_pushargr, the problem becomes apparent:

0x7f251128002c      mov    %rbx,%rdi

So this sets the value of *argv as the first argument of the called function (rdi being the register that holds the first argument in the x64 calling convention). It does so multiple times because it's in a loop, but it's always the first argument that's being set. So when the function is called after the loop, rdi will hold the value that was last written to it in the loop (i.e. the last value inside argv) and the other argument registers / memory locations won't have been written to at all.

The way that jit_pushargr works is that it will write to the first argument when you call it the first time, then the second argument when you call it the second time and so on. But in your code you only call it once, so it only ever writes the first argument.

So what you'd need to do to create an apply function using lightning would be to actually call jit_pushargr argc times. That means that instead of generating a general apply function, you'd want to define the apply function itself in normal C and instead generate a helper function that pushes the given number of arguments.

Alternatively you could do this entirely without lightning and instead use libffi for this, which would be the more traditional tool for uses like this and wouldn't incur the overhead of generating new code every time apply is called. Of course this wouldn't prevent you to call your apply function from code generated by lightning.

sepp2k
  • 363,768
  • 54
  • 674
  • 675
  • Thank you very much. The intent is to make a JIT for an interpreter that inline the code of APPLY. This must accept N single arguments and a last one as a list of other arguments...similar to this: int g(p1, p2, p3, p4, p5, p6, p7, p8) { ... } int array[] = {4, 5, 6, 7}; f(g, 0, 1, 2, 3, array); the numer of arguments must be arbitrary. – Tommy Mar 31 '20 at 13:07
  • @Tommy I'm sorry, I'm not entirely clear on what you mean by "inline the code of APPLY" or what you're trying to get at in general. What I get from your first sentence is that you're writing a JIT compiler for your language, and that you want your `apply` function to be usable from that language. Then the second sentence looks to be an explanation of what `apply` does (which seems redundant at this point). What I don't understand is what you're asking me (if anything). – sepp2k Mar 31 '20 at 13:27
  • @Tommy So did you get it working? If you're still stuck, I'd be happy to explain more. – sepp2k Apr 04 '20 at 11:18
  • I'm sorry I didn't answer you anymore but I gave up on the idea. I'm not ready to change the architecture of the interpreter – Tommy Jun 17 '20 at 15:48