| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | // Copyright Contributors to the OpenVDB Project | ||
| 2 | // SPDX-License-Identifier: MPL-2.0 | ||
| 3 | |||
| 4 | /// @file compiler/Compiler.h | ||
| 5 | /// | ||
| 6 | /// @authors Nick Avramoussis, Francisco Gochez, Richard Jones | ||
| 7 | /// | ||
| 8 | /// @brief The OpenVDB AX Compiler class provides methods to generate | ||
| 9 | /// AX executables from a provided AX AST (or directly from a given | ||
| 10 | /// string). The class object exists to cache various structures, | ||
| 11 | /// primarily LLVM constructs, which benefit from existing across | ||
| 12 | /// additional compilation runs. | ||
| 13 | /// | ||
| 14 | |||
| 15 | #ifndef OPENVDB_AX_COMPILER_HAS_BEEN_INCLUDED | ||
| 16 | #define OPENVDB_AX_COMPILER_HAS_BEEN_INCLUDED | ||
| 17 | |||
| 18 | #include "CompilerOptions.h" | ||
| 19 | #include "CustomData.h" | ||
| 20 | #include "Logger.h" | ||
| 21 | |||
| 22 | #include "openvdb_ax/ax.h" // backward compat support for initialize() | ||
| 23 | #include "openvdb_ax/ast/Parse.h" | ||
| 24 | |||
| 25 | #include <openvdb/version.h> | ||
| 26 | |||
| 27 | #include <memory> | ||
| 28 | #include <sstream> | ||
| 29 | |||
| 30 | // forward | ||
| 31 | namespace llvm { | ||
| 32 | class LLVMContext; | ||
| 33 | } | ||
| 34 | |||
| 35 | namespace openvdb { | ||
| 36 | OPENVDB_USE_VERSION_NAMESPACE | ||
| 37 | namespace OPENVDB_VERSION_NAME { | ||
| 38 | |||
| 39 | namespace ax { | ||
| 40 | |||
| 41 | namespace codegen { | ||
| 42 | // forward | ||
| 43 | class FunctionRegistry; | ||
| 44 | } | ||
| 45 | |||
| 46 | /// @brief The compiler class. This holds an llvm context and set of compiler | ||
| 47 | /// options, and constructs executable objects (e.g. PointExecutable or | ||
| 48 | /// VolumeExecutable) from a syntax tree or snippet of code. | ||
| 49 | class OPENVDB_AX_API Compiler | ||
| 50 | { | ||
| 51 | public: | ||
| 52 | |||
| 53 | using Ptr = std::shared_ptr<Compiler>; | ||
| 54 | using UniquePtr = std::unique_ptr<Compiler>; | ||
| 55 | |||
| 56 | /// @brief Construct a compiler object with given settings | ||
| 57 | /// @param options CompilerOptions object with various settings | ||
| 58 | Compiler(const CompilerOptions& options = CompilerOptions()); | ||
| 59 | |||
| 60 |
1/2✓ Branch 0 taken 1480 times.
✗ Branch 1 not taken.
|
1480 | ~Compiler() = default; |
| 61 | |||
| 62 | /// @brief Static method for creating Compiler objects | ||
| 63 | static UniquePtr create(const CompilerOptions& options = CompilerOptions()); | ||
| 64 | |||
| 65 | /// @brief Compile a given AST into an executable object of the given type. | ||
| 66 | /// @param syntaxTree An abstract syntax tree to compile | ||
| 67 | /// @param logger Logger for errors and warnings during compilation, this | ||
| 68 | /// should be linked to an ast::Tree and populated with AST node + line | ||
| 69 | /// number mappings for this Tree, e.g. during ast::parse(). This Tree can | ||
| 70 | /// be different from the syntaxTree argument. | ||
| 71 | /// @param data Optional external/custom data which is to be referenced by | ||
| 72 | /// the executable object. It allows one to reference data held elsewhere, | ||
| 73 | /// such as inside of a DCC, from inside the AX code | ||
| 74 | /// @note If the logger has not been populated with AST node and line | ||
| 75 | /// mappings, all messages will appear without valid line and column | ||
| 76 | /// numbers. | ||
| 77 | template <typename ExecutableT> | ||
| 78 | typename ExecutableT::Ptr | ||
| 79 | compile(const ast::Tree& syntaxTree, | ||
| 80 | Logger& logger, | ||
| 81 | const CustomData::Ptr data = CustomData::Ptr()); | ||
| 82 | |||
| 83 | /// @brief Compile a given snippet of AX code into an executable object of | ||
| 84 | /// the given type. | ||
| 85 | /// @param code A string of AX code | ||
| 86 | /// @param logger Logger for errors and warnings during compilation, will be | ||
| 87 | /// cleared of existing data | ||
| 88 | /// @param data Optional external/custom data which is to be referenced by | ||
| 89 | /// the executable object. It allows one to reference data held elsewhere, | ||
| 90 | /// such as inside of a DCC, from inside the AX code | ||
| 91 | /// @note If compilation is unsuccessful, will return nullptr. Logger can | ||
| 92 | /// then be queried for errors. | ||
| 93 | template <typename ExecutableT> | ||
| 94 | typename ExecutableT::Ptr | ||
| 95 | 8 | compile(const std::string& code, | |
| 96 | Logger& logger, | ||
| 97 | const CustomData::Ptr data = CustomData::Ptr()) | ||
| 98 | { | ||
| 99 | 8 | logger.clear(); | |
| 100 | 8 | const ast::Tree::ConstPtr syntaxTree = ast::parse(code.c_str(), logger); | |
| 101 |
3/6✓ Branch 0 taken 6 times.
✓ Branch 1 taken 2 times.
✓ Branch 3 taken 6 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
|
20 | if (syntaxTree) return compile<ExecutableT>(*syntaxTree, logger, data); |
| 102 | else return nullptr; | ||
| 103 | } | ||
| 104 | |||
| 105 | /// @brief Compile a given snippet of AX code into an executable object of | ||
| 106 | /// the given type. | ||
| 107 | /// @param code A string of AX code | ||
| 108 | /// @param data Optional external/custom data which is to be referenced by | ||
| 109 | /// the executable object. It allows one to reference data held elsewhere, | ||
| 110 | /// such as inside of a DCC, from inside the AX code | ||
| 111 | /// @note Parser errors are handled separately from compiler errors. | ||
| 112 | /// Each are collected and produce runtime errors. | ||
| 113 | template <typename ExecutableT> | ||
| 114 | typename ExecutableT::Ptr | ||
| 115 | 83 | compile(const std::string& code, | |
| 116 | const CustomData::Ptr data = CustomData::Ptr()) | ||
| 117 | { | ||
| 118 | 58 | std::vector<std::string> errors; | |
| 119 |
3/8✓ Branch 3 taken 83 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 83 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 83 times.
✗ Branch 9 not taken.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
|
357 | openvdb::ax::Logger logger( |
| 120 | 32 | [&errors] (const std::string& error) { | |
| 121 |
1/4✓ Branch 2 taken 16 times.
✗ Branch 3 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
|
32 | errors.emplace_back(error + "\n"); |
| 122 | }, | ||
| 123 | [] (const std::string&) {} // ignore warnings | ||
| 124 | ); | ||
| 125 |
1/2✓ Branch 1 taken 83 times.
✗ Branch 2 not taken.
|
83 | const ast::Tree::ConstPtr syntaxTree = ast::parse(code.c_str(), logger); |
| 126 |
2/2✓ Branch 0 taken 10 times.
✓ Branch 1 taken 73 times.
|
83 | if (!errors.empty()) { |
| 127 |
1/2✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
|
20 | std::ostringstream os; |
| 128 |
3/4✓ Branch 0 taken 10 times.
✓ Branch 1 taken 10 times.
✓ Branch 3 taken 10 times.
✗ Branch 4 not taken.
|
30 | for (const auto& e : errors) os << e << "\n"; |
| 129 |
2/6✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 10 times.
✗ Branch 5 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
|
50 | OPENVDB_THROW(AXSyntaxError, os.str()); |
| 130 | } | ||
| 131 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 73 times.
|
73 | assert(syntaxTree); |
| 132 |
6/6✓ Branch 1 taken 64 times.
✓ Branch 2 taken 9 times.
✓ Branch 3 taken 6 times.
✓ Branch 4 taken 58 times.
✓ Branch 5 taken 15 times.
✓ Branch 6 taken 10 times.
|
162 | typename ExecutableT::Ptr exe = this->compile<ExecutableT>(*syntaxTree, logger, data); |
| 133 |
2/2✓ Branch 0 taken 6 times.
✓ Branch 1 taken 58 times.
|
64 | if (!errors.empty()) { |
| 134 |
1/2✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
|
12 | std::ostringstream os; |
| 135 |
3/4✓ Branch 0 taken 6 times.
✓ Branch 1 taken 6 times.
✓ Branch 3 taken 6 times.
✗ Branch 4 not taken.
|
18 | for (const auto& e : errors) os << e << "\n"; |
| 136 |
2/6✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 6 times.
✗ Branch 5 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
|
30 | OPENVDB_THROW(AXCompilerError, os.str()); |
| 137 | } | ||
| 138 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 58 times.
|
58 | assert(exe); |
| 139 | 58 | return exe; | |
| 140 | } | ||
| 141 | |||
| 142 | /// @brief Compile a given AST into an executable object of the given type. | ||
| 143 | /// @param syntaxTree An abstract syntax tree to compile | ||
| 144 | /// @param data Optional external/custom data which is to be referenced by | ||
| 145 | /// the executable object. It allows one to reference data held elsewhere, | ||
| 146 | /// such as inside of a DCC, from inside the AX code | ||
| 147 | /// @note Any errors encountered are collected into a single runtime error | ||
| 148 | template <typename ExecutableT> | ||
| 149 | typename ExecutableT::Ptr | ||
| 150 | 2 | compile(const ast::Tree& syntaxTree, | |
| 151 | const CustomData::Ptr data = CustomData::Ptr()) | ||
| 152 | { | ||
| 153 | ✗ | std::vector<std::string> errors; | |
| 154 |
2/6✓ Branch 3 taken 2 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 2 times.
✗ Branch 6 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
|
8 | openvdb::ax::Logger logger( |
| 155 | 4 | [&errors] (const std::string& error) { | |
| 156 |
1/2✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
|
4 | errors.emplace_back(error + "\n"); |
| 157 | }, | ||
| 158 | [] (const std::string&) {} // ignore warnings | ||
| 159 | ); | ||
| 160 |
2/4✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
✗ Branch 4 not taken.
|
6 | auto exe = compile<ExecutableT>(syntaxTree, logger, data); |
| 161 |
1/2✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
|
2 | if (!errors.empty()) { |
| 162 |
1/2✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
|
4 | std::ostringstream os; |
| 163 |
3/4✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2 times.
✓ Branch 3 taken 2 times.
✗ Branch 4 not taken.
|
6 | for (const auto& e : errors) os << e << "\n"; |
| 164 |
2/6✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
|
10 | OPENVDB_THROW(AXCompilerError, os.str()); |
| 165 | } | ||
| 166 | ✗ | assert(exe); | |
| 167 | ✗ | return exe; | |
| 168 | } | ||
| 169 | |||
| 170 | /// @brief Sets the compiler's function registry object. | ||
| 171 | /// @param functionRegistry A unique pointer to a FunctionRegistry object. | ||
| 172 | /// The compiler will take ownership of the registry that was passed in. | ||
| 173 | /// @todo Perhaps allow one to register individual functions into this | ||
| 174 | /// class rather than the entire registry at once, and/or allow one to | ||
| 175 | /// extract a pointer to the registry and update it manually. | ||
| 176 | void setFunctionRegistry(std::unique_ptr<codegen::FunctionRegistry>&& functionRegistry); | ||
| 177 | |||
| 178 | /////////////////////////////////////////////////////////////////////////// | ||
| 179 | |||
| 180 | private: | ||
| 181 | template <typename ExeT, typename GenT> | ||
| 182 | typename ExeT::Ptr | ||
| 183 | compile(const ast::Tree& tree, | ||
| 184 | const std::string& moduleName, | ||
| 185 | const std::vector<std::string>& functions, | ||
| 186 | CustomData::Ptr data, | ||
| 187 | Logger& logger); | ||
| 188 | |||
| 189 | private: | ||
| 190 | std::shared_ptr<llvm::LLVMContext> mContext; | ||
| 191 | const CompilerOptions mCompilerOptions; | ||
| 192 | std::shared_ptr<codegen::FunctionRegistry> mFunctionRegistry; | ||
| 193 | }; | ||
| 194 | |||
| 195 | |||
| 196 | } // namespace ax | ||
| 197 | } // namespace OPENVDB_VERSION_NAME | ||
| 198 | } // namespace openvdb | ||
| 199 | |||
| 200 | #endif // OPENVDB_AX_COMPILER_HAS_BEEN_INCLUDED | ||
| 201 | |||
| 202 |