reference, declarationdefinition
definition → references, declarations, derived classes, virtual overrides
reference to multiple definitions → definitions
unreferenced
    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
//==-- 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.setMCJITMemoryManager(std::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;
}