Подтвердить что ты не робот

Понимание простейшего 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"?
4b9b3361

Ответ 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", он устранит хранилище стека.