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 |