LINUX.ORG.RU

Немного не так выразился с простыми языками. Под простым языком понимаю простой рантайм/VM, та же JVM довольно большая,а CLR так вообще гигантский.

@no-such-file

HHVM вроде довольно комплексная штуковина,но спасибо,гляну.

@LINUX-ORG-RU @i-rinat

Я не знаю на сколько верно называть gcc file.c; a.out JIT,для меня JIT компиляция это когда код функции генерируется во время исполнения и сразу может исполнятся.

Вот пример JIT’a,код генерируется во время исполнения,аллоцируется память для функции и ее сразу вызывают:


int main(int argc, char *argv[]) {
  // Machine code for:
  //   mov eax, 0
  //   ret
  unsigned char code[] = {0xb8, 0x00, 0x00, 0x00, 0x00, 0xc3};

  if (argc < 2) {
    fprintf(stderr, "Usage: jit1 <integer>\n");
    return 1;
  }

  // Overwrite immediate value "0" in the instruction
  // with the user's value.  This will make our code:
  //   mov eax, <user's value>
  //   ret
  int num = atoi(argv[1]);
  memcpy(&code[1], &num, 4);

  // Allocate writable/executable memory.
  // Note: real programs should not map memory both writable
  // and executable because it is a security risk.
  void *mem = mmap(NULL, sizeof(code), PROT_WRITE | PROT_EXEC,
                   MAP_ANON | MAP_PRIVATE, -1, 0);
  memcpy(mem, code, sizeof(code));

  // The function will return the user's value.
  int (*func)() = mem;
  return func();
}
guixoid
() автор топика
Ответ на: комментарий от guixoid

Тут у тебя код уже сгенерированный лежит, ты его просто через одно место вызываешь.

anonymous
()
Ответ на: комментарий от guixoid

для меня JIT компиляция это когда код функции генерируется во время исполнения и сразу может исполнятся.

А если ты, скажем, в программе на ходу позовёшь gcc для файла, который на ходу сгенерируешь, потом загрузишь получившуюся so-шку и вызовешь её функцию, это будет JIT?

i-rinat ★★★★★
()
Ответ на: комментарий от i-rinat

Это я думаю можно назвать JIT'ом,есть даже библиотеки помогающие редактировать dylib файлы,но на счет редактирования кода функций не знаю

guixoid
() автор топика
Ответ на: комментарий от guixoid
// tabulate.c
#define _GNU_SOURCE
#include <assert.h>
#include <dlfcn.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <unistd.h>

int
main(int argc, char *argv[])
{
    if (argc < 2) {
        printf("argc < 2\n");
        return 2;
    }

    int pipe1[2];
    int pipe2[2];

    pipe(pipe1);
    pipe(pipe2);

    int c_in = pipe1[1];
    int c_out = pipe2[0];

    if (fork() == 0) {
        close(pipe1[1]);
        close(pipe2[0]);

        dup2(pipe1[0], 0);
        dup2(pipe2[1], 1);

        execlp("gcc", "gcc", "-shared", "-fPIC", "-x", "c", "-", "-lm", "-o",
               "-", NULL);
        return 1;
    }

    close(pipe1[0]);
    close(pipe2[1]);

    FILE *c_inp = fdopen(c_in, "w");
    fprintf(c_inp, "#include <math.h>\n");
    fprintf(c_inp, "double func(double x) {\n");
    fprintf(c_inp, "  return %s;\n", argv[1]);
    fprintf(c_inp, "}\n");
    fclose(c_inp);

    int code_fd = memfd_create("code", 0);
    assert(code_fd);
    FILE *c_outp = fdopen(c_out, "r");
    assert(c_outp);
    while (!feof(c_outp)) {
        char buf[4096];
        size_t read_bytes = fread(buf, 1, sizeof(buf), c_outp);
        write(code_fd, buf, read_bytes);
    }
    fclose(c_outp);

    char code_fd_name[64];
    snprintf(code_fd_name, sizeof(code_fd_name), "/proc/self/fd/%d", code_fd);

    void *handle = dlopen(code_fd_name, RTLD_NOW);
    assert(handle);
    close(code_fd);

    double (*func)(double);
    func = dlsym(handle, "func");
    assert(func);

    printf("tabulate %s:\n", argv[1]);
    for (double x = 0; x <= 1.0; x += 0.125) {
        printf("%5.3f  %f\n", x, func(x));
    }

    dlclose(handle);
    return 0;
}

Собирать так:

gcc tabulate.c -ldl -o tabulate

Использовать примерно так:

$ ./tabulate "sin(x) + cos(x) * cos(x)"
tabulate sin(x) + cos(x) * cos(x):
0.000  1.000000
0.125  1.109131
0.250  1.186195
0.375  1.232117
0.500  1.249577
0.625  1.242758
0.750  1.217007
0.875  1.178420
1.000  1.133398

Я надеюсь, понятно, что делает программа, и как именно она это делает. (И почему так делать не стоит.)

Это считается за JIT или нет? :-D Лишних файлов в файловой системе не создаётся.

i-rinat ★★★★★
()
Ответ на: комментарий от i-rinat

Ахаха, ахаха… нет.

Я вот не называл бы JIT-(пере)компиляцией сценарии, в которых (пере)компиляция не является альтернативой другом доступном в том же рантайме способу исполнения. Но мир со мной не согласен, и называет JIT-компиляцией вообще любую компиляцию во время исполнения, а, значит, и твой пример тоже.

Я придумал, как долить наркомании в твой пример. Скорми арифметическое выражение компилятору, но не выполняй код, а выдерни ответ в виде константы из какого-нибудь LLVM IR, который он выплюнет. И пусть следующий за мной думает, как это назвать, а кто придумает, повышает градус неадекватства.

t184256 ★★★★★
()
Ответ на: комментарий от t184256

Как-то так:

#!/usr/bin/xonsh

import re

def eval_int_c(expr):
    code = f'int main() { return {expr}; }'
    ir = $(echo @(code) | clang -O2 -S -emit-llvm -x c - -o-)
    return re.findall(r'ret i32 (.*)', ir)[0]

print(eval_int_c(input()))

Кто классифицирует это?

t184256 ★★★★★
()
Ответ на: комментарий от t184256

не является альтернативой другом доступном в том же рантайме способу исполнения

Писать интерпретатор выражений мне лениво. Но в этом нереального нет. Будет опция командной строки, управляющая вариантом подсчёта, либо интерпретация, либо предварительная компиляция и вызов функции.

Я вот не называл бы JIT-(пере)компиляцией сценарии

В ядре можно выключить eBPF интерпретатор, и оставить только eBPF JIT-компилятор. То есть любая программа, загружаемая в ядро сначала преобразуется в родной код для процессора, и только потом исполняется. По твоему определению это получается не-JIT.

i-rinat ★★★★★
()
Ответ на: комментарий от i-rinat

Ну, да. Если без вариантов «сначала преобразуется в родной код для процессора, и только потом исполняется», то какой же это JIT? Это уже AOT.

t184256 ★★★★★
()
Ответ на: комментарий от t184256

сначала преобразуется в родной код для процессора, и только потом исполняется

Другой вариант будет: сначала исполняется, а уже потом генерируется код. И это уже будет настоящий JIT? Я всё понял!

i-rinat ★★★★★
()
Ответ на: комментарий от i-rinat

Ить, вы двое глупые. Если программа сначала конпеляется, а потом запускается — это aot. Если сначала запускается, а потом конпеляется — это jit. Если запускается и не конпеляется — это интерпретатор.

Зовите, когда дойдёте до контейнеров против виртуальных машин.

anonymous
()
Ответ на: комментарий от anonymous

Если запускается и не конпеляется — это интерпретатор.

Питон «компиляется», но результат потом интерпретируется. Что ты на это скажешь, Эло Такседо Маск?

i-rinat ★★★★★
()
Ответ на: комментарий от i-rinat

Скажу, что питоноисходник конпеляется в питонобайтокод, а питонобайтокод интерпретируется и это две раздельных операции. А раз они раздельные, нафига их загонять под одно определение-то?

anonymous
()
Ответ на: комментарий от t184256

Позови, когда дойдешь до рассмотрения совокупностей как новых систем.

Хочешь совокуплять и плодить сущности? Оккам неодобряет. Но если уж взялся, то не надо натягивать сову определение одной сущности на совокупность нескольких.

anonymous
()
Ответ на: комментарий от anonymous

Определение для абстрактного исполнителя? Комбинация им является? Ну все, пошла сова.

t184256 ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.