Генерация кода из трехадресного кода в байт-код JVM
Я работаю над компилятором байт-кода для Renjin (R для JVM) и экспериментирую с переводом нашего представления промежуточного трехадресного кода (TAC) в байт-код. Все учебники по компиляторам, с которыми я консультировался, обсуждают распределение регистров во время генерации кода, но я не смог найти никаких ресурсов для генерации кода на виртуальных машинах на основе стека, таких как JVM.
Простые инструкции TAC легко перевести в байт-код, но я немного теряюсь, когда речь идет о временных. Есть ли у кого-нибудь ссылки на ресурсы, которые описывают это?
Вот полный пример:
Оригинальный код R выглядит так:
x + sqrt(x * y)
TAC IR:
0: _t2 := primitive<*>(x, y)
1: _t3 := primitive<sqrt>(_t2)
2: return primitive<+>(x, _t3)
(на секунду игнорируем тот факт, что мы не всегда можем разрешить вызовы функций примитивам во время компиляции)
Результирующий байт-код JVM будет выглядеть (примерно) примерно так:
aload_x
dup
aload_y
invokestatic r/primitives/Ops.multiply(Lr/lang/Vector;Lr/lang/Vector;)
invokestatic r/primitives/Ops.sqrt(Lr/lang/Vector;)
invokestatic r/primitives/Ops.plus(Lr/lang/Vector;Lr/lang/Vector;)
areturn
По сути, в начале программы мне уже нужно думать, что мне понадобится локальная переменная x в начале стека к тому времени, когда я доберусь до инструкции TAC 2. Я могу обдумать это вручную, но я У меня проблемы с продумыванием алгоритма, чтобы сделать это правильно. Есть указатели?
1 ответ
Преобразование 3-адресного представления в стек проще, чем в стеке, в 3-адресное.
Ваша последовательность должна быть следующей:
- Форма основных блоков
- Выполнить SSA-преобразование
- Построить деревья выражений в основных блоках
- Выполните планирование регистров (и одновременное удаление), чтобы выделить локальные переменные для регистров, не исключенных предыдущим шагом
- Выдать код JVM - регистры переходят в переменные, деревья выражений тривиально расширяются в операции стека