LLVM - это набор библиотек и инструментов для разработки компиляторов. В состав LLVM входят компиляторы C и С++, которые можно использовать как отдельные инструменты, так и в форме библиотек для получения промежуточных результатов компиляции.
В основе LLVM лежит промежуточное представление кода (Intermediate Representation, IR), которое можно обработать в памяти, используя функции библиотек LLVM, либо выгрузить его в виде бинарного представления (bitcode, файлы с расширением .bc) или ассемблерного кода (файлы с расширением .ll).
Из этого представления можно либо сгенерировать оптимизированный машинный код для целого ряда аппаратных платформ, либо выполнить его, непосредственно интерпретируя или получив машинный код в памяти (JIT-компиляция).
LLVM IR является статически типизированным, при этом диапазон разрядности операндов является произвольным. С точки зрения генерации кода команды LLVM IR являются тетрадами в форме:\
*%результат = операция %операнд1, %операнд2*\
где %результат, %операнд1, %операнд2 являются именами регистров для хранения временных значений. Количество регистров не ограничено, более того - каждому регистру выполняется присваивание только один раз, что упрощает статический анализ потока данных.
Модуль LLVM состоит из глобальных переменных, констант и функций. Определение функции содержит один или более базовых блоков, которые являются последовательностью команд, не являющихся командами перехода. Ключевая идея базового блока состоит в том, что если одна команда базового блока выполняется, то выполняются все остальные команды базового блока. Это упрощает анализ потока исполнения. В конце каждого базового блока есть команда перехода, ret возвращает поток управления вызывающей функции, а br выполняет переход, условный или безусловный.
В LLVM используется 2 способа добавления команд LLVM IR в генерируемый код.
- С помощью набора классов, производных от Instruction: UnaryOperator, BinaryOperator, CallInst, LoadInst, StoreInst, AllocaInst, CastInst, BranchInst и т.д. Например:\
``Value *res=BinaryOperator::Create(Instruction::Add, left, right, "", context.currentBlock());``
- С помощью набора класса IRBuilder. Например:\
``Value *res=builder.CreateAdd(left, right);``
IRBuilder предоставляет более простой API для генерации кода (например, не нужно указывать явно место для вставки команды), но поддерживается не весь набор команд.
Для операндов арифметических команд используется класс Value и производный от него Constant. В IRBuilder есть следующие методы для добавления команд в текущий базовый блок:
Дополнительными аргументами можно указать префикс для имени регистра и флаги обработки переполнения. Для сравнения целых чисел первым аргументом нужно указать ICMP_EQ, ICMP_NE, ICMP_UGT, ICMP_UGE, ICMP_ULT, ICMP_ULE, ICMP_SGT,
ICMP_SGE, ICMP_SLT, ICMP_SLE; вещественных - FCMP_OEQ, FCMP_OGT, FCMP_OGE,
FCMP_OLT, FCMP_OLE, FCMP_ONE. Также имеются команды для преобразования из целого в вещественный и обратно, расширения и обрезки целых и вещественных значений.
Команды для создания констант, размещения переменных в памяти, загрузки и сохранения значений в памяти добавляются с помощью следующих методов:
```C++
Constant* getInt1(bool V); // константы
Constant* getInt32(uint32_t C);
Constant* getInt64(uint64_t C);
Constant* CreateGlobalStringPtr(StringRef Str); // i8*
Value* CreateLoad(Type* Ty, Value* Ptr); // загрузка из памяти
StoreInst* CreateStore(Value* Val, Value* Ptr); // сохранение
AllocaInst* CreateAlloca(Type* Ty, Value* ArraySize=nullptr); // размещение на стеке
Value* CreateGEP(Type* Ty, Value* Ptr, Value* Idx); // доступ к элементу массива или структуры (указатель на память)
Value* CreateInBoundsGEP(Type*Ty, Value* Ptr, ArrayRef<Value*> IdxList); // доступ к элементу массива или структуры с проверкой
CallInst* CreateCall(Function* Callee, ArrayRef<Value*> Args=None); // вызов функции
```
Для создания блока используется функция класса BasicBlock:\
``BasicBlock* BasicBlock::Create(LLVMContext& Context, const Twine &Prefix="", Function *Parent=nullptr);``\
а для переключения блоков метод\
``void SetInsertPoint(BasicBlock* block);``