SeExpr
Evaluator.h
Go to the documentation of this file.
1 /*
2  Copyright Disney Enterprises, Inc. All rights reserved.
3 
4  Licensed under the Apache License, Version 2.0 (the "License");
5  you may not use this file except in compliance with the License
6  and the following modification to it: Section 6 Trademarks.
7  deleted and replaced with:
8 
9  6. Trademarks. This License does not grant permission to use the
10  trade names, trademarks, service marks, or product names of the
11  Licensor and its affiliates, except as required for reproducing
12  the content of the NOTICE file.
13 
14  You may obtain a copy of the License at
15  http://www.apache.org/licenses/LICENSE-2.0
16 */
17 
18 #include "ExprConfig.h"
19 #include "ExprLLVMAll.h"
20 #include "VarBlock.h"
21 
22 extern "C" void SeExpr2LLVMEvalVarRef(SeExpr2::ExprVarRef *seVR, double *result);
23 extern "C" void SeExpr2LLVMEvalCustomFunction(int *opDataArg,
24  double *fpArg,
25  char **strArg,
26  void **funcdata,
27  const SeExpr2::ExprFuncNode *node);
28 
29 namespace SeExpr2 {
30 #ifdef SEEXPR_ENABLE_LLVM
31 
32 LLVM_VALUE promoteToDim(LLVM_VALUE val, unsigned dim, llvm::IRBuilder<> &Builder);
33 
34 class LLVMEvaluator {
35  // TODO: this seems needlessly complex, let's fix it
36  // TODO: let the dev code allocate memory?
37  // FP is the native function for this expression.
38  template <class T>
39  class LLVMEvaluationContext {
40  private:
41  typedef void (*FunctionPtr)(T *, char **, uint32_t);
42  typedef void (*FunctionPtrMultiple)(char **, uint32_t, uint32_t, uint32_t);
43  FunctionPtr functionPtr;
44  FunctionPtrMultiple functionPtrMultiple;
45  T *resultData;
46 
47  public:
48  LLVMEvaluationContext(const LLVMEvaluationContext &) = delete;
49  LLVMEvaluationContext &operator=(const LLVMEvaluationContext &) = delete;
50  ~LLVMEvaluationContext() {delete [] resultData;}
51  LLVMEvaluationContext() : functionPtr(nullptr), resultData(nullptr) {}
52  void init(void *fp, void *fpLoop, int dim) {
53  reset();
54  functionPtr = reinterpret_cast<FunctionPtr>(fp);
55  functionPtrMultiple = reinterpret_cast<FunctionPtrMultiple>(fpLoop);
56  resultData = new T[dim];
57  }
58  void reset() {
59  if (resultData) delete[] resultData;
60  functionPtr = nullptr;
61  resultData = nullptr;
62  }
63  const T *operator()(VarBlock *varBlock) {
64  assert(functionPtr && resultData);
65  functionPtr(resultData, varBlock ? varBlock->data() : nullptr, varBlock ? varBlock->indirectIndex : 0);
66  return resultData;
67  }
68  void operator()(VarBlock *varBlock, size_t outputVarBlockOffset, size_t rangeStart, size_t rangeEnd) {
69  assert(functionPtr && resultData);
70  functionPtrMultiple(varBlock ? varBlock->data() : nullptr, outputVarBlockOffset, rangeStart, rangeEnd);
71  }
72  };
73  std::unique_ptr<LLVMEvaluationContext<double>> _llvmEvalFP;
74  std::unique_ptr<LLVMEvaluationContext<char *>> _llvmEvalStr;
75 
76  std::unique_ptr<llvm::LLVMContext> _llvmContext;
77  std::unique_ptr<llvm::ExecutionEngine> TheExecutionEngine;
78 
79  public:
80  LLVMEvaluator() {}
81 
82  const char *evalStr(VarBlock *varBlock) { return *(*_llvmEvalStr)(varBlock); }
83 
84  const double *evalFP(VarBlock *varBlock) { return (*_llvmEvalFP)(varBlock); }
85  void evalMultiple(VarBlock *varBlock, uint32_t outputVarBlockOffset, uint32_t rangeStart, uint32_t rangeEnd) {
86  return (*_llvmEvalFP)(varBlock, outputVarBlockOffset, rangeStart, rangeEnd);
87  }
88 
89  void debugPrint() {
90  // TheModule->dump();
91  }
92 
93  bool prepLLVM(ExprNode *parseTree, ExprType desiredReturnType) {
94  using namespace llvm;
95  InitializeNativeTarget();
96  InitializeNativeTargetAsmPrinter();
97  InitializeNativeTargetAsmParser();
98 
99  std::string uniqueName = getUniqueName();
100 
101  // create Module
102  _llvmContext.reset(new LLVMContext());
103 
104  std::unique_ptr<Module> TheModule(new Module(uniqueName + "_module", *_llvmContext));
105 
106 
107  // create bindings to helper functions for variables and fucntions
108  Function *SeExpr2LLVMEvalCustomFunctionFunc=nullptr,*SeExpr2LLVMEvalVarRefFunc=nullptr;
109  {
110  Type *i8PtrTy = Type::getInt8PtrTy(*_llvmContext);
111  Type *i32PtrTy = Type::getInt32PtrTy(*_llvmContext);
112  Type *i64Ty = Type::getInt64Ty(*_llvmContext);
113  Type *doublePtrTy = Type::getDoublePtrTy(*_llvmContext);
114  PointerType *i8PtrPtr = PointerType::getUnqual(i8PtrTy);
115  Type *ParamTys[] = {i32PtrTy, doublePtrTy, i8PtrPtr, i8PtrPtr, i64Ty};
116  {
117  FunctionType *FT = FunctionType::get(Type::getVoidTy(*_llvmContext), ParamTys, false);
118  SeExpr2LLVMEvalCustomFunctionFunc=Function::Create(FT, GlobalValue::ExternalLinkage, "SeExpr2LLVMEvalCustomFunction", TheModule.get());
119  }{
120  Type *ParamTys[2] = {i8PtrTy, doublePtrTy};
121  FunctionType *FT = FunctionType::get(Type::getVoidTy(*_llvmContext), ParamTys, false);
122  SeExpr2LLVMEvalVarRefFunc=Function::Create(FT, GlobalValue::ExternalLinkage, "SeExpr2LLVMEvalVarRef", TheModule.get());
123  }
124  }
125 
126  // create function and entry BB
127  bool desireFP = desiredReturnType.isFP();
128  Type *ParamTys[] = {desireFP ? Type::getDoublePtrTy(*_llvmContext)
129  : PointerType::getUnqual(Type::getInt8PtrTy(*_llvmContext)),
130  PointerType::get(Type::getDoublePtrTy(*_llvmContext),0), Type::getInt32Ty(*_llvmContext), };
131  FunctionType *FT = FunctionType::get(Type::getVoidTy(*_llvmContext), ParamTys, false);
132  Function *F = Function::Create(FT, Function::ExternalLinkage, uniqueName + "_func", TheModule.get());
133  F->addAttribute(llvm::AttributeSet::FunctionIndex, llvm::Attribute::AlwaysInline);
134  {
135  // label the function with names
136  const char *names[] = {"outputPointer", "dataBlock", "indirectIndex"};
137  int idx = 0;
138  for (auto &arg : F->args()) arg.setName(names[idx++]);
139  }
140 
141  unsigned int dimDesired = (unsigned)desiredReturnType.dim();
142  unsigned int dimGenerated = parseTree->type().dim();
143  {
144  BasicBlock *BB = BasicBlock::Create(*_llvmContext, "entry", F);
145  IRBuilder<> Builder(BB);
146 
147  // codegen
148  Value *lastVal = parseTree->codegen(Builder);
149 
150  // return values through parameter.
151  Value *firstArg = &*F->arg_begin();
152  if (desireFP) {
153  if (dimGenerated > 1) {
154  Value *newLastVal = promoteToDim(lastVal, dimDesired, Builder);
155  assert(newLastVal->getType()->getVectorNumElements() >= dimDesired);
156  for (unsigned i = 0; i < dimDesired; ++i) {
157  Value *idx = ConstantInt::get(Type::getInt64Ty(*_llvmContext), i);
158  Value *val = Builder.CreateExtractElement(newLastVal, idx);
159  Value *ptr = Builder.CreateInBoundsGEP(firstArg, idx);
160  Builder.CreateStore(val, ptr);
161  }
162  } else if (dimGenerated == 1) {
163  for (unsigned i = 0; i < dimDesired; ++i) {
164  Value *ptr = Builder.CreateConstInBoundsGEP1_32(nullptr, firstArg, i);
165  Builder.CreateStore(lastVal, ptr);
166  }
167  } else {
168  assert(false && "error. dim of FP is less than 1.");
169  }
170  } else {
171  Builder.CreateStore(lastVal, firstArg);
172  }
173  Builder.CreateRetVoid();
174  }
175 
176  // write a new function
177  Type *LOOPParamTys[] = {Type::getInt8PtrTy(*_llvmContext), Type::getInt32Ty(*_llvmContext),
178  Type::getInt32Ty(*_llvmContext), Type::getInt32Ty(*_llvmContext), };
179  FunctionType *FTLOOP = FunctionType::get(Type::getVoidTy(*_llvmContext), LOOPParamTys, false);
180 
181  Function *FLOOP =
182  Function::Create(FTLOOP, Function::ExternalLinkage, uniqueName + "_loopfunc", TheModule.get());
183  {
184  // label the function with names
185  const char *names[] = {"dataBlock", "outputVarBlockOffset", "rangeStart", "rangeEnd"};
186  int idx = 0;
187  for (auto &arg : FLOOP->args()) {
188  arg.setName(names[idx++]);
189  }
190  }
191  {
192  // Local variables
193  Value *dimValue = ConstantInt::get(Type::getInt32Ty(*_llvmContext), dimDesired);
194  Value *oneValue = ConstantInt::get(Type::getInt32Ty(*_llvmContext), 1);
195 
196  // Basic blocks
197  BasicBlock *entryBlock = BasicBlock::Create(*_llvmContext, "entry", FLOOP);
198  BasicBlock *loopCmpBlock = BasicBlock::Create(*_llvmContext, "loopCmp", FLOOP);
199  BasicBlock *loopRepeatBlock = BasicBlock::Create(*_llvmContext, "loopRepeat", FLOOP);
200  BasicBlock *loopIncBlock = BasicBlock::Create(*_llvmContext, "loopInc", FLOOP);
201  BasicBlock *loopEndBlock = BasicBlock::Create(*_llvmContext, "loopEnd", FLOOP);
202  IRBuilder<> Builder(entryBlock);
203  Builder.SetInsertPoint(entryBlock);
204  Function::arg_iterator argIterator = FLOOP->arg_begin();
205  // Value* outputDoublePtr=&*argIterator;++argIterator;
206  Value *varBlockCharPtrPtrArg = &*argIterator;
207  ++argIterator;
208  Value *outputVarBlockOffsetArg = &*argIterator;
209  ++argIterator;
210  Value *rangeStartArg = &*argIterator;
211  ++argIterator;
212  Value *rangeEndArg = &*argIterator;
213  ++argIterator;
214 
215  // Allocate Variables
216  Value *rangeStartVar = Builder.CreateAlloca(Type::getInt32Ty(*_llvmContext), oneValue, "rangeStartVar");
217  Value *rangeEndVar = Builder.CreateAlloca(Type::getInt32Ty(*_llvmContext), oneValue, "rangeEndVar");
218  Value *indexVar = Builder.CreateAlloca(Type::getInt32Ty(*_llvmContext), oneValue, "indexVar");
219  Value *outputVarBlockOffsetVar =
220  Builder.CreateAlloca(Type::getInt32Ty(*_llvmContext), oneValue, "outputVarBlockOffsetVar");
221  Type *doublePtrPtrTy = llvm::PointerType::get(Type::getDoublePtrTy(*_llvmContext), 0);
222  Value *varBlockDoublePtrPtrVar = Builder.CreateAlloca(doublePtrPtrTy, oneValue, "varBlockDoublePtrPtrVar");
223  // Value*
224  // currentOutputVar=Builder.CreateAlloca(Type::getDoublePtrTy(*_llvmContext),oneValue,"currentOutputVar");
225  // Copy variables from args
226  Value *varBlockDoublePtrPtr =
227  Builder.CreatePointerCast(varBlockCharPtrPtrArg, doublePtrPtrTy, "varBlockAsDoublePtrPtr");
228  Builder.CreateStore(varBlockDoublePtrPtr, varBlockDoublePtrPtrVar);
229  Builder.CreateStore(rangeStartArg, rangeStartVar);
230  Builder.CreateStore(rangeEndArg, rangeEndVar);
231  Builder.CreateStore(outputVarBlockOffsetArg, outputVarBlockOffsetVar);
232 
233  // TODO: we need char* support right here
234  Value *outputBasePtrPtr = Builder.CreateGEP(
235  nullptr, Builder.CreateLoad(varBlockDoublePtrPtrVar), outputVarBlockOffsetArg, "outputBasePtrPtr");
236  Value *outputBasePtr = Builder.CreateLoad(outputBasePtrPtr, "outputBasePtr");
237  // Value*
238  // outputBasePtrOffset=Builder.CreateGEP(nullptr,outputBasePtr,Builder.CreateMul(dimValue,Builder.CreateLoad(rangeStartVar)),"outputPtr");
239  // Builder.CreateStore(outputBasePtrOffset,currentOutputVar);
240  Builder.CreateStore(Builder.CreateLoad(rangeStartVar), indexVar);
241 
242  Builder.CreateBr(loopCmpBlock);
243 
244  Builder.SetInsertPoint(loopCmpBlock);
245  Value *cond = Builder.CreateICmpULT(Builder.CreateLoad(indexVar), Builder.CreateLoad(rangeEndVar));
246  Builder.CreateCondBr(cond, loopRepeatBlock, loopEndBlock);
247 
248  Builder.SetInsertPoint(loopRepeatBlock);
249  Value *myOutputPtr =
250  Builder.CreateGEP(nullptr, outputBasePtr, Builder.CreateMul(dimValue, Builder.CreateLoad(indexVar)));
251  Builder.CreateCall(
252  F, {myOutputPtr, Builder.CreateLoad(varBlockDoublePtrPtrVar), Builder.CreateLoad(indexVar)});
253  // Builder.CreateStore(ConstantFP::get(Type::getDoubleTy(*_llvmContext),
254  // 3.14),Builder.CreateLoad(ptrVariable));
255 
256  Builder.CreateBr(loopIncBlock);
257 
258  Builder.SetInsertPoint(loopIncBlock);
259  Builder.CreateStore(Builder.CreateAdd(Builder.CreateLoad(indexVar), oneValue), indexVar); // i++
260  // Builder.CreateStore(Builder.CreateGEP(Builder.CreateLoad(currentOutputVar),dimValue),currentOutputVar);
261  // // currentOutput+=dim
262  Builder.CreateBr(loopCmpBlock);
263 
264  Builder.SetInsertPoint(loopEndBlock);
265  Builder.CreateRetVoid();
266  }
267 
268  if (Expression::debugging) {
269  std::cerr << "Pre verified LLVM byte code " << std::endl;
270  TheModule->dump();
271  }
272 
273  // TODO: Find out if there is a new way to veirfy
274  // if (verifyModule(*TheModule)) {
275  // std::cerr << "Logic error in code generation of LLVM alert developers" << std::endl;
276  // TheModule->dump();
277  // }
278  Module *altModule = TheModule.get();
279  std::string ErrStr;
280  TheExecutionEngine.reset(EngineBuilder(std::move(TheModule))
281  .setErrorStr(&ErrStr)
282  // .setUseMCJIT(true)
283  .setOptLevel(CodeGenOpt::Aggressive)
284  .create());
285 
286  altModule->setDataLayout(TheExecutionEngine->getDataLayout());
287 
288  // Add bindings to C linkage helper functions
289  TheExecutionEngine->addGlobalMapping(SeExpr2LLVMEvalVarRefFunc, (void*)SeExpr2LLVMEvalVarRef);
290  TheExecutionEngine->addGlobalMapping(SeExpr2LLVMEvalCustomFunctionFunc, (void*)SeExpr2LLVMEvalCustomFunction);
291 
292 
293  // [verify]
294  std::string errorStr;
295  llvm::raw_string_ostream raw(errorStr);
296  if(llvm::verifyModule(*altModule,&raw)){
297  parseTree->addError(raw.str());
298  return false;
299  }
300 
301  // Setup optimization
302  llvm::PassManagerBuilder builder;
303  std::unique_ptr<llvm::legacy::PassManager> pm(new llvm::legacy::PassManager);
304  std::unique_ptr<llvm::legacy::FunctionPassManager> fpm(new llvm::legacy::FunctionPassManager(altModule));
305  builder.OptLevel = 3;
306  builder.Inliner = llvm::createAlwaysInlinerPass();
307  builder.populateModulePassManager(*pm);
308  // fpm->add(new llvm::DataLayoutPass());
309  builder.populateFunctionPassManager(*fpm);
310  fpm->run(*F);
311  fpm->run(*FLOOP);
312  pm->run(*altModule);
313 
314  // Create the JIT. This takes ownership of the module.
315 
316  if (!TheExecutionEngine) {
317  fprintf(stderr, "Could not create ExecutionEngine: %s\n", ErrStr.c_str());
318  exit(1);
319  }
320 
321  TheExecutionEngine->finalizeObject();
322  void *fp = TheExecutionEngine->getPointerToFunction(F);
323  void *fpLoop = TheExecutionEngine->getPointerToFunction(FLOOP);
324  if (desireFP) {
325  _llvmEvalFP.reset(new LLVMEvaluationContext<double>);
326  _llvmEvalFP->init(fp, fpLoop, dimDesired);
327  } else {
328  _llvmEvalStr.reset(new LLVMEvaluationContext<char *>);
329  _llvmEvalStr->init(fp, fpLoop, dimDesired);
330  }
331 
332  if (Expression::debugging) {
333  std::cerr << "Pre verified LLVM byte code " << std::endl;
334  altModule->dump();
335  }
336 
337  return true;
338  }
339 
340  std::string getUniqueName() const {
341  std::ostringstream o;
342  o << std::setbase(16) << (uint64_t)(this);
343  return ("_" + o.str());
344  }
345 };
346 
347 #else // no LLVM support
349  public:
350  void unsupported() { throw std::runtime_error("LLVM is not enabled in build"); }
351  const char* evalStr(VarBlock* varBlock) {
352  unsupported();
353  return "";
354  }
355  const double* evalFP(VarBlock* varBlock) { unsupported(); return 0; }
356  bool prepLLVM(ExprNode* parseTree, ExprType desiredReturnType) { unsupported(); return false;}
357  void evalMultiple(VarBlock* varBlock, int outputVarBlockOffset, size_t rangeStart, size_t rangeEnd) {
358  unsupported();
359  }
360  void debugPrint() {}
361 };
362 #endif
363 
364 } // end namespace SeExpr2
A thread local evaluation context. Just allocate and fill in with data.
Definition: VarBlock.h:35
void SeExpr2LLVMEvalCustomFunction(int *opDataArg, double *fpArg, char **strArg, void **funcdata, const SeExpr2::ExprFuncNode *node)
Definition: ExprFuncX.cpp:108
void evalMultiple(VarBlock *varBlock, int outputVarBlockOffset, size_t rangeStart, size_t rangeEnd)
Definition: Evaluator.h:357
Node that calls a function.
Definition: ExprNode.h:514
static bool debugging
Whether to debug expressions.
Definition: Expression.h:86
bool prepLLVM(ExprNode *parseTree, ExprType desiredReturnType)
Definition: Evaluator.h:356
const char * evalStr(VarBlock *varBlock)
Definition: Evaluator.h:351
void SeExpr2LLVMEvalVarRef(SeExpr2::ExprVarRef *seVR, double *result)
const double * evalFP(VarBlock *varBlock)
Definition: Evaluator.h:355
double LLVM_VALUE
Definition: ExprLLVM.h:33
abstract class for implementing variable references
Definition: Expression.h:45