Понимание простейшего LLVM IR
Я преобразую простейший код C
#include <stdio.h>
int main()
{
return 0;
}
к его LLVM IR, используя
clang -emit-llvm -S hello.c
Сгенерированный IR:
define i32 @main() #0 {
%1 = alloca i32, align 4
store i32 0, i32* %1
ret i32 0
}
Однако я не понимаю этого IR. (LLVM doc помогает, но не так много для начинающих)
- Почему у нас есть
%1 = alloca i32, align 4
? Как это соотносится в исходном коде?
- Тот же вопрос для
store i32 0, i32* %1
- Предоставляет ли alloca выделение в стеке (вместо динамического выделения)?
- Что означает "выровнять 4"?
Ответы
Ответ 1
define i32 @main() #0
Определяет функцию с именем main
, которая возвращает 32-битное целое число. #0
означает использование атрибутов с именем #0
для функции. Например, в IR может быть что-то вроде attributes #0 = { alwaysinline alignstack=4 }
, и эти атрибуты будут применены к main
.
%1 = alloca i32, align 4
Это выделяет 32-битное целое в стеке. %1
- это имя указателя на это место в стеке. align 4
гарантирует, что адрес будет кратным 4
store i32 0, i32* %1
Это устанавливает 32-битное целое число, на которое указывает %1
на 32-битное значение 0. Это похоже на высказывание *x = 1
в С++
ret i32 0
Это возвращает функцию с 32-битным значением возврата 0
Назначение нечетное, учитывая, что у вас нет локальной переменной в main
. LLVM использует BasicBlock
для представления групп инструкций, а базовый блок имеет точку выхода и список инструкций. Я предполагаю, что компилятор решил использовать return
как выход из базового блока и решил включить хотя бы одну команду в блок. Назначение в основном не работает.
Ответ 2
%n
- это виртуальные регистры, которые будут разрешены для фактических регистров при генерации кода для целевой машины.
i32
существует информация о типе. В исходном коде это был int
, который ваш компилятор считал 32-битным целым.
alloca
предназначен для выделения пространства в стеке. В этом примере это i32
(32-разрядное целое число), поэтому вы можете загрузить в 0 для возвращаемого значения. align 4
дает это распределение 4 байт, то есть указатель стека будет на выровненном по 4 байт адресе.
Это не самое эффективное представление, но это не цель, если IR. ИК-порт должен быть портативным для разных архитектур. Затем он переходит к серверу, чтобы создать эффективный машинный код.
Справочное руководство по языку LLVM
Почему alloca
и store
связаны с тем, что это функция main
. Если бы вы назвали эту функцию чем-то еще, IR просто содержала бы ret
, как вы ожидали. Исследуя сборку, созданную для основного, она, по-видимому, связана с указателем базовой таблицы
но я не совсем понимаю, почему он там. Время, чтобы вытащить стандарт С. Я думаю.
Обновление: я не могу найти что-либо в стандарте C, но, похоже, clang делает это для каждой основной функции. Я не знаю, что код clang code достаточно хорошо, чтобы отслеживать его.
Обновление: см. комментарии с Биллом Линчем ниже. Эти установки существуют:
для возможного неявного return 0
, что основные функции имеют
Ответ 3
Переменные обычно помещаются в стек в неоптимизированных сборках для отладки. В оптимизированных сборках, которые используют реальные регистры, значение может исчезнуть до выхода функции.
Комментарий о переносимости не совсем корректен, если этот IR прошел через "opt", он устранит хранилище стека.