1 //==-- handle_llvm.cpp - Helper function for Clang fuzzers -----------------==//
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
7 //===----------------------------------------------------------------------===//
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.
14 //===----------------------------------------------------------------------===//
16 #include "handle_llvm.h"
17 #include "input_arrays.h"
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"
47 // Define a type for the functions that are compiled and executed
48 typedef void (*LLVMFunc
)(int*, int*, int*, int);
50 // Helper function to parse command line args and find the optimization level
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])) {
59 errs() << "error: opt level must be between 0 and 3.\n";
67 static void ErrorAndExit(std::string message
) {
68 errs()<< "ERROR: " << message
<< "\n";
72 // Helper function to add optimization passes to the TargetMachine at the
73 // specified optimization level, OptLevel
74 static void RunOptimizationPasses(raw_ostream
&OS
, Module
&M
,
75 CodeGenOptLevel OptLevel
) {
76 llvm::OptimizationLevel OL
;
78 case CodeGenOptLevel::None
:
79 OL
= OptimizationLevel::O0
;
81 case CodeGenOptLevel::Less
:
82 OL
= OptimizationLevel::O1
;
84 case CodeGenOptLevel::Default
:
85 OL
= OptimizationLevel::O2
;
87 case CodeGenOptLevel::Aggressive
:
88 OL
= OptimizationLevel::O3
;
92 LoopAnalysisManager LAM
;
93 FunctionAnalysisManager FAM
;
94 CGSCCAnalysisManager CGAM
;
95 ModuleAnalysisManager MAM
;
99 PB
.registerModuleAnalyses(MAM
);
100 PB
.registerCGSCCAnalyses(CGAM
);
101 PB
.registerFunctionAnalyses(FAM
);
102 PB
.registerLoopAnalyses(LAM
);
103 PB
.crossRegisterProxies(LAM
, FAM
, CGAM
, MAM
);
105 ModulePassManager MPM
= PB
.buildPerModuleDefaultPipeline(OL
);
106 MPM
.addPass(PrintModulePass(OS
));
111 // Mimics the opt tool to run an optimization pass over the provided IR
112 static std::string
OptLLVM(const std::string
&IR
, CodeGenOptLevel OLvl
) {
113 // Create a module that will run the optimization passes
116 std::unique_ptr
<Module
> M
= parseIR(MemoryBufferRef(IR
, "IR"), Err
, Context
);
117 if (!M
|| verifyModule(*M
, &errs()))
118 ErrorAndExit("Could not parse IR");
120 Triple
ModuleTriple(M
->getTargetTriple());
121 const TargetOptions Options
=
122 codegen::InitTargetOptionsFromCodeGenFlags(ModuleTriple
);
124 const Target
*TheTarget
=
125 TargetRegistry::lookupTarget(codegen::getMArch(), ModuleTriple
, E
);
129 std::unique_ptr
<TargetMachine
> TM(TheTarget
->createTargetMachine(
130 M
->getTargetTriple(), codegen::getCPUStr(), codegen::getFeaturesStr(),
131 Options
, codegen::getExplicitRelocModel(),
132 codegen::getExplicitCodeModel(), OLvl
));
134 ErrorAndExit("Could not create target machine");
136 codegen::setFunctionAttributes(codegen::getCPUStr(),
137 codegen::getFeaturesStr(), *M
);
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
);
147 // Takes a function and runs it on a set of inputs
148 // First determines whether f is the optimized or unoptimized function
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)],
155 // Takes a string of IR and compiles it using LLVM's JIT Engine
156 static void CreateAndRunJITFunc(const std::string
&IR
, CodeGenOptLevel OLvl
) {
159 std::unique_ptr
<Module
> M
= parseIR(MemoryBufferRef(IR
, "IR"), Err
, Context
);
161 ErrorAndExit("Could not parse IR");
163 Function
*EntryFunc
= M
->getFunction("foo");
165 ErrorAndExit("Function not found in module");
167 std::string ErrorMsg
;
168 Triple
ModuleTriple(M
->getTargetTriple());
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
));
181 std::unique_ptr
<ExecutionEngine
> EE(builder
.create());
183 ErrorAndExit("Could not create execution engine");
185 EE
->finalizeObject();
186 EE
->runStaticConstructorsDestructors(false);
188 LLVMFunc f
= reinterpret_cast<LLVMFunc
>(EE
->getPointerToFunction(EntryFunc
));
190 // Figure out if we are running the optimized func or the unoptimized func
191 RunFuncOnInputs(f
, (OLvl
== CodeGenOptLevel::None
) ? UnoptArrays
: OptArrays
);
193 EE
->runStaticConstructorsDestructors(true);
196 // Main fuzz target called by ExampleClangLLVMProtoFuzzer.cpp
197 // Mimics the lli tool to JIT the LLVM IR code and execute it
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
);
204 // Parse ExtraArgs to set the optimization level
205 CodeGenOptLevel OLvl
= getOptLevel(ExtraArgs
);
207 // First we optimize the IR by running a loop vectorizer pass
208 std::string OptIR
= OptLLVM(IR
, OLvl
);
210 CreateAndRunJITFunc(OptIR
, OLvl
);
211 CreateAndRunJITFunc(IR
, CodeGenOptLevel::None
);
213 if (memcmp(OptArrays
, UnoptArrays
, kTotalSize
))
214 ErrorAndExit("!!!BUG!!!");