← На главную

QBE 1.3: производительность до 70% от GCC

02.06.2026 17:37 · hackernews

Вышел QBE 1.3 — самый крупный релиз после версии 1.0. Добавлено около 7 тысяч строк кода, удалено 1,5 тысячи. Исправлены баги, появился новый алгоритм сопоставления IL, свежие оптимизации от Roland Paterson-Jones, поддержка Windows ABI от Scott Graham и возможность генерировать позиционно-независимый код (shared objects) по плану Michael Forney.

Основной фокус — производительность. На бенчмарке coremark QBE 1.2 выдавал лишь 40% от gcc -O2, а не обещанные 70%. Профилирование показало, что главные тормоза — две функции: ee_isdigit (обычно встраивается текстуально, использует && вместо &) и crcu8 (лучше делать с таблицей). QBE не умеет inlining, поэтому напрямую эти случаи не оптимизировать. Тем не менее реализовали GVN/GCM, оптимизацию циклов, if-elimination, упрощение CFG и ещё несколько проходов. В итоге на чистом coremark QBE 1.3 выдаёт больше 63% от GCC. Если вручную заинлайнить ee_isdigit и переписать crcu8 без ветвлений, достигается цель в 70%. На тестовом наборе языка Hare QBE 1.3 отработал на 33% быстрее предыдущей версии (1.7 с против 2.6 с).

Другое крупное улучшение — новый инструмент сопоставления IL-паттернов. Раньше QBE использовал алгоритм нумерации деревьев, вдохновлённый Plan9 C compiler Кена Томпсона. В 1.3 появился mgen — утилита на OCaml. Она ищет в C-файлах специальные комментарии с IL-шаблонами и генерирует подходящий C-код для их сопоставления. Паттерны могут содержать переменные; для их сбора mgen создаёт программы на простом байткоде, который интерпретирует функция runmatch(). В будущем mgen может упростить выбор инструкций в других бэкендах и даже распознавать битовые вращения.

Поддержка Windows ABI — заслуга Scott Graham, который внёс код из своего форка. QBE по-прежнему генерирует AT&T-синтаксис, лучше всего собирать его ассемблером mingw. Для компиляции под Windows достаточно флага -t amd64_win.

Наконец, улучшена поддержка позиционно-независимого кода. Теперь QBE может линковаться с shared objects и сам их создавать на большинстве платформ. Для этого на уровне IL добавлен флаг DYNCONST (динамическая константа) — это адрес символа, который неизвестен на этапе компиляции, но постоянен во время исполнения. Пример: для загрузки переменной из динамической библиотеки пишут %v =w load extern $dlvar.

Читать оригинал →