| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | // Copyright Contributors to the OpenVDB Project | ||
| 2 | // SPDX-License-Identifier: MPL-2.0 | ||
| 3 | |||
| 4 | /// @file codegen/ComputeGenerator.h | ||
| 5 | /// | ||
| 6 | /// @authors Nick Avramoussis, Matt Warner, Francisco Gochez, Richard Jones | ||
| 7 | /// | ||
| 8 | /// @brief The core visitor framework for code generation | ||
| 9 | /// | ||
| 10 | |||
| 11 | #ifndef OPENVDB_AX_COMPUTE_GENERATOR_HAS_BEEN_INCLUDED | ||
| 12 | #define OPENVDB_AX_COMPUTE_GENERATOR_HAS_BEEN_INCLUDED | ||
| 13 | |||
| 14 | #include "FunctionRegistry.h" | ||
| 15 | #include "FunctionTypes.h" | ||
| 16 | #include "SymbolTable.h" | ||
| 17 | |||
| 18 | #include "../ast/AST.h" | ||
| 19 | #include "../ast/Visitor.h" | ||
| 20 | #include "../compiler/CompilerOptions.h" | ||
| 21 | #include "../compiler/Logger.h" | ||
| 22 | |||
| 23 | #include <openvdb/version.h> | ||
| 24 | |||
| 25 | #include <llvm/Analysis/TargetLibraryInfo.h> | ||
| 26 | #include <llvm/IR/BasicBlock.h> | ||
| 27 | #include <llvm/IR/Function.h> | ||
| 28 | #include <llvm/IR/IRBuilder.h> | ||
| 29 | #include <llvm/IR/LLVMContext.h> | ||
| 30 | #include <llvm/IR/Module.h> | ||
| 31 | |||
| 32 | #include <stack> | ||
| 33 | |||
| 34 | namespace openvdb { | ||
| 35 | OPENVDB_USE_VERSION_NAMESPACE | ||
| 36 | namespace OPENVDB_VERSION_NAME { | ||
| 37 | |||
| 38 | namespace ax { | ||
| 39 | namespace codegen { | ||
| 40 | |||
| 41 | /// @brief The function definition and signature which is built by the | ||
| 42 | /// ComputeGenerator. | ||
| 43 | /// | ||
| 44 | /// The argument structure is as follows: | ||
| 45 | /// | ||
| 46 | /// 1) - A void pointer to the CustomData | ||
| 47 | /// | ||
| 48 | struct ComputeKernel | ||
| 49 | { | ||
| 50 | /// The name of the generated function | ||
| 51 | static const std::string Name; | ||
| 52 | |||
| 53 | /// The signature of the generated function | ||
| 54 | using Signature = void(const void* const); | ||
| 55 | using FunctionTraitsT = codegen::FunctionTraits<Signature>; | ||
| 56 | static const size_t N_ARGS = FunctionTraitsT::N_ARGS; | ||
| 57 | |||
| 58 | /// The argument key names available during code generation | ||
| 59 | static const std::array<std::string, N_ARGS>& getArgumentKeys(); | ||
| 60 | static std::string getDefaultName(); | ||
| 61 | }; | ||
| 62 | |||
| 63 | |||
| 64 | /////////////////////////////////////////////////////////////////////////// | ||
| 65 | /////////////////////////////////////////////////////////////////////////// | ||
| 66 | |||
| 67 | namespace codegen_internal { | ||
| 68 | |||
| 69 | /// @brief Visitor object which will generate llvm IR for a syntax tree. This | ||
| 70 | /// provides the majority of the code generation functionality except for | ||
| 71 | /// attribute access. This design allows for custom geometry to define their | ||
| 72 | /// IR implementations for these accesses by deriving and extending this | ||
| 73 | /// generator with ast::Attribute handling (see PointComputeGenerator.h and | ||
| 74 | /// VolumeComputeGenerator.h for examples). | ||
| 75 | /// @note The visit/traverse methods work slightly differently to the normal | ||
| 76 | /// Visitor to allow proper handling of errors and visitation history. Nodes | ||
| 77 | /// that inherit from ast::Expression can return false from visit() (and so | ||
| 78 | /// traverse()), but this will not necessarily stop traversal altogether. | ||
| 79 | /// Instead, any ast::Statements that are not also ast::Expressions i.e. | ||
| 80 | /// Block, ConditionalStatement, Loop, DeclareLocal, etc override their visit | ||
| 81 | /// and traverse methods to handle custom traversal order, and the catching | ||
| 82 | /// of failed child Expression visit/traverse calls. This allows errors in | ||
| 83 | /// independent Statements to not halt traversal for future Statements and so | ||
| 84 | /// allow capturing of multiple errors in an ast::Tree in a single call to | ||
| 85 | /// ComputeGenerator::generate(). | ||
| 86 | struct OPENVDB_AX_API ComputeGenerator : public ast::Visitor<ComputeGenerator> | ||
| 87 | { | ||
| 88 | ComputeGenerator(llvm::Module& module, | ||
| 89 | const FunctionOptions& options, | ||
| 90 | FunctionRegistry& functionRegistry, | ||
| 91 | Logger& logger); | ||
| 92 | |||
| 93 | 3632 | virtual ~ComputeGenerator() = default; | |
| 94 | |||
| 95 | bool generate(const ast::Tree&); | ||
| 96 | |||
| 97 | inline SymbolTable& globals() { return mSymbolTables.globals(); } | ||
| 98 | inline const SymbolTable& globals() const { return mSymbolTables.globals(); } | ||
| 99 | |||
| 100 | // Visitor pattern | ||
| 101 | |||
| 102 | using ast::Visitor<ComputeGenerator>::traverse; | ||
| 103 | using ast::Visitor<ComputeGenerator>::visit; | ||
| 104 | |||
| 105 | /// @brief Code generation always runs post order | ||
| 106 | inline bool postOrderNodes() const { return true; } | ||
| 107 | |||
| 108 | /// @brief Custom traversal of scoped blocks | ||
| 109 | /// @note This overrides the default traversal to incorporate | ||
| 110 | /// the scoping of variables declared in this block | ||
| 111 | bool traverse(const ast::Block* block) | ||
| 112 | { | ||
| 113 |
4/8✓ Branch 0 taken 1816 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 200 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 160 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 50 times.
✗ Branch 7 not taken.
|
2226 | if (!block) return true; |
| 114 |
8/10✓ Branch 1 taken 1526 times.
✓ Branch 2 taken 281 times.
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 168 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 200 times.
✓ Branch 10 taken 1 times.
✓ Branch 11 taken 159 times.
✗ Branch 13 not taken.
✓ Branch 14 taken 50 times.
|
2395 | if (!this->visit(block)) return false; |
| 115 | return true; | ||
| 116 | } | ||
| 117 | |||
| 118 | /// @brief Custom traversal of comma expression | ||
| 119 | /// @note This overrides the default traversal to handle errors | ||
| 120 | /// without stopping generation of entire list | ||
| 121 | /// @todo Replace with a binary operator that simply returns the second value | ||
| 122 | bool traverse(const ast::CommaOperator* comma) | ||
| 123 | { | ||
| 124 | if (!comma) return true; | ||
| 125 |
2/2✓ Branch 1 taken 2 times.
✓ Branch 2 taken 176 times.
|
178 | if (!this->visit(comma)) return false; |
| 126 | return true; | ||
| 127 | } | ||
| 128 | |||
| 129 | |||
| 130 | /// @brief Custom traversal of conditional statements | ||
| 131 | /// @note This overrides the default traversal to handle | ||
| 132 | /// branching between different code paths | ||
| 133 | bool traverse(const ast::ConditionalStatement* cond) | ||
| 134 | { | ||
| 135 | if (!cond) return true; | ||
| 136 |
2/2✓ Branch 1 taken 14 times.
✓ Branch 2 taken 159 times.
|
173 | if (!this->visit(cond)) return false; |
| 137 | return true; | ||
| 138 | } | ||
| 139 | |||
| 140 | /// @brief Custom traversal of binary operators | ||
| 141 | /// @note This overrides the default traversal to handle | ||
| 142 | /// short-circuiting in logical AND and OR | ||
| 143 | bool traverse(const ast::BinaryOperator* bin) | ||
| 144 | { | ||
| 145 | if (!bin) return true; | ||
| 146 |
2/2✓ Branch 1 taken 125 times.
✓ Branch 2 taken 1719 times.
|
1844 | if (!this->visit(bin)) return false; |
| 147 | return true; | ||
| 148 | } | ||
| 149 | |||
| 150 | /// @brief Custom traversal of ternary operators | ||
| 151 | /// @note This overrides the default traversal to handle | ||
| 152 | /// branching between different code paths | ||
| 153 | bool traverse(const ast::TernaryOperator* tern) | ||
| 154 | { | ||
| 155 | if (!tern) return true; | ||
| 156 |
2/2✓ Branch 1 taken 33 times.
✓ Branch 2 taken 151 times.
|
184 | if (!this->visit(tern)) return false; |
| 157 | return true; | ||
| 158 | } | ||
| 159 | |||
| 160 | /// @brief Custom traversal of loops | ||
| 161 | /// @note This overrides the default traversal to handle | ||
| 162 | /// branching between different code paths and the | ||
| 163 | /// scoping of variables in for-loop initialisation | ||
| 164 | bool traverse(const ast::Loop* loop) | ||
| 165 | { | ||
| 166 | if (!loop) return true; | ||
| 167 |
2/2✓ Branch 1 taken 36 times.
✓ Branch 2 taken 164 times.
|
200 | if (!this->visit(loop)) return false; |
| 168 | return true; | ||
| 169 | } | ||
| 170 | |||
| 171 | /// @brief Custom traversal of declarations | ||
| 172 | /// @note This overrides the default traversal to | ||
| 173 | /// handle traversal of the local and | ||
| 174 | /// assignment of initialiser, if it exists | ||
| 175 | bool traverse(const ast::DeclareLocal* decl) | ||
| 176 | { | ||
| 177 | if (!decl) return true; | ||
| 178 |
2/2✓ Branch 1 taken 10 times.
✓ Branch 2 taken 3577 times.
|
3587 | if (!this->visit(decl)) return false; |
| 179 | return true; | ||
| 180 | } | ||
| 181 | |||
| 182 | ///@{ | ||
| 183 | /// @brief Visitor methods for all AST nodes which implement IR generation | ||
| 184 | virtual bool visit(const ast::CommaOperator*); | ||
| 185 | virtual bool visit(const ast::AssignExpression*); | ||
| 186 | virtual bool visit(const ast::Crement*); | ||
| 187 | virtual bool visit(const ast::FunctionCall*); | ||
| 188 | virtual bool visit(const ast::Attribute*); | ||
| 189 | virtual bool visit(const ast::Tree*); | ||
| 190 | virtual bool visit(const ast::Block*); | ||
| 191 | virtual bool visit(const ast::ConditionalStatement*); | ||
| 192 | virtual bool visit(const ast::Loop*); | ||
| 193 | virtual bool visit(const ast::Keyword*); | ||
| 194 | virtual bool visit(const ast::UnaryOperator*); | ||
| 195 | virtual bool visit(const ast::BinaryOperator*); | ||
| 196 | virtual bool visit(const ast::TernaryOperator*); | ||
| 197 | virtual bool visit(const ast::Cast*); | ||
| 198 | virtual bool visit(const ast::DeclareLocal*); | ||
| 199 | virtual bool visit(const ast::Local*); | ||
| 200 | virtual bool visit(const ast::ExternalVariable*); | ||
| 201 | virtual bool visit(const ast::ArrayUnpack*); | ||
| 202 | virtual bool visit(const ast::ArrayPack*); | ||
| 203 | virtual bool visit(const ast::Value<bool>*); | ||
| 204 | virtual bool visit(const ast::Value<int16_t>*); | ||
| 205 | virtual bool visit(const ast::Value<int32_t>*); | ||
| 206 | virtual bool visit(const ast::Value<int64_t>*); | ||
| 207 | virtual bool visit(const ast::Value<float>*); | ||
| 208 | virtual bool visit(const ast::Value<double>*); | ||
| 209 | virtual bool visit(const ast::Value<std::string>*); | ||
| 210 | |||
| 211 | template <typename ValueType> | ||
| 212 | typename std::enable_if<std::is_integral<ValueType>::value, bool>::type | ||
| 213 | visit(const ast::Value<ValueType>* node); | ||
| 214 | template <typename ValueType> | ||
| 215 | |||
| 216 | typename std::enable_if<std::is_floating_point<ValueType>::value, bool>::type | ||
| 217 | visit(const ast::Value<ValueType>* node); | ||
| 218 | ///@} | ||
| 219 | |||
| 220 | protected: | ||
| 221 | |||
| 222 | const FunctionGroup* getFunction(const std::string& identifier, | ||
| 223 | const bool allowInternal = false); | ||
| 224 | |||
| 225 | bool binaryExpression(llvm::Value*& result, llvm::Value* lhs, llvm::Value* rhs, | ||
| 226 | const ast::tokens::OperatorToken op, const ast::Node* node); | ||
| 227 | bool assignExpression(llvm::Value* lhs, llvm::Value*& rhs, const ast::Node* node); | ||
| 228 | |||
| 229 | /// @brief Clear any strings which were allocated in a given function. | ||
| 230 | /// This method accepts an IRBuilder which is expected to be attached to | ||
| 231 | /// a valid block/function. For each block in the function with a return | ||
| 232 | /// instruction, this function calls the appropriate memory methods to | ||
| 233 | /// deallocate any strings (which are alloced in the function prologue). | ||
| 234 | void createFreeSymbolStrings(llvm::IRBuilder<>&); | ||
| 235 | |||
| 236 | llvm::Module& mModule; | ||
| 237 | llvm::LLVMContext& mContext; | ||
| 238 | llvm::IRBuilder<> mBuilder; | ||
| 239 | |||
| 240 | // The stack of accessed values | ||
| 241 | std::stack<llvm::Value*> mValues; | ||
| 242 | |||
| 243 | // The stack of blocks for keyword branching | ||
| 244 | std::stack<std::pair<llvm::BasicBlock*, llvm::BasicBlock*>> mBreakContinueStack; | ||
| 245 | |||
| 246 | // The current scope number used to track scoped declarations | ||
| 247 | size_t mScopeIndex; | ||
| 248 | |||
| 249 | // The map of scope number to local variable names to values | ||
| 250 | SymbolTableBlocks mSymbolTables; | ||
| 251 | |||
| 252 | // The function used as the base code block | ||
| 253 | llvm::Function* mFunction; | ||
| 254 | |||
| 255 | const FunctionOptions mOptions; | ||
| 256 | |||
| 257 | Logger& mLog; | ||
| 258 | |||
| 259 | private: | ||
| 260 | FunctionRegistry& mFunctionRegistry; | ||
| 261 | }; | ||
| 262 | |||
| 263 | } // codegen_internal | ||
| 264 | |||
| 265 | } // namespace codegen | ||
| 266 | } // namespace ax | ||
| 267 | } // namespace OPENVDB_VERSION_NAME | ||
| 268 | } // namespace openvdb | ||
| 269 | |||
| 270 | #endif // OPENVDB_AX_COMPUTE_GENERATOR_HAS_BEEN_INCLUDED | ||
| 271 | |||
| 272 |