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 |