summaryrefslogtreecommitdiffstats
path: root/tools/clang-fuzzer/handle-llvm/handle_llvm.cpp
blob: d0d35d51e80e5dd0e515758cfcdb4b2f0f26e86c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
//==-- handle_llvm.cpp - Helper function for Clang fuzzers -----------------==//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// Implements HandleLLVM for use by the Clang fuzzers. First runs a loop
// vectorizer optimization pass over the given IR code. Then mimics lli on both
// versions to JIT the generated code and execute it. Currently, functions are 
// executed on dummy inputs.
//
//===----------------------------------------------------------------------===//

#include "handle_llvm.h"
#include "input_arrays.h"

#include "llvm/ADT/Triple.h"
#include "llvm/Analysis/TargetLibraryInfo.h"
#include "llvm/Analysis/TargetTransformInfo.h"
#include "llvm/CodeGen/CommandFlags.inc"
#include "llvm/CodeGen/MachineModuleInfo.h"
#include "llvm/CodeGen/TargetPassConfig.h"
#include "llvm/ExecutionEngine/JITEventListener.h"
#include "llvm/ExecutionEngine/JITSymbol.h"
#include "llvm/ExecutionEngine/MCJIT.h"
#include "llvm/ExecutionEngine/ObjectCache.h"
#include "llvm/ExecutionEngine/RTDyldMemoryManager.h"
#include "llvm/ExecutionEngine/SectionMemoryManager.h"
#include "llvm/IR/IRPrintingPasses.h"
#include "llvm/IR/LegacyPassManager.h"
#include "llvm/IR/LegacyPassNameParser.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/Verifier.h"
#include "llvm/IRReader/IRReader.h"
#include "llvm/Pass.h"
#include "llvm/PassRegistry.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/SourceMgr.h"
#include "llvm/Support/TargetRegistry.h"
#include "llvm/Support/TargetSelect.h"
#include "llvm/Target/TargetMachine.h"
#include "llvm/Transforms/IPO/PassManagerBuilder.h"
#include "llvm/Transforms/IPO.h"
#include "llvm/Transforms/Vectorize.h"

using namespace llvm;

// Define a type for the functions that are compiled and executed
typedef void (*LLVMFunc)(int*, int*, int*, int);

// Helper function to parse command line args and find the optimization level
static void getOptLevel(const std::vector<const char *> &ExtraArgs,
                              CodeGenOpt::Level &OLvl) {
  // Find the optimization level from the command line args
  OLvl = CodeGenOpt::Default;
  for (auto &A : ExtraArgs) {
    if (A[0] == '-' && A[1] == 'O') {
      switch(A[2]) {
        case '0': OLvl = CodeGenOpt::None; break;
        case '1': OLvl = CodeGenOpt::Less; break;
        case '2': OLvl = CodeGenOpt::Default; break;
        case '3': OLvl = CodeGenOpt::Aggressive; break;
        default:
          errs() << "error: opt level must be between 0 and 3.\n";
          std::exit(1);
      }
    }
  }
}

static void ErrorAndExit(std::string message) {
  errs()<< "ERROR: " << message << "\n";
  std::exit(1);
}

// Helper function to add optimization passes to the TargetMachine at the 
// specified optimization level, OptLevel
static void AddOptimizationPasses(legacy::PassManagerBase &MPM,
                                  CodeGenOpt::Level OptLevel,
                                  unsigned SizeLevel) {
  // Create and initialize a PassManagerBuilder
  PassManagerBuilder Builder;
  Builder.OptLevel = OptLevel;
  Builder.SizeLevel = SizeLevel;
  Builder.Inliner = createFunctionInliningPass(OptLevel, SizeLevel, false);
  Builder.LoopVectorize = true;
  Builder.populateModulePassManager(MPM);
}

// Mimics the opt tool to run an optimization pass over the provided IR
static std::string OptLLVM(const std::string &IR, CodeGenOpt::Level OLvl) {
  // Create a module that will run the optimization passes
  SMDiagnostic Err;
  LLVMContext Context;
  std::unique_ptr<Module> M = parseIR(MemoryBufferRef(IR, "IR"), Err, Context);
  if (!M || verifyModule(*M, &errs()))
    ErrorAndExit("Could not parse IR");

  Triple ModuleTriple(M->getTargetTriple());
  const TargetOptions Options = InitTargetOptionsFromCodeGenFlags();
  std::string E;
  const Target *TheTarget = TargetRegistry::lookupTarget(MArch, ModuleTriple, E);
  TargetMachine *Machine =
      TheTarget->createTargetMachine(M->getTargetTriple(), getCPUStr(),
                                     getFeaturesStr(), Options, getRelocModel(),
                                     getCodeModel(), OLvl);
  std::unique_ptr<TargetMachine> TM(Machine);
  setFunctionAttributes(getCPUStr(), getFeaturesStr(), *M);

  legacy::PassManager Passes;
  
  Passes.add(new TargetLibraryInfoWrapperPass(ModuleTriple));
  Passes.add(createTargetTransformInfoWrapperPass(TM->getTargetIRAnalysis()));

  LLVMTargetMachine &LTM = static_cast<LLVMTargetMachine &>(*TM);
  Passes.add(LTM.createPassConfig(Passes));

  Passes.add(createVerifierPass());

  AddOptimizationPasses(Passes, OLvl, 0);

  // Add a pass that writes the optimized IR to an output stream
  std::string outString;
  raw_string_ostream OS(outString);
  Passes.add(createPrintModulePass(OS, "", false));

  Passes.run(*M);

  return OS.str();
}

// Takes a function and runs it on a set of inputs
// First determines whether f is the optimized or unoptimized function
static void RunFuncOnInputs(LLVMFunc f, int Arr[kNumArrays][kArraySize]) {
  for (int i = 0; i < kNumArrays / 3; i++)
    f(Arr[i], Arr[i + (kNumArrays / 3)], Arr[i + (2 * kNumArrays / 3)],
      kArraySize);
}

// Takes a string of IR and compiles it using LLVM's JIT Engine
static void CreateAndRunJITFunc(const std::string &IR, CodeGenOpt::Level OLvl) {
  SMDiagnostic Err;
  LLVMContext Context;
  std::unique_ptr<Module> M = parseIR(MemoryBufferRef(IR, "IR"), Err, Context);
  if (!M)
    ErrorAndExit("Could not parse IR");

  Function *EntryFunc = M->getFunction("foo");
  if (!EntryFunc)
    ErrorAndExit("Function not found in module");

  std::string ErrorMsg;
  EngineBuilder builder(std::move(M));
  builder.setMArch(MArch);
  builder.setMCPU(getCPUStr());
  builder.setMAttrs(getFeatureList());
  builder.setErrorStr(&ErrorMsg);
  builder.setEngineKind(EngineKind::JIT);
  builder.setUseOrcMCJITReplacement(false);
  builder.setMCJITMemoryManager(make_unique<SectionMemoryManager>());
  builder.setOptLevel(OLvl);
  builder.setTargetOptions(InitTargetOptionsFromCodeGenFlags());

  std::unique_ptr<ExecutionEngine> EE(builder.create());
  if (!EE)
    ErrorAndExit("Could not create execution engine");

  EE->finalizeObject();
  EE->runStaticConstructorsDestructors(false);

#if defined(__GNUC__) && !defined(__clang) &&                                  \
    ((__GNUC__ == 4) && (__GNUC_MINOR__ < 9))
// Silence
//
//   warning: ISO C++ forbids casting between pointer-to-function and
//   pointer-to-object [-Wpedantic]
//
// Since C++11 this casting is conditionally supported and GCC versions
// starting from 4.9.0 don't warn about the cast.
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wpedantic"
#endif
  LLVMFunc f = reinterpret_cast<LLVMFunc>(EE->getPointerToFunction(EntryFunc));
#if defined(__GNUC__) && !defined(__clang) &&                                  \
    ((__GNUC__ == 4) && (__GNUC_MINOR__ < 9))
#pragma GCC diagnostic pop
#endif

  // Figure out if we are running the optimized func or the unoptimized func
  RunFuncOnInputs(f, (OLvl == CodeGenOpt::None) ? UnoptArrays : OptArrays);

  EE->runStaticConstructorsDestructors(true);
}

// Main fuzz target called by ExampleClangLLVMProtoFuzzer.cpp
// Mimics the lli tool to JIT the LLVM IR code and execute it
void clang_fuzzer::HandleLLVM(const std::string &IR,
                              const std::vector<const char *> &ExtraArgs) {
  // Populate OptArrays and UnoptArrays with the arrays from InputArrays
  memcpy(OptArrays, InputArrays, kTotalSize);
  memcpy(UnoptArrays, InputArrays, kTotalSize);

  // Parse ExtraArgs to set the optimization level
  CodeGenOpt::Level OLvl;
  getOptLevel(ExtraArgs, OLvl);

  // First we optimize the IR by running a loop vectorizer pass
  std::string OptIR = OptLLVM(IR, OLvl);

  CreateAndRunJITFunc(OptIR, OLvl);
  CreateAndRunJITFunc(IR, CodeGenOpt::None);

  if (memcmp(OptArrays, UnoptArrays, kTotalSize))
    ErrorAndExit("!!!BUG!!!");

  return;
}