| 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 |