Предложенный JIT примечателен очень высокой скоростью генерации кода, простотой сопровождения и полной интеграцией с интерпретатором. Предложенный метод позволяет автоматически преобразовать интерпретатор, написанный на языке Си, в JIT-компилятор, без отдельного формирования логики генерации кода и без ручного создания ассемблерных представлений. При таком подходе исправление ошибки в интерпретаторе автоматически приведёт и к устранению той же проблемы в JIT, так как используется общий генератор кода.
Работа метода Copy-and-Patch основывается на том, что релокация кода в памяти при загрузке компоновщиком объектных файлов и подстановка машинных инструкций вместо байткода в JIT, являются сходными задачами. При работе JIT в процессе выполнения программы осуществляется перебор созданных интерпретатором инструкций байткода и копирование для каждой инструкции заранее скомпилированного машинного кода в область памяти с исполняемым кодом, а также изменение на лету инструкций для подстановки в них обрабатываемых данных (JIT копирует готовые шаблоны уже скомпилированных функций и подставляет в них необходимые значения, такие как аргументы и константы). При загрузке объектных файлов также осуществляется копирование машинного кода в память и подстановка внешних символов.
В Copy-and-Patch JIT при помощи LLVM собирается объектный файл в формате ELF, содержащий данные об инструкциях байткода и информацию о необходимой замене данных. JIT заменяет сгенерированные в ходе интерпретации программы инструкции байткода на представления в машинном коде, попутно подставляя необходимые для вычислений данные. Реализация JIT требует в качестве зависимости LLVM на этапе сборки, но runtime-компоненты не привязаны к внешним зависимостям и сводятся примерно к 300 вручную написанным строкам на языке Си и 3000 сгенерированным строкам кода на Си.
По сравнению с традиционным JIT-инструментарием (LLVM -O0) предложенный JIT обеспечивает в 100 раз более быструю генерацию кода и на 15% более быстрый результирующий код. По сравнению с компиляцией в
WebAssembly (Liftoff) новый JIT генерирует код в 5 раз быстрее, а результирующий код работает на 50% быстрее. Если сравнивать новый JIT с оптимизирующим JIT (LuaJIT), использующим вручную написанный код на ассемблере, то предложенный вариант оказался быстрее в 13 из 44 тестах, а в среднем отстал по производительности на 35%, при существенном упрощении сопровождения и уменьшении уровня сложности реализации.
Источник: http://www.opennet.ru/opennews/art.shtml?num=60352