GNU Lightning - Лисп как функция "Применить" - PullRequest
2 голосов
/ 27 марта 2020

Я пытаюсь сделать что-то вроде Lisp-функции "Apply" с помощью GNU Lightning: функция 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(100,200);
f3(1000,2000,3000);

Реальный выход:

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

1 Ответ

1 голос
/ 28 марта 2020

Если мы добавим вызов к jit_disassemble() в ваш код, мы увидим, что сгенерированный код для вашей 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_pushargr, проблема становится очевидной:

0x7f251128002c      mov    %rbx,%rdi

Таким образом, это устанавливает значение *argv в качестве первого аргумента вызываемой функции (rdi является регистром, который содержит первый аргумент в x64 Соглашение о вызовах). Это происходит несколько раз, потому что это в al oop, но это всегда первый установленный аргумент. Таким образом, когда функция вызывается после l oop, rdi будет содержать значение, которое было записано в него последним в l oop (то есть последнем значении внутри argv) и других регистрах аргументов / ячейках памяти. не будет записано вообще.

Способ, которым работает jit_pushargr, заключается в том, что он будет записывать первый аргумент при первом вызове, а затем второй аргумент при вызове второго время и так далее. Но в вашем коде вы вызываете его только один раз, поэтому первый аргумент записывается только один раз.

Поэтому для создания функции применения с использованием молнии вам нужно будет вызвать jit_pushargr * 1020. * раз. Это означает, что вместо генерации общей функции apply вы захотите определить саму функцию apply в обычном C и вместо этого сгенерировать вспомогательную функцию, которая выдвигает заданное количество аргументов.

В качестве альтернативы вы можете сделать это полностью без молнии, и вместо этого используйте libffi для этого, который был бы более традиционным инструментом для подобных применений и не потребовал бы затрат на генерацию нового кода каждый раз, когда вызывается apply. Конечно, это не помешает вам вызывать функцию apply из кода, сгенерированного молнией.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...