xref: /llvm-project/clang/tools/clang-fuzzer/handle-llvm/handle_llvm.cpp (revision 37da4e3d80d7136121e74e2b8d23afb14ae7ab69)
1 //==-- handle_llvm.cpp - Helper function for Clang fuzzers -----------------==//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // Implements HandleLLVM for use by the Clang fuzzers. First runs a loop
10 // vectorizer optimization pass over the given IR code. Then mimics lli on both
11 // versions to JIT the generated code and execute it. Currently, functions are
12 // executed on dummy inputs.
13 //
14 //===----------------------------------------------------------------------===//
15 
16 #include "handle_llvm.h"
17 #include "input_arrays.h"
18 
19 #include "llvm/Analysis/TargetLibraryInfo.h"
20 #include "llvm/Analysis/TargetTransformInfo.h"
21 #include "llvm/CodeGen/CommandFlags.h"
22 #include "llvm/CodeGen/MachineModuleInfo.h"
23 #include "llvm/CodeGen/TargetPassConfig.h"
24 #include "llvm/ExecutionEngine/JITEventListener.h"
25 #include "llvm/ExecutionEngine/JITSymbol.h"
26 #include "llvm/ExecutionEngine/MCJIT.h"
27 #include "llvm/ExecutionEngine/ObjectCache.h"
28 #include "llvm/ExecutionEngine/RTDyldMemoryManager.h"
29 #include "llvm/ExecutionEngine/SectionMemoryManager.h"
30 #include "llvm/IR/IRPrintingPasses.h"
31 #include "llvm/IR/LLVMContext.h"
32 #include "llvm/IR/Module.h"
33 #include "llvm/IR/Verifier.h"
34 #include "llvm/IRPrinter/IRPrintingPasses.h"
35 #include "llvm/IRReader/IRReader.h"
36 #include "llvm/MC/TargetRegistry.h"
37 #include "llvm/Passes/OptimizationLevel.h"
38 #include "llvm/Passes/PassBuilder.h"
39 #include "llvm/Support/MemoryBuffer.h"
40 #include "llvm/Support/SourceMgr.h"
41 #include "llvm/Support/TargetSelect.h"
42 #include "llvm/Target/TargetMachine.h"
43 #include "llvm/TargetParser/Triple.h"
44 
45 using namespace llvm;
46 
47 // Define a type for the functions that are compiled and executed
48 typedef void (*LLVMFunc)(int*, int*, int*, int);
49 
50 // Helper function to parse command line args and find the optimization level
getOptLevel(const std::vector<const char * > & ExtraArgs)51 static CodeGenOptLevel getOptLevel(const std::vector<const char *> &ExtraArgs) {
52   // Find the optimization level from the command line args
53   CodeGenOptLevel OLvl = CodeGenOptLevel::Default;
54   for (auto &A : ExtraArgs) {
55     if (A[0] == '-' && A[1] == 'O') {
56       if (auto Level = CodeGenOpt::parseLevel(A[2])) {
57         OLvl = *Level;
58       } else {
59         errs() << "error: opt level must be between 0 and 3.\n";
60         std::exit(1);
61       }
62     }
63   }
64   return OLvl;
65 }
66 
ErrorAndExit(std::string message)67 static void ErrorAndExit(std::string message) {
68   errs()<< "ERROR: " << message << "\n";
69   std::exit(1);
70 }
71 
72 // Helper function to add optimization passes to the TargetMachine at the
73 // specified optimization level, OptLevel
RunOptimizationPasses(raw_ostream & OS,Module & M,CodeGenOptLevel OptLevel)74 static void RunOptimizationPasses(raw_ostream &OS, Module &M,
75                                   CodeGenOptLevel OptLevel) {
76   llvm::OptimizationLevel OL;
77   switch (OptLevel) {
78   case CodeGenOptLevel::None:
79     OL = OptimizationLevel::O0;
80     break;
81   case CodeGenOptLevel::Less:
82     OL = OptimizationLevel::O1;
83     break;
84   case CodeGenOptLevel::Default:
85     OL = OptimizationLevel::O2;
86     break;
87   case CodeGenOptLevel::Aggressive:
88     OL = OptimizationLevel::O3;
89     break;
90   }
91 
92   LoopAnalysisManager LAM;
93   FunctionAnalysisManager FAM;
94   CGSCCAnalysisManager CGAM;
95   ModuleAnalysisManager MAM;
96 
97   PassBuilder PB;
98 
99   PB.registerModuleAnalyses(MAM);
100   PB.registerCGSCCAnalyses(CGAM);
101   PB.registerFunctionAnalyses(FAM);
102   PB.registerLoopAnalyses(LAM);
103   PB.crossRegisterProxies(LAM, FAM, CGAM, MAM);
104 
105   ModulePassManager MPM = PB.buildPerModuleDefaultPipeline(OL);
106   MPM.addPass(PrintModulePass(OS));
107 
108   MPM.run(M, MAM);
109 }
110 
111 // Mimics the opt tool to run an optimization pass over the provided IR
OptLLVM(const std::string & IR,CodeGenOptLevel OLvl)112 static std::string OptLLVM(const std::string &IR, CodeGenOptLevel OLvl) {
113   // Create a module that will run the optimization passes
114   SMDiagnostic Err;
115   LLVMContext Context;
116   std::unique_ptr<Module> M = parseIR(MemoryBufferRef(IR, "IR"), Err, Context);
117   if (!M || verifyModule(*M, &errs()))
118     ErrorAndExit("Could not parse IR");
119 
120   Triple ModuleTriple(M->getTargetTriple());
121   const TargetOptions Options =
122       codegen::InitTargetOptionsFromCodeGenFlags(ModuleTriple);
123   std::string E;
124   const Target *TheTarget =
125       TargetRegistry::lookupTarget(codegen::getMArch(), ModuleTriple, E);
126   if (!TheTarget)
127     ErrorAndExit(E);
128 
129   std::unique_ptr<TargetMachine> TM(TheTarget->createTargetMachine(
130       M->getTargetTriple(), codegen::getCPUStr(), codegen::getFeaturesStr(),
131       Options, codegen::getExplicitRelocModel(),
132       codegen::getExplicitCodeModel(), OLvl));
133   if (!TM)
134     ErrorAndExit("Could not create target machine");
135 
136   codegen::setFunctionAttributes(codegen::getCPUStr(),
137                                  codegen::getFeaturesStr(), *M);
138 
139   // Add a pass that writes the optimized IR to an output stream
140   std::string outString;
141   raw_string_ostream OS(outString);
142   RunOptimizationPasses(OS, *M, OLvl);
143 
144   return outString;
145 }
146 
147 // Takes a function and runs it on a set of inputs
148 // First determines whether f is the optimized or unoptimized function
RunFuncOnInputs(LLVMFunc f,int Arr[kNumArrays][kArraySize])149 static void RunFuncOnInputs(LLVMFunc f, int Arr[kNumArrays][kArraySize]) {
150   for (int i = 0; i < kNumArrays / 3; i++)
151     f(Arr[i], Arr[i + (kNumArrays / 3)], Arr[i + (2 * kNumArrays / 3)],
152       kArraySize);
153 }
154 
155 // Takes a string of IR and compiles it using LLVM's JIT Engine
CreateAndRunJITFunc(const std::string & IR,CodeGenOptLevel OLvl)156 static void CreateAndRunJITFunc(const std::string &IR, CodeGenOptLevel OLvl) {
157   SMDiagnostic Err;
158   LLVMContext Context;
159   std::unique_ptr<Module> M = parseIR(MemoryBufferRef(IR, "IR"), Err, Context);
160   if (!M)
161     ErrorAndExit("Could not parse IR");
162 
163   Function *EntryFunc = M->getFunction("foo");
164   if (!EntryFunc)
165     ErrorAndExit("Function not found in module");
166 
167   std::string ErrorMsg;
168   Triple ModuleTriple(M->getTargetTriple());
169 
170   EngineBuilder builder(std::move(M));
171   builder.setMArch(codegen::getMArch());
172   builder.setMCPU(codegen::getCPUStr());
173   builder.setMAttrs(codegen::getFeatureList());
174   builder.setErrorStr(&ErrorMsg);
175   builder.setEngineKind(EngineKind::JIT);
176   builder.setMCJITMemoryManager(std::make_unique<SectionMemoryManager>());
177   builder.setOptLevel(OLvl);
178   builder.setTargetOptions(
179       codegen::InitTargetOptionsFromCodeGenFlags(ModuleTriple));
180 
181   std::unique_ptr<ExecutionEngine> EE(builder.create());
182   if (!EE)
183     ErrorAndExit("Could not create execution engine");
184 
185   EE->finalizeObject();
186   EE->runStaticConstructorsDestructors(false);
187 
188   LLVMFunc f = reinterpret_cast<LLVMFunc>(EE->getPointerToFunction(EntryFunc));
189 
190   // Figure out if we are running the optimized func or the unoptimized func
191   RunFuncOnInputs(f, (OLvl == CodeGenOptLevel::None) ? UnoptArrays : OptArrays);
192 
193   EE->runStaticConstructorsDestructors(true);
194 }
195 
196 // Main fuzz target called by ExampleClangLLVMProtoFuzzer.cpp
197 // Mimics the lli tool to JIT the LLVM IR code and execute it
HandleLLVM(const std::string & IR,const std::vector<const char * > & ExtraArgs)198 void clang_fuzzer::HandleLLVM(const std::string &IR,
199                               const std::vector<const char *> &ExtraArgs) {
200   // Populate OptArrays and UnoptArrays with the arrays from InputArrays
201   memcpy(OptArrays, InputArrays, kTotalSize);
202   memcpy(UnoptArrays, InputArrays, kTotalSize);
203 
204   // Parse ExtraArgs to set the optimization level
205   CodeGenOptLevel OLvl = getOptLevel(ExtraArgs);
206 
207   // First we optimize the IR by running a loop vectorizer pass
208   std::string OptIR = OptLLVM(IR, OLvl);
209 
210   CreateAndRunJITFunc(OptIR, OLvl);
211   CreateAndRunJITFunc(IR, CodeGenOptLevel::None);
212 
213   if (memcmp(OptArrays, UnoptArrays, kTotalSize))
214     ErrorAndExit("!!!BUG!!!");
215 }
216