## 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"; } ```