Можно ли реализовать JIT для интерпретатора байт-кода в C, не прибегая к сборке? - PullRequest
3 голосов
/ 04 ноября 2019

Я написал интерпретатор байт-кода для простой виртуальной машины на основе стека в (GNU) C. Все инструкции VM реализованы в виде кода между метками. Большинство инструкций не используют встроенные аргументы, поскольку они просто выталкивают их из стека виртуальных машин (например, add извлекает два числа a, b из стека и возвращает a+b обратно).

IЯ думал, что JIT будет так же просто, как идентифицировать основные блоки в байт-коде и генерировать «динамические суперинструкции», просто соединяя реализации инструкций, memcpy вставляя между метками в буфер, помеченный как исполняемый, используя mmap / mprotect. Это работает для всех инструкций, кроме инструкции push b, которая помещает следующий байт потока байт-кода в стек VM (например, push 17).

Существует ли какой-либо, по крайней мере, несколько переносимый способ, которым я могу реализоватьинструкции, которые используют аргументы аналогичным образом, либо «вставляя» аргументы в каждый экземпляр инструкции push, которая получает memcpy (таким образом, имитируя замыкания во время выполнения (?)), либо, по крайней мере, каким-то образом доступ к ПК (хотя любое упоминание о ПК, вероятно, изначально непереносимо) работающего экземпляра с использованием некоторого варианта setjmp / longjmp, который позволяет мне прочитать значение ПК?

РЕДАКТИРОВАТЬ: ПК потенциально может быть полезен при выполнениичто-то вроде следующего в псевдо-C (???)

char arg;
char args[] = {1, 2, 3};

push:
  uintptr_t pc = get_pc();
  arg = args[some_clever_hash(pc)];
  stack.push(arg);

Может быть, что-то вроде этого будет более реалистичным?:

char arg;
char args[] = {1, 2, 3};

push:
  setarg();
  stack.push(arg);

void setarg(void)
{
  uintptr_t pc = get_pc_of_caller();
  arg = args[some_clever_hash(pc)];
}

Ответы [ 2 ]

2 голосов
/ 04 ноября 2019

Хорошим трюком может быть наличие двух «шаблонов» для вашего push-кода - один, который выталкивает константу 0x01020304, и второй, который проталкивает константу 0x10203040 (для 32-битных констант). Затем вы xor два шаблона - если все будет хорошо, все, кроме 4 байтов результата будет 0, и эти 4 байта будут 0x11, 0x22, 0x33 и 0x44, и те идентифицируют, какие 4 байта шаблона заполнитьс вашими 4 байтами, чтобы генерировать толчок для любой конкретной константы. Если шаблоны не одинакового размера или отличаются от этих четырех значений, вы знаете, что вам нужно сделать что-то еще.

1 голос
/ 04 ноября 2019

Я не уверен, что понимаю вопрос, но вам не нужно ничего делать с ПК, не так ли? Это только так в байт-коде . Все, что вам действительно нужно, - это реализация немедленного (соответствующего размера) стека виртуальных машин и функция фиксации для каждой архитектуры. Реализация может выдвинуть любое произвольное значение (хотя ради общности вам, вероятно, следует избегать таких значений, как 0, которые могут иметь более эффективную кодировку, и в действительности ваша лучшая ставка может быть чем-то вроде 0x01020304),и функция fixup обрабатывает , заменяя некоторых байтов реализации соответствующей константой из байтового кода. Очевидно, куда и куда идут байты, и детали таких вещей, как непосредственная замена, будут различаться в зависимости от архитектуры процессора и реализации. Очевидно, что переносимость здесь затруднена, и детали нужно будет тщательно выяснить, но как только вы сделаете это для данной системы, вы можете добавить assert, что байты 0x01020304 найдены в ожидаемом месте вшаблон реализации. (Может быть, даже генерировать две версии с двумя различными значениями для дополнительной безопасности). Таким образом, если реализация изменится из-под вас, вы, по крайней мере, сможете начинать бомбардировку на ранней стадии компиляции вместо выполнения ошибочного кода во время выполнения.

...