UZILOG - LLVM Examples## LLVM Examples
1. Static Code Analysis
- Goal: Analyze the code to find functions without a return statement.
``` cpp
#include
#include
#include
#include
void AnalyzeModule(llvm::Module &M) {
for (auto &F : M) {
bool hasReturn = false;
for (auto &BB : F) {
for (auto &I : BB) {
if (llvm::isa(I)) {
hasReturn = true;
break;
}
}
if (hasReturn) break;
}
if (!hasReturn) {
llvm::outs() << "Function '" << F.getName() << "' does not have a return statement.\n";
}
}
}
```
2. Optimization (Simplify Arithmetic Operations)
- Goal: Simplify arithmetic operations in the code, e.g., converting x * 2 into x + x.
``` cpp
#include
#include
#include
#include
void OptimizeModule(llvm::Module &M) {
for (auto &F : M) {
for (auto &BB : F) {
for (auto &I : BB) {
if (auto *Mul = llvm::dyn_cast(&I)) {
if (Mul->getOpcode() == llvm::Instruction::Mul) {
if (auto *Const = llvm::dyn_cast(Mul->getOperand(1))) {
if (Const->getValue() == 2) {
llvm::Value *NewAdd = llvm::BinaryOperator::CreateAdd(Mul->getOperand(0), Mul->getOperand(0), "", &I);
I.replaceAllUsesWith(NewAdd);
llvm::outs() << "Simplified multiplication to addition in function '" << F.getName() << "'.\n";
}
}
}
}
}
}
}
}
```
3. Program Instrumentation
- Goal: Insert code to print the name of each function when it is entered.
``` cpp
#include
#include
#include
#include
void InstrumentModule(llvm::Module &M) {
llvm::LLVMContext &Context = M.getContext();
llvm::IRBuilder<> Builder(Context);
llvm::Function *Printf = llvm::cast(M.getOrInsertFunction(
"printf", llvm::FunctionType::get(llvm::IntegerType::getInt32Ty(Context), llvm::PointerType::get(llvm::Type::getInt8Ty(Context), 0), true)
).getCallee());
for (auto &F : M) {
if (F.getName() == "printf") continue; // Avoid instrumenting the printf function itself
llvm::BasicBlock &EntryBlock = F.getEntryBlock();
Builder.SetInsertPoint(&EntryBlock, EntryBlock.getFirstInsertionPt());
llvm::Value *FuncName = Builder.CreateGlobalStringPtr(F.getName());
Builder.CreateCall(Printf, {FuncName});
llvm::outs() << "Instrumented function '" << F.getName() << "'.\n";
}
}
```
4. Target-Specific Code Generation
- Goal: Generate code specifically for a certain target, e.g., x86 architecture.
``` cpp
#include
#include
#include
#include
#include
#include
#include
void GenerateTargetCode(llvm::Module &M) {
llvm::InitializeNativeTarget();
llvm::InitializeNativeTargetAsmPrinter();
llvm::InitializeNativeTargetAsmParser();
std::string TargetTriple = llvm::sys::getDefaultTargetTriple();
M.setTargetTriple(TargetTriple);
std::string Error;
const llvm::Target *Target = llvm::TargetRegistry::lookupTarget(TargetTriple, Error);
if (!Target) {
llvm::errs() << "Error: " << Error;
return;
}
llvm::TargetOptions opt;
llvm::TargetMachine *TargetMachine = Target->createTargetMachine(TargetTriple, "generic", "", opt, llvm::Reloc::PIC_);
M.setDataLayout(TargetMachine->createDataLayout());
std::string Filename = "output.o";
std::error_code EC;
llvm::raw_fd_ostream dest(Filename, EC, llvm::sys::fs::OF_None);
if (EC) {
llvm::errs() << "Could not open file: " << EC.message();
return;
}
llvm::legacy::PassManager pass;
auto FileType = llvm::CGFT_ObjectFile;
if (TargetMachine->addPassesToEmitFile(pass, dest, nullptr, FileType)) {
llvm::errs() << "TargetMachine can't emit a file of this type";
return;
}
pass.run(M);
dest.flush();
llvm::outs() << "Generated target-specific code and wrote it to " << Filename << "\n";
}
```
5. JIT Compilation
- Goal: Compile and execute code at runtime using LLVM’s Just-In-Time compilation features.
``` cpp
#include
#include
#include
#include
#include
#include
#include
using namespace llvm;
using namespace llvm::orc;
void JITCompileAndRun() {
InitializeNativeTarget();
InitializeNativeTargetAsmPrinter();
auto Context = std::make_unique();
auto Module = std::make_unique("JITModule", *Context);
auto Builder = std::make_unique>(*Context);
// Create a simple function: int add(int a, int b) { return a + b; }
FunctionType *FT = FunctionType::get(Builder->getInt32Ty(), {Builder->getInt32Ty(), Builder->getInt32Ty()}, false);
Function *AddFunc = Function::Create(FT, Function::ExternalLinkage, "add", *Module);
BasicBlock *BB = BasicBlock::Create(*Context, "EntryBlock", AddFunc);
Builder->SetInsertPoint(BB);
auto args = AddFunc->arg_begin();
Value *A = &*args++;
A->setName("a");
Value *B = &*args++;
B->setName("b");
Value *Sum = Builder->CreateAdd(A, B, "sum");
Builder->CreateRet(Sum);
// Create JIT instance
auto JIT = LLJITBuilder().create();
if (!JIT) {
errs() << "Error creating JIT: " << toString(JIT.takeError()) << "\n";
return;
}
if (auto Err = JIT->get()->addIRModule(ThreadSafeModule(std::move(Module), std::move(Context)))) {
errs() << "Error adding module: " << toString(std::move(Err)) << "\n";
return;
}
// Lookup function and run it
auto AddSymbol = JIT->get()->lookup("add");
if (!AddSymbol) {
errs() << "Error finding function: " << toString(AddSymbol.takeError()) << "\n";
return;
}
auto AddFunction = (int (*)(int, int)) AddSymbol->getAddress();
int result = AddFunction(2, 3);
outs() << "Result of add(2, 3): " << result << "\n";
}
```
6. Sanitizers (AddressSanitizer)
- Goal: Detect memory errors like buffer overflows.
``` cpp
#include
#include
#include
#include
#include
void AddAddressSanitizer(llvm::Module &M) {
llvm::LLVMContext &Context = M.getContext();
llvm::Function *AsanCtor = llvm::Function::Create(
llvm::FunctionType::get(llvm::Type::getVoidTy(Context), false),
llvm::GlobalValue::InternalLinkage, "__asan_init", M);
llvm::appendToGlobalCtors(M, AsanCtor, 0);
llvm::outs() << "Added AddressSanitizer initialization to the module.\n";
}
```