KSeExpr 6.0.0.0
Evaluator.h
Go to the documentation of this file.
1// SPDX-FileCopyrightText: 2011-2019 Disney Enterprises, Inc.
2// SPDX-License-Identifier: LicenseRef-Apache-2.0
3// SPDX-FileCopyrightText: 2020 L. E. Segovia <amy@amyspark.me>
4// SPDX-License-Identifier: GPL-3.0-or-later
5
6#include <cstdint>
7
8#include "ExprConfig.h"
9#include "ExprLLVMAll.h"
10#include "ExprNode.h"
11#include "VarBlock.h"
12
13extern "C" void KSeExprLLVMEvalFPVarRef(KSeExpr::ExprVarRef *seVR, double *result);
14extern "C" void KSeExprLLVMEvalStrVarRef(KSeExpr::ExprVarRef *seVR, double *result);
15extern "C" void KSeExprLLVMEvalCustomFunction(int *opDataArg, double *fpArg, char **strArg, void **funcdata, const KSeExpr::ExprFuncNode *node);
16
17namespace KSeExpr
18{
19#if defined(SEEXPR_ENABLE_LLVM)
20
21LLVM_VALUE promoteToDim(LLVM_VALUE val, unsigned dim, llvm::IRBuilder<> &Builder);
22
23class LLVMEvaluator
24{
25 // TODO: this seems needlessly complex, let's fix it
26 // TODO: let the dev code allocate memory?
27 // FP is the native function for this expression.
28 template<class T> class LLVMEvaluationContext
29 {
30 private:
31 using FunctionPtr = void (*)(T *, char **, uint32_t);
32 using FunctionPtrMultiple = void (*)(char **, uint32_t, uint32_t, uint32_t);
33 FunctionPtr functionPtr{nullptr};
34 FunctionPtrMultiple functionPtrMultiple{nullptr};
35 T *resultData{nullptr};
36
37 public:
38 LLVMEvaluationContext(const LLVMEvaluationContext &) = delete;
39 LLVMEvaluationContext &operator=(const LLVMEvaluationContext &) = delete;
40 LLVMEvaluationContext(LLVMEvaluationContext &&) noexcept = default;
41 LLVMEvaluationContext& operator=(LLVMEvaluationContext &&) noexcept = default;
42 ~LLVMEvaluationContext()
43 {
44 delete[] resultData;
45 }
46 LLVMEvaluationContext() = default;
47
48 void init(void *fp, void *fpLoop, int dim)
49 {
50 reset();
51 functionPtr = reinterpret_cast<FunctionPtr>(fp);
52 functionPtrMultiple = reinterpret_cast<FunctionPtrMultiple>(fpLoop);
53 resultData = new T[dim];
54 }
55 void reset()
56 {
57 delete[] resultData;
58 resultData = nullptr;
59 functionPtr = nullptr;
60 resultData = nullptr;
61 }
62 const T *operator()(VarBlock *varBlock)
63 {
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 {
70 assert(functionPtr && resultData);
71 functionPtrMultiple(varBlock ? varBlock->data() : nullptr, outputVarBlockOffset, rangeStart, rangeEnd);
72 }
73 };
74 std::unique_ptr<LLVMEvaluationContext<double>> _llvmEvalFP;
75 std::unique_ptr<LLVMEvaluationContext<char *>> _llvmEvalStr;
76
77 std::unique_ptr<llvm::LLVMContext> _llvmContext;
78 std::unique_ptr<llvm::ExecutionEngine> TheExecutionEngine;
79
80public:
81 LLVMEvaluator() = default;
82
83 const char *evalStr(VarBlock *varBlock)
84 {
85 return *(*_llvmEvalStr)(varBlock);
86 }
87 const double *evalFP(VarBlock *varBlock)
88 {
89 return (*_llvmEvalFP)(varBlock);
90 }
91
92 void evalMultiple(VarBlock *varBlock, uint32_t outputVarBlockOffset, uint32_t rangeStart, uint32_t rangeEnd)
93 {
94 return (*_llvmEvalFP)(varBlock, outputVarBlockOffset, rangeStart, rangeEnd);
95 }
96
97 void debugPrint()
98 {
99 // TheModule->print(llvm::errs(), nullptr);
100 }
101
102 bool prepLLVM(ExprNode *parseTree, const ExprType &desiredReturnType)
103 {
104 using namespace llvm;
105 InitializeNativeTarget();
106 InitializeNativeTargetAsmPrinter();
107 InitializeNativeTargetAsmParser();
108
109 std::string uniqueName = getUniqueName();
110
111 // create Module
112 _llvmContext = std::make_unique<LLVMContext>();
113
114 std::unique_ptr<Module> TheModule(new Module(uniqueName + "_module", *_llvmContext));
115
116 // create all needed types
117 Type *i8PtrTy = Type::getInt8PtrTy(*_llvmContext); // char *
118 PointerType *i8PtrPtrTy = PointerType::getUnqual(i8PtrTy); // char **
119 PointerType *i8PtrPtrPtrTy = PointerType::getUnqual(i8PtrPtrTy); // char ***
120 Type *i32Ty = Type::getInt32Ty(*_llvmContext); // int
121 Type *i32PtrTy = Type::getInt32PtrTy(*_llvmContext); // int *
122 Type *i64Ty = Type::getInt64Ty(*_llvmContext); // int64 *
123 Type *doublePtrTy = Type::getDoublePtrTy(*_llvmContext); // double *
124 PointerType *doublePtrPtrTy = PointerType::getUnqual(doublePtrTy); // double **
125 Type *voidTy = Type::getVoidTy(*_llvmContext); // void
126
127 // create bindings to helper functions for variables and fucntions
128 Function *KSeExprLLVMEvalCustomFunctionFunc = nullptr;
129 Function *KSeExprLLVMEvalFPVarRefFunc = nullptr;
130 Function *KSeExprLLVMEvalStrVarRefFunc = nullptr;
131 Function *KSeExprLLVMEvalstrlenFunc = nullptr;
132 Function *KSeExprLLVMEvalmallocFunc = nullptr;
133 Function *KSeExprLLVMEvalfreeFunc = nullptr;
134 Function *KSeExprLLVMEvalmemsetFunc = nullptr;
135 Function *KSeExprLLVMEvalstrcatFunc = nullptr;
136 Function *KSeExprLLVMEvalstrcmpFunc = nullptr;
137 {
138 {
139 FunctionType *FT = FunctionType::get(voidTy, {i32PtrTy, doublePtrTy, i8PtrPtrTy, i8PtrPtrTy, i64Ty}, false);
140 KSeExprLLVMEvalCustomFunctionFunc = Function::Create(FT, GlobalValue::ExternalLinkage, "KSeExprLLVMEvalCustomFunction", TheModule.get());
141 }
142 {
143 FunctionType *FT = FunctionType::get(voidTy, {i8PtrTy, doublePtrTy}, false);
144 KSeExprLLVMEvalFPVarRefFunc = Function::Create(FT, GlobalValue::ExternalLinkage, "KSeExprLLVMEvalFPVarRef", TheModule.get());
145 }
146 {
147 FunctionType *FT = FunctionType::get(voidTy, {i8PtrTy, i8PtrPtrTy}, false);
148 KSeExprLLVMEvalStrVarRefFunc = Function::Create(FT, GlobalValue::ExternalLinkage, "KSeExprLLVMEvalStrVarRef", TheModule.get());
149 }
150 {
151 FunctionType *FT = FunctionType::get(i32Ty, {i8PtrTy}, false);
152 KSeExprLLVMEvalstrlenFunc = Function::Create(FT, Function::ExternalLinkage, "strlen", TheModule.get());
153 }
154 {
155 FunctionType *FT = FunctionType::get(i8PtrTy, {i32Ty}, false);
156 KSeExprLLVMEvalmallocFunc = Function::Create(FT, Function::ExternalLinkage, "malloc", TheModule.get());
157 }
158 {
159 FunctionType *FT = FunctionType::get(voidTy, {i8PtrTy}, false);
160 KSeExprLLVMEvalfreeFunc = Function::Create(FT, Function::ExternalLinkage, "free", TheModule.get());
161 }
162 {
163 FunctionType *FT = FunctionType::get(voidTy, {i8PtrTy, i32Ty, i32Ty}, false);
164 KSeExprLLVMEvalmemsetFunc = Function::Create(FT, Function::ExternalLinkage, "memset", TheModule.get());
165 }
166 {
167 FunctionType *FT = FunctionType::get(i8PtrTy, {i8PtrTy, i8PtrTy}, false);
168 KSeExprLLVMEvalstrcatFunc = Function::Create(FT, Function::ExternalLinkage, "strcat", TheModule.get());
169 }
170 {
171 FunctionType *FT = FunctionType::get(i32Ty, {i8PtrTy, i8PtrTy}, false);
172 KSeExprLLVMEvalstrcmpFunc = Function::Create(FT, Function::ExternalLinkage, "strcmp", TheModule.get());
173 }
174 }
175
176 // create function and entry BB
177 bool desireFP = desiredReturnType.isFP();
178 std::array<Type *, 3> ParamTys = {desireFP ? doublePtrTy : i8PtrPtrTy, doublePtrPtrTy, i32Ty};
179 FunctionType *FT = FunctionType::get(voidTy, ParamTys, false);
180 Function *F = Function::Create(FT, Function::ExternalLinkage, uniqueName + "_func", TheModule.get());
181#if LLVM_VERSION_MAJOR > 4
182 F->addAttribute(llvm::AttributeList::FunctionIndex, llvm::Attribute::AlwaysInline);
183#else
184 F->addAttribute(llvm::AttributeSet::FunctionIndex, llvm::Attribute::AlwaysInline);
185#endif
186 {
187 // label the function with names
188 std::array<const char *, 3> names = {"outputPointer", "dataBlock", "indirectIndex"};
189 int idx = 0;
190 for (auto &arg : F->args())
191 arg.setName(names[idx++]);
192 }
193
194 auto dimDesired = desiredReturnType.dim();
195 auto dimGenerated = parseTree->type().dim();
196 {
197 BasicBlock *BB = BasicBlock::Create(*_llvmContext, "entry", F);
198 IRBuilder<> Builder(BB);
199
200 // codegen
201 Value *lastVal = parseTree->codegen(Builder);
202
203 // return values through parameter.
204 Value *firstArg = &*F->arg_begin();
205 if (desireFP) {
206 Value *newLastVal = promoteToDim(lastVal, dimDesired, Builder);
207 if (newLastVal->getType()->isVectorTy()) {
208 // Output is vector - copy values (if possible)
209
210 assert(dimDesired >= 1 && "error. dim of FP is less than 1.");
211
212 assert(dimGenerated >= 1 && "error. dim of FP is less than 1.");
213
214 assert(dimGenerated == 1 || dimGenerated >= dimDesired && "error: unable to match between FP of differing dimensions");
215
216 auto *VT = llvm::cast<llvm::VectorType>(newLastVal->getType());
217#if LLVM_VERSION_MAJOR >= 13
218 if (VT && VT->getElementCount().getKnownMinValue() >= dimDesired) {
219#else
220 if (VT && VT->getNumElements() >= dimDesired) {
221#endif
222 for (unsigned i = 0; i < dimDesired; ++i) {
223 Value *idx = ConstantInt::get(Type::getInt64Ty(*_llvmContext), i);
224 Value *val = Builder.CreateExtractElement(newLastVal, idx);
225 Value *ptr = IN_BOUNDS_GEP(Builder, firstArg, idx);
226 Builder.CreateStore(val, ptr);
227 }
228 } else {
229 for (unsigned i = 0; i < dimDesired; ++i) {
230 Value *idx = ConstantInt::get(Type::getInt64Ty(*_llvmContext), i);
231 Value *original_idx = ConstantInt::get(Type::getInt64Ty(*_llvmContext), 0);
232 Value *val = Builder.CreateExtractElement(newLastVal, original_idx);
233 Value *ptr = IN_BOUNDS_GEP(Builder, firstArg, idx);
234 Builder.CreateStore(val, ptr);
235 }
236 }
237 } else {
238 if (dimGenerated > 1) {
239 Value *newLastVal = promoteToDim(lastVal, dimDesired, Builder);
240#ifndef NDEBUG
241 auto *VT = llvm::cast<llvm::VectorType>(newLastVal->getType());
242#if LLVM_VERSION_MAJOR >= 13
243 assert(VT && VT->getElementCount().getKnownMinValue() >= dimDesired);
244#else
245 assert(VT && VT->getNumElements() >= dimDesired);
246#endif
247#endif
248 for (unsigned i = 0; i < dimDesired; ++i) {
249 Value *idx = ConstantInt::get(Type::getInt64Ty(*_llvmContext), i);
250 Value *val = Builder.CreateExtractElement(newLastVal, idx);
251 Value *ptr = IN_BOUNDS_GEP(Builder, firstArg, idx);
252 Builder.CreateStore(val, ptr);
253 }
254 } else if (dimGenerated == 1) {
255 for (unsigned i = 0; i < dimDesired; ++i) {
256 Value *ptr = Builder.CreateConstInBoundsGEP1_32(nullptr, firstArg, i);
257 Builder.CreateStore(lastVal, ptr);
258 }
259 } else {
260 assert(false && "error. dim of FP is less than 1.");
261 }
262 }
263 } else {
264 Builder.CreateStore(lastVal, firstArg);
265 }
266
267 Builder.CreateRetVoid();
268 }
269
270 // write a new function
271 FunctionType *FTLOOP = FunctionType::get(voidTy, {i8PtrTy, i32Ty, i32Ty, i32Ty}, false);
272 Function *FLOOP = Function::Create(FTLOOP, Function::ExternalLinkage, uniqueName + "_loopfunc", TheModule.get());
273 {
274 // label the function with names
275 std::array<const char *, 4> names = {"dataBlock", "outputVarBlockOffset", "rangeStart", "rangeEnd"};
276 int idx = 0;
277 for (auto &arg : FLOOP->args()) {
278 arg.setName(names[idx++]);
279 }
280 }
281 {
282 // Local variables
283 Value *dimValue = ConstantInt::get(i32Ty, dimDesired);
284 Value *oneValue = ConstantInt::get(i32Ty, 1);
285
286 // Basic blocks
287 BasicBlock *entryBlock = BasicBlock::Create(*_llvmContext, "entry", FLOOP);
288 BasicBlock *loopCmpBlock = BasicBlock::Create(*_llvmContext, "loopCmp", FLOOP);
289 BasicBlock *loopRepeatBlock = BasicBlock::Create(*_llvmContext, "loopRepeat", FLOOP);
290 BasicBlock *loopIncBlock = BasicBlock::Create(*_llvmContext, "loopInc", FLOOP);
291 BasicBlock *loopEndBlock = BasicBlock::Create(*_llvmContext, "loopEnd", FLOOP);
292 IRBuilder<> Builder(entryBlock);
293 Builder.SetInsertPoint(entryBlock);
294
295 // Get arguments
296 Function::arg_iterator argIterator = FLOOP->arg_begin();
297 Value *varBlockCharPtrPtrArg = &*argIterator;
298 ++argIterator;
299 Value *outputVarBlockOffsetArg = &*argIterator;
300 ++argIterator;
301 Value *rangeStartArg = &*argIterator;
302 ++argIterator;
303 Value *rangeEndArg = &*argIterator;
304 ++argIterator;
305
306 // Allocate Variables
307 Value *rangeStartVar = Builder.CreateAlloca(Type::getInt32Ty(*_llvmContext), oneValue, "rangeStartVar");
308 Value *rangeEndVar = Builder.CreateAlloca(Type::getInt32Ty(*_llvmContext), oneValue, "rangeEndVar");
309 Value *indexVar = Builder.CreateAlloca(Type::getInt32Ty(*_llvmContext), oneValue, "indexVar");
310 Value *outputVarBlockOffsetVar = Builder.CreateAlloca(Type::getInt32Ty(*_llvmContext), oneValue, "outputVarBlockOffsetVar");
311 Value *varBlockDoublePtrPtrVar = Builder.CreateAlloca(doublePtrPtrTy, oneValue, "varBlockDoublePtrPtrVar");
312 Value *varBlockTPtrPtrVar = Builder.CreateAlloca(desireFP == true ? doublePtrPtrTy : i8PtrPtrPtrTy, oneValue, "varBlockTPtrPtrVar");
313
314 // Copy variables from args
315 Builder.CreateStore(Builder.CreatePointerCast(varBlockCharPtrPtrArg, doublePtrPtrTy, "varBlockAsDoublePtrPtr"), varBlockDoublePtrPtrVar);
316 Builder.CreateStore(Builder.CreatePointerCast(varBlockCharPtrPtrArg, desireFP ? doublePtrPtrTy : i8PtrPtrPtrTy, "varBlockAsTPtrPtr"), varBlockTPtrPtrVar);
317 Builder.CreateStore(rangeStartArg, rangeStartVar);
318 Builder.CreateStore(rangeEndArg, rangeEndVar);
319 Builder.CreateStore(outputVarBlockOffsetArg, outputVarBlockOffsetVar);
320
321 // Set output pointer
322 Value *outputBasePtrPtr = Builder.CreateGEP(nullptr, CREATE_LOAD(Builder, varBlockTPtrPtrVar), outputVarBlockOffsetArg, "outputBasePtrPtr");
323 Value *outputBasePtr = CREATE_LOAD_WITH_ID(Builder, outputBasePtrPtr, "outputBasePtr");
324 Builder.CreateStore(CREATE_LOAD(Builder, rangeStartVar), indexVar);
325
326 Builder.CreateBr(loopCmpBlock);
327 Builder.SetInsertPoint(loopCmpBlock);
328 Value *cond = Builder.CreateICmpULT(CREATE_LOAD(Builder, indexVar), CREATE_LOAD(Builder, rangeEndVar));
329 Builder.CreateCondBr(cond, loopRepeatBlock, loopEndBlock);
330
331 Builder.SetInsertPoint(loopRepeatBlock);
332 Value *myOutputPtr = Builder.CreateGEP(nullptr, outputBasePtr, Builder.CreateMul(dimValue, CREATE_LOAD(Builder, indexVar)));
333 Builder.CreateCall(F, {myOutputPtr, CREATE_LOAD(Builder, varBlockDoublePtrPtrVar), CREATE_LOAD(Builder, indexVar)});
334
335 Builder.CreateBr(loopIncBlock);
336
337 Builder.SetInsertPoint(loopIncBlock);
338 Builder.CreateStore(Builder.CreateAdd(CREATE_LOAD(Builder, indexVar), oneValue), indexVar);
339 Builder.CreateBr(loopCmpBlock);
340
341 Builder.SetInsertPoint(loopEndBlock);
342 Builder.CreateRetVoid();
343 }
344
346#ifdef DEBUG
347 std::cerr << "Pre verified LLVM byte code " << std::endl;
348 TheModule->print(llvm::errs(), nullptr);
349#endif
350 }
351
352 // TODO: Find out if there is a new way to veirfy
353 // if (verifyModule(*TheModule)) {
354 // std::cerr << "Logic error in code generation of LLVM alert developers" << std::endl;
355 // TheModule->print(llvm::errs(), nullptr);
356 // }
357 Module *altModule = TheModule.get();
358 std::string ErrStr;
359 TheExecutionEngine.reset(EngineBuilder(std::move(TheModule))
360 .setErrorStr(&ErrStr)
361 // .setUseMCJIT(true)
362 .setOptLevel(CodeGenOpt::Aggressive)
363 .create());
364
365 altModule->setDataLayout(TheExecutionEngine->getDataLayout());
366
367 // Add bindings to C linkage helper functions
368 TheExecutionEngine->addGlobalMapping(KSeExprLLVMEvalFPVarRefFunc,
369 reinterpret_cast<void *>(KSeExprLLVMEvalFPVarRef));
370 TheExecutionEngine->addGlobalMapping(KSeExprLLVMEvalStrVarRefFunc,
371 reinterpret_cast<void *>(KSeExprLLVMEvalStrVarRef));
372 TheExecutionEngine->addGlobalMapping(KSeExprLLVMEvalCustomFunctionFunc,
373 reinterpret_cast<void *>(KSeExprLLVMEvalCustomFunction));
374 TheExecutionEngine->addGlobalMapping(KSeExprLLVMEvalstrlenFunc,
375 reinterpret_cast<void *>(strlen));
376 TheExecutionEngine->addGlobalMapping(KSeExprLLVMEvalstrcatFunc,
377 reinterpret_cast<void *>(strcat));
378 TheExecutionEngine->addGlobalMapping(KSeExprLLVMEvalstrcmpFunc,
379 reinterpret_cast<void *>(strcmp));
380 TheExecutionEngine->addGlobalMapping(KSeExprLLVMEvalmemsetFunc,
381 reinterpret_cast<void *>(memset));
382 TheExecutionEngine->addGlobalMapping(KSeExprLLVMEvalmallocFunc,
383 reinterpret_cast<void *>(malloc));
384 TheExecutionEngine->addGlobalMapping(KSeExprLLVMEvalfreeFunc,
385 reinterpret_cast<void *>(free));
386
387 // [verify]
388 std::string errorStr;
389 llvm::raw_string_ostream raw(errorStr);
390 if (llvm::verifyModule(*altModule, &raw)) {
391 parseTree->addError(ErrorCode::Unknown, {errorStr});
392 return false;
393 }
394
395 // Setup optimization
396 llvm::PassManagerBuilder builder;
397 std::unique_ptr<llvm::legacy::PassManager> pm(new llvm::legacy::PassManager);
398 std::unique_ptr<llvm::legacy::FunctionPassManager> fpm(new llvm::legacy::FunctionPassManager(altModule));
399 builder.OptLevel = 3;
400#if (LLVM_VERSION_MAJOR >= 4)
401 builder.Inliner = llvm::createAlwaysInlinerLegacyPass();
402#else
403 builder.Inliner = llvm::createAlwaysInlinerPass();
404#endif
405 builder.populateModulePassManager(*pm);
406 // fpm->add(new llvm::DataLayoutPass());
407 builder.populateFunctionPassManager(*fpm);
408 fpm->run(*F);
409 fpm->run(*FLOOP);
410 pm->run(*altModule);
411
412 // Create the JIT. This takes ownership of the module.
413
414 if (!TheExecutionEngine) {
415 std::cerr << "Could not create ExecutionEngine: " << ErrStr << std::endl;
416 exit(1);
417 }
418
419 TheExecutionEngine->finalizeObject();
420 void *fp = TheExecutionEngine->getPointerToFunction(F);
421 void *fpLoop = TheExecutionEngine->getPointerToFunction(FLOOP);
422 if (desireFP) {
423 _llvmEvalFP = std::make_unique<LLVMEvaluationContext<double>>();
424 _llvmEvalFP->init(fp, fpLoop, dimDesired);
425 } else {
426 _llvmEvalStr = std::make_unique<LLVMEvaluationContext<char *>>();
427 _llvmEvalStr->init(fp, fpLoop, dimDesired);
428 }
429
431#ifdef DEBUG
432 std::cerr << "Pre verified LLVM byte code " << std::endl;
433 altModule->print(llvm::errs(), nullptr);
434#endif
435 }
436
437 return true;
438 }
439
440 std::string getUniqueName() const
441 {
442 std::ostringstream o;
443 o << std::setbase(16) << reinterpret_cast<uintptr_t>(this);
444 return ("_" + o.str());
445 }
446};
447
448#else // no LLVM support
450{
451public:
452 static void unsupported()
453 {
454 assert(false && "LLVM is not enabled in build");
455 }
456 static const char *evalStr(VarBlock *)
457 {
458 unsupported();
459 return nullptr;
460 }
461 static const double *evalFP(VarBlock *)
462 {
463 unsupported();
464 return nullptr;
465 }
466 static bool prepLLVM(ExprNode *, ExprType)
467 {
468 unsupported();
469 return false;
470 }
471 static void evalMultiple(VarBlock *, int, size_t, size_t)
472 {
473 unsupported();
474 }
476 {
477 }
478};
479#endif
480
481} // end namespace KSeExpr
void KSeExprLLVMEvalFPVarRef(KSeExpr::ExprVarRef *seVR, double *result)
void KSeExprLLVMEvalStrVarRef(KSeExpr::ExprVarRef *seVR, double *result)
void KSeExprLLVMEvalCustomFunction(int *opDataArg, double *fpArg, char **strArg, void **funcdata, const KSeExpr::ExprFuncNode *node)
double LLVM_VALUE
Definition ExprLLVM.h:25
Node that calls a function.
Definition ExprNode.h:654
abstract class for implementing variable references
Definition Expression.h:36
static bool debugging
Whether to debug expressions.
Definition Expression.h:77
static bool prepLLVM(ExprNode *, ExprType)
Definition Evaluator.h:466
static const double * evalFP(VarBlock *)
Definition Evaluator.h:461
static const char * evalStr(VarBlock *)
Definition Evaluator.h:456
static void evalMultiple(VarBlock *, int, size_t, size_t)
Definition Evaluator.h:471
static void unsupported()
Definition Evaluator.h:452
A thread local evaluation context. Just allocate and fill in with data.
Definition VarBlock.h:22
@ Unknown
Unknown error (message = %1)
Definition ErrorCode.h:64