GCC Code Coverage Report


Directory: ./
File: openvdb_ax/openvdb_ax/compiler/Compiler.cc
Date: 2022-07-25 17:40:05
Exec Total Coverage
Lines: 212 259 81.9%
Functions: 41 42 97.6%
Branches: 225 463 48.6%

Line Branch Exec Source
1 // Copyright Contributors to the OpenVDB Project
2 // SPDX-License-Identifier: MPL-2.0
3
4 /// @file compiler/Compiler.cc
5
6 #include "Compiler.h"
7
8 #include "PointExecutable.h"
9 #include "VolumeExecutable.h"
10
11 #include "openvdb_ax/ast/Scanners.h"
12 #include "openvdb_ax/codegen/Functions.h"
13 #include "openvdb_ax/codegen/PointComputeGenerator.h"
14 #include "openvdb_ax/codegen/VolumeComputeGenerator.h"
15 #include "openvdb_ax/Exceptions.h"
16
17 #include <openvdb/Exceptions.h>
18
19 #include <llvm/ADT/Optional.h>
20 #include <llvm/ADT/Triple.h>
21 #include <llvm/Analysis/TargetLibraryInfo.h>
22 #include <llvm/Analysis/TargetTransformInfo.h>
23 #include <llvm/Config/llvm-config.h>
24 #include <llvm/ExecutionEngine/ExecutionEngine.h>
25 #include <llvm/IR/LegacyPassManager.h>
26 #include <llvm/IR/LLVMContext.h>
27 #include <llvm/IR/Mangler.h>
28 #include <llvm/IR/Module.h>
29 #include <llvm/IR/PassManager.h>
30 #include <llvm/IR/Verifier.h>
31 #include <llvm/IRReader/IRReader.h>
32 #include <llvm/MC/SubtargetFeature.h>
33 #include <llvm/Passes/PassBuilder.h>
34 #include <llvm/Support/Host.h>
35 #include <llvm/Support/MemoryBuffer.h>
36 #include <llvm/Support/raw_os_ostream.h>
37 #include <llvm/Support/SourceMgr.h> // SMDiagnostic
38 #include <llvm/Support/TargetRegistry.h>
39 #include <llvm/Target/TargetMachine.h>
40 #include <llvm/Target/TargetOptions.h>
41
42 // @note As of adding support for LLVM 5.0 we not longer explicitly
43 // perform standard compiler passes (-std-compile-opts) based on the changes
44 // to the opt binary in the llvm codebase (tools/opt.cpp). We also no
45 // longer explicitly perform:
46 // - llvm::createStripSymbolsPass()
47 // And have never performed any specific target machine analysis passes
48 //
49 // @todo Properly identify the IPO passes that we would benefit from using
50 // as well as what user controls would otherwise be appropriate
51
52 #include <llvm/Transforms/IPO.h> // Inter-procedural optimization passes
53 #include <llvm/Transforms/IPO/AlwaysInliner.h>
54 #include <llvm/Transforms/IPO/PassManagerBuilder.h>
55
56 #include <unordered_map>
57
58 namespace openvdb {
59 OPENVDB_USE_VERSION_NAMESPACE
60 namespace OPENVDB_VERSION_NAME {
61
62 namespace ax {
63
64 namespace
65 {
66
67 /// @brief Initialize a target machine for the host platform. Returns a nullptr
68 /// if a target could not be created.
69 /// @note This logic is based off the Kaleidoscope tutorial below with extensions
70 /// for CPU and CPU featrue set targetting
71 /// https://llvm.org/docs/tutorial/MyFirstLanguageFrontend/LangImpl08.html
72 inline std::unique_ptr<llvm::ExecutionEngine>
73 1535 initializeExecutionEngine(std::unique_ptr<llvm::Module> M, Logger& logger)
74 {
75 // This handles MARCH (i.e. we don't need to set it on the EngineBuilder)
76
2/4
✓ Branch 2 taken 1535 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 1535 times.
✗ Branch 7 not taken.
1535 M->setTargetTriple(llvm::sys::getDefaultTargetTriple());
77 llvm::Module* module = M.get();
78
79 // stringref->bool map of features->enabled
80 1535 llvm::StringMap<bool> HostFeatures;
81
2/4
✓ Branch 1 taken 1535 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 1535 times.
1535 if (!llvm::sys::getHostCPUFeatures(HostFeatures)) {
82 logger.warning("Unable to determine CPU host features");
83 }
84
85 std::vector<llvm::StringRef> features;
86
2/2
✓ Branch 0 taken 115125 times.
✓ Branch 1 taken 1535 times.
116660 for (auto& feature : HostFeatures) {
87
3/6
✓ Branch 0 taken 62935 times.
✓ Branch 1 taken 52190 times.
✓ Branch 3 taken 62935 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
115125 if (feature.second) features.emplace_back(feature.first());
88 }
89
90 std::string error;
91 std::unique_ptr<llvm::ExecutionEngine>
92
1/2
✓ Branch 1 taken 1535 times.
✗ Branch 2 not taken.
3070 EE(llvm::EngineBuilder(std::move(M))
93 .setErrorStr(&error)
94 .setEngineKind(llvm::EngineKind::JIT)
95 .setOptLevel(llvm::CodeGenOpt::Level::Default)
96
1/2
✓ Branch 1 taken 1535 times.
✗ Branch 2 not taken.
1535 .setMCPU(llvm::sys::getHostCPUName())
97
1/2
✓ Branch 1 taken 1535 times.
✗ Branch 2 not taken.
1535 .setMAttrs(features)
98 .create());
99
100
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1535 times.
1535 if (!EE) {
101 logger.error("Fatal AX Compiler error; the LLVM Execution engine could "
102 "not be initialized:\n" + error);
103 return nullptr;
104 }
105
106 // Data layout is also handled in the MCJIT from the generated target machine
107 // but we set it on the module in case opt passes request it
108
2/4
✓ Branch 1 taken 1535 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1535 times.
✗ Branch 4 not taken.
1535 if (auto* TM = EE->getTargetMachine()) {
109
1/4
✓ Branch 1 taken 1535 times.
✗ Branch 2 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
1535 module->setDataLayout(TM->createDataLayout());
110 }
111
112 return EE;
113 }
114
115 #ifndef USE_NEW_PASS_MANAGER
116
117 1503 void addStandardLinkPasses(llvm::legacy::PassManagerBase& passes)
118 {
119 3006 llvm::PassManagerBuilder builder;
120 1503 builder.VerifyInput = true;
121
1/2
✓ Branch 1 taken 1503 times.
✗ Branch 2 not taken.
1503 builder.Inliner = llvm::createFunctionInliningPass();
122
1/2
✓ Branch 1 taken 1503 times.
✗ Branch 2 not taken.
1503 builder.populateLTOPassManager(passes);
123 1503 }
124
125 /// This routine adds optimization passes based on selected optimization level
126 ///
127 1503 void addOptimizationPasses(llvm::legacy::PassManagerBase& passes,
128 llvm::legacy::FunctionPassManager& functionPasses,
129 llvm::TargetMachine* targetMachine,
130 const unsigned optLevel,
131 const unsigned sizeLevel,
132 const bool disableInline = false,
133 const bool disableUnitAtATime = false,
134 const bool disableLoopUnrolling = false,
135 const bool disableLoopVectorization = false,
136 const bool disableSLPVectorization = false)
137 {
138 3006 llvm::PassManagerBuilder builder;
139 1503 builder.OptLevel = optLevel;
140 1503 builder.SizeLevel = sizeLevel;
141
142
1/2
✓ Branch 0 taken 1503 times.
✗ Branch 1 not taken.
1503 if (disableInline) {
143 // No inlining pass
144
1/2
✓ Branch 0 taken 1503 times.
✗ Branch 1 not taken.
1503 } else if (optLevel > 1) {
145 1503 builder.Inliner =
146
1/2
✓ Branch 1 taken 1503 times.
✗ Branch 2 not taken.
1503 llvm::createFunctionInliningPass(optLevel, sizeLevel,
147 /*DisableInlineHotCallSite*/false);
148 } else {
149 builder.Inliner = llvm::createAlwaysInlinerLegacyPass();
150 }
151
152 #if LLVM_VERSION_MAJOR < 9
153 // Enable IPO. This corresponds to gcc's -funit-at-a-time
154 builder.DisableUnitAtATime = disableUnitAtATime;
155 #else
156 // unused from llvm 9
157 (void)(disableUnitAtATime);
158 #endif
159
160 // Disable loop unrolling in all relevant passes
161 1503 builder.DisableUnrollLoops =
162
1/2
✓ Branch 0 taken 1503 times.
✗ Branch 1 not taken.
1503 disableLoopUnrolling ? disableLoopUnrolling : optLevel == 0;
163
164 // See the following link for more info on vectorizers
165 // http://llvm.org/docs/Vectorizers.html
166 // (-vectorize-loops, -loop-vectorize)
167 1503 builder.LoopVectorize =
168
1/2
✓ Branch 0 taken 1503 times.
✗ Branch 1 not taken.
1503 disableLoopVectorization ? false : optLevel > 1 && sizeLevel < 2;
169 1503 builder.SLPVectorize =
170
1/2
✓ Branch 0 taken 1503 times.
✗ Branch 1 not taken.
1503 disableSLPVectorization ? false : optLevel > 1 && sizeLevel < 2;
171
172 // If a target machine is provided, allow the target to modify the pass manager
173 // e.g. by calling PassManagerBuilder::addExtension.
174
1/2
✓ Branch 0 taken 1503 times.
✗ Branch 1 not taken.
1503 if (targetMachine) {
175
1/2
✓ Branch 1 taken 1503 times.
✗ Branch 2 not taken.
1503 targetMachine->adjustPassManager(builder);
176 }
177
178
1/2
✓ Branch 1 taken 1503 times.
✗ Branch 2 not taken.
1503 builder.populateFunctionPassManager(functionPasses);
179
1/2
✓ Branch 1 taken 1503 times.
✗ Branch 2 not taken.
1503 builder.populateModulePassManager(passes);
180 1503 }
181
182 1503 void LLVMoptimise(llvm::Module& module,
183 const unsigned optLevel,
184 const unsigned sizeLevel,
185 llvm::TargetMachine* TM)
186 {
187 // Pass manager setup and IR optimisations
188
189 3006 llvm::legacy::PassManager passes;
190
2/4
✓ Branch 2 taken 1503 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 1503 times.
✗ Branch 6 not taken.
3006 llvm::TargetLibraryInfoImpl TLII(llvm::Triple(module.getTargetTriple()));
191
3/6
✓ Branch 1 taken 1503 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1503 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 1503 times.
✗ Branch 8 not taken.
1503 passes.add(new llvm::TargetLibraryInfoWrapperPass(TLII));
192
193 // Add internal analysis passes from the target machine.
194
4/8
✓ Branch 0 taken 1503 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 1503 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 1503 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 1503 times.
✗ Branch 10 not taken.
3006 if (TM) passes.add(llvm::createTargetTransformInfoWrapperPass(TM->getTargetIRAnalysis()));
195 else passes.add(llvm::createTargetTransformInfoWrapperPass(llvm::TargetIRAnalysis()));
196
197
1/2
✓ Branch 1 taken 1503 times.
✗ Branch 2 not taken.
3006 llvm::legacy::FunctionPassManager functionPasses(&module);
198
4/8
✓ Branch 0 taken 1503 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 1503 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 1503 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 1503 times.
✗ Branch 10 not taken.
3006 if (TM) functionPasses.add(llvm::createTargetTransformInfoWrapperPass(TM->getTargetIRAnalysis()));
199 else functionPasses.add(llvm::createTargetTransformInfoWrapperPass(llvm::TargetIRAnalysis()));
200
201
202
1/2
✓ Branch 1 taken 1503 times.
✗ Branch 2 not taken.
1503 addStandardLinkPasses(passes);
203
1/2
✓ Branch 1 taken 1503 times.
✗ Branch 2 not taken.
1503 addOptimizationPasses(passes, functionPasses, TM, optLevel, sizeLevel);
204
205
1/2
✓ Branch 1 taken 1503 times.
✗ Branch 2 not taken.
1503 functionPasses.doInitialization();
206
2/2
✓ Branch 0 taken 20383 times.
✓ Branch 1 taken 1503 times.
21886 for (llvm::Function& function : module) {
207
1/2
✓ Branch 1 taken 20383 times.
✗ Branch 2 not taken.
20383 functionPasses.run(function);
208 }
209
1/2
✓ Branch 1 taken 1503 times.
✗ Branch 2 not taken.
1503 functionPasses.doFinalization();
210
211
1/2
✓ Branch 1 taken 1503 times.
✗ Branch 2 not taken.
1503 passes.run(module);
212 1503 }
213
214 // OptimizationLevel moved from llvm 13
215 #if LLVM_VERSION_MAJOR <= 13
216 using LLVM_OPTIMIZATION_LEVEL = llvm::PassBuilder::OptimizationLevel;
217 #else
218 using LLVM_OPTIMIZATION_LEVEL = llvm::OptimizationLevel;
219 #endif
220
221 1503 void LLVMoptimise(llvm::Module& module,
222 const LLVM_OPTIMIZATION_LEVEL opt,
223 llvm::TargetMachine* TM)
224 {
225 unsigned optLevel = 0, sizeLevel = 0;
226
227 // LLVM_OPTIMIZATION_LEVEL is an enum in llvm 10
228 // and earlier, a class in llvm 11 and later (which holds
229 // various member data about the optimization level)
230 #if LLVM_VERSION_MAJOR < 11
231
1/6
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 1503 times.
✗ Branch 5 not taken.
1503 switch (opt) {
232 case LLVM_OPTIMIZATION_LEVEL::O0 : {
233 optLevel = 0; sizeLevel = 0;
234 break;
235 }
236 case LLVM_OPTIMIZATION_LEVEL::O1 : {
237 optLevel = 1; sizeLevel = 0;
238 break;
239 }
240 case LLVM_OPTIMIZATION_LEVEL::O2 : {
241 optLevel = 2; sizeLevel = 0;
242 break;
243 }
244 case LLVM_OPTIMIZATION_LEVEL::Os : {
245 optLevel = 2; sizeLevel = 1;
246 break;
247 }
248 case LLVM_OPTIMIZATION_LEVEL::Oz : {
249 optLevel = 2; sizeLevel = 2;
250 break;
251 }
252 1503 case LLVM_OPTIMIZATION_LEVEL::O3 : {
253 optLevel = 3; sizeLevel = 0;
254 1503 break;
255 }
256 1503 default : {}
257 }
258 #else
259 optLevel = opt.getSpeedupLevel();
260 sizeLevel = opt.getSizeLevel();
261 #endif
262
263 1503 LLVMoptimise(module, optLevel, sizeLevel, TM);
264 1503 }
265
266 #else
267
268 void LLVMoptimise(llvm::Module& module,
269 const LLVM_OPTIMIZATION_LEVEL optLevel,
270 llvm::TargetMachine* TM)
271 {
272 // use the PassBuilder for optimisation pass management
273 // see llvm's llvm/Passes/PassBuilder.h, tools/opt/NewPMDriver.cpp
274 // and clang's CodeGen/BackEndUtil.cpp for more info/examples
275 llvm::PassBuilder PB(TM);
276
277 llvm::LoopAnalysisManager LAM;
278 llvm::FunctionAnalysisManager FAM;
279 llvm::CGSCCAnalysisManager cGSCCAM;
280 llvm::ModuleAnalysisManager MAM;
281
282 // register all of the analysis passes available by default
283 PB.registerModuleAnalyses(MAM);
284 PB.registerCGSCCAnalyses(cGSCCAM);
285 PB.registerFunctionAnalyses(FAM);
286 PB.registerLoopAnalyses(LAM);
287
288 // the analysis managers above are interdependent so
289 // register dependent managers with each other via proxies
290 PB.crossRegisterProxies(LAM, FAM, cGSCCAM, MAM);
291
292 // the PassBuilder does not produce -O0 pipelines, so do that ourselves
293 if (optLevel == LLVM_OPTIMIZATION_LEVEL::O0) {
294 // matching clang -O0, only add inliner pass
295 // ref: clang CodeGen/BackEndUtil.cpp EmitAssemblyWithNewPassManager
296 llvm::ModulePassManager MPM;
297 MPM.addPass(llvm::AlwaysInlinerPass());
298 MPM.run(module, MAM);
299 }
300 else {
301 // create a clang-like optimisation pipeline for -O1, 2, s, z, 3
302 llvm::ModulePassManager MPM =
303 PB.buildPerModuleDefaultPipeline(optLevel);
304 MPM.run(*module, MAM);
305 }
306 }
307 #endif
308
309 3010 bool verify(const llvm::Module& module, Logger& logger)
310 {
311 6020 std::ostringstream os;
312 3010 llvm::raw_os_ostream out(os);
313
2/4
✓ Branch 1 taken 3010 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 3010 times.
3010 if (llvm::verifyModule(module, &out)) {
314 out.flush();
315 logger.error("Fatal AX Compiler error; the generated IR was invalid:\n" + os.str());
316 return false;
317 }
318 return true;
319 }
320
321 1507 void optimise(llvm::Module& module,
322 const CompilerOptions::OptLevel optLevel,
323 llvm::TargetMachine* TM)
324 {
325
2/7
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 1503 times.
✓ Branch 6 taken 4 times.
1507 switch (optLevel) {
326 case CompilerOptions::OptLevel::O0 : {
327 LLVMoptimise(module, LLVM_OPTIMIZATION_LEVEL::O0, TM);
328 break;
329 }
330 case CompilerOptions::OptLevel::O1 : {
331 LLVMoptimise(module, LLVM_OPTIMIZATION_LEVEL::O1, TM);
332 break;
333 }
334 case CompilerOptions::OptLevel::O2 : {
335 LLVMoptimise(module, LLVM_OPTIMIZATION_LEVEL::O2, TM);
336 break;
337 }
338 case CompilerOptions::OptLevel::Os : {
339 LLVMoptimise(module, LLVM_OPTIMIZATION_LEVEL::Os, TM);
340 break;
341 }
342 case CompilerOptions::OptLevel::Oz : {
343 LLVMoptimise(module, LLVM_OPTIMIZATION_LEVEL::Oz, TM);
344 break;
345 }
346 1503 case CompilerOptions::OptLevel::O3 : {
347 1503 LLVMoptimise(module, LLVM_OPTIMIZATION_LEVEL::O3, TM);
348 1503 break;
349 }
350 1507 case CompilerOptions::OptLevel::NONE :
351 default : {}
352 }
353 1507 }
354
355 1507 bool initializeGlobalFunctions(const codegen::FunctionRegistry& registry,
356 llvm::ExecutionEngine& engine,
357 llvm::Module& module,
358 Logger& logger)
359 {
360 const size_t count = logger.errors();
361
362 /// @note This is a copy of ExecutionEngine::getMangledName. LLVM's ExecutionEngine
363 /// provides two signatures for updating global mappings, one which takes a void* and
364 /// another which takes a uint64_t address. When providing function mappings,
365 /// it is potentially unsafe to cast pointers-to-functions to pointers-to-objects
366 /// as they are not guaranteed to have the same size on some (albeit non "standard")
367 /// platforms. getMangledName is protected, so a copy exists here to allows us to
368 /// call the uint64_t method.
369 /// @note This is only caught by -pendantic so this work around may be overkill
370
1/2
✓ Branch 1 taken 20356 times.
✗ Branch 2 not taken.
20356 auto getMangledName = [](const llvm::GlobalValue* GV,
371 const llvm::ExecutionEngine& E) -> std::string
372 {
373 llvm::SmallString<128> FullName;
374 const llvm::DataLayout& DL =
375
1/2
✓ Branch 1 taken 20356 times.
✗ Branch 2 not taken.
20356 GV->getParent()->getDataLayout().isDefault()
376
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 20356 times.
20356 ? E.getDataLayout()
377
1/2
✓ Branch 1 taken 20356 times.
✗ Branch 2 not taken.
20356 : GV->getParent()->getDataLayout();
378
3/8
✓ Branch 1 taken 20356 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 20356 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✓ Branch 8 taken 20356 times.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
20356 llvm::Mangler::getNameWithPrefix(FullName, GV->getName(), DL);
379 20356 return std::string(FullName.str());
380 };
381
382 /// @note Could use InstallLazyFunctionCreator here instead as follows:
383 ///
384 /// engine.InstallLazyFunctionCreator([](const std::string& name) -> void * {
385 /// // Loop through register and find matching symbol
386 /// });
387 ///
388 /// However note that if functions have been compiled with mLazyFunctions that the
389 /// below code using addGlobalMapping() only adds mapping for instantiated
390 /// functions anyway.
391 ///
392 /// @note Depending on how functions are inserted into LLVM (Linkage Type) in
393 /// the future, InstallLazyFunctionCreator may be required
394
2/2
✓ Branch 0 taken 159742 times.
✓ Branch 1 taken 1507 times.
161249 for (const auto& iter : registry.map()) {
395 const codegen::FunctionGroup* const function = iter.second.function();
396
2/2
✓ Branch 0 taken 153997 times.
✓ Branch 1 taken 5745 times.
159742 if (!function) continue;
397
398 const codegen::FunctionGroup::FunctionList& list = function->list();
399
2/2
✓ Branch 0 taken 93950 times.
✓ Branch 1 taken 5745 times.
99695 for (const codegen::Function::Ptr& decl : list) {
400
401 // llvmFunction may not exists if compiled without mLazyFunctions
402
1/2
✓ Branch 1 taken 93950 times.
✗ Branch 2 not taken.
93950 const llvm::Function* llvmFunction = module.getFunction(decl->symbol());
403
404 // if the function has an entry block, it's not a C binding - this is a
405 // quick check to improve performance (so we don't call virtual methods
406 // for every function)
407
2/2
✓ Branch 0 taken 82093 times.
✓ Branch 1 taken 11857 times.
95591 if (!llvmFunction) continue;
408
2/2
✓ Branch 0 taken 1641 times.
✓ Branch 1 taken 10216 times.
11857 if (llvmFunction->size() > 0) continue;
409
410 const codegen::CFunctionBase* binding =
411
1/2
✓ Branch 0 taken 10216 times.
✗ Branch 1 not taken.
10216 dynamic_cast<const codegen::CFunctionBase*>(decl.get());
412
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 10216 times.
10216 if (!binding) {
413 #ifndef NDEBUG
414 // some internally supported LLVm symbols (malloc, free, etc) are
415 // not prefixed with ax. and we don't generated a function body
416 if (llvmFunction->getName().startswith("ax.")) {
417 OPENVDB_LOG_WARN("Function with symbol \"" << decl->symbol() << "\" has "
418 "no function body and is not a C binding.");
419 }
420 #endif
421 continue;
422 }
423
424
1/2
✓ Branch 1 taken 10216 times.
✗ Branch 2 not taken.
10216 const uint64_t address = binding->address();
425
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 10216 times.
10216 if (address == 0) {
426 logger.error("Fatal AX Compiler error; No available mapping for C Binding "
427 "with symbol \"" + std::string(decl->symbol()) + "\"");
428 continue;
429 }
430 const std::string mangled =
431
1/2
✓ Branch 1 taken 10216 times.
✗ Branch 2 not taken.
10216 getMangledName(llvm::cast<llvm::GlobalValue>(llvmFunction), engine);
432
433 // error if updateGlobalMapping returned a previously mapped address, as
434 // we've overwritten something
435
1/2
✓ Branch 1 taken 10216 times.
✗ Branch 2 not taken.
10216 const uint64_t oldAddress = engine.updateGlobalMapping(mangled, address);
436
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 10216 times.
10216 if (oldAddress != 0 && oldAddress != address) {
437 logger.error("Fatal AX Compiler error; multiple functions are using the "
438 "same symbol \"" + std::string(decl->symbol()) + "\".");
439 }
440 }
441 }
442
443 #ifndef NDEBUG
444 // Loop through all functions and check to see if they have valid engine mappings.
445 // This can occur if lazy functions don't initialize their dependencies properly.
446 // @todo Really we should just loop through the module functions to begin with
447 // to init engine mappings - it would probably be faster but we'd have to do
448 // some string manipulation and it would assume function names have been set up
449 // correctly
450 const auto& list = module.getFunctionList();
451
2/2
✓ Branch 0 taken 21491 times.
✓ Branch 1 taken 1507 times.
22998 for (const auto& F : list) {
452
2/2
✓ Branch 0 taken 9825 times.
✓ Branch 1 taken 11666 times.
23017 if (F.size() > 0) continue;
453 // Some LLVM functions may also not be defined at this stage which is expected
454
2/2
✓ Branch 1 taken 1526 times.
✓ Branch 2 taken 10140 times.
11666 if (!F.getName().startswith("ax.")) continue;
455 const std::string mangled =
456 10140 getMangledName(llvm::cast<llvm::GlobalValue>(&F), engine);
457 const uint64_t address =
458
1/2
✓ Branch 1 taken 10140 times.
✗ Branch 2 not taken.
10140 engine.getAddressToGlobalIfAvailable(mangled);
459
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 10140 times.
10140 assert(address != 0 && "Unbound function!");
460 }
461 #endif
462
463 1507 return count == logger.errors();
464 }
465
466
1/2
✓ Branch 1 taken 1539 times.
✗ Branch 2 not taken.
1539 bool verifyTypedAccesses(const ast::Tree& tree, openvdb::ax::Logger& logger)
467 {
468 // verify the attributes and external variables requested in the syntax tree
469 // only have a single type. Note that the executer will also throw a runtime
470 // error if the same attribute is accessed with different types, but as that's
471 // currently not a valid state on a PointDataGrid, error in compilation as well
472 // @todo - introduce a framework for supporting custom preprocessors
473
474 const size_t errs = logger.errors();
475
476 std::unordered_map<std::string, std::string> nameType;
477
478 auto attributeOp =
479
1/2
✓ Branch 2 taken 10151 times.
✗ Branch 3 not taken.
22246 [&nameType, &logger](const ast::Attribute& node) -> bool {
480
2/2
✓ Branch 0 taken 10151 times.
✓ Branch 1 taken 1944 times.
12095 auto iter = nameType.find(node.name());
481
2/2
✓ Branch 0 taken 10151 times.
✓ Branch 1 taken 1944 times.
12095 if (iter == nameType.end()) {
482 10151 nameType[node.name()] = node.typestr();
483 }
484
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 1942 times.
1944 else if (iter->second != node.typestr()) {
485
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 logger.error("failed to compile ambiguous @ parameters. "
486
1/2
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
4 "\"" + node.name() + "\" has been accessed with different type elsewhere.", &node);
487 }
488 12095 return true;
489 1539 };
490
491
1/2
✓ Branch 1 taken 1539 times.
✗ Branch 2 not taken.
1539 ast::visitNodeType<ast::Attribute>(tree, attributeOp);
492
493 nameType.clear();
494
495 auto externalOp =
496
1/2
✓ Branch 2 taken 79 times.
✗ Branch 3 not taken.
160 [&nameType, &logger](const ast::ExternalVariable& node) -> bool {
497
2/2
✓ Branch 0 taken 79 times.
✓ Branch 1 taken 2 times.
81 auto iter = nameType.find(node.name());
498
2/2
✓ Branch 0 taken 79 times.
✓ Branch 1 taken 2 times.
81 if (iter == nameType.end()) {
499 79 nameType[node.name()] = node.typestr();
500 }
501
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 else if (iter->second != node.typestr()) {
502
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 logger.error("failed to compile ambiguous $ parameters. "
503
1/2
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
4 "\"" + node.name() + "\" has been accessed with different type elsewhere.", &node);
504 }
505 81 return true;
506
1/2
✓ Branch 1 taken 1539 times.
✗ Branch 2 not taken.
1539 };
507
508 ast::visitNodeType<ast::ExternalVariable>(tree, externalOp);
509
510 1539 return logger.errors() == errs;
511 }
512
513 inline void
514 1507 registerAccesses(const codegen::SymbolTable& globals, const AttributeRegistry& registry)
515 {
516 std::string name, type;
517
518
2/2
✓ Branch 0 taken 10222 times.
✓ Branch 1 taken 1507 times.
11729 for (const auto& global : globals.map()) {
519
520 // detect if this global variable is an attribute access
521 10222 const std::string& token = global.first;
522
3/4
✓ Branch 1 taken 10222 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 77 times.
✓ Branch 4 taken 10145 times.
10222 if (!ast::Attribute::nametypeFromToken(token, &name, &type)) continue;
523
524 const ast::tokens::CoreType typetoken =
525 10145 ast::tokens::tokenFromTypeString(type);
526
527 // add the access to the registry - this will force the executables
528 // to always request or create the data type
529
530
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 10145 times.
10145 const size_t index = registry.accessIndex(name, typetoken);
531
532 // should always be a GlobalVariable.
533
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 10145 times.
10145 assert(llvm::isa<llvm::GlobalVariable>(global.second));
534
535 // Assign the attribute index global a valid index.
536 // @note executionEngine->addGlobalMapping() can also be used if the indices
537 // ever need to vary positions without having to force a recompile (previously
538 // was used unnecessarily)
539
540 llvm::GlobalVariable* variable =
541 10145 llvm::cast<llvm::GlobalVariable>(global.second);
542
2/4
✓ Branch 1 taken 10145 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 10145 times.
10145 assert(variable->getValueType()->isIntegerTy(64));
543
544
2/4
✓ Branch 1 taken 10145 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 10145 times.
✗ Branch 5 not taken.
10145 variable->setInitializer(llvm::ConstantInt::get(variable->getValueType(), index));
545 variable->setConstant(true); // is not written to at runtime
546 }
547 1507 }
548
549 template <typename T, typename MetadataType = TypedMetadata<T>>
550 inline llvm::Constant*
551 154 initializeMetadataPtr(CustomData& data,
552 const std::string& name,
553 llvm::LLVMContext& C)
554 {
555 154 MetadataType* meta = data.getOrInsertData<MetadataType>(name);
556
1/2
✓ Branch 0 taken 77 times.
✗ Branch 1 not taken.
308 if (meta) return codegen::LLVMType<T>::get(C, &(meta->value()));
557 return nullptr;
558 }
559
560 inline bool
561 1507 registerExternalGlobals(const codegen::SymbolTable& globals,
562 CustomData::Ptr& dataPtr,
563 llvm::LLVMContext& C,
564 Logger& logger)
565 {
566 auto initializerFromToken =
567 77 [&](const ast::tokens::CoreType type, const std::string& name, CustomData& data) -> llvm::Constant* {
568
19/20
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 4 times.
✓ Branch 3 taken 5 times.
✓ Branch 4 taken 4 times.
✓ Branch 5 taken 4 times.
✓ Branch 6 taken 4 times.
✓ Branch 7 taken 4 times.
✓ Branch 8 taken 4 times.
✓ Branch 9 taken 4 times.
✓ Branch 10 taken 4 times.
✓ Branch 11 taken 4 times.
✓ Branch 12 taken 4 times.
✓ Branch 13 taken 4 times.
✓ Branch 14 taken 4 times.
✓ Branch 15 taken 4 times.
✓ Branch 16 taken 4 times.
✓ Branch 17 taken 4 times.
✓ Branch 18 taken 4 times.
✗ Branch 19 not taken.
77 switch (type) {
569 4 case ast::tokens::BOOL : return initializeMetadataPtr<bool>(data, name, C);
570 4 case ast::tokens::INT32 : return initializeMetadataPtr<int32_t>(data, name, C);
571 4 case ast::tokens::INT64 : return initializeMetadataPtr<int64_t>(data, name, C);
572 5 case ast::tokens::FLOAT : return initializeMetadataPtr<float>(data, name, C);
573 4 case ast::tokens::DOUBLE : return initializeMetadataPtr<double>(data, name, C);
574 4 case ast::tokens::VEC2I : return initializeMetadataPtr<math::Vec2<int32_t>>(data, name, C);
575 4 case ast::tokens::VEC2F : return initializeMetadataPtr<math::Vec2<float>>(data, name, C);
576 4 case ast::tokens::VEC2D : return initializeMetadataPtr<math::Vec2<double>>(data, name, C);
577 4 case ast::tokens::VEC3I : return initializeMetadataPtr<math::Vec3<int32_t>>(data, name, C);
578 4 case ast::tokens::VEC3F : return initializeMetadataPtr<math::Vec3<float>>(data, name, C);
579 4 case ast::tokens::VEC3D : return initializeMetadataPtr<math::Vec3<double>>(data, name, C);
580 4 case ast::tokens::VEC4I : return initializeMetadataPtr<math::Vec4<int32_t>>(data, name, C);
581 4 case ast::tokens::VEC4F : return initializeMetadataPtr<math::Vec4<float>>(data, name, C);
582 4 case ast::tokens::VEC4D : return initializeMetadataPtr<math::Vec4<double>>(data, name, C);
583 4 case ast::tokens::MAT3F : return initializeMetadataPtr<math::Mat3<float>>(data, name, C);
584 4 case ast::tokens::MAT3D : return initializeMetadataPtr<math::Mat3<double>>(data, name, C);
585 4 case ast::tokens::MAT4F : return initializeMetadataPtr<math::Mat4<float>>(data, name, C);
586 4 case ast::tokens::MAT4D : return initializeMetadataPtr<math::Mat4<double>>(data, name, C);
587 // @note could be const char*, but not all functions have support for const char* args
588 4 case ast::tokens::STRING : return initializeMetadataPtr<ax::codegen::String>(data, name, C);
589 case ast::tokens::UNKNOWN :
590 default : {
591 // grammar guarantees this is unreachable as long as all types are supported
592 assert(false && "Attribute type unsupported or not recognised");
593 return nullptr;
594 }
595 }
596 1507 };
597
598 bool success = true;
599 std::string name, typestr;
600
2/2
✓ Branch 0 taken 10222 times.
✓ Branch 1 taken 1507 times.
11729 for (const auto& global : globals.map()) {
601
602 10222 const std::string& token = global.first;
603
3/4
✓ Branch 1 taken 10222 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 10145 times.
✓ Branch 4 taken 77 times.
10222 if (!ast::ExternalVariable::nametypeFromToken(token, &name, &typestr)) continue;
604
605 const ast::tokens::CoreType typetoken =
606 77 ast::tokens::tokenFromTypeString(typestr);
607
608 // if we have any external variables, the custom data must be initialized to at least hold
609 // zero values (initialized by the default metadata types)
610
1/6
✗ Branch 0 not taken.
✓ Branch 1 taken 77 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
77 if (!dataPtr) dataPtr.reset(new CustomData);
611
612 // should always be a GlobalVariable.
613
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 77 times.
77 assert(llvm::isa<llvm::GlobalVariable>(global.second));
614
615 77 llvm::GlobalVariable* variable = llvm::cast<llvm::GlobalVariable>(global.second);
616
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 77 times.
77 assert(variable->getValueType() == codegen::LLVMType<uintptr_t>::get(C));
617
618
1/2
✓ Branch 1 taken 77 times.
✗ Branch 2 not taken.
77 llvm::Constant* initializer = initializerFromToken(typetoken, name, *dataPtr);
619
620
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 77 times.
77 if (!initializer) {
621 logger.error("Custom data \"" + name + "\" already exists with a different type.");
622 success = false;
623 continue;
624 }
625
626
1/2
✓ Branch 1 taken 77 times.
✗ Branch 2 not taken.
77 variable->setInitializer(initializer);
627 variable->setConstant(true); // is not written to at runtime
628 }
629
630 1507 return success;
631 }
632
633
1/2
✓ Branch 0 taken 771 times.
✗ Branch 1 not taken.
776 struct PointDefaultModifier :
634 public openvdb::ax::ast::Visitor<PointDefaultModifier, /*non-const*/false>
635 {
636 using openvdb::ax::ast::Visitor<PointDefaultModifier, false>::traverse;
637 using openvdb::ax::ast::Visitor<PointDefaultModifier, false>::visit;
638
639 const std::set<std::string> autoVecAttribs {"P", "v", "N", "Cd"};
640
641
2/2
✓ Branch 0 taken 90 times.
✓ Branch 1 taken 5996 times.
6086 bool visit(ast::Attribute* attrib) {
642
2/2
✓ Branch 0 taken 90 times.
✓ Branch 1 taken 5996 times.
6086 if (!attrib->inferred()) return true;
643
2/2
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 83 times.
90 if (autoVecAttribs.find(attrib->name()) == autoVecAttribs.end()) return true;
644
645 openvdb::ax::ast::Attribute::UniquePtr
646 7 replacement(new openvdb::ax::ast::Attribute(attrib->name(), ast::tokens::VEC3F, true));
647
2/4
✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 7 times.
7 if (!attrib->replace(replacement.get())) {
648 OPENVDB_THROW(AXCompilerError,
649 "Auto conversion of inferred attributes failed.");
650 }
651 replacement.release();
652
653 return true;
654 }
655 };
656
657 } // anonymous namespace
658
659 /////////////////////////////////////////////////////////////////////////////
660
661
1/2
✓ Branch 1 taken 1480 times.
✗ Branch 2 not taken.
1480 Compiler::Compiler(const CompilerOptions& options)
662 : mContext()
663 , mCompilerOptions(options)
664 1480 , mFunctionRegistry()
665 {
666
3/6
✓ Branch 1 taken 1480 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1480 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 1480 times.
✗ Branch 8 not taken.
1480 mContext.reset(new llvm::LLVMContext);
667
1/4
✓ Branch 1 taken 1480 times.
✗ Branch 2 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
2960 mFunctionRegistry = codegen::createDefaultRegistry(&options.mFunctionOptions);
668 1480 }
669
670 11 Compiler::UniquePtr Compiler::create(const CompilerOptions &options)
671 {
672
1/2
✓ Branch 2 taken 11 times.
✗ Branch 3 not taken.
11 UniquePtr compiler(new Compiler(options));
673 11 return compiler;
674 }
675
676 void Compiler::setFunctionRegistry(std::unique_ptr<codegen::FunctionRegistry>&& functionRegistry)
677 {
678 mFunctionRegistry = std::move(functionRegistry);
679 }
680
681 template <typename ExeT, typename GenT>
682 inline typename ExeT::Ptr
683 3078 Compiler::compile(const ast::Tree& tree,
684 const std::string& moduleName,
685 const std::vector<std::string>& functions,
686 CustomData::Ptr data,
687 Logger& logger)
688 {
689 // @todo Not technically necessary for volumes but does the
690 // executer/bindings handle this?
691
2/2
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 1535 times.
3078 if (!verifyTypedAccesses(tree, logger)) {
692 return nullptr;
693 }
694
695 // initialize the module and execution engine - the latter isn't needed
696 // for IR generation but we leave the creation of the TM to the EE.
697
698
3/6
✓ Branch 1 taken 1535 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1535 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 1535 times.
✗ Branch 8 not taken.
6122 std::unique_ptr<llvm::Module> M(new llvm::Module(moduleName, *mContext));
699 llvm::Module* module = M.get();
700
2/4
✓ Branch 1 taken 1535 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 1535 times.
6158 std::unique_ptr<llvm::ExecutionEngine> EE = initializeExecutionEngine(std::move(M), logger);
701
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1535 times.
3070 if (!EE) return nullptr;
702
703
1/2
✓ Branch 1 taken 1535 times.
✗ Branch 2 not taken.
3070 GenT codeGenerator(*module, mCompilerOptions.mFunctionOptions, *mFunctionRegistry, logger);
704
2/2
✓ Branch 1 taken 1526 times.
✓ Branch 2 taken 9 times.
3070 AttributeRegistry::Ptr attributes = codeGenerator.generate(tree);
705
706 // if there has been a compilation error through user error, exit
707
2/2
✓ Branch 0 taken 19 times.
✓ Branch 1 taken 1507 times.
3052 if (!attributes) {
708
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 19 times.
38 assert(logger.hasError());
709 return nullptr;
710 }
711
712 // map accesses (always do this prior to optimising as globals may be removed)
713
714
1/2
✓ Branch 1 taken 1507 times.
✗ Branch 2 not taken.
3014 registerAccesses(codeGenerator.globals(), *attributes);
715
716
2/4
✓ Branch 1 taken 1507 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 1507 times.
3014 if (!registerExternalGlobals(codeGenerator.globals(), data, *mContext, logger)) {
717 return nullptr;
718 }
719
720 // optimise and verify
721
722
3/6
✓ Branch 0 taken 1507 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 1507 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 1507 times.
3014 if (mCompilerOptions.mVerify && !verify(*module, logger)) return nullptr;
723
2/4
✓ Branch 1 taken 1507 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1507 times.
✗ Branch 5 not taken.
3014 optimise(*module, mCompilerOptions.mOptLevel, EE->getTargetMachine());
724
2/2
✓ Branch 0 taken 1503 times.
✓ Branch 1 taken 4 times.
3014 if (mCompilerOptions.mOptLevel != CompilerOptions::OptLevel::NONE) {
725
3/6
✓ Branch 0 taken 1503 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 1503 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 1503 times.
3006 if (mCompilerOptions.mVerify && !verify(*module, logger)) return nullptr;
726 }
727
728 // @todo re-constant fold!! although constant folding will work with constant
729 // expressions prior to optimisation, expressions like "int a = 1; cosh(a);"
730 // will still keep a call to cosh. This is because the current AX folding
731 // only checks for an immediate constant expression and for C bindings,
732 // like cosh, llvm its unable to optimise the call out (as it isn't aware
733 // of the function body). What llvm can do, however, is change this example
734 // into "cosh(1)" which we can then handle.
735
736 // map functions
737
738
2/4
✓ Branch 1 taken 1507 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 1507 times.
3014 if (!initializeGlobalFunctions(*mFunctionRegistry, *EE, *module, logger)) {
739 return nullptr;
740 }
741
742 // finalize mapping
743
744
1/2
✓ Branch 1 taken 1507 times.
✗ Branch 2 not taken.
3014 EE->finalizeObject();
745
746 // get the built function pointers
747
748 std::unordered_map<std::string, uint64_t> functionMap;
749
750
2/2
✓ Branch 0 taken 3014 times.
✓ Branch 1 taken 1507 times.
9042 for (const std::string& name : functions) {
751
1/2
✓ Branch 1 taken 3014 times.
✗ Branch 2 not taken.
6028 const uint64_t address = EE->getFunctionAddress(name);
752
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3014 times.
6028 if (!address) {
753 logger.error("Fatal AX Compiler error; Unable to compile compute "
754 "function \"" + name + "\"");
755 return nullptr;
756 }
757 6028 functionMap[name] = address;
758 }
759
760 // create final executable object
761
2/4
✓ Branch 1 taken 1507 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1507 times.
✗ Branch 5 not taken.
3014 return typename ExeT::Ptr(new ExeT(mContext,
762 std::move(EE),
763 attributes,
764 data,
765 functionMap,
766
3/12
✓ Branch 0 taken 1507 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1507 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1507 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
12056 tree));
767 }
768
769 template<>
770 OPENVDB_AX_API PointExecutable::Ptr
771 776 Compiler::compile<PointExecutable>(const ast::Tree& syntaxTree,
772 Logger& logger,
773 const CustomData::Ptr customData)
774 {
775 using GenT = codegen::codegen_internal::PointComputeGenerator;
776
777 776 openvdb::SharedPtr<ast::Tree> tree(syntaxTree.copy());
778
1/2
✓ Branch 1 taken 776 times.
✗ Branch 2 not taken.
776 PointDefaultModifier modifier;
779 modifier.traverse(tree.get());
780
781 const std::vector<std::string> functionNames {
782 codegen::PointKernelBufferRange::getDefaultName(),
783 codegen::PointKernelAttributeArray::getDefaultName()
784
5/12
✓ Branch 1 taken 776 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 776 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 776 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 776 times.
✗ Branch 11 not taken.
✗ Branch 13 not taken.
✓ Branch 14 taken 776 times.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
1552 };
785
786 return this->compile<PointExecutable, GenT>(*tree, "ax.point.module",
787
5/6
✓ Branch 1 taken 776 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 771 times.
✓ Branch 5 taken 5 times.
✓ Branch 6 taken 728 times.
✓ Branch 7 taken 43 times.
3056 functionNames, customData, logger);
788 }
789
790 template<>
791 OPENVDB_AX_API VolumeExecutable::Ptr
792 763 Compiler::compile<VolumeExecutable>(const ast::Tree& syntaxTree,
793 Logger& logger,
794 const CustomData::Ptr customData)
795 {
796 using GenT = codegen::codegen_internal::VolumeComputeGenerator;
797
798 const std::vector<std::string> functionNames {
799 // codegen::VolumeKernelValue::getDefaultName(), // currently unused directly
800 codegen::VolumeKernelBuffer::getDefaultName(),
801 codegen::VolumeKernelNode::getDefaultName()
802
5/12
✓ Branch 1 taken 763 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 763 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 763 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 763 times.
✗ Branch 11 not taken.
✗ Branch 13 not taken.
✓ Branch 14 taken 763 times.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
1526 };
803
804 return this->compile<VolumeExecutable, GenT>(syntaxTree, "ax.volume.module",
805
5/6
✓ Branch 1 taken 763 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 759 times.
✓ Branch 5 taken 4 times.
✓ Branch 6 taken 715 times.
✓ Branch 7 taken 44 times.
2245 functionNames, customData, logger);
806 }
807
808
809 } // namespace ax
810 } // namespace OPENVDB_VERSION_NAME
811 } // namespace openvdb
812
813