Я ищу алгоритм оптимизации байт-кода, который вычисляет ненужные вычисления и удаляет мертвые локальные переменные. Представьте себе этот код:
iconst_2
iconst_3
iadd
invokestatic someMethod (I)V
Его можно легко оптимизировать для:
iconst_5
invokestatic someMethod (I)V
Моя первая идея реализовать это состояла в том, чтобы использовать собственный интерпретатор ASM, который захватывает инструкции (например, SourceInterpeter
) и автоматически вычисляет значения стека. Затем замените последний расчет известным результатом (в данном случае iadd
) действительным значением и повторите итерацию в обратном порядке, чтобы удалить все захваченные инструкции. Это будет работать во многих случаях, но не в каждом случае. Здесь оптимизация будет немного сложнее:
iconst_2
iconst_3
dup
getstatic someUnknownInt I
iadd
invokestatic someOtherMethod (I)V
iadd
invokestatic someMethod (I)V
Если iadd
заменить на iconst_5
, а iconst_2
и iconst_3
удалить, то у нас будет недействительный байт-код. Моя вторая мысль была сделать несколько проходов. Сначала замените все dup
инструкции их действительным значением в качестве новой инструкции pu sh (если это не ссылка на метод / поле), затем удалите все всплывающие инструкции (но не удаляйте методы) и, наконец, вычислите вычисления. , Это также не всегда срабатывает.
iconst_2
invokestatic someMethod ()I
dup2
invokestatic someOtherMethod (I)V
swap
invokestatic someOtherMethod (I)V
iadd //this can be computed -> iconst_4
invokestatic someOtherMethod (I)V
Последнее, что я попробовал, это использовал SourceInterpreter
и переписал инструкции. Добавляются только обязательные инструкции (например, вызовы методов, monitorenter
, return и т. Д. c.) Со стеком, который они принимают в качестве переписанного кода перед ними. Это приводит к неверному байт-коду, поскольку операции pop не сохраняются (после методов) и дублируются вызовы методов (если следующая обязательная инструкция принимает предыдущий метод в качестве аргумента). Каков наилучший способ сделать это?